Skip to content

Commit

Permalink
Merge branch 'main' into feature/3944-Ability-to-use-recommenders-as-…
Browse files Browse the repository at this point in the history
…curator

* main:
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release inception-27.6
  #3958 - Dynamically sized multi-line string fields no resize automatically
  #3957 - Cannot create project using project-creator role
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release inception-27.5
  #3932 - Upgrade dependencies
  #3953 - Switch core footer items to auto-configuration
  #3951 - Lots of errors and warnings from logging system during startup
  No issue: Demote some logged message to debug level
  #3940 - Merge label into eligible existing annotation even when stacking is enabled

% Conflicts:
%	inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/service/RecommendationServiceImpl.java
%	inception/inception-recommendation/src/main/java/de/tudarmstadt/ukp/inception/recommendation/tasks/PredictionTask.java
  • Loading branch information
reckart committed Apr 18, 2023
2 parents d7c6741 + 6144292 commit 8370acb
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
</span>
</label>
<div class="feature-editor-value">
<textarea wicket:id="value" class="form-control dynamic-textarea" style="resize: none;" onfocus="resizeDynamicTextArea(this)" oninput="resizeDynamicTextArea(this)" onblur="resizeDynamicTextArea(this)"></textarea>
<textarea wicket:id="value" class="form-control dynamic-textarea" style="resize: none;"/>
</div>
</div>
</wicket:panel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ protected AbstractTextComponent<String> createInputField()
public void renderHead(IHeaderResponse aResponse)
{
aResponse.render(JavaScriptHeaderItem.forReference(DynamicTextAreaScriptReference.get()));
aResponse.render(OnDomReadyHeaderItem.forScript(
"window.addEventListener('load', function(){resizeDynamicTextArea(document.getElementById('"
+ textarea.getMarkupId() + "'));});"));
aResponse.render(OnDomReadyHeaderItem.forScript(String.join("\n", //
"let ta = document.getElementById('" + textarea.getMarkupId() + "');", //
"ta && window.addEventListener('load', () => resizeDynamicTextArea(ta));", //
"ta?.addEventListener('focus', ev => resizeDynamicTextArea(ta));", //
"ta?.addEventListener('input', ev => resizeDynamicTextArea(ta));", //
"ta?.addEventListener('blur', ev => resizeDynamicTextArea(ta));")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;

import java.util.List;

Expand All @@ -38,6 +37,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import de.tudarmstadt.ukp.clarin.webanno.api.annotation.exception.MultipleSentenceCoveredException;
Expand All @@ -56,8 +56,9 @@
@ExtendWith(MockitoExtension.class)
public class SpanAdapterTest
{
private @Mock FeatureSupportRegistry featureSupportRegistry;

private LayerSupportRegistry layerSupportRegistry;
private FeatureSupportRegistry featureSupportRegistry;
private Project project;
private AnnotationLayer neLayer;
private JCas jcas;
Expand Down Expand Up @@ -89,7 +90,6 @@ public void setup() throws Exception
neLayer.setId(1l);

layerSupportRegistry = new LayerSupportRegistryImpl(asList());
featureSupportRegistry = mock(FeatureSupportRegistry.class);

behaviors = asList(new SpanOverlapBehavior(), new SpanCrossSentenceBehavior(),
new SpanAnchoringModeBehavior());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="de.tudarmstadt.ukp">
<Configuration status="WARN">
<properties>
<SpringProfile name="!app &amp; !cli"> <!-- While booting before Spring is there-->
<property name="pattern">%d{yyyy-MM-dd HH:mm:ss} %level{length=5} [%encode{$${ctx:username:-SYSTEM}}{CRLF}] %logger{1} - %encode{%msg}{CRLF}%n</property>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ logging.level.de.tudarmstadt.ukp.inception.security=TRACE
== Custom logging

A custom logging configuration can be specified when starting up {product-name} using the parameter
`-Dlog4j.configurationFile=/path/to/your/log4.xml`. This should be a standard Log4J configuration file.
`-Dlogging.config=/path/to/your/log4.xml`. This should be a standard Log4J2 configuration file.
A good starting point is the default configuration used by {product-name} which can be found in link:https://github.com/inception-project/inception/blob/main/inception/inception-app-webapp/src/main/resources/log4j2.xml[our code repository].

== Logging in JSON format

If you would like to integrate the logging output of {product-name} with something like LogStash and
Kibana, you may want log output to be in a properly interpretable JSON format, instead of the usual
plain text format. {product-name} comes with several JSON configurations that are compatible with
popular tools like LogStash and others. You can activate it by adding the following sections to a custom `log4j.xml` file in the `Appenders` sections and in the `Root` logger.
popular tools like LogStash and others. You can activate it by adding the following sections to a custom `log4j2.xml` file in the `Appenders` sections and in the `Root` logger.

[source,text]
----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class ViewportTracker {
leafTrackingCandidates.forEach(e => this.observer.observe(e))

const endTime = new Date().getTime()
console.log(`Tracking visibility of ${leafTrackingCandidates.size} elements in ${Math.abs(endTime - startTime)}ms`)
console.debug(`Tracking visibility of ${leafTrackingCandidates.size} elements in ${Math.abs(endTime - startTime)}ms`)
}

private handleIntersectRange (entries: IntersectionObserverEntry[], observer: IntersectionObserver): void {
Expand All @@ -117,7 +117,7 @@ export class ViewportTracker {
})

if (visibleElementsAdded || !this.initialized) {
console.log(`Visible elements changed: ${visibleElementsAdded} added, ${this._visibleElements.size} visible elements in total`)
console.debug(`Visible elements changed: ${visibleElementsAdded} added, ${this._visibleElements.size} visible elements in total`)
// the first time the callback is called, we want to make sure that the annotations are
// loaded at least once
this.initialized = true
Expand All @@ -143,7 +143,7 @@ export class ViewportTracker {
})
const endTime = new Date().getTime()

console.log(`Visible: ${begin}-${end} (${this._visibleElements.size} visible elements, ${Math.abs(endTime - startTime)}ms)`)
console.debug(`Visible: ${begin}-${end} (${this._visibleElements.size} visible elements, ${Math.abs(endTime - startTime)}ms)`)
return [begin, end]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,11 @@ public int upsertSpanFeature(SourceDocument aDocument, String aDocumentOwner, CA
.at(aBegin, aEnd).nullOK().get();

int address;
if (annoFS == null || aLayer.isAllowStacking()) {
if (annoFS != null && adapter.getFeatureValue(aFeature, annoFS) == null) {
// If there is an annotation where the predicted feature is unset, use it ...
address = ICasUtil.getAddr(annoFS);
}
else if (annoFS == null || aLayer.isAllowStacking()) {
// ... if not or if stacking is allowed, then we create a new annotation - this also
// takes care of attaching to an annotation if necessary
var newAnnotation = adapter.add(aDocument, aDocumentOwner, aCas, aBegin, aEnd);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void execute()

appEventPublisher.publishEvent(RecommenderTaskNotificationEvent
.builder(this, project, sessionOwner.getUsername()) //
.withMessage(LogMessage.info(this, "New preditions available")) //
.withMessage(LogMessage.info(this, "New predictions available")) //
.build());

// We reset this in case the state was not properly cleared, e.g. the AL session
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
*/
package de.tudarmstadt.ukp.inception.recommendation.service;

import static de.tudarmstadt.ukp.clarin.webanno.model.OverlapMode.ANY_OVERLAP;
import static de.tudarmstadt.ukp.clarin.webanno.model.OverlapMode.NO_OVERLAP;
import static de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService.FEATURE_NAME_AUTO_ACCEPT_MODE_SUFFIX;
import static de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService.FEATURE_NAME_IS_PREDICTION;
import static de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService.FEATURE_NAME_SCORE_EXPLANATION_SUFFIX;
Expand All @@ -27,6 +29,7 @@
import static org.apache.uima.fit.factory.JCasFactory.createText;
import static org.apache.uima.util.TypeSystemUtil.typeSystem2TypeSystemDescription;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -60,15 +63,20 @@
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer;
import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocument;
import de.tudarmstadt.ukp.dkpro.core.api.ner.type.NamedEntity;
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.string.StringFeatureSupport;
import de.tudarmstadt.ukp.inception.annotation.layer.span.SpanAdapter;
import de.tudarmstadt.ukp.inception.annotation.storage.CasStorageSession;
import de.tudarmstadt.ukp.inception.recommendation.api.RecommenderFactoryRegistry;
import de.tudarmstadt.ukp.inception.recommendation.api.model.Offset;
import de.tudarmstadt.ukp.inception.recommendation.api.model.Recommender;
import de.tudarmstadt.ukp.inception.recommendation.api.recommender.RecommendationEngineFactory;
import de.tudarmstadt.ukp.inception.schema.layer.LayerSupportRegistry;
import de.tudarmstadt.ukp.inception.schema.service.AnnotationSchemaServiceImpl;
import de.tudarmstadt.ukp.inception.schema.service.FeatureSupportRegistryImpl;

@ExtendWith(MockitoExtension.class)
@ContextConfiguration(classes = SpringConfig.class)
Expand All @@ -80,10 +88,13 @@ public class RecommendationServiceImplIntegrationTest

private @Autowired TestEntityManager testEntityManager;

private RecommendationServiceImpl sut;
private @Mock RecommenderFactoryRegistry recommenderFactoryRegistry;
private @Mock AnnotationSchemaServiceImpl annoService;
private @Mock AnnotationSchemaServiceImpl schemaService;
private @Mock LayerSupportRegistry layerSupportRegistry;

private RecommendationServiceImpl sut;

private FeatureSupportRegistryImpl featureSupportRegistry;
private Project project;
private AnnotationLayer layer;
private Recommender rec;
Expand All @@ -93,7 +104,10 @@ public class RecommendationServiceImplIntegrationTest
public void setUp() throws Exception
{
sut = new RecommendationServiceImpl(null, null, null, recommenderFactoryRegistry, null,
annoService, null, null, testEntityManager.getEntityManager());
schemaService, null, null, testEntityManager.getEntityManager());

featureSupportRegistry = new FeatureSupportRegistryImpl(asList(new StringFeatureSupport()));
featureSupportRegistry.init();

project = createProject(PROJECT_NAME);
layer = createAnnotationLayer();
Expand Down Expand Up @@ -190,10 +204,10 @@ public void monkeyPatchTypeSystem_WithNer_CreatesScoreFeatures() throws Exceptio
JCas jCas = createText("I am text CAS", "de");
session.add("jCas", CasAccessMode.EXCLUSIVE_WRITE_ACCESS, jCas.getCas());

when(annoService.getFullProjectTypeSystem(project))
when(schemaService.getFullProjectTypeSystem(project))
.thenReturn(typeSystem2TypeSystemDescription(jCas.getTypeSystem()));
when(annoService.listAnnotationLayer(project)).thenReturn(asList(layer));
doCallRealMethod().when(annoService).upgradeCas(any(CAS.class), any(CAS.class),
when(schemaService.listAnnotationLayer(project)).thenReturn(asList(layer));
doCallRealMethod().when(schemaService).upgradeCas(any(CAS.class), any(CAS.class),
any(TypeSystemDescription.class));

sut.cloneAndMonkeyPatchCAS(project, jCas.getCas(), jCas.getCas());
Expand Down Expand Up @@ -236,6 +250,88 @@ void thatZeroWithAnnotationsAreCorrectlyAnchoredOnTokens() throws Exception
.isEqualTo(new Offset(9, 9));
}

@Test
void testUpsertSpanFeature() throws Exception
{
var docOwner = "dummy";
var doc = SourceDocument.builder() //
.withProject(project) //
.build();
var feature = AnnotationFeature.builder() //
.withName(NamedEntity._FeatName_value) //
.withType(CAS.TYPE_NAME_STRING) //
.build();
var layer = AnnotationLayer.builder() //
.forJCasClass(NamedEntity.class) //
.build();
var adapter = new SpanAdapter(layerSupportRegistry, featureSupportRegistry, null, layer,
() -> asList(), asList());

when(schemaService.getAdapter(layer)).thenReturn(adapter);

layer.setOverlapMode(NO_OVERLAP);
var cas = createJCas();
var targetFS = new NamedEntity(cas, 0, 10);
targetFS.addToIndexes();
assertThat(targetFS.getValue()).isNull();

sut.upsertSpanFeature(doc, docOwner, cas.getCas(), layer, feature, "V1",
targetFS.getBegin(), targetFS.getEnd());

assertThat(targetFS.getValue()) //
.as("Label was merged into existing annotation replacing unset label") //
.isEqualTo("V1");

sut.upsertSpanFeature(doc, docOwner, cas.getCas(), layer, feature, "V2",
targetFS.getBegin(), targetFS.getEnd());

assertThat(targetFS.getValue()) //
.as("Label was merged into existing annotation replacing previous label") //
.isEqualTo("V2");

sut.upsertSpanFeature(doc, docOwner, cas.getCas(), layer, feature, "V3", 10, 20);

assertThat(cas.select(NamedEntity.class).asList()) //
.as("Label was merged as new annotation") //
.extracting(NamedEntity::getBegin, NamedEntity::getEnd, NamedEntity::getValue)
.containsExactlyInAnyOrder( //
tuple(0, 10, "V2"), //
tuple(10, 20, "V3"));

layer.setOverlapMode(ANY_OVERLAP);
cas.reset();
targetFS = new NamedEntity(cas, 0, 10);
targetFS.addToIndexes();
assertThat(targetFS.getValue()).isNull();

sut.upsertSpanFeature(doc, docOwner, cas.getCas(), layer, feature, "V1",
targetFS.getBegin(), targetFS.getEnd());

assertThat(targetFS.getValue()) //
.as("Label was merged into existing annotation replacing unset label") //
.isEqualTo("V1");

sut.upsertSpanFeature(doc, docOwner, cas.getCas(), layer, feature, "V2",
targetFS.getBegin(), targetFS.getEnd());

assertThat(cas.select(NamedEntity.class).asList()) //
.as("Label was merged as new annotation") //
.extracting(NamedEntity::getBegin, NamedEntity::getEnd, NamedEntity::getValue)
.containsExactlyInAnyOrder( //
tuple(0, 10, "V1"), //
tuple(0, 10, "V2"));

sut.upsertSpanFeature(doc, docOwner, cas.getCas(), layer, feature, "V3", 10, 20);

assertThat(cas.select(NamedEntity.class).asList()) //
.as("Label was merged as new annotation") //
.extracting(NamedEntity::getBegin, NamedEntity::getEnd, NamedEntity::getValue)
.containsExactlyInAnyOrder( //
tuple(0, 10, "V1"), //
tuple(0, 10, "V2"), //
tuple(10, 20, "V3"));
}

// Helper

private Project createProject(String aName)
Expand Down
4 changes: 4 additions & 0 deletions inception/inception-ui-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

<!-- Spring security dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import org.springframework.core.annotation.Order;

@Order(FooterItem.ORDER_CENTER)
@org.springframework.stereotype.Component
public class VersionFooterItem
implements FooterItem
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import org.springframework.core.annotation.Order;

@Order(FooterItem.ORDER_RIGHT)
@org.springframework.stereotype.Component
public class WarningsFooterItem
implements FooterItem
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import de.tudarmstadt.ukp.clarin.webanno.ui.core.footer.FooterItem;

@Order(FooterItem.ORDER_RIGHT + 100)
@org.springframework.stereotype.Component
public class AboutFooterItem
implements FooterItem
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,35 @@
*/
package de.tudarmstadt.ukp.inception.ui.core.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import de.tudarmstadt.ukp.clarin.webanno.ui.core.footer.VersionFooterItem;
import de.tudarmstadt.ukp.clarin.webanno.ui.core.footer.WarningsFooterItem;
import de.tudarmstadt.ukp.inception.ui.core.about.AboutFooterItem;

@Configuration
@EnableConfigurationProperties(CspPropertiesImpl.class)
public class CoreUiAutoConfiguration
{
// No beans yet
@ConditionalOnMissingBean(value = VersionFooterItem.class)
@Bean
public VersionFooterItem versionFooterItem()
{
return new VersionFooterItem();
}

@Bean
public WarningsFooterItem warningsFooterItem()
{
return new WarningsFooterItem();
}

@Bean
public AboutFooterItem aboutFooterItem()
{
return new AboutFooterItem();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,18 @@ private boolean isMenubarVisibleToCurrentUser()
return true;
}

if (userRepository.isAdministrator(user.getObject())) {
if (userRepository.isAdministrator(user.getObject())
|| userRepository.isProjectCreator(user.getObject())) {
return true;
}

// The project might be null if it is in the process of being created. Normally, this can
// only be done by admisn and project creators that are handled above - so returning false
// here is really just a sanity fallback that should never kick in.
if (project.getObject().getId() == null) {
return false;
}

var roles = dashboardProperties.getAccessibleByRoles().toArray(PermissionLevel[]::new);
return projectService.hasRole(user.getObject(), project.getObject(), MANAGER, roles);
}
Expand Down
Loading

0 comments on commit 8370acb

Please sign in to comment.