Implemented rudimentary Chart overview for all Speeches.
This commit is contained in:
parent
6ef665c38f
commit
36e2453a7e
7 changed files with 146 additions and 5 deletions
|
@ -4,11 +4,10 @@ import com.mongodb.MongoClientSettings;
|
|||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
import com.mongodb.bulk.BulkWriteResult;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.*;
|
||||
import com.mongodb.client.model.*;
|
||||
import org.texttechnologylab.project.gruppe_05_1.domain.html.HtmlSpeech;
|
||||
import org.texttechnologylab.project.gruppe_05_1.domain.nlp.Topic;
|
||||
import org.texttechnologylab.project.gruppe_05_1.exceptions.ServerErrorException;
|
||||
import org.texttechnologylab.project.gruppe_05_1.exceptions.SessionNotFoundException;
|
||||
import org.bson.Document;
|
||||
|
@ -781,6 +780,7 @@ public class MongoDBHandler {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean sessionExists(String sessionNumber) {
|
||||
Document filter = new Document("sessionId", Integer.valueOf(sessionNumber));
|
||||
long count = sessionsCollection.countDocuments(filter);
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package org.texttechnologylab.project.gruppe_05_1.rest;
|
||||
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import gnu.trove.impl.sync.TSynchronizedShortObjectMap;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.openapi.*;
|
||||
import org.apache.commons.collections.bag.SynchronizedSortedBag;
|
||||
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.html.HtmlSpeech;
|
||||
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.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;
|
||||
|
@ -15,13 +18,24 @@ 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.util.PPRUtils;
|
||||
import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech;
|
||||
import com.mongodb.client.AggregateIterable;
|
||||
import com.mongodb.client.model.Aggregates;
|
||||
import com.mongodb.client.model.Accumulators;
|
||||
import com.mongodb.client.model.Facet;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.Projections;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import java.util.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static javax.management.Query.eq;
|
||||
import static org.texttechnologylab.project.gruppe_05_1.util.PPRUtils.listFractionsFromMembers;
|
||||
|
||||
public class FrontEndController {
|
||||
|
@ -67,6 +81,74 @@ public class FrontEndController {
|
|||
ctx.render("parlamentarier.ftl", attributes);
|
||||
}
|
||||
|
||||
public static void getCharts(Context ctx) {
|
||||
MongoCollection<Document> col = MongoPprUtils.getSpeechCollection();
|
||||
|
||||
List<Bson> topicsPipeline = List.of(
|
||||
Aggregates.match(Filters.exists("analysisResults.topics.0")),
|
||||
Aggregates.unwind("$analysisResults.topics"),
|
||||
Aggregates.group(
|
||||
"$analysisResults.topics.topic",
|
||||
Accumulators.sum("totalScore", "$analysisResults.topics.score")
|
||||
)
|
||||
);
|
||||
List<Bson> posPipeline = List.of(
|
||||
Aggregates.match(Filters.exists("analysisResults.tokens")),
|
||||
Aggregates.unwind("$analysisResults.tokens"),
|
||||
Aggregates.group("$analysisResults.tokens.pos", Accumulators.sum("count", 1))
|
||||
);
|
||||
List<Bson> nePipeline = List.of(
|
||||
Aggregates.match(Filters.exists("analysisResults.namedEntities")),
|
||||
Aggregates.unwind("$analysisResults.namedEntities"),
|
||||
Aggregates.group("$analysisResults.namedEntities.type", Accumulators.sum("count", 1))
|
||||
);
|
||||
List<Bson> sentimentsPipeline = List.of(
|
||||
Aggregates.match(Filters.exists("analysisResults.sentiments")),
|
||||
Aggregates.project(Projections.computed("firstSentiment",
|
||||
new Document("$arrayElemAt", List.of("$analysisResults.sentiments", 0)))),
|
||||
Aggregates.replaceRoot("$firstSentiment")
|
||||
);
|
||||
|
||||
CompletableFuture<List<Document>> topicsF = CompletableFuture.supplyAsync(() -> col.aggregate(topicsPipeline).into(new ArrayList<>()));
|
||||
CompletableFuture<List<Document>> posF = CompletableFuture.supplyAsync(() -> col.aggregate(posPipeline).into(new ArrayList<>()));
|
||||
CompletableFuture<List<Document>> neF = CompletableFuture.supplyAsync(() -> col.aggregate(nePipeline).into(new ArrayList<>()));
|
||||
CompletableFuture<List<Document>> sentF = CompletableFuture.supplyAsync(() -> col.aggregate(sentimentsPipeline).into(new ArrayList<>()));
|
||||
|
||||
CompletableFuture.allOf(topicsF, posF, neF, sentF).join();
|
||||
|
||||
List<Topic> aggregatedTopics = topicsF.join().stream()
|
||||
.map(d -> new Topic(d.getString("_id"), d.getDouble("totalScore"), null))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Token> aggregatedPOS = posF.join().stream()
|
||||
.map(d -> new Token(d.getString("_id"), String.valueOf(d.getInteger("count")), ""))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, Map<String, Integer>> aggregatedNE = new HashMap<>();
|
||||
neF.join().forEach(d -> {
|
||||
List<Document> entities = d.getList("entities", Document.class);
|
||||
Map<String, Integer> typeMap = (entities == null)
|
||||
? new HashMap<>()
|
||||
: entities.stream()
|
||||
.collect(Collectors.toMap(
|
||||
e -> e.getString("text"),
|
||||
e -> e.getInteger("count")
|
||||
));
|
||||
aggregatedNE.put(d.getString("_id"), typeMap);
|
||||
});
|
||||
|
||||
List<Sentiment> aggregatedSentiments = Sentiment.readSentimentsFromMongo(sentF.join());
|
||||
|
||||
ctx.render("charts.ftl", Map.of(
|
||||
"aggregatedTopics", aggregatedTopics,
|
||||
"aggregatedPOS", aggregatedPOS,
|
||||
"aggregatedNE", aggregatedNE,
|
||||
"aggregatedSentiments", aggregatedSentiments
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
TODO: Achtung: getParlamentarierDetails gibt es ab jetzt LEDIGLICH im ParlamentarierController!
|
||||
*/
|
||||
|
|
|
@ -66,6 +66,9 @@ public class RESTHandler {
|
|||
|
||||
app.get("/reden", SpeechController::listAllSpeeches); // zeige alle Reden an (Filtern möglich)
|
||||
|
||||
// Charts
|
||||
app.get("/charts", FrontEndController::getCharts);
|
||||
|
||||
app.get("/export/pdf/speech/{id}", 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
|
||||
|
|
52
src/main/resources/templates/charts.ftl
Normal file
52
src/main/resources/templates/charts.ftl
Normal file
|
@ -0,0 +1,52 @@
|
|||
<head>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<title>Parliament Explorer</title>
|
||||
</head>
|
||||
<#include "header.ftl">
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
<body>
|
||||
<h2>Chart Sortiment</h2>
|
||||
<div class="chart-container">
|
||||
<div class="chart">
|
||||
<#if aggregatedTopics?? && (aggregatedTopics?size gt 0)>
|
||||
<h3>Topics Information (als Bubble Chart)</h3>
|
||||
<#assign condenseTopicInformation = aggregatedTopics>
|
||||
<#include "topicsBubbleChart.ftl">
|
||||
<#else>
|
||||
<h3>Keine Topics Information verfügbar</h3>
|
||||
</#if>
|
||||
</div>
|
||||
|
||||
<div class="chart">
|
||||
<#if aggregatedPOS?? && (aggregatedPOS?size gt 0)>
|
||||
<h3>POS Information (als Bar Chart)</h3>
|
||||
<#assign posList = aggregatedPOS>
|
||||
<#include "posBarChart.ftl">
|
||||
<#else>
|
||||
<h3>Keine POS Information verfügbar</h3>
|
||||
</#if>
|
||||
</div>
|
||||
|
||||
<div class="chart">
|
||||
<#if aggregatedSentiment?? && (aggregatedSentiment?size gt 0)>
|
||||
<h3>Sentiments Information (als Radar Chart)</h3>
|
||||
<#-- Wrap the aggregated sentiment map in a list so that the chart partial can iterate -->
|
||||
<#assign sentiments = [aggregatedSentiment]>
|
||||
<#include "sentimentsRadarChart.ftl">
|
||||
<#else>
|
||||
<h3>Keine Sentiments Information verfügbar</h3>
|
||||
</#if>
|
||||
</div>
|
||||
|
||||
<div class="chart">
|
||||
<#if aggregatedNE?? && (aggregatedNE?size gt 0)>
|
||||
<h3>Named Entities Information (als Sunburst Chart)</h3>
|
||||
<#assign neMap = aggregatedNE>
|
||||
<#include "namedEntitiesSunburstChart.ftl">
|
||||
<#else>
|
||||
<h3>Keine Named Entities Information verfügbar</h3>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<#include "footer.ftl">
|
|
@ -12,6 +12,8 @@
|
|||
<br>
|
||||
<a href="/reden">Reden</a>
|
||||
<br>
|
||||
<a href="/charts">Charts</a>
|
||||
<br>
|
||||
<a href="/export">Exportieren</a>
|
||||
<br>
|
||||
<a href="/about">Über</a>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
posData.push({ pos: "No Data", count: 0 });
|
||||
</#if>
|
||||
|
||||
console.log("Final POS Data being used:", posData);
|
||||
console.log("POS Data:", posData);
|
||||
|
||||
var svg = d3.select("#posBarchart")
|
||||
.attr("width", barChartWidth)
|
||||
|
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
@ -8,6 +8,8 @@
|
|||
</#list>
|
||||
];
|
||||
|
||||
console.log("Topics Data:", topicsData);
|
||||
|
||||
const topics_bc_width = 1000;
|
||||
const topics_bc_height = 800;
|
||||
|
||||
|
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Loading…
Add table
Add a link
Reference in a new issue