diff --git a/README.md b/README.md index 05365b3..22f9b2c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,137 @@ # Multimodal Parliament Explorer (Gruppe_05_1) +## Umgebung - wichtiger Hinweis! +Damit der Zugriff auf die Docker-Treiber funktioniert, muss man in IntelliJ folgendes tun: -## Classdiagram puml generator +Edit Run Environment --> Modify Option --> Add VM options + +Dann im neu erscheinenden "VM options"-Feld folgendes schreiben: + +--add-opens java.base/java.util=ALL-UNNAMED + +## Aufruf + +Der Nutzer führt die **Main.java** Datei aus. +Folgende Command Line Argumente werden akzeptiert: + +- onlyRunWeb: Fährt die WebServies hoch und wartet auf Requests. Die Einstiegsseite kann im Browser geöffnet werden: http://localhost:5876 (Port 5876 ist in der Resourcendatei javalin.properties definiert). In der Regel würde man Main nur mit diesem Parameter aufrufen. +- uploadMemberPhotos: Um Parlamentarierfotos vom Resource-Verzeichnis in die Datenbank hochzuladen. +- forceUploadMembers: Lädt die Daten der Parlamentariern erneut in die Datenbank. +- forceUploadSpeeches: Lädt die Reden erneut in die Datenbank, führt die NLP-Analyse auf der Reden und speichert die Analyseergebnisse in die Datenbank. +- rebuildMetadata: Berechnet die Metadaten neu. In der Metadata-Collection stehen Daten, welche oft benötigt werden, aber nicht jedes Mal erneut berechnet werden sollen. Momentan sind es die Liste der Parteien und die Liste der möglichen NLP-Topics. +- debugLogging: Diese Flag bestimmt, ob Debug-Ausgaben des Loggers angezeigt werden sollen. + +## Dokumentation + +- Use Case Diagramm +- Package/Klassen-Diagramm +- Die Plannung +- Das Gantt Diagramm +stehen im "doku"-Verzeichnis. + +Die Plannung beinhaltet: +- Die Liste der Aufgaben, nach Bereichen gruppiert. Die Bereiche sind: Plannung, Domain-Entitäten, XML, Datenbank, Rest Services, Front End, NLP-Verarbeitug, Export, Dokumentation +- Wer macht was +- Geschätzter Aufwand (niedrig / mittel / hoch) + + +### Classdiagram puml generator +Das Classdiagram wird durch den puml generator generiert: ```shell python puml_generator.py ``` -## Upload member Images + +## Datenbank + +Die wichtigsten Collections sind *"speech"* und *"speaker"*. In "pictures" stehen die Bilder der Abgeordneten. In "metadata" stehen die bereits erwähnten Metadaten. +Die Collections "agendaItems" und "sessions" werden zwar initial befüllt und kurzzeitig verwendet, um die Zeitstempel der Reden sowie die Agendapunkte zu konstruieren, werden aber danach nicht weiter verwendet. + +## Erzeugung der NLP-Daten +TODO HENRY + + + +## Komponenten und Package Struktur +- database: die Klasse *MongoDBHandler* kümmert sich um die Herstellung der Datenbankverbindung und um generelle CRUD-Operationen. Die Klasse *MongoPprUtils* kümmert sich um das Lesen der Objekte und Datenstrukturen, die für diese Übung notwendig sind. + +- domain: hier werden die Entitäten definiert. Subpackage "database" behandelt die Objekte, die aus der Datenbank kommen. Subpackage "html" behandelt die Objekte, die nicht in der Datenbank sind und lediglich angezeigt werden. + +- rest: alles, was mit den WebServices zu tun hat - die 4 Controller (für Parlamentarier, Fotos, Videos und Reden), eine Configklasse und der Handler. Im Handler wird die Javalin-Konfiguration definiert sowie die Routes für die 8 Endpoints. + +- export: alles, was mit dem Export (LaTex/PDF, XML) zu tun hat, findet hier Platz. + +- xml: für das Einlesen der Parlamentarier- und Rede-Daten. + +- util: eine kleine Sammlung von Utility-Klassen. + +- nlp: Utils für die NLP-Verarbeitung + +- exceptions: für die Exceptions + + +## Struktur des resources-Ordner + +- config: config files für javalin und für XML (im letzteren steht die URL zum Herunterladen der Parlamentarier) + +- plenarprotokolle: enthält die DTD-Datei zum Parsen der Abgeordnetendaten + +- speeches: enthält die Datei TypeSystem.xml + +- static: enthält das Stylesheet und das Favicon + +- templates: enthält die FreeMarker-Templates + +- tex: enthält die nötigen Resourcen für Latex, etwa die preamble.tex + + +## Ablauf / Workflow + +Wir gehen hier vom Workflow des Endnutzers aus. + +Wie man die Daten hochlädt, wurde bereit oben kurz erklärt. + +1. Die Klasse Main wird ausgefüht und zwar mit dem "onlyRunWeb" Parameter. +2. Javalin fährt die Webservices hoch und wartet auf Requests. +3. Der User lädt die Einstiegsseite im Browser, etwas http://localhost:5876 . Die Startseite wird angezeigt. Das Hauptmenü enthält im Burgermenü Links zu "Parlamentarier", "Reden", "Exportieren" sowie "Home" und "Über". + +### Parlamentarier-Seite + +Eine Liste der Parlamentarier wird angezeigt. Man kann die Liste filtern. Man kann auf die jeweiligen Parlamentarier klicken und gelangt dann auf die Seite eines Parlamentariers. + +Die Seite eines Parlamentariers beinhaltet Name, Foto, persöhnliche Daten, Mitgliedschaften und einen Link zu den Reden des Parlamentariers. + +### Reden-Seite + +Es werden alle Reden der jetzigen Legislaturperiode angezeigt. +Man kann die Liste filtern (MOMENTAN NOCH ZU IMPLEMENTIEREN). Man kann auf die jeweilige Rede klicken und gelangt dann auf die Rede-Seite. + +### Redeseite + +Folgende Informationen werden angezeigt: +- Informationen zum Redner (Name, Partei, Foto). +- Informationen zur Rede (Datum, Uhrzeit, Agendapunkt). +- Redetext. Vorstellung und Kommentare werden farblich gekennzeichnet. +- NLP Informationen. + +Der NLP-Abschnitt beinhaltet folgende Informationen: +- Topics Information (als Bubble Chart): dabei entspricht die Größe der Bubbles der Häufigkeit/Prävalenz der Topics. Durch einen Mouse-Hover verändert sich die Schriftgröße, damit man auch die Texte der kleinen Bubbles sehen kann. +- POS Information (als Bar Chart) - TODO +- Sentiments Information (als Radar Chart) - TODO +- Named Entities (als Sunburst Chart): der innere Kreis steht für den Typ (LOC, PER, ORG, MISC). Der äußere Kreis steht für die jeweiligen Named Entities. Die Größe der Bögen entsprechen den Häufigkeiten der Entitäten bzw. der Typen. + + +Ein "Zurück"-Button ist auf den Seiten implementiert. Er führt erwartungsgemäß zur vorherigen Seite. + + + + +## Verschiedenes + +### Nachladen neuer Reden (Thread) - TODO + + +### Upload member Images Crawl member images (not required as already in repository) ```shell cd src/main/resources diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/TeXUtil.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/TeXUtil.java index 2f01145..7180e6b 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/TeXUtil.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/TeXUtil.java @@ -53,16 +53,6 @@ public class TeXUtil { tex.append(speech.toTeX()); Map topics = Topic.condenseTopicInformation(getHtmlSpeechByKey(speechId).getNlp().getTopics()); - // loop through topics and Logger.pink them - for (Map.Entry entry : topics.entrySet()) { - Logger.pink(entry + " " + entry.getValue()); - } - - /*Map pos = getPOSInformationCardinalitiesForSpeechById(speechId); - // loop through topics and Logger.pink them - for (Map.Entry entry : pos.entrySet()) { - Logger.pink(entry + " " + entry.getValue()); - }*/ return tex.toString().replace("$$SPEAKERINFO$$", speaker.toTeX()) .replace("$$NLPMETADATA$$", @@ -79,7 +69,20 @@ public class TeXUtil { tex.append(speech.toTeX()); - return tex.toString().replace("$$SPEAKERINFO$$", speaker.toTeX()); + Map topics = null; + + Logger.pink(String.valueOf(speech.getSpeechKey())); + + try { + topics = Topic.condenseTopicInformation(getHtmlSpeechByKey(speech.getSpeechKey()).getNlp().getTopics()); + } catch (Exception e) { + topics = Map.of(); + } + + return tex.toString().replace("$$SPEAKERINFO$$", speaker.toTeX()).replace("$$NLPMETADATA$$", + generateChartView(generateBubbleChartLatex(topics), + generateBarChartLatex(getPOSInformationCardinalitiesForSpeechById(speech.getSpeechKey())), + generateRadarChartLatex(getHtmlSpeechByKey(speech.getSpeechKey()).getNlp().getSentiments()), "")); } public static String getExportedSpeechBase64StringBySpeechId(String speechId) throws IOException, InterruptedException { @@ -163,6 +166,7 @@ public class TeXUtil { // Format tex string to UTF-8 tex = new String(tex.getBytes("UTF-8")); + tex = tex.replaceAll("#", "\\\\#"); // Replace all # with \# tex = tex.replaceAll("[^\\x00-\\x7F]", ""); // Replace all non-ASCII characters // Local datetime stamp @@ -229,6 +233,16 @@ public class TeXUtil { }); } + public static void tryDeleteTeXTempDirContents() { + try { + deleteTeXTempDirContents(); + } catch (IOException e) { + Logger.error("Failed to delete temporary folder."); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + } + public static boolean isTeXSdkInstalled() { try { Process process = Runtime.getRuntime().exec("pdflatex --version"); @@ -299,8 +313,8 @@ public class TeXUtil { StringBuilder xCords = new StringBuilder(); xCords.append("{"); for (Map.Entry entry : barData.entrySet()) { - xCords.append(entry.getKey()).append(", "); - graphData.append("\t(").append(entry.getKey()).append(", ").append(entry.getValue()).append(")\n"); + xCords.append(entry.getKey().replace(",", "\\,")).append(", "); + graphData.append("\t(").append(entry.getKey().replace(",", "\\,")).append(", ").append(entry.getValue()).append(")\n"); } xCords.append("}"); String xCordsString = xCords.toString().replace("$", "\\$"); diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/XMLUtil.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/XMLUtil.java index c9baea5..64e766d 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/XMLUtil.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/XMLUtil.java @@ -124,6 +124,9 @@ public class XMLUtil { Speaker_MongoDB_Impl speaker = getSpeakerById(String.valueOf(speech.getSpeakerId())); + HtmlSpeech htmlSpeech = getHtmlSpeechByKey(speech.getSpeechKey()); + addNlpData(doc, htmlSpeech); + speechElement.appendChild(speaker.toXML(doc)); speechElement.appendChild(speech.toXML(doc)); } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesLatexExportController.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesLatexExportController.java index f3b6205..abf729e 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesLatexExportController.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesLatexExportController.java @@ -48,14 +48,7 @@ public class SpeechesLatexExportController { // Send the PDF as a response ctx.result(stream); - try { - // delete the temporary folder - deleteTeXTempDirContents(); - } catch (IOException e) { - Logger.error("Failed to delete temporary folder."); - Logger.error(e.getMessage()); - Logger.debug(Arrays.toString(e.getStackTrace())); - } + tryDeleteTeXTempDirContents(); } @OpenApi( @@ -91,15 +84,7 @@ public class SpeechesLatexExportController { // Send the PDF as a response ctx.result(stream); - try { - // delete the temporary folder - deleteTeXTempDirContents(); - Logger.debug("Temporary folder deleted."); - } catch (IOException e) { - Logger.error("Failed to delete temporary folder."); - Logger.error(e.getMessage()); - Logger.debug(Arrays.toString(e.getStackTrace())); - } + tryDeleteTeXTempDirContents(); } @OpenApi( @@ -135,15 +120,7 @@ public class SpeechesLatexExportController { // Send the PDF as a response ctx.result(stream); - try { - // delete the temporary folder - deleteTeXTempDirContents(); - Logger.debug("Temporary folder deleted."); - } catch (IOException e) { - Logger.error("Failed to delete temporary folder."); - Logger.error(e.getMessage()); - Logger.debug(Arrays.toString(e.getStackTrace())); - } + tryDeleteTeXTempDirContents(); } @OpenApi( @@ -179,15 +156,7 @@ public class SpeechesLatexExportController { // Send the PDF as a response ctx.result(stream); - try { - // delete the temporary folder - deleteTeXTempDirContents(); - Logger.debug("Temporary folder deleted."); - } catch (IOException e) { - Logger.error("Failed to delete temporary folder."); - Logger.error(e.getMessage()); - Logger.debug(Arrays.toString(e.getStackTrace())); - } + tryDeleteTeXTempDirContents(); } @OpenApi( @@ -230,14 +199,6 @@ public class SpeechesLatexExportController { // Send the PDF as response ctx.result(stream); - try { - // Clean up temporary files - deleteTeXTempDirContents(); - Logger.debug("Temporary folder deleted."); - } catch (IOException e) { - Logger.error("Failed to delete temporary folder."); - Logger.error(e.getMessage()); - Logger.debug(Arrays.toString(e.getStackTrace())); - } + tryDeleteTeXTempDirContents(); } } diff --git a/src/main/resources/static/index.css b/src/main/resources/static/index.css index c77d335..73f5f71 100644 --- a/src/main/resources/static/index.css +++ b/src/main/resources/static/index.css @@ -296,3 +296,9 @@ select { margin-bottom: 20px; border-radius: 10px; } + +.export-button { + flex: 1; + flex-direction: row; + justify-content: center; +} diff --git a/src/main/resources/templates/parlamentarierDetails.ftl b/src/main/resources/templates/parlamentarierDetails.ftl index 9bd8a6a..d256399 100644 --- a/src/main/resources/templates/parlamentarierDetails.ftl +++ b/src/main/resources/templates/parlamentarierDetails.ftl @@ -32,6 +32,18 @@ +
+

Reden Export von allen Reden von ${p.vorname} ${p.nachname}

+
+ +
+ + +
<#else> Keine Reden vorhanden diff --git a/src/main/resources/templates/speech.ftl b/src/main/resources/templates/speech.ftl index 24a20e1..d4a6f31 100644 --- a/src/main/resources/templates/speech.ftl +++ b/src/main/resources/templates/speech.ftl @@ -48,6 +48,20 @@

+

Reden Export von dieser Rede

+
+ +
+ + +
+ +
+
<#list s.content as c> <#if sentimentIcons??>