From e2244a4b45b301f2ca4bbadf031d785111615fe5 Mon Sep 17 00:00:00 2001 From: Picman2000 <65342372+Picman2000@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:05:48 +0100 Subject: [PATCH] code needs 21718261765125gb ram :D --- .../project/gruppe_05_1/Main.java | 11 +- .../gruppe_05_1/database/MongoDBHandler.java | 18 +- .../speeches/Speech_MongoDB_Impl.java | 6 +- .../project/gruppe_05_1/nlp/NlpUtils.java | 161 +++++++++++++++++- .../xml/speeches/Impls/Speech_File_Impl.java | 9 +- .../xml/speeches/Interfaces/Speech.java | 7 + .../xml/speeches/SpeechParser.java | 4 +- src/main/resources/speeches/TypeSystem.xml.gz | Bin 0 -> 18985 bytes 8 files changed, 199 insertions(+), 17 deletions(-) create mode 100644 src/main/resources/speeches/TypeSystem.xml.gz diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/Main.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/Main.java index 6a14729..02ccfac 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/Main.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/Main.java @@ -5,6 +5,7 @@ import com.mongodb.client.MongoDatabase; import org.texttechnologylab.project.gruppe_05_1.database.*; import org.texttechnologylab.project.gruppe_05_1.domain.mdb.Mdb; import org.texttechnologylab.project.gruppe_05_1.domain.mdb.MdbDocument; +import org.texttechnologylab.project.gruppe_05_1.nlp.NlpUtils; import org.texttechnologylab.project.gruppe_05_1.rest.RESTHandler; import org.texttechnologylab.project.gruppe_05_1.util.Logger; import org.texttechnologylab.project.gruppe_05_1.util.PPRUtils; @@ -56,12 +57,13 @@ public class Main { //TEST - Logger.pink("Parsing XML and inserting data into DB (Uebung 2)..."); + SpeechIndexFactoryImpl speechIndexFactory = new SpeechIndexFactoryImpl(); if (MongoPprUtils.getSpeechCollection().countDocuments() != 0) { System.out.println("Speeches werden nicht gelesen, da sie bereits in der Datenbank stehen"); } else { + Logger.pink("Parsing XML and inserting data into DB (Uebung 2)..."); SpeechIndex speechIndex = speechIndexFactory .parseLegislativePeriods(TRUE) .builder() @@ -91,10 +93,6 @@ public class Main { mongoDBHandler.close(); // Close the connection to the DB } - - //TEST - - // Stellt fest, dass alle nötigen Datenbank-Collections existieren PPRUtils.ensureCollectionExist(); @@ -102,6 +100,9 @@ public class Main { PPRUtils.parlamentExplorerInit(xmlFactory, mongoFactory); // NLP-Verarbeitung - TODO + NlpUtils.importXmiData(); + + RESTHandler restHandler = new RESTHandler(); restHandler.startJavalin(); diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/MongoDBHandler.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/MongoDBHandler.java index 8361a70..d892fc5 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/MongoDBHandler.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/MongoDBHandler.java @@ -7,9 +7,7 @@ import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; -import com.mongodb.client.model.Filters; -import com.mongodb.client.model.Indexes; -import com.mongodb.client.model.Updates; +import com.mongodb.client.model.*; import exceptions.AgendaItemNotFoundException; import exceptions.MemberNotFoundException; import exceptions.ServerErrorException; @@ -537,7 +535,8 @@ public class MongoDBHandler { .append("speechId", speech.getSpeechId()) .append("speakerId", speech.getSpeakerId()) .append("speakerName", speech.getSpeakerName()) - .append("fraction", speech.getFraction()); + .append("fraction", speech.getFraction()) + .append("speechKey", speech.getSpeechKey()); // Convert speechContents to a list of Documents List contentDocuments = new ArrayList<>(); @@ -638,6 +637,12 @@ public class MongoDBHandler { return result; } + public void updateXmiData(String speechKey, String xmiContent) { + speechesCollection.updateOne( + Filters.eq("speechKey", speechKey), + Updates.set("xmiData", xmiContent) + ); + } public void deleteAllDocuments() { speechesCollection.deleteMany(new Document()); @@ -646,6 +651,11 @@ public class MongoDBHandler { //historyCollection.deleteMany(new Document()); } + public void bulkUpdateDocuments(String collectionName, List> updates) { + BulkWriteOptions options = new BulkWriteOptions().ordered(false); + speechesCollection.bulkWrite(updates, options); + } + public void close() { mongoClient.close(); } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimp/speeches/Speech_MongoDB_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimp/speeches/Speech_MongoDB_Impl.java index 0ac537c..5f2fbb3 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimp/speeches/Speech_MongoDB_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimp/speeches/Speech_MongoDB_Impl.java @@ -15,7 +15,10 @@ public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech { mongoDocument.getInteger("speechId"), mongoDocument.getInteger("speakerId"), mongoDocument.getString("speakerName"), - mongoDocument.getString("fraction")); + mongoDocument.getString("fraction"), + mongoDocument.getString("speechKey") + ); + for (Document content : (List) mongoDocument.get("speechContents")) { switch (content.getString("type")) { @@ -32,5 +35,6 @@ public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech { throw new IllegalArgumentException("Unknown content type: " + content.getString("type")); } } + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/nlp/NlpUtils.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/nlp/NlpUtils.java index 7b80788..27f1dea 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/nlp/NlpUtils.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/nlp/NlpUtils.java @@ -1,15 +1,29 @@ package org.texttechnologylab.project.gruppe_05_1.nlp; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.UpdateOneModel; +import com.mongodb.client.model.Updates; +import com.mongodb.client.model.WriteModel; import de.tudarmstadt.ukp.dkpro.core.api.metadata.type.DocumentMetaData; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Sentence; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Token; import org.apache.commons.io.FileUtils; import org.apache.uima.UIMAException; +import org.apache.uima.UIMAFramework; +import org.apache.uima.cas.CAS; import org.apache.uima.cas.CASException; +import org.apache.uima.cas.Feature; +import org.apache.uima.cas.impl.XmiCasDeserializer; +import org.apache.uima.cas.text.AnnotationFS; +import org.apache.uima.cas.text.AnnotationIndex; import org.apache.uima.fit.factory.JCasFactory; import org.apache.uima.fit.util.JCasUtil; import org.apache.uima.jcas.JCas; import org.apache.uima.resource.ResourceInitializationException; +import org.apache.uima.resource.metadata.TypeSystemDescription; +import org.apache.uima.util.CasCreationUtils; +import org.apache.uima.util.XMLInputSource; +import org.bson.Document; import org.dkpro.core.io.xmi.XmiWriter; import org.texttechnologylab.DockerUnifiedUIMAInterface.DUUIComposer; import org.texttechnologylab.DockerUnifiedUIMAInterface.driver.DUUIDockerDriver; @@ -17,25 +31,31 @@ import org.texttechnologylab.DockerUnifiedUIMAInterface.driver.DUUIRemoteDriver; import org.texttechnologylab.DockerUnifiedUIMAInterface.driver.DUUIUIMADriver; import org.texttechnologylab.DockerUnifiedUIMAInterface.lua.DUUILuaContext; import org.texttechnologylab.annotation.NamedEntity; +import org.texttechnologylab.project.gruppe_05_1.database.MongoDBHandler; import org.texttechnologylab.uima.type.Sentiment; import org.xml.sax.SAXException; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Base64; -import java.util.Collection; +import java.util.*; +import java.util.zip.GZIPInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import static org.apache.uima.fit.factory.AnalysisEngineFactory.createEngineDescription; +import static org.springframework.core.io.buffer.DataBufferUtils.readInputStream; public class NlpUtils { // common class-attributes private static DUUIComposer pComposer = null; private static int iWorkers = 1; + private static final String TYPE_SYSTEM_DESCRIPTOR_PATH = "/speeches/TypeSystem.xml.gz"; + private static final int MAX_FEATURE_LENGTH = 10000; public static void createNlpData() { @@ -310,4 +330,137 @@ public class NlpUtils { private static void createSentimentInfo() { } + + /** + * Liest die ZIP-Datei aus dem Ressourcenordner (/speeches/20.zip), + * iteriert über alle .gz-Dateien, dekomprimiert sie, wandelt den XMI-Inhalt in einen JCas um, + * verarbeitet den JCas (iteriert über alle Annotationen) und extrahiert z. B. NLP-Daten (z. B. POS-Tags). + * Anschließend wird das jeweilige Dokument in MongoDB aktualisiert. + * + * @throws IOException falls ein Fehler beim Dateizugriff auftritt + */ + public static void importXmiData() throws IOException { + MongoDBHandler mongoDBHandler = new MongoDBHandler(); + List> bulkOperations = new ArrayList<>(); + InputStream zipStream = NlpUtils.class.getResourceAsStream("/speeches/20.zip"); + if (zipStream == null) { + throw new IOException("20.zip nicht gefunden im Ressourcenordner /speeches"); + } + try (ZipInputStream zis = new ZipInputStream(zipStream)) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + if (!entry.isDirectory() && entry.getName().endsWith(".gz")) { + String speechKey = entry.getName().substring(0, entry.getName().length() - 3); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int len; + while ((len = zis.read(buffer)) != -1) { + baos.write(buffer, 0, len); + } + byte[] gzData = baos.toByteArray(); + try { + + ByteArrayOutputStream decompressedBaos = new ByteArrayOutputStream(); + try (GZIPInputStream gzis = new GZIPInputStream(new ByteArrayInputStream(gzData))) { + byte[] buf = new byte[4096]; + int bytesRead; + while ((bytesRead = gzis.read(buf)) != -1) { + decompressedBaos.write(buf, 0, bytesRead); + } + } + byte[] xmiBytes = decompressedBaos.toByteArray(); + String xmiContent = new String(xmiBytes, StandardCharsets.UTF_8); + JCas jcas = convertXmiToJCas(new ByteArrayInputStream(xmiBytes)); + List> annotations = processJCas(jcas); + Document filter = new Document("RedeID", speechKey); + Document updateFields = new Document() + .append("annotations", annotations) + .append("xmi", xmiContent); + Document update = new Document("$set", updateFields); + UpdateOneModel updateModel = new UpdateOneModel<>(filter, update); + bulkOperations.add(updateModel); + } catch (Exception e) { + e.printStackTrace(); + } + } + zis.closeEntry(); + } + } + if (!bulkOperations.isEmpty()) { + mongoDBHandler.bulkUpdateDocuments("Rede", bulkOperations); + System.out.println("Bulk update erfolgreich für " + bulkOperations.size() + " Dokumente."); + } + mongoDBHandler.close(); + } + + + /** + * Liest einen InputStream vollständig in einen String ein. + * + * @param is InputStream + * @return Den gesamten String-Inhalt + * @throws IOException falls ein Fehler beim Lesen auftritt + */ + private static String readInputStream(InputStream is) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + return sb.toString(); + } + + /** + * Wandelt den XMI-Inhalt aus dem InputStream in einen JCas um. + * Dabei wird der hardcodierte, komprimierte TypeSystem-Descriptor zuerst dekomprimiert. + * + * @param xmiInputStream InputStream des XMI-Inhalts + * @return Den konvertierten JCas + * @throws Exception falls beim Laden oder Deserialisieren ein Fehler auftritt + */ + private static JCas convertXmiToJCas(InputStream xmiInputStream) throws Exception { + InputStream tsCompressedStream = NlpUtils.class.getResourceAsStream(TYPE_SYSTEM_DESCRIPTOR_PATH); + if (tsCompressedStream == null) { + throw new IllegalArgumentException("TypeSystem-Descriptor nicht gefunden: " + TYPE_SYSTEM_DESCRIPTOR_PATH); + } + try (GZIPInputStream tsStream = new GZIPInputStream(tsCompressedStream)) { + XMLInputSource inputSource = new XMLInputSource(tsStream); + TypeSystemDescription tsd = UIMAFramework.getXMLParser().parseTypeSystemDescription(inputSource); + CAS cas = CasCreationUtils.createCas(tsd, null, null); + XmiCasDeserializer.deserialize(xmiInputStream, cas, true); + return cas.getJCas(); + } + } + + /** + * Iteriert über alle Annotationen im JCas und verarbeitet diese. + * Hier kannst du deine eigene Logik einfügen, um die JCas-Daten weiter zu verarbeiten. + * + * @param jcas Der konvertierte JCas + */ + private static List> processJCas(JCas jcas) { + List> annotationsData = new ArrayList<>(); + CAS cas = jcas.getCas(); + AnnotationIndex index = cas.getAnnotationIndex(); + for (AnnotationFS annotation : index) { + Map annotationData = new HashMap<>(); + annotationData.put("type", annotation.getType().getName()); + Map featuresMap = new HashMap<>(); + for (Feature feature : annotation.getType().getFeatures()) { + String featureName = feature.getShortName(); + String featureValue = annotation.getFeatureValueAsString(feature); + if (featureValue != null && featureValue.length() > MAX_FEATURE_LENGTH) { + featureValue = featureValue.substring(0, MAX_FEATURE_LENGTH); // trim for efficiency + } + featuresMap.put(featureName, featureValue); + featuresMap.put(featureName, featureValue); + } + annotationData.put("features", featuresMap); + + annotationsData.add(annotationData); + } + return annotationsData; + } + } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Speech_File_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Speech_File_Impl.java index 739a909..2d1dff8 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Speech_File_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Speech_File_Impl.java @@ -17,6 +17,7 @@ public class Speech_File_Impl implements Speech { private final String speakerName; private final String fraction; private final List speechContents; + private final String speechKey; @Override public int getSessionId() { @@ -53,13 +54,19 @@ public class Speech_File_Impl implements Speech { return speechContents; } - public Speech_File_Impl(int sessionId, int agendaItemId, int speechId, int speakerId, String speakerName, String fraction) { + @Override + public String getSpeechKey() { + return speechKey; + } + + public Speech_File_Impl(int sessionId, int agendaItemId, int speechId, int speakerId, String speakerName, String fraction, String speechKey) { this.speakerId = speakerId; this.agendaItemId = agendaItemId; this.speechId = speechId; this.speakerName = speakerName; this.fraction = fraction; this.sessionId = sessionId; + this.speechKey = speechKey; this.speechContents = new ArrayList<>(); } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Interfaces/Speech.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Interfaces/Speech.java index 786741e..9445234 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Interfaces/Speech.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Interfaces/Speech.java @@ -54,6 +54,13 @@ public interface Speech { */ int getSessionId(); + /** + * Returns the session Key. + * + * @return The session Key. + */ + String getSpeechKey(); + /** * Returns the speech contents. * diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/SpeechParser.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/SpeechParser.java index d09eee1..124d359 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/SpeechParser.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/SpeechParser.java @@ -112,14 +112,14 @@ public class SpeechParser { int speakerId = Integer.parseInt(speakerElement.getAttribute("id")); Element nameElement = (Element) speakerElement.getElementsByTagName("name").item(0); if (nameElement == null) continue; - + String redeID = speechElement.getAttribute("id"); String title = getOptionalTextContent(nameElement, "titel"); String firstName = getOptionalTextContent(nameElement, "vorname"); String lastName = getOptionalTextContent(nameElement, "nachname"); String fraction = getOptionalTextContent(nameElement, "fraktion"); String speakerName = (title != null ? title + " " : "") + firstName + " " + lastName; - Speech_File_Impl speech = new Speech_File_Impl(sessionId, agendaItemId, speechId, speakerId, speakerName, fraction); + Speech_File_Impl speech = new Speech_File_Impl(sessionId, agendaItemId, speechId, speakerId, speakerName, fraction, redeID); // Add the speaker to speech contents speech.addContent(new Speaker_File_Impl(0, speechId, speakerId, speakerName, fraction)); diff --git a/src/main/resources/speeches/TypeSystem.xml.gz b/src/main/resources/speeches/TypeSystem.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..3d45d40bf0731696188e4aed17d14688574a3c68 GIT binary patch literal 18985 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