Compare commits

...

14 commits

Author SHA1 Message Date
s5260822
67e117b647 fixed bad merge 2025-03-24 01:01:08 +01:00
Jonas Werner
4db18688b5 Merge branch 'main' of https://ppr.gitlab.texttechnologylab.org/s1188354/multimodal_parliament_explorer_05_1 2025-03-24 00:51:55 +01:00
Leon Kastner
a312b7bef0 Benutzerhandbuch 2025-03-23 23:40:19 +00:00
s5260822
88cef04f49 fixed about page missing 2025-03-24 00:34:47 +01:00
s5260822
5cf33608cf final touches 2025-03-24 00:28:39 +01:00
s5260822
bfb8fb65e8 final Class Diagram 2025-03-24 00:22:09 +01:00
Leon Kastner
e8272742db Mockup Uploaded 2025-03-23 22:56:00 +00:00
Picman2000
7b68a4988b updated diagrams 2025-03-23 23:53:21 +01:00
Picman2000
ac79a35bfb Merge remote-tracking branch 'origin/main' 2025-03-23 23:49:49 +01:00
Picman2000
8bd75e1f73 updated readme 2025-03-23 23:49:34 +01:00
vysitor
2a1127d68c Sped up loading of individual speech list 2025-03-23 23:37:58 +01:00
Jonas Werner
846e130418 added support for speech export on /export page 2025-03-23 17:46:38 +01:00
Jonas Werner
e26bb412aa added export and about page 2025-03-23 17:23:52 +01:00
Jonas Werner
d2e9cf4dba export now opens in new tab 2025-03-23 17:17:06 +01:00
21 changed files with 4207 additions and 3898 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.

View file

@ -154,6 +154,7 @@ 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:

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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Before After
Before After

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

@ -331,6 +331,46 @@ public class MongoPprUtils {
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

View file

@ -42,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!
@ -70,6 +84,34 @@ public class FrontEndController {
ctx.render("parlamentarier.ftl", attributes);
}
@OpenApi(
summary = "Get the export page.",
description = "Get the export page",
operationId = "getExportPage",
path = "/export",
methods = HttpMethod.GET,
tags = {"Export"},
responses = {
@OpenApiResponse(status = "200")
})
public static void getExportPage(Context ctx) {
ctx.render("export.ftl");
}
@OpenApi(
summary = "Get the about page.",
description = "Get the about page",
operationId = "getAboutPage",
path = "/about",
methods = HttpMethod.GET,
tags = {"About"},
responses = {
@OpenApiResponse(status = "200")
})
public static void getAboutPage(Context ctx) {
ctx.render("about.ftl");
}
/**
* Aggregiert für alle Reden die NLPErgebnisse (Topics, POS, Named Entities, erste SentimentObjekte)
* und liefert die zusammengefassten Daten an die ChartsAnsicht.

View file

@ -62,6 +62,8 @@ public class RESTHandler {
app.get("/", FrontEndController::getHomepage);
app.get("/members", FrontEndController::getAllParlamentarier);
app.get("/portfolio/{id}", ParlamentarierController::getParlamentarierDetails);
app.get("/export", FrontEndController::getExportPage);
app.get("/about", FrontEndController::getAboutPage);
// Reden
app.get("/reden/{id}", SpeechController::listSpeeches); // zeige Reden eines Parlamentariers an
@ -74,12 +76,14 @@ public class RESTHandler {
app.get("/charts", FrontEndController::getCharts);
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
app.get("/export/pdf/topic/{topic}", SpeechesLatexExportController::exportSpeechesWithTopic); // exportiere alle Reden zu einem Thema als PDF
app.get("/export/pdf/all", SpeechesLatexExportController::exportAllSpeeches); // exportiere alle Reden als PDF CAUTION!!!: This will take forever but is required in the exercise
app.get("/export/pdf/speeches/{speechIds}", SpeechesLatexExportController::exportSpeeches); // exportiere eine Liste von Reden als PDF
app.get("/export/xml/speech/{id}", SpeechesXMLExportController::exportSpeech); // exportiere eine Rede als XML
app.get("/export/xml/speech", SpeechesXMLExportController::exportSpeech); // exportiere eine Rede als XML
app.get("/export/xml/speaker/{id}", SpeechesXMLExportController::exportSpeechesFromSpeaker); // exportiere alle Reden eines Parlamentariers als XML
app.get("/export/xml/topic/{topic}", SpeechesXMLExportController::exportSpeechesWithTopic); // exportiere alle Reden zu einem Thema als XML
app.get("/export/xml/all", SpeechesXMLExportController::exportAllSpeeches); // exportiere alle Reden als XML

View file

@ -45,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);

View file

@ -30,11 +30,18 @@ public class SpeechesLatexExportController {
@OpenApiResponse(status = "200")
})
public static void exportSpeech(Context ctx) {
String speechId = null;
try {
speechId = ctx.pathParam("id");
} catch (Exception e) {
// check query param
speechId = ctx.queryParam("speechId");
}
byte[] pdfBytes = new byte[0];
try {
pdfBytes = Base64.getDecoder().decode(getExportedSpeechBase64StringBySpeechId(ctx.pathParam("id")));
pdfBytes = Base64.getDecoder().decode(getExportedSpeechBase64StringBySpeechId(speechId));
} catch (Exception e) {
Logger.error("Failed to generate Export of Speech with ID " + ctx.pathParam("id"));
Logger.error("Failed to generate Export of Speech with ID " + speechId);
Logger.error(e.getMessage());
Logger.debug(Arrays.toString(e.getStackTrace()));
}

View file

@ -32,9 +32,16 @@ public class SpeechesXMLExportController {
@OpenApiResponse(status = "200")
})
public static void exportSpeech(Context ctx) {
String speechId = null;
try {
speechId = ctx.pathParam("id");
} catch (Exception e) {
// check query param
speechId = ctx.queryParam("speechId");
}
String xmlContent;
try {
xmlContent = getExportedSpeechById(ctx.pathParam("id"));
xmlContent = getExportedSpeechById(speechId);
ByteArrayInputStream stream = new ByteArrayInputStream(xmlContent.getBytes());
if (stream.available() == 0) {
Logger.error("XML stream is empty.");
@ -45,7 +52,7 @@ public class SpeechesXMLExportController {
ctx.contentType("application/xml");
ctx.result(stream);
} catch (Exception e) {
Logger.error("Failed to generate Export of Speech with ID " + ctx.pathParam("id"));
Logger.error("Failed to generate Export of Speech with ID " + speechId);
Logger.error(e.getMessage());
Logger.debug(Arrays.toString(e.getStackTrace()));
ctx.result("Internal Server Error");

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,43 @@
<head>
<link rel="stylesheet" href="index.css">
<title>Parliament Explorer</title>
</head>
<#include "header.ftl">
<body>
<main>
<h2>Export von Reden</h2>
<p>Export von allen Reden (Dieser Prozess kanne einige Zeit dauern):</p>
<div class="export-button centered-flex-button">
<div class="red-button centered-flex-button">
<a href="/export/pdf/all" target="_blank">PDF Export</a>
</div>
<br>
<div class="red-button centered-flex-button">
<a href="/export/xml/all" target="_blank">XML Export</a>
</div>
<br>
<br>
<p>Export von allen Reden eines Parlamentariers.</p>
</div>
<#assign formAction = "/members">
<#include "filterForm.ftl">
<br>
<br>
<p>Export einer bestimmten Rede nach ID</p>
<div class="filter-form">
<form name="searchForm" action="/export/pdf/speech" method="GET">
<input type="text" name="speechId" placeholder="Reden ID">
<button type="submit">Als PDF Exportieren</button>
</form>
</div>
<div class="filter-form">
<form name="searchForm" action="/export/xml/speech" method="GET">
<input type="text" name="speechId" placeholder="Reden ID">
<button type="submit">Als XML Exportieren</button>
</form>
</div>
</main>
</body>
<#include "footer.ftl">

View file

@ -36,11 +36,11 @@
<h2>Reden Export von allen Reden von ${p.vorname} ${p.nachname}</h2>
<div class="export-button centered-flex-button">
<div class="red-button centered-flex-button">
<a href="/export/pdf/speaker/${p.id}">PDF Export</a>
<a href="/export/pdf/speaker/${p.id}" target="_blank">PDF Export</a>
</div>
<br>
<div class="red-button centered-flex-button">
<a href="/export/xml/speaker/${p.id}">XML Export</a>
<a href="/export/xml/speaker/${p.id}" target="_blank">XML Export</a>
</div>
</div>

View file

@ -32,7 +32,7 @@
<#list speechesMetaDataList as redeMd>
<tr>
<td>${redeMd.dateTimeString}</td>
<td><a href="/reden/${p.id}/${redeMd.speechKey}">${redeMd.agendaTitle}</a></td>
<td><a href="/reden/${p.id}/${redeMd.speechKey}">${redeMd.agendaTitel}</a></td>
</tr>
</#list>

View file

@ -51,11 +51,11 @@
<h2>Reden Export von dieser Rede</h2>
<div class="export-button centered-flex-button">
<div class="red-button centered-flex-button">
<a href="/export/pdf/speech/${s.speechKey}">PDF Export</a>
<a href="/export/pdf/speech/${s.speechKey}" target="_blank">PDF Export</a>
</div>
<br>
<div class="red-button centered-flex-button">
<a href="/export/xml/speech/${s.speechKey}">XML Export</a>
<a href="/export/xml/speech/${s.speechKey}" target="_blank">XML Export</a>
</div>
</div>