Frontend: List of Speeches is now displayed

This commit is contained in:
vysitor 2025-03-10 18:15:55 +01:00
parent c64a280405
commit 8439b52f65
8 changed files with 407 additions and 13 deletions

View file

@ -1,14 +1,22 @@
package org.texttechnologylab.project.gruppe_05_1.database;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
import org.texttechnologylab.project.gruppe_05_1.database.domainimp.speeches.Speech_MongoDB_Impl;
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.speaker.Membership;
import org.texttechnologylab.project.gruppe_05_1.domain.speech.SpeechMetaData;
import org.texttechnologylab.project.gruppe_05_1.util.GeneralUtils;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Speech_File_Impl;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
@ -32,6 +40,8 @@ public class MongoPprUtils {
private static MongoCollection<Document> speakerCollection = null;
private static MongoCollection<Document> speechCollection = null;
private static MongoCollection<Document> sessionCollection = null;
private static MongoCollection<Document> agendaItemsCollection = null;
private static MongoCollection<Document> picturesCollection = null;
private static MongoCollection<Document> commentCollection = null;
@ -45,6 +55,15 @@ public class MongoPprUtils {
return speechCollection;
}
public static MongoCollection<Document> getSessionCollection() {
if (sessionCollection == null) sessionCollection = MongoDBHandler.getMongoDatabase().getCollection(SESSION_COLLECTION_NAME);
return sessionCollection;
}
public static MongoCollection<Document> getAgendaItemsCollection() {
if (agendaItemsCollection == null) agendaItemsCollection = MongoDBHandler.getMongoDatabase().getCollection(AGENDA_ITEMS_COLLECTION_NAME);
return agendaItemsCollection;
}
/**
* Create the Speaker Collection and useful indices for it
@ -259,4 +278,114 @@ public class MongoPprUtils {
}
// TODO: kopiere die Speech-Sachen von Übung 4 hierher!
/**
* Aufzählen, wie viele Reden eines bestimmten Redners gespeichert sind
* @param speakerId
* @return Anzahl Reden
*/
public static Long countSpeechesOfSpeaker(String speakerId) {
return getSpeechCollection().countDocuments(new Document("speakerId", Integer.parseInt(speakerId)));
}
/**
* Liefert alle Reden eines Redners zurück
* @param speakerId
* @return Alle Reden eines Redners
*/
public static List<Speech> getSpeechesOfSpeaker(String speakerId) {
List<Speech> speeches = new ArrayList<>();
Document filter = new Document("speakerId", Integer.parseInt(speakerId));
List<Document> docs = getSpeechCollection().find(filter).into(new ArrayList<>());
for (Document doc : docs) {
speeches.add(new Speech_MongoDB_Impl(doc));
}
return speeches;
}
/**
* Liefert Metadaten (aber keine Inhalte!) für alle Reden eines Redners zurück.
* Als Metadaten zählen das Datum, Agenda-ID etc.
* @param speakerId
* @return
*/
public static List<SpeechMetaData> getSpeechesMetadataForSeaker(String speakerId) {
List<SpeechMetaData> speechMetaDataList = new ArrayList<>();
List<Speech> speeches = MongoPprUtils.getSpeechesOfSpeaker(speakerId);
for (Speech speech : speeches) {
SpeechMetaData md = new SpeechMetaData();
md.setSpeechKey(speech.getSpeechKey());
md.setSpeechId(speech.getSpeechId());
md.setSpeakerId(speech.getSpeakerId());
int sessionId = speech.getSessionId();
md.setSessionId(sessionId);
// aus "sessions" Collection
String dateTimeString = getSessionDateTime(sessionId);
if (dateTimeString != null) {
md.setDateTimeString(dateTimeString);
LocalDateTime tmp = GeneralUtils.parseDateTime(dateTimeString, "dd.MM.yyyy HH:mm");
if (tmp == null) {
tmp = GeneralUtils.parseDateTime(dateTimeString, "dd.MM.yyyy H:mm");
if (tmp == null) {
System.out.println(dateTimeString + " could not be parsed");
}
}
md.setDateTime(tmp);
}
// aus "agendaItems" Collection
int agendaItemId = speech.getAgendaItemId();
String agendaTitel = getAgendaTitle(sessionId, agendaItemId);
md.setAgendaTitle(agendaTitel);
speechMetaDataList.add(md);
}
// Sortiere nach Datum, absteigend
speechMetaDataList.sort((md1, md2) -> {
return md2.getDateTime().compareTo(md1.getDateTime());
});
return speechMetaDataList;
}
/**
* Liefert das Datum und die Uhrzeit einer Sitzung zurück
* @param sessionId
* @return
*/
public static String getSessionDateTime(int sessionId) {
Document filter = new Document("sessionId", sessionId);
Object dateTime = getSessionCollection().find(filter).first().get("dateTime");
if (dateTime == null) {
return null;
} else {
return (String) dateTime;
}
}
/**
* Liefert den Agenda-Titel zurück
* @param sessionId
* @return
*/
public static String getAgendaTitle(int sessionId, int agendaItemId) {
Document filter = new Document("sessionId", sessionId).append("id", agendaItemId);
FindIterable<Document> iter = getAgendaItemsCollection().find(filter);
if ((iter == null || (iter.first() == null))) {
return "(kein Agendatitel)";
} else {
return (String) iter.first().get("title");
}
}
}

View file

@ -0,0 +1,104 @@
package org.texttechnologylab.project.gruppe_05_1.domain.speech;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.StringJoiner;
/**
* 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 speakerId;
int sessionId; // TODO: nötig?
// aus "sessions" Collection
LocalDateTime dateTime;
String dateTimeString;
// aus "agendaItems" Collection
String agendaTitle;
public String getSpeechKey() {
return speechKey;
}
public void setSpeechKey(String speechKey) {
this.speechKey = speechKey;
}
public int getSpeechId() {
return speechId;
}
public void setSpeechId(int speechId) {
this.speechId = speechId;
}
public int getSpeakerId() {
return speakerId;
}
public void setSpeakerId(int speakerId) {
this.speakerId = speakerId;
}
public int getSessionId() {
return sessionId;
}
public void setSessionId(int sessionId) {
this.sessionId = sessionId;
}
public LocalDateTime getDateTime() {
return dateTime;
}
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
public String getDateTimeString() {
return dateTimeString;
}
public void setDateTimeString(String dateTimeString) {
this.dateTimeString = dateTimeString;
}
public String getAgendaTitle() {
return agendaTitle;
}
public void setAgendaTitle(String agendaTitle) {
this.agendaTitle = agendaTitle;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SpeechMetaData that)) return false;
return speechId == that.speechId && speakerId == that.speakerId && sessionId == that.sessionId && Objects.equals(speechKey, that.speechKey) && Objects.equals(dateTime, that.dateTime) && Objects.equals(dateTimeString, that.dateTimeString) && Objects.equals(agendaTitle, that.agendaTitle);
}
@Override
public int hashCode() {
return Objects.hash(speechKey, speechId, speakerId, sessionId, dateTime, dateTimeString, agendaTitle);
}
@Override
public String toString() {
return new StringJoiner(", ", SpeechMetaData.class.getSimpleName() + "[", "]")
.add("speechKey='" + speechKey + "'")
.add("speechId=" + speechId)
.add("speakerId=" + speakerId)
.add("sessionId=" + sessionId)
.add("dateTime=" + dateTime)
.add("dateTimeString='" + dateTimeString + "'")
.add("agendaTitle='" + agendaTitle + "'")
.toString();
}
}

View file

@ -6,7 +6,9 @@ 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.database.MongoPprUtils;
import org.texttechnologylab.project.gruppe_05_1.util.PPRUtils;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -31,6 +33,8 @@ public class ParlamentarierController {
* @param ctx JavaLin-Context
*/
private static List<Integer> emptyList = new ArrayList<>(); // passed as an indicator if a certain list exists or not (in latter case: pass null)
@OpenApi(
summary = "Get alle Parlamentarier. Man kann nach Vor-, Nachname oder Partei filtern.",
description = "Listet alle Parlamentarier bzw. diejenige, welche den Filter entsprechen",
@ -88,7 +92,16 @@ public class ParlamentarierController {
Map<String, Object> attributes = new HashMap<>();
attributes.put("p", pd);
attributes.put("speechesCount", pd.getReden() == null ? 0 : pd.getReden().size());
Long speechCount = MongoPprUtils.countSpeechesOfSpeaker(pd.getId());
attributes.put("speechesCount", speechCount);
if (speechCount == 0) {
attributes.put("speechesPlaceholder", null);
} else {
attributes.put("speechesPlaceholder", emptyList);
}
List<Speech> speeches = MongoPprUtils.getSpeechesOfSpeaker(pd.getId());
ctx.render("parlamentarierDetails.ftl", attributes);
}

View file

@ -57,17 +57,9 @@ public class RESTHandler {
app.get("/portfolio/{id}", ParlamentarierController::getParlamentarierDetails);
app.delete("/deleteParlamentarier", ParlamentarierController::deleteAllParlamentarier);
/* - TODO
// Fotos
app.get("/editFoto/{id}", FotosController::editFotos);
app.post("/editFoto/updateFotoUrl/{id}/{pictureId}", FotosController::updateFotoUrl);
app.post("/editFoto/defineFotoAsPrimary/{id}", FotosController::defineFotoAsPrimary);
// Reden
app.get("/reden/{id}", RedenController::listSpeeches); // zeige Reden eines Parlamentariers an
app.get("/reden/{id}/{redeId}", RedenController::showSpeech); // zeige eine bestimmte Rede des Parlamentariers an
app.post("/reden/updateComment/{id}/{speechId}/{commentId}", RedenController::updateComment); // aktualisiere Änderung an einer Kommentar
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

@ -0,0 +1,105 @@
package org.texttechnologylab.project.gruppe_05_1.rest;
import io.javalin.http.Context;
import io.javalin.openapi.*;
import org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils;
import org.texttechnologylab.project.gruppe_05_1.domain.html.ParlamentarierDetails;
import org.texttechnologylab.project.gruppe_05_1.domain.speech.Protocol;
import org.texttechnologylab.project.gruppe_05_1.domain.speech.SpeechMetaData;
import org.texttechnologylab.project.gruppe_05_1.util.GeneralUtils;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Comment;
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SpeechController {
/**
* Liste alle Reden eines Parlamentariers an
* @param ctx Javalin Context
*/
@OpenApi(
summary = "Liste alle Reden eines Parlamentariers an",
description = "Liste alle Reden eines Parlamentariers an",
operationId = "listSpeeches",
path = "/reden/{id}",
methods = HttpMethod.GET,
tags = {"Rede"},
pathParams = {
@OpenApiParam(name = "id", description = "id des Parlamentariers", required = true),
},
responses = {
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = Speech[].class)})
})
public static void listSpeeches(Context ctx) {
String parlamentarierId = ctx.pathParam("id");
ParlamentarierDetails p = MongoPprUtils.getParlamentarierDetailsByID(parlamentarierId);
List<SpeechMetaData> speechMetaDataList = MongoPprUtils.getSpeechesMetadataForSeaker(parlamentarierId);
Map<String, Object> attributes = new HashMap<>();
attributes.put("p", p);
attributes.put("speechesMetaDataList", speechMetaDataList);
ctx.render("showSpeechesList.ftl", attributes);
}
/**
* Zeige eine bestimmte Rede des Parlamentariers an
* @param ctx Javalin Context
*/
@OpenApi(
summary = "Zeige eine bestimmte Rede des Parlamentariers an",
description = "Zeige eine bestimmte Rede des Parlamentariers an",
operationId = "showSpeech",
path = "/reden/{id}/{redeID}",
methods = HttpMethod.GET,
tags = {"Rede"},
pathParams = {
@OpenApiParam(name = "id", description = "id des Parlamentariers", required = true),
@OpenApiParam(name = "redeId", description = "id der Rede", required = true),
},
responses = {
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = Speech.class)})
})
public static void showSpeech(Context ctx) {
String parlamentarierId = ctx.pathParam("id");
String redeId = ctx.pathParam("redeId");
Map<String, Object> attributes = new HashMap<>();
/*
Speech rede = MongoPprUtils.getSpeechesById(redeId);
List<Comment> comments = MongoPprUtils.getCommentsForSpeech(redeId);
ParlamentarierDetails p = MongoPprUtils.getParlamentarierDetailsByID(parlamentarierId);
attributes.put("p", p);
attributes.put("rede", rede);
attributes.put("comments", comments.size() > 0 ? comments : null);
Protocol protocol = rede.getProtocol();
if (protocol == null) {
attributes.put("date", "(keine Angaben)");
attributes.put("time", "(keine Angaben)");
} else {
LocalDate date = protocol.getDate();
LocalTime time = protocol.getStarttime();
if (date == null) {
attributes.put("date", "(keine Angaben)");
} else {
attributes.put("date", GeneralUtils.formatDate(date));
}
if (time == null) {
attributes.put("time", "(keine Angaben)");
} else {
attributes.put("time", GeneralUtils.formatTime(time));
}
}
*/
ctx.render("speechWithComments.ftl", attributes);
}
}

View file

@ -1,6 +1,7 @@
package org.texttechnologylab.project.gruppe_05_1.util;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@ -74,4 +75,11 @@ public abstract class GeneralUtils {
return time.format(DateTimeFormatter.ofPattern("HH.mm"));
}
public static LocalDateTime parseDateTime(String dateTime, String timeFormat) {
try {
return LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern(timeFormat));
} catch (DateTimeParseException ex) {
return null;
}
}
}

View file

@ -26,10 +26,10 @@
<main>
<section>
<h2>Reden</h2>
<#if p.reden??>
<#if speechesPlaceholder??>
<a href="/reden/${p.id}">${speechesCount} Reden vorhanden</a>
<#else>
Keine Reden
Keine Reden vorhanden
</#if>
<br> <br>
</section>

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reden von ${p.vorname} ${p.nachname} (${p.partei})</title>
<style type="text/css">
th, td {
padding: 12px;
text-align: center; /* Center-aligns both header and data cells */
border: 1px solid #ddd;
}
</style>
</head>
<body>
<header>
<h1>Reden von ${p.vorname} ${p.nachname} (${p.partei})</h1>
</header>
<main>
<section>
<table>
<thead>
<tr>
<th>Datum</th>
<th>Sitzung / Agenda</th>
</tr>
</thead>
<tbody>
<#list speechesMetaDataList as redeMd>
<tr>
<td>${redeMd.dateTimeString}</td>
<td><a href="/reden/${p.id}/${redeMd.speechKey}">${redeMd.sessionId} / ${redeMd.agendaTitle}</a></td>
</tr>
</#list>
</tbody>
</table>
</section>
</main>
<body>