Merge branch 'main' into 'Sentiments-Radar-Chart'

# Conflicts:
#   src/main/java/org/texttechnologylab/project/gruppe_05_1/domain/html/HtmlSpeech.java
#   src/main/java/org/texttechnologylab/project/gruppe_05_1/domain/nlp/Sentiment.java
#   src/main/resources/templates/nlp.ftl
This commit is contained in:
Leon Kastner 2025-03-20 19:53:56 +00:00
commit 54ff4b1c7f
8 changed files with 133 additions and 73 deletions

View file

@ -623,6 +623,7 @@ public class MongoPprUtils {
System.out.println("SpeechDoc "+ speechDoc); // TODO: remove when no longer needed
if (speechDoc == null) {
Logger.error("Rede " + key + " nicht gefunden");
return null;
}
return new HtmlSpeech(speechDoc);

View file

@ -3,10 +3,7 @@ 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.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.nlp.*;
import org.texttechnologylab.project.gruppe_05_1.domain.speech.SpeechMetaData;
import java.util.ArrayList;
@ -69,6 +66,7 @@ public class HtmlSpeech {
List<Document> dependenciesDocs = nlpDoc.get("dependencies", MongoDBHandler.DOC_LIST_CLASS);
List<Document> namedEntitiesDocs = nlpDoc.get("namedEntities", MongoDBHandler.DOC_LIST_CLASS);
nlp.setNamedEntities(NamedEntity.readNamedEntitiesFromMongo(namedEntitiesDocs));
List<Document> sentimentDocs = nlpDoc.get("sentiments", MongoDBHandler.DOC_LIST_CLASS);
nlp.setSentiments(List.of(Sentiment.readFirstSentimentFromMongo(sentimentDocs)));

View file

@ -1,12 +1,14 @@
package org.texttechnologylab.project.gruppe_05_1.domain.nlp;
import org.bson.Document;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
public class NamedEntity {
String type; // PER, LOC etc.
// int begin; // TODO: momentan nicht in MongoDB
// int end; // TODO: momentan nicht in MongoDB
String text;
public NamedEntity() {
@ -52,4 +54,15 @@ public class NamedEntity {
.add("text='" + text + "'")
.toString();
}
public static List<NamedEntity> readNamedEntitiesFromMongo(List<Document> nadocs) {
List<NamedEntity> nes = new ArrayList<>();
for (Document doc : nadocs) {
nes.add(new NamedEntity(
doc.getString("type"),
doc.getString("text")
));
}
return nes;
}
}

View file

@ -360,8 +360,8 @@ public class NlpUtils {
bulkOperations.add(new UpdateOneModel<>(updateFilter, update));
}
if (!bulkOperations.isEmpty()) {
System.out.println("Processing of " + bulkOperations.size() + " documents finished");
System.out.println("uploading...");
Logger.debug("Processing of " + bulkOperations.size() + " documents finished");
Logger.debug("uploading...");
mongoDBHandler.bulkWriteNlpData(bulkOperations);
Logger.debug("Bulk write completed for " + bulkOperations.size() + " documents.");
mongoDBHandler.close();

View file

@ -184,12 +184,12 @@ public class FrontEndController {
.map(entry -> new Token(entry.getKey(), String.valueOf(entry.getValue()), "")) // Lemma remains empty
.collect(Collectors.toList());
System.out.println("DEBUG: Sending POS List to NLP - " + posList);
Logger.debug("Sending POS List to NLP - " + posList);
speech.getNlp().setPosList((List) posList);
} else {
System.out.println("DEBUG: POS List is EMPTY");
Logger.debug("POS List is EMPTY");
speech.getNlp().setPosList((List) new ArrayList<Token>()); // Ensure it's never null
}

View file

@ -72,44 +72,59 @@ public class SpeechController {
Map<String, Object> attributes = new HashMap<>();
HtmlSpeech speech = MongoPprUtils.getSpeechByKey(redeId);
if (speech == null) {
attributes.put("error", "Rede " + redeId + " nicht vorhanden");
ctx.render("speech.ftl", attributes);
return;
}
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
if (speech.getNlp() != null) {
// NLP: Topic
if ((speech.getNlp().getTopics() != null) && (speech.getNlp().getTopics().size() > 0)) {
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()));
} else {
speech.getNlp().setTopics(null);
}
// NLP: POS
if (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
}
// 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
if (speech.getNlp().getSentiments() != null) {
}
}
// 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());
System.out.println("DEBUG: Sending POS List to NLP - " + posList);
speech.getNlp().setPosList((List) posList);
} else {
System.out.println("DEBUG: POS List is EMPTY");
speech.getNlp().setPosList((List) new ArrayList<Token>()); // Ensure it's never 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
ctx.render("speech.ftl", attributes);
}

View file

@ -82,7 +82,14 @@ body {
main {
width: 80%;
flex: 1
}
.centered-content {
display: flex;
justify-content: center;
align-items: center;
padding: 0 auto;
}
/* Heading Styling */
@ -227,6 +234,18 @@ tbody tr:hover {
text-align: center;
}
.sentiment-positive {
color: green;
}
.sentiment-negative {
color: red;
}
.sentiment-neutral {
color: grey;
}
.back-link {
position: fixed;
bottom: 50px;

View file

@ -4,7 +4,13 @@
<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>
<title>
<#if s??>
Rede von ${s.speakerName} <#if s.fraction??> (${s.fraction}) </#if>
<#else>
Fehler
</#if>
</title>
<style type="text/css">
th, td {
padding: 12px;
@ -15,44 +21,52 @@
</head>
<#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>
<#if s.dateTimeString??> vom ${s.dateTimeString} </#if>
</h1>
<#if s??>
<script src="https://d3js.org/d3.v7.min.js"></script>
<h1>
Rede von ${s.speakerName}
<#if s.fraction??> (${s.fraction}) </#if>
<#if s.dateTimeString??> vom ${s.dateTimeString} </#if>
</h1>
<br>
<br>
<h2>
Rede ${s.speechKey}
<#if s.agendaTitle??> / Agendapunkt: ${s.agendaTitle} </#if>
</h2>
<h2>
Rede ${s.speechKey}
<#if s.agendaTitle??> / Agendapunkt: ${s.agendaTitle} </#if>
</h2>
<br>
<main>
<#if picture??>
<img style="max-width: 400px; height: auto;" src="data:image/jpeg;base64,${picture}" alt="Foto von ${s.speakerName}" />
<br>
<main>
<#if picture??>
<div class="centered-content">
<img style="max-width: 400px; height: auto; margin-bottom: 20px" src="data:image/jpeg;base64,${picture}" alt="Foto von ${s.speakerName}" />
</div>
<#else>
<h2>(kein Foto verfügbar)</h2>
</#if>
<br>
<br>
<#list s.content as c>
<#include "speechContent.ftl">
</#list>
<br><br>
<#if s.nlp??>
<h2>NLP Information</h2>
<#assign nlp = "${s.nlp}">
<#include "nlp.ftl">
<#else>
<h2>Keine NLP Information verfügbar für diese Rede</h2>
</#if>
<br> <br>
</main>
<#else>
<h2>(kein Foto verfügbar)</h2>
<h2> ${error} </h2>
</#if>
<br> <br>
<#list s.content as c>
<#include "speechContent.ftl">
</#list>
<br><br>
<#if s.nlp??>
<h2>NLP Information</h2>
<#assign nlp = "${s.nlp}">
<#include "nlp.ftl">
<#else>
<h2>Keine NLP Information verfügbar für diese Rede</h2>
</#if>
<br> <br>
</main>
</body>
<#include "footer.ftl">
</html>