Fixed all Chart issues, all 4 now displayed. FrontendController largerly absorbed into Speech-/ Parlamentariercontroller

This commit is contained in:
vysitor 2025-03-21 01:07:41 +01:00
parent 8a10ef9364
commit 3a2dce4853
7 changed files with 50 additions and 174 deletions

View file

@ -69,7 +69,7 @@ public class HtmlSpeech {
nlp.setNamedEntities(NamedEntity.readNamedEntitiesFromMongo(namedEntitiesDocs));
List<Document> sentimentDocs = nlpDoc.get("sentiments", MongoDBHandler.DOC_LIST_CLASS);
nlp.setSentiments(List.of(Sentiment.readFirstSentimentFromMongo(sentimentDocs)));
nlp.setSentiments(Sentiment.readSentimentsFromMongo((sentimentDocs)));
List<Document> topicsDocs = nlpDoc.get("topics", MongoDBHandler.DOC_LIST_CLASS);
nlp.setTopics(Topic.readTopicsFromMongo(topicsDocs));

View file

@ -99,14 +99,24 @@ public class Sentiment {
.toString();
}
public static Sentiment readFirstSentimentFromMongo(List<Document> sentimentDocs) {
Document doc = sentimentDocs.get(0);
return new Sentiment(doc.getInteger("begin"),
doc.getInteger("end"),
doc.getDouble("score"),
doc.getDouble("pos"),
doc.getDouble("neu"),
doc.getDouble("neg")
);
/**
*
* @param sentimentDocs Die Sentiment-Dokumente (Speech --> analysisResults --> sentiment) aus der MongoDB lesen.
* Das erste Dokument ist für die gesamte Rede, Sentiments 1..n entsprechen Sentences 0..n-1
* @return
*/
public static List<Sentiment> readSentimentsFromMongo(List<Document> sentimentDocs) {
List<Sentiment> sentiments = new ArrayList<>();
for (Document doc : sentimentDocs) {
sentiments.add(new Sentiment(
doc.getInteger("begin"),
doc.getInteger("end"),
doc.getDouble("score"),
doc.getDouble("pos"),
doc.getDouble("neu"),
doc.getDouble("neg")
));
}
return sentiments;
}
}

View file

@ -39,6 +39,10 @@ public class FrontEndController {
ctx.render("home.ftl");
}
/*
TODO: getAllParlamentarier gibt es hier UND im ParlamentarierController (etwas unterschiedliche Implementierungen)
--> konsolidieren!
*/
@OpenApi(
summary = "Get alle Parlamentarier. Man kann nach Vor-, Nachname oder Partei filtern.",
description = "Listet alle Parlamentarier bzw. diejenige, welche den Filter entsprechen",
@ -63,157 +67,16 @@ public class FrontEndController {
ctx.render("parlamentarier.ftl", attributes);
}
/**
* Zeigt die Details eines Parlamentariers an:
* - persönliche Daten (Geburtsdatum, -ort, Vita, Religion etc.).
* - Mitgliederschaften, falls vorhanden
* - Fotos, falls vorhanden
* @param ctx JavaLin-Context
/*
TODO: Achtung: getParlamentarierDetails gibt es ab jetzt LEDIGLICH im ParlamentarierController!
*/
@OpenApi(
summary = "Zeigt die Details eines Parlamentariers an",
description = "Zeigt persönliche Daten, Mitgliederschaften, Fotos",
operationId = "getParlamentarierDetails",
path = "/portfolio/{id}",
methods = HttpMethod.GET,
tags = {"Parlamentarier"},
pathParams = {
@OpenApiParam(name = "id", description = "id des Parlamentariers", required = true),
},
responses = {
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = ParlamentarierDetails.class)})
})
public static void getParlamentarierDetails(Context ctx) {
String id = ctx.pathParam("id");
Logger.info("getParlamentarierDetails, ID = " + id);
ParlamentarierDetails pd = MongoPprUtils.getParlamentarierDetailsByID(id);
Map<String, Object> attributes = new HashMap<>();
attributes.put("p", pd);
Long speechCount = MongoPprUtils.countSpeechesOfSpeaker(pd.getId());
attributes.put("speechesCount", speechCount);
attributes.put("pic", MongoPprUtils.getMemberPhoto(pd.getId()));
if (speechCount == 0) {
attributes.put("speechesPlaceholder", null);
} else {
attributes.put("speechesPlaceholder", new ArrayList<>());
}
ctx.render("parlamentarierDetails.ftl", attributes);
}
/**
* Liste alle Reden eines Parlamentariers an
* @param ctx Javalin Context
/*
TODO: Achtung: showSpeech gibt es ab jetzt LEDIGLICH im SpeechController!
*/
@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.getSpeechesMetadataForSpeaker(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
/*
TODO: Achtung: showSpeech gibt es ab jetzt LEDIGLICH im SpeechController!
*/
@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<>();
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"...
// ... und ersetzen
speech.getNlp().setTopics(
topics.entrySet().stream()
.map(me -> new Topic(me.getKey(), me.getValue(), null))
.collect(Collectors.toList()));
}
// NLP: POS
if (speech.getNlp() != null && speech.getNlp().getTokens() != null) {
List<Token> tokens = speech.getNlp().getTokens();
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
.collect(Collectors.toList());
Logger.debug("Sending POS List to NLP - " + posList);
speech.getNlp().setPosList((List) posList);
} else {
Logger.debug("POS List is EMPTY");
speech.getNlp().setPosList((List) new ArrayList<Token>()); // Ensure it's never null
}
// NLP: Sentiments
Sentiment sentimentObj = null;
if (speech.getNlp() != null && speech.getNlp().getSentiments() != null && !speech.getNlp().getSentiments().isEmpty()) {
Sentiment first = speech.getNlp().getSentiments().get(0);
sentimentObj = new Sentiment(
first.getBegin(),
first.getEnd(),
first.getSentiment(),
first.getNegative(),
first.getNeutral(),
first.getPositive()
);
} else {
// No sentiment found: use a default sentiment with zero values
sentimentObj = new Sentiment(0, 0, 0.0, 0.0, 0.0, 0.0);
}
Logger.debug("Speech Sentiment - " + sentimentObj);
attributes.put("sentiment", sentimentObj);
ctx.render("speech.ftl", attributes);
}
}

