diff --git a/inception/inception-brat-editor/src/test/java/de/tudarmstadt/ukp/clarin/webanno/brat/render/BratSerializerImplTest.java b/inception/inception-brat-editor/src/test/java/de/tudarmstadt/ukp/clarin/webanno/brat/render/BratSerializerImplTest.java index 17b42b19d05..94a860d4ec3 100644 --- a/inception/inception-brat-editor/src/test/java/de/tudarmstadt/ukp/clarin/webanno/brat/render/BratSerializerImplTest.java +++ b/inception/inception-brat-editor/src/test/java/de/tudarmstadt/ukp/clarin/webanno/brat/render/BratSerializerImplTest.java @@ -23,6 +23,8 @@ import static java.util.Arrays.asList; import static org.apache.uima.fit.factory.AnalysisEngineFactory.createEngine; import static org.apache.uima.fit.factory.CollectionReaderFactory.createReader; +import static org.apache.uima.fit.util.CasUtil.getType; +import static org.apache.uima.fit.util.CasUtil.select; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.contentOf; import static org.mockito.ArgumentMatchers.any; @@ -32,6 +34,7 @@ import org.apache.uima.analysis_engine.AnalysisEngine; import org.apache.uima.cas.CAS; +import org.apache.uima.cas.text.AnnotationFS; import org.apache.uima.collection.CollectionReader; import org.apache.uima.fit.factory.JCasFactory; import org.dkpro.core.io.tcf.TcfReader; @@ -59,6 +62,7 @@ import de.tudarmstadt.ukp.clarin.webanno.model.Project; import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocument; import de.tudarmstadt.ukp.dkpro.core.api.lexmorph.type.pos.POS; +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.annotation.feature.bool.BooleanFeatureSupport; import de.tudarmstadt.ukp.inception.annotation.feature.link.LinkFeatureSupport; @@ -76,7 +80,6 @@ import de.tudarmstadt.ukp.inception.schema.api.AnnotationSchemaService; import de.tudarmstadt.ukp.inception.schema.service.FeatureSupportRegistryImpl; import de.tudarmstadt.ukp.inception.support.json.JSONUtil; -import de.tudarmstadt.ukp.inception.support.uima.WebAnnoCasUtil; @ExtendWith(MockitoExtension.class) public class BratSerializerImplTest @@ -180,7 +183,7 @@ public void thatSentenceOrientedStrategyRenderCorrectly() throws Exception state.setAllAnnotationLayers(schemaService.listAnnotationLayer(project)); state.setPagingStrategy(new SentenceOrientedPagingStrategy()); state.getPreferences().setWindowSize(10); - state.setFirstVisibleUnit(WebAnnoCasUtil.getFirstSentence(cas)); + state.setFirstVisibleUnit(getFirstSentence(cas)); state.setProject(project); state.setDocument(sourceDocument, asList(sourceDocument)); @@ -219,7 +222,7 @@ public void thatLineOrientedStrategyRenderCorrectly() throws Exception AnnotatorState state = new AnnotatorStateImpl(Mode.ANNOTATION); state.setPagingStrategy(new LineOrientedPagingStrategy()); state.getPreferences().setWindowSize(10); - state.setFirstVisibleUnit(WebAnnoCasUtil.getFirstSentence(cas)); + state.setFirstVisibleUnit(getFirstSentence(cas)); state.setProject(project); state.setDocument(sourceDocument, asList(sourceDocument)); @@ -258,7 +261,7 @@ public void thatTokenWrappingStrategyRenderCorrectly() throws Exception AnnotatorState state = new AnnotatorStateImpl(Mode.ANNOTATION); state.setPagingStrategy(new TokenWrappingPagingStrategy(80)); state.getPreferences().setWindowSize(10); - state.setFirstVisibleUnit(WebAnnoCasUtil.getFirstSentence(cas)); + state.setFirstVisibleUnit(getFirstSentence(cas)); state.setProject(project); state.setDocument(sourceDocument, asList(sourceDocument)); @@ -281,4 +284,22 @@ public void thatTokenWrappingStrategyRenderCorrectly() throws Exception assertThat(contentOf(new File("src/test/resources/longlines.json"), UTF_8)) .isEqualToNormalizingNewlines(contentOf(new File(jsonFilePath), UTF_8)); } + + /** + * Get the internal address of the first sentence annotation from CAS. This will be used as a + * reference for moving forward/backward sentences positions + * + * @param aCas + * The CAS object assumed to contains some sentence annotations + * @return the sentence number or -1 if aCas don't have sentence annotation + */ + private static AnnotationFS getFirstSentence(CAS aCas) + { + AnnotationFS firstSentence = null; + for (AnnotationFS s : select(aCas, getType(aCas, Sentence.class))) { + firstSentence = s; + break; + } + return firstSentence; + } } diff --git a/inception/inception-concept-linking/src/main/java/de/tudarmstadt/ukp/inception/conceptlinking/service/ConceptLinkingServiceImpl.java b/inception/inception-concept-linking/src/main/java/de/tudarmstadt/ukp/inception/conceptlinking/service/ConceptLinkingServiceImpl.java index b3dd2550e95..deb162aa965 100644 --- a/inception/inception-concept-linking/src/main/java/de/tudarmstadt/ukp/inception/conceptlinking/service/ConceptLinkingServiceImpl.java +++ b/inception/inception-concept-linking/src/main/java/de/tudarmstadt/ukp/inception/conceptlinking/service/ConceptLinkingServiceImpl.java @@ -24,13 +24,13 @@ import static de.tudarmstadt.ukp.inception.conceptlinking.model.CandidateEntity.KEY_QUERY; import static de.tudarmstadt.ukp.inception.conceptlinking.model.CandidateEntity.KEY_QUERY_BEST_MATCH_TERM_NC; import static de.tudarmstadt.ukp.inception.conceptlinking.model.CandidateEntity.KEY_QUERY_NC; -import static de.tudarmstadt.ukp.inception.support.uima.WebAnnoCasUtil.selectSentenceCovering; -import static de.tudarmstadt.ukp.inception.support.uima.WebAnnoCasUtil.selectTokensCovered; import static java.lang.System.currentTimeMillis; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; import static java.util.Comparator.comparingInt; import static java.util.stream.Collectors.toCollection; +import static org.apache.uima.fit.util.CasUtil.getType; +import static org.apache.uima.fit.util.CasUtil.select; import java.io.File; import java.net.URISyntaxException; @@ -47,6 +47,7 @@ import org.apache.commons.lang3.Validate; import org.apache.uima.cas.CAS; import org.apache.uima.cas.text.AnnotationFS; +import org.apache.uima.fit.util.CasUtil; import org.eclipse.rdf4j.common.net.ParsedIRI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,6 +59,8 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator; import de.tudarmstadt.ukp.clarin.webanno.model.Project; +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.conceptlinking.config.EntityLinkingProperties; import de.tudarmstadt.ukp.inception.conceptlinking.config.EntityLinkingPropertiesImpl; import de.tudarmstadt.ukp.inception.conceptlinking.config.EntityLinkingServiceAutoConfiguration; @@ -513,4 +516,31 @@ public List searchItems(KnowledgeBase aKB, String aQuery) { return disambiguate(aKB, null, ConceptFeatureValueType.ANY_OBJECT, aQuery, null, 0, null); } + + /** + * Get the sentence based on the annotation begin offset + * + * @param aCas + * the CAS. + * @param aBegin + * the begin offset. + * @return the sentence. + */ + private static AnnotationFS selectSentenceCovering(CAS aCas, int aBegin) + { + AnnotationFS currentSentence = null; + for (AnnotationFS sentence : select(aCas, getType(aCas, Sentence.class))) { + if (sentence.getBegin() <= aBegin && sentence.getEnd() > aBegin) { + currentSentence = sentence; + break; + } + } + return currentSentence; + } + + private static Collection selectTokensCovered(AnnotationFS aCover) + { + return CasUtil.selectCovered(aCover.getCAS(), getType(aCover.getCAS(), Token.class), + aCover); + } } diff --git a/inception/inception-diag/src/main/java/de/tudarmstadt/ukp/clarin/webanno/diag/checks/UnreachableAnnotationsCheck.java b/inception/inception-diag/src/main/java/de/tudarmstadt/ukp/clarin/webanno/diag/checks/UnreachableAnnotationsCheck.java index b49a7d5c443..32a784a45b2 100644 --- a/inception/inception-diag/src/main/java/de/tudarmstadt/ukp/clarin/webanno/diag/checks/UnreachableAnnotationsCheck.java +++ b/inception/inception-diag/src/main/java/de/tudarmstadt/ukp/clarin/webanno/diag/checks/UnreachableAnnotationsCheck.java @@ -24,10 +24,13 @@ import static org.apache.uima.cas.impl.Serialization.deserializeCASComplete; import static org.apache.uima.cas.impl.Serialization.serializeCASComplete; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.uima.cas.CAS; +import org.apache.uima.cas.FeatureStructure; import org.apache.uima.cas.impl.CASImpl; import org.apache.uima.resource.ResourceInitializationException; @@ -93,8 +96,15 @@ public static CASImpl makeDummyCas() public static Map countFeatureStructures(CASImpl casImpl) { - return WebAnnoCasUtil.findAllFeatureStructures(casImpl).stream() // + return findAllFeatureStructures(casImpl).stream() // .map(fs -> fs.getType().getName()) // .collect(groupingBy(identity(), counting())); } + + public static Set findAllFeatureStructures(CAS aCas) + { + Set allFSes = new LinkedHashSet<>(); + ((CASImpl) aCas).walkReachablePlusFSsSorted(allFSes::add, null, null, null); + return allFSes; + } } diff --git a/inception/inception-external-search-pubmed/pom.xml b/inception/inception-external-search-pubmed/pom.xml index 0884f530447..003cc265e0a 100644 --- a/inception/inception-external-search-pubmed/pom.xml +++ b/inception/inception-external-search-pubmed/pom.xml @@ -86,6 +86,10 @@ com.fasterxml.jackson.core jackson-annotations + + com.fasterxml.jackson.core + jackson-databind + com.fasterxml.jackson.dataformat jackson-dataformat-xml diff --git a/inception/inception-external-search-pubmed/src/main/java/de/tudarmstadt/ukp/inception/externalsearch/pubmed/pmcoa/PmcOaClient.java b/inception/inception-external-search-pubmed/src/main/java/de/tudarmstadt/ukp/inception/externalsearch/pubmed/pmcoa/PmcOaClient.java index 6a6ad19bae7..a020025c47a 100644 --- a/inception/inception-external-search-pubmed/src/main/java/de/tudarmstadt/ukp/inception/externalsearch/pubmed/pmcoa/PmcOaClient.java +++ b/inception/inception-external-search-pubmed/src/main/java/de/tudarmstadt/ukp/inception/externalsearch/pubmed/pmcoa/PmcOaClient.java @@ -17,10 +17,12 @@ */ package de.tudarmstadt.ukp.inception.externalsearch.pubmed.pmcoa; +import java.io.IOException; import java.util.Map; import org.springframework.http.HttpMethod; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; +import org.springframework.web.client.HttpClientErrorException.NotFound; import org.springframework.web.client.RestTemplate; import de.tudarmstadt.ukp.inception.externalsearch.pubmed.traits.PubMedProviderTraits; @@ -40,14 +42,25 @@ public PmcOaClient() restTemplate.getMessageConverters().add(new MappingJackson2XmlHttpMessageConverter()); } - public byte[] bioc(PubMedProviderTraits aTraits, String aID) + public byte[] bioc(PubMedProviderTraits aTraits, String aID) throws IOException { - var variables = Map.of( // - PARAM_ID, aID); + try { + var variables = Map.of( // + PARAM_ID, aID); - var response = restTemplate.exchange(BIOC_URL, HttpMethod.GET, null, byte[].class, - variables); + var response = restTemplate.exchange(BIOC_URL, HttpMethod.GET, null, byte[].class, + variables); - return response.getBody(); + return response.getBody(); + } + catch (NotFound e) { + throw new IOException("BioC version of document [" + aID + "] not found at [" + + BIOC_URL.replace("{id}", aID) + + "]. The Open Access files and BioC versions are not updated as " + + "quickly as the PMC website itself is updated. It may take a couple of days until " + + "a particular file is available as BioC. Another reason could be that the document you " + + "are looking for is not included in the Open Access set. Try adding " + + "`\"open access\"[filter]` without \"`\" to your search to filter by Open Access files."); + } } } diff --git a/inception/inception-external-search-pubmed/src/main/java/de/tudarmstadt/ukp/inception/externalsearch/pubmed/traits/PubMedProviderTraits.java b/inception/inception-external-search-pubmed/src/main/java/de/tudarmstadt/ukp/inception/externalsearch/pubmed/traits/PubMedProviderTraits.java index c2a52c61449..9e2e704ba28 100644 --- a/inception/inception-external-search-pubmed/src/main/java/de/tudarmstadt/ukp/inception/externalsearch/pubmed/traits/PubMedProviderTraits.java +++ b/inception/inception-external-search-pubmed/src/main/java/de/tudarmstadt/ukp/inception/externalsearch/pubmed/traits/PubMedProviderTraits.java @@ -19,6 +19,13 @@ import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +//The @JsonSerialize annotation avoid the "InvalidDefinitionException: No serializer found" +//exception without having to set SerializationFeature.FAIL_ON_EMPTY_BEANS +@JsonSerialize +@JsonIgnoreProperties(ignoreUnknown = true) public class PubMedProviderTraits implements Serializable { diff --git a/inception/inception-imls-ollama/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/ollama/OllamaRecommender.java b/inception/inception-imls-ollama/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/ollama/OllamaRecommender.java index d356f463684..0dd36ae367e 100644 --- a/inception/inception-imls-ollama/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/ollama/OllamaRecommender.java +++ b/inception/inception-imls-ollama/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/ollama/OllamaRecommender.java @@ -49,6 +49,7 @@ import de.tudarmstadt.ukp.inception.recommendation.imls.ollama.response.ResponseAsLabelExtractor; import de.tudarmstadt.ukp.inception.recommendation.imls.ollama.response.ResponseExtractor; import de.tudarmstadt.ukp.inception.rendering.model.Range; +import de.tudarmstadt.ukp.inception.support.logging.LogMessage; public class OllamaRecommender extends NonTrainableRecommenderEngineImplBase @@ -103,8 +104,9 @@ public Range predict(PredictionContext aContext, CAS aCas, int aBegin, int aEnd) responseExtractor.extract(this, aCas, promptContext, response); } catch (IOException e) { - aContext.error("Ollama [%s] failed to respond: %s", traits.getModel(), - ExceptionUtils.getRootCauseMessage(e)); + aContext.log(LogMessage.warn(getRecommender().getName(), + "Ollama [%s] failed to respond: %s", traits.getModel(), + ExceptionUtils.getRootCauseMessage(e))); LOG.error("Ollama [{}] failed to respond: {}", traits.getModel(), ExceptionUtils.getRootCauseMessage(e)); } diff --git a/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/doccat/OpenNlpDoccatRecommender.java b/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/doccat/OpenNlpDoccatRecommender.java index 115b1180055..f8ee25f0058 100644 --- a/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/doccat/OpenNlpDoccatRecommender.java +++ b/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/doccat/OpenNlpDoccatRecommender.java @@ -56,6 +56,7 @@ import de.tudarmstadt.ukp.inception.recommendation.api.recommender.RecommenderContext.Key; import de.tudarmstadt.ukp.inception.recommendation.api.recommender.TrainingCapability; import de.tudarmstadt.ukp.inception.rendering.model.Range; +import de.tudarmstadt.ukp.inception.support.logging.LogMessage; import opennlp.tools.doccat.DoccatFactory; import opennlp.tools.doccat.DoccatModel; import opennlp.tools.doccat.DocumentCategorizerME; @@ -108,13 +109,14 @@ public void train(RecommenderContext aContext, List aCasses) throws Recomme var docSamples = extractSamples(aCasses); if (docSamples.size() < 2) { - aContext.warn("Not enough training data: [%d] items", docSamples.size()); + aContext.log(LogMessage.warn(getRecommender().getName(), + "Not enough training data: [%d] items", docSamples.size())); return; } if (docSamples.stream().map(DocumentSample::getCategory).distinct().count() <= 1) { - aContext.warn("Training data requires at least two different labels", - docSamples.size()); + aContext.log(LogMessage.warn(getRecommender().getName(), + "Training data requires at least two different labels", docSamples.size())); return; } diff --git a/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/ner/OpenNlpNerRecommender.java b/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/ner/OpenNlpNerRecommender.java index 54aa3c79ce2..fc8f0a86be2 100644 --- a/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/ner/OpenNlpNerRecommender.java +++ b/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/ner/OpenNlpNerRecommender.java @@ -50,6 +50,7 @@ import de.tudarmstadt.ukp.inception.recommendation.api.recommender.RecommenderContext.Key; import de.tudarmstadt.ukp.inception.recommendation.api.recommender.TrainingCapability; import de.tudarmstadt.ukp.inception.rendering.model.Range; +import de.tudarmstadt.ukp.inception.support.logging.LogMessage; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import opennlp.tools.ml.BeamSearch; @@ -95,7 +96,8 @@ public void train(RecommenderContext aContext, List aCasses) throws Recomme var nameSamples = extractNameSamples(aCasses); if (nameSamples.size() < 2) { - aContext.warn("Not enough training data: [%d] items", nameSamples.size()); + aContext.log(LogMessage.warn(getRecommender().getName(), + "Not enough training data: [%d] items", nameSamples.size())); return; } @@ -104,10 +106,10 @@ public void train(RecommenderContext aContext, List aCasses) throws Recomme // OpenNLP int beamSize = Math.max(maxRecommendations, NameFinderME.DEFAULT_BEAM_SIZE); - TrainingParameters params = traits.getParameters(); + var params = traits.getParameters(); params.put(BeamSearch.BEAM_SIZE_PARAMETER, Integer.toString(beamSize)); - TokenNameFinderModel model = train(nameSamples, params); + var model = train(nameSamples, params); aContext.put(KEY_MODEL, model); } @@ -304,7 +306,7 @@ private String determineLabel(Span aName, int aTokenIdx) return label; } - private List extractNameSamples(List aCasses) + private List extractNameSamples(Iterable aCasses) { var nameSamples = new ArrayList(); @@ -402,8 +404,8 @@ private TokenNameFinderModel train(List aNameSamples, TrainingParameters aParameters) throws RecommendationException { - try (NameSampleStream stream = new NameSampleStream(aNameSamples)) { - TokenNameFinderFactory finderFactory = new TokenNameFinderFactory(); + try (var stream = new NameSampleStream(aNameSamples)) { + var finderFactory = new TokenNameFinderFactory(); return NameFinderME.train("unknown", null, stream, aParameters, finderFactory); } catch (IOException e) { diff --git a/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/pos/OpenNlpPosRecommender.java b/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/pos/OpenNlpPosRecommender.java index 7a67d4d6552..db2af3c001c 100644 --- a/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/pos/OpenNlpPosRecommender.java +++ b/inception/inception-imls-opennlp/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/opennlp/pos/OpenNlpPosRecommender.java @@ -55,6 +55,7 @@ import de.tudarmstadt.ukp.inception.recommendation.api.recommender.RecommenderContext.Key; import de.tudarmstadt.ukp.inception.recommendation.api.recommender.TrainingCapability; import de.tudarmstadt.ukp.inception.rendering.model.Range; +import de.tudarmstadt.ukp.inception.support.logging.LogMessage; import opennlp.tools.ml.BeamSearch; import opennlp.tools.postag.POSModel; import opennlp.tools.postag.POSSample; @@ -97,7 +98,8 @@ public void train(RecommenderContext aContext, List aCasses) throws Recomme var posSamples = extractPosSamples(aCasses); if (posSamples.size() < 2) { - aContext.warn("Not enough training data: [%d] items", posSamples.size()); + aContext.log(LogMessage.warn(getRecommender().getName(), + "Not enough training data: [%d] items", posSamples.size())); return; } diff --git a/inception/inception-imls-stringmatch/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/stringmatch/span/StringMatchingRecommender.java b/inception/inception-imls-stringmatch/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/stringmatch/span/StringMatchingRecommender.java index 294a9b6d5e2..328979b37ab 100644 --- a/inception/inception-imls-stringmatch/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/stringmatch/span/StringMatchingRecommender.java +++ b/inception/inception-imls-stringmatch/src/main/java/de/tudarmstadt/ukp/inception/recommendation/imls/stringmatch/span/StringMatchingRecommender.java @@ -69,6 +69,7 @@ import de.tudarmstadt.ukp.inception.recommendation.imls.stringmatch.span.trie.Trie; import de.tudarmstadt.ukp.inception.recommendation.imls.stringmatch.span.trie.WhitespaceNormalizingSanitizer; import de.tudarmstadt.ukp.inception.rendering.model.Range; +import de.tudarmstadt.ukp.inception.support.logging.LogMessage; public class StringMatchingRecommender extends RecommendationEngine @@ -138,7 +139,9 @@ public void pretrain(List aData, RecommenderContext aContext) for (GazeteerEntry entry : aData) { learn(dict, entry.text, entry.label); } - aContext.info("Loaded [%d] entries from gazeteer", aData.size()); + + aContext.log(LogMessage.info(getRecommender().getName(), + "Loaded [%d] entries from gazeteer", aData.size())); } aContext.put(KEY_MODEL, dict); @@ -159,8 +162,8 @@ public void train(RecommenderContext aContext, List aCasses) throws Recomme pretrain(gazeteerService.readGazeteerFile(gaz), aContext); } catch (IOException e) { - aContext.error("Unable to load gazeteer [%s]: %s", gaz.getName(), - e.getMessage()); + aContext.log(LogMessage.error(getRecommender().getName(), + "Unable to load gazeteer [%s]: %s", gaz.getName(), e.getMessage())); log.error( "Unable to load gazeteer [{}] for recommender [{}]({}) in project [{}]({})", gaz.getName(), gaz.getRecommender().getName(), @@ -192,8 +195,9 @@ public void train(RecommenderContext aContext, List aCasses) throws Recomme } } - aContext.info("Learned dictionary model with %d entries on %d documents", dict.size(), - aCasses.size()); + aContext.log(LogMessage.info(getRecommender().getName(), + "Learned dictionary model with %d entries on %d documents", dict.size(), + aCasses.size())); aContext.put(KEY_MODEL, dict); } diff --git a/inception/inception-io-bioc/src/main/java/de/tudarmstadt/ukp/inception/io/bioc/BioCFormatSupport.java b/inception/inception-io-bioc/src/main/java/de/tudarmstadt/ukp/inception/io/bioc/BioCFormatSupport.java index a17b1ca2036..7eb66cfd709 100644 --- a/inception/inception-io-bioc/src/main/java/de/tudarmstadt/ukp/inception/io/bioc/BioCFormatSupport.java +++ b/inception/inception-io-bioc/src/main/java/de/tudarmstadt/ukp/inception/io/bioc/BioCFormatSupport.java @@ -17,8 +17,11 @@ */ package de.tudarmstadt.ukp.inception.io.bioc; +import static org.apache.uima.fit.factory.AnalysisEngineFactory.createEngineDescription; import static org.apache.uima.fit.factory.CollectionReaderFactory.createReaderDescription; +import org.apache.uima.analysis_engine.AnalysisEngineDescription; +import org.apache.uima.cas.CAS; import org.apache.uima.collection.CollectionReaderDescription; import org.apache.uima.resource.ResourceInitializationException; import org.apache.uima.resource.metadata.TypeSystemDescription; @@ -57,6 +60,12 @@ public boolean isReadable() return true; } + @Override + public boolean isWritable() + { + return true; + } + @Override public CollectionReaderDescription getReaderDescription(Project aProject, TypeSystemDescription aTSD) @@ -64,4 +73,12 @@ public CollectionReaderDescription getReaderDescription(Project aProject, { return createReaderDescription(BioCReader.class, aTSD); } + + @Override + public AnalysisEngineDescription getWriterDescription(Project aProject, + TypeSystemDescription aTSD, CAS aCAS) + throws ResourceInitializationException + { + return createEngineDescription(BioCWriter.class, aTSD); + } } diff --git a/inception/inception-layer-docmetadata/src/main/java/de/tudarmstadt/ukp/inception/ui/core/docanno/recommendation/MetadataSuggestionSupport.java b/inception/inception-layer-docmetadata/src/main/java/de/tudarmstadt/ukp/inception/ui/core/docanno/recommendation/MetadataSuggestionSupport.java index 84a418097f2..da8bfc176c1 100644 --- a/inception/inception-layer-docmetadata/src/main/java/de/tudarmstadt/ukp/inception/ui/core/docanno/recommendation/MetadataSuggestionSupport.java +++ b/inception/inception-layer-docmetadata/src/main/java/de/tudarmstadt/ukp/inception/ui/core/docanno/recommendation/MetadataSuggestionSupport.java @@ -19,8 +19,6 @@ import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_ALL; import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_OVERLAP; -import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_TRANSIENT_REJECTED; -import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.REJECTED; import java.lang.invoke.MethodHandles; import java.util.ArrayList; @@ -29,7 +27,6 @@ import java.util.Objects; import java.util.Optional; -import org.apache.commons.lang3.NotImplementedException; import org.apache.uima.cas.AnnotationBaseFS; import org.apache.uima.cas.CAS; import org.apache.uima.cas.Feature; @@ -45,7 +42,6 @@ import de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService; import de.tudarmstadt.ukp.inception.recommendation.api.SuggestionRenderer; import de.tudarmstadt.ukp.inception.recommendation.api.SuggestionSupport_ImplBase; -import de.tudarmstadt.ukp.inception.recommendation.api.event.RecommendationRejectedEvent; import de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion; import de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecord; import de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordChangeLocation; @@ -135,36 +131,6 @@ else if (candidates.isEmpty() || !aAdapter.getTraits(DocumentMetadataLayerTraits return annotation; } - @Override - public void rejectSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, - AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aAction) - throws AnnotationException - { - var suggestion = (MetadataSuggestion) aSuggestion; - - // Hide the suggestion. This is faster than having to recalculate the visibility status - // for the entire document or even for the part visible on screen. - suggestion.hide(FLAG_TRANSIENT_REJECTED); - - var recommender = recommendationService.getRecommender(suggestion); - var feature = recommender.getFeature(); - // Log the action to the learning record - learningRecordService.logRecord(aSessionOwner, aDocument, aDataOwner, suggestion, feature, - REJECTED, aAction); - - // Send an application event that the suggestion has been rejected - applicationEventPublisher.publishEvent(new RecommendationRejectedEvent(this, aDocument, - aDataOwner, feature, suggestion.getLabel())); - } - - @Override - public void skipSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, - AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aAction) - throws AnnotationException - { - throw new NotImplementedException("Not yet implemented"); - } - @Override public void calculateSuggestionVisibility(String aSessionOwner, SourceDocument aDocument, CAS aCas, String aDataOwner, AnnotationLayer aLayer, @@ -172,15 +138,15 @@ public void calculateSuggestionVisibility(Strin { LOG.trace("calculateSuggestionVisibility() for layer {} on document {}", aLayer, aDocument); - var type = aCas.getTypeSystem().getType(aLayer.getName()); - if (type == null) { + var predictedType = aCas.getTypeSystem().getType(aLayer.getName()); + if (predictedType == null) { // The type does not exist in the type system of the CAS. Probably it has not // been upgraded to the latest version of the type system yet. If this is the case, // we'll just skip. return; } - var annotations = aCas. select(type).asList(); + var annotations = aCas. select(predictedType).asList(); var suggestionsForLayer = aRecommendations.stream() // Only suggestions for the given layer @@ -194,7 +160,7 @@ public void calculateSuggestionVisibility(Strin var adapter = schemaService.getAdapter(aLayer); var traits = adapter.getTraits(DocumentMetadataLayerTraits.class).get(); for (var feature : schemaService.listSupportedFeatures(aLayer)) { - var feat = type.getFeatureByBaseName(feature.getName()); + var feat = predictedType.getFeatureByBaseName(feature.getName()); if (feat == null) { // The feature does not exist in the type system of the CAS. Probably it has not @@ -267,14 +233,16 @@ static void hideSuggestionsRejectedOrSkipped(MetadataSuggestion aSuggestion, } @Override - public LearningRecord toLearningRecord(SourceDocument aDocument, String aUsername, + public LearningRecord toLearningRecord(SourceDocument aDocument, String aDataOwner, AnnotationSuggestion aSuggestion, AnnotationFeature aFeature, LearningRecordUserAction aUserAction, LearningRecordChangeLocation aLocation) { var record = new LearningRecord(); - record.setUser(aUsername); + record.setUser(aDataOwner); record.setSourceDocument(aDocument); record.setUserAction(aUserAction); + record.setOffsetBegin(-1); + record.setOffsetEnd(-1); record.setOffsetBegin2(-1); record.setOffsetEnd2(-1); record.setAnnotation(aSuggestion.getLabel()); diff --git a/inception/inception-pdf-editor/pom.xml b/inception/inception-pdf-editor/pom.xml index 8825040b093..23c6e6a719d 100644 --- a/inception/inception-pdf-editor/pom.xml +++ b/inception/inception-pdf-editor/pom.xml @@ -87,6 +87,10 @@ org.dkpro.core dkpro-core-io-pdf-asl + + org.dkpro.core + dkpro-core-api-segmentation-asl + org.dkpro.core dkpro-core-api-resources-asl @@ -168,11 +172,6 @@ dkpro-core-api-lexmorph-asl test - - org.dkpro.core - dkpro-core-api-segmentation-asl - test - de.tudarmstadt.ukp.inception.app inception-schema diff --git a/inception/inception-pdf-editor/src/main/java/de/tudarmstadt/ukp/inception/pdfeditor/PdfAnnotationEditor.java b/inception/inception-pdf-editor/src/main/java/de/tudarmstadt/ukp/inception/pdfeditor/PdfAnnotationEditor.java index 3345b62ca75..631da7a440b 100644 --- a/inception/inception-pdf-editor/src/main/java/de/tudarmstadt/ukp/inception/pdfeditor/PdfAnnotationEditor.java +++ b/inception/inception-pdf-editor/src/main/java/de/tudarmstadt/ukp/inception/pdfeditor/PdfAnnotationEditor.java @@ -20,10 +20,10 @@ import static de.tudarmstadt.ukp.inception.pdfeditor.pdfanno.render.PdfAnnoSerializer.convertToDocumentOffset; import static de.tudarmstadt.ukp.inception.pdfeditor.pdfanno.render.PdfAnnoSerializer.convertToDocumentOffsets; import static de.tudarmstadt.ukp.inception.rendering.vmodel.VID.NONE_ID; -import static de.tudarmstadt.ukp.inception.support.uima.WebAnnoCasUtil.selectSentenceAt; import static de.tudarmstadt.ukp.inception.support.wicket.ServletContextUtils.referenceToUrl; import static java.lang.String.join; import static java.util.Arrays.asList; +import static org.apache.uima.fit.util.CasUtil.getType; import java.io.IOException; import java.util.ArrayList; @@ -35,6 +35,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.uima.cas.CAS; import org.apache.uima.cas.text.AnnotationFS; +import org.apache.uima.fit.util.CasUtil; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.feedback.IFeedback; @@ -46,6 +47,7 @@ import org.slf4j.LoggerFactory; import de.tudarmstadt.ukp.clarin.webanno.api.casstorage.CasProvider; +import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Sentence; import de.tudarmstadt.ukp.inception.documents.api.DocumentService; import de.tudarmstadt.ukp.inception.editor.AnnotationEditorExtensionRegistry; import de.tudarmstadt.ukp.inception.editor.action.AnnotationActionHandler; @@ -313,4 +315,20 @@ private String getAnnotationsJS(PdfAnnoModel aPdfAnnoModel) "'colorMap': {},", // "'annotations':[annoFile]}, true);"); } + + /** + * Get the sentence for this CAS based on the begin and end offsets. This is basically used to + * transform sentence address in one CAS to other sentence address for different CAS + * + * @param aCas + * the CAS. + * @param aBegin + * the begin offset. + * @return the sentence. + */ + private static AnnotationFS selectSentenceAt(CAS aCas, int aBegin) + { + return CasUtil.select(aCas, getType(aCas, Sentence.class)).stream() + .filter(s -> s.getBegin() == aBegin).findFirst().orElse(null); + } } diff --git a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/LearningRecordService.java b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/LearningRecordService.java index c77a3fa1f68..97157aec5fc 100644 --- a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/LearningRecordService.java +++ b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/LearningRecordService.java @@ -44,6 +44,8 @@ List listLearningRecords(String aSessionOwner, SourceDocument aD * used, e.g. for loading only a reduced part of the history in the active learning * sidebar. Learning records with the action {@link LearningRecordUserAction#SHOWN} are * not returned by this method. + * @param aSessionOwner + * the user performing the action * @param aDataOwner * the annotator user * @param aLayer @@ -61,6 +63,8 @@ List listLearningRecords(String aSessionOwner, String aDataOwner * duplicates of the new action are removed as part of this action. Note that the actual action * the user performed is not taken into account to determine duplicateness. * + * @param aSessionOwner + * the user performing the action * @param aDocument * the document * @param aDataOwner @@ -73,12 +77,28 @@ List listLearningRecords(String aSessionOwner, String aDataOwner * the annotators reaction to the suggestion * @param aLocation * where the action on the suggestion was triggered + * @deprecated Use {@link #logRecord(String, LearningRecord)} instead. */ + @Deprecated void logRecord(String aSessionOwner, SourceDocument aDocument, String aDataOwner, AnnotationSuggestion aSuggestion, AnnotationFeature aFeature, LearningRecordUserAction aUserAction, LearningRecordChangeLocation aLocation); /** + * Updates the learning log with an entry for the given suggestion. Any entries which are + * duplicates of the new action are removed as part of this action. Note that the actual action + * the user performed is not taken into account to determine redundance. + * + * @param aSessionOwner + * the user performing the action + * @param aRecord + * the record + */ + void logRecord(String aSessionOwner, LearningRecord aRecord); + + /** + * @param aSessionOwner + * the user performing the action * @param aDataOwner * the annotator user * @param aLayer @@ -93,6 +113,8 @@ void logRecord(String aSessionOwner, SourceDocument aDocument, String aDataOwner * Removes all records of type {@link LearningRecordUserAction#SKIPPED} in the history of the * given layer for the given user. * + * @param aSessionOwner + * the user performing the action * @param aDataOwner * the annotator user * @param aLayer diff --git a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/SuggestionSupport.java b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/SuggestionSupport.java index 3d83e2ada1a..f6b4bbe6661 100644 --- a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/SuggestionSupport.java +++ b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/SuggestionSupport.java @@ -41,29 +41,70 @@ public interface SuggestionSupport extends Extension { + /** + * @param aCtx + * the extraction context containing all important information. + * @return the suggestions extracted from the prediction CAS provided in the context. + */ + List extractSuggestions(ExtractionContext aCtx); + + /** + * @return the renderer used to render suggestions provided by this suggestion support. + */ + Optional getRenderer(); + + /** + * Calculate the visibility for the given suggestions. The suggestions must have been produced + * by this suggestion support. Also, they must all be from the same layer - be sure to process + * one layer at a time. The suggestions may come from different recommenders though. + */ + void calculateSuggestionVisibility(String aSessionOwner, + SourceDocument aDocument, CAS aCas, String aDataOwner, AnnotationLayer aLayer, + Collection> aRecommendations, int aWindowBegin, int aWindowEnd); + + /** + * Accept the given suggestion. + * + * @param aSuggestion + * the suggestion to accept. + * @return the annotation created from the suggestion. + * @throws AnnotationException + * if there was a problem creating the annotation. + */ AnnotationBaseFS acceptSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, CAS aCas, TypeAdapter aAdapter, AnnotationFeature aFeature, AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aLocation, LearningRecordUserAction aAction) throws AnnotationException; + /** + * Reject the given suggestion. + * + * @param aSuggestion + * the suggestion to reject. + * @throws AnnotationException + * if there was a problem rejecting the annotation. + */ void rejectSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, - AnnotationSuggestion suggestion, LearningRecordChangeLocation aAction) + AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aLocation) throws AnnotationException; + /** + * Skip the given suggestion. + * + * @param aSuggestion + * the suggestion to skip. + * @throws AnnotationException + * if there was a problem skipping the annotation. + */ void skipSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, - AnnotationSuggestion suggestion, LearningRecordChangeLocation aAction) + AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aAction) throws AnnotationException; - void calculateSuggestionVisibility(String aSessionOwner, - SourceDocument aDocument, CAS aCas, String aDataOwner, AnnotationLayer aLayer, - Collection> aRecommendations, int aWindowBegin, int aWindowEnd); - - LearningRecord toLearningRecord(SourceDocument aDocument, String aUsername, + /** + * Create a learning record from the given suggestion. + */ + LearningRecord toLearningRecord(SourceDocument aDocument, String aDataOwner, AnnotationSuggestion aSuggestion, AnnotationFeature aFeature, LearningRecordUserAction aUserAction, LearningRecordChangeLocation aLocation); - - Optional getRenderer(); - - List extractSuggestions(ExtractionContext aCtx); } diff --git a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/SuggestionSupport_ImplBase.java b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/SuggestionSupport_ImplBase.java index 68436fc97c4..e2d15fa3480 100644 --- a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/SuggestionSupport_ImplBase.java +++ b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/SuggestionSupport_ImplBase.java @@ -17,9 +17,13 @@ */ package de.tudarmstadt.ukp.inception.recommendation.api; +import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_SKIPPED; import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_TRANSIENT_ACCEPTED; import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_TRANSIENT_CORRECTED; +import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_TRANSIENT_REJECTED; import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.ACCEPTED; +import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.REJECTED; +import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.SKIPPED; import org.apache.uima.cas.AnnotationBaseFS; import org.apache.uima.cas.CAS; @@ -32,10 +36,12 @@ import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature; import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocument; import de.tudarmstadt.ukp.inception.recommendation.api.event.RecommendationAcceptedEvent; +import de.tudarmstadt.ukp.inception.recommendation.api.event.RecommendationRejectedEvent; import de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion; import de.tudarmstadt.ukp.inception.recommendation.api.model.AutoAcceptMode; import de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordChangeLocation; import de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction; +import de.tudarmstadt.ukp.inception.recommendation.api.model.SpanSuggestion; import de.tudarmstadt.ukp.inception.schema.api.AnnotationSchemaService; import de.tudarmstadt.ukp.inception.schema.api.adapter.AnnotationException; import de.tudarmstadt.ukp.inception.schema.api.adapter.TypeAdapter; @@ -91,8 +97,9 @@ protected void commmitLabel(String aSessionOwner, SourceDocument aDocument, Stri // Log the action to the learning record if (!aAdapter.isSilenced()) { - learningRecordService.logRecord(aSessionOwner, aDocument, aDataOwner, aSuggestion, - aFeature, aAction, aLocation); + var record = toLearningRecord(aDocument, aDataOwner, aSuggestion, aFeature, aAction, + aLocation); + learningRecordService.logRecord(aSessionOwner, record); // Send an application event that the suggestion has been accepted aAdapter.publishEvent(() -> new RecommendationAcceptedEvent(this, aDocument, aDataOwner, @@ -124,4 +131,46 @@ public static String[] getPredictedLabels(FeatureStructure predictedFS, return new String[] { predictedFS.getFeatureValueAsString(predictedFeature) }; } + + @Override + public void rejectSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, + AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aLocation) + throws AnnotationException + { + var suggestion = (SpanSuggestion) aSuggestion; + + // Hide the suggestion. This is faster than having to recalculate the visibility status + // for the entire document or even for the part visible on screen. + suggestion.hide(FLAG_TRANSIENT_REJECTED); + + var recommender = recommendationService.getRecommender(suggestion); + var feature = recommender.getFeature(); + // Log the action to the learning record + var record = toLearningRecord(aDocument, aDataOwner, aSuggestion, feature, REJECTED, + aLocation); + learningRecordService.logRecord(aSessionOwner, record); + + // Send an application event that the suggestion has been rejected + applicationEventPublisher.publishEvent(new RecommendationRejectedEvent(this, aDocument, + aDataOwner, suggestion.getBegin(), suggestion.getEnd(), suggestion.getCoveredText(), + feature, suggestion.getLabel())); + } + + @Override + public void skipSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, + AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aLocation) + throws AnnotationException + { + // Hide the suggestion. This is faster than having to recalculate the visibility status + // for the entire document or even for the part visible on screen. + aSuggestion.hide(FLAG_SKIPPED); + + var recommender = recommendationService.getRecommender(aSuggestion); + var feature = recommender.getFeature(); + + // Log the action to the learning record + var record = toLearningRecord(aDocument, aDataOwner, aSuggestion, feature, SKIPPED, + aLocation); + learningRecordService.logRecord(aSessionOwner, record); + } } diff --git a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/recommender/PredictionContext.java b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/recommender/PredictionContext.java index 2020df9a604..c509be7da93 100644 --- a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/recommender/PredictionContext.java +++ b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/recommender/PredictionContext.java @@ -44,31 +44,13 @@ synchronized public Optional get(Key aKey) return modelContext.get(aKey); } - synchronized public void info(String aFormat, Object... aValues) + synchronized public void log(LogMessage aMessage) { if (closed) { throw new IllegalStateException("Adding data to a closed context is not permitted."); } - messages.add(LogMessage.info(this, aFormat, aValues)); - } - - synchronized public void warn(String aFormat, Object... aValues) - { - if (closed) { - throw new IllegalStateException("Adding data to a closed context is not permitted."); - } - - messages.add(LogMessage.warn(this, aFormat, aValues)); - } - - synchronized public void error(String aFormat, Object... aValues) - { - if (closed) { - throw new IllegalStateException("Adding data to a closed context is not permitted."); - } - - messages.add(LogMessage.error(this, aFormat, aValues)); + messages.add(aMessage); } public List getMessages() diff --git a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/recommender/RecommenderContext.java b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/recommender/RecommenderContext.java index db159e58c5e..c86e048dcfd 100644 --- a/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/recommender/RecommenderContext.java +++ b/inception/inception-recommendation-api/src/main/java/de/tudarmstadt/ukp/inception/recommendation/api/recommender/RecommenderContext.java @@ -55,31 +55,13 @@ synchronized public void put(Key aKey, T aValue) store.put(aKey.name, aValue); } - synchronized public void info(String aFormat, Object... aValues) + synchronized public void log(LogMessage aMessage) { if (closed) { throw new IllegalStateException("Adding data to a closed context is not permitted."); } - messages.add(LogMessage.info(this, aFormat, aValues)); - } - - synchronized public void warn(String aFormat, Object... aValues) - { - if (closed) { - throw new IllegalStateException("Adding data to a closed context is not permitted."); - } - - messages.add(LogMessage.warn(this, aFormat, aValues)); - } - - synchronized public void error(String aFormat, Object... aValues) - { - if (closed) { - throw new IllegalStateException("Adding data to a closed context is not permitted."); - } - - messages.add(LogMessage.error(this, aFormat, aValues)); + messages.add(aMessage); } public List getMessages() diff --git a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/config/RecommenderServiceAutoConfiguration.java b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/config/RecommenderServiceAutoConfiguration.java index bb043fe68c0..7525f45194d 100644 --- a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/config/RecommenderServiceAutoConfiguration.java +++ b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/config/RecommenderServiceAutoConfiguration.java @@ -53,9 +53,9 @@ import de.tudarmstadt.ukp.inception.recommendation.project.RecommenderProjectSettingsPanelFactory; import de.tudarmstadt.ukp.inception.recommendation.relation.RelationSuggestionSupport; import de.tudarmstadt.ukp.inception.recommendation.render.RecommendationRenderer; -import de.tudarmstadt.ukp.inception.recommendation.service.LayerRecommendtionSupportRegistryImpl; import de.tudarmstadt.ukp.inception.recommendation.service.RecommendationServiceImpl; import de.tudarmstadt.ukp.inception.recommendation.service.RecommenderFactoryRegistryImpl; +import de.tudarmstadt.ukp.inception.recommendation.service.SuggestionSupportRegistryImpl; import de.tudarmstadt.ukp.inception.recommendation.sidebar.RecommendationSidebarFactory; import de.tudarmstadt.ukp.inception.recommendation.span.SpanSuggestionSupport; import de.tudarmstadt.ukp.inception.scheduling.SchedulingService; @@ -231,6 +231,6 @@ public RelationSuggestionSupport relationRecommendationSupport( public SuggestionSupportRegistry layerRecommendtionSupportRegistry( @Lazy @Autowired(required = false) List aExtensions) { - return new LayerRecommendtionSupportRegistryImpl(aExtensions); + return new SuggestionSupportRegistryImpl(aExtensions); } } diff --git a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/relation/RelationSuggestionSupport.java b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/relation/RelationSuggestionSupport.java index dde66836c6a..be12d8aa938 100644 --- a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/relation/RelationSuggestionSupport.java +++ b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/relation/RelationSuggestionSupport.java @@ -18,11 +18,8 @@ package de.tudarmstadt.ukp.inception.recommendation.relation; import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_OVERLAP; -import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_SKIPPED; -import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_TRANSIENT_REJECTED; import static de.tudarmstadt.ukp.inception.support.WebAnnoConst.FEAT_REL_SOURCE; import static de.tudarmstadt.ukp.inception.support.WebAnnoConst.FEAT_REL_TARGET; -import static java.util.stream.Collectors.toList; import static org.apache.uima.cas.text.AnnotationPredicates.colocated; import static org.apache.uima.fit.util.CasUtil.select; import static org.apache.uima.fit.util.CasUtil.selectAt; @@ -206,33 +203,6 @@ else if (candidates.size() > 1) { return annotation; } - @Override - public void rejectSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, - AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aAction) - { - // Hide the suggestion. This is faster than having to recalculate the visibility status - // for - // the entire document or even for the part visible on screen. - aSuggestion.hide(FLAG_TRANSIENT_REJECTED); - - // TODO: See span recommendation support... - - } - - @Override - public void skipSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, - AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aAction) - throws AnnotationException - { - // Hide the suggestion. This is faster than having to recalculate the visibility status - // for - // the entire document or even for the part visible on screen. - aSuggestion.hide(FLAG_SKIPPED); - - // TODO: Log rejection - // TODO: Publish rejection event - } - @Override public void calculateSuggestionVisibility(String aSessionOwner, SourceDocument aDocument, CAS aCas, String aUser, AnnotationLayer aLayer, @@ -272,8 +242,8 @@ public void calculateSuggestionVisibility(Strin // Collect all suggestions of the given layer var groupedSuggestions = aRecommendations.stream() .filter(group -> group.getLayerId() == aLayer.getId()) // - .map(group -> (SuggestionGroup) group) // - .collect(toList()); + .map(group -> (SuggestionGroup) group) // + .toList(); // Get previously rejected suggestions var groupedRecordedAnnotations = new ArrayListValuedHashMap(); @@ -296,7 +266,7 @@ public void calculateSuggestionVisibility(Strin return; } - for (SuggestionGroup group : groupedSuggestions) { + for (var group : groupedSuggestions) { if (!feature.getName().equals(group.getFeature())) { continue; } @@ -422,7 +392,7 @@ public List extractSuggestions(ExtractionContext ctx) .withId(RelationSuggestion.NEW_ID) // .withGeneration(ctx.getGeneration()) // .withRecommender(ctx.getRecommender()) // - .withDocumentName(ctx.getDocument().getName()) // + .withDocument(ctx.getDocument()) // .withPosition(position) // .withLabel(label) // .withUiLabel(label) // diff --git a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImpl.java b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImpl.java index fa2f29b2f64..639ad3c689a 100644 --- a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImpl.java +++ b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImpl.java @@ -1052,23 +1052,25 @@ public AnnotationFS correctSuggestion(String aSessionOwner, SourceDocument aDocu var layer = schemaService.getLayer(aOriginalSuggestion.getLayerId()); var feature = schemaService.getFeature(aOriginalSuggestion.getFeature(), layer); - var originalRls = suggestionSupportRegistry + var originalSuggestionSupport = suggestionSupportRegistry .findGenericExtension(getRecommender(aOriginalSuggestion)); - if (originalRls.isPresent()) { + if (originalSuggestionSupport.isPresent()) { // If the action was a correction (i.e. suggestion label != annotation value) then // generate a rejection for the original value - we do not want the original value to // re-appear - logRecord(aSessionOwner, aDocument, aDataOwner, aOriginalSuggestion, feature, REJECTED, - aLocation); + var record = originalSuggestionSupport.get().toLearningRecord(aDocument, aDataOwner, + aOriginalSuggestion, feature, REJECTED, aLocation); + logRecord(aSessionOwner, record); } - var correctedRls = suggestionSupportRegistry + var correctedSuggestionSupport = suggestionSupportRegistry .findGenericExtension(getRecommender(aCorrectedSuggestion)); - if (correctedRls.isPresent()) { + if (correctedSuggestionSupport.isPresent()) { var adapter = schemaService.getAdapter(layer); - return (AnnotationFS) originalRls.get().acceptSuggestion(aSessionOwner, aDocument, - aDataOwner, aCas, adapter, feature, aCorrectedSuggestion, aLocation, CORRECTED); + return (AnnotationFS) originalSuggestionSupport.get().acceptSuggestion(aSessionOwner, + aDocument, aDataOwner, aCas, adapter, feature, aCorrectedSuggestion, aLocation, + CORRECTED); } return null; @@ -1830,7 +1832,7 @@ static ReconciliationResult reconcile(Predictions aActivePredictions, SourceDocu s.getScore() == newSuggestion.getScore() && // Objects.equals(s.getScoreExplanation(), newSuggestion.getScoreExplanation())) - .collect(toList()); + .toList(); if (existingSuggestions.isEmpty()) { addedSuggestions.add(newSuggestion); @@ -1863,8 +1865,10 @@ public void calculateSuggestionVisibility(Strin SourceDocument aDocument, CAS aCas, String aDataOwner, AnnotationLayer aLayer, Collection> aRecommendations, int aWindowBegin, int aWindowEnd) { - var maybeSuggestion = aRecommendations.stream().filter(group -> !group.isEmpty()) - .flatMap(group -> group.stream()).findAny(); + var maybeSuggestion = aRecommendations.stream() // + .filter(group -> !group.isEmpty()) // + .flatMap(group -> group.stream()) // + .findAny(); if (maybeSuggestion.isEmpty()) { return; @@ -2042,6 +2046,7 @@ public void skipSuggestion(String aSessionOwner, SourceDocument aDocument, Strin @Transactional @Override + @Deprecated public void logRecord(String aSessionOwner, SourceDocument aDocument, String aDataOwner, AnnotationSuggestion aSuggestion, AnnotationFeature aFeature, LearningRecordUserAction aUserAction, LearningRecordChangeLocation aLocation) @@ -2060,16 +2065,23 @@ record = rls.get().toLearningRecord(aDocument, aDataOwner, aSuggestion, aFeature "Unsupported suggestion type [" + aSuggestion.getClass().getName() + "]"); } + logRecord(aSessionOwner, record); + } + + @Transactional + @Override + public void logRecord(String aSessionOwner, LearningRecord aRecord) + { if (aSessionOwner != null) { - var state = getState(aSessionOwner, aDocument.getProject()); + var state = getState(aSessionOwner, aRecord.getSourceDocument().getProject()); synchronized (state) { - state.removeLearningRecords(record); - state.logRecord(record); + state.removeLearningRecords(aRecord); + state.logRecord(aRecord); } } - deleteLearningRecords(record); - createLearningRecord(record); + deleteLearningRecords(aRecord); + createLearningRecord(aRecord); } private void deleteLearningRecords(LearningRecord aRecord) diff --git a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/LayerRecommendtionSupportRegistryImpl.java b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/SuggestionSupportRegistryImpl.java similarity index 94% rename from inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/LayerRecommendtionSupportRegistryImpl.java rename to inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/SuggestionSupportRegistryImpl.java index 57742ff5de1..8e82dbffcfc 100644 --- a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/LayerRecommendtionSupportRegistryImpl.java +++ b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/SuggestionSupportRegistryImpl.java @@ -28,12 +28,12 @@ import de.tudarmstadt.ukp.inception.recommendation.api.model.Recommender; import de.tudarmstadt.ukp.inception.support.extensionpoint.ExtensionPoint_ImplBase; -public class LayerRecommendtionSupportRegistryImpl +public class SuggestionSupportRegistryImpl extends ExtensionPoint_ImplBase implements SuggestionSupportRegistry { @Autowired - public LayerRecommendtionSupportRegistryImpl( + public SuggestionSupportRegistryImpl( @Lazy @Autowired(required = false) List aExtensions) { super(aExtensions); diff --git a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/span/SpanSuggestionSupport.java b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/span/SpanSuggestionSupport.java index b97b01aac50..c0ffb1fb3ac 100644 --- a/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/span/SpanSuggestionSupport.java +++ b/inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/span/SpanSuggestionSupport.java @@ -18,10 +18,6 @@ package de.tudarmstadt.ukp.inception.recommendation.span; import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_OVERLAP; -import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_SKIPPED; -import static de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion.FLAG_TRANSIENT_REJECTED; -import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.REJECTED; -import static de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordUserAction.SKIPPED; import static java.lang.Math.max; import static java.lang.Math.min; import static java.util.Comparator.comparingInt; @@ -67,7 +63,6 @@ import de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService; import de.tudarmstadt.ukp.inception.recommendation.api.SuggestionRenderer; import de.tudarmstadt.ukp.inception.recommendation.api.SuggestionSupport_ImplBase; -import de.tudarmstadt.ukp.inception.recommendation.api.event.RecommendationRejectedEvent; import de.tudarmstadt.ukp.inception.recommendation.api.model.AnnotationSuggestion; import de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecord; import de.tudarmstadt.ukp.inception.recommendation.api.model.LearningRecordChangeLocation; @@ -174,52 +169,6 @@ else if (candidates.isEmpty() || aAdapter.getLayer().isAllowStacking()) { return annotation; } - @Override - public void rejectSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, - AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aAction) - { - var suggestion = (SpanSuggestion) aSuggestion; - - // Hide the suggestion. This is faster than having to recalculate the visibility status - // for the entire document or even for the part visible on screen. - suggestion.hide(FLAG_TRANSIENT_REJECTED); - - var recommender = recommendationService.getRecommender(suggestion); - var feature = recommender.getFeature(); - // Log the action to the learning record - learningRecordService.logRecord(aSessionOwner, aDocument, aDataOwner, suggestion, feature, - REJECTED, aAction); - - // Send an application event that the suggestion has been rejected - applicationEventPublisher.publishEvent(new RecommendationRejectedEvent(this, aDocument, - aDataOwner, suggestion.getBegin(), suggestion.getEnd(), suggestion.getCoveredText(), - feature, suggestion.getLabel())); - - } - - @Override - public void skipSuggestion(String aSessionOwner, SourceDocument aDocument, String aDataOwner, - AnnotationSuggestion aSuggestion, LearningRecordChangeLocation aAction) - throws AnnotationException - { - // Hide the suggestion. This is faster than having to recalculate the visibility status - // for the entire document or even for the part visible on screen. - aSuggestion.hide(FLAG_SKIPPED); - - var recommender = recommendationService.getRecommender(aSuggestion); - var feature = recommender.getFeature(); - - // Log the action to the learning record - learningRecordService.logRecord(aSessionOwner, aDocument, aDataOwner, aSuggestion, feature, - SKIPPED, aAction); - - // // Send an application event that the suggestion has been rejected - // applicationEventPublisher.publishEvent(new RecommendationSkippedEvent(this, - // aDocument, - // aDataOwner, spanSuggestion.getBegin(), spanSuggestion.getEnd(), - // spanSuggestion.getCoveredText(), feature, spanSuggestion.getLabel())); - } - @Override public void calculateSuggestionVisibility(String aSessionOwner, SourceDocument aDocument, CAS aCas, String aDataOwner, AnnotationLayer aLayer, @@ -441,13 +390,13 @@ static void hideSuggestionsRejectedOrSkipped(SpanSuggestion aSuggestion, } @Override - public LearningRecord toLearningRecord(SourceDocument aDocument, String aUsername, + public LearningRecord toLearningRecord(SourceDocument aDocument, String aDataOwner, AnnotationSuggestion aSuggestion, AnnotationFeature aFeature, LearningRecordUserAction aUserAction, LearningRecordChangeLocation aLocation) { var pos = ((SpanSuggestion) aSuggestion).getPosition(); var record = new LearningRecord(); - record.setUser(aUsername); + record.setUser(aDataOwner); record.setSourceDocument(aDocument); record.setUserAction(aUserAction); record.setOffsetBegin(pos.getBegin()); diff --git a/inception/inception-recommendation/src/main/resources/META-INF/asciidoc/developer-guide/recommendation.adoc b/inception/inception-recommendation/src/main/resources/META-INF/asciidoc/developer-guide/recommendation.adoc index adf95053ad6..28137482fb7 100644 --- a/inception/inception-recommendation/src/main/resources/META-INF/asciidoc/developer-guide/recommendation.adoc +++ b/inception/inception-recommendation/src/main/resources/META-INF/asciidoc/developer-guide/recommendation.adoc @@ -15,7 +15,7 @@ // limitations under the License. [[sect_recommendation]] -= Recommendation += Recommenders system For information on the different recommenders, please refer to <>. @@ -31,3 +31,26 @@ For information on the different recommenders, please refer to <()).when(learningRecordService).listLearningRecords(TEST_USER, - TEST_USER, layer); + doReturn(emptyList()).when(learningRecordService).listLearningRecords(TEST_USER, TEST_USER, + layer); var cas = getTestCas(); var suggestions = makeRelationSuggestionGroup(doc, feature, @@ -105,8 +104,8 @@ public void testCalculateVisibilityNoRecordsAllHidden() throws Exception @Test public void thatVisibilityIsRestoredWhenOverlappingAnnotationIsRemoved() throws Exception { - doReturn(new ArrayList<>()).when(learningRecordService).listLearningRecords(TEST_USER, - TEST_USER, layer); + doReturn(emptyList()).when(learningRecordService).listLearningRecords(TEST_USER, TEST_USER, + layer); var cas = getTestCas(); var suggestions = makeRelationSuggestionGroup(doc, feature, diff --git a/inception/inception-recommendation/src/test/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImplIntegrationTest.java b/inception/inception-recommendation/src/test/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImplIntegrationTest.java index 8821b35fd9d..ccb41fbad95 100644 --- a/inception/inception-recommendation/src/test/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImplIntegrationTest.java +++ b/inception/inception-recommendation/src/test/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImplIntegrationTest.java @@ -106,7 +106,7 @@ public class RecommendationServiceImplIntegrationTest private RecommendationServiceImpl sut; private FeatureSupportRegistryImpl featureSupportRegistry; - private LayerRecommendtionSupportRegistryImpl layerRecommendtionSupportRegistry; + private SuggestionSupportRegistryImpl layerRecommendtionSupportRegistry; private Project project; private AnnotationLayer spanLayer; private Recommender spanLayerRecommender; @@ -115,7 +115,7 @@ public class RecommendationServiceImplIntegrationTest @BeforeEach public void setUp() throws Exception { - layerRecommendtionSupportRegistry = new LayerRecommendtionSupportRegistryImpl(asList( // + layerRecommendtionSupportRegistry = new SuggestionSupportRegistryImpl(asList( // new SpanSuggestionSupport(sut, sut, null, schemaService, null, null), new RelationSuggestionSupport(sut, sut, null, schemaService, null))); layerRecommendtionSupportRegistry.init(); diff --git a/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/uima/WebAnnoCasUtil.java b/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/uima/WebAnnoCasUtil.java index af21c693acd..e4f540396aa 100644 --- a/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/uima/WebAnnoCasUtil.java +++ b/inception/inception-support/src/main/java/de/tudarmstadt/ukp/inception/support/uima/WebAnnoCasUtil.java @@ -26,7 +26,6 @@ import static org.apache.uima.cas.impl.Serialization.serializeCASComplete; import static org.apache.uima.fit.util.CasUtil.getType; import static org.apache.uima.fit.util.CasUtil.select; -import static org.apache.uima.fit.util.CasUtil.selectAt; import static org.apache.uima.fit.util.CasUtil.selectCovering; import static org.apache.uima.fit.util.CasUtil.selectSingle; @@ -39,9 +38,7 @@ import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; import java.util.stream.StreamSupport; import org.apache.commons.lang3.Validate; @@ -222,22 +219,6 @@ public static boolean isBeginEndInSameSentence(CAS aCas, int aBegin, int aEnd) .findFirst().isPresent(); } - /** - * Get the sentence for this CAS based on the begin and end offsets. This is basically used to - * transform sentence address in one CAS to other sentence address for different CAS - * - * @param aCas - * the CAS. - * @param aBegin - * the begin offset. - * @return the sentence. - */ - public static AnnotationFS selectSentenceAt(CAS aCas, int aBegin) - { - return CasUtil.select(aCas, getType(aCas, Sentence.class)).stream() - .filter(s -> s.getBegin() == aBegin).findFirst().orElse(null); - } - public static AnnotationFS createToken(CAS aCas, int aBegin, int aEnd) { return aCas.createAnnotation(getType(aCas, Token.class), aBegin, aEnd); @@ -272,9 +253,8 @@ public static boolean exists(CAS aCas, Type aType) */ public static List selectOverlapping(CAS aCas, Type aType, int aBegin, int aEnd) { - - List annotations = new ArrayList<>(); - for (AnnotationFS t : select(aCas, aType)) { + var annotations = new ArrayList(); + for (var t : select(aCas, aType)) { if (t.getBegin() >= aEnd) { break; } @@ -288,90 +268,6 @@ public static List selectOverlapping(CAS aCas, Type aType, int aBe return annotations; } - /** - * Get the internal address of the first sentence annotation from CAS. This will be used as a - * reference for moving forward/backward sentences positions - * - * @param aCas - * The CAS object assumed to contains some sentence annotations - * @return the sentence number or -1 if aCas don't have sentence annotation - */ - public static AnnotationFS getFirstSentence(CAS aCas) - { - AnnotationFS firstSentence = null; - for (AnnotationFS s : select(aCas, getType(aCas, Sentence.class))) { - firstSentence = s; - break; - } - return firstSentence; - } - - /** - * Get the current sentence based on the annotation begin/end offset - * - * @param aCas - * the CAS. - * @param aBegin - * the begin offset. - * @param aEnd - * the end offset. - * @return the sentence. - */ - public static AnnotationFS getCurrentSentence(CAS aCas, int aBegin, int aEnd) - { - AnnotationFS currentSentence = null; - for (AnnotationFS sentence : selectSentences(aCas)) { - if (sentence.getBegin() <= aBegin && sentence.getEnd() > aBegin - && sentence.getEnd() <= aEnd) { - currentSentence = sentence; - break; - } - } - return currentSentence; - } - - /** - * Get the sentence based on the annotation begin offset - * - * @param aCas - * the CAS. - * @param aBegin - * the begin offset. - * @return the sentence. - */ - public static AnnotationFS selectSentenceCovering(CAS aCas, int aBegin) - { - AnnotationFS currentSentence = null; - for (AnnotationFS sentence : select(aCas, getType(aCas, Sentence.class))) { - if (sentence.getBegin() <= aBegin && sentence.getEnd() > aBegin) { - currentSentence = sentence; - break; - } - } - return currentSentence; - } - - public static AnnotationFS getNextToken(CAS aCas, int aBegin, int aEnd) - { - Type tokenType = getType(aCas, Token.class); - - AnnotationFS currentToken = selectAt(aCas, tokenType, aBegin, aEnd).stream().findFirst() - .orElse(null); - // this happens when tokens such as Dr. OR Ms. selected with double - // click, which make seletected text as Dr OR Ms - if (currentToken == null) { - currentToken = selectAt(aCas, tokenType, aBegin, aEnd + 1).stream().findFirst() - .orElse(null); - } - AnnotationFS nextToken = null; - - for (AnnotationFS token : CasUtil.selectFollowing(aCas, tokenType, currentToken, 1)) { - nextToken = token; - } - - return nextToken; - } - @SuppressWarnings("unchecked") public static T getNext(T aRef) { @@ -491,42 +387,6 @@ public static Collection selectTokens(CAS aCas) return CasUtil.select(aCas, getType(aCas, Token.class)); } - public static Collection selectTokensCovered(CAS aCas, int aBegin, int aEnd) - { - return CasUtil.selectCovered(aCas, getType(aCas, Token.class), aBegin, aEnd); - } - - public static Collection selectTokensCovered(AnnotationFS aCover) - { - return CasUtil.selectCovered(aCover.getCAS(), getType(aCover.getCAS(), Token.class), - aCover); - } - - /** - * For a span annotation, if a sub-token is selected, display the whole text so that the user is - * aware of what is being annotated, based on - * {@link WebAnnoCasUtil#selectOverlapping(CAS, Type, int, int)} ISSUE - Affected text not - * correctly displayed in annotation dialog (Bug #272) - * - * @param aCas - * the CAS. - * @param aBeginOffset - * the begin offset. - * @param aEndOffset - * the end offset. - * @return the selected text. - */ - public static String getSelectedText(CAS aCas, int aBeginOffset, int aEndOffset) - { - List tokens = selectOverlapping(aCas, getType(aCas, Token.class), - aBeginOffset, aEndOffset); - StringBuilder seletedTextSb = new StringBuilder(); - for (AnnotationFS token : tokens) { - seletedTextSb.append(token.getCoveredText()).append(" "); - } - return seletedTextSb.toString(); - } - public static boolean isNativeUimaType(String aType) { Validate.notNull(aType, "Type must not be null"); @@ -574,18 +434,6 @@ public static boolean isNativeUimaType(String aType) return false; } - public static boolean isPrimitiveFeature(FeatureStructure aFS, String aFeatureName) - { - Feature feature = aFS.getType().getFeatureByBaseName(aFeatureName); - - if (feature == null) { - throw new IllegalArgumentException("Type [" + aFS.getType().getName() - + "] has no feature called [" + aFeatureName + "]"); - } - - return isPrimitiveType(feature.getRange()); - } - public static boolean isPrimitiveType(Type aType) { switch (aType.getName()) { @@ -731,13 +579,6 @@ public static String getDocumentTitle(CAS aCas) } } - public static Set findAllFeatureStructures(CAS aCas) - { - Set allFSes = new LinkedHashSet<>(); - ((CASImpl) aCas).walkReachablePlusFSsSorted(allFSes::add, null, null, null); - return allFSes; - } - public static byte[] casToByteArray(CASCompleteSerializer aSer) throws IOException { try (var bos = new ByteArrayOutputStream()) { diff --git a/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/DocumentDetailsPage.java b/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/DocumentDetailsPage.java index 6f0910bf520..6617897aeaf 100644 --- a/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/DocumentDetailsPage.java +++ b/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/DocumentDetailsPage.java @@ -23,8 +23,6 @@ import static de.tudarmstadt.ukp.inception.app.ui.externalsearch.DocumentDetailsPage.PAGE_PARAM_DOCUMENT_ID; import static de.tudarmstadt.ukp.inception.app.ui.externalsearch.DocumentDetailsPage.PAGE_PARAM_REPOSITORY_ID; -import java.io.IOException; - import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.request.mapper.parameter.PageParameters; @@ -95,7 +93,7 @@ private String getDocumentText() try { return externalSearchService.getDocumentText(repo, collectionId, documentId); } - catch (IOException e) { + catch (Exception e) { return "ERROR: " + e.getMessage(); } } @@ -105,7 +103,7 @@ private ExternalSearchResult getDocumentResult() try { return externalSearchService.getDocumentResult(repo, collectionId, documentId); } - catch (IOException e) { + catch (Exception e) { return new ExternalSearchResult(repo, collectionId, documentId); } } diff --git a/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/ResultRowView.html b/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/ResultRowView.html index ac3793f9ea9..a1b160a25e9 100644 --- a/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/ResultRowView.html +++ b/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/ResultRowView.html @@ -19,11 +19,11 @@ -
-
- +
+
+
- +
@@ -33,12 +33,12 @@
-
- +
+ - + diff --git a/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/SearchPage.html b/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/SearchPage.html index 7a8b0c51650..261f4f32306 100644 --- a/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/SearchPage.html +++ b/inception/inception-ui-external-search/src/main/java/de/tudarmstadt/ukp/inception/app/ui/externalsearch/SearchPage.html @@ -16,6 +16,19 @@ limitations under the License. --> + + + + +
@@ -40,7 +53,7 @@
- +
[Results table]
diff --git a/inception/pom.xml b/inception/pom.xml index fb7d5a008d7..3672f557b3a 100644 --- a/inception/pom.xml +++ b/inception/pom.xml @@ -383,7 +383,7 @@ org.codehaus.mojo buildnumber-maven-plugin - 3.0.0 + 3.2.0 validate