use of de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff.ConfigurationSet in project webanno by webanno.
the class AgreementUtils method dumpAgreementConfigurationSets.
private static void dumpAgreementConfigurationSets(PrintStream aOut, AgreementResult<ICodingAnnotationStudy> aAgreement, List<ConfigurationSet> aSets) {
for (ConfigurationSet cfgSet : aSets) {
StringBuilder sb = new StringBuilder();
sb.append(cfgSet.getPosition());
for (Configuration cfg : cfgSet.getConfigurations()) {
if (sb.length() > 0) {
sb.append(" \t");
}
sb.append(cfg.toString());
}
aOut.println(sb);
}
}
use of de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff.ConfigurationSet in project webanno by webanno.
the class CasMerge method reMergeCas.
/**
* Using {@code DiffResult}, determine the annotations to be deleted from the randomly generated
* MergeCase. The initial Merge CAs is stored under a name {@code CurationPanel#CURATION_USER}.
* <p>
* Any similar annotations stacked in a {@code CasDiff2.Position} will be assumed a difference
* <p>
* Any two annotation with different value will be assumed a difference
* <p>
* Any non stacked empty/null annotations are assumed agreement
* <p>
* Any non stacked annotations with similar values for each of the features are assumed
* agreement
* <p>
* Any two link mode / slotable annotations which agree on the base features are assumed
* agreement
*
* @param aDiff
* the {@link DiffResult}
* @param aCases
* a map of {@code CAS}s for each users and the random merge
*/
public void reMergeCas(DiffResult aDiff, SourceDocument aTargetDocument, String aTargetUsername, CAS aTargetCas, Map<String, CAS> aCases) throws AnnotationException, UIMAException {
silenceEvents = true;
int updated = 0;
int created = 0;
Set<LogMessage> messages = new LinkedHashSet<>();
// Remove any annotations from the target CAS - keep type system, sentences and tokens
clearAnnotations(aTargetCas);
// If there is nothing to merge, bail out
if (aCases.isEmpty()) {
return;
}
// Set up a cache for resolving type to layer to avoid hammering the DB as we process each
// position
Map<String, AnnotationLayer> type2layer = aDiff.getPositions().stream().map(Position::getType).distinct().map(type -> schemaService.findLayer(aTargetDocument.getProject(), type)).collect(toMap(AnnotationLayer::getName, identity()));
List<String> layerNames = new ArrayList<>(type2layer.keySet());
// Move token layer to front
if (layerNames.contains(Token.class.getName())) {
layerNames.remove(Token.class.getName());
layerNames.add(0, Token.class.getName());
}
// Move sentence layer to front
if (layerNames.contains(Sentence.class.getName())) {
layerNames.remove(Sentence.class.getName());
layerNames.add(0, Sentence.class.getName());
}
// and sentences before the others)
for (String layerName : layerNames) {
List<SpanPosition> positions = aDiff.getPositions().stream().filter(pos -> layerName.equals(pos.getType())).filter(pos -> pos instanceof SpanPosition).map(pos -> (SpanPosition) pos).filter(pos -> pos.getFeature() == null).collect(Collectors.toList());
if (positions.isEmpty()) {
continue;
}
LOG.debug("Processing {} span positions on layer {}", positions.size(), layerName);
// Slots are also excluded for the moment
for (SpanPosition position : positions) {
LOG.trace(" | processing {}", position);
ConfigurationSet cfgs = aDiff.getConfigurationSet(position);
if (!shouldMerge(aDiff, cfgs)) {
continue;
}
try {
Map<String, List<CAS>> casMap = new LinkedHashMap<>();
aCases.forEach((k, v) -> casMap.put(k, asList(v)));
AnnotationFS sourceFS = (AnnotationFS) cfgs.getConfigurations().get(0).getRepresentative(casMap);
CasMergeOperationResult result = mergeSpanAnnotation(aTargetDocument, aTargetUsername, type2layer.get(position.getType()), aTargetCas, sourceFS, false);
LOG.trace(" `-> merged annotation with agreement");
switch(result.getState()) {
case CREATED:
created++;
break;
case UPDATED:
updated++;
break;
}
} catch (AnnotationException e) {
LOG.trace(" `-> not merged annotation: {}", e.getMessage());
messages.add(LogMessage.error(this, "%s", e.getMessage()));
}
}
}
// After the spans are in place, we can merge the slot features
for (String layerName : layerNames) {
List<SpanPosition> positions = aDiff.getPositions().stream().filter(pos -> layerName.equals(pos.getType())).filter(pos -> pos instanceof SpanPosition).map(pos -> (SpanPosition) pos).filter(pos -> pos.getFeature() != null).collect(Collectors.toList());
if (positions.isEmpty()) {
continue;
}
LOG.debug("Processing {} slot positions on layer [{}]", positions.size(), layerName);
for (SpanPosition position : positions) {
LOG.trace(" | processing {}", position);
ConfigurationSet cfgs = aDiff.getConfigurationSet(position);
if (!shouldMerge(aDiff, cfgs)) {
continue;
}
try {
Map<String, List<CAS>> casMap = new LinkedHashMap<>();
aCases.forEach((k, v) -> casMap.put(k, asList(v)));
AnnotationFS sourceFS = (AnnotationFS) cfgs.getConfigurations().get(0).getRepresentative(casMap);
AID sourceFsAid = cfgs.getConfigurations().get(0).getRepresentativeAID();
mergeSlotFeature(aTargetDocument, aTargetUsername, type2layer.get(position.getType()), aTargetCas, sourceFS, sourceFsAid.feature, sourceFsAid.index);
LOG.trace(" `-> merged annotation with agreement");
} catch (AnnotationException e) {
LOG.trace(" `-> not merged annotation: {}", e.getMessage());
messages.add(LogMessage.error(this, "%s", e.getMessage()));
}
}
}
// Finally, we merge the relations
for (String layerName : layerNames) {
List<RelationPosition> positions = aDiff.getPositions().stream().filter(pos -> layerName.equals(pos.getType())).filter(pos -> pos instanceof RelationPosition).map(pos -> (RelationPosition) pos).collect(Collectors.toList());
if (positions.isEmpty()) {
continue;
}
LOG.debug("Processing {} relation positions on layer [{}]", positions.size(), layerName);
for (RelationPosition position : positions) {
LOG.trace(" | processing {}", position);
ConfigurationSet cfgs = aDiff.getConfigurationSet(position);
if (!shouldMerge(aDiff, cfgs)) {
continue;
}
try {
Map<String, List<CAS>> casMap = new LinkedHashMap<>();
aCases.forEach((k, v) -> casMap.put(k, asList(v)));
AnnotationFS sourceFS = (AnnotationFS) cfgs.getConfigurations().get(0).getRepresentative(casMap);
CasMergeOperationResult result = mergeRelationAnnotation(aTargetDocument, aTargetUsername, type2layer.get(position.getType()), aTargetCas, sourceFS, false);
LOG.trace(" `-> merged annotation with agreement");
switch(result.getState()) {
case CREATED:
created++;
break;
case UPDATED:
updated++;
break;
}
} catch (AnnotationException e) {
LOG.trace(" `-> not merged annotation: {}", e.getMessage());
messages.add(LogMessage.error(this, "%s", e.getMessage()));
}
}
}
if (eventPublisher != null) {
eventPublisher.publishEvent(new BulkAnnotationEvent(this, aTargetDocument, aTargetUsername, null));
}
}
use of de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff.ConfigurationSet in project webanno by webanno.
the class SuggestionViewPanel method addSuggestionColor.
/**
* For each {@link ConfigurationSet}, where there are some differences in users annotation and
* the curation annotation.
*/
private void addSuggestionColor(Project aProject, Mode aMode, Map<String, CAS> aCasMap, Map<String, Map<VID, AnnotationState>> aSuggestionColors, Collection<ConfigurationSet> aCfgSet, boolean aDisagree, boolean aAgree) {
for (ConfigurationSet cs : aCfgSet) {
boolean use = false;
for (String u : cs.getCasGroupIds()) {
Map<VID, AnnotationState> colors = aSuggestionColors.computeIfAbsent(u, k -> new HashMap<>());
for (Configuration c : cs.getConfigurations(u)) {
FeatureStructure fs = c.getFs(u, aCasMap);
AnnotationLayer layer = schemaService.findLayer(aProject, fs.getType().getName());
TypeAdapter typeAdapter = schemaService.getAdapter(layer);
VID vid;
// link FS
if (c.getPosition().getFeature() != null) {
int fi = 0;
for (AnnotationFeature f : typeAdapter.listFeatures()) {
if (f.getName().equals(c.getPosition().getFeature())) {
break;
}
fi++;
}
vid = new VID(WebAnnoCasUtil.getAddr(fs), fi, c.getAID(u).index);
} else {
vid = new VID(WebAnnoCasUtil.getAddr(fs));
}
if (aAgree) {
colors.put(vid, AGREE);
continue;
}
// automation and correction projects
if (!aMode.equals(CURATION) && !aAgree) {
if (cs.getCasGroupIds().size() == 2) {
colors.put(vid, DO_NOT_USE);
} else {
colors.put(vid, DISAGREE);
}
continue;
}
// this set agree with the curation annotation
if (c.getCasGroupIds().contains(CURATION_USER)) {
use = true;
} else {
use = false;
}
// this curation view
if (u.equals(CURATION_USER)) {
continue;
}
if (aAgree) {
colors.put(vid, AGREE);
} else if (use) {
colors.put(vid, USE);
} else if (aDisagree) {
colors.put(vid, DISAGREE);
} else if (!cs.getCasGroupIds().contains(CURATION_USER)) {
colors.put(vid, DISAGREE);
} else {
colors.put(vid, DO_NOT_USE);
}
}
}
}
}
use of de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff.ConfigurationSet in project webanno by webanno.
the class SuggestionViewPanel method init.
/**
* Initializes the user annotation segments later to be filled with content.
*/
public void init(AjaxRequestTarget aTarget, CurationContainer aCurationContainer, Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress, SourceListView aCurationSegment) throws UIMAException, ClassNotFoundException, IOException {
AnnotatorState state = aCurationContainer.getState();
SourceDocument sourceDocument = state.getDocument();
Map<String, CAS> casses = new HashMap<>();
// This is the CAS that the user can actively edit
CAS annotatorCas = getAnnotatorCas(state, aAnnotationSelectionByUsernameAndAddress, sourceDocument, casses);
// We store the CAS that the user will edit as the "CURATION USER"
casses.put(CURATION_USER, annotatorCas);
List<DiffAdapter> adapters = getDiffAdapters(schemaService, state.getAnnotationLayers());
Map<String, Map<VID, AnnotationState>> annoStates1 = new HashMap<>();
Project project = state.getProject();
Mode mode1 = state.getMode();
DiffResult diff;
if (mode1.equals(CURATION)) {
diff = doDiffSingle(adapters, LINK_ROLE_AS_LABEL, casses, aCurationSegment.getCurationBegin(), aCurationSegment.getCurationEnd()).toResult();
} else {
diff = doDiffSingle(adapters, LINK_ROLE_AS_LABEL, casses, aCurationSegment.getBegin(), aCurationSegment.getEnd()).toResult();
}
Collection<ConfigurationSet> d = diff.getDifferingConfigurationSets().values();
Collection<ConfigurationSet> i = diff.getIncompleteConfigurationSets().values();
for (ConfigurationSet cfgSet : d) {
if (i.contains(cfgSet)) {
i.remove(cfgSet);
}
}
addSuggestionColor(project, mode1, casses, annoStates1, d, false, false);
addSuggestionColor(project, mode1, casses, annoStates1, i, true, false);
List<ConfigurationSet> all = new ArrayList<>();
all.addAll(diff.getConfigurationSets());
all.removeAll(d);
all.removeAll(i);
addSuggestionColor(project, mode1, casses, annoStates1, all, false, true);
// get differing feature structures
Map<String, Map<VID, AnnotationState>> annoStates = annoStates1;
List<String> usernamesSorted = new ArrayList<>(casses.keySet());
Collections.sort(usernamesSorted);
final Mode mode = state.getMode();
boolean isAutomationMode = mode.equals(Mode.AUTOMATION);
boolean isCorrectionMode = mode.equals(Mode.CORRECTION);
boolean isCurationMode = mode.equals(Mode.CURATION);
List<UserAnnotationSegment> segments = new ArrayList<>();
for (String username : usernamesSorted) {
if ((!username.equals(CURATION_USER) && isCurationMode) || (username.equals(CURATION_USER) && (isAutomationMode || isCorrectionMode))) {
CAS cas = casses.get(username);
// Set up coloring strategy
ColoringStrategy curationColoringStrategy = makeColoringStrategy(annoStates.get(username));
// Create curation view for the current user
UserAnnotationSegment seg = new UserAnnotationSegment();
seg.setUsername(username);
seg.setAnnotatorState(state);
seg.setCollectionData(getCollectionInformation(schemaService, aCurationContainer));
seg.setDocumentResponse(render(cas, state, curationColoringStrategy));
seg.setSelectionByUsernameAndAddress(aAnnotationSelectionByUsernameAndAddress);
segments.add(seg);
}
}
sentenceListView.setModelObject(segments);
if (aTarget != null) {
aTarget.add(this);
}
}
use of de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff.ConfigurationSet in project webanno by webanno.
the class SuggestionViewPanel method updatePanel.
/**
* @param aTarget
* the AJAX target.
* @param aCurationContainer
* the container.
* @param aAnnotationSelectionByUsernameAndAddress
* selections by user.
* @param aCurationSegment
* the segment.
* @throws UIMAException
* hum?
* @throws ClassNotFoundException
* hum?
* @throws IOException
* hum?
* @throws AnnotationException
* hum?
*/
private void updatePanel(AjaxRequestTarget aTarget, CurationContainer aCurationContainer, Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress, SourceListView aCurationSegment) throws UIMAException, ClassNotFoundException, IOException, AnnotationException {
LOG.trace("call update");
AnnotatorState state = aCurationContainer.getState();
if (state.getDocument() == null) {
return;
}
SourceDocument sourceDocument = state.getDocument();
Map<String, CAS> casses = new HashMap<>();
// This is the CAS that the user can actively edit
CAS annotatorCas = getAnnotatorCas(state, aAnnotationSelectionByUsernameAndAddress, sourceDocument, casses);
// We store the CAS that the user will edit as the "CURATION USER"
casses.put(CURATION_USER, annotatorCas);
List<DiffAdapter> adapters = getDiffAdapters(schemaService, state.getAnnotationLayers());
Map<String, Map<VID, AnnotationState>> annoStates = new HashMap<>();
Project project = state.getProject();
Mode mode = state.getMode();
DiffResult diff;
if (mode.equals(CURATION)) {
diff = doDiffSingle(adapters, LINK_ROLE_AS_LABEL, casses, aCurationSegment.getCurationBegin(), aCurationSegment.getCurationEnd()).toResult();
} else {
diff = doDiffSingle(adapters, LINK_ROLE_AS_LABEL, casses, aCurationSegment.getBegin(), aCurationSegment.getEnd()).toResult();
}
Collection<ConfigurationSet> d = diff.getDifferingConfigurationSets().values();
Collection<ConfigurationSet> i = diff.getIncompleteConfigurationSets().values();
for (ConfigurationSet cfgSet : d) {
if (i.contains(cfgSet)) {
i.remove(cfgSet);
}
}
addSuggestionColor(project, mode, casses, annoStates, d, false, false);
addSuggestionColor(project, mode, casses, annoStates, i, true, false);
List<ConfigurationSet> all = new ArrayList<>();
all.addAll(diff.getConfigurationSets());
all.removeAll(d);
all.removeAll(i);
addSuggestionColor(project, mode, casses, annoStates, all, false, true);
// get differing feature structures
sentenceListView.visitChildren(BratSuggestionVisualizer.class, (v, visit) -> {
BratSuggestionVisualizer vis = (BratSuggestionVisualizer) v;
UserAnnotationSegment seg = vis.getModelObject();
CAS cas = casses.get(seg.getUsername());
if (cas == null) {
// This may happen if a user has not yet finished document
return;
}
// Set up coloring strategy
ColoringStrategy curationColoringStrategy = makeColoringStrategy(annoStates.get(seg.getUsername()));
// Create curation view for the current user
try {
seg.setCollectionData(getCollectionInformation(schemaService, aCurationContainer));
seg.setDocumentResponse(render(cas, state, curationColoringStrategy));
seg.setAnnotatorState(state);
seg.setSelectionByUsernameAndAddress(aAnnotationSelectionByUsernameAndAddress);
} catch (IOException e) {
error("Unable to render: " + e.getMessage());
LOG.error("Unable to render", e);
}
if (isBlank(vis.getDocumentData())) {
return;
}
vis.render(aTarget);
});
}
Aggregations