View file

@ -61,8 +61,8 @@ public class RESTHandler {
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}", 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)
}

View file

@ -6,6 +6,7 @@ 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;
import org.texttechnologylab.project.gruppe_05_1.domain.nlp.NamedEntity;
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;
@ -99,7 +100,7 @@ public class SpeechController {
}
// NLP: POS
if (speech.getNlp().getTokens() != null) {
if (speech.getNlp() != null && speech.getNlp().getTokens() != null) {
List<Token> tokens = speech.getNlp().getTokens();
Map<String, Integer> posCounts = Token.countPOS(tokens);
@ -136,13 +137,7 @@ public class SpeechController {
text,
typeAppearance.get(text) + 1) ;
} else {
// ... aber der Text unbekannt --> erstelle einen neuen Eintrag für den Text und füge diesen dem Type-Eintrag hinzu
// TODO: DELETE
//Map<String, Integer> firstTextAppearance = new HashMap<>();
//firstTextAppearance.put(type, 1);
typeAppearance.put(text, 1);
//namedEntitiesMapOfMaps.put(type, firstTextAppearance);
}
} else {
// Named Entity Type unbekannt: erstelle einen neuen Eintrag für Type sowie einen Eintrag für den ihm gehörigen Text
@ -159,10 +154,18 @@ public class SpeechController {
// 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 - TODO
if (speech.getNlp().getSentiments() != null) {
// NLP: Sentiments
// Der erste Sentiment gilt der gesamten Rede. Die weitere Sentiments entsprechen die Sätze.
List<Sentiment> sentiments = speech.getNlp().getSentiments();
if ((sentiments != null) && ! sentiments.isEmpty()) {
Sentiment overallSentiment = sentiments.get(0);
attributes.put("overallSentiment", overallSentiment);
sentiments.remove(0);
} else {
attributes.put("overallSentiment", null);
}
attributes.put("sentiments", sentiments);
}
ctx.render("speech.ftl", attributes);

View file

@ -20,7 +20,7 @@
</div>
<div class="chart">
<#if sentiment??>
<#if overallSentiment??>
<h3>Sentiments Information (als Radar Chart)</h3>
<#include "sentimentsRadarChart.ftl">
<#else>

View file

@ -1,11 +1,11 @@
<svg class="chart-svg" id="sentimentsRadarChart"></svg>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script>
// Retrieve sentiment values from the passed attribute "sentiment"
// Retrieve sentiment values from the passed attribute "overallSentiment"
var sentimentData = {
pos: ${sentiment.positive?number},
neu: ${sentiment.neutral?number},
neg: ${sentiment.negative?number}
pos: ${overallSentiment.positive?string?replace(',', '.')},
neu: ${overallSentiment.neutral?string?replace(',', '.')},
neg: ${overallSentiment.negative?string?replace(',', '.')}
};
console.log("Sentiment Data:", sentimentData);

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before After
Before After