Skip to content

Commit

Permalink
Merge pull request #2078 from inception-project/bugfix/2077-Unable-to…
Browse files Browse the repository at this point in the history
…-merge-via-curation-siderbar-if-username-contains-DOT

#2077 - Unable to merge via curation siderbar if username contains "."
  • Loading branch information
reckart committed Mar 12, 2021
2 parents 25d3b09 + eb0268d commit e572280
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@
*/
package de.tudarmstadt.ukp.clarin.webanno.api.annotation.model;

import static de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst.FEAT_REL_SOURCE;
import static de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst.FEAT_REL_TARGET;
import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.getAddr;
import static org.apache.wicket.event.Broadcast.BREADTH;

import java.io.Serializable;
import java.util.Optional;

import org.apache.uima.cas.CAS;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxRequestTarget;
Expand Down Expand Up @@ -67,6 +71,16 @@ public Selection()
// Nothing to do
}

public void selectArc(AnnotationFS aFS)
{
Type depType = aFS.getType();
Feature originFeat = depType.getFeatureByBaseName(FEAT_REL_SOURCE);
Feature targetFeat = depType.getFeatureByBaseName(FEAT_REL_TARGET);
AnnotationFS originFS = (AnnotationFS) aFS.getFeatureValue(originFeat);
AnnotationFS targetFS = (AnnotationFS) aFS.getFeatureValue(targetFeat);
selectArc(new VID(aFS), originFS, targetFS);
}

public void selectArc(VID aVid, AnnotationFS aOriginFs, AnnotationFS aTargetFs)
{
selectedAnnotationId = aVid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.uima.cas.CAS;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.slf4j.Logger;
Expand All @@ -41,7 +37,6 @@

import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationSchemaService;
import de.tudarmstadt.ukp.clarin.webanno.api.DocumentService;
import de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.AnnotationEditorExtension;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.AnnotationEditorExtensionImplBase;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.action.AnnotationActionHandler;
Expand Down Expand Up @@ -97,104 +92,85 @@ public void handleAction(AnnotationActionHandler aPanel, AnnotatorState aState,
if (!aParamId.getExtensionId().equals(EXTENSION_ID)) {
return;
}
VID extendedVID = parse(aParamId);

if (!aAction.equals(ACTION_SELECT_ARC) && !aAction.equals(ACTION_SELECT_SPAN)) {
return;
}

// Annotation has been selected for gold
saveAnnotation(aAction, aPanel, aState, aTarget, aCas, extendedVID);
CurationVID extendedVID = CurationVID.parse(aParamId.getExtensionPayload());
if (extendedVID != null) {
saveAnnotation(aAction, aPanel, aState, aTarget, aCas, extendedVID);
}
}

/**
* Save annotation identified by aVID from user CAS to given curator's CAS
*/
private void saveAnnotation(String aAction, AnnotationActionHandler aPanel,
AnnotatorState aState, AjaxRequestTarget aTarget, CAS aTargetCas, VID aVID)
AnnotatorState aState, AjaxRequestTarget aTarget, CAS aTargetCas,
CurationVID aCurationVid)
throws IOException, AnnotationException
{
AnnotationLayer layer = annotationService.getLayer(aVID.getLayerId());

// get user CAS and annotation (to be merged into curator's)
SourceDocument doc = aState.getDocument();
String srcUser = ((CurationVID) aVID).getUsername();
String srcUser = aCurationVid.getUsername();

if (!documentService.existsAnnotationDocument(doc, srcUser)) {
log.error(String.format("Source CAS of %s for curation not found", srcUser));
return;
}

VID vid = VID.parse(aCurationVid.getExtensionPayload());

AnnotationLayer layer = annotationService.getLayer(vid.getLayerId());

CAS srcCas = documentService.readAnnotationCas(doc, srcUser);
AnnotationFS sourceAnnotation = selectAnnotationByAddr(srcCas, aVID.getId());
AnnotationFS sourceAnnotation = selectAnnotationByAddr(srcCas, vid.getId());

// merge into curator's CAS depending on annotation type (span or arc)
CasMerge casMerge = new CasMerge(annotationService);
CasMergeOperationResult mergeResult;
if (ACTION_SELECT_SPAN.equals(aAction.toString())) {
mergeResult = casMerge.mergeSpanAnnotation(doc, srcUser, layer, aTargetCas,
sourceAnnotation, layer.isAllowStacking());
// open created/updates FS in annotation detail editorpanel
aState.getSelection().selectSpan(new VID(mergeResult.getResultFSAddress()), aTargetCas,
sourceAnnotation.getBegin(), sourceAnnotation.getEnd());

// open created/updates FS in annotation detail editorpanel
AnnotationFS mergedAnno = selectAnnotationByAddr(aTargetCas,
mergeResult.getResultFSAddress());
aState.getSelection().selectSpan(mergedAnno);
}
else if (ACTION_SELECT_ARC.equals(aAction.toString())) {
// this is a slot arc
if (aVID.isSlotSet()) {
if (vid.isSlotSet()) {
TypeAdapter adapter = annotationService.getAdapter(layer);
AnnotationFeature feature = adapter.listFeatures().stream().sequential()
.skip(aVID.getAttribute()).findFirst().get();
.skip(vid.getAttribute()).findFirst().get();

mergeResult = casMerge.mergeSlotFeature(doc, srcUser, layer, aTargetCas,
sourceAnnotation, feature.getName(), aVID.getSlot());
sourceAnnotation, feature.getName(), vid.getSlot());

// open created/updates FS in annotation detail editorpanel
aState.getSelection().selectSpan(new VID(mergeResult.getResultFSAddress()),
aTargetCas, sourceAnnotation.getBegin(), sourceAnnotation.getEnd());
AnnotationFS mergedAnno = selectAnnotationByAddr(aTargetCas,
mergeResult.getResultFSAddress());
aState.getSelection().selectSpan(mergedAnno);
}
// normal relation annotation arc is clicked
else {
mergeResult = casMerge.mergeRelationAnnotation(doc, srcUser, layer, aTargetCas,
sourceAnnotation, layer.isAllowStacking());

// open created/updates FS in annotation detail editorpanel
AnnotationFS mergedAnno = selectAnnotationByAddr(aTargetCas,
mergeResult.getResultFSAddress());
Type depType = mergedAnno.getType();
Feature originFeat = depType.getFeatureByBaseName(WebAnnoConst.FEAT_REL_SOURCE);
Feature targetFeat = depType.getFeatureByBaseName(WebAnnoConst.FEAT_REL_TARGET);
AnnotationFS originFS = (AnnotationFS) mergedAnno.getFeatureValue(originFeat);
AnnotationFS targetFS = (AnnotationFS) mergedAnno.getFeatureValue(targetFeat);
aState.getSelection().selectArc(new VID(mergeResult.getResultFSAddress()), originFS,
targetFS);
aState.getSelection().selectArc(mergedAnno);
}
}

aPanel.actionSelect(aTarget);
aPanel.actionCreateOrUpdate(aTarget, aTargetCas); // should also update timestamps
}

/**
* Parse extension payload of given VID into CurationVID
*/
protected VID parse(VID aParamId)
{
// format of extension payload is <USER>:<VID> with standard VID format
// <ID>-<SUB>.<ATTR>.<SLOT>@<LAYER>
Matcher matcher = Pattern.compile("(?:(?<USER>\\w+)\\:)" + "(?<VID>.+)")
.matcher(aParamId.getExtensionPayload());
if (!matcher.matches()) {
return aParamId;
}

if (matcher.group("VID") == null || matcher.group("USER") == null) {
return aParamId;
}

String vidStr = matcher.group("VID");
String username = matcher.group("USER");
return new CurationVID(aParamId.getExtensionId(), username, VID.parse(vidStr),
aParamId.getExtensionPayload());
}

@Override
public void render(CAS aCas, AnnotatorState aState, VDocument aVdoc, int aWindowBeginOffset,
int aWindowEndOffset)
Expand Down Expand Up @@ -246,7 +222,7 @@ public void render(CAS aCas, AnnotatorState aState, VDocument aVdoc, int aWindow
for (VSpan vspan : tmpDoc.spans()) {
VID aDepVID = vspan.getVid();
VID prevVID = VID.copyVID(aDepVID);
VID newVID = new CurationVID(EXTENSION_ID, username,
VID newVID = new CurationVID(username,
new VID(vspan.getLayer().getId(), aDepVID.getId(), aDepVID.getSubId(),
aDepVID.getAttribute(), aDepVID.getSlot()));
vspan.setVid(newVID);
Expand All @@ -263,9 +239,8 @@ public void render(CAS aCas, AnnotatorState aState, VDocument aVdoc, int aWindow
for (VArc varc : tmpDoc.arcs()) {
// update varc vid
VID vid = varc.getVid();
VID extendedVID = new CurationVID(EXTENSION_ID, username,
new VID(varc.getLayer().getId(), vid.getId(), vid.getSubId(),
vid.getAttribute(), vid.getSlot()));
VID extendedVID = new CurationVID(username, new VID(varc.getLayer().getId(),
vid.getId(), vid.getSubId(), vid.getAttribute(), vid.getSlot()));
// set target and src with new vids for arc
VSpan targetSpan = newIdSpan.get(varc.getTarget());
VSpan srcSpan = newIdSpan.get(varc.getSource());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@
*/
package de.tudarmstadt.ukp.inception.curation;

import static de.tudarmstadt.ukp.inception.curation.CurationEditorExtension.EXTENSION_ID;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.VID;

public class CurationVID
Expand All @@ -30,17 +39,9 @@ public class CurationVID

private final String username;

public CurationVID(String aExtId, String aUsername, VID aVID)
public CurationVID(String aUsername, VID aVID)
{
super(aExtId, aVID.getLayerId(), aVID.getId(), aVID.getSubId(), aVID.getAttribute(),
aVID.getSlot(), aUsername + ":" + aVID.toString());
username = aUsername;
}

public CurationVID(String aExtId, String aUsername, VID aVID, String aExtensionPayload)
{
super(aExtId, aVID.getLayerId(), aVID.getId(), aVID.getSubId(), aVID.getAttribute(),
aVID.getSlot(), aExtensionPayload);
super(aVID.getId(), EXTENSION_ID, aUsername + "!" + aVID.toString());
username = aUsername;
}

Expand All @@ -50,15 +51,51 @@ public String getUsername()
}

@Override
public int hashCode()
public String getExtensionPayload()
{
return StringUtils.substringAfter(super.getExtensionPayload(), "!");
}

/**
* Parse extension payload of given VID into CurationVID
*/
public static CurationVID parse(String aParamId)
{
return super.hashCode() * 31 + username.hashCode();
// format of extension payload is <USER>!<VID> with standard VID format
// <ID>-<SUB>.<ATTR>.<SLOT>@<LAYER>
Matcher matcher = Pattern.compile("(?:(?<USER>[^!]+)\\!)(?<VID>.+)").matcher(aParamId);
if (!matcher.matches()) {
return null;
}

if (matcher.group("VID") == null || matcher.group("USER") == null) {
return null;
}

String vidStr = matcher.group("VID");
String username = matcher.group("USER");
return new CurationVID(username, VID.parse(vidStr));
}

@Override
public boolean equals(Object aObj)
public boolean equals(final Object other)
{
return super.equals(aObj) && ((CurationVID) aObj).getUsername().equals(username);
if (!(other instanceof CurationVID)) {
return false;
}

CurationVID castOther = (CurationVID) other;
return new EqualsBuilder() //
.append(username, castOther.username) //
.append(getId(), castOther.getId()) //
.append(getExtensionPayload(), castOther.getExtensionPayload()) //
.isEquals();
}

@Override
public int hashCode()
{
return new HashCodeBuilder().append(username).append(getId()).append(getExtensionPayload())
.toHashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,50 +21,47 @@
*/
package de.tudarmstadt.ukp.inception.curation;

import static org.junit.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Before;
import org.junit.Test;

import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.VID;

public class CurationVIDTest
{
private CurationEditorExtension extension;

@Before
public void setup()
{
extension = new CurationEditorExtension();
}

@Test
public void testParse()
{
assertParseVid(VID.parse("ext:10-kevin:10"), "ext", -1, 10, -1, -1, -1, "kevin",
"kevin:10");
assertParseVid(VID.parse("ext:10-kevin:10.1"), "ext", -1, 10, -1, 1, -1, "kevin",
"kevin:10.1");
assertParseVid(VID.parse("ext:10-kevin:10.1.2"), "ext", -1, 10, -1, 1, 2, "kevin",
"kevin:10.1.2");
assertParseVid(VID.parse("ext:10-kevin:10-1.2.3"), "ext", -1, 10, 1, 2, 3, "kevin",
"kevin:10-1.2.3");
assertParseVid(VID.parse("ext:10-kevin:10-1.2.3@1"), "ext", 1, 10, 1, 2, 3, "kevin",
"kevin:10-1.2.3@1");
assertParseVid(VID.parse("curationEditorExtension:10-kevin!10"), //
"curationEditorExtension", "kevin", "10", -1, 10, -1, -1, -1);

assertParseVid(VID.parse("curationEditorExtension:10-kevin!10.1"), //
"curationEditorExtension", "kevin", "10.1", -1, 10, -1, 1, -1);

assertParseVid(VID.parse("curationEditorExtension:10-kevin!10.1.2"), //
"curationEditorExtension", "kevin", "10.1.2", -1, 10, -1, 1, 2);

assertParseVid(VID.parse("curationEditorExtension:10-kevin!10-1.2.3"), //
"curationEditorExtension", "kevin", "10-1.2.3", -1, 10, 1, 2, 3);

assertParseVid(VID.parse("curationEditorExtension:10-kevin!10-1.2.3@1"), //
"curationEditorExtension", "kevin", "10-1.2.3@1", 1, 10, 1, 2, 3);
}

private void assertParseVid(VID aVID, String aExtensionId, int aLayerId, int aAnnotationID,
int aSubAnnotationId, int aAttribute, int aSlot, String aUsername,
String aExtensionPayload)
private void assertParseVid(VID aVID, String aExtensionId, String aUsername, String aPayload,
int aLayerId, int aAnnotationID, int aSubAnnotationId, int aAttribute, int aSlot)
{
VID a = extension.parse(aVID);
assertEquals(aExtensionId, a.getExtensionId());
assertEquals(aExtensionPayload, a.getExtensionPayload());
assertEquals(aUsername, ((CurationVID) a).getUsername());
assertEquals(aLayerId, a.getLayerId());
assertEquals(aAnnotationID, a.getId());
assertEquals(aSubAnnotationId, a.getSubId());
assertEquals(aAttribute, a.getAttribute());
assertEquals(aSlot, a.getSlot());
CurationVID a = CurationVID.parse(aVID.getExtensionPayload());
VID b = VID.parse(a.getExtensionPayload());

assertThat(a.getExtensionId()).isEqualTo(aExtensionId);
assertThat(a.getExtensionPayload()).isEqualTo(aPayload);
assertThat(a.getUsername()).isEqualTo(aUsername);

assertThat(b.getLayerId()).isEqualTo(aLayerId);
assertThat(b.getId()).isEqualTo(aAnnotationID);
assertThat(b.getSubId()).isEqualTo(aSubAnnotationId);
assertThat(b.getAttribute()).isEqualTo(aAttribute);
assertThat(b.getSlot()).isEqualTo(aSlot);
}
}

0 comments on commit e572280

Please sign in to comment.