Jonas Werner 2025-03-24 00:51:55 +01:00
commit 4db18688b5
108 changed files with 6418 additions and 1235 deletions

38
Benutzerhandbuch.txt Normal file
View file

@ -0,0 +1,38 @@
BENUTZERHANDBUCH MULTIMODAL PARLIAMENT EXPLORER
GRUPPE_05_01
Der Multimodal Parliament Explorer ist eine client-server basierte Anwendung zum automatischen abrufen, analysieren, visualisieren und exportieren aller Reden des Bundestags der aktuellen Legislaturperiode.
Mit unserem Programm kann man Reden des Bundestags interaktiv erkunden, Statistiken zu den zugehörigen Reden einsehen und diese selbst weiterverwenden.
1. Systemanforderungen
- Java 17+
- Maven
- Einen modernen Webbrowser (Chrome, Firefox, Edge)
- Einen Internetzugang
2. Hauptfunktionen
Datenimport:
Automatischer Download der XML Protokolle inklusive Videos
NLP-Analyse:
Analyse der Reden durch einen NLP-Docker
Datenexploration:
Filterbar nach Redner, Thema und Datum
Visualisierung der analysierten Daten:
Durch verschiedene Charts werden die Analysedaten anschaulich und verdaubar wiedergegeben
Export:
Alle Reden können als PDF und XML exportiert werden.
3. Support und Weiterführende Dokumentation
Detaillierte Entwicklungs und Nutzungsanleitungen finden Sie im Repository unter /doc oder auf unserer GitLab-Page bei https://ppr.gitlab.texttechnologylab.org/leonkastner/multimodal-parliament-explorer-docu
Bei Fragen oder Fehlern können sie sich gerne bei uns melden.

188
README.md
View file

@ -1,12 +1,196 @@
# Multimodal Parliament Explorer (Gruppe_05_1)
## Umgebung - wichtiger Hinweis!
### Docker und NLP
Damit der Zugriff auf die Docker-Treiber funktioniert, muss man in IntelliJ folgendes tun:
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
### LaTex Export
Damit der LaTeX Export funktioniert, muss der LaTeX Compiler installiert sein.
Unter Ubuntu (Linux) kann das Paket mit dem folgenden Befehl installiert werden:
```shell
sudo apt install texlive-latex-extra
```
Unter Windows ist die Installation über MikTex möglich.
Ist kein Compiler installiert, wird beim Start des Programms eine Warnung ausgegeben:
```shell
-------------------------------------------------o
TeX SDK not installed. PDF export will not work.
-------------------------------------------------o
```
## 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 Planung
- Das Gantt Diagramm
stehen im "doku"-Verzeichnis.
Die Planung beinhaltet:
- Die Liste der Aufgaben, nach Bereichen gruppiert. Die Bereiche sind: Planung, 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:
## Classdiagram puml generator
```shell
python puml_generator.py
```
## Upload member Images
Die aktuelle Version des Klassen-/Package-Diagramms ist bei der Abgabe unter `generated_class_diagram.puml` zu finden. Außerdem gibt es das Diagram auch als LaTe, PDF und Bild (png) Version im `/doc` Ordner zu funden.
## 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
Ressourcenbereitstellung:
- Die benötigten Annotationen werden aus dem Ressourcenordner geladen
- Es wird vorausgesetzt, dass in der "speeches"-Collection eine ZIP-Datei (20.zip) vorhanden ist, die alle erforderlichen Annotationen enthält
- Zusätzlich muss das TypeSystem in Form der Datei TypeSystem.xml vorliegen, um die korrekte Umwandlung der Annotationen in den JCas zu ermöglichen.
Abruf der Reden:
- Die Reden werden anhand der eindeutigen ID (speechKey) aus der Datenbank abgerufen.
- Dabei werden die Reden, die bereits annotiert vorliegen, zur weiteren Verarbeitung selektiert
Annotationen verarbeiten:
- Die geladenen Annotationen werden in einen JCas (Java Common Analysis Structure) umgewandelt.
- Im JCas werden die Annotationen serialisiert, sodass sie in einem einheitlichen Format vorliegen.
- Anschließend werden diese serialisierten Daten in der Datenbank dem entsprechenden Speech-Dokument zugeordnet und gespeichert
Remote-Verarbeitung:
- Reden, die nicht in der 20.zip vorhanden sind also noch nicht lokal annotiert wurden werden anschließend durch den RemoteDriver verarbeitet.
- Der RemoteDriver ruft dabei externe NLP-Komponenten (z.B. spaCy oder Vader) auf, um fehlende Annotationen zu erzeugen.
- Auch die Ergebnisse der Remote-Verarbeitung werden serialisiert und in der Datenbank an das zugehörige Speech-Dokument angehängt.
## 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.
- Video bei Reden der Sitzung 187 Tagesordnungspunkt 4
- 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): Auf der X-Achse werden alle verschiedenen POS-Elemente die in der Rede erfasst wurden abgebildet, auf der Y-Achse die genaue Anzahl dieser Elemente.
- Sentiments Information (als Radar Chart): Dieses Chart besitzt 3 Achsen, eine für den wert des positiven sentiments, eine für das negative sentiment, und die letzte für das neutrale sentiment. Die mitte des Charts steht für den Wert 0, und jede Ecke steht für den Wert 1 des zugehörigen sentiments. Es wird jeder analysierte Satz inklusive die gesamte Rede abgebildet. Ein Polygon steht hierbei für einen bestimmten analysierten Abschnitt der rede. Rechts neben dem Chart ist eine Legende aller Analysierten Objekte, von welchem das erste die Werte der gesamten Rede, und alle danach die einzelnen Satzteile darstellen.
- 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.
### Charts-Seite
Gleiche Struktur wie die Charts für die einzelnen reden, hier nur auf alle in der Datenbank vorhandenen Reden angewendet. Die sammlung der Daten passiert hier dynamisch ab Abruf der Seite.
### Export-Seite
Wie auch auf den einzelnen Seiten, gibt es hier die Option Reden zu exportieren. Es kann zwischen PDF und XML gewählt werden. Die verschiedenen Export-Optionen sind:
- Export einer einzelnen Rede (mit Reden-ID)
- Export aller Reden eines Parlamentariers (mit Parlamentarier-ID)
- Export aller Reden eines NLP-Topics (mit Topic-String)
- Export aller Reden (ohne Parameter)
Achtung: Der Export kann je nach Anzahl der Reden und der gewählten Option einige Zeit in Anspruch nehmen. Meist wird der Nutzer darüber informiert, dass der Prozess länger dauern kann.
## Verschiedenes
### Nachladen neuer Reden (Thread)
Alle 10 Minuten checkt das Programm, ob auf der Bundestags.de Website neue Reden erschienen sind.
Wenn neue Reden erkannt werden, werden diese heruntergeladen, mit dem RemoteDriver verarbeitert und abschließend noch in
die Datenbank hochegladen. Auch wenn einfach so Reden in die Datenbank hinzugefügt werden, werden diese verarbeitet.
### Upload member Images
Crawl member images (not required as already in repository)
```shell
cd src/main/resources

Binary file not shown.

Binary file not shown.

BIN
doc/PPR_Mockup.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

77
doc/UseCasePlantUML Normal file
View file

@ -0,0 +1,77 @@
@startuml
left to right direction
actor Admin
actor Nutzer
actor SystemScheduler as "Zeitgesteuerter Dienst"
rectangle "Multimodal Parliament Explorer" {
package "Datenimport" {
usecase "XML Protokolle herunterladen" as UC1
usecase "XML verarbeiten & speichern" as UC2
usecase "Videos herunterladen & speichern" as UC3
usecase "Fotos der Abgeordneten speichern" as UC4
usecase "NLP-Daten einlesen & verarbeiten" as UC5
}
package "NLP Verarbeitung" {
usecase "NLP Analyse mit DUUI durchführen" as UC6
usecase "NLP-Resultate serialisieren\n& in DB speichern" as UC7
}
package "Export / Serialisierung" {
usecase "Rede als PDF exportieren" as UC8
usecase "Rede als XML exportieren" as UC9
usecase "Rede als XMI exportieren" as UC10
}
package "Visualisierung & UI" {
usecase "Reden durchsuchen" as UC11
usecase "Rede anzeigen (HTML, NLP, Video)" as UC12
usecase "POS / Sentiment / NER / Topics visualisieren" as UC13
}
package "Datenbankoperationen" {
usecase "Reden / Sessions / AgendaItems\nin MongoDB speichern" as UC14
usecase "Metadaten zu Reden abfragen" as UC15
usecase "Video-Referenzen zu Reden abfragen" as UC16
}
package "Systemdienste" {
usecase "Auf neue Protokolle automatisch prüfen und herunterladen" as UC17
usecase "Neue NLP-Reden automatisch analysieren" as UC18
}
' Verbindungen
Admin--> UC1
Admin--> UC2
Admin--> UC3
Admin--> UC4
Admin--> UC5
Admin--> UC6
Admin--> UC7
Admin--> UC8
Admin--> UC9
Admin--> UC10
Nutzer--> UC8
Nutzer--> UC9
Nutzer--> UC10
Nutzer --> UC11
Nutzer --> UC12
Nutzer --> UC13
UC6 --> UC7
UC5 --> UC6
UC2 --> UC14
UC3 --> UC14
UC4 --> UC14
UC14 --> UC15
UC14 --> UC16
UC12 --> UC13
UC12 --> UC16
SystemScheduler --> UC17
SystemScheduler --> UC18
}
@enduml

BIN
doc/class_diagram.pdf Normal file

Binary file not shown.

BIN
doc/class_diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

3564
doc/class_diagram.tex Normal file

File diff suppressed because it is too large Load diff

BIN
doc/usecasediagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@ import org.texttechnologylab.project.gruppe_05_1.nlp.XmiExtractor;
import org.texttechnologylab.project.gruppe_05_1.rest.RESTHandler;
import org.texttechnologylab.project.gruppe_05_1.util.Logger;
import org.texttechnologylab.project.gruppe_05_1.util.PPRUtils;
import org.texttechnologylab.project.gruppe_05_1.util.SpeechVideoUpdater;
import org.texttechnologylab.project.gruppe_05_1.xml.FileObjectFactory;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Session;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.SpeechParser;
@ -43,6 +44,13 @@ public class Main {
public static final String MEMBER_IMAGES_DIR = "src/main/resources/membersOfParliamentImages/";
public static final String TEMP_EXPORT_DIR = "src/main/resources/tempExport/";
/**
* Main Methode zum Start des Multimodalen Parlament Explorers
* Programm-Flag Implementierung und DIR Konstanten von Jonas
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
UPLOAD_MEMBER_PHOTOS = Arrays.asList(args).contains("uploadMemberPhotos");
FORCE_UPLOAD_MEMBERS = Arrays.asList(args).contains("forceUploadMembers");
@ -146,8 +154,9 @@ public class Main {
if (UPLOAD_MEMBER_PHOTOS) {
Logger.pink("Uploading Member Photos to DB...");
mongoDBHandler.uploadMemberPhotos();
mongoDBHandler.uploadMemberPhotosFromResourceFolder();
}
SpeechVideoUpdater.init();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
try {

View file

@ -4,11 +4,10 @@ import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.*;
import com.mongodb.client.model.*;
import org.texttechnologylab.project.gruppe_05_1.domain.html.HtmlSpeech;
import org.texttechnologylab.project.gruppe_05_1.domain.nlp.Topic;
import org.texttechnologylab.project.gruppe_05_1.exceptions.ServerErrorException;
import org.texttechnologylab.project.gruppe_05_1.exceptions.SessionNotFoundException;
import org.bson.Document;
@ -31,6 +30,8 @@ import java.util.concurrent.TimeUnit;
import static com.mongodb.client.model.Filters.eq;
import static org.texttechnologylab.project.gruppe_05_1.Main.MEMBER_IMAGES_DIR;
import static org.texttechnologylab.project.gruppe_05_1.util.PPRUtils.fetchMemberImageBase64FromNameString;
import static org.texttechnologylab.project.gruppe_05_1.util.PPRUtils.getSessionCookies;
public class MongoDBHandler {
@ -57,7 +58,9 @@ public class MongoDBHandler {
private MongoCollection<Document> memberPhotoCollection;
private MongoCollection<Document> historyCollection;
/**
* Implementiert von Valentin
*/
public MongoDBHandler() {
// Set loglevel for slf4j to avoid spam // TODO: Fix this (optional)
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "error");
@ -116,6 +119,7 @@ public class MongoDBHandler {
/**
* Get the MongoDB according to properties.
* If a local server URI is defined, use it. Otherwise, use remote server.
* Implementiert von Valentin
* @return MongoDatabase
*/
static public MongoDatabase getMongoDatabase() {
@ -176,6 +180,7 @@ public class MongoDBHandler {
/**
*
* @return List<String> with the names of all collections
* Implementiert von Valentin
*/
public Set<String> getCollectionNames() {
// return getDatabase().listCollectionNames().into(new ArrayList<>());
@ -186,6 +191,7 @@ public class MongoDBHandler {
*
* @param name Name of collection to check for existance
* @return does the collection exist
* Implementiert von Valentin
*/
public boolean collectionExists(String name) {
return getDatabase().listCollectionNames().into(new ArrayList<>()).contains(name);
@ -194,6 +200,7 @@ public class MongoDBHandler {
/**
* Tries to create a collection. If the collection exists and contains documents, throw an exception
* Implementiert von Valentin
* @param database
* @param collectionName
*/
@ -219,6 +226,7 @@ public class MongoDBHandler {
/**
* Create Collection
* Implementiert von Valentin
* @param database
* @param collectionName
*/
@ -237,6 +245,7 @@ public class MongoDBHandler {
/**
* Creates a collection. If the collection exists already, delete all its document
* Implementiert von Valentin
* @param database
* @param collectionName
*/
@ -252,7 +261,12 @@ public class MongoDBHandler {
}
}
/**
* Implementiert von Valentin
* @param collection
* @param indexName
* @param isAscending
*/
static public void createIndexForCollection(MongoCollection<Document> collection, String indexName, boolean isAscending) {
// MongoDB creates automatically an index on "_id"
if (indexName.equals(DEFAULT_ID_FIELD_NAME)) {
@ -266,6 +280,12 @@ public class MongoDBHandler {
}
}
/**
* Implementiert von Valentin
* @param collection
* @param indexNames
* @param isAscending
*/
static public void createIndexForCollection(MongoCollection<Document> collection, List<String> indexNames, boolean isAscending) {
// MongoDB creates automatically an index on "_id"
if (indexNames.contains(DEFAULT_ID_FIELD_NAME)) {
@ -280,6 +300,9 @@ public class MongoDBHandler {
}
}
/**
* Implementiert von Valentin
*/
public void createIndicesForSpeakerCollection() {
if (speakerCollection.listIndexes().into(new ArrayList<>()).size() == 1) {
MongoDBHandler.createIndexForCollection(speakerCollection,"name", true);
@ -288,10 +311,15 @@ public class MongoDBHandler {
}
}
/**
* Implementiert von Valentin
*/
public void createIndicesForSpeechCollection() {
if (speechesCollection.listIndexes().into(new ArrayList<>()).size() == 1) {
MongoDBHandler.createIndexForCollection(speechesCollection, "speakerId", true);
MongoDBHandler.createIndexForCollection(speechesCollection, "speechKey", true);
MongoDBHandler.createIndexForCollection(speechesCollection, "dateTime", false);
}
}
@ -301,6 +329,7 @@ public class MongoDBHandler {
/**
* Does a document with a given ID (for the "_id"-field) exists in a given collection?
* Implementiert von Valentin
* @param collection
* @param id
* @return
@ -314,6 +343,7 @@ public class MongoDBHandler {
/**
* Find a document with a given ID (for the "_id"-field) in a given collection
* Implementiert von Valentin
* @param collection
* @param id
* @return the document (null if not found)
@ -325,7 +355,7 @@ public class MongoDBHandler {
}
/**
*
*Implementiert von Valentin
* @param collection
* @return count of documents in the collection
*/
@ -339,7 +369,7 @@ public class MongoDBHandler {
/**
*
*Implementiert von Valentin
* @param database
* @param collectionName
* @return count of documents in the collection
@ -353,7 +383,7 @@ public class MongoDBHandler {
}
/**
*
*Implementiert von Valentin
* @param database
* @param collectionName
*/
@ -372,6 +402,7 @@ public class MongoDBHandler {
*/
/**
* Implementiert von Valentin
* Creates a BSON document containing only simple fields according to fields given in a map
* @param attributes
* @return
@ -390,6 +421,7 @@ public class MongoDBHandler {
}
/**
* Implementiert von Valentin
* Creates a BSON document containing simple fields (attributes) as well as other (possibly nested) objects
* @param attributes the simple fields
* @param fields the (possibly nested) objects
@ -418,6 +450,7 @@ public class MongoDBHandler {
}
/**
* Implementiert von Valentin
* Liefert ein Feldwert aks Double, auch wenn er in der Datenbank als Integer oder String steht
* @param doc Mongo-Dokument
* @param fieldName Feldname
@ -439,7 +472,7 @@ public class MongoDBHandler {
/**
*
*Implementiert von Valentin
* @param collection
* @param doc
* @return
@ -449,7 +482,7 @@ public class MongoDBHandler {
}
/**
*
*Implementiert von Valentin
* @param collection
* @param docs
* @return
@ -459,7 +492,7 @@ public class MongoDBHandler {
}
/**
*
*Implementiert von Valentin
* @param collection
* @param fieldName
* @param fieldValue
@ -471,6 +504,7 @@ public class MongoDBHandler {
/**
* Implementiert von Valentin
* Searches a document and performs an update on it
* The document to update must be matched by name and value of a certain field
* @param collection
@ -493,7 +527,7 @@ public class MongoDBHandler {
/**
*
*Implementiert von Valentin
* @param collection
* @param searchCriteriaName search criteria: name of the field
* @param searchCriteriaValue search criteria: value of the field
@ -504,11 +538,12 @@ public class MongoDBHandler {
collection.deleteOne(deleteQuery);
}
/*
* Justus Jonas operations
* =======================
*/
/**
* Fügt eine Session in die Datenbank ein.
*
* @param session Das Session-Objekt, das eingefügt werden soll.
*/
public void insertSession(Session session) {
Document sessionDocument = new Document("sessionId", session.getId())
.append("dateTime", session.getDateTime())
@ -518,6 +553,15 @@ public class MongoDBHandler {
sessionsCollection.insertOne(sessionDocument);
}
/**
* Erstellt eine neue Session anhand der übergebenen Parameter, generiert eine eindeutige sessionId,
* fügt sie in die Datenbank ein und gibt die erstellte Session zurück.
*
* @param dateTime Startzeit der Session.
* @param endTime Endzeit der Session.
* @param legislativePeriod Die Legislaturperiode.
* @return Die neu erstellte Session.
*/
public Session insertSession(String dateTime, String endTime, String legislativePeriod) {
// get a new random sessionId that is not already in use
int sessionId = 0;
@ -535,13 +579,22 @@ public class MongoDBHandler {
return session;
}
/**
* Fügt eine Liste von Sessions in die Datenbank ein.
*
* @param sessions Liste der Sessions, die eingefügt werden sollen.
*/
public void insertSessions(List<Session> sessions) {
for (Session session : sessions) {
insertSession(session);
}
}
/**
* Fügt eine Liste von AgendaItems in die Datenbank ein.
*
* @param agendaItems Liste der AgendaItems, die eingefügt werden sollen.
*/
public void insertAgendaItems(List<AgendaItem> agendaItems) {
List<Document> agendaItemDocuments = new ArrayList<>();
for (AgendaItem agendaItem : agendaItems) {
@ -555,7 +608,15 @@ public class MongoDBHandler {
agendaItemsCollection.insertMany(agendaItemDocuments);
}
/**
* Erstellt ein neues AgendaItem für eine bestimmte Session und einen Titel.
*
* @param sessionId Die Session-ID, zu der das AgendaItem gehört.
* @param title Der Titel des AgendaItems.
* @return Das neu erstellte AgendaItem.
* @throws SessionNotFoundException Falls keine Session mit der angegebenen sessionId existiert.
* @throws ServerErrorException Falls ein Serverfehler auftritt.
*/
public AgendaItem insertAgendaItem(int sessionId, String title) throws SessionNotFoundException, ServerErrorException {
// check if session exists
List<Session> sessions = retrieveAllSessions(Filters.eq("sessionId", sessionId));
@ -579,6 +640,11 @@ public class MongoDBHandler {
return agendaItem;
}
/**
* Fügt eine Liste von Speech-Objekten in die Datenbank ein.
*
* @param speeches Liste der Speeches, die eingefügt werden sollen.
*/
public void insertSpeeches(List<Speech> speeches) {
// Convert each Speech to a Document
List<Document> speechDocuments = new ArrayList<>();
@ -629,7 +695,11 @@ public class MongoDBHandler {
speechesCollection.insertMany(speechDocuments);
}
/**
* Ruft alle Speech-Dokumente ohne Filter ab und wandelt sie in eine Liste von Speech-Objekten um.
*
* @return Eine Liste aller Speeches.
*/
public List<Speech> retrieveAllSpeeches() {
List<Document> speeches = speechesCollection.find().into(new ArrayList<>());
List<Speech> result = new ArrayList<>();
@ -640,6 +710,13 @@ public class MongoDBHandler {
return result;
}
/**
* Ruft alle Speech-Dokumente ab, die dem übergebenen Filter entsprechen, und wandelt sie in eine Liste von Speech-Objekten um.
*
* @param filter Der Filter als Bson-Objekt.
* @return Eine Liste der Speeches, die dem Filter entsprechen.
*/
public List<Speech> retrieveAllSpeeches(Bson filter) {
List<Document> speeches = speechesCollection.find(filter).into(new ArrayList<>());
List<Speech> result = new ArrayList<>();
@ -650,6 +727,11 @@ public class MongoDBHandler {
return result;
}
/**
* Ruft alle Session-Dokumente ohne Filter ab und wandelt sie in eine Liste von Session-Objekten um.
*
* @return Eine Liste aller Sessions.
*/
public List<Session> retrieveAllSessions() {
List<Document> sessions = sessionsCollection.find().into(new ArrayList<>());
List<Session> result = new ArrayList<>();
@ -660,6 +742,12 @@ public class MongoDBHandler {
return result;
}
/**
* Ruft alle Session-Dokumente ab, die dem übergebenen Filter entsprechen, und wandelt sie in eine Liste von Session-Objekten um.
*
* @param filter Der Filter als Bson-Objekt.
* @return Eine Liste der Sessions, die dem Filter entsprechen.
*/
public List<Session> retrieveAllSessions(Bson filter) {
List<Document> speeches = sessionsCollection.find(filter).into(new ArrayList<>());
List<Session> result = new ArrayList<>();
@ -670,6 +758,11 @@ public class MongoDBHandler {
return result;
}
/**
* Ruft alle AgendaItem-Dokumente ohne Filter ab und wandelt sie in eine Liste von AgendaItem-Objekten um.
*
* @return Eine Liste aller AgendaItems.
*/
public List<AgendaItem> retrieveAllAgendaItems() {
List<Document> agendaItems = agendaItemsCollection.find().into(new ArrayList<>());
List<AgendaItem> result = new ArrayList<>();
@ -680,6 +773,12 @@ public class MongoDBHandler {
return result;
}
/**
* Ruft alle AgendaItem-Dokumente ab, die dem übergebenen Filter entsprechen, und wandelt sie in eine Liste von AgendaItem-Objekten um.
*
* @param filter Der Filter als Bson-Objekt.
* @return Eine Liste der AgendaItems, die dem Filter entsprechen.
*/
public List<AgendaItem> retrieveAllAgendaItems(Bson filter) {
List<Document> speeches = agendaItemsCollection.find(filter).into(new ArrayList<>());
List<AgendaItem> result = new ArrayList<>();
@ -690,6 +789,13 @@ public class MongoDBHandler {
return result;
}
/**
* Aktualisiert das Speech-Dokument, das durch den speechKey identifiziert wird,
* und setzt das Feld "xmiData" auf den übergebenen xmiContent.
*
* @param speechKey Der Schlüssel der Rede.
* @param xmiContent Der XMI-Inhalt als String.
*/
public void updateXmiData(String speechKey, String xmiContent) {
speechesCollection.updateOne(
Filters.eq("speechKey", speechKey),
@ -697,6 +803,9 @@ public class MongoDBHandler {
);
}
/**
* Löscht alle Dokumente, die mit den Reden, Sessions, AgendaItems (und History) zusammenhängen.
*/
public void deleteSpeechRelatedDocuments() {
speechesCollection.deleteMany(new Document());
sessionsCollection.deleteMany(new Document());
@ -704,6 +813,11 @@ public class MongoDBHandler {
//historyCollection.deleteMany(new Document());
}
/**
* Führt einen Bulk-Write für NLP-bezogene Updates in der Speech-Collection aus.
*
* @param bulkOperations Eine Liste von Bulk-Update-Operationen.
*/
public void bulkWriteNlpData(List<WriteModel<Document>> bulkOperations) {
if (!bulkOperations.isEmpty()) {
BulkWriteOptions options = new BulkWriteOptions().ordered(false);
@ -721,6 +835,11 @@ public class MongoDBHandler {
}
}
/**
* Zählt die Anzahl der Speech-Dokumente, die ein Feld "analysisResults" besitzen.
*
* @return Die Anzahl der Speech-Dokumente mit einem "analysisResults"-Feld.
*/
public long checkAnalysisResultsField() {
return speechesCollection.countDocuments(Filters.exists("analysisResults"));
}
@ -737,6 +856,12 @@ public class MongoDBHandler {
return loadMemberImageFromFileByName(firstName, name);
}
/**
* Lädt das Bild eines Mitglieds aus dem Ordner resources/membersOfParliamentImages anhand des Namens.
* @param firstName Der Vorname des Mitglieds.
* @return das Bild des Mitglieds als base64-String.
* Implementiert von Jonas
**/
public String loadMemberImageFromFileByName(String firstName, String name) {
// get the member photo from the resources/membersOfParliamentImages folder
File photo = new File(MEMBER_IMAGES_DIR + name + "_" + firstName + ".jpg");
@ -754,6 +879,12 @@ public class MongoDBHandler {
return image_data;
}
/**
* Lädt das Bild eines Mitglieds aus dem Ordner resources/membersOfParliamentImages anhand des Namens.
* @param memberId Der Vorname des Mitglieds.
* @param base64String Der base64-String des Bildes.
* Implementiert von Jonas
**/
public void uploadMemberPhoto(String memberId, String base64String) {
if (memberPhotoCollection.find(eq("memberId", memberId)).first() != null) {
Logger.warn("Member photo for " + memberId + " already exists in the database. Overwriting...");
@ -764,7 +895,41 @@ public class MongoDBHandler {
memberPhotoCollection.insertOne(photoDocument);
}
/**
* Lädt die Bilder aller Mitglieder in die Datenbank.
* Implementiert von Jonas
**/
public void uploadMemberPhotos() {
// get a list of the string of first and last name of all members from the DB
// only fetch the first and lastname
List<Document> speakers = speakerCollection.find().projection(Projections.include("_id", "name", "firstName")).into(new ArrayList<>());
try {
Logger.pink(getSessionCookies());
} catch (IOException e) {
Logger.error("Failed to get session cookies: " + e.getMessage());
}
for (Document speaker : speakers) {
String memberId = speaker.getString("_id");
String name = speaker.getString("name");
String firstName = speaker.getString("firstName");
try {
String base64String = fetchMemberImageBase64FromNameString(firstName + " " + name);
uploadMemberPhoto(memberId, base64String);
Logger.debug("Uploaded member photo for " + firstName + " " + name);
} catch (IOException e) {
Logger.error("Failed to fetch member image for " + firstName + " " + name + ": " + e.getMessage());
}
}
}
/**
* Lädt die Bilder aller Mitglieder aus dem Ordner resources/membersOfParliamentImages in die Datenbank.
* Implementiert von Jonas
**/
public void uploadMemberPhotosFromResourceFolder() {
Logger.info("Found " + PPRUtils.listFilesInDirectory(MEMBER_IMAGES_DIR).size() + " member photos to upload.");
// loop over file names in the directory
// for each file name, extract the name of the member
@ -781,6 +946,12 @@ public class MongoDBHandler {
}
}
/**
* Überprüft, ob eine Session mit der angegebenen Session-Nummer in der sessionsCollection existiert.
*
* @param sessionNumber Die Session-Nummer als String.
* @return true, wenn mindestens ein Dokument mit der angegebenen sessionId existiert, andernfalls false.
*/
public boolean sessionExists(String sessionNumber) {
Document filter = new Document("sessionId", Integer.valueOf(sessionNumber));
long count = sessionsCollection.countDocuments(filter);
@ -795,8 +966,20 @@ public class MongoDBHandler {
return photoDocument.getString("base64");
}
/**
* Schließt die MongoDB-Verbindung.
*/
public void close() {
mongoClient.close();
}
/**
* Gibt ein einzelnes Speech-Dokument zurück, das dem angegebenen Filter entspricht.
*
* @param filter Der Filter als Document.
* @return Das erste Speech-Dokument, das dem Filter entspricht, oder null, wenn keines gefunden wird.
*/
public Document getSpeech(Document filter) {
return speechesCollection.find(filter).first();
}
}

View file

@ -13,7 +13,7 @@ public class MongoObjectFactory {
private static MongoObjectFactory factory = null;
/**
*
*Gesamte File implementiert von Valentin
* @return MongoObjectFactory
*/
public static MongoObjectFactory getFactory() {

View file

@ -4,6 +4,10 @@ import org.bson.Document;
import java.util.List;
/**
* Implementiert von Valentin
* @param <T>
*/
public interface MongoOperations<T> {
MongoObjectFactory factory = MongoObjectFactory.getFactory();
public Document createEntity(T entity);

View file

@ -57,11 +57,18 @@ public class MongoPprUtils {
private static MongoCollection<Document> commentCollection = null;
private static MongoCollection<Document> metadataCollection = null;
/**
* Implementiert von Valentin
* @return
*/
public static MongoCollection<Document> getSpeakerCollection() {
if (speakerCollection == null) speakerCollection = MongoDBHandler.getMongoDatabase().getCollection(SPEAKER_COLLECTION_NAME);
return speakerCollection;
}
/**
* Implementiert von Valentin
* @return
*/
public static MongoCollection<Document> getSpeechCollection() {
if (speechCollection == null) speechCollection = MongoDBHandler.getMongoDatabase().getCollection(SPEECH_COLLECTION_NAME);
return speechCollection;
@ -82,12 +89,17 @@ public class MongoPprUtils {
return picturesCollection;
}
/**
* Implementiert von Valentin
* @return
*/
public static MongoCollection<Document> getMetadataCollection() {
if (metadataCollection == null) metadataCollection = MongoDBHandler.getMongoDatabase().getCollection(METADATA_COLLECTION_NAME);
return metadataCollection;
}
/**
* Implementiert von Valentin
* Create the Speaker Collection and useful indices for it
*/
public static void createIndexForSpeakerCollection() {
@ -98,6 +110,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Create the Speech Collection and useful indices for it
*/
public static void createIndexForSpeechCollection() {
@ -107,6 +120,7 @@ public class MongoPprUtils {
/**
* Implementiert von Valentin
* Truncate the Speaker Collection.
* Note that it is quicker (and saves space) to drop and re-create rather than removing all documents using "remove({})"
*/
@ -123,6 +137,7 @@ public class MongoPprUtils {
/**
* Implementiert von Valentin
* Holt alle Parlamentarier, die einen Suchkriterium erfüllen.
* Das Suchkriterium wird auf allen Feldern angewandt: Vorname, Nachname, Partei.
* Ist das Suchkriterium leer, werden alle Parlamentarier zurückgeliefert
@ -161,6 +176,14 @@ public class MongoPprUtils {
return plist;
}
/**
* Fetched alle Parlamentarier, die einen Suchkriterium erfüllen.
* Das Suchkriterium wird auf allen Feldern angewandt: Vorname, Nachname, Partei.
* Ist das Suchkriterium leer, werden alle Parlamentarier zurückgeliefert
* Implementiert von Jonas
* @param ctx Session Context
* @return List<Parlamentarier>
*/
public static List<Parlamentarier> getFilteredMembers(Context ctx) {
// Get optional filter arguments
String memberIdParam = ctx.queryParam("memberId");
@ -229,6 +252,14 @@ public class MongoPprUtils {
}
}
/**
* Fetched alle Parlamentarier aus der Datenbank, die einen Filter erfüllen
* es wird nur die gegebene Projektion zurückgegeben
* Implementiert von Jonas
* @param filter Filter, der auf die Datenbank angewendet wird
* @param projection Projektion, die auf die Datenbank angewendet wird
* @return Liste von Parlamentariern
**/
public static List<Parlamentarier> retrieveAllMembersOfParliament(Bson filter, Bson projection) throws IOException {
List<Document> speeches = getSpeakerCollection().find(filter).projection(projection).into(new ArrayList<>());
List<Parlamentarier> result = new ArrayList<>();
@ -246,6 +277,103 @@ public class MongoPprUtils {
/**
* Implementiert von Valentin
* Hole alle Reden gefiltert nach den Form-Parameter
* @param ctx Session Context, aus dem man alle Parameter abfragen kann
* @return
*/
public static List<SpeechOverview> getFilteredSpeechesOverview(Context ctx) {
String name = ctx.queryParam("name");
String party = (!Objects.equals(ctx.queryParam("party"), "")) ? ctx.queryParam("party") : null;
String topic = (!Objects.equals(ctx.queryParam("topic"), "")) ? ctx.queryParam("topic") : null;
List<Bson> filters = new ArrayList<>();
if (name != null) filters.add(Filters.regex("speakerName", ".*" + name + ".*", "i"));
if (party != null) filters.add(Filters.regex("fraction", ".*" + party + ".*", "i"));
Bson filter;
if (filters.isEmpty()) {
filter = Filters.empty();
} else {
filter = Filters.and(filters);
}
List<SpeechOverview> result = new ArrayList<>();
MongoCollection<Document> collection = getSpeechCollection();
Document projection = new Document("speechKey", 1)
.append("speakerId", 1)
.append("dateTimeString", 1)
.append("speakerName", 1)
.append("fraction", 1)
.append("agendaTitel", 1);
List<Document> docs = collection.find(filter)
.projection(projection)
.sort(Sorts.descending("dateTime"))
.into(new ArrayList<>());
for (Document doc : docs) {
result.add(new SpeechOverview(
doc.getString("speechKey"),
doc.getInteger("speakerId"),
doc.getString("dateTimeString"),
doc.getString("speakerName"),
doc.getString("fraction"),
doc.getString("agendaTitel")
));
}
return result;
}
/**
* Implementiert von Valentin
* Holt alle Reden eines Parlamentariers
* @param speakerId
* @return
*/
public static List<SpeechOverview> getSpeechesOverviewForSpeaker(Integer speakerId) {
List<SpeechOverview> result = new ArrayList<>();
MongoCollection<Document> collection = getSpeechCollection();
Document projection = new Document("speechKey", 1)
.append("speakerId", 1)
.append("dateTimeString", 1)
.append("speakerName", 1)
.append("fraction", 1)
.append("agendaTitel", 1);
Bson filter = Filters.eq("speakerId", speakerId);
List<Document> docs = collection.find(filter)
.projection(projection)
.sort(Sorts.descending("dateTime"))
.into(new ArrayList<>());
for (Document doc : docs) {
result.add(new SpeechOverview(
doc.getString("speechKey"),
doc.getInteger("speakerId"),
doc.getString("dateTimeString"),
doc.getString("speakerName"),
doc.getString("fraction"),
doc.getString("agendaTitel")
));
}
return result;
}
/**
* Implementiert von Valentin
* Liest einen Parlamentarier von der MongoDB
* @param doc - MongoDB Dokument eines Parlamentariers
* @return Parlamentarier
@ -298,8 +426,9 @@ public class MongoPprUtils {
/**
* Holt einen Speaker aus der Datenbank
* @param id
* @return
* Implementiert von Jonas
* @param id ID des Parlamentariers
* @return Speaker
*/
public static Speaker_MongoDB_Impl getSpeakerById(String id) {
Logger.debug("ID: " + id);
@ -312,6 +441,7 @@ public class MongoPprUtils {
/**
* Implementiert von Valentin
* Holt die Details eines Parlamentariers
* @param id Parlamentarier-ID (Integer)
* @return ParlamentarierDetails
@ -419,9 +549,9 @@ public class MongoPprUtils {
// Speech
// TODO: kopiere die Speech-Sachen von Übung 4 hierher!
/**
* Implementiert von Valentin
* Aufzählen, wie viele Reden eines bestimmten Redners gespeichert sind
* @param speakerId
* @return Anzahl Reden
@ -432,6 +562,7 @@ public class MongoPprUtils {
/**
* Implementiert von Valentin
* Liefert alle Reden eines Redners zurück
* @param speakerId
* @return Alle Reden eines Redners
@ -450,6 +581,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Liefert alle Reden zurück
* Die Auswahl kann durch einen (textuellen) Filter eingeschränkt werden
* @param filter
@ -486,6 +618,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Liefert Metadaten (aber keine Inhalte!) für alle Reden eines Redners zurück.
* Als Metadaten zählen das Datum, Agenda-ID etc.
* @param speakerId
@ -529,6 +662,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Liefert Metadaten (aber keine Inhalte!) für alle Reden zurück.
* Die Auswahl kann durch einen (textuellen) Filter eingeschränkt werden
* Als Metadaten zählen das Datum, Agenda-ID etc.
@ -591,6 +725,7 @@ public class MongoPprUtils {
/**
* Implementiert von Valentin
* Holt die Redeinformationen aus der Datenbank, die wichtig sind, um eine Liste der Reden in HTML darzustellen
* @return
*/
@ -626,8 +761,48 @@ public class MongoPprUtils {
return result;
}
/**
* Implementiert von Valentin
* Holt die Redeinformationen aus der Datenbank, die wichtig sind, um eine Liste der Reden eines Parlamentariers in HTML darzustellen
* @param speakerId
* @return
*/
public static List<SpeechOverview> getSpeechOverviewBySpeaker(Integer speakerId) {
List<SpeechOverview> result = new ArrayList<>();
MongoCollection<Document> collection = getSpeechCollection();
Document projection = new Document("speechKey", 1)
.append("speakerId", 1)
.append("dateTimeString", 1)
.append("speakerName", 1)
.append("fraction", 1)
.append("agendaTitel", 1);
Bson filter = Filters.eq("speakerId", speakerId);
List<Document> docs = collection.find(filter)
.projection(projection)
.sort(Sorts.descending("dateTime"))
.into(new ArrayList<>());
for (Document doc : docs) {
result.add(new SpeechOverview(
doc.getString("speechKey"),
doc.getInteger("speakerId"),
doc.getString("dateTimeString"),
doc.getString("speakerName"),
doc.getString("fraction"),
doc.getString("agendaTitel")
));
}
return result;
}
/**
* Implementiert von Valentin
* Füge Rede-Metadaten (welche in der Session-Collection stehen) der Rede hinzu.
* Achtung: Redezeit ist in der Datenbank in unterschiedlichen Formaten vorhanden.
* @param sessionId
@ -653,6 +828,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Liefert das Datum und die Uhrzeit einer Sitzung zurück
* @param sessionId
* @return
@ -668,6 +844,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Liefert den Agenda-Titel zurück
* @param sessionId
* @return
@ -684,6 +861,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Liefert die Rede-Informationen für die Anzeige einer Rede:
* - die Rede-ID
* - Name und Fraktion des Redners
@ -702,6 +880,12 @@ public class MongoPprUtils {
return new HtmlSpeech(speechDoc);
}
/**
* Implementiert von Jonas
* Liefert die Rede-Informationen für die Anzeige einer Rede
* @param key: Rede ID
* @return Speech
*/
public static Speech getSpeechByKey(String key) {
Document filter = new Document("speechKey", key);
Document speechDoc = getSpeechCollection().find(filter).first();
@ -734,6 +918,7 @@ public class MongoPprUtils {
/**
* Implementiert von Valentin
* Aktualisiert (or erzeugt, falls nicht bereits vorhanden) diverse Metadaten:
* - Die Liste der Parteien/Fraktionen, wie sie im Speaker-Collection stehen
* - Die Liste der Parteien/Fraktionen, wie sie im Speech-Collection stehen (diese Listen sind recht unterschiedlich)
@ -786,6 +971,11 @@ public class MongoPprUtils {
Logger.info("Updating Metadata Collection: end");
}
/**
* Implementiert von Valentin
* @param speakerId
* @return
*/
public static List<Speech> getSpeechesBySpeakerId(String speakerId) {
List<Speech> speechIds = new ArrayList<>();
Document filter = new Document("speakerId", Integer.parseInt(speakerId));
@ -796,6 +986,10 @@ public class MongoPprUtils {
return speechIds;
}
/**
* Implementiert von Valentin
* @return
*/
public static List<Speech> getAllSpeeches() {
List<Speech> speechIds = new ArrayList<>();
Document filter = new Document();
@ -806,6 +1000,12 @@ public class MongoPprUtils {
return speechIds;
}
/**
* Implementiert von Jonas
* Liefert alle Reden zurück, die ein bestimmtes Topic haben
* @param topic Topic der Reden
* @return Liste von Reden
*/
public static List<Speech> getAllSpeechesWithTopic(String topic) {
List<Speech> speechIds = new ArrayList<>();
Document filter = new Document("analysisResults.topics.topic", topic);
@ -815,6 +1015,13 @@ public class MongoPprUtils {
}
return speechIds;
}
/**
* Implementiert von Jonas
* Liefert eine Map der POS Einträge und deren Häufigkeit für eine Rede
* @param speechId ID der Rede
* @return Map<String, Integer> POS Einträge und deren Häufigkeit
*/
public static Map<String, Integer> getPOSInformationCardinalitiesForSpeechById(String speechId) {
List<Token> tokens = getHtmlSpeechByKey(speechId).getNlp().getTokens();
Map<String, Integer> posCounts = Token.countPOS(tokens);
@ -826,6 +1033,12 @@ public class MongoPprUtils {
return posCounts;
}
/**
* Implementiert von Jonas
* Liefert eine Map der Named Entities und deren Häufigkeit für eine Rede
* @param speechId ID der Rede
* @return Map<String, Integer> Named Entities und deren Häufigkeit
*/
public static Map<String, Integer> getNamedEntitiesInformationCardinalitiesForSpeechById(String speechId) {
Map<String, Map<String, Integer>> namedEntitiesMapOfMaps = new HashMap<>();
@ -856,6 +1069,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Liefert die Liste aller Parteien/Fraktionen, welche in der Liste der Parlamentarier stehen, zurück.
* Diese Liste dient zur Filterung der Parlamentarier auf der entsprechenden Seite.
* @return List<String> Liste aller Parteien/Fraktionen, welche in der Liste der Parlamentarier stehen
@ -877,6 +1091,7 @@ public class MongoPprUtils {
);
/**
* Implementiert von Valentin
* Liefert die Liste aller Parteien/Fraktionen, welche in der Liste der Reden stehen, zurück.
* Diese Liste dient zur Filterung der Reden auf der entsprechenden Seite.
* Da die Datenqualität dieses Feldes extrem schlecht ist, muss man hier etwas tricksen:
@ -893,6 +1108,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Reichere die Rede-Dokumente um Informationen an:
* - Datum und Uhrzeit der Rede (als DateTime und textuell): dateTimeString , dateTime
* - Agenda-Titel: agendaTitel
@ -958,6 +1174,7 @@ public class MongoPprUtils {
}
/**
* Implementiert von Valentin
* Liefert die Liste aller Topics, zurück.
* Diese Liste dient zur Filterung der Reden auf der entsprechenden Seite.
* @return Liste aller Topics

View file

@ -4,7 +4,9 @@ import org.bson.Document;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.AgendaItem_File_Impl;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.AgendaItem;
/**
* Datei implementiert von Henry
*/
public class AgendaItem_MongoDB_Impl extends AgendaItem_File_Impl implements AgendaItem {
public AgendaItem_MongoDB_Impl(Document mongoDocument) {
super(

View file

@ -11,6 +11,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Datei implementiert von Valentin
*/
public class BiographicalInformation_MongoDB_Impl extends BiografischeAngaben implements MongoOperations<BiografischeAngaben> {
@Override
public Document createEntity(BiografischeAngaben entity) {

View file

@ -10,6 +10,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Datei implementiert von Valentin
*/
public class Institution_MongoDB_Impl extends Institution implements MongoOperations<Institution> {
@Override
public Document createEntity(Institution entity) {

View file

@ -11,6 +11,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Datei implementiert von Valentin
*/
public class LegislativePeriod_MongoDB_Impl extends Wahlperiode implements MongoOperations<Wahlperiode> {
@Override
public Document createEntity(Wahlperiode entity) {

View file

@ -10,6 +10,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Datei implementiert von Valentin
*/
public class MdbName_MongoDB_Impl extends MdbName implements MongoOperations<MdbName> {
@Override
public Document createEntity(MdbName entity) {

View file

@ -11,6 +11,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Datei implementiert von Valentin
*/
public class Mdb_MongoDB_Impl extends Mdb implements MongoOperations<Mdb> {
@Override
public Document createEntity(Mdb entity) {

View file

@ -10,6 +10,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Datei implementiert von Valentin
*/
public class Membership_MongoDB_Impl extends Membership implements MongoOperations<Membership> {
@Override
public Document createEntity(Membership entity) {

View file

@ -4,7 +4,9 @@ import org.bson.Document;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Session_File_Impl;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Session;
/**
* Datei implementiert von Henry
*/
public class Session_MongoDB_Impl extends Session_File_Impl implements Session {
public Session_MongoDB_Impl(Document mongoDocument) {

View file

@ -17,6 +17,11 @@ import java.util.*;
import static org.texttechnologylab.project.gruppe_05_1.Main.TEMP_EXPORT_DIR;
import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.getMemberPhoto;
/**
* Datei implementiert von Valentin
* Datei modifiziert von Jonas
* toTeX() und toXML() hinzugefügt von Jonas
*/
public class Speaker_MongoDB_Impl extends Speaker implements MongoOperations<Speaker> {
public Speaker_MongoDB_Impl createSpeakerMongoDBImpl(Document mongoDocument) {
this.setId(mongoDocument.getString("_id"));

View file

@ -19,6 +19,11 @@ import java.util.List;
import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.getAgendaTitle;
import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.getSessionDateTime;
/**
* Datei implementiert von Henry
* Modifiziert von Jonas
* toTeX und toXML von Jonas
*/
public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech {
public Speech_MongoDB_Impl(Document mongoDocument, boolean includeContent) {
super(
@ -50,29 +55,41 @@ public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech {
}
}
/**
* Gibt den vollständigen Text der Rede zurück, indem alle Zeilen- und Kommentarinhalte
* zusammengesetzt werden. Inhalte vom Typ "speaker" werden dabei ignoriert.
*
* @return Der vollständige Redetext als String
*/
public String getFullText() {
StringBuilder fullText = new StringBuilder();
// Iteriere über alle Inhalte, die bereits in der Rede gespeichert wurden.
for (Object content : this.getSpeechContents()) {
if (content instanceof Line_MongoDB_Impl) {
// Wir gehen davon aus, dass Line_MongoDB_Impl eine Methode getContent() hat, die den Text zurückgibt.
String lineText = ((Line_MongoDB_Impl) content).getContent();
if (lineText != null && !lineText.isEmpty()) {
fullText.append(lineText).append("\n");
}
} else if (content instanceof Comment_MongoDB_Impl) {
// Wir gehen davon aus, dass Comment_MongoDB_Impl eine Methode getComment() hat, die den Kommentartext liefert.
String commentText = ((Comment_MongoDB_Impl) content).getComment();
if (commentText != null && !commentText.isEmpty()) {
fullText.append(commentText).append("\n");
}
}
// Inhalte vom Typ "speaker" werden ignoriert.
}
return fullText.toString().trim();
}
//TODO not going fuckin insane
/**
* Wandelt die aktuelle Rede in ein JCas-Objekt um, um sie z.B. für NLP-Verarbeitung
* weiterzuverwenden.
*
* @return JCas mit dem Redetext und der speechKey als Metadaten
* @throws UIMAException falls ein Fehler bei der Erstellung des JCas auftritt
*/
public JCas toCas() throws UIMAException {
JCas jCas = JCasFactory.createJCas();
jCas.setDocumentText(this.getFullText());
@ -82,6 +99,11 @@ public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech {
return jCas;
}
/**
* Exportiert die Rede im LaTeX-Format inkl. Platzhalter für Sprecher- und NLP-Informationen.
*
* @return LaTeX-Darstellung der Rede
*/
public String toTeX() {
StringBuilder tex = new StringBuilder();
@ -117,6 +139,12 @@ public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech {
return tex.toString();
}
/**
* Wandelt die Rede in ein XML-Element um, das in ein vollständiges XML-Dokument eingefügt werden kann.
*
* @param doc das übergeordnete XML-Dokument
* @return das XML-Element, das die Rede darstellt
*/
public Element toXML(org.w3c.dom.Document doc) {
Element speech = doc.createElement("speech");
speech.setAttribute("sessionId", String.valueOf(this.getSessionId()));

View file

@ -5,7 +5,9 @@ import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Comment_File
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Comment;
import org.w3c.dom.Element;
/*
* Klassen-Implementieren von Jonas
*/
public class Comment_MongoDB_Impl extends Comment_File_Impl implements Comment {
public Comment_MongoDB_Impl(Document mongoDocument) {

View file

@ -5,7 +5,9 @@ import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Line_File_Im
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Line;
import org.w3c.dom.Element;
/*
* Klassen-Implementieren von Jonas
*/
public class Line_MongoDB_Impl extends Line_File_Impl implements Line {
public Line_MongoDB_Impl(Document mongoDocument) {
super(

View file

@ -5,6 +5,9 @@ import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Speaker_File
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speaker;
import org.w3c.dom.Element;
/*
* Klassen-Implementieren von Jonas
*/
public class Speaker_MongoDB_Impl extends Speaker_File_Impl implements Speaker {
public Speaker_MongoDB_Impl(Document mongoDocument) {
super(

View file

@ -1,5 +1,7 @@
package org.texttechnologylab.project.gruppe_05_1.domain;
/**
* Datei implementiert von Valentin
*/
public enum Gender { // TODO: Delete
M("männlich"),

View file

@ -10,6 +10,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class HtmlSpeech {
String speechKey;
@ -19,6 +22,8 @@ public class HtmlSpeech {
String agendaTitle; // aus "agendaItems" Collection
List<SpeechContent> content = new ArrayList<>();
NlpInfo nlp = null;
String video = null;
String videoData;
public HtmlSpeech() {
}
@ -27,7 +32,7 @@ public class HtmlSpeech {
setSpeechKey(doc.getString("speechKey"));
setSpeakerName(doc.getString("speakerName"));
setFraction(doc.getString("fraction"));
setVideo(doc.getString("video"));
List<Document> contentDocList = doc.get("speechContents", MongoDBHandler.DOC_LIST_CLASS);
if (contentDocList == null) {
setContent(new ArrayList<>());
@ -51,13 +56,13 @@ public class HtmlSpeech {
// Ergänzung um NLP-Informationen
Document nlpDoc = (Document) doc.get("analysisResults");
nlp = readNlpInfo(nlpDoc);
}
private NlpInfo readNlpInfo(Document nlpDoc) {
if (nlpDoc == null) return null;
NlpInfo nlp = new NlpInfo();
// TODO: HERE
List<Document> tokensDocs = nlpDoc.get("tokens", MongoDBHandler.DOC_LIST_CLASS);
nlp.setTokens(Token.readTokensFromMongo(tokensDocs));
@ -74,7 +79,6 @@ public class HtmlSpeech {
List<Document> topicsDocs = nlpDoc.get("topics", MongoDBHandler.DOC_LIST_CLASS);
nlp.setTopics(Topic.readTopicsFromMongo(topicsDocs));
// TODO: Video
return nlp;
}
@ -157,4 +161,20 @@ public class HtmlSpeech {
.add("nlp=" + nlp)
.toString();
}
public void setVideo(String video) {
this.video = video;
}
public void setVideoData(String videoData) {
this.videoData = videoData;
}
public String getVideo() {
return video;
}
public String getVideoData() {
return videoData;
}
}

View file

@ -2,7 +2,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.html;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class Parlamentarier {
String id;
String vorname;

View file

@ -8,7 +8,9 @@ import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class ParlamentarierDetails {
String id;
String vorname;

View file

@ -2,6 +2,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.html;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class SpeechOverview {
String speechKey;

View file

@ -3,6 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.mdb;
import java.time.LocalDate;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class BiografischeAngaben {

View file

@ -3,6 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.mdb;
import java.time.LocalDate;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class Institution {

View file

@ -1,6 +1,9 @@
package org.texttechnologylab.project.gruppe_05_1.domain.mdb;
public enum Mandatsart {
/**
* Datei implementiert von Valentin
*/
DIREKT("Direktwahl"),
LANDESLISTE("Landesliste"),

View file

@ -3,6 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.mdb;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class Mdb {

View file

@ -3,6 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.mdb;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class MdbDocument {

View file

@ -3,6 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.mdb;
import java.time.LocalDate;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class MdbName {

View file

@ -4,6 +4,9 @@ import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class Wahlperiode {

View file

@ -2,7 +2,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.nlp;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class AudioToken {
private int begin;

View file

@ -2,7 +2,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.nlp;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class Dependency {
String type;
String governor;

View file

@ -7,7 +7,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
* toXML implementiert von Jonas
*/
public class NamedEntity {
String type; // PER, LOC etc.
String text;

View file

@ -4,7 +4,9 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class NlpInfo {
List<Token> tokens;
List<Sentence> sentences;

View file

@ -4,7 +4,10 @@ import org.w3c.dom.Element;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
* toXML implementiert von Jonas
*/
public class Pos {
String posValue; // ART, NN...
String coarseValue; // PROPN...

View file

@ -2,10 +2,10 @@ package org.texttechnologylab.project.gruppe_05_1.domain.nlp;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class Sentence {
// int begin; // TODO: momentan nicht in MongoDB
// int end; // TODO: momentan nicht in MongoDB
String text;
public Sentence() {

View file

@ -8,7 +8,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
* toXML-Methode implementiert von Jonas
*/
public class Sentiment {
int begin;
int end;

View file

@ -3,7 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.nlp;
import org.bson.Document;
import java.util.*;
/**
* Datei implementiert von Valentin
*/
public class Token {
String text;
String pos;
@ -67,6 +69,7 @@ public class Token {
* Die Token-Dokumente (Speech --> analysisResults --> token) aus der MongoDB lesen
* @param tokenDocs Eine Liste von Mongo-Dokumenten
* @return Eine Liste der Token
* Implementiert von Leon
*/
public static List<Token> readTokensFromMongo(List<Document> tokenDocs) {
List<Token> tokens = new ArrayList<>();
@ -83,6 +86,7 @@ public class Token {
* Zählt alle verschiedenen POS Vorkommen auf
* @param tokenList
* @return Jede POS art mit ihrer Anzahl an Vorkommen
* Implementiert von Leon
*/
public static Map<String, Integer> countPOS(List<Token> tokenList) {
Map<String, Integer> posCounts = new HashMap<>();

View file

@ -5,7 +5,10 @@ import org.w3c.dom.Element;
import java.util.*;
import java.util.stream.Collectors;
/**
* Datei implementiert von Valentin
* toXML-Methode implementiert von Jonas
*/
public class Topic {
String topic;
Double score;

View file

@ -3,7 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.nlp;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class VideoInformation {
List<AudioToken> audioTokens;

View file

@ -3,6 +3,7 @@ package org.texttechnologylab.project.gruppe_05_1.domain.nlp.html;
import java.util.Objects;
/**
* Datei implementiert von Valentin
* Diese Klasse ordnet das entspreche Sentiment zu einem Satz zu.
* Sie ist ein Datencontainer für die Darstellung über FreeMarker
*/

View file

@ -3,12 +3,14 @@ package org.texttechnologylab.project.gruppe_05_1.domain.speaker;
import java.time.LocalDate;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public class Membership {
String role;
String member; // TODO: wozu? Dr. Abrami hat hier die ID des Parlamentariers gespeichert
LocalDate begin; // TODO: in Mongo eigentlich Date?
LocalDate end; // TODO: in Mongo eigentlich Date?
String member;
LocalDate begin;
LocalDate end;
String label;
Integer wp;

View file

@ -4,7 +4,9 @@ import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class Speaker {
String id;
String name;
@ -23,9 +25,6 @@ public abstract class Speaker {
String primaryFoto;
List<Membership> memberships;
// TODO: List<Speech> speeches;
// TODO: Photos
public String getId() {
return id;

View file

@ -2,7 +2,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.speech;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class Agenda {
String index;
String id;

View file

@ -4,7 +4,9 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class Comment {
String id;

View file

@ -4,7 +4,9 @@ import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class Protocol {
LocalDate date;
LocalTime startTime;

View file

@ -3,7 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.speech;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class Speech {
String id;
String text;

View file

@ -5,16 +5,17 @@ import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
* Diese Klasse dient der Darstellung aller Reden eines Parlamentariers.
*/
public class SpeechMetaData {
// aus "speech" Collection
String speechKey; // z.B. "ID2011400300"
int speechId; // TODO: nötig?
int speechId;
int speakerId;
String speakerName;
String fraktion;
int sessionId; // TODO: nötig?
int sessionId;
// aus "sessions" Collection
LocalDateTime dateTime;

View file

@ -2,7 +2,9 @@ package org.texttechnologylab.project.gruppe_05_1.domain.speech;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Datei implementiert von Valentin
*/
public abstract class TextContent {
String id;
String speakerId;

View file

@ -24,6 +24,10 @@ import static org.texttechnologylab.project.gruppe_05_1.Main.RESOURCES_DIR;
import static org.texttechnologylab.project.gruppe_05_1.Main.TEMP_EXPORT_DIR;
import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.*;
/**
* Utility Klasse für die Erstellung von TeX-Dateien und Konvertierung in Base64-kodierte PDF-Dateien.
* Implementiert von Jonas
*/
public class TeXUtil {
private static final String PREAMBLE = readFileContentFromTeXDir();
private static final String BEGIN_DOCUMENT = "\\begin{document}\n";

View file

@ -19,6 +19,10 @@ import javax.xml.parsers.ParserConfigurationException;
import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.*;
/**
* Utility Klasse für die Erstellung von XML-Dateien und Konvertierung in Base64-kodierte PDF-Dateien.
* Implementiert von Jonas
*/
public class XMLUtil {
public static String documentToString(Document doc) {
try {

View file

@ -48,9 +48,10 @@ public class NlpUtils {
private static final String TYPE_SYSTEM_DESCRIPTOR_PATH = "/speeches/TypeSystem.xml.gz";
private static final int MAX_FEATURE_LENGTH = 10000;
/**
* implementiert von Valentin
*/
public static void createNlpData() {
// Source: Dr. Abrami - Beispiel TODO
duuiInit();
runDockerDriver();
try {
@ -68,6 +69,9 @@ public class NlpUtils {
createSentimentInfo();
}
/**
* implementiert von Valentin
*/
private static void duuiInit() {
DUUILuaContext ctx;
@ -116,6 +120,7 @@ public class NlpUtils {
}
/**
* implementiert von Valentin
* Initialization of a sample CAS document
* @return JCas object
*/
@ -143,7 +148,9 @@ public class NlpUtils {
return pCas;
}
/**
* implementiert von Valentin
*/
public static void runDockerDriver() {
// reset existing pipeline-components
@ -188,7 +195,9 @@ public class NlpUtils {
Logger.info(JCasUtil.selectCovered(org.hucompute.textimager.uima.type.Sentiment.class, sentence).toString());
});
}
/**
* implementiert von Valentin
*/
private static void casInit() {
JCas jcas;
try {
@ -242,6 +251,7 @@ public class NlpUtils {
/**
* implementiert von Valentin
* Execution of video processing via DUUI using the RemoteDriver
* @throws Exception in case of an error
*/
@ -315,6 +325,15 @@ public class NlpUtils {
}
/**
* Führt den Remote-NLP-Driver auf unprozessierten Reden aus und aktualisiert die Datenbank.
* <p>
* Es werden alle Reden ohne "analysisResults" abgerufen, mit den Remote-Komponenten (spaCy und Vader)
* verarbeitet, die NLP-Ergebnisse extrahiert und per Bulk-Update in der Datenbank gespeichert.
* </p>
*
* @throws Exception falls ein Fehler auftritt.
*/
public static void runRemoteDriver() throws Exception {
DUUILuaContext luaContext = new DUUILuaContext().withJsonLibrary();
pComposer = new DUUIComposer()

View file

@ -6,7 +6,6 @@ import com.mongodb.client.model.WriteModel;
import org.apache.uima.fit.util.JCasUtil;
import org.bson.Document;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@ -24,7 +23,12 @@ import org.texttechnologylab.project.gruppe_05_1.util.Logger;
import static org.texttechnologylab.project.gruppe_05_1.Main.JCAS_SPEECHES_TYPESYSTEM_DIR;
/**
* Extrahiert NLPAnalyseergebnisse aus komprimierten XMIDateien und lädt sie batchweise in MongoDB hoch.
* Liest .xmi.gz Dateien aus einem ZIPArchiv, erstellt für jede Rede ein "analysisResults" Dokument und führt BulkWrites durch.
* Implementiert von Leon
* Modifiziert von Henry
*/
public class XmiExtractor {
private final List<WriteModel<Document>> bulkOperations = Collections.synchronizedList(new ArrayList<>());
@ -36,6 +40,13 @@ public class XmiExtractor {
mongoDBHandler = new MongoDBHandler();
}
/**
* Liest alle .xmi.gz Dateien aus dem ZIPArchiv "speeches/20.zip", extrahiert deren NLPDaten
* und lädt sie in Form von BATCH_SIZE Dokumenten in MongoDB hoch.
* @throws IOException falls die ZIPDatei nicht gefunden oder ein Lese-/Schreibfehler auftritt
* Implementiert von Leon
* Modifiziert von Henry
*/
public void extractAndUploadXmiData() throws IOException {
InputStream resourceStream = getClass().getClassLoader().getResourceAsStream("speeches/20.zip");
if (resourceStream == null) {
@ -85,6 +96,13 @@ public class XmiExtractor {
mongoDBHandler.close();
}
/**
* Liest ein komprimiertes XMI ein und erstellt BulkUpdateOperationen für MongoDB.
* @param inputStream komprimierter XMIInputStream
* @param filename Name der Datei im ZIPArchiv (für speechKey)
* Implementiert von Leon
* Modifiziert von Henry
*/
private void processXmiGzStream(InputStream inputStream, String filename) {
JCas jCas = null;
try (GZIPInputStream gis = new GZIPInputStream(inputStream)) {
@ -181,11 +199,21 @@ public class XmiExtractor {
}
}
/**
* Extrahiert aus dem Dateinamen (z.B. "20/ABC123.xmi.gz") den speechKey.
* @param filename Name der Datei innerhalb des ZIPArchivs
* @return speechKey oder null, wenn das Format nicht erkannt wird
* Implementiert von Leon
*/
private static String extractSpeechKeyFromFilename(String filename) {
String baseName = filename.replace(".xmi.gz", "");
return baseName.replace("20/", "");
}
/**
* Führt alle gesammelten BulkWrite-Operationen in MongoDB aus und leert den Batch.
* Implementiert von Henry
*/
private synchronized void flushBatch() {
if (!bulkOperations.isEmpty()) {
mongoDBHandler.bulkWriteNlpData(bulkOperations);

View file

@ -1,29 +1,32 @@
package org.texttechnologylab.project.gruppe_05_1.rest;
import gnu.trove.impl.sync.TSynchronizedShortObjectMap;
import com.mongodb.client.MongoCollection;
import io.javalin.http.Context;
import io.javalin.openapi.*;
import org.apache.commons.collections.bag.SynchronizedSortedBag;
import org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils;
import org.texttechnologylab.project.gruppe_05_1.domain.html.HtmlSpeech;
import org.texttechnologylab.project.gruppe_05_1.domain.html.Parlamentarier;
import org.texttechnologylab.project.gruppe_05_1.domain.html.ParlamentarierDetails;
import org.texttechnologylab.project.gruppe_05_1.domain.nlp.Sentiment;
import org.texttechnologylab.project.gruppe_05_1.domain.nlp.Token;
import org.texttechnologylab.project.gruppe_05_1.domain.nlp.Topic;
import org.texttechnologylab.project.gruppe_05_1.domain.speech.SpeechMetaData;
import org.texttechnologylab.project.gruppe_05_1.util.Logger;
import org.texttechnologylab.project.gruppe_05_1.util.PPRUtils;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Accumulators;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static org.texttechnologylab.project.gruppe_05_1.util.PPRUtils.listFractionsFromMembers;
/**
* Controller für die FrontendRouten.
* Größtenteils implementiert von Jonas
*/
public class FrontEndController {
@OpenApi(
summary = "Get the homepage.",
@ -39,6 +42,20 @@ public class FrontEndController {
ctx.render("home.ftl");
}
@OpenApi(
summary = "Get the about page.",
description = "Get the about",
operationId = "getAbout",
path = "/about",
methods = HttpMethod.GET,
tags = {"About"},
responses = {
@OpenApiResponse(status = "200")
})
public static void getAbout(Context ctx) {
ctx.render("about.ftl");
}
/*
TODO: getAllParlamentarier gibt es hier UND im ParlamentarierController (etwas unterschiedliche Implementierungen)
--> konsolidieren!

View file

@ -9,6 +9,7 @@ import java.io.InputStream;
import java.util.Properties;
/**
* Datei implementiert von Valentin
* Diese Klasse dient der Konfiguruerung von Javalin
*/
public class JavalinConfig extends Properties {

View file

@ -15,6 +15,7 @@ import java.util.List;
import java.util.Map;
/**
* Datei implementiert von Valentin
* Dieser Kontroller zeigt Informationen um den Parlamentariern:
* 1. Die Einstiegsseite: eine Filter-fähige Auflistung der Parlamentariern.
* Angezeigt werden Vor- und Nachname, Partei sowie ID.

View file

@ -15,6 +15,9 @@ import java.io.IOException;
import static org.texttechnologylab.project.gruppe_05_1.Main.JAVALIN_STATIC_FILES_DIR;
import static org.texttechnologylab.project.gruppe_05_1.Main.JAVALIN_TEMPLATE_DIR;
/**
* Datei implementiert von Valentin
*/
public class RESTHandler {
public void startJavalin() {
@ -66,8 +69,14 @@ public class RESTHandler {
app.get("/reden/{id}", SpeechController::listSpeeches); // zeige Reden eines Parlamentariers an
app.get("/reden/{id}/{redeId}", SpeechController::showSpeech); // zeige eine bestimmte Rede des Parlamentariers an
app.get("/reden", SpeechController::listAllSpeeches); // zeige alle Reden an (Filtern möglich)
// Charts
app.get("/charts", FrontEndController::getCharts);
app.get("/about", FrontEndController::getAbout);
app.get("/export/pdf/speech/{id}", SpeechesLatexExportController::exportSpeech); // exportiere eine Rede als PDF
app.get("/export/pdf/speech", SpeechesLatexExportController::exportSpeech); // exportiere eine Rede als PDF
app.get("/export/pdf/speaker/{id}", SpeechesLatexExportController::exportSpeechesFromSpeaker); // exportiere alle Reden eines Parlamentariers als PDF

View file

@ -1,68 +0,0 @@
package org.texttechnologylab.project.gruppe_05_1.rest;
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
import io.javalin.Javalin;
import io.javalin.http.staticfiles.Location;
import io.javalin.openapi.plugin.OpenApiPlugin;
import io.javalin.openapi.plugin.redoc.ReDocPlugin;
import io.javalin.rendering.template.JavalinFreemarker;
import org.texttechnologylab.project.gruppe_05_1.util.Logger;
import java.io.File;
import java.io.IOException;
import static org.texttechnologylab.project.gruppe_05_1.Main.JAVALIN_STATIC_FILES_DIR;
import static org.texttechnologylab.project.gruppe_05_1.Main.JAVALIN_TEMPLATE_DIR;
public class RESTHandlerOld {
public void startJavalin() {
// Javalin Konfiguration (z.B. port)
JavalinConfig jlConfig = new JavalinConfig();
int port = jlConfig.getPort();
// FreeMarker Konfiguration
Configuration fmConfig = new Configuration(Configuration.VERSION_2_3_33);
fmConfig.setDefaultEncoding("UTF-8");
try {
fmConfig.setDirectoryForTemplateLoading(new File(JAVALIN_TEMPLATE_DIR));
} catch (IOException e) {
throw new RuntimeException(e);
}
fmConfig.setLogTemplateExceptions(true);
fmConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
// Erzeuge die Javalin app
Javalin app = Javalin.create(config -> {
config.staticFiles.add(JAVALIN_STATIC_FILES_DIR, Location.EXTERNAL); // momentan nicht benutzt
config.fileRenderer(new JavalinFreemarker(fmConfig));
config.registerPlugin(new OpenApiPlugin(pluginConfig -> {
// Define OpenAPI spec configuration
pluginConfig.withDefinitionConfiguration((version, definition) -> {
definition.withOpenApiInfo(info -> info.setTitle("Javalin OpenAPI Documentation"));
});
}));
config.registerPlugin(new ReDocPlugin());
})
.start(port);
Logger.info("Javalin app started on http://localhost:" + port);
// Routes
// ======
// Parlamentarier
app.get("/", ParlamentarierController::getAllParlamentarier);
app.get("/portfolio/{id}", ParlamentarierController::getParlamentarierDetails);
// Reden
app.get("/reden/{id}", SpeechController::listSpeeches); // zeige Reden eines Parlamentariers an
app.get("/reden/{id}/{redeId}", SpeechController::showSpeech); // zeige eine bestimmte Rede des Parlamentariers an
}
}

View file

@ -1,7 +1,11 @@
package org.texttechnologylab.project.gruppe_05_1.rest;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import io.javalin.http.Context;
import io.javalin.openapi.*;
import org.bson.types.ObjectId;
import org.texttechnologylab.project.gruppe_05_1.database.MongoDBHandler;
import org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils;
import org.texttechnologylab.project.gruppe_05_1.domain.html.HtmlSpeech;
import org.texttechnologylab.project.gruppe_05_1.domain.html.ParlamentarierDetails;
@ -14,11 +18,13 @@ import org.texttechnologylab.project.gruppe_05_1.domain.speech.SpeechMetaData;
import org.texttechnologylab.project.gruppe_05_1.util.Logger;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.stream.Collectors;
public class SpeechController {
/**
* Datei implementiert von Valentin
* Liste alle Reden eines Parlamentariers an
* @param ctx Javalin Context
*/
@ -39,8 +45,7 @@ public class SpeechController {
String parlamentarierId = ctx.pathParam("id");
ParlamentarierDetails p = MongoPprUtils.getParlamentarierDetailsByID(parlamentarierId);
List<SpeechMetaData> speechMetaDataList = MongoPprUtils.getSpeechesMetadataForSpeaker(parlamentarierId);
List<SpeechOverview> speechMetaDataList = MongoPprUtils.getSpeechesOverviewForSpeaker(Integer.parseInt(parlamentarierId));
Map<String, Object> attributes = new HashMap<>();
attributes.put("p", p);
attributes.put("speechesMetaDataList", speechMetaDataList);
@ -50,6 +55,8 @@ public class SpeechController {
/**
* Zeige eine bestimmte Rede des Parlamentariers an
* @param ctx Javalin Context
* Implementiert von Valentin
* Modifiziert von Leon
*/
@OpenApi(
summary = "Zeige eine bestimmte Rede des Parlamentariers an",
@ -79,6 +86,24 @@ public class SpeechController {
}
attributes.put("s", speech);
if (speech.getVideo() != null && !speech.getVideo().trim().isEmpty()) {
MongoDBHandler mongoDBHandler = new MongoDBHandler();
try {
GridFSBucket gridFSBucket = GridFSBuckets.create(mongoDBHandler.getDatabase(), "videos");
ObjectId fileId = new ObjectId(speech.getVideo());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
gridFSBucket.downloadToStream(fileId, baos);
byte[] videoBytes = baos.toByteArray();
String base64Video = Base64.getEncoder().encodeToString(videoBytes);
speech.setVideoData(base64Video);
} catch (Exception e) {
System.err.println("Error fetching video from GridFS: " + e.getMessage());
} finally {
mongoDBHandler.close();
}
}
// Foto des Abgeordnetes
String picture = MongoPprUtils.getParlamentarierPictureByID(parlamentarierId);
attributes.put("picture", picture);
@ -105,7 +130,7 @@ public class SpeechController {
Map<String, Integer> posCounts = Token.countPOS(tokens);
List<Token> posList = posCounts.entrySet().stream()
.map(entry -> new Token(entry.getKey(), String.valueOf(entry.getValue()), "")) // Lemma remains empty
.map(entry -> new Token(entry.getKey(), String.valueOf(entry.getValue()), ""))
.collect(Collectors.toList());
Logger.debug("Sending POS List to NLP - " + posList);
@ -114,7 +139,7 @@ public class SpeechController {
} else {
Logger.debug("POS List is EMPTY");
speech.getNlp().setPosList((List) new ArrayList<Token>()); // Ensure it's never null
speech.getNlp().setPosList((List) new ArrayList<Token>());
}
// NLP: Named Entities
@ -151,10 +176,8 @@ public class SpeechController {
attributes.put("na_info", null);
}
// TODO: Token wird momentan etwas komisch abgespeichert, da im Attribut text die POS art steht, und in pos die Anzahl dieser POS arten. Umstrukturieren damit keine Verwirrung herrscht
// NLP: Sentiments
// Der erste Sentiment gilt der gesamten Rede. Die weitere Sentiments entsprechen die Sätze.
// Der erste Sentiment gilt der gesamten Rede. Die weitere Sentiments entsprechen die Sätze. overallSentiments speichert alle Analyseobjekte und sentiments nur die der einzelnen Sätze
List<Sentiment> sentiments = speech.getNlp().getSentiments();
if ((sentiments != null) && ! sentiments.isEmpty()) {
List<Sentiment> overallSentiments = new ArrayList<>(sentiments);
@ -212,30 +235,25 @@ public class SpeechController {
tags = {"Rede"},
queryParams = {
@OpenApiParam(name = "filter", description = "Full-Text-Filter. Kann Vorname, Nachname oder Partei filtern", required = false),
// TODO: Topic Filter
},
responses = {
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = Speech[].class)})
})
public static void listAllSpeeches(Context ctx) {
String filter = ctx.queryParam("filter");
Logger.info("Filter: '" + filter + "'");
List<SpeechOverview> speechOverviews = MongoPprUtils.getSpeechOverview();
List<SpeechOverview> speechOverviews = MongoPprUtils.getFilteredSpeechesOverview(ctx);
Map<String, Object> attributes = new HashMap<>();
attributes.put("speechesMetaDataList", speechOverviews);
// Filtern nach Text
attributes.put("filter", filter == null || filter.isBlank() ? null : filter);
String name = ctx.queryParam("name");
if ((name != null) && ( ! name.isBlank())) attributes.put("name", name);
// Filtern nach Partei/Fraktion
attributes.put("parties", MongoPprUtils.getAllPartiesFromSpeeches());
// Filtern nach Topics
List<String> topics = MongoPprUtils.getAllTopics();
attributes.put("topics", topics);
ctx.render("showAllSpeechesList.ftl", attributes);
}
}

View file

@ -14,6 +14,10 @@ import java.util.List;
import static org.texttechnologylab.project.gruppe_05_1.export.TeXUtil.*;
/**
* Controller für die Endpunkte zum Export von Reden als PDF.
* Implementiert von Jonas
*/
public class SpeechesLatexExportController {
@OpenApi(
summary = "Get a speech as a PDF",

View file

@ -16,6 +16,10 @@ import java.util.List;
import static org.texttechnologylab.project.gruppe_05_1.export.XMLUtil.*;
import static org.texttechnologylab.project.gruppe_05_1.export.TeXUtil.*;
/**
* Controller für die Endpunkte zum Export von Reden als XML.
* Implementiert von Jonas
*/
public class SpeechesXMLExportController {
@OpenApi(
summary = "Get a speech as XML",

View file

@ -17,6 +17,7 @@ public abstract class FileUtils {
/**
* Datei implementiert von Valentin
* Creates a (possibly nested) directory
* @param dir (e.g. "generated" , "level1/level2/level3" etc.
*/

View file

@ -9,7 +9,7 @@ import java.time.format.DateTimeParseException;
public abstract class GeneralUtils {
/**
*
* Datei implementiert von Valentin
* @param integer the integer to be parsed
* @return the parsed integer or null if the integer could not be parsed
*/

View file

@ -4,6 +4,10 @@ import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static org.texttechnologylab.project.gruppe_05_1.Main.DEBUG_LOGGING;
/**
* Logger Klasse für die Ausgabe von Lognachrichten
* Implementiert von Jonas
*/
public class Logger {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
// info, warn, error with message and colors and datetime

View file

@ -1,6 +1,8 @@
package org.texttechnologylab.project.gruppe_05_1.util;
import com.mongodb.client.MongoCollection;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
@ -22,6 +24,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -41,6 +44,7 @@ public abstract class PPRUtils {
/**
* Von Valentin angelegt, von allen verändert
* Alle Informationen lesen...
* - Parlamentarier
* - Reden
@ -61,21 +65,16 @@ public abstract class PPRUtils {
Logger.warn("Members already in the DB. Skipping...");
}
// Reden und Kommentare einlesen und persistieren - TODO
// Reden und Kommentare einlesen und persistieren
readSpeechesAndComments(xmlFactory, mongoFactory);
// Fotos hochladen - TODO
// Fotos hochladen
readPhotos(mongoFactory);
// NLP-Analyse (Text und Video)
// TODO: Anpassung notwendig, Daten aus dem NEtz statt aus resources holen
// Achtung: läuft nicht unter Windows. Verwende Linux (nativ oder im Virtual Box)
// NlpUtils.createNlpData();
}
/**
* Fotos hochladen - TODO
* Fotos hochladen
* @param mongoFactory Factory für die MongoDB-Objekte
*/
public static void readPhotos(MongoObjectFactory mongoFactory) {
@ -83,7 +82,7 @@ public abstract class PPRUtils {
}
/**
* Reden und Kommentare einlesen - TODO
* Reden und Kommentare einlesen
* @param xmlFactory Factory für die XML-Objekte
* @param mongoFactory Factory für die MongoDB-Objekte
*/
@ -99,7 +98,6 @@ public abstract class PPRUtils {
public static void readAndPersistMdbs(String mdbUrl, FileObjectFactory xmlFactory, MongoObjectFactory mongoFactory) {
org.w3c.dom.Document mdbRoot = getMdbFromRemoteXmlZipfile(mdbUrl);
Element rootElement = mdbRoot.getDocumentElement();
// TODO: optional! persist the metadata of the <VERSION>1723434311</VERSION> element
List<Node> mdbNodes = XmlUtils.getChildrenByName(rootElement, "MDB");
@ -114,12 +112,9 @@ public abstract class PPRUtils {
}
}
// TODO: persist each speaker!
// TODO: guard: if not in the DB already
}
// TODO - HERE
private static boolean mdbActiveInWp(Speaker speaker, Integer legislaturPeriode) {
List<Integer> wps = speaker.getMemberships().stream()
.map(Membership::getWp)
@ -156,7 +151,7 @@ public abstract class PPRUtils {
zipInputStream.closeEntry();
}
if (xmlOutputStream.size() == 0 || dtdOutputStream.size() == 0) { // TODO
if (xmlOutputStream.size() == 0 || dtdOutputStream.size() == 0) {
// throw new FileNotFoundException("XML or DTD not found in the ZIP archive");
}
InputStream xmlInputStreamFinal = new ByteArrayInputStream(xmlOutputStream.toByteArray());
@ -171,7 +166,6 @@ public abstract class PPRUtils {
return doc;
} catch (IOException e) {
// TODO
throw new RuntimeException(e);
}
}
@ -209,8 +203,6 @@ public abstract class PPRUtils {
});
}
// TODO: altes Zeug, sortieren...
/**
* Alle Parteien (aus einer Liste der MdBs) herausfinden.
* null-Einträge durch einen Platzhalter ersetzen, damit später keine null pointer exceptions auftretten
@ -327,7 +319,19 @@ public abstract class PPRUtils {
});
}
/**
* Liest XML-Protokolle von der Bundestag-OpenData-URL und verarbeitet sie.
*
* <p>
* Diese Methode ruft wiederholt Seiten mit XML-Links (Protokollen) ab, basierend auf einem
* Offset und Limit. Für jeden gefundenen Link wird die XML-Datei heruntergeladen und geparst.
* Einzigartige Protokolle werden anhand des "sitzung-nr"-Attributs identifiziert. Falls ein Protokoll
* bereits verarbeitet wurde (gespeichert in {@code processedProtocols}), wird es übersprungen.
* Die verarbeiteten XML-Dokumente werden in der globalen Menge {@code xmlProtocols} gesammelt.
* </p>
*
* @return Ein Set von {@code org.w3c.dom.Document}, das die verarbeiteten XML-Protokolle enthält.
*/
public static Set<org.w3c.dom.Document> processXML() {
int offset = 0;
int limit = 10;
@ -411,6 +415,12 @@ public abstract class PPRUtils {
return doc;
}
/**
* Listet die Dateien im gegebenen Verzeichnis auf.
* Implementiert von Jonas
* @param directory Verzeichnis
* @return Liste der Dateinamen
*/
public static ArrayList<String> listFilesInDirectory(String directory) {
File folder = new File(directory);
File[] files = folder.listFiles();
@ -425,6 +435,24 @@ public abstract class PPRUtils {
return fileNames;
}
/**
* Ruft neue Protokoll-XML-Dokumente von der Bundestag OpenData-API ab und verarbeitet sie.
*
* Diese Methode verwendet eine paginierte Abfrage (über Offset und Limit) der URL
* "https://www.bundestag.de/ajax/filterlist/de/services/opendata/866354-866354".
* Für jeden gefundenen XML-Link wird anhand des Dateinamens (z.B "20212.xml") die Sitzungsnummer extrahiert.
* Protokolle mit den Dateinamen "20007" oder "20212" werden als fehlerhaft erkannt und übersprungen.
* Falls die extrahierte Sitzungsnummer (nach Entfernen eines möglichen "20"-Präfixes) noch nicht in der Datenbank existiert
* (bestimmt durch mongoDBHandler.sessionExists(sessionNumber)), wird das XML-Dokument heruntergeladen und geparst.
* Die neu verarbeiteten XML-Dokumente werden in einem Set gesammelt und zurückgegeben.
*
* Die Pagination erfolgt über das HTML-Element <div class="meta-slider"> mit dem Attribut
* data-nextoffset. Ist kein nächster Offset vorhanden, wird die Schleife beendet.
*
*
* @param mongoDBHandler der MongoDBHandler, der für die Prüfung der Existenz einer Session in der Datenbank verwendet wird
* @return ein Setvon org.w3c.dom.Document, das alle neuen (noch nicht verarbeiteten) Protokoll-XML-Dokumente enthält
*/
public static Set<org.w3c.dom.Document> checkAndProcessNewProtocols(MongoDBHandler mongoDBHandler) {
Set<org.w3c.dom.Document> newProtocols = new HashSet<>();
int offset = 0;
@ -485,6 +513,12 @@ public abstract class PPRUtils {
return newProtocols;
}
/**
* Listet die Fraktionen von einer Liste an Membern auf
* Implementiert von Jonas
* @param mdbList Liste der Mitglieder
* @return Liste der Fraktionen
*/
public static ArrayList<String> listFractionsFromMembers(List<Parlamentarier> mdbList) {
ArrayList<String> fractions = new ArrayList<>();
for (Parlamentarier parlamentarier : mdbList) {
@ -500,4 +534,105 @@ public abstract class PPRUtils {
}
return fractions;
}
/**
* Fetched das Bild eines Mitglieds aus der Bundestagsdatenbank und gibt es als Base64-String zurück
* Implementiert von Jonas
* @param inputString Name des Mitglieds
* @return Base64-String des Bildes
* @throws IOException
*/
public static String fetchMemberImageBase64FromNameString(String inputString) throws IOException {
// Step 1: Send POST request
String urlString = "https://bilddatenbank.bundestag.de/ajax/picture-result";
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Cookie", getSessionCookies());
conn.setDoOutput(true);
// Form data
String postData = "query=" + URLEncoder.encode(inputString, "UTF-8") + "&sortVal=2";
try (OutputStream os = conn.getOutputStream()) {
byte[] input = postData.getBytes("UTF-8");
os.write(input, 0, input.length);
}
// Read response
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
// Parse JSON response
JSONObject jsonResponse = new JSONObject(response.toString());
JSONArray fotosArray = jsonResponse.optJSONArray("fotos");
if (fotosArray != null && !fotosArray.isEmpty()) {
String hqBild = fotosArray.getJSONObject(0).optString("hqBild", "");
if (!hqBild.isEmpty()) {
// Step 2: Fetch image
String imageUrl = "https://bilddatenbank.bundestag.de/fotos/" + hqBild;
URL imageDownloadUrl = new URL(imageUrl);
HttpURLConnection imageConn = (HttpURLConnection) imageDownloadUrl.openConnection();
imageConn.setRequestMethod("GET");
if (imageConn.getResponseCode() == HttpURLConnection.HTTP_OK) {
try (InputStream is = imageConn.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
}
}
}
}
}
return "Error: Unable to retrieve image";
}
/**
* Gibt die Session-Cookies zurück, die für die Requests der Bildersuche benötigt werden
* Implementiert von Jonas
* @return Session-Cookies
* @throws IOException
*/
public static String getSessionCookies() throws IOException {
String urlString = "https://bilddatenbank.bundestag.de/search/picture-result?query=Angela+Merkel&sortVal=2";
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
// get both header fields with the name "Set-Cookie"
Map<String, List<String>> headerFields = conn.getHeaderFields();
String phpSessId = "";
String csrfToken = "";
// iterate over the header fields
for (Map.Entry<String, List<String>> entry : headerFields.entrySet()) {
// if header field is "Set-Cookie"
if (entry.getKey() != null && entry.getKey().equals("set-cookie")) {
// iterate over the values of the header field
for (String value : entry.getValue()) {
// if value contains "PHPSESSID"
if (value.contains("PHPSESSID")) {
phpSessId = value.split(";")[0];
}
// if value contains "_csrf"
if (value.contains("_csrf")) {
csrfToken = value.split(";")[0];
}
}
}
}
return "PHPSESSID=" + phpSessId + "; _csrf=" + csrfToken;
}
}

View file

@ -3,7 +3,9 @@ package org.texttechnologylab.project.gruppe_05_1.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Datei implementiert von Valentin
*/
public abstract class PropertiesUtils {
public static Properties readPropertiesFromResource(String propertiesFileName) {

View file

@ -0,0 +1,153 @@
package org.texttechnologylab.project.gruppe_05_1.util;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.model.GridFSUploadOptions;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.WriteModel;
import org.apache.commons.logging.Log;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.texttechnologylab.project.gruppe_05_1.database.MongoDBHandler;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
//Implementiert von Henry
public class SpeechVideoUpdater {
private static final int BASE_VIDEO_ID = 7615435;
private static final String BASE_URL = "https://cldf-od.r53.cdn.tv1.eu/1000153copo/ondemand/app144277506/145293313/";
private static final String SUFFIX = "_h264_720_400_2000kb_baseline_de_2192.mp4?fdl=1";
/**
* Initialisiert den Video-Upload-Prozess.
* Diese Methode prüft, ob bereits ein Video für die erste Rede (speechId 0) in Session 187 und AgendaItem 4 vorhanden ist.
* Falls ein Video vorhanden ist, wird der Download übersprungen. Andernfalls wird findVideos() aufgerufen,
* um die Videos herunterzuladen und in GridFS hochzuladen sowie die Speech-Dokumente zu aktualisieren.
*/
public static void init() {
MongoDBHandler mongoDBHandler = new MongoDBHandler();
try {
Document filter = new Document("sessionId", 187)
.append("agendaItemId", 4)
.append("speechId", 0);
Document firstSpeech = mongoDBHandler.getSpeech(filter);
if (firstSpeech != null && firstSpeech.containsKey("video")
&& firstSpeech.getString("video") != null
&& !firstSpeech.getString("video").trim().isEmpty()) {
Logger.info("Videos sind bereits vorhanden. Kein Download notwendig.");
} else {
Logger.info("Keine Videos gefunden. Starte Download für die Reden der Session 187, AgendaItem 4...");
findVideos();
}
} catch (Exception e) {
System.err.println("Fehler beim Prüfen der Video-Felder: " + e.getMessage());
e.printStackTrace();
} finally {
mongoDBHandler.close();
}
}
/**
* Lädt Videos für Reden in Session 187, AgendaItem 4 herunter und lädt diese in GridFS hoch.
* Anschließend wird für jede Rede (speechId 0 bis 8) ein Bulk-Update erstellt, um das Speech-Dokument
* mit dem Videolink (GridFS-ID als Hex-String) zu aktualisieren.
*
* @throws Exception Falls beim Download oder Upload ein Fehler auftritt.
*/
public static void findVideos() throws Exception {
MongoDBHandler mongoDBHandler = new MongoDBHandler();
MongoDatabase db = mongoDBHandler.getDatabase();
GridFSBucket gridFSBucket = GridFSBuckets.create(db, "videos");
List<WriteModel<Document>> bulkOperations = new ArrayList<>();
for (int speechId = 0; speechId < 9; speechId++) {
int videoId = BASE_VIDEO_ID + speechId;
String videoUrl = BASE_URL + videoId + "/" + videoId + SUFFIX;
Logger.info("Downloading video for speech " + speechId + ": " + videoUrl);
File videoFile = downloadVideo(videoUrl, "video_" + videoId + ".mp4");
ObjectId gridFsId = uploadVideoToGridFS(gridFSBucket, videoFile, "video_" + videoId + ".mp4");
Logger.info("Uploaded video with GridFS ID: " + gridFsId);
Document filter = new Document("sessionId", 187)
.append("agendaItemId", 4)
.append("speechId", speechId);
Document update = new Document("$set", new Document("video", gridFsId.toHexString()));
bulkOperations.add(new UpdateOneModel<>(filter, update));
videoFile.delete();
}
if (!bulkOperations.isEmpty()) {
Logger.info("Uploading bulk update for " + bulkOperations.size() + " documents...");
mongoDBHandler.bulkWriteNlpData(bulkOperations);
Logger.info("Bulk update completed.");
}
mongoDBHandler.close();
}
/**
* Lädt das Video von der angegebenen URL herunter und speichert es als temporäre Datei.
*
* @param videoUrl Die URL des Videos.
* @param fileName Der gewünschte Dateiname für die temporäre Datei.
* @return Eine {@code File}-Instanz, die auf die heruntergeladene Datei verweist.
* @throws IOException Falls beim Download oder Schreiben der Datei ein Fehler auftritt.
*/
private static File downloadVideo(String videoUrl, String fileName) throws IOException {
URL url = new URL(videoUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
File tempFile = new File(fileName);
try (InputStream in = connection.getInputStream();
FileOutputStream out = new FileOutputStream(tempFile)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
return tempFile;
}
/**
* Lädt die übergebene Videodatei in GridFS hoch.
*
* @param gridFSBucket Der {@code GridFSBucket}, der für die Collection "videos" in der Datenbank konfiguriert ist.
* @param videoFile Die zu hochladende Videodatei.
* @param fileName Der Name, unter dem die Datei in GridFS gespeichert werden soll.
* @return Die {@code ObjectId} der in GridFS gespeicherten Datei.
* @throws IOException Falls beim Hochladen der Datei ein Fehler auftritt.
*/
private static ObjectId uploadVideoToGridFS(GridFSBucket gridFSBucket, File videoFile, String fileName) throws IOException {
try (InputStream streamToUploadFrom = new FileInputStream(videoFile)) {
GridFSUploadOptions options = new GridFSUploadOptions()
.chunkSizeBytes(358400)
.metadata(new Document("type", "video").append("fileName", fileName));
ObjectId fileId = gridFSBucket.uploadFromStream(fileName, streamToUploadFrom, options);
return fileId;
}
}
}

View file

@ -16,6 +16,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
/**
* Datei implementiert von Valentin
*/
public abstract class XmlUtils {
@ -43,7 +46,6 @@ public abstract class XmlUtils {
try {
document = builder.parse(xmlInputStream);
// TODO: Error handling...
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (IOException e) {

View file

@ -5,7 +5,9 @@ import org.texttechnologylab.project.gruppe_05_1.domain.speaker.Speaker;
import org.texttechnologylab.project.gruppe_05_1.xml.mdb.*;
import org.texttechnologylab.project.gruppe_05_1.xml.speaker.Speaker_File_Impl;
import org.w3c.dom.Node;
/**
* implementiert von Valentin
*/
public class FileObjectFactory {
private static FileObjectFactory oFactory = null;

View file

@ -1,7 +1,9 @@
package org.texttechnologylab.project.gruppe_05_1.xml;
import org.w3c.dom.Node;
/**
* implementiert von Valentin
*/
public interface XmlOperations {
FileObjectFactory factory = FileObjectFactory.getFactory();
Object fromXmlNode(Node node);

View file

@ -5,7 +5,9 @@ import org.texttechnologylab.project.gruppe_05_1.util.GeneralUtils;
import org.texttechnologylab.project.gruppe_05_1.util.Logger;
import org.texttechnologylab.project.gruppe_05_1.xml.XmlOperations;
import org.w3c.dom.Node;
/**
* implementiert von Valentin
*/
public class BiografischeAngaben_File_Impl extends BiografischeAngaben implements XmlOperations {
@Override
public BiografischeAngaben fromXmlNode(Node node) {

View file

@ -5,7 +5,9 @@ import org.texttechnologylab.project.gruppe_05_1.util.GeneralUtils;
import org.texttechnologylab.project.gruppe_05_1.util.Logger;
import org.texttechnologylab.project.gruppe_05_1.xml.XmlOperations;
import org.w3c.dom.Node;
/**
* implementiert von Valentin
*/
public class Institution_File_Impl extends Institution implements XmlOperations {
@Override
public Institution fromXmlNode(Node node) {

View file

@ -8,7 +8,9 @@ import org.w3c.dom.Node;
import java.util.ArrayList;
import java.util.List;
/**
* implementiert von Valentin
*/
public class MdbDocument_File_Impl extends MdbDocument implements XmlOperations {
@Override
public MdbDocument fromXmlNode(Node node) {

View file

@ -5,7 +5,9 @@ import org.texttechnologylab.project.gruppe_05_1.util.GeneralUtils;
import org.texttechnologylab.project.gruppe_05_1.util.Logger;
import org.texttechnologylab.project.gruppe_05_1.xml.XmlOperations;
import org.w3c.dom.Node;
/**
* implementiert von Valentin
*/
public class MdbName_File_Impl extends MdbName implements XmlOperations {
@Override
public MdbName fromXmlNode(Node node) {

View file

@ -9,7 +9,9 @@ import org.w3c.dom.Node;
import java.util.ArrayList;
import java.util.List;
/**
* implementiert von Valentin
*/
public class Mdb_File_Impl extends Mdb implements XmlOperations {
@Override
public Mdb fromXmlNode(Node node) {

View file

@ -9,7 +9,9 @@ import org.w3c.dom.Node;
import java.util.ArrayList;
import java.util.List;
/**
* implementiert von Valentin
*/
public class Wahlperiode_File_Impl extends Wahlperiode implements XmlOperations {
@Override
public Wahlperiode fromXmlNode(Node node) {

View file

@ -11,7 +11,9 @@ import org.w3c.dom.Node;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
/**
* implementiert von Valentin
*/
public class Speaker_File_Impl extends Speaker implements XmlOperations {
@Override
public Speaker fromXmlNode(Node node) {
@ -32,7 +34,7 @@ public class Speaker_File_Impl extends Speaker implements XmlOperations {
mdbNameList.add(mdbName);
}
mdbNameList.sort((n1, n2 )-> n2.getHistorieVon().compareTo(n1.getHistorieVon())); // Descending order
MdbName newestMdbName = mdbNameList.get(0); // TODO
MdbName newestMdbName = mdbNameList.get(0);
speaker.setName(newestMdbName.getNachname());
speaker.setFirstName(newestMdbName.getVorname());
speaker.setAkademischertitel(newestMdbName.getAkadTitel());

View file

@ -45,11 +45,25 @@ public class Comment_File_Impl implements Content, Comment {
return MongoDBEntryType.SPEECH_COMMENT;
}
/**
* Gibt den Kommentar als LaTeX-kompatiblen String zurück.
* Der Kommentar wird farbig dargestellt (blau) mit dem Label "Kommentar".
*
* @return String im LaTeX-Format für den Kommentar
*/
@Override
public String toTeX() {
return "\\textcolor{blue}{Kommentar}: " + this.getComment() + "\\\n";
}
/**
* Wandelt den Kommentar in ein XML-Element um.
* Das erzeugte Element enthält die contentId, speechId, den Namen des Kommentators
* sowie den eigentlichen Kommentartext als Inhalt.
*
* @param doc Das XML-Dokument, in das das Element eingebettet wird
* @return Ein XML-Element, das den Kommentar repräsentiert
*/
@Override
public Element toXML(Document doc) {
Element comment = doc.createElement("comment");

View file

@ -35,6 +35,13 @@ public class Constituency_File_Impl implements Constituency {
return MongoDBEntryType.CONSTIUENCY;
}
/**
* Gibt eine HTML-Darstellung des Wahlkreises zurück.
* Die HTML-Ausgabe enthält die ID, den Namen und das Bundesland des Wahlkreises.
*
* @return HTML-String mit den Details des Wahlkreises
*/
@Override
public String toHTML() {
return "<div class='constituency'>"

View file

@ -47,6 +47,12 @@ public class Fraction_File_Impl implements Fraction {
this.name = name;
}
/**
* Gibt eine HTML-Darstellung der Fraktion zurück.
* Die HTML-Ausgabe enthält den Namen sowie den Langnamen der Fraktion.
*
* @return HTML-String mit den Details der Fraktion
*/
@Override
public String toHTML() {
return "<div class='fraction'>"

View file

@ -42,6 +42,12 @@ public class LegislativePeriod_File_Impl implements LegislativePeriod {
return MongoDBEntryType.LEGISLATIVE_PERIOD;
}
/**
* Gibt eine HTML-Darstellung der aktuellen Legislaturperiode zurück.
* Die HTML-Ausgabe enthält Informationen wie ID, Start- und Enddatum sowie den Wahlkreis.
*
* @return HTML-String mit den Details der Legislaturperiode
*/
@Override
public String toHTML() {
return "<div class='legislative-period'>"

View file

@ -37,11 +37,25 @@ public class Line_File_Impl implements Content, Line {
return MongoDBEntryType.SPEECH_LINE;
}
/**
* Konvertiert die aktuelle Zeile (Line) in LaTeX-Format.
* Jede Zeile endet mit einem Zeilenumbruch (`\\`), wie es in LaTeX üblich ist.
*
* @return Ein String im LaTeX-Format
*/
@Override
public String toTeX() {
return this.getContent() + "\\\n";
}
/**
* Wandelt die Zeile (Line) in ein XML-Element um.
* Dabei werden `contentId` und `speechId` als Attribute gesetzt,
* der eigentliche Inhalt wird als Text eingefügt.
*
* @param doc Das übergeordnete XML-Dokument
* @return Ein XML-Element vom Typ <line>
*/
@Override
public Element toXML(Document doc) {
Element line = doc.createElement("line");

View file

@ -70,6 +70,12 @@ public class MemberOfParliament_File_Impl extends Individual_File_Impl implement
return MongoDBEntryType.MEMBER_OF_PARLIAMENT;
}
/**
* Erzeugt eine HTML-Repräsentation eines Abgeordneten mit Basisinformationen,
* Foto und Daten zu den legislativen Perioden.
*
* @return HTML-String zur Darstellung des Abgeordneten
*/
@Override
public String toHTML() {
StringBuilder html = new StringBuilder();

View file

@ -57,6 +57,12 @@ public class Session_File_Impl implements Session {
this.agendaItems.add(item);
}
/**
* Gibt die Session-Informationen als HTML-String zurück.
* Enthält Angaben zur ID, zur Legislaturperiode sowie Start- und Endzeitpunkt der Sitzung.
*
* @return HTML-Darstellung der Sitzung als String
*/
@Override
public String toHTML() {
return "<div class='session'>"

View file

@ -52,11 +52,24 @@ public class Speaker_File_Impl implements Content, Speaker {
return MongoDBEntryType.SPEECH_SPEAKER;
}
/**
* Gibt die Sprecherdaten als TeX-String zurück.
* Der Name des Redners bzw. der Rednerin wird farblich hervorgehoben.
*
* @return TeX-Darstellung des Redners/der Rednerin
*/
@Override
public String toTeX() {
return "\\textcolor{blue}{Redner/Rednerin}: " + this.getSpeakerName() + "\\\n";
}
/**
* Erstellt ein XML-Element mit Informationen zum Redner/zur Rednerin.
* Enthält ID, Name, Fraktion und zugehörige Redeinformationen.
*
* @param doc Das übergeordnete XML-Dokument
* @return XML-Element für den Redner/die Rednerin
*/
@Override
public Element toXML(Document doc) {
Element speaker = doc.createElement("speaker");

View file

@ -83,6 +83,14 @@ public class Speech_File_Impl implements Speech {
//Logger.pink("Added paragraph to speech: " + paragraph);
}
/**
* Gibt die Rede als HTML-Darstellung zurück.
* Die HTML-Ausgabe enthält Informationen zur Sitzung, zum Redner und zur Fraktion.
* <p>
* Hinweis: Es ist geplant, die gesprochenen Textzeilen visuell hervorzuheben (siehe TODO).
*
* @return HTML-String, der die Metadaten der Rede repräsentiert
*/
@Override
public String toHTML() {
//TODO: Implement a logic that highlights the lines that the speaker is saying
@ -105,6 +113,13 @@ public class Speech_File_Impl implements Speech {
return null;
}
/**
* Gibt die gesamte Rede im TeX-Format zurück.
* Der Inhalt der Rede wird Abschnitt für Abschnitt eingebunden,
* zusätzlich wird ein Platzhalter für NLP-Metadaten eingefügt.
*
* @return TeX-Darstellung der Rede inklusive NLP-Platzhalter und Redeinhalten
*/
@Override
public String toTeX() {
StringBuilder tex = new StringBuilder();
@ -124,6 +139,14 @@ public class Speech_File_Impl implements Speech {
return tex.toString();
}
/**
* Erstellt ein XML-Element für die gesamte Rede.
* Es werden alle relevanten Metadaten der Rede sowie die Inhalte
* (z.B. Redner, Textzeilen, Kommentare) als Kindelemente hinzugefügt.
*
* @param doc Das XML-Dokument, zu dem das Element gehört
* @return XML-Element, das die Rede und deren Inhalte beschreibt
*/
@Override
public Element toXML(Document doc) {
Element speech = doc.createElement("speech");

View file

@ -39,6 +39,13 @@ public class SpeechParser {
public void setParseLegislativePeriods(Boolean parseLegislativePeriods) {
}
/**
* Parst alle Sessions aus den globalen XML-Dokumenten, die über PPRUtils.processXML() abgerufen werden.
* Dabei werden auch die zugehörigen Reden und AgendaItems (sowie deren Inhalte) geparst.
* Die temporären Dateien werden nach der Verarbeitung gelöscht.
*
* @return Eine Liste von Session-Objekten, die aus den XML-Dokumenten erstellt wurden.
*/
public List<Session> parseAllSessions() {
List<Session> sessions = new ArrayList<>();
this.speeches = new ArrayList<>();
@ -60,6 +67,15 @@ public class SpeechParser {
}
/**
* Parst alle neuen Sessions aus den übergebenen XML-Dokumenten.
* Diese Methode wird verwendet, wenn bereits ein Set von XML-Dokumenten (z.B. von neuen Protokollen)
* vorliegt. Für jedes XML-Dokument wird eine temporäre Datei erstellt, geparst und anschließend gelöscht.
*
* @param xmlDocuments Ein Set von XML-Dokumenten (als org.w3c.dom.Document),
* die geparst werden sollen.
* @return Eine Liste von Session-Objekten, die aus den XML-Dokumenten erstellt wurden.
*/
public List<Session> parseAllSessions(Set<Document> xmlDocuments) {
List<Session> sessions = new ArrayList<>();
this.speeches = new ArrayList<>();
@ -80,6 +96,21 @@ public class SpeechParser {
}
/**
* Parst eine einzelne Session aus einer XML-Datei.
*
*
* Diese Methode erstellt mithilfe eines DocumentBuilder ein org.w3c.dom.Document aus der übergebenen Datei.
* Anschließend werden die Sitzungsdaten (z.B. Legislaturperiode, Sitzung-Nummer, Datum und Uhrzeit) aus dem Root-Element extrahiert.
* Danach werden die AgendaItems der Session und die zugehörigen Reden (inkl. Redner und Inhalte) geparst.
* Für jeden AgendaItem werden die zugehörigen Reden aus den untergeordneten XML-Elementen ("rede") extrahiert.
* Die geparsten Daten werden in Objekten vom Typ Session_File_Impl und AgendaItem_File_Impl abgelegt.
*
*
* @param file Die temporäre XML-Datei, die die Session-Daten enthält.
* @return Ein Session-Objekt, das aus der XML-Datei erstellt wurde.
* @throws Exception Falls beim Parsen der XML-Datei oder der Konvertierung in ein Session-Objekt ein Fehler auftritt.
*/
private Session parseSessionFile(File file) throws Exception {
//file = removeDoctypeAnnotation(file.getAbsolutePath());
@ -184,6 +215,16 @@ public class SpeechParser {
return session;
}
/**
* Retrieves the text content of the first child element with the specified tag name.
* The method searches for child elements with the given tag name in the provided element.
* If at least one such element is found, its text content is returned after trimming leading and trailing whitespace.
* If no child element with the specified tag name exists, the method returns null.
*
* @param parent the parent XML element in which to search for the child element
* @param tagName the name of the child element whose text content is to be retrieved
* @return the trimmed text content of the first matching child element, or null if no such element is found
*/
private static String getOptionalTextContent(Element parent, String tagName) {
NodeList nodes = parent.getElementsByTagName(tagName);
if (nodes.getLength() > 0) {
@ -196,7 +237,12 @@ public class SpeechParser {
}
/**
* Konvertiert ein org.w3c.dom.Document in eine temporäre Datei.
* Konvertiert ein XML-Dokument (org.w3c.dom.Document) in eine temporäre Datei.
* Diese Datei wird zur weiteren Verarbeitung, z.B. mit XML-Parsern, verwendet.
*
* @param xmlDoc Das zu konvertierende XML-Dokument.
* @return Eine temporäre Datei, die den Inhalt des XML-Dokuments enthält.
* @throws Exception Falls beim Erstellen oder Schreiben der Datei ein Fehler auftritt.
*/
private File convertDocumentToFile(org.w3c.dom.Document xmlDoc) throws Exception {
File tempFile = File.createTempFile("session_", ".xml");

View file

@ -2,5 +2,9 @@
<link rel="stylesheet" href="index.css">
</head>
<#include "header.ftl">
<h1>About</h1>
<h1>Über</h1>
<p>Alle Infos zur Nutzung und die Dokumentation kann auf der <a href="https://ppr.gitlab.texttechnologylab.org/leonkastner/multimodal-parliament-explorer-docu">Doku GitLab Repository eingesehen werden</a></p>
<br>
<br>
<p>PPR WS24/25 - Gruppe_05_1</p>
<#include "footer.ftl">

View file

@ -0,0 +1,52 @@
<head>
<link rel="stylesheet" href="index.css">
<title>Parliament Explorer</title>
</head>
<#include "header.ftl">
<script src="https://d3js.org/d3.v7.min.js"></script>
<body>
<h1>Chart Sortiment</h1>
<h2>NLP Analyse aller Reden</h2>
<div class="chart-container">
<div class="chart">
<#if aggregatedTopics?? && (aggregatedTopics?size gt 0)>
<h3>Topics Information (als Bubble Chart)</h3>
<#assign condenseTopicInformation = aggregatedTopics>
<#include "topicsBubbleChart.ftl">
<#else>
<h3>Keine Topics Information verfügbar</h3>
</#if>
</div>
<div class="chart">
<#if aggregatedPOS?? && (aggregatedPOS?size gt 0)>
<h3>POS Information (als Bar Chart)</h3>
<#assign posList = aggregatedPOS>
<#include "posBarChart.ftl">
<#else>
<h3>Keine POS Information verfügbar</h3>
</#if>
</div>
<div class="chart">
<#if aggregatedSentiment?? && (aggregatedSentiment?size gt 0)>
<h3>Sentiments Information (als Radar Chart)</h3>
<#assign sentiments = [aggregatedSentiment]>
<#include "sentimentsRadarChart.ftl">
<#else>
<h3>Keine Sentiments Information verfügbar</h3>
</#if>
</div>
<div class="chart">
<#if aggregatedNE?? && (aggregatedNE?size gt 0)>
<h3>Named Entities Information (als Sunburst Chart)</h3>
<#assign neMap = aggregatedNE>
<#include "namedEntitiesSunburstChart.ftl">
<#else>
<h3>Keine Named Entities Information verfügbar</h3>
</#if>
</div>
</div>
</body>
<#include "footer.ftl">

Some files were not shown because too many files have changed in this diff Show more