Initial commit
This commit is contained in:
0
app/speeches/__init__.py
Executable file
0
app/speeches/__init__.py
Executable file
3
app/speeches/admin.py
Executable file
3
app/speeches/admin.py
Executable file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
22
app/speeches/apps.py
Executable file
22
app/speeches/apps.py
Executable file
@ -0,0 +1,22 @@
|
||||
from django.apps import AppConfig
|
||||
from watson import search as watson
|
||||
|
||||
|
||||
class SpeechesConfig(AppConfig):
|
||||
name = 'speeches'
|
||||
|
||||
def ready(self):
|
||||
Protocol = self.get_model("Protocol")
|
||||
watson.register(Protocol, fields=["protocol_id",
|
||||
"protocol_period",
|
||||
"session_date_str"])
|
||||
|
||||
Speech = self.get_model("Speech")
|
||||
watson.register(Speech,
|
||||
fields=["speech_id",
|
||||
"foreign_protocol__protocol_id",
|
||||
"foreign_protocol__session_date_str",
|
||||
"foreign_speaker__id",
|
||||
"foreign_speaker__first_name",
|
||||
"foreign_speaker__last_name"],
|
||||
exclude=["speech_content"])
|
21
app/speeches/forms.py
Executable file
21
app/speeches/forms.py
Executable file
@ -0,0 +1,21 @@
|
||||
from django import forms
|
||||
|
||||
|
||||
class SearchForm(forms.Form):
|
||||
"""
|
||||
Configures the html input form for the protocol search.
|
||||
"""
|
||||
query = forms.CharField(label="Suche Protokoll", max_length="200")
|
||||
|
||||
query.widget.attrs.update({"class": "autocomplete materialize-textarea",
|
||||
"id": "icon_prefix2"})
|
||||
|
||||
|
||||
class SearchFormSpeech(forms.Form):
|
||||
"""
|
||||
Configures the html input form for the speech search.
|
||||
"""
|
||||
query = forms.CharField(label="Suche Rede", max_length="200")
|
||||
|
||||
query.widget.attrs.update({"class": "autocomplete materialize-textarea",
|
||||
"id": "icon_prefix2"})
|
120
app/speeches/management/commands/import_protocols.py
Executable file
120
app/speeches/management/commands/import_protocols.py
Executable file
@ -0,0 +1,120 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from speeches.models import Protocol, Speech
|
||||
from speakers.models import Speaker
|
||||
from lxml import etree
|
||||
import os
|
||||
import fnmatch
|
||||
import datetime
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = ("Adds protocols to the database using the django models"
|
||||
" syntax. Protocols will be added from the xml protocol files."
|
||||
" Input is a path pointing to all/multiple protocols in one"
|
||||
" directory with one level of subdirectories. First imports"
|
||||
" toc, attachments and metadata with model Protocol. Speeches will be put into realtion with the model Speech."
|
||||
" to the protocols later on.")
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("input_path",
|
||||
type=str)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
path = options["input_path"]
|
||||
list_of_files = []
|
||||
for path, subdirs, files in os.walk(path):
|
||||
for name in files:
|
||||
if fnmatch.fnmatch(name, "*.xml"):
|
||||
list_of_files.append(os.path.join(path, name))
|
||||
for file_path in tqdm(sorted(list_of_files), desc="Importing protocol data"):
|
||||
# self.stdout.write("Reading data from file: " + file_path)
|
||||
tree = etree.parse(file_path)
|
||||
protocol = Protocol()
|
||||
|
||||
protocol.protocol_id = os.path.basename(file_path)[:-4]
|
||||
# self.stdout.write("\tProtocol ID is: " + protocol.protocol_id)
|
||||
# self.stdout.write("\tReading toc and attachment.")
|
||||
|
||||
session_nr = tree.xpath("//sitzungsnr")[0]
|
||||
protocol.session_nr = session_nr
|
||||
|
||||
protocol_period = tree.xpath("@wahlperiode")[0]
|
||||
protocol.protocol_period = protocol_period
|
||||
|
||||
session_date = tree.xpath("//@date")[0]
|
||||
protocol.session_date_str = session_date
|
||||
session_date = datetime.datetime.strptime(session_date, "%d.%m.%Y")
|
||||
session_date = datetime.datetime.strftime(session_date, "%Y-%m-%d")
|
||||
protocol.session_date = session_date
|
||||
|
||||
correct_start_time = None
|
||||
start_of_session = tree.xpath("//@sitzung-start-uhrzeit")[0]
|
||||
try:
|
||||
start_of_session = datetime.datetime.strptime(start_of_session,
|
||||
"%H:%M")
|
||||
correct_start_time = True
|
||||
except ValueError as e:
|
||||
correct_start_time = False
|
||||
if(correct_start_time is True):
|
||||
protocol.start_of_session = start_of_session
|
||||
else:
|
||||
protocol.start_of_session = None
|
||||
|
||||
end_of_session = tree.xpath("//@sitzung-ende-uhrzeit")[0]
|
||||
correct_end_time = None
|
||||
try:
|
||||
end_of_session = datetime.datetime.strptime(end_of_session,
|
||||
"%H:%M")
|
||||
correct_end_time = True
|
||||
except ValueError as e:
|
||||
correct_end_time = False
|
||||
if(correct_end_time is True):
|
||||
protocol.end_of_session = end_of_session
|
||||
else:
|
||||
protocol.end_of_session = None
|
||||
|
||||
session_nr = tree.xpath("//sitzungsnr")[0]
|
||||
protocol.session_nr = session_nr.text
|
||||
|
||||
election_period = tree.xpath("//wahlperiode")[0]
|
||||
protocol.election_period = election_period.text
|
||||
|
||||
toc = tree.xpath("//inhaltsverzeichnis")[0]
|
||||
protocol.toc = toc.text
|
||||
|
||||
attachment = tree.xpath("//anlagen")[0]
|
||||
protocol.attachment = attachment.text
|
||||
protocol.save()
|
||||
|
||||
speeches = tree.xpath("//sitzungsbeginn | //rede")
|
||||
for previous_e, current_e, next_e in zip([None]+speeches[:-1], speeches, speeches[1:]+[None]):
|
||||
# self.stdout.write("\tReading speech from " + protocol.protocol_id)
|
||||
speech = Speech()
|
||||
speech.foreign_protocol = protocol
|
||||
if(previous_e is not None):
|
||||
previous_speech_id = previous_e.xpath("@id")[0]
|
||||
speech.previous_speech_id = previous_speech_id
|
||||
speech_id = current_e.xpath("@id")[0]
|
||||
speech.speech_id = speech_id
|
||||
if(next_e is not None):
|
||||
next_speech_id = next_e.xpath("@id")[0]
|
||||
speech.next_speech_id = next_speech_id
|
||||
# self.stdout.write("\tSpeech ID is:" + str(speech.speech_id))
|
||||
# self.stdout.write("\tPrevious Speech ID is:" + str(speech.previous_speech_id))
|
||||
# self.stdout.write("\tNext Speech ID is:" + str(speech.next_speech_id))
|
||||
speaker_type = current_e.xpath("//@typ")[0]
|
||||
speech.speaker_type = speaker_type
|
||||
speaker_id = current_e.xpath(".//redner/@id")[0]
|
||||
# self.stdout.write("\tCurrent speaker ID is:" + str(speaker_id))
|
||||
if(speaker_id != "None"):
|
||||
speech.foreign_speaker = Speaker.objects.filter(pk=speaker_id)[0]
|
||||
# self.stdout.write("\tSpeaker ID (Foreign key) is:" + str(speech.foreign_speaker))
|
||||
speech_content = current_e.xpath(".//p")
|
||||
speech_content = [str(etree.tostring(p)) for p in speech_content]
|
||||
speech_content = "".join(speech_content)
|
||||
speech.speech_content = speech_content
|
||||
original_string = current_e.xpath(".//redner/name")[0]
|
||||
speech.original_string = original_string.tail
|
||||
# self.stdout.write("\t-------------------------------------------")
|
||||
speech.save()
|
0
app/speeches/migrations/__init__.py
Normal file
0
app/speeches/migrations/__init__.py
Normal file
44
app/speeches/models.py
Executable file
44
app/speeches/models.py
Executable file
@ -0,0 +1,44 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Protocol(models.Model):
|
||||
"""
|
||||
This models contains the data about one protocol. Data will be imported from
|
||||
the XML protocols via the custom django-admin command import_protocols.py.
|
||||
Does not contain speeches. Speeches will be related to this model though.
|
||||
Only contains table of contents, metadata etc.
|
||||
"""
|
||||
protocol_id = models.IntegerField(primary_key=True, verbose_name="Protokoll ID")
|
||||
protocol_period = models.IntegerField(verbose_name="Wahlperiode", null=True, blank=True)
|
||||
session_nr = models.IntegerField(verbose_name="Sitzungsnummer", null=True, blank=True)
|
||||
session_date = models.DateField(verbose_name="Datum")
|
||||
session_date_str = models.CharField(verbose_name="Datums String", max_length=12, blank=True, default=None, null=True)
|
||||
start_of_session = models.TimeField(null=True, verbose_name="Startuhrzeit")
|
||||
end_of_session = models.TimeField(null=True, verbose_name="Enduhrzeit")
|
||||
toc = models.TextField(verbose_name="Inhaltsverzeichnis")
|
||||
attachment = models.TextField(verbose_name="Anlagen")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.protocol_id) + " " + str(self.session_date)
|
||||
|
||||
|
||||
class Speech(models.Model):
|
||||
"""
|
||||
This models contains the data about one speech. Data will be imported from
|
||||
the XML protocols via the custom django-admin command import_speeches.py.
|
||||
"""
|
||||
foreign_protocol = models.ForeignKey("Protocol", on_delete=models.CASCADE,
|
||||
verbose_name="Foreign Protokoll",
|
||||
default=None)
|
||||
speech_id = models.CharField(verbose_name="Rede ID", primary_key=True, max_length=14)
|
||||
previous_speech_id = models.CharField(verbose_name="Vorherige Rede ID", max_length=14, blank=True, default=None, null=True)
|
||||
next_speech_id = models.CharField(verbose_name="Nächste Rede ID", max_length=14, blank=True, default=None, null=True)
|
||||
speaker_type = models.CharField(verbose_name="Rolle des MdBs", max_length=50)
|
||||
foreign_speaker = models.ForeignKey("speakers.Speaker", on_delete=models.CASCADE,
|
||||
null=True, blank=True, verbose_name="MdB ID", )
|
||||
speech_content = models.TextField(verbose_name="Redeinhalt") # import as XML element to string
|
||||
original_string = models.TextField(verbose_name="Original String")
|
||||
|
||||
def __str__(self):
|
||||
return (str(self.foreign_protocol) + " " + str(self.speech_id) + " "
|
||||
+ self.speech_content[:20])
|
53
app/speeches/tables.py
Executable file
53
app/speeches/tables.py
Executable file
@ -0,0 +1,53 @@
|
||||
import django_tables2 as tables
|
||||
from .models import Speech, Protocol
|
||||
from django_tables2.utils import A # alias for Accessor
|
||||
|
||||
|
||||
class SpeechTable(tables.Table):
|
||||
"""
|
||||
Configures the table showing all speeches. Inserts a column with links to
|
||||
the speeches. Also defines all shown columns.
|
||||
"""
|
||||
link = tables.LinkColumn("Rede", text="Rede", args=[A("speech_id")],
|
||||
orderable=False,
|
||||
attrs={"a": {"class": "waves-effect waves-light btn light-green darken-3"}}) # Adds colum with Link to Rede
|
||||
|
||||
class Meta:
|
||||
model = Speech
|
||||
fields = ("speech_id", "foreign_protocol.protocol_id",
|
||||
"foreign_protocol.session_date", "foreign_speaker.id",
|
||||
"foreign_speaker.first_name", "foreign_speaker.last_name")
|
||||
template_name = "speeches/table.html"
|
||||
empty_text = ("Für den eingegebenen Suchbegriff gibt es leider keine Ergebnisse.")
|
||||
|
||||
|
||||
class SpeakerSpeechTable(tables.Table):
|
||||
"""
|
||||
Configures the table showing all speeches of one speaker in his profile.
|
||||
Inserts a column with links to the speeches. Also defines all shown columns.
|
||||
"""
|
||||
link = tables.LinkColumn("Rede", text="Rede", args=[A("speech_id")],
|
||||
orderable=False,
|
||||
attrs={"a": {"class": "waves-effect waves-light btn light-green darken-3"}}) # Adds colum with Link to Speaker
|
||||
|
||||
class Meta:
|
||||
model = Speech
|
||||
fields = ("speech_id", "foreign_protocol.protocol_id", "foreign_protocol.session_date")
|
||||
template_name = "speeches/table.html"
|
||||
empty_text = ("Für den eingegebenen Suchbegriff gibt es leider keine Ergebnisse.")
|
||||
|
||||
|
||||
class ProtocolTable(tables.Table):
|
||||
"""
|
||||
Configures the table showing all protocols.
|
||||
Inserts a column with links to the protocols. Also defines all shown columns.
|
||||
"""
|
||||
link = tables.LinkColumn("Protokoll", text="Protokoll", args=[A("protocol_id")],
|
||||
orderable=False,
|
||||
attrs={"a": {"class": "waves-effect waves-light btn light-green darken-3"}}) # Adds colum with Link to protocol
|
||||
|
||||
class Meta:
|
||||
model = Protocol
|
||||
fields = ("protocol_id", "session_date", "protocol_period")
|
||||
template_name = "speeches/table.html"
|
||||
empty_text = ("Für den eingegebenen Suchbegriff gibt es leider keine Ergebnisse.")
|
84
app/speeches/templates/speeches/protocol.html
Executable file
84
app/speeches/templates/speeches/protocol.html
Executable file
@ -0,0 +1,84 @@
|
||||
{% extends "blog/base.html" %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12 m4">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title center-align">
|
||||
Protokoll:<br />{{current_protocol.protocol_id}}</span>
|
||||
<p class="center-align"><i class="large material-icons blue-grey-text darken-4">insert_drive_file</i></p>
|
||||
<span class="card-title">Metadaten</span>
|
||||
<ul>
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">date_range</i>Wahlperiode: {{current_protocol.protocol_period}}</li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">date_range</i>Sitzungsnummer: {{current_protocol.session_nr}}</li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">date_range</i>Datum: {{current_protocol.session_date}}</li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">access_time</i>Startuhrzeit der Sitzung: {{current_protocol.start_of_session}} Uhr</li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">av_timer</i>Enduhrzeit der Sitzung: {{current_protocol.end_of_session}} Uhr</li>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<ul class="collapsible hoverable white">
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="material-icons left blue-grey-text darken-4">account_circle</i>MdBs dieser Rede</div>
|
||||
<div class="collapsible-body">
|
||||
<ul>
|
||||
{% for speaker in speakers %}
|
||||
<li>
|
||||
<a href="/mdbs/mdb/{{speaker.id}}">{{speaker.id}}: {{speaker.last_name}}, {{speaker.first_name}}</a>
|
||||
</li>
|
||||
<br />
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="material-icons left blue-grey-text darken-4">toc</i>Inhaltsverzeichnis</div>
|
||||
<div class="collapsible-body"><span>{{current_protocol.toc}}</span></div>
|
||||
</li>
|
||||
{% if current_protocol.attachment %}
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="material-icons left blue-grey-text darken-4">folder</i>Anlagen</div>
|
||||
<div class="collapsible-body"><span>{{current_protocol.attachment}}</span></div>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col s12 m8">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Gesamtes Protokol</span>
|
||||
{% for speaker, speech, related_speech in speaker_speech_html %}
|
||||
{% autoescape off%}
|
||||
{% if speaker.id %}
|
||||
<h5><a href="/mdbs/mdb/{{speaker.id}}">{% if speaker.title %}
|
||||
{{speaker.title}}
|
||||
{% endif %}
|
||||
{% if speaker.nobility %}
|
||||
{{speaker.nobility}}
|
||||
{% endif %}
|
||||
{{speaker.first_name}}
|
||||
{% if speaker.name_prefix %}
|
||||
{{speaker.name_prefix}}
|
||||
{% endif %}
|
||||
{{speaker.last_name}}:</a></h5>
|
||||
{% else %}
|
||||
<span class="card-title">Rede von: Unbekannt<a class="tooltipped" data-position="bottom" data-tooltip="Dieses Mitglied des Bundestags konnte leider nicht automatisch erkannt werden. Grundlegende Infos zu Namen etc. können beim zugehörigen Redeeintrag unter dem Punkt Original String gefunden werden."><i
|
||||
class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">info_outline</i></a></span>
|
||||
{% endif %}
|
||||
<h6><a href="/protokolle/rede/{{related_speech.speech_id}}">Rede ID: {{related_speech.speech_id}}</a></h6>
|
||||
{{speech}}
|
||||
{% endautoescape %}
|
||||
{% endfor%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
31
app/speeches/templates/speeches/protocols.html
Executable file
31
app/speeches/templates/speeches/protocols.html
Executable file
@ -0,0 +1,31 @@
|
||||
{% extends "blog/base.html" %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Alle Protokolle des Bundestag</span>
|
||||
<p>Liste aller Bundestagsplenarprotokolle von der ersten bis zur 18. Wahlperiode. Eine Volltextsuche ist zurzeit noch nicht implementiert.</p>
|
||||
<div class="row">
|
||||
<form method="GET" class="col l4 offset-l8 m6 offset-m6 s12">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
{{form}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div style="overflow-x:auto;">
|
||||
{% render_table table %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
144
app/speeches/templates/speeches/speech.html
Executable file
144
app/speeches/templates/speeches/speech.html
Executable file
@ -0,0 +1,144 @@
|
||||
{% extends "blog/base.html" %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12 m4">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title center-align">
|
||||
Rede:<br />{{current_speech.speech_id}}</span>
|
||||
<p class="center-align"><i class="large material-icons blue-grey-text darken-4">insert_comment</i></p>
|
||||
<span class="card-title">Metadaten</span>
|
||||
<ul>
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">insert_drive_file</i><a href="/protokolle/protokoll/{{current_speech.foreign_protocol.protocol_id}}">Aus Protokoll:
|
||||
{{current_speech.foreign_protocol.protocol_id}}</a></li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">date_range</i>Datum: {{current_speech.foreign_protocol.session_date}}</li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">access_time</i>Startuhrzeit der Sitzung: {{current_speech.foreign_protocol.start_of_session}} Uhr</li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">av_timer</i>Enduhrzeit der Sitzung: {{current_speech.foreign_protocol.end_of_session}} Uhr</li>
|
||||
<br />
|
||||
{% if current_speech.foreign_speaker.id %}
|
||||
<a href="/mdbs/mdb/{{current_speech.foreign_speaker.id}}">
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">fingerprint</i>Redner ID: {{current_speech.foreign_speaker.id}}</li>
|
||||
</a>
|
||||
{%else%}
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">fingerprint</i>Redner ID: Nicht erkannt {{current_speech.foreign_speaker.id}}</li>
|
||||
{% endif %}
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">perm_identity</i>Rednertyp: {{current_speech.speaker_type}}</li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">short_text</i>Original String: {{current_speech.original_string}}<a class="tooltipped" data-position="bottom" data-tooltip="Dies ist die Zeichenfolge, mit der der aktuelle Redner oder die aktuelle Rednerin im original Protokoll vor ihrem Redebeitrag genannt wurde. Passt dieser nicht zum Namen, der über der Rede steht, ist leider etwas bei der automatischen Erkennung schiefgegangen. Steht kein Name über der Rede, wurde der Redner nicht automatisch erkannt."><i
|
||||
class="material-icons blue-grey-text darken-4">info_outline</i></a></li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">record_voice_over</i>Unterbrechungen/Zurufe: {{interruptions}}</li>
|
||||
<br />
|
||||
<li><i class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">subject</i>Länge: {{words}} Wörter</li>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="collapsible hoverable">
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="material-icons left blue-grey-text darken-4">sort_by_alpha</i>Vokabular</div>
|
||||
<div class="collapsible-body white"><span><b>Vokabeln: {{unique_words}}</b><br /><ol>{{ vocabulary|safe }}</ol></span></div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="material-icons left blue-grey-text darken-4">toc</i>Inhaltsverzeichnis</div>
|
||||
<div class="collapsible-body white"><span>{{current_speech.foreign_protocol.toc}}</span></div>
|
||||
</li>
|
||||
{% if current_speech.foreign_protocol.attachment %}
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="material-icons left blue-grey-text darken-4">folder</i>Anlagen</div>
|
||||
<div class="collapsible-body white"><span>{{current_speech.foreign_protocol.attachment}}</span></div>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col s12 m8">
|
||||
{% if previous_speech.speech_content %}
|
||||
<ul class="collapsible hoverable white">
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="large material-icons blue-grey-text darken-4">insert_comment</i>Vorherige Rede als Kontext</div>
|
||||
<div class="collapsible-body">
|
||||
<h6>{% if previous_speech.foreign_speaker.id %}
|
||||
Rede von {% if previous_speech.foreign_speaker.title %}
|
||||
{{previous_speech.foreign_speaker.title}}
|
||||
{% endif %}
|
||||
{% if previous_speech.foreign_speaker.nobility %}
|
||||
{{previous_speech.foreign_speaker.nobility}}
|
||||
{% endif %}
|
||||
{{previous_speech.foreign_speaker.first_name}}
|
||||
{% if previous_speech.foreign_speaker.name_prefix %}
|
||||
{{previous_speech.foreign_speaker.name_prefix}}
|
||||
{% endif %}
|
||||
{{previous_speech.foreign_speaker.last_name}}
|
||||
({{previous_speech.foreign_speaker.party}})
|
||||
{% else %}
|
||||
<span class="card-title">Rede von: Unbekannt<a class="tooltipped" data-position="bottom" data-tooltip="Dieses Mitglied des Bundestags konnte leider nicht automatisch erkannt werden. Grundlegende Infos zu Namen etc. können beim zugehörigen Redeeintrag unter dem Punkt Original String gefunden werden."><i
|
||||
class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">info_outline</i></a></span>
|
||||
{% endif %}
|
||||
</h6>
|
||||
<span>{% autoescape off %}{{previous_speech_html}}{% endautoescape %}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
{% if previous_speech %}
|
||||
<div class="center-align"><a href="/protokolle/rede/{{previous_speech.speech_id}}" class="waves-effect waves-light light-green darken-3 btn"><i class="material-icons left">arrow_upward</i>Zur Rede davor</a></div>
|
||||
<br />
|
||||
{% endif %}
|
||||
{% if current_speech.foreign_speaker.id%}
|
||||
<span class="card-title">Rede von {% if current_speech.foreign_speaker.title %}
|
||||
{{current_speech.foreign_speaker.title}}
|
||||
{% endif %}
|
||||
{% if current_speech.foreign_speaker.nobility %}
|
||||
{{current_speech.foreign_speaker.nobility}}
|
||||
{% endif %}
|
||||
{{current_speech.foreign_speaker.first_name}}
|
||||
{% if current_speech.foreign_speaker.name_prefix %}
|
||||
{{current_speech.foreign_speaker.name_prefix}}
|
||||
{% endif %}
|
||||
{{current_speech.foreign_speaker.last_name}}
|
||||
({{current_speech.foreign_speaker.party}})</span>
|
||||
{% else %}
|
||||
<span class="card-title">Rede von: Unbekannt<a class="tooltipped" data-position="bottom" data-tooltip="Dieses Mitglied des Bundestags konnte leider nicht automatisch erkannt werden. Grundlegende Infos zu Namen etc. können links unter 'Original String' gelesen werden."><i
|
||||
class="material-icons blue-grey-text darken-4" style="margin-right: 10px;">info_outline</i></a></span>
|
||||
{% endif %}
|
||||
{% autoescape off %}
|
||||
{{current_speech_html}}
|
||||
{% endautoescape %}
|
||||
{% if next_speech %}
|
||||
<div class="center-align"><br /><a href="/protokolle/rede/{{next_speech.speech_id}}" class="waves-effect waves-light light-green darken-3 btn"><i class="material-icons left">arrow_downward</i>Zur Rede danach</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if next_speech.speech_content %}
|
||||
<ul class="collapsible hoverable white">
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="large material-icons blue-grey-text darken-4">insert_comment</i>Nächste Rede als Kontext</div>
|
||||
<div class="collapsible-body">
|
||||
<h6>Rede von {% if next_speech.foreign_speaker.title %}
|
||||
{{next_speech.foreign_speaker.title}}
|
||||
{% endif %}
|
||||
{% if next_speech.foreign_speaker.nobility %}
|
||||
{{next_speech.foreign_speaker.nobility}}
|
||||
{% endif %}
|
||||
{{next_speech.foreign_speaker.first_name}}
|
||||
{% if next_speech.foreign_speaker.name_prefix %}
|
||||
{{next_speech.foreign_speaker.name_prefix}}
|
||||
{% endif %}
|
||||
{{next_speech.foreign_speaker.last_name}}
|
||||
({{next_speech.foreign_speaker.party}})</h6>
|
||||
<span><span>{% autoescape off %}{{next_speech_html}}{% endautoescape %}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
31
app/speeches/templates/speeches/speeches.html
Executable file
31
app/speeches/templates/speeches/speeches.html
Executable file
@ -0,0 +1,31 @@
|
||||
{% extends "blog/base.html" %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Reden und Redebeiträge aller Mitglieder des Bundestags seit 1949 bis 2017</span>
|
||||
<p>Hier ist eine liste aller Reden, die Mitglieder des Bundestags gehalten haben. Eine Volltextsuche ist zurzeit noch nicht implementiert.</p>
|
||||
<div class="row">
|
||||
<form method="GET" class="col l4 offset-l8 m6 offset-m6 s12">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
{{form}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div style="overflow-x:auto;">
|
||||
{% render_table table %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
95
app/speeches/templates/speeches/table.html
Executable file
95
app/speeches/templates/speeches/table.html
Executable file
@ -0,0 +1,95 @@
|
||||
{% load django_tables2 %}
|
||||
{% load i18n %}
|
||||
{% block table-wrapper %}
|
||||
{% block table %}
|
||||
<table {% render_attrs table.attrs %} class="highlight">
|
||||
{% block table.thead %}
|
||||
{% if table.show_header %}
|
||||
<thead {{ table.attrs.thead.as_html }}>
|
||||
<tr>
|
||||
{% for column in table.columns %}
|
||||
<th {{ column.attrs.th.as_html }}>
|
||||
{% if column.orderable %}
|
||||
<a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}"><i class="material-icons ">sort</i> {{ column.header }}</a>
|
||||
{% else %}
|
||||
{{ column.header }}
|
||||
{% endif %}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
{% endif %}
|
||||
{% endblock table.thead %}
|
||||
{% block table.tbody %}
|
||||
<tbody {{ table.attrs.tbody.as_html }}>
|
||||
{% for row in table.paginated_rows %}
|
||||
{% block table.tbody.row %}
|
||||
<tr {{ row.attrs.as_html }}>
|
||||
{% for column, cell in row.items %}
|
||||
<td {{ column.attrs.td.as_html }}>{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endblock table.tbody.row %}
|
||||
{% empty %}
|
||||
{% if table.empty_text %}
|
||||
{% block table.tbody.empty_text %}
|
||||
<tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
|
||||
{% endblock table.tbody.empty_text %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endblock table.tbody %}
|
||||
{% block table.tfoot %}
|
||||
{% if table.has_footer %}
|
||||
<tfoot {{ table.attrs.tfoot.as_html }}>
|
||||
<tr>
|
||||
{% for column in table.columns %}
|
||||
<td {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tfoot>
|
||||
{% endif %}
|
||||
{% endblock table.tfoot %}
|
||||
</table>
|
||||
{% endblock table %}
|
||||
|
||||
{% block pagination %}
|
||||
{% if table.page and table.paginator.num_pages > 1 %}
|
||||
<ul class="pagination">
|
||||
{% if table.page.has_previous %}
|
||||
{% block pagination.previous %}
|
||||
<li class="previous waves-effect">
|
||||
<a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">
|
||||
{% trans '<i class="material-icons">chevron_left</i>' %}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock pagination.previous %}
|
||||
{% endif %}
|
||||
{% if table.page.has_previous or table.page.has_next %}
|
||||
{% block pagination.range %}
|
||||
{% for p in table.page|table_page_range:table.paginator %}
|
||||
<li {% if p == table.page.number %}class="active light-green darken-3"{% endif %} class="waves-effect">
|
||||
{% if p == '...' %}
|
||||
<a href="#">{{ p }}</a>
|
||||
{% else %}
|
||||
<a href="{% querystring table.prefixed_page_field=p %}">
|
||||
{{ p }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endblock pagination.range %}
|
||||
{% endif %}
|
||||
{% if table.page.has_next %}
|
||||
{% block pagination.next %}
|
||||
<li class="next waves-effect">
|
||||
<a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">
|
||||
{% trans '<i class="material-icons">chevron_right</i>' %}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock pagination.next %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock pagination %}
|
||||
{% endblock table-wrapper %}
|
3
app/speeches/tests.py
Executable file
3
app/speeches/tests.py
Executable file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
9
app/speeches/urls.py
Executable file
9
app/speeches/urls.py
Executable file
@ -0,0 +1,9 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("reden/", views.speeches, name="Reden"),
|
||||
path("liste-protokolle/", views.protocols, name="Protokoll-list"),
|
||||
path("protokoll/<int:protocol_id>", views.protocol, name="Protokoll"),
|
||||
path("rede/<str:speech_id>", views.speech, name="Rede")
|
||||
]
|
37
app/speeches/utils.py
Executable file
37
app/speeches/utils.py
Executable file
@ -0,0 +1,37 @@
|
||||
import re
|
||||
from lxml import etree
|
||||
|
||||
|
||||
def create_html_speech(speech_content_xml_string):
|
||||
"""
|
||||
COnverts the XML speech content into styled html. Also counts the words and
|
||||
shows the vocabulary.
|
||||
"""
|
||||
speech_html = "<div>" + speech_content_xml_string + "</div>"
|
||||
speech_html = etree.fromstring(speech_html)
|
||||
raw_text = []
|
||||
interruptions = 0
|
||||
for element in speech_html.iter():
|
||||
if(element.tag == "p"):
|
||||
raw_text.append(element.text)
|
||||
element.tag = "span"
|
||||
element.attrib["class"]="line"
|
||||
element.attrib.pop("klasse", None)
|
||||
elif(element.tag == "kommentar"):
|
||||
interruptions += 1
|
||||
element.tag = "span"
|
||||
element.attrib["class"]="comment"
|
||||
element.attrib.pop("klasse", None)
|
||||
elif(element.tag == "metadata"):
|
||||
element.tag = "blockquote"
|
||||
element.attrib["class"]="metadata"
|
||||
element.attrib.pop("klasse", None)
|
||||
element.text = "Metadaten/Kopzeile:" + "\\n" + element.text
|
||||
raw_text = [element for element in raw_text if element != None]
|
||||
raw_text = "".join(raw_text)
|
||||
speech_html = etree.tostring(speech_html, pretty_print=True, encoding='unicode')
|
||||
speech_html = re.sub(r"b'", "", speech_html)
|
||||
speech_html = re.sub(r"\\n\s+\'", "<br/>", speech_html)
|
||||
speech_html = re.sub(r"\\n", "<br/>", speech_html)
|
||||
speech_html = re.sub(r"\\'", "'", speech_html)
|
||||
return(speech_html, raw_text, interruptions)
|
109
app/speeches/views.py
Executable file
109
app/speeches/views.py
Executable file
@ -0,0 +1,109 @@
|
||||
from django.shortcuts import render
|
||||
from django_tables2 import RequestConfig
|
||||
from .models import Speech, Protocol
|
||||
from .tables import SpeechTable, ProtocolTable
|
||||
from django.http import Http404
|
||||
from .utils import create_html_speech
|
||||
from .forms import SearchForm, SearchFormSpeech
|
||||
from watson import search as watson
|
||||
from collections import Counter
|
||||
|
||||
|
||||
def speech(request, speech_id):
|
||||
try:
|
||||
current_speech = Speech.objects.get(pk=speech_id)
|
||||
if(current_speech.previous_speech_id is not None):
|
||||
previous_speech = Speech.objects.get(pk=current_speech.previous_speech_id)
|
||||
previous_speech_html = create_html_speech(previous_speech.speech_content)[0]
|
||||
else:
|
||||
previous_speech = None
|
||||
previous_speech_html = None
|
||||
if(current_speech.next_speech_id is not None):
|
||||
next_speech = Speech.objects.get(pk=current_speech.next_speech_id)
|
||||
next_speech_html = create_html_speech(next_speech.speech_content)[0]
|
||||
else:
|
||||
next_speech = None
|
||||
next_speech_html = None
|
||||
current_speech_html, raw_text, interruptions = create_html_speech(current_speech.speech_content)
|
||||
vocabulary = Counter(raw_text.split()).most_common()
|
||||
unique_words = len(vocabulary)
|
||||
tmp_str = []
|
||||
for pair in vocabulary:
|
||||
tmp_str.append("<li>{}: {}</li>".format(pair[0], pair[1]))
|
||||
vocabulary = "".join(tmp_str)
|
||||
except Speech.DoesNotExist:
|
||||
raise Http404("Speech does not exist")
|
||||
context = {"title": ("Rede – "
|
||||
+ " "
|
||||
+ current_speech.speech_id),
|
||||
"current_speech": current_speech,
|
||||
"current_speech_html": current_speech_html,
|
||||
"previous_speech_html": previous_speech_html,
|
||||
"next_speech_html": next_speech_html,
|
||||
"previous_speech": previous_speech,
|
||||
"next_speech": next_speech,
|
||||
"interruptions": interruptions,
|
||||
"words": len(raw_text.split()),
|
||||
"vocabulary": vocabulary,
|
||||
"unique_words": unique_words}
|
||||
return render(request, "speeches/speech.html", context)
|
||||
|
||||
|
||||
def speeches(request):
|
||||
if(request.method == "GET"):
|
||||
form = SearchFormSpeech(request.GET)
|
||||
if(form.is_valid()):
|
||||
query = form.cleaned_data["query"]
|
||||
search_results = watson.filter(Speech, query)
|
||||
table = SpeechTable(search_results)
|
||||
RequestConfig(request, paginate={'per_page': 20}).configure(table)
|
||||
context = {"title": "Suchergebnisse für " + query,
|
||||
"form": form, "table": table}
|
||||
return render(request, "speeches/speeches.html", context)
|
||||
else:
|
||||
form = SearchFormSpeech()
|
||||
table = SpeechTable(Speech.objects.all().order_by("speech_id"))
|
||||
RequestConfig(request, paginate={'per_page': 20}).configure(table)
|
||||
context = {"title": "Suche", "table": table, "form": form}
|
||||
return render(request, "speeches/speeches.html", context)
|
||||
|
||||
|
||||
def protocol(request, protocol_id):
|
||||
try:
|
||||
current_protocol = Protocol.objects.get(pk=protocol_id)
|
||||
related_speeches = Speech.objects.filter(foreign_protocol=protocol_id).order_by("speech_id")
|
||||
speakers = []
|
||||
speeches_html = []
|
||||
for speech in related_speeches:
|
||||
speakers.append(speech.foreign_speaker)
|
||||
speech_html = create_html_speech(speech.speech_content)[0]
|
||||
speeches_html.append(speech_html)
|
||||
speaker_speech_html = zip(speakers, speeches_html, related_speeches)
|
||||
except Protocol.DoesNotExist:
|
||||
raise Http404("Protocol does not exist")
|
||||
context = {"title": ("Protokoll – " + str(current_protocol.protocol_id)),
|
||||
"current_protocol": current_protocol,
|
||||
"related_speeches": related_speeches,
|
||||
"speeches_html": speeches_html,
|
||||
"speakers": set(speakers),
|
||||
"speaker_speech_html": speaker_speech_html}
|
||||
return render(request, "speeches/protocol.html", context)
|
||||
|
||||
|
||||
def protocols(request):
|
||||
if(request.method == "GET"):
|
||||
form = SearchForm(request.GET)
|
||||
if(form.is_valid()):
|
||||
query = form.cleaned_data["query"]
|
||||
search_results = watson.filter(Protocol, query)
|
||||
table = ProtocolTable(search_results)
|
||||
RequestConfig(request, paginate={'per_page': 20}).configure(table)
|
||||
context = {"title": "Suchergebnisse für " + query,
|
||||
"form": form, "table": table}
|
||||
return render(request, "speeches/protocols.html", context)
|
||||
else:
|
||||
form = SearchForm()
|
||||
table = ProtocolTable(Protocol.objects.all().order_by("session_date"))
|
||||
RequestConfig(request, paginate={'per_page': 20}).configure(table)
|
||||
context = {"title": "Suche", "table": table, "form": form}
|
||||
return render(request, "speeches/protocols.html", context)
|
Reference in New Issue
Block a user