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 098d601..7670218 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 @@ -21,6 +21,7 @@ import java.util.concurrent.TimeUnit; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; +import static org.texttechnologylab.project.gruppe_05_1.export.TeXUtil.isTeXSdkInstalled; import static org.texttechnologylab.project.gruppe_05_1.util.PPRUtils.checkAndProcessNewProtocols; public class Main { @@ -34,11 +35,13 @@ public class Main { private static final FileObjectFactory xmlFactory = FileObjectFactory.getFactory(); private static final MongoObjectFactory mongoFactory = MongoObjectFactory.getFactory(); + public static final String RESOURCES_DIR = "src/main/resources"; public static final String CONFIG_DIR = "src/main/resources/config"; public static final String JAVALIN_TEMPLATE_DIR = "src/main/resources/templates"; public static final String JAVALIN_STATIC_FILES_DIR = "src/main/resources/static"; public static final String JCAS_SPEECHES_TYPESYSTEM_DIR = "src/main/resources/speeches/TypeSystem"; public static final String MEMBER_IMAGES_DIR = "src/main/resources/membersOfParliamentImages/"; + public static final String TEMP_EXPORT_DIR = "src/main/resources/tempExport/"; public static void main(String[] args) throws Exception { UPLOAD_MEMBER_PHOTOS = Arrays.asList(args).contains("uploadMemberPhotos"); @@ -59,6 +62,12 @@ public class Main { System.out.println(" - Debug Logging: " + DEBUG_LOGGING); System.out.println("--------------------------------------------o"); + if (!isTeXSdkInstalled()) { + Logger.orange("-------------------------------------------------o"); + Logger.orange("TeX SDK not installed. PDF export will not work."); + Logger.orange("-------------------------------------------------o"); + } + if (ONLY_RUN_WEB) { Logger.info("Starting Web Service..."); RESTHandler restHandler = new RESTHandler(); diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/MongoPprUtils.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/MongoPprUtils.java index 8e928a6..32446c0 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/MongoPprUtils.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/MongoPprUtils.java @@ -8,6 +8,7 @@ import com.mongodb.client.model.Projections; import io.javalin.http.Context; import org.bson.Document; import org.bson.conversions.Bson; +import org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.Speaker_MongoDB_Impl; import org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.Speech_MongoDB_Impl; import org.texttechnologylab.project.gruppe_05_1.domain.html.HtmlSpeech; import org.texttechnologylab.project.gruppe_05_1.domain.html.Parlamentarier; @@ -294,6 +295,15 @@ public class MongoPprUtils { return readParlamentarierDetailsFromSpeaker(doc); } + public static Speaker_MongoDB_Impl getSpeakerById(String id) { + Logger.debug("ID: " + id); + Document doc = MongoDBHandler.findFirstDocumentInCollection(getSpeakerCollection(), "_id", id); + Logger.debug("Speaker: " + doc); + Speaker_MongoDB_Impl speaker = new Speaker_MongoDB_Impl().createSpeakerMongoDBImpl(doc); + Logger.debug("Speaker parsed" + speaker); + return speaker; + } + /** * Holt die Details eines Parlamentariers @@ -638,7 +648,7 @@ public class MongoPprUtils { * @param key: Rede ID * @return */ - public static HtmlSpeech getSpeechByKey(String key) { + public static HtmlSpeech getHTMLSpeechByKey(String key) { Document filter = new Document("speechKey", key); Document speechDoc = getSpeechCollection().find(filter).first(); if (speechDoc == null) { @@ -649,6 +659,18 @@ public class MongoPprUtils { return new HtmlSpeech(speechDoc); } + public static Speech getSpeechByKey(String key) { + Document filter = new Document("speechKey", key); + Document speechDoc = getSpeechCollection().find(filter).first(); + + if (speechDoc == null) { + Logger.error("Rede " + key + " nicht gefunden"); + return null; + } + + return new Speech_MongoDB_Impl(speechDoc, true); + } + // getMemberPhoto /** @@ -721,6 +743,36 @@ public class MongoPprUtils { Logger.info("Updating Metadata Collection: end"); } + public static List getSpeechesBySpeakerId(String speakerId) { + List speechIds = new ArrayList<>(); + Document filter = new Document("speakerId", Integer.parseInt(speakerId)); + List docs = getSpeechCollection().find(filter).into(new ArrayList<>()); + for (Document doc : docs) { + speechIds.add(new Speech_MongoDB_Impl(doc, true)); + } + return speechIds; + } + + public static List getAllSpeeches() { + List speechIds = new ArrayList<>(); + Document filter = new Document(); + List docs = getSpeechCollection().find(filter).into(new ArrayList<>()); + for (Document doc : docs) { + speechIds.add(new Speech_MongoDB_Impl(doc, true)); + } + return speechIds; + } + + public static List getAllSpeechesWithTopic(String topic) { + List speechIds = new ArrayList<>(); + Document filter = new Document("analysisResults.topics.topic", topic); + List docs = getSpeechCollection().find(filter).into(new ArrayList<>()); + for (Document doc : docs) { + speechIds.add(new Speech_MongoDB_Impl(doc, true)); + } + return speechIds; + } + public static List getAllPartiesOfSpeakers() { Document doc = MongoDBHandler.findFirstDocumentInCollection(getMetadataCollection(), "type", "parties_of_speakers"); if (doc == null) {return new ArrayList<>();} diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/Speaker_MongoDB_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/Speaker_MongoDB_Impl.java index 5c357e0..f3d5133 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/Speaker_MongoDB_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/Speaker_MongoDB_Impl.java @@ -5,15 +5,59 @@ import org.texttechnologylab.project.gruppe_05_1.database.MongoDBHandler; import org.texttechnologylab.project.gruppe_05_1.database.MongoOperations; import org.texttechnologylab.project.gruppe_05_1.domain.speaker.Membership; import org.texttechnologylab.project.gruppe_05_1.domain.speaker.Speaker; +import org.texttechnologylab.project.gruppe_05_1.util.Logger; +import org.w3c.dom.Element; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.time.LocalDate; +import java.util.*; +import static org.texttechnologylab.project.gruppe_05_1.Main.TEMP_EXPORT_DIR; import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.getMemberPhoto; public class Speaker_MongoDB_Impl extends Speaker implements MongoOperations { + public Speaker_MongoDB_Impl createSpeakerMongoDBImpl(Document mongoDocument) { + this.setId(mongoDocument.getString("_id")); + this.setName(mongoDocument.getString("name")); + this.setFirstName(mongoDocument.getString("firstName")); + this.setTitle(mongoDocument.getString("title")); + this.setGeburtsdatum(parseTimestampSafely(mongoDocument.getDate("geburtsdatum"))); + this.setGeburtsort(mongoDocument.getString("geburtsort")); + this.setSterbedatum(parseTimestampSafely(mongoDocument.getDate("sterbedatum"))); + this.setGeschlecht(mongoDocument.getString("geschlecht")); + this.setBeruf(mongoDocument.getString("beruf")); + this.setAkademischertitel(mongoDocument.getString("akademischertitel")); + this.setFamilienstand(mongoDocument.getString("familienstand")); + this.setReligion(mongoDocument.getString("religion")); + this.setVita(mongoDocument.getString("vita")); + this.setParty(mongoDocument.getString("party")); + + List memberships = (List) mongoDocument.get("memberships"); + List membershipList = new ArrayList<>(); + for (Document membership : memberships) { + Membership membershipObj = new Membership(); + membershipObj.setRole(membership.getString("role")); + membershipObj.setMember(membership.getString("member")); + membershipObj.setBegin(parseTimestampSafely(membership.getDate("begin"))); + membershipObj.setEnd(parseTimestampSafely(membership.getDate("end"))); + membershipObj.setLabel(membership.getString("label")); + membershipObj.setWp(membership.getInteger("wp")); + membershipList.add(membershipObj); + } + this.setMemberships(membershipList); + + return this; + } + + public LocalDate parseTimestampSafely(Date timestamp) { + try { + return LocalDate.parse(timestamp.toString()); + } catch (Exception e) { + return null; + } + } @Override public Document createEntity(Speaker entity) { @@ -48,4 +92,96 @@ public class Speaker_MongoDB_Impl extends Speaker implements MongoOperations memberships = this.getMemberships(); + for (Membership membership : memberships) { + Element membershipElement = doc.createElement("membership"); + membershipElement.setAttribute("role", membership.getRole()); + membershipElement.setAttribute("member", membership.getMember()); + membershipElement.setAttribute("begin", membership.getBegin() != null ? membership.getBegin().toString() : ""); + membershipElement.setAttribute("end", membership.getEnd() != null ? membership.getEnd().toString() : ""); + membershipElement.setAttribute("label", membership.getLabel()); + membershipElement.setAttribute("wp", String.valueOf(membership.getWp())); + speakerElement.appendChild(membershipElement); + } + + return speakerElement; + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/Speech_MongoDB_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/Speech_MongoDB_Impl.java index 49e26c9..326da2e 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/Speech_MongoDB_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/Speech_MongoDB_Impl.java @@ -9,11 +9,16 @@ import org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.speechl import org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.speechline.Line_MongoDB_Impl; import org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.speechline.Speaker_MongoDB_Impl; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Speech_File_Impl; +import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Content; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech; +import org.w3c.dom.Element; import java.util.List; +import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.getAgendaTitle; +import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.getSessionDateTime; + public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech { public Speech_MongoDB_Impl(Document mongoDocument, boolean includeContent) { super( @@ -80,10 +85,51 @@ public class Speech_MongoDB_Impl extends Speech_File_Impl implements Speech { public String toTeX() { StringBuilder tex = new StringBuilder(); - tex.append(""); + String speechTitle = "Rede " + + this.getSpeechKey() + + "/" + + getAgendaTitle(this.getSessionId(), this.getAgendaItemId()) + + " von " + + this.getSpeakerName() + + " (" + + this.getFraction() + + ") vom " + + getSessionDateTime(this.getSessionId()); + + tex.append("\\section*{").append(speechTitle).append("}\n"); + tex.append("\\addcontentsline{toc}{section}{").append(speechTitle).append("}\n"); + + tex.append("$$SPEAKERINFO$$\n"); + + tex.append("\\subsection*{NLP Metadata}\n"); + + tex.append("$$NLPMETADATA$$\n"); + + tex.append("\\subsection*{Speech Content}\n"); + + // Add content block of speeches + for (Content content: this.getSpeechContents()) { + tex.append(content.toTeX()); + } return tex.toString(); } + public Element toXML(org.w3c.dom.Document doc) { + Element speech = doc.createElement("speech"); + speech.setAttribute("sessionId", String.valueOf(this.getSessionId())); + speech.setAttribute("agendaItemId", String.valueOf(this.getAgendaItemId())); + speech.setAttribute("speechId", String.valueOf(this.getSpeechId())); + speech.setAttribute("speakerId", String.valueOf(this.getSpeakerId())); + speech.setAttribute("speakerName", this.getSpeakerName()); + speech.setAttribute("fraction", this.getFraction()); + speech.setAttribute("speechKey", this.getSpeechKey()); + + for (Content content: this.getSpeechContents()) { + speech.appendChild(content.toXML(doc)); + } + + return speech; + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Comment_MongoDB_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Comment_MongoDB_Impl.java index 8aab442..d2ca74d 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Comment_MongoDB_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Comment_MongoDB_Impl.java @@ -3,6 +3,7 @@ package org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.speech import org.bson.Document; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Comment_File_Impl; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Comment; +import org.w3c.dom.Element; public class Comment_MongoDB_Impl extends Comment_File_Impl implements Comment { @@ -14,4 +15,17 @@ public class Comment_MongoDB_Impl extends Comment_File_Impl implements Comment { mongoDocument.getString("commentatorName"), mongoDocument.getString("comment")); } + + public String toTeX() { + return "\\textcolor{blue}{Kommentar}: " + this.getComment() + "\\\\\n"; + } + + public Element toXML(org.w3c.dom.Document doc) { + Element comment = doc.createElement("comment"); + comment.setAttribute("contentId", String.valueOf(this.getContentId())); + comment.setAttribute("speechId", String.valueOf(this.getSpeechId())); + comment.setAttribute("commentatorName", this.getCommentatorName()); + comment.setTextContent(this.getComment()); + return comment; + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Line_MongoDB_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Line_MongoDB_Impl.java index ad4a362..d2d81f4 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Line_MongoDB_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Line_MongoDB_Impl.java @@ -3,6 +3,7 @@ package org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.speech import org.bson.Document; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Line_File_Impl; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Line; +import org.w3c.dom.Element; public class Line_MongoDB_Impl extends Line_File_Impl implements Line { @@ -12,4 +13,16 @@ public class Line_MongoDB_Impl extends Line_File_Impl implements Line { mongoDocument.getInteger("speechId"), mongoDocument.getString("content")); } + + public String toTeX() { + return this.getContent() + "\\\\\n"; + } + + public Element toXML(org.w3c.dom.Document doc) { + Element line = doc.createElement("line"); + line.setAttribute("contentId", String.valueOf(this.getContentId())); + line.setAttribute("speechId", String.valueOf(this.getSpeechId())); + line.setTextContent(this.getContent()); + return line; + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Speaker_MongoDB_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Speaker_MongoDB_Impl.java index 1bbebd9..4c6ffff 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Speaker_MongoDB_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/database/domainimpl/mdb/speechline/Speaker_MongoDB_Impl.java @@ -3,6 +3,7 @@ package org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.speech import org.bson.Document; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls.Speaker_File_Impl; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speaker; +import org.w3c.dom.Element; public class Speaker_MongoDB_Impl extends Speaker_File_Impl implements Speaker { public Speaker_MongoDB_Impl(Document mongoDocument) { @@ -13,4 +14,18 @@ public class Speaker_MongoDB_Impl extends Speaker_File_Impl implements Speaker { mongoDocument.getString("speakerName"), mongoDocument.getString("fraction")); } + + public String toTeX() { + return "\\textcolor{darkgreen}{Redner/Rednerin}: " + this.getSpeakerName() + "\\\\\n"; + } + + public Element toXML(org.w3c.dom.Document doc) { + Element speaker = doc.createElement("speaker"); + speaker.setAttribute("contentId", String.valueOf(this.getContentId())); + speaker.setAttribute("speechId", String.valueOf(this.getSpeechId())); + speaker.setAttribute("speakerId", String.valueOf(this.getSpeakerId())); + speaker.setAttribute("speakerName", this.getSpeakerName()); + speaker.setAttribute("fraction", this.getFraction()); + return speaker; + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/TeXUtil.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/TeXUtil.java new file mode 100644 index 0000000..16cbe83 --- /dev/null +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/TeXUtil.java @@ -0,0 +1,226 @@ +package org.texttechnologylab.project.gruppe_05_1.export; + +import org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.Speaker_MongoDB_Impl; +import org.texttechnologylab.project.gruppe_05_1.util.Logger; +import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; + +import static org.texttechnologylab.project.gruppe_05_1.Main.RESOURCES_DIR; +import static org.texttechnologylab.project.gruppe_05_1.Main.TEMP_EXPORT_DIR; +import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.*; + +public class TeXUtil { + private static final String PREAMBLE = readFileContentFromTeXDir(); + private static final String BEGIN_DOCUMENT = "\\begin{document}\n"; + private static final String END_DOCUMENT = "\\end{document}"; + private static final String TABLEOFCONTENTS = "\\tableofcontents\n\\newpage\n"; + private static final String NEWPAGE = "\\newpage\n"; + + private static String readFileContentFromTeXDir() { + try { + return Files.readString(new File(RESOURCES_DIR, "tex/preamble.tex").toPath()); + } catch (IOException e) { + Logger.error("Failed to read file content from tex directory."); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + return ""; + } + } + + public static String getSpeechToTexComponent(String speechId) { + createTempDir(); + Speech speech = getSpeechByKey(speechId); + + Speaker_MongoDB_Impl speaker = getSpeakerById(String.valueOf(speech.getSpeakerId())); + + StringBuilder tex = new StringBuilder(); + + tex.append(speech.toTeX()); + + return tex.toString().replace("$$SPEAKERINFO$$", speaker.toTeX()); + } + + public static String getSpeechToTexComponent(Speech speech) { + createTempDir(); + Speaker_MongoDB_Impl speaker = getSpeakerById(String.valueOf(speech.getSpeakerId())); + + StringBuilder tex = new StringBuilder(); + + tex.append(speech.toTeX()); + + return tex.toString().replace("$$SPEAKERINFO$$", speaker.toTeX()); + } + + public static String getExportedSpeechBase64StringBySpeechId(String speechId) throws IOException, InterruptedException { + // Read preamble from resources directory tex/preamble.tex + return convertTexToBase64PDF(PREAMBLE.replace("$$EXPORTCATEGORY$$", "Speech " + speechId) + BEGIN_DOCUMENT + getSpeechToTexComponent(speechId) + END_DOCUMENT); + } + + public static String getBulkExportedSpeechBase64StringFromSpeakerById(String speakerId) throws IOException, InterruptedException { + // Fetch all speechIDs from the speaker + List speechIds = getSpeechesBySpeakerId(speakerId); + + StringBuilder tex = new StringBuilder(); + + tex.append(PREAMBLE.replace("$$EXPORTCATEGORY$$", "Speaker ID" + speakerId)); + tex.append(BEGIN_DOCUMENT); + tex.append(TABLEOFCONTENTS); + for (Speech speech : speechIds) { + tex.append(getSpeechToTexComponent(speech)); + tex.append(NEWPAGE); + } + tex.append(END_DOCUMENT); + + return convertTexToBase64PDF(tex.toString()); + } + + public static String getBulkExportedAllSpeechesBase64String() throws IOException, InterruptedException { + // Fetch all speechIDs from the speaker + List speechIds = getAllSpeeches(); + + StringBuilder tex = new StringBuilder(); + + tex.append(PREAMBLE.replace("$$EXPORTCATEGORY$$", "all speeches")); + tex.append(BEGIN_DOCUMENT); + tex.append(TABLEOFCONTENTS); + for (Speech speech : speechIds) { + tex.append(getSpeechToTexComponent(speech)); + tex.append(NEWPAGE); + } + tex.append(END_DOCUMENT); + + return convertTexToBase64PDF(tex.toString()); + } + + public static String getBulkExportedAllSpeechesWithTopicBase64String(String topic) throws IOException, InterruptedException { + // Fetch all speechIDs from the speaker + List speechIds = getAllSpeechesWithTopic(topic); + + StringBuilder tex = new StringBuilder(); + + tex.append(PREAMBLE.replace("$$EXPORTCATEGORY$$", "Speeches with topic " + topic)); + tex.append(BEGIN_DOCUMENT); + tex.append(TABLEOFCONTENTS); + for (Speech speech : speechIds) { + tex.append(getSpeechToTexComponent(speech)); + tex.append(NEWPAGE); + } + tex.append(END_DOCUMENT); + + return convertTexToBase64PDF(tex.toString()); + } + + public static String getBulkExportedSpeechesBase64String(List speechIds) throws IOException, InterruptedException { + StringBuilder tex = new StringBuilder(); + + tex.append(PREAMBLE.replace("$$EXPORTCATEGORY$$", "selected speeches")); + tex.append(BEGIN_DOCUMENT); + tex.append(TABLEOFCONTENTS); + for (String speechId : speechIds) { + tex.append(getSpeechToTexComponent(speechId)); + tex.append(NEWPAGE); + } + tex.append(END_DOCUMENT); + + return convertTexToBase64PDF(tex.toString()); + } + + public static String convertTexToBase64PDF(String tex) throws IOException, InterruptedException { + // Create a temporary directory + File tempDir = new File(TEMP_EXPORT_DIR); + createTempDir(); + + // Format tex string to UTF-8 + tex = new String(tex.getBytes("UTF-8")); + tex = tex.replaceAll("[^\\x00-\\x7F]", ""); // Replace all non-ASCII characters + + // Local datetime stamp + String dateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss").format(LocalDateTime.now()); + // Write the LaTeX content to a temporary .tex file + File texFile = new File(tempDir, "speech_export" + dateTime + ".tex"); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(texFile, StandardCharsets.UTF_8))) { + writer.write(tex); + } + + // Run pdflatex to generate the .pdf file + //String command = "pdflatex -interaction=nonstopmode -output-directory=" + tempDir.getAbsolutePath() + " " + texFile.getAbsolutePath(); + // using latexmk instead of pdflatex to fix TOC not generating properly + String command = "latexmk -pdf -interaction=nonstopmode -outdir=" + tempDir.getAbsolutePath() + " " + texFile.getAbsolutePath(); + Process process = Runtime.getRuntime().exec(command); + + + BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); + Logger.debug("Standard Output:"); + String s; + while ((s = stdInput.readLine()) != null) { + Logger.debug(s); + } + Logger.debug("LaTeX Process ended with exit code " + process.waitFor()); + + // Path to the generated PDF file + File pdfFile = new File(tempDir, "speech_export" + dateTime + ".pdf"); + + // Check if the PDF was created + if (!pdfFile.exists()) { + throw new IOException("PDF generation failed."); + } + + // Read the PDF file into a byte array + byte[] pdfBytes = Files.readAllBytes(pdfFile.toPath()); + + // Convert the byte array to a Base64 encoded string + return Base64.getEncoder().encodeToString(pdfBytes); + } + + public static void createTempDir() { + File tempDir = new File(TEMP_EXPORT_DIR); + if (!tempDir.exists()) { + tempDir.mkdirs(); + } + } + + public static void deleteTeXTempDirContents() throws IOException { + // Walk through the directory + Files.walkFileTree(Path.of(TEMP_EXPORT_DIR), new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + // Delete file + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + // Delete the directory after its contents are deleted + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + + public static boolean isTeXSdkInstalled() { + try { + Process process = Runtime.getRuntime().exec("pdflatex --version"); + BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); + String s; + while ((s = stdInput.readLine()) != null) { + if (s.contains("pdfTeX")) { + return true; + } + } + } catch (IOException ignored) {} + return false; + } +} diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/XMLUtil.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/XMLUtil.java new file mode 100644 index 0000000..4011614 --- /dev/null +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/export/XMLUtil.java @@ -0,0 +1,137 @@ +package org.texttechnologylab.project.gruppe_05_1.export; + +import org.eclipse.jetty.xml.XmlParser; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Node; +import org.texttechnologylab.project.gruppe_05_1.database.domainimpl.mdb.Speaker_MongoDB_Impl; +import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.StringWriter; +import java.util.List; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import static org.texttechnologylab.project.gruppe_05_1.database.MongoPprUtils.*; + +public class XMLUtil { + public static String documentToString(Document doc) { + try { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(doc); + + // Writer to store the XML string + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + + // Perform transformation + transformer.transform(source, result); + + return writer.toString(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static Document createXmlDocument() throws ParserConfigurationException { + // create new doc + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + Document doc = factory.newDocumentBuilder().newDocument(); + + Element speechesElement = doc.createElement("speeches"); + doc.appendChild(speechesElement); + + return doc; + } + + public static void addSpeechById(Document doc, String speechId) { + // get speeches element + Element speechesElement = (Element) doc.getElementsByTagName("speeches").item(0); + + // create new speech element + Element speechElement = doc.createElement("speech"); + speechesElement.appendChild(speechElement); + + Speech speech = getSpeechByKey(speechId); + Speaker_MongoDB_Impl speaker = getSpeakerById(String.valueOf(speech.getSpeakerId())); + + speechElement.appendChild(speaker.toXML(doc)); + speechElement.appendChild(speech.toXML(doc)); + } + + public static void addSpeechBySpeech(Document doc, Speech speech) { + // get speeches element + Element speechesElement = (Element) doc.getElementsByTagName("speeches").item(0); + + // create new speech element + Element speechElement = doc.createElement("speech"); + speechesElement.appendChild(speechElement); + + Speaker_MongoDB_Impl speaker = getSpeakerById(String.valueOf(speech.getSpeakerId())); + + speechElement.appendChild(speaker.toXML(doc)); + speechElement.appendChild(speech.toXML(doc)); + } + + public static String getExportedSpeechById(String speechId) throws ParserConfigurationException { + Document doc = createXmlDocument(); + + addSpeechById(doc, speechId); + + return documentToString(doc); + } + + public static String getExportedSpeechesFromSpeakerById(String speakerId) throws ParserConfigurationException { + Document doc = createXmlDocument(); + + List speeches = getSpeechesBySpeakerId(speakerId); + + for (Speech speech : speeches) { + addSpeechBySpeech(doc, speech); + } + + return documentToString(doc); + } + + public static String getExportedAllSpeeches() throws ParserConfigurationException { + Document doc = createXmlDocument(); + + List speeches = getAllSpeeches(); + + for (Speech speech : speeches) { + addSpeechBySpeech(doc, speech); + } + + return documentToString(doc); + } + + public static String getExportedSpeechesWhithTopic(String topic) throws ParserConfigurationException { + Document doc = createXmlDocument(); + + List speeches = getAllSpeechesWithTopic(topic); + + for (Speech speech : speeches) { + addSpeechBySpeech(doc, speech); + } + + return documentToString(doc); + } + + public static String getExportedSpeechesbyIds(List speechIds) throws ParserConfigurationException { + Document doc = createXmlDocument(); + + for (String speechId : speechIds) { + addSpeechById(doc, speechId); + } + + return documentToString(doc); + } +} diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/RESTHandler.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/RESTHandler.java index 2d50db1..f45ab21 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/RESTHandler.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/RESTHandler.java @@ -65,5 +65,17 @@ public class RESTHandler { 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) + + 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 + app.get("/export/pdf/all", SpeechesLatexExportController::exportAllSpeeches); // exportiere alle Reden als PDF CAUTION!!!: This will take forever but is required in the exercise + app.get("/export/pdf/speeches/{speechIds}", SpeechesLatexExportController::exportSpeeches); // exportiere eine Liste von Reden als PDF + + app.get("/export/xml/speech/{id}", SpeechesXMLExportController::exportSpeech); // exportiere eine Rede als XML + app.get("/export/xml/speaker/{id}", SpeechesXMLExportController::exportSpeechesFromSpeaker); // exportiere alle Reden eines Parlamentariers als XML + app.get("/export/xml/topic/{topic}", SpeechesXMLExportController::exportSpeechesWithTopic); // exportiere alle Reden zu einem Thema als XML + app.get("/export/xml/all", SpeechesXMLExportController::exportAllSpeeches); // exportiere alle Reden als XML + app.get("/export/xml/speeches/{speechIds}", SpeechesXMLExportController::exportSpeeches); // exportiere eine Liste von Reden als XML } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechController.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechController.java index e565230..e064d0a 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechController.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechController.java @@ -72,7 +72,7 @@ public class SpeechController { Map attributes = new HashMap<>(); - HtmlSpeech speech = MongoPprUtils.getSpeechByKey(redeId); + HtmlSpeech speech = MongoPprUtils.getHTMLSpeechByKey(redeId); if (speech == null) { attributes.put("error", "Rede " + redeId + " nicht vorhanden"); ctx.render("speech.ftl", attributes); diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesLatexExportController.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesLatexExportController.java new file mode 100644 index 0000000..f3b6205 --- /dev/null +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesLatexExportController.java @@ -0,0 +1,243 @@ +package org.texttechnologylab.project.gruppe_05_1.rest; + +import io.javalin.http.Context; +import io.javalin.openapi.HttpMethod; +import io.javalin.openapi.OpenApi; +import io.javalin.openapi.OpenApiResponse; +import org.texttechnologylab.project.gruppe_05_1.util.Logger; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; + +import static org.texttechnologylab.project.gruppe_05_1.export.TeXUtil.*; + +public class SpeechesLatexExportController { + @OpenApi( + summary = "Get a speech as a PDF", + description = "Returns a LaTeX generated pdf of a selected speech", + operationId = "getSpeechExport", + path = "/export/pdf/speech/{id}", + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "PDF"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportSpeech(Context ctx) { + byte[] pdfBytes = new byte[0]; + try { + pdfBytes = Base64.getDecoder().decode(getExportedSpeechBase64StringBySpeechId(ctx.pathParam("id"))); + } catch (Exception e) { + Logger.error("Failed to generate Export of Speech with ID " + ctx.pathParam("id")); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + + // Set the response content type to PDF + ctx.contentType("application/pdf"); + + ByteArrayInputStream stream = new ByteArrayInputStream(pdfBytes); + if (stream.available() == 0) { + Logger.error("PDF stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + // Send the PDF as a response + ctx.result(stream); + + try { + // delete the temporary folder + deleteTeXTempDirContents(); + } catch (IOException e) { + Logger.error("Failed to delete temporary folder."); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + } + + @OpenApi( + summary = "Get all speeches from a speaker as a PDF", + description = "Returns a LaTeX generated pdf of all speeches of a selected speech", + operationId = "getSpeechesFromSpeakerExport", + path = "/export/pdf/speaker/{id}", + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "PDF"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportSpeechesFromSpeaker(Context ctx) { + byte[] pdfBytes = new byte[0]; + try { + pdfBytes = Base64.getDecoder().decode(getBulkExportedSpeechBase64StringFromSpeakerById(ctx.pathParam("id"))); + } catch (Exception e) { + Logger.error("Failed to generate Export of Speeches from Speaker with ID " + ctx.pathParam("id")); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + + // Set the response content type to PDF + ctx.contentType("application/pdf"); + + ByteArrayInputStream stream = new ByteArrayInputStream(pdfBytes); + if (stream.available() == 0) { + Logger.error("PDF stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + // Send the PDF as a response + ctx.result(stream); + + try { + // delete the temporary folder + deleteTeXTempDirContents(); + Logger.debug("Temporary folder deleted."); + } catch (IOException e) { + Logger.error("Failed to delete temporary folder."); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + } + + @OpenApi( + summary = "Get all speeches as a PDF", + description = "Returns a LaTeX generated pdf of all speeches", + operationId = "getAllSpeeches", + path = "/export/pdf/all", + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "PDF"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportAllSpeeches(Context ctx) { + byte[] pdfBytes = new byte[0]; + try { + pdfBytes = Base64.getDecoder().decode(getBulkExportedAllSpeechesBase64String()); + } catch (Exception e) { + Logger.error("Failed to generate Export of all Speeches"); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + + // Set the response content type to PDF + ctx.contentType("application/pdf"); + + ByteArrayInputStream stream = new ByteArrayInputStream(pdfBytes); + if (stream.available() == 0) { + Logger.error("PDF stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + // Send the PDF as a response + ctx.result(stream); + + try { + // delete the temporary folder + deleteTeXTempDirContents(); + Logger.debug("Temporary folder deleted."); + } catch (IOException e) { + Logger.error("Failed to delete temporary folder."); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + } + + @OpenApi( + summary = "Get all speeches with specific topic as a PDF", + description = "Returns a LaTeX generated pdf of all speeches with specific topic", + operationId = "getAllSpeechesWithTopic", + path = "/export/pdf/topic/{topic}", + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "PDF"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportSpeechesWithTopic(Context ctx) { + byte[] pdfBytes = new byte[0]; + try { + pdfBytes = Base64.getDecoder().decode(getBulkExportedAllSpeechesWithTopicBase64String(ctx.pathParam("topic"))); + } catch (Exception e) { + Logger.error("Failed to generate Export of all Speeches with Topic " + ctx.pathParam("topic")); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + + // Set the response content type to PDF + ctx.contentType("application/pdf"); + + ByteArrayInputStream stream = new ByteArrayInputStream(pdfBytes); + if (stream.available() == 0) { + Logger.error("PDF stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + // Send the PDF as a response + ctx.result(stream); + + try { + // delete the temporary folder + deleteTeXTempDirContents(); + Logger.debug("Temporary folder deleted."); + } catch (IOException e) { + Logger.error("Failed to delete temporary folder."); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + } + + @OpenApi( + summary = "Get speeches by IDs as a PDF", + description = "Returns a LaTeX-generated PDF of the speeches specified by their IDs", + operationId = "getSpeechesByIds", + path = "/export/pdf/speeches/{speechIds}", // Comma-separated IDs + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "PDF"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportSpeeches(Context ctx) { + byte[] pdfBytes = new byte[0]; + + try { + // Extract speech IDs from the path + String speechIdsParam = ctx.pathParam("speechIds"); + List speechIds = Arrays.asList(speechIdsParam.split(",")); + + // Generate PDF for given speech IDs + pdfBytes = Base64.getDecoder().decode(getBulkExportedSpeechesBase64String(speechIds)); + } catch (Exception e) { + Logger.error("Failed to generate export for speeches: " + ctx.pathParam("speechIds")); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + + // Set response content type + ctx.contentType("application/pdf"); + + ByteArrayInputStream stream = new ByteArrayInputStream(pdfBytes); + if (stream.available() == 0) { + Logger.error("PDF stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + + // Send the PDF as response + ctx.result(stream); + + try { + // Clean up temporary files + deleteTeXTempDirContents(); + Logger.debug("Temporary folder deleted."); + } catch (IOException e) { + Logger.error("Failed to delete temporary folder."); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + } + } +} diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesXMLExportController.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesXMLExportController.java new file mode 100644 index 0000000..2b73910 --- /dev/null +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/rest/SpeechesXMLExportController.java @@ -0,0 +1,181 @@ +package org.texttechnologylab.project.gruppe_05_1.rest; + +import io.javalin.http.Context; +import io.javalin.openapi.HttpMethod; +import io.javalin.openapi.OpenApi; +import io.javalin.openapi.OpenApiResponse; +import org.texttechnologylab.project.gruppe_05_1.util.Logger; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; + +import static org.texttechnologylab.project.gruppe_05_1.export.XMLUtil.*; +import static org.texttechnologylab.project.gruppe_05_1.export.TeXUtil.*; + +public class SpeechesXMLExportController { + @OpenApi( + summary = "Get a speech as XML", + description = "Returns an XML file of a selected speech", + operationId = "getSpeechExport", + path = "/export/xml/speech/{id}", + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "XML"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportSpeech(Context ctx) { + String xmlContent; + try { + xmlContent = getExportedSpeechById(ctx.pathParam("id")); + ByteArrayInputStream stream = new ByteArrayInputStream(xmlContent.getBytes()); + if (stream.available() == 0) { + Logger.error("XML stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + ctx.contentType("application/xml"); + ctx.result(stream); + } catch (Exception e) { + Logger.error("Failed to generate Export of Speech with ID " + ctx.pathParam("id")); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + ctx.result("Internal Server Error"); + ctx.status(500); + } + } + + @OpenApi( + summary = "Get all speeches from a speaker as XML", + description = "Returns an XML file of all speeches of a selected speech", + operationId = "getSpeechesFromSpeakerExport", + path = "/export/xml/speaker/{id}", + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "XML"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportSpeechesFromSpeaker(Context ctx) { + String xmlContent; + try { + xmlContent = getExportedSpeechesFromSpeakerById(ctx.pathParam("id")); + ByteArrayInputStream stream = new ByteArrayInputStream(xmlContent.getBytes()); + if (stream.available() == 0) { + Logger.error("XML stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + ctx.contentType("application/xml"); + ctx.result(stream); + } catch (Exception e) { + Logger.error("Failed to generate Export of Speeches from Speaker with ID " + ctx.pathParam("id")); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + ctx.result("Internal Server Error"); + ctx.status(500); + } + } + + @OpenApi( + summary = "Get all speeches as XML", + description = "Returns an XML file of all speeches", + operationId = "getAllSpeeches", + path = "/export/xml/all", + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "XML"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportAllSpeeches(Context ctx) { + String xmlContent; + try { + xmlContent = getExportedAllSpeeches(); + ByteArrayInputStream stream = new ByteArrayInputStream(xmlContent.getBytes()); + if (stream.available() == 0) { + Logger.error("XML stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + ctx.contentType("application/xml"); + ctx.result(stream); + } catch (Exception e) { + Logger.error("Failed to generate Export of all Speeches"); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + ctx.result("Internal Server Error"); + ctx.status(500); + } + } + + @OpenApi( + summary = "Get all speeches with specific topic as XML", + description = "Returns an XML file of all speeches with specific topic", + operationId = "getAllSpeechesWithTopic", + path = "/export/xml/topic/{topic}", + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "XML"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportSpeechesWithTopic(Context ctx) { + String xmlContent; + try { + xmlContent = getExportedSpeechesWhithTopic(ctx.pathParam("topic")); + ByteArrayInputStream stream = new ByteArrayInputStream(xmlContent.getBytes()); + if (stream.available() == 0) { + Logger.error("XML stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + ctx.contentType("application/xml"); + ctx.result(stream); + } catch (Exception e) { + Logger.error("Failed to generate Export of all Speeches"); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + ctx.result("Internal Server Error"); + ctx.status(500); + } + } + + @OpenApi( + summary = "Get speeches by IDs as XML", + description = "Returns an XML file of the speeches specified by their IDs", + operationId = "getSpeechesByIds", + path = "/export/xml/speeches/{speechIds}", // Comma-separated IDs + methods = HttpMethod.GET, + tags = {"Export", "Speeches", "XML"}, + responses = { + @OpenApiResponse(status = "200") + }) + public static void exportSpeeches(Context ctx) { + String xmlContent; + try { + String speechIdsParam = ctx.pathParam("speechIds"); + List speechIds = Arrays.asList(speechIdsParam.split(",")); + xmlContent = getExportedSpeechesbyIds(speechIds); + ByteArrayInputStream stream = new ByteArrayInputStream(xmlContent.getBytes()); + if (stream.available() == 0) { + Logger.error("XML stream is empty."); + ctx.result("Internal Server Error"); + ctx.status(500); + return; + } + ctx.contentType("application/xml"); + ctx.result(stream); + } catch (Exception e) { + Logger.error("Failed to generate Export of all Speeches"); + Logger.error(e.getMessage()); + Logger.debug(Arrays.toString(e.getStackTrace())); + ctx.result("Internal Server Error"); + ctx.status(500); + } + } +} diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/util/Logger.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/util/Logger.java index 6dbfd8f..796498b 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/util/Logger.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/util/Logger.java @@ -28,4 +28,8 @@ public class Logger { public static void pink(String message) { System.out.println("\u001B[35m" + java.time.LocalTime.now() + " PINK: " + message + "\u001B[0m"); } + + public static void orange(String message) { + System.out.println("\u001B[38;5;214m" + message + "\u001B[0m"); + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Comment_File_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Comment_File_Impl.java index 036b204..4a824b1 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Comment_File_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Comment_File_Impl.java @@ -3,6 +3,8 @@ package org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Comment; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Content; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.enums.MongoDBEntryType; +import org.w3c.dom.Document; +import org.w3c.dom.Element; public class Comment_File_Impl implements Content, Comment { @@ -42,4 +44,19 @@ public class Comment_File_Impl implements Content, Comment { public MongoDBEntryType getType() { return MongoDBEntryType.SPEECH_COMMENT; } + + @Override + public String toTeX() { + return "\\textcolor{blue}{Kommentar}: " + this.getComment() + "\\\n"; + } + + @Override + public Element toXML(Document doc) { + Element comment = doc.createElement("comment"); + comment.setAttribute("contentId", String.valueOf(this.getContentId())); + comment.setAttribute("speechId", String.valueOf(this.getSpeechId())); + comment.setAttribute("commentatorName", this.getCommentatorName()); + comment.setTextContent(this.getComment()); + return comment; + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Line_File_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Line_File_Impl.java index d230aaf..72149fc 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Line_File_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Line_File_Impl.java @@ -3,6 +3,8 @@ package org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Content; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Line; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.enums.MongoDBEntryType; +import org.w3c.dom.Document; +import org.w3c.dom.Element; public class Line_File_Impl implements Content, Line { private final int contentId; @@ -34,4 +36,18 @@ public class Line_File_Impl implements Content, Line { public MongoDBEntryType getType() { return MongoDBEntryType.SPEECH_LINE; } + + @Override + public String toTeX() { + return this.getContent() + "\\\n"; + } + + @Override + public Element toXML(Document doc) { + Element line = doc.createElement("line"); + line.setAttribute("contentId", String.valueOf(this.getContentId())); + line.setAttribute("speechId", String.valueOf(this.getSpeechId())); + line.setTextContent(this.getContent()); + return line; + } } \ No newline at end of file diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Speaker_File_Impl.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Speaker_File_Impl.java index a71b8c1..8959244 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Speaker_File_Impl.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Impls/Speaker_File_Impl.java @@ -3,6 +3,8 @@ package org.texttechnologylab.project.gruppe_05_1.xml.speeches.Impls; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Content; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speaker; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.enums.MongoDBEntryType; +import org.w3c.dom.Document; +import org.w3c.dom.Element; public class Speaker_File_Impl implements Content, Speaker { @@ -49,4 +51,20 @@ public class Speaker_File_Impl implements Content, Speaker { public MongoDBEntryType getType() { return MongoDBEntryType.SPEECH_SPEAKER; } + + @Override + public String toTeX() { + return "\\textcolor{blue}{Redner/Rednerin}: " + this.getSpeakerName() + "\\\n"; + } + + @Override + public Element toXML(Document doc) { + Element speaker = doc.createElement("speaker"); + speaker.setAttribute("contentId", String.valueOf(this.getContentId())); + speaker.setAttribute("speechId", String.valueOf(this.getSpeechId())); + speaker.setAttribute("speakerId", String.valueOf(this.getSpeakerId())); + speaker.setAttribute("speakerName", this.getSpeakerName()); + speaker.setAttribute("fraction", this.getFraction()); + return speaker; + } } 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 fa765c5..aa62491 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 @@ -5,6 +5,8 @@ import org.apache.uima.jcas.JCas; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Content; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces.Speech; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.enums.MongoDBEntryType; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import java.util.ArrayList; @@ -103,4 +105,40 @@ public class Speech_File_Impl implements Speech { return null; } + @Override + public String toTeX() { + StringBuilder tex = new StringBuilder(); + + tex.append("\\NLP Metadata\\\n"); + + tex.append("$$NLPMETADATA$$\n"); + + tex.append("\\subsection*{Speech Content}\n"); + + // Add content block of speeches + for (Content content: this.getSpeechContents()) { + tex.append(content.toTeX()); + } + + + return tex.toString(); + } + + @Override + public Element toXML(Document doc) { + Element speech = doc.createElement("speech"); + speech.setAttribute("sessionId", String.valueOf(this.getSessionId())); + speech.setAttribute("agendaItemId", String.valueOf(this.getAgendaItemId())); + speech.setAttribute("speechId", String.valueOf(this.getSpeechId())); + speech.setAttribute("speakerId", String.valueOf(this.getSpeakerId())); + speech.setAttribute("speakerName", this.getSpeakerName()); + speech.setAttribute("fraction", this.getFraction()); + speech.setAttribute("speechKey", this.getSpeechKey()); + + for (Content content: this.getSpeechContents()) { + speech.appendChild(content.toXML(doc)); + } + + return speech; + } } diff --git a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Interfaces/Content.java b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Interfaces/Content.java index 1138cb7..da5955e 100644 --- a/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Interfaces/Content.java +++ b/src/main/java/org/texttechnologylab/project/gruppe_05_1/xml/speeches/Interfaces/Content.java @@ -1,6 +1,7 @@ package org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.enums.MongoDBEntryType; +import org.w3c.dom.Element; public interface Content { /** @@ -24,4 +25,19 @@ public interface Content { * @return The speech ID. */ int getSpeechId(); + + /** + * Returns the content as LaTeX. + * + * @return The content. + */ + String toTeX(); + + /** + * Returns the content as XML. + * + * @param doc The XML document. + * @return The content. + */ + Element toXML(org.w3c.dom.Document doc); } 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 642a24c..2152a6a 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 @@ -3,6 +3,8 @@ package org.texttechnologylab.project.gruppe_05_1.xml.speeches.Interfaces; import org.apache.uima.UIMAException; import org.apache.uima.jcas.JCas; import org.texttechnologylab.project.gruppe_05_1.xml.speeches.enums.MongoDBEntryType; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import java.util.List; @@ -85,4 +87,8 @@ public interface Speech { String getFullText(); JCas toCas() throws UIMAException; + + String toTeX(); + + Element toXML(Document doc); } diff --git a/src/main/resources/tex/preamble.tex b/src/main/resources/tex/preamble.tex new file mode 100644 index 0000000..ec2bf02 --- /dev/null +++ b/src/main/resources/tex/preamble.tex @@ -0,0 +1,24 @@ +\documentclass[a4paper]{article} +\usepackage{graphicx} +\usepackage{fancyhdr} +\usepackage{geometry} +\usepackage{xcolor} +\usepackage[T1]{fontenc} + +\pagestyle{fancy} + +\definecolor{darkgreen}{rgb}{0.0, 0.5, 0.0} + +\geometry{ + top=2.5cm, % Top margin + bottom=3cm, % Bottom margin + left=2.5cm, % Left margin + right=2.5cm, % Right margin + headheight=25pt, % Header height + footskip=1.5cm, % Space from the bottom margin to the baseline of the footer + headsep=0.5cm, % Space from the top margin to the baseline of the header + %showframe, % Uncomment to show how the type block is set on the page +} + +\rhead{Multimodal Parliament Explorer} +\lhead{PDF Speech Export for $$EXPORTCATEGORY$$}