Modified Bubble Chart, Added Images to Speech, Added Speeches Tab, modified filter, loading of speeches for individual politicians is quicker
This commit is contained in:
parent
253a6ed78b
commit
4f3ee4ef51
25 changed files with 377 additions and 139 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
BIN
src/.DS_Store
vendored
BIN
src/.DS_Store
vendored
Binary file not shown.
BIN
src/main/.DS_Store
vendored
BIN
src/main/.DS_Store
vendored
Binary file not shown.
BIN
src/main/java/.DS_Store
vendored
BIN
src/main/java/.DS_Store
vendored
Binary file not shown.
BIN
src/main/java/org/.DS_Store
vendored
BIN
src/main/java/org/.DS_Store
vendored
Binary file not shown.
BIN
src/main/java/org/texttechnologylab/.DS_Store
vendored
BIN
src/main/java/org/texttechnologylab/.DS_Store
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -621,7 +621,7 @@ public class MongoDBHandler {
|
|||
List<Document> speeches = speechesCollection.find().into(new ArrayList<>());
|
||||
List<Speech> result = new ArrayList<>();
|
||||
for (Document speech : speeches) {
|
||||
result.add(new Speech_MongoDB_Impl(speech));
|
||||
result.add(new Speech_MongoDB_Impl(speech, true));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -631,7 +631,7 @@ public class MongoDBHandler {
|
|||
List<Document> speeches = speechesCollection.find(filter).into(new ArrayList<>());
|
||||
List<Speech> result = new ArrayList<>();
|
||||
for (Document speech : speeches) {
|
||||
result.add(new Speech_MongoDB_Impl(speech));
|
||||
result.add(new Speech_MongoDB_Impl(speech, true));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -18,6 +18,7 @@ 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.util.Logger;
|
||||
import org.texttechnologylab.project.gruppe_05_1.util.PPRUtils;
|
||||
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -421,7 +422,43 @@ public class MongoPprUtils {
|
|||
List<Document> docs = getSpeechCollection().find(filter).into(new ArrayList<>());
|
||||
|
||||
for (Document doc : docs) {
|
||||
speeches.add(new Speech_MongoDB_Impl(doc));
|
||||
speeches.add(new Speech_MongoDB_Impl(doc, true));
|
||||
}
|
||||
|
||||
return speeches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert alle Reden zurück
|
||||
* Die Auswahl kann durch einen (textuellen) Filter eingeschränkt werden
|
||||
* @param filter
|
||||
* @return
|
||||
*/
|
||||
public static List<Speech> getSpeeches(String filter) {
|
||||
List<Speech> speeches = new ArrayList<>();
|
||||
|
||||
MongoCursor<Document> cursor;
|
||||
|
||||
if (filter== null || filter.isBlank()) {
|
||||
cursor = getSpeechCollection().find().iterator();
|
||||
} else {
|
||||
String pattern = ".*" + filter + ".*";
|
||||
Document searchDocument = new Document("$or", List.of(
|
||||
new Document("speakerName", new Document("$regex", pattern).append("$options", "i")),
|
||||
new Document("fraction", new Document("$regex", pattern).append("$options", "i")),
|
||||
new Document("speechKey", new Document("$regex", pattern).append("$options", "i"))
|
||||
));
|
||||
cursor = getSpeechCollection().find(searchDocument).cursor();
|
||||
}
|
||||
|
||||
try {
|
||||
while (cursor.hasNext()) {
|
||||
speeches.add(new Speech_MongoDB_Impl(cursor.next(), false));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Logger.error(String.valueOf(t));
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return speeches;
|
||||
|
@ -448,18 +485,7 @@ public class MongoPprUtils {
|
|||
|
||||
|
||||
// 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) {
|
||||
Logger.error(dateTimeString + " could not be parsed");
|
||||
}
|
||||
}
|
||||
md.setDateTime(tmp);
|
||||
}
|
||||
augmentSpeechMetaDataFromSession(sessionId, md);
|
||||
|
||||
// aus "agendaItems" Collection
|
||||
int agendaItemId = speech.getAgendaItemId();
|
||||
|
@ -472,16 +498,84 @@ public class MongoPprUtils {
|
|||
|
||||
// Sortiere nach Datum, absteigend
|
||||
speechMetaDataList.sort((md1, md2) -> {
|
||||
try {
|
||||
return md2.getDateTime().compareTo(md1.getDateTime());
|
||||
} catch (NullPointerException e) {
|
||||
return 0;
|
||||
}
|
||||
if ((md2.getDateTime() == null) && (md1.getDateTime()) == null) return 0;
|
||||
if (md2.getDateTime() == null) return -1;
|
||||
if (md1.getDateTime() == null) return 1;
|
||||
return md2.getDateTime().compareTo(md1.getDateTime());
|
||||
});
|
||||
|
||||
return speechMetaDataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param filter
|
||||
* @return
|
||||
*/
|
||||
public static List<SpeechMetaData> getSpeechesMetadata(String filter) {
|
||||
|
||||
List<SpeechMetaData> speechMetaDataList = new ArrayList<>();
|
||||
List<Speech> speeches = MongoPprUtils.getSpeeches(filter);
|
||||
for (Speech speech : speeches) {
|
||||
SpeechMetaData md = new SpeechMetaData();
|
||||
|
||||
md.setSpeechKey(speech.getSpeechKey());
|
||||
md.setSpeechId(speech.getSpeechId());
|
||||
md.setSpeakerId(speech.getSpeakerId());
|
||||
md.setSpeakerName(speech.getSpeakerName());
|
||||
md.setFraktion(speech.getFraction());
|
||||
if (md.getFraktion() == null) {md.setFraktion(PPRUtils.PARTEILOS_KUERZEL);}
|
||||
int sessionId = speech.getSessionId();
|
||||
md.setSessionId(sessionId);
|
||||
|
||||
// aus "sessions" Collection
|
||||
augmentSpeechMetaDataFromSession(sessionId, md);
|
||||
|
||||
// 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) -> {
|
||||
if ((md2.getDateTime() == null) && (md1.getDateTime()) == null) return 0;
|
||||
if (md2.getDateTime() == null) return -1;
|
||||
if (md1.getDateTime() == null) return 1;
|
||||
return md2.getDateTime().compareTo(md1.getDateTime());
|
||||
});
|
||||
|
||||
return speechMetaDataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param md
|
||||
*/
|
||||
public static void augmentSpeechMetaDataFromSession(int sessionId, SpeechMetaData md) {
|
||||
String dateTimeString = getSessionDateTime(sessionId);
|
||||
if (dateTimeString != null) {
|
||||
md.setDateTimeString(dateTimeString);
|
||||
|
||||
for (String format : Arrays.asList("dd.MM.yyyy HH:mm",
|
||||
"dd.MM.yyyy H:mm",
|
||||
"dd.MM.yyyy HH.mm",
|
||||
"dd.MM.yyyy H.mm")) {
|
||||
LocalDateTime tmp = GeneralUtils.parseDateTime(dateTimeString,format);
|
||||
if (tmp != null) {
|
||||
md.setDateTime(tmp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Logger.error(dateTimeString + " could not be parsed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert das Datum und die Uhrzeit einer Sitzung zurück
|
||||
|
@ -522,11 +616,14 @@ public class MongoPprUtils {
|
|||
* @return
|
||||
*/
|
||||
public static HtmlSpeech getSpeechByKey(String key) {
|
||||
System.out.println(key);
|
||||
System.out.println(key); // TODO: remove when no longer needed
|
||||
Document filter = new Document("speechKey", key);
|
||||
Document speechDoc = getSpeechCollection().find(filter).first();
|
||||
System.out.println(getSpeechCollection().find().filter(Filters.eq("speechKey", key)).first());
|
||||
System.out.println("SpeechDoc "+ speechDoc);
|
||||
System.out.println(getSpeechCollection().find().filter(Filters.eq("speechKey", key)).first()); // TODO: remove when no longer needed
|
||||
System.out.println("SpeechDoc "+ speechDoc); // TODO: remove when no longer needed
|
||||
if (speechDoc == null) {
|
||||
Logger.error("Rede " + key + " nicht gefunden");
|
||||
}
|
||||
|
||||
return new HtmlSpeech(speechDoc);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech;
|
|||
import java.util.List;
|
||||
|
||||
public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech {
|
||||
public Speech_MongoDB_Impl(Document mongoDocument) {
|
||||
public Speech_MongoDB_Impl(Document mongoDocument, boolean includeContent) {
|
||||
super(
|
||||
mongoDocument.getInteger("sessionId"),
|
||||
mongoDocument.getInteger("agendaItemId"),
|
||||
|
@ -24,23 +24,23 @@ public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech {
|
|||
mongoDocument.getString("speechKey")
|
||||
);
|
||||
|
||||
|
||||
for (Document content : (List<Document>) mongoDocument.get("speechContents")) {
|
||||
switch (content.getString("type")) {
|
||||
case "line":
|
||||
this.addContent(new Line_MongoDB_Impl(content));
|
||||
break;
|
||||
case "comment":
|
||||
this.addContent(new Comment_MongoDB_Impl(content));
|
||||
break;
|
||||
case "speaker":
|
||||
this.addContent(new Speaker_MongoDB_Impl(content));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown content type: " + content.getString("type"));
|
||||
if (includeContent) {
|
||||
for (Document content : (List<Document>) mongoDocument.get("speechContents")) {
|
||||
switch (content.getString("type")) {
|
||||
case "line":
|
||||
this.addContent(new Line_MongoDB_Impl(content));
|
||||
break;
|
||||
case "comment":
|
||||
this.addContent(new Comment_MongoDB_Impl(content));
|
||||
break;
|
||||
case "speaker":
|
||||
this.addContent(new Speaker_MongoDB_Impl(content));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown content type: " + content.getString("type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getFullText() {
|
||||
|
|
|
@ -2,9 +2,11 @@ package org.texttechnologylab.project.gruppe_05_1.domain.html;
|
|||
|
||||
import org.bson.Document;
|
||||
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.nlp.NlpInfo;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -15,6 +17,8 @@ public class HtmlSpeech {
|
|||
String speechKey;
|
||||
String speakerName;
|
||||
String fraction;
|
||||
String dateTimeString; // aus "sessions" Collection
|
||||
String agendaTitle; // aus "agendaItems" Collection
|
||||
List<SpeechContent> content = new ArrayList<>();
|
||||
NlpInfo nlp = null;
|
||||
|
||||
|
@ -35,6 +39,18 @@ public class HtmlSpeech {
|
|||
}
|
||||
}
|
||||
|
||||
// ergänzen um Datum, Uhrzeit und Agendapunkt der Rede
|
||||
SpeechMetaData md = new SpeechMetaData();
|
||||
int sessionId = doc.getInteger("sessionId");
|
||||
md.setSessionId(sessionId);
|
||||
MongoPprUtils.augmentSpeechMetaDataFromSession(sessionId, md);
|
||||
dateTimeString = md.getDateTimeString();
|
||||
|
||||
int agendaItemId = doc.getInteger("agendaItemId");
|
||||
String title = MongoPprUtils.getAgendaTitle(sessionId, agendaItemId);
|
||||
agendaTitle = title;
|
||||
|
||||
// Ergänzung um NLP-Informationen
|
||||
Document nlpDoc = (Document) doc.get("analysisResults");
|
||||
nlp = readNlpInfo(nlpDoc);
|
||||
}
|
||||
|
@ -86,6 +102,13 @@ public class HtmlSpeech {
|
|||
public void setFraction(String fraction) {
|
||||
this.fraction = fraction;
|
||||
}
|
||||
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;}
|
||||
|
||||
public List<SpeechContent> getContent() {
|
||||
return content;
|
||||
|
@ -112,13 +135,14 @@ public class HtmlSpeech {
|
|||
if (this == o) return true;
|
||||
if (!(o instanceof HtmlSpeech that)) return false;
|
||||
return Objects.equals(speechKey, that.speechKey) && Objects.equals(speakerName, that.speakerName)
|
||||
&& Objects.equals(fraction, that.fraction) && Objects.equals(content, that.content)
|
||||
&& Objects.equals(fraction, that.fraction) && Objects.equals(dateTimeString, that.dateTimeString)
|
||||
&& Objects.equals(agendaTitle, that.agendaTitle) && Objects.equals(content, that.content)
|
||||
&& Objects.equals(nlp, that.nlp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(speechKey, speakerName, fraction, content, nlp);
|
||||
return Objects.hash(speechKey, speakerName, fraction, dateTimeString, agendaTitle, content, nlp);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -127,6 +151,8 @@ public class HtmlSpeech {
|
|||
.add("speechKey='" + speechKey + "'")
|
||||
.add("speakerName='" + speakerName + "'")
|
||||
.add("fraction='" + fraction + "'")
|
||||
.add("dateTimeString='" + dateTimeString + "'")
|
||||
.add("agendaTitle='" + agendaTitle + "'")
|
||||
.add("content=" + content)
|
||||
.add("nlp=" + nlp)
|
||||
.toString();
|
||||
|
|
|
@ -12,6 +12,8 @@ public class SpeechMetaData {
|
|||
String speechKey; // z.B. "ID2011400300"
|
||||
int speechId; // TODO: nötig?
|
||||
int speakerId;
|
||||
String speakerName;
|
||||
String fraktion;
|
||||
int sessionId; // TODO: nötig?
|
||||
|
||||
// aus "sessions" Collection
|
||||
|
@ -45,6 +47,14 @@ public class SpeechMetaData {
|
|||
this.speakerId = speakerId;
|
||||
}
|
||||
|
||||
public String getSpeakerName() {return speakerName;}
|
||||
|
||||
public void setSpeakerName(String speakerName) {this.speakerName = speakerName;}
|
||||
|
||||
public String getFraktion() {return fraktion;}
|
||||
|
||||
public void setFraktion(String fraktion) {this.fraktion = fraktion;}
|
||||
|
||||
public int getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
@ -81,12 +91,15 @@ public class SpeechMetaData {
|
|||
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);
|
||||
return speechId == that.speechId && speakerId == that.speakerId && sessionId == that.sessionId
|
||||
&& Objects.equals(speechKey, that.speechKey) && Objects.equals(speakerName, that.speakerName)
|
||||
&& Objects.equals(fraktion, that.fraktion) && 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);
|
||||
return Objects.hash(speechKey, speechId, speakerId, speakerName, fraktion, sessionId, dateTime, dateTimeString, agendaTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,6 +108,8 @@ public class SpeechMetaData {
|
|||
.add("speechKey='" + speechKey + "'")
|
||||
.add("speechId=" + speechId)
|
||||
.add("speakerId=" + speakerId)
|
||||
.add("speakerName='" + speakerName + "'")
|
||||
.add("fraktion='" + fraktion + "'")
|
||||
.add("sessionId=" + sessionId)
|
||||
.add("dateTime=" + dateTime)
|
||||
.add("dateTimeString='" + dateTimeString + "'")
|
||||
|
|
|
@ -109,30 +109,4 @@ public class ParlamentarierController {
|
|||
ctx.render("parlamentarierDetails.ftl", attributes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lösche alle Abgeordnete.
|
||||
* @param ctx JavaLin-Context
|
||||
*/
|
||||
|
||||
@OpenApi(
|
||||
summary = "Lösche alle Parlamentarier",
|
||||
description = "Lösche alle Parlamentarier aus der Datenbank",
|
||||
operationId = "deleteAllParlamentarier",
|
||||
path = "/deleteParlamentarier",
|
||||
methods = HttpMethod.DELETE,
|
||||
tags = {"Parlamentarier"},
|
||||
responses = {
|
||||
@OpenApiResponse(status = "204", content = {@OpenApiContent(from = Parlamentarier[].class)})
|
||||
})
|
||||
public static void deleteAllParlamentarier(Context ctx) {
|
||||
|
||||
MongoPprUtils.truncateSpeakerCollection();
|
||||
List<Parlamentarier> parlamentarier = MongoPprUtils.getAllParlamentarier("");
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("parlamentarier", parlamentarier);
|
||||
attributes.put("filter", "filter");
|
||||
ctx.render("parlamentarier.ftl", attributes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,11 +58,12 @@ public class RESTHandler {
|
|||
// Parlamentarier
|
||||
app.get("/", FrontEndController::getHomepage);
|
||||
app.get("/members", FrontEndController::getAllParlamentarier);
|
||||
app.get("/portfolio/{id}", FrontEndController::getParlamentarierDetails);
|
||||
app.delete("/deleteParlamentarier", ParlamentarierController::deleteAllParlamentarier);
|
||||
app.get("/portfolio/{id}", ParlamentarierController::getParlamentarierDetails);
|
||||
|
||||
// Reden
|
||||
app.get("/reden/{id}", FrontEndController::listSpeeches); // zeige Reden eines Parlamentariers an
|
||||
app.get("/reden/{id}/{redeId}", FrontEndController::showSpeech); // zeige eine bestimmte Rede des 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,6 @@ public class RESTHandlerOld {
|
|||
// Parlamentarier
|
||||
app.get("/", ParlamentarierController::getAllParlamentarier);
|
||||
app.get("/portfolio/{id}", ParlamentarierController::getParlamentarierDetails);
|
||||
app.delete("/deleteParlamentarier", ParlamentarierController::deleteAllParlamentarier);
|
||||
|
||||
// Reden
|
||||
app.get("/reden/{id}", SpeechController::listSpeeches); // zeige Reden eines Parlamentariers an
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.texttechnologylab.project.gruppe_05_1.rest;
|
||||
|
||||
import de.tudarmstadt.ukp.dkpro.core.api.syntax.type.constituent.S;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.openapi.*;
|
||||
import org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils;
|
||||
|
@ -8,14 +9,14 @@ import org.texttechnologylab.project.gruppe_05_1.domain.html.ParlamentarierDetai
|
|||
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.xml.speeches.Interfaces.Speech;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.texttechnologylab.project.gruppe_05_1.util.PPRUtils.listFractionsFromMembers;
|
||||
|
||||
public class SpeechController {
|
||||
/**
|
||||
* Liste alle Reden eines Parlamentariers an
|
||||
|
@ -65,6 +66,7 @@ public class SpeechController {
|
|||
@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<>();
|
||||
|
@ -72,6 +74,10 @@ public class SpeechController {
|
|||
HtmlSpeech speech = MongoPprUtils.getSpeechByKey(redeId);
|
||||
attributes.put("s", speech);
|
||||
|
||||
// Foto des Abgeordnetes
|
||||
String picture = MongoPprUtils.getParlamentarierPictureByID(parlamentarierId);
|
||||
attributes.put("picture", picture);
|
||||
|
||||
// NLP: Topic
|
||||
if ((speech.getNlp() != null) && (speech.getNlp().getTopics() != null)) {
|
||||
Map<String, Double> topics = Topic.condenseTopicInformation(speech.getNlp().getTopics()); // Daten "verdichten"...
|
||||
|
@ -107,4 +113,39 @@ public class SpeechController {
|
|||
ctx.render("speech.ftl", attributes);
|
||||
}
|
||||
|
||||
@OpenApi(
|
||||
summary = "Liste alle Reden (Filtern ist möglich)",
|
||||
description = "Liste alle Reden. Man kann nach Freitext (MdB Name, Partei/Fraktion) oder nach Thema (Topic) filtern",
|
||||
operationId = "listAllSpeeches",
|
||||
path = "/reden",
|
||||
methods = HttpMethod.GET,
|
||||
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<SpeechMetaData> speechMetaDataList = MongoPprUtils.getSpeechesMetadata(filter);
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("speechesMetaDataList", speechMetaDataList);
|
||||
|
||||
// Filtern nach Text
|
||||
attributes.put("filter", filter == null || filter.isBlank() ? null : filter);
|
||||
|
||||
// Filtern nach Partei/Fraktion
|
||||
attributes.put("parties", listFractionsFromMembers(MongoPprUtils.getAllParlamentarier("")));
|
||||
|
||||
// Filtern nach Topics - TODO
|
||||
List<String> topics = Arrays.asList("International", "Government", "Labor", "Economy", "Public");
|
||||
attributes.put("topics", topics);
|
||||
|
||||
ctx.render("showAllSpeechesList.ftl", attributes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,6 +126,7 @@ th, td {
|
|||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th {
|
||||
|
|
|
@ -1,14 +1,29 @@
|
|||
<div class="filter-form">
|
||||
<form method="GET" action="/members">
|
||||
<form name="searchForm" action="${formAction}" method="GET">
|
||||
|
||||
<input type="text" name="firstName" placeholder="First Name">
|
||||
<input type="text" name="name" placeholder="Last Name">
|
||||
<select id="party" name="party">
|
||||
<option value="">All</option>
|
||||
<#-- Iterate over the parties list and create an option for each one -->
|
||||
<#list parties as party>
|
||||
<option value="${party}">${party}</option>
|
||||
</#list>
|
||||
</select>
|
||||
|
||||
<#if parties??>
|
||||
<select id="party" name="party">
|
||||
<option value="">Partei/Fraktion</option>
|
||||
<#-- Iterate over the parties list and create an option for each one -->
|
||||
<#list parties as party>
|
||||
<option value="${party}">${party}</option>
|
||||
</#list>
|
||||
</select>
|
||||
</#if>
|
||||
|
||||
<#if topics??>
|
||||
<select id="topic" name="topic">
|
||||
<option value="">Topic</option>
|
||||
<#-- Iterate over the topics list and create an option for each one -->
|
||||
<#list topics as topic>
|
||||
<option value="${topic}">${topic}</option>
|
||||
</#list>
|
||||
</select>
|
||||
</#if>
|
||||
|
||||
<button type="submit">Search</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
<nav>
|
||||
<p></p>
|
||||
<a href="/">Home</a>
|
||||
<a href="/members">Members</a>
|
||||
<a href="/export">Export</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/members">Parlamentarier</a>
|
||||
<a href="/reden">Reden</a>
|
||||
<a href="/export">Exportieren</a>
|
||||
<a href="/about">Über</a>
|
||||
<p></p>
|
||||
</nav>
|
||||
</header>
|
|
@ -5,20 +5,13 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Abgeordnete Übersicht</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<style type="text/css">
|
||||
th, td {
|
||||
padding: 12px;
|
||||
text-align: center; /* Center-aligns both header and data cells */
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<#include "header.ftl">
|
||||
<body>
|
||||
<h2>Abgeordnete (alphabetisch sortiert)</h2>
|
||||
<main>
|
||||
|
||||
<#assign formAction = "/members">
|
||||
<#include "filterForm.ftl">
|
||||
|
||||
<br>
|
||||
|
|
47
src/main/resources/templates/showAllSpeechesList.ftl
Normal file
47
src/main/resources/templates/showAllSpeechesList.ftl
Normal file
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Alle Reden</title>
|
||||
</head>
|
||||
<#include "header.ftl">
|
||||
<body>
|
||||
<h2>Reden</h2>
|
||||
<main>
|
||||
|
||||
<br>
|
||||
<#assign formAction = "/reden">
|
||||
<#include "filterForm.ftl">
|
||||
<br><br>
|
||||
|
||||
<section>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Datum</th>
|
||||
<th>Name</th>
|
||||
<th>Fraktion</th>
|
||||
<th>Sitzung / Agenda</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<#list speechesMetaDataList as redeMd>
|
||||
<tr>
|
||||
<td>${redeMd.dateTimeString}</td>
|
||||
<td>${redeMd.speakerName}</td>
|
||||
<td>${redeMd.fraktion}</td>
|
||||
<td><a href="/reden/${redeMd.speakerId}/${redeMd.speechKey}">${redeMd.sessionId} / ${redeMd.agendaTitle}</a></td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
<#include "footer.ftl">
|
||||
</html>
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Rede von ${s.speakerName} <#if s.fraction??> (${s.fraction}) </#if></title>
|
||||
|
@ -16,11 +16,26 @@
|
|||
<#include "header.ftl">
|
||||
<body>
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
<h1>Rede von ${s.speakerName} <#if s.fraction??> (${s.fraction}) </#if> </h1>
|
||||
<h1>
|
||||
Rede von ${s.speakerName}
|
||||
<#if s.fraction??> (${s.fraction}) </#if>
|
||||
<#if s.dateTimeString??> vom ${s.dateTimeString} </#if>
|
||||
</h1>
|
||||
|
||||
<br>
|
||||
|
||||
<h2>Rede ${s.speechKey} </h2>
|
||||
<h2>
|
||||
Rede ${s.speechKey}
|
||||
<#if s.agendaTitle??> / Agendapunkt: ${s.agendaTitle} </#if>
|
||||
</h2>
|
||||
|
||||
<br>
|
||||
<#if picture??>
|
||||
<img style="max-width: 400px; height: auto;" src="data:image/jpeg;base64,${picture}" alt="Foto von ${s.speakerName}" />
|
||||
<#else>
|
||||
<h2>(kein Foto verfügbar)</h2>
|
||||
</#if>
|
||||
<br> <br>
|
||||
|
||||
<#list s.content as c>
|
||||
<#include "speechContent.ftl">
|
||||
|
|
|
@ -1,52 +1,65 @@
|
|||
<svg id="topicsBubblechart"></svg>
|
||||
|
||||
<svg id="topicsBubblechart"></svg>
|
||||
<script>
|
||||
|
||||
<script>
|
||||
var topicsData = [
|
||||
<#list condenseTopicInformation as topicTuple>
|
||||
{ topic: "${topicTuple.topic}", score: ${topicTuple.score?string?replace(',', '.')}} <#sep>,
|
||||
</#list>
|
||||
];
|
||||
|
||||
var topicsData = [
|
||||
<#list condenseTopicInformation as topicTuple>
|
||||
{ topic: "${topicTuple.topic}", score: "${topicTuple.score?string?replace(',', '.')}"} <#sep>,
|
||||
</#list>
|
||||
];
|
||||
const topics_bc_width = 1000;
|
||||
const topics_bc_height = 800;
|
||||
|
||||
const topics_bc_width = 1000;
|
||||
const topics_bc_height = 800;
|
||||
const minScore = Math.min(...topicsData.map(item => item.score));
|
||||
const maxScore = Math.max(...topicsData.map(item => item.score));
|
||||
|
||||
fillOpacity = 0.5;
|
||||
fillOpacity = 0.5;
|
||||
|
||||
var svg = d3.select("#topicsBubblechart")
|
||||
.attr("width", topics_bc_width)
|
||||
.attr("height", topics_bc_height)
|
||||
var svg = d3.select("#topicsBubblechart")
|
||||
.attr("width", topics_bc_width)
|
||||
.attr("height", topics_bc_height)
|
||||
|
||||
var topics_bc_color = d3.scaleOrdinal(d3.schemeCategory10);
|
||||
var topics_bc_size = d3.scaleLinear().domain([1, 10]).range([30, 100]);
|
||||
var topics_bc_color = d3.scaleOrdinal(d3.schemeCategory10);
|
||||
var topics_bc_size = d3.scaleLinear().domain([minScore, maxScore]).range([30, 70]);
|
||||
|
||||
var bubbles = svg.selectAll("circle")
|
||||
.data(topicsData)
|
||||
.enter().append("circle")
|
||||
.attr("r", d => topics_bc_size(d.score))
|
||||
.attr("fill", d => topics_bc_color(d.topic))
|
||||
.attr("fill-opacity", fillOpacity);
|
||||
var bubbles = svg.selectAll("circle")
|
||||
.data(topicsData)
|
||||
.enter().append("circle")
|
||||
.attr("r", d => topics_bc_size(d.score))
|
||||
.attr("fill", d => topics_bc_color(d.topic))
|
||||
.attr("fill-opacity", fillOpacity);
|
||||
|
||||
var topics_labels = svg.selectAll("text")
|
||||
.data(topicsData)
|
||||
.enter().append("text")
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("font-size", d => (10 * d.score) + "px")
|
||||
.attr("fill", "#000")
|
||||
.text(d => d.topic);
|
||||
var topics_labels = svg.selectAll("text")
|
||||
.data(topicsData)
|
||||
.enter().append("text")
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("font-size", d => Math.max(12, 10 * Math.sqrt(d.score)) + "px")
|
||||
.attr("fill", "#000")
|
||||
.text(d => d.topic)
|
||||
.on("mouseover", function(event, d) {
|
||||
d3.select(this)
|
||||
.transition()
|
||||
.duration(200)
|
||||
.attr("font-size", 25 + "px"); // Increase font size on hover
|
||||
})
|
||||
.on("mouseout", function(event, d) {
|
||||
d3.select(this)
|
||||
.transition()
|
||||
.duration(200)
|
||||
.attr("font-size", d => Math.max(12, 10 * Math.sqrt(d.score)) + "px"); // Reset font size after hover
|
||||
});
|
||||
|
||||
var topics_bc_simulation = d3.forceSimulation(topicsData)
|
||||
.force("charge", d3.forceManyBody().strength(5))
|
||||
.force("center", d3.forceCenter(topics_bc_width / 2, topics_bc_height / 2))
|
||||
.force("collision", d3.forceCollide(d => topics_bc_size(d.score) + 25))
|
||||
.force("x", d3.forceX(topics_bc_width / 2).strength(0.1))
|
||||
.force("y", d3.forceY(topics_bc_height / 2).strength(0.1));
|
||||
var topics_bc_simulation = d3.forceSimulation(topicsData)
|
||||
.force("charge", d3.forceManyBody().strength(5))
|
||||
.force("center", d3.forceCenter(topics_bc_width / 2, topics_bc_height / 2))
|
||||
.force("collision", d3.forceCollide(d => topics_bc_size(d.score) + 25))
|
||||
.force("x", d3.forceX(topics_bc_width / 2).strength(0.1))
|
||||
.force("y", d3.forceY(topics_bc_height / 2).strength(0.1));
|
||||
|
||||
topics_bc_simulation.on("tick", () => {
|
||||
bubbles.attr("cx", d => d.x).attr("cy", d => d.y);
|
||||
topics_labels.attr("x", d => d.x).attr("y", d => d.y);
|
||||
});
|
||||
|
||||
</script>
|
||||
topics_bc_simulation.on("tick", () => {
|
||||
bubbles.attr("cx", d => d.x).attr("cy", d => d.y);
|
||||
topics_labels.attr("x", d => d.x).attr("y", d => d.y);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.6 KiB |
Loading…
Add table
Add a link
Reference in a new issue