From 784f125ed839f775c856cbe84ba628763e50c405 Mon Sep 17 00:00:00 2001 From: Richard Eckart de Castilho Date: Tue, 25 Jun 2024 11:47:27 +0200 Subject: [PATCH 1/5] #4904 - Upgrade to RDF4J 5.x - rdf4j 4.3.12 -> 5.0.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 377ced49f05..9417b15ce03 100644 --- a/pom.xml +++ b/pom.xml @@ -132,9 +132,9 @@ - 4.3.12 + 5.0.0 - + 4.6.1 From 41b137d532c1d24d7b5f02624144164b4251a878 Mon Sep 17 00:00:00 2001 From: Richard Eckart de Castilho Date: Sat, 7 Sep 2024 10:30:01 +0200 Subject: [PATCH 2/5] #5043 - Ability to specify token breaking zones when calling tokenizer - Added new signature to the tokenizer call - Added test - Consolicated existing code --- .../export/SegmentationUtilsTest.java | 35 +++-- .../support/uima/SegmentationUtils.java | 125 +++++++++++------- 2 files changed, 101 insertions(+), 59 deletions(-) diff --git a/inception/inception-export/src/test/java/de/tudarmstadt/ukp/inception/export/SegmentationUtilsTest.java b/inception/inception-export/src/test/java/de/tudarmstadt/ukp/inception/export/SegmentationUtilsTest.java index 8d146bf00e1..b777b439519 100644 --- a/inception/inception-export/src/test/java/de/tudarmstadt/ukp/inception/export/SegmentationUtilsTest.java +++ b/inception/inception-export/src/test/java/de/tudarmstadt/ukp/inception/export/SegmentationUtilsTest.java @@ -17,11 +17,13 @@ */ package de.tudarmstadt.ukp.inception.export; +import static de.tudarmstadt.ukp.inception.support.uima.SegmentationUtils.splitSentences; +import static de.tudarmstadt.ukp.inception.support.uima.SegmentationUtils.tokenize; +import static org.apache.uima.fit.factory.JCasFactory.createText; import static org.apache.uima.fit.util.CasUtil.toText; import static org.apache.uima.fit.util.JCasUtil.select; import static org.assertj.core.api.Assertions.assertThat; -import org.apache.uima.fit.factory.JCasFactory; import org.junit.jupiter.api.Test; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Div; @@ -29,16 +31,15 @@ import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Paragraph; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Sentence; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Token; -import de.tudarmstadt.ukp.inception.support.uima.SegmentationUtils; public class SegmentationUtilsTest { @Test public void testSplitSentences() throws Exception { - var jcas = JCasFactory.createText("I am one. I am two.", "en"); + var jcas = createText("I am one. I am two.", "en"); - SegmentationUtils.splitSentences(jcas.getCas()); + splitSentences(jcas.getCas()); assertThat(toText(select(jcas, Sentence.class))) // .containsExactly("I am one.", "I am two."); @@ -47,11 +48,11 @@ public void testSplitSentences() throws Exception @Test public void testSplitSentencesWithZones() throws Exception { - var jcas = JCasFactory.createText("Heading I am two.", "en"); + var jcas = createText("Heading I am two.", "en"); new Heading(jcas, 0, 7).addToIndexes(); new Paragraph(jcas, 8, 17).addToIndexes(); - SegmentationUtils.splitSentences(jcas.getCas(), jcas.select(Div.class)); + splitSentences(jcas.getCas(), jcas.select(Div.class)); assertThat(toText(select(jcas, Sentence.class))) // .containsExactly("Heading", "I am two."); @@ -60,11 +61,11 @@ public void testSplitSentencesWithZones() throws Exception @Test public void testTokenize() throws Exception { - var jcas = JCasFactory.createText("i am one.i am two.", "en"); + var jcas = createText("i am one.i am two.", "en"); new Sentence(jcas, 0, 9).addToIndexes(); new Sentence(jcas, 9, 18).addToIndexes(); - SegmentationUtils.tokenize(jcas.getCas()); + tokenize(jcas.getCas()); assertThat(toText(select(jcas, Sentence.class))) // .containsExactly("i am one.", "i am two."); @@ -72,4 +73,22 @@ public void testTokenize() throws Exception assertThat(toText(select(jcas, Token.class))) // .containsExactly("i", "am", "one", ".", "i", "am", "two", "."); } + + @Test + public void testTokenizeWitZones() throws Exception + { + var jcas = createText("i am one.i am two.", "en"); + new Sentence(jcas, 0, 9).addToIndexes(); + new Sentence(jcas, 9, 18).addToIndexes(); + new Div(jcas, 3, 3).addToIndexes(); + new Div(jcas, 12, 15).addToIndexes(); + + tokenize(jcas.getCas(), jcas.select(Div.class)); + + assertThat(toText(select(jcas, Sentence.class))) // + .containsExactly("i am one.", "i am two."); + + assertThat(toText(select(jcas, Token.class))) // + .containsExactly("i", "a", "m", "one", ".", "i", "a", "m", "t", "wo", "."); + } } diff --git a/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/uima/SegmentationUtils.java b/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/uima/SegmentationUtils.java index d5bc3633ba4..cb6b7894f45 100644 --- a/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/uima/SegmentationUtils.java +++ b/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/uima/SegmentationUtils.java @@ -20,7 +20,8 @@ import static de.tudarmstadt.ukp.inception.support.uima.WebAnnoCasUtil.createSentence; import static de.tudarmstadt.ukp.inception.support.uima.WebAnnoCasUtil.createToken; import static de.tudarmstadt.ukp.inception.support.uima.WebAnnoCasUtil.selectSentences; -import static org.apache.uima.fit.util.CasUtil.getType; +import static java.text.BreakIterator.DONE; +import static java.util.Locale.US; import java.text.BreakIterator; import java.util.Locale; @@ -28,7 +29,6 @@ import org.apache.uima.cas.CAS; import org.apache.uima.cas.text.AnnotationFS; -import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Sentence; import de.tudarmstadt.ukp.inception.support.text.TrimUtils; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -42,7 +42,7 @@ private SegmentationUtils() public static void segment(CAS aCas) { splitSentences(aCas, null); - tokenize(aCas); + tokenize(aCas, null); } public static void splitSentences(CAS aCas) @@ -56,12 +56,11 @@ public static void splitSentences(CAS aCas, int aBegin, int aEnd) bi.setText(aCas.getDocumentText().substring(aBegin, aEnd)); var last = bi.first(); var cur = bi.next(); - while (cur != BreakIterator.DONE) { - var sentence = aCas.createAnnotation(getType(aCas, Sentence.class), last + aBegin, - cur + aBegin); - sentence.trim(); - if (sentence.getBegin() != sentence.getEnd()) { - aCas.addFsToIndexes(sentence); + while (cur != DONE) { + var span = new int[] { last + aBegin, cur + aBegin }; + TrimUtils.trim(aCas.getDocumentText(), span); + if (!isEmpty(span[0], span[1])) { + aCas.addFsToIndexes(createSentence(aCas, span[0], span[1])); } last = cur; cur = bi.next(); @@ -74,62 +73,65 @@ public static void splitSentences(CAS aCas, Iterable aZo return; } - int[] sortedZoneBoundaries = null; - - if (aZones != null) { - var zoneBoundaries = new IntArrayList(); - for (var zone : aZones) { - zoneBoundaries.add(zone.getBegin()); - zoneBoundaries.add(zone.getEnd()); - } - - sortedZoneBoundaries = zoneBoundaries.intStream().distinct().sorted().toArray(); - } - - if (sortedZoneBoundaries == null || sortedZoneBoundaries.length < 2) { - sortedZoneBoundaries = new int[] { 0, aCas.getDocumentText().length() }; - } + int[] sortedZoneBoundaries = sortedZoneBoundaries(aCas, aZones); for (int i = 1; i < sortedZoneBoundaries.length; i++) { var begin = sortedZoneBoundaries[i - 1]; var end = sortedZoneBoundaries[i]; - var bi = BreakIterator.getSentenceInstance(Locale.US); - bi.setText(aCas.getDocumentText().substring(begin, end)); - var last = bi.first(); - var cur = bi.next(); - while (cur != BreakIterator.DONE) { - var span = new int[] { last + begin, cur + begin }; - TrimUtils.trim(aCas.getDocumentText(), span); - if (!isEmpty(span[0], span[1])) { - aCas.addFsToIndexes(createSentence(aCas, span[0], span[1])); - } - last = cur; - cur = bi.next(); - } + + splitSentences(aCas, begin, end); } } public static void tokenize(CAS aCas) + { + tokenize(aCas, null); + } + + public static void tokenize(CAS aCas, Iterable aZones) { if (aCas.getDocumentText() == null) { return; } - BreakIterator bi = BreakIterator.getWordInstance(Locale.US); - for (AnnotationFS s : selectSentences(aCas)) { - bi.setText(s.getCoveredText()); - int last = bi.first(); - int cur = bi.next(); - while (cur != BreakIterator.DONE) { - int[] span = new int[] { last, cur }; - TrimUtils.trim(s.getCoveredText(), span); - if (!isEmpty(span[0], span[1])) { - aCas.addFsToIndexes( - createToken(aCas, span[0] + s.getBegin(), span[1] + s.getBegin())); - } - last = cur; - cur = bi.next(); + var sortedZoneBoundaries = sortedZoneBoundaries(aCas, aZones); + var zbi = 0; + + for (var s : selectSentences(aCas)) { + var innerZoneBoundariesBuffer = new IntArrayList(); + innerZoneBoundariesBuffer.add(s.getBegin()); + innerZoneBoundariesBuffer.add(s.getEnd()); + while (zbi < sortedZoneBoundaries.length && sortedZoneBoundaries[zbi] >= s.getBegin() + && sortedZoneBoundaries[zbi] < s.getEnd()) { + innerZoneBoundariesBuffer.add(sortedZoneBoundaries[zbi]); + zbi++; + } + + var innerZoneBoundaries = innerZoneBoundariesBuffer.intStream().distinct().sorted() + .toArray(); + + for (int i = 1; i < innerZoneBoundaries.length; i++) { + var begin = innerZoneBoundaries[i - 1]; + var end = innerZoneBoundaries[i]; + tokenize(aCas, begin, end); + } + } + } + + private static void tokenize(CAS aCas, int aBegin, int aEnd) + { + var bi = BreakIterator.getWordInstance(US); + bi.setText(aCas.getDocumentText().substring(aBegin, aEnd)); + var last = bi.first(); + var cur = bi.next(); + while (cur != DONE) { + var span = new int[] { last + aBegin, cur + aBegin }; + TrimUtils.trim(aCas.getDocumentText(), span); + if (!isEmpty(span[0], span[1])) { + aCas.addFsToIndexes(createToken(aCas, span[0], span[1])); } + last = cur; + cur = bi.next(); } } @@ -137,4 +139,25 @@ public static boolean isEmpty(int aBegin, int aEnd) { return aBegin >= aEnd; } + + private static int[] sortedZoneBoundaries(CAS aCas, Iterable aZones) + { + int[] sortedZoneBoundaries = null; + + if (aZones != null) { + var zoneBoundaries = new IntArrayList(); + for (var zone : aZones) { + zoneBoundaries.add(zone.getBegin()); + zoneBoundaries.add(zone.getEnd()); + } + + sortedZoneBoundaries = zoneBoundaries.intStream().distinct().sorted().toArray(); + } + + if (sortedZoneBoundaries == null || sortedZoneBoundaries.length < 2) { + sortedZoneBoundaries = new int[] { 0, aCas.getDocumentText().length() }; + } + + return sortedZoneBoundaries; + } } From c3ff859f73e8407cc7dfe00cf0c25bae3a5e510c Mon Sep 17 00:00:00 2001 From: Richard Eckart de Castilho Date: Sat, 7 Sep 2024 10:58:03 +0200 Subject: [PATCH 3/5] No issue: Minor cleaning up --- .../webanno/api/format/FormatSupport.java | 30 +++++++++---------- .../DocumentImportExportServiceImpl.java | 21 ++++++++++--- .../io/html/MHtmlDocumentReader.java | 2 +- .../io/html/dkprocore/CasXmlNodeVisitor.java | 7 +++-- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/inception/inception-api-formats/src/main/java/de/tudarmstadt/ukp/clarin/webanno/api/format/FormatSupport.java b/inception/inception-api-formats/src/main/java/de/tudarmstadt/ukp/clarin/webanno/api/format/FormatSupport.java index 06731ccc668..6a20d467aa9 100644 --- a/inception/inception-api-formats/src/main/java/de/tudarmstadt/ukp/clarin/webanno/api/format/FormatSupport.java +++ b/inception/inception-api-formats/src/main/java/de/tudarmstadt/ukp/clarin/webanno/api/format/FormatSupport.java @@ -19,9 +19,13 @@ import static de.tudarmstadt.ukp.inception.support.io.ZipUtils.zipFolder; import static java.io.File.createTempFile; +import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableSet; +import static java.util.Optional.empty; import static org.apache.commons.io.FileUtils.copyFile; +import static org.apache.commons.io.FilenameUtils.getBaseName; import static org.apache.commons.io.FilenameUtils.getExtension; +import static org.apache.commons.lang3.StringUtils.rightPad; import static org.apache.uima.fit.factory.AnalysisEngineFactory.createEngine; import static org.apache.uima.fit.factory.CollectionReaderFactory.createReader; import static org.apache.uima.fit.factory.ConfigurationParameterFactory.addConfigurationParameters; @@ -32,13 +36,10 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.uima.analysis_engine.AnalysisEngine; import org.apache.uima.analysis_engine.AnalysisEngineDescription; import org.apache.uima.analysis_engine.AnalysisEngineProcessException; @@ -124,7 +125,7 @@ default InputStream openResourceStream(File aDocFile, String aResourcePath) thro */ default List getCssStylesheets() { - return Collections.emptyList(); + return emptyList(); } /** @@ -132,12 +133,12 @@ default List getCssStylesheets() */ default List getSectionElements() { - return Collections.emptyList(); + return emptyList(); } default Optional getPolicy() throws IOException { - return Optional.empty(); + return empty(); } /** @@ -203,7 +204,7 @@ default File write(SourceDocument aDocument, CAS aCas, File aTargetFolder, boolean aStripExtension) throws ResourceInitializationException, AnalysisEngineProcessException, IOException { - AnalysisEngineDescription writer = getWriterDescription(aDocument.getProject(), null, aCas); + var writer = getWriterDescription(aDocument.getProject(), null, aCas); addConfigurationParameters(writer, // JCasFileWriter_ImplBase.PARAM_USE_DOCUMENT_ID, true, JCasFileWriter_ImplBase.PARAM_ESCAPE_FILENAME, false, @@ -225,21 +226,18 @@ default File write(SourceDocument aDocument, CAS aCas, File aTargetFolder, // If the writer produced more than one file, we package it up as a ZIP file if (aTargetFolder.listFiles().length > 1) { - File exportFile = createTempFile("inception-document", ".zip"); - // File exportFile = new File(aTargetFolder.getAbsolutePath() + ".zip"); + var exportFile = createTempFile("inception-document", ".zip"); zipFolder(aTargetFolder, exportFile); return exportFile; } // If the writer produced only a single file, then that is the result - String filename = FilenameUtils.getBaseName(aTargetFolder.listFiles()[0].getName()); + var exportedFile = aTargetFolder.listFiles()[0]; // temp-file prefix must be at least 3 chars - filename = StringUtils.rightPad(filename, 3, "_"); - File exportFile = createTempFile(filename, - "." + FilenameUtils.getExtension(aTargetFolder.listFiles()[0].getName())); - // File exportFile = new File(aTargetFolder.getParent(), - // aTargetFolder.listFiles()[0].getName()); - copyFile(aTargetFolder.listFiles()[0], exportFile); + var baseName = rightPad(getBaseName(exportedFile.getName()), 3, "_"); + var extension = getExtension(exportedFile.getName()); + var exportFile = createTempFile(baseName, "." + extension); + copyFile(exportedFile, exportFile); return exportFile; } } diff --git a/inception/inception-export/src/main/java/de/tudarmstadt/ukp/inception/export/DocumentImportExportServiceImpl.java b/inception/inception-export/src/main/java/de/tudarmstadt/ukp/inception/export/DocumentImportExportServiceImpl.java index 2b8a6f56c27..2fb672975db 100644 --- a/inception/inception-export/src/main/java/de/tudarmstadt/ukp/inception/export/DocumentImportExportServiceImpl.java +++ b/inception/inception-export/src/main/java/de/tudarmstadt/ukp/inception/export/DocumentImportExportServiceImpl.java @@ -335,8 +335,11 @@ private CAS importCasFromFileNoChecks(File aFile, SourceDocument aDocument, Stri // Create sentence / token annotations if they are missing - sentences first because // tokens are then generated inside the sentences - splitSenencesIfNecssaryAndCheckQuota(cas, format); - splitTokensIfNecssaryAndCheckQuota(cas, format); + splitSenencesIfNecssary(cas, format); + checkSentenceQuota(cas, format); + + splitTokens(cas, format); + checkTokenQuota(cas, format); LOG.info("Imported CAS with [{}] tokens and [{}] sentences from file [{}] (size: {} bytes)", cas.getAnnotationIndex(getType(cas, Token.class)).size(), @@ -363,7 +366,7 @@ private void runCasDoctorOnImport(SourceDocument aDocument, FormatSupport aForma casDoctor.analyze(aDocument.getProject(), aCas, messages, true); } - private void splitTokensIfNecssaryAndCheckQuota(CAS cas, FormatSupport aFormat) + private void splitTokens(CAS cas, FormatSupport aFormat) throws IOException { var tokenType = getType(cas, Token.class); @@ -371,6 +374,11 @@ private void splitTokensIfNecssaryAndCheckQuota(CAS cas, FormatSupport aFormat) if (!exists(cas, tokenType)) { SegmentationUtils.tokenize(cas); } + } + + private void checkTokenQuota(CAS cas, FormatSupport aFormat) throws IOException + { + var tokenType = getType(cas, Token.class); if (properties.getMaxTokens() > 0) { var tokenCount = cas.getAnnotationIndex(tokenType).size(); @@ -388,7 +396,7 @@ private void splitTokensIfNecssaryAndCheckQuota(CAS cas, FormatSupport aFormat) } } - private void splitSenencesIfNecssaryAndCheckQuota(CAS cas, FormatSupport aFormat) + private void splitSenencesIfNecssary(CAS cas, FormatSupport aFormat) throws IOException { var sentenceType = getType(cas, Sentence.class); @@ -396,6 +404,11 @@ private void splitSenencesIfNecssaryAndCheckQuota(CAS cas, FormatSupport aFormat if (!exists(cas, sentenceType)) { SegmentationUtils.splitSentences(cas); } + } + + private void checkSentenceQuota(CAS cas, FormatSupport aFormat) throws IOException + { + var sentenceType = getType(cas, Sentence.class); if (properties.getMaxSentences() > 0) { var sentenceCount = cas.getAnnotationIndex(sentenceType).size(); diff --git a/inception/inception-io-html/src/main/java/de/tudarmstadt/ukp/inception/io/html/MHtmlDocumentReader.java b/inception/inception-io-html/src/main/java/de/tudarmstadt/ukp/inception/io/html/MHtmlDocumentReader.java index 5239d8c8c96..94b5187361c 100644 --- a/inception/inception-io-html/src/main/java/de/tudarmstadt/ukp/inception/io/html/MHtmlDocumentReader.java +++ b/inception/inception-io-html/src/main/java/de/tudarmstadt/ukp/inception/io/html/MHtmlDocumentReader.java @@ -61,7 +61,7 @@ public class MHtmlDocumentReader @Override public void getNext(JCas aJCas) throws IOException, CollectionException { - Resource res = nextFile(); + var res = nextFile(); initCas(aJCas, res); try (var is = res.getInputStream()) { diff --git a/inception/inception-io-html/src/main/java/de/tudarmstadt/ukp/inception/io/html/dkprocore/CasXmlNodeVisitor.java b/inception/inception-io-html/src/main/java/de/tudarmstadt/ukp/inception/io/html/dkprocore/CasXmlNodeVisitor.java index d0859325870..ea6103d94d1 100644 --- a/inception/inception-io-html/src/main/java/de/tudarmstadt/ukp/inception/io/html/dkprocore/CasXmlNodeVisitor.java +++ b/inception/inception-io-html/src/main/java/de/tudarmstadt/ukp/inception/io/html/dkprocore/CasXmlNodeVisitor.java @@ -24,7 +24,6 @@ import java.util.Map; import org.apache.uima.jcas.JCas; -import org.jsoup.nodes.Attribute; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; @@ -75,17 +74,19 @@ else if (node instanceof TextNode textNode) { handler.characters(text, 0, text.length); } else if (node instanceof Element element) { + // Insert a space character after
elements to give the tokenizer an opportunity + // to insert a token here. if (!handler.getText().isEmpty() && (element.isBlock() || "br".equalsIgnoreCase(element.nodeName())) && !lastCharIsWhitespace(handler.getText())) { - char[] text = " ".toCharArray(); + var text = " ".toCharArray(); handler.characters(text, 0, text.length); } var attributes = new AttributesImpl(); if (element.attributes() != null) { - for (Attribute attr : element.attributes()) { + for (var attr : element.attributes()) { attributes.addAttribute("", "", attr.getKey(), "CDATA", attr.getValue()); } } From 18d1ad1f0bad19ac0a8ecf9ed094256f4787bdf0 Mon Sep 17 00:00:00 2001 From: Richard Eckart de Castilho Date: Sat, 7 Sep 2024 14:44:17 +0200 Subject: [PATCH 4/5] #4949 - Showing the start and end points of relations in left side bar - Display start and end of a relation in layer-grouping mode - Sort link targets under link sources in layer-grouping mode - Generally try to impove the visuals a bit --- .../diam/model/compactv2/CompactLayer.java | 9 +- .../compactv2/CompactSerializerV2Impl.java | 2 +- .../test/resources/compactv2/reference.json | 6 +- .../main/ts/src/AnnotationsByLabelList.svelte | 4 +- .../main/ts/src/AnnotationsByLayerList.svelte | 131 +++++++++++++----- .../ts/src/AnnotationsByPositionList.svelte | 6 +- .../src/main/ts/src/LabelBadge.svelte | 6 +- .../src/main/ts/src/SpanText.svelte | 2 +- .../src/main/ts/src/Utils.ts | 4 + .../src/main/ts/src/model/Layer.ts | 1 + .../ts/src/model/compact_v2/CompactLayer.ts | 2 + 11 files changed, 125 insertions(+), 48 deletions(-) diff --git a/inception/inception-diam-compactv2/src/main/java/de/tudarmstadt/ukp/inception/diam/model/compactv2/CompactLayer.java b/inception/inception-diam-compactv2/src/main/java/de/tudarmstadt/ukp/inception/diam/model/compactv2/CompactLayer.java index b4fa16f6962..05740aa7fa6 100644 --- a/inception/inception-diam-compactv2/src/main/java/de/tudarmstadt/ukp/inception/diam/model/compactv2/CompactLayer.java +++ b/inception/inception-diam-compactv2/src/main/java/de/tudarmstadt/ukp/inception/diam/model/compactv2/CompactLayer.java @@ -21,11 +21,13 @@ public class CompactLayer { private final long id; private final String name; + private final String type; - public CompactLayer(long aId, String aName) + public CompactLayer(long aId, String aName, String aType) { id = aId; name = aName; + type = aType; } public long getId() @@ -37,4 +39,9 @@ public String getName() { return name; } + + public String getType() + { + return type; + } } diff --git a/inception/inception-diam-compactv2/src/main/java/de/tudarmstadt/ukp/inception/diam/model/compactv2/CompactSerializerV2Impl.java b/inception/inception-diam-compactv2/src/main/java/de/tudarmstadt/ukp/inception/diam/model/compactv2/CompactSerializerV2Impl.java index b90b0cd26bf..b110e3c6cdb 100644 --- a/inception/inception-diam-compactv2/src/main/java/de/tudarmstadt/ukp/inception/diam/model/compactv2/CompactSerializerV2Impl.java +++ b/inception/inception-diam-compactv2/src/main/java/de/tudarmstadt/ukp/inception/diam/model/compactv2/CompactSerializerV2Impl.java @@ -99,7 +99,7 @@ private void renderLayers(RenderRequest aRequest, CompactAnnotatedText aResponse continue; } - layers.add(new CompactLayer(layer.getId(), layer.getUiName())); + layers.add(new CompactLayer(layer.getId(), layer.getUiName(), layer.getType())); for (VSpan vspan : aVDoc.spans(layer.getId())) { var cspan = renderSpan(aRequest, vspan); diff --git a/inception/inception-diam-compactv2/src/test/resources/compactv2/reference.json b/inception/inception-diam-compactv2/src/test/resources/compactv2/reference.json index 1f35213b881..f905050963d 100644 --- a/inception/inception-diam-compactv2/src/test/resources/compactv2/reference.json +++ b/inception/inception-diam-compactv2/src/test/resources/compactv2/reference.json @@ -2,10 +2,12 @@ "action" : "getAnnotatedDocument", "layers" : [ { "id" : 1, - "name" : "Span" + "name" : "Span", + "type" : "span" }, { "id" : 2, - "name" : "Relation" + "name" : "Relation", + "type" : "relation" } ], "text" : "This is a test.", "window" : [ 0, 15 ], diff --git a/inception/inception-diam-editor/src/main/ts/src/AnnotationsByLabelList.svelte b/inception/inception-diam-editor/src/main/ts/src/AnnotationsByLabelList.svelte index f38788d8cb6..fffece6ffd6 100644 --- a/inception/inception-diam-editor/src/main/ts/src/AnnotationsByLabelList.svelte +++ b/inception/inception-diam-editor/src/main/ts/src/AnnotationsByLabelList.svelte @@ -88,7 +88,9 @@ return b.score - a.score; } - return compareOffsets(targetA.offsets[0], targetB.offsets[0]) + const targetA = a.arguments[0].target as Span + const targetB = b.arguments[0].target as Span + return compareOffsets(targetA.offsets[0], targetB.offsets[0]); } console.error("Unexpected annotation type combination", a, b); diff --git a/inception/inception-diam-editor/src/main/ts/src/AnnotationsByLayerList.svelte b/inception/inception-diam-editor/src/main/ts/src/AnnotationsByLayerList.svelte index cffb09dcca0..8578b7c88af 100644 --- a/inception/inception-diam-editor/src/main/ts/src/AnnotationsByLayerList.svelte +++ b/inception/inception-diam-editor/src/main/ts/src/AnnotationsByLayerList.svelte @@ -30,12 +30,13 @@ import { compareOffsets } from "@inception-project/inception-js-api/src/model/Offsets"; import LabelBadge from "./LabelBadge.svelte"; import SpanText from "./SpanText.svelte"; - import { compareSpanText, debounce, filterAnnotations, getCoveredText, groupBy, uniqueLayers } from "./Utils"; + import { compareSpanText, debounce, filterAnnotations, groupRelationsBySource, groupBy, uniqueLayers } from "./Utils"; import { sortByScore, recommendationsFirst } from "./AnnotationBrowserState" export let ajaxClient: DiamAjax; export let data: AnnotatedText; + let groupedRelations: Record; let groupedAnnotations: Record; let groups: { layer: Layer, collapsed: boolean }[] let collapsedGroups = new Set() @@ -55,6 +56,8 @@ (s) => s.layer.name ) + groupedRelations = groupRelationsBySource(data); + for (let [key, items] of Object.entries(groupedAnnotations)) { items = filterAnnotations(data, items, filter) items.sort((a, b) => { @@ -193,6 +196,7 @@ {#each groups as group}
  • +
    toggleCollapsed(group)} @@ -200,49 +204,104 @@ - {group.layer.name} + {group.layer.name} {group.layer.type}
      {#if groupedAnnotations[group.layer.name]} {#each groupedAnnotations[group.layer.name] as ann} -
    • mouseOverAnnotation(ev, ann)} - on:mouseout={ev => mouseOutAnnotation(ev, ann)} - > -
      mouseOverAnnotation(ev, ann)} + on:mouseout={ev => mouseOutAnnotation(ev, ann)} > - {#if ann instanceof Span} -
      - {:else if ann instanceof Relation} -
      - {/if} -
      - -
      scrollTo(ann)} - > -
      - + + +
      scrollTo(ann)}> +
      + +
      +
      +
    • - {#if ann instanceof Span} - - {:else if ann instanceof Relation} - - {/if} - - + {@const relations = groupedRelations[`${ann.vid}`]} + {#if relations} + {#each relations as relation} + {@const target = relation.arguments[1].target} + +
    • + mouseOverAnnotation(ev, relation)} + on:mouseout={(ev) => + mouseOutAnnotation(ev, relation)} + > +
      + +
      + + +
      scrollTo(target)} + > +
      + +
      + + +
      +
    • + {/each} + {/if} + {:else if ann instanceof Relation && group.layer.type === "relation"} +
    • mouseOverAnnotation(ev, ann)} + on:mouseout={ev => mouseOutAnnotation(ev, ann)} + > + + +
      +
      + +
      +
      scrollTo(ann.arguments[0].target)}> +
      + +
      + +
      +
      scrollTo(ann.arguments[1].target)}> +
      + +
      + +
      +
      +
    • + {/if} {/each} {:else}
    • diff --git a/inception/inception-diam-editor/src/main/ts/src/AnnotationsByPositionList.svelte b/inception/inception-diam-editor/src/main/ts/src/AnnotationsByPositionList.svelte index aedd0a62b36..4dbd02f19e1 100644 --- a/inception/inception-diam-editor/src/main/ts/src/AnnotationsByPositionList.svelte +++ b/inception/inception-diam-editor/src/main/ts/src/AnnotationsByPositionList.svelte @@ -29,7 +29,7 @@ import LabelBadge from "./LabelBadge.svelte"; import SpanText from "./SpanText.svelte"; import { - debounce, + debounce, groupRelationsByPosition, groupSpansByPosition, uniqueOffsets, @@ -103,7 +103,7 @@ class="flex-grow-1 my-1 mx-2 overflow-hidden" on:click={() => scrollToSpan(firstSpan)} > -
      +
      {#each spans as span} @@ -144,7 +144,7 @@ class="flex-grow-1 my-1 mx-2 overflow-hidden" on:click={() => scrollToRelation(relation)} > -
      +
      {#if annotation.vid.toString().startsWith("rec:")} -
      +