Merge branch 'RED-7784-additional-fixes' into 'master'

RED-7785 - Fix false positives not getting removed for unprocessed changes & refactor merging unprocessed changes into entitylog logic.

Closes RED-7784

See merge request redactmanager/persistence-service!228
This commit is contained in:
Andrei Isvoran 2023-11-29 11:03:52 +01:00
commit 9e04a47e90
4 changed files with 335 additions and 259 deletions

View File

@ -0,0 +1,316 @@
package com.iqser.red.service.persistence.management.v1.processor.service;
import static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Change;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ChangeType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualRedactionType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.BaseAnnotation;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRecategorization;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;
import com.iqser.red.service.redaction.v1.model.UnprocessedManualEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
@RequiredArgsConstructor
public class EntityLogMergeService {
private final DictionaryPersistenceService dictionaryPersistenceService;
public EntityLog mergeEntityLog(ManualRedactions manualRedactions, List<UnprocessedManualEntity> unprocessedManualEntities, EntityLog entityLog, DossierEntity dossier) {
log.info("Merging EntityLog");
List<BaseAnnotation> allManualChanges = allManualChanges(manualRedactions);
// Sort manual changes by date so we process them in order of when they were requested
allManualChanges = allManualChanges.stream().sorted(Comparator.comparing(BaseAnnotation::getRequestDate)).toList();
allManualChanges.forEach(manualChange -> {
// this is ugly and should be replaced with switch pattern matching https://openjdk.org/jeps/406 -> requires Java 17 (preview) or higher
if (manualChange instanceof ManualRedactionEntry) {
mergeManualRedactionEntries((ManualRedactionEntry) manualChange, unprocessedManualEntities, entityLog, dossier);
} else if (manualChange instanceof IdRemoval) {
mergeIdsToRemove((IdRemoval) manualChange, entityLog);
} else if (manualChange instanceof ManualResizeRedaction) {
mergeResizeRedactions((ManualResizeRedaction) manualChange, entityLog);
} else if (manualChange instanceof ManualLegalBasisChange) {
mergeLegalBasisChanges((ManualLegalBasisChange) manualChange, entityLog);
} else if (manualChange instanceof ManualRecategorization) {
mergeRecategorizations((ManualRecategorization) manualChange, entityLog, dossier);
} else if (manualChange instanceof ManualForceRedaction) {
mergeForceRedactions((ManualForceRedaction) manualChange, entityLog);
}
});
log.info("EntityLog merged successfully!");
return entityLog;
}
private void mergeManualRedactionEntries(ManualRedactionEntry manualRedactionEntry, List<UnprocessedManualEntity> unprocessedManualEntities, EntityLog entityLog, DossierEntity dossier) {
UnprocessedManualEntity unprocessedManualEntity = unprocessedManualEntities.stream()
.filter(manualEntity -> manualEntity.getAnnotationId().equals(manualRedactionEntry.getAnnotationId()))
.findFirst().orElseThrow(() -> new NotFoundException("Entry with annotationId " + manualRedactionEntry.getAnnotationId() + " not found"));
List<Change> changes = new ArrayList<>();
changes.add(Change.builder()
.analysisNumber(entityLog.getAnalysisNumber())
.dateTime(OffsetDateTime.now())
.type(ChangeType.ADDED)
.build());
boolean isHint = isHint(manualRedactionEntry.getType(), dossier);
if (manualRedactionEntry.getDictionaryEntryType().equals(DictionaryEntryType.FALSE_POSITIVE)) {
var matchingEntities = entityLog.getEntityLogEntry().stream()
.filter(entityLogEntry -> equalPosition(manualRedactionEntry.getPositions().get(0), entityLogEntry.getPositions().get(0)))
.toList();
matchingEntities.forEach(matchingEntity -> {
mergeFalsePositive(entityLog, matchingEntity);
});
return;
}
var entityLogEntryBuilder = EntityLogEntry.builder()
.id(manualRedactionEntry.getAnnotationId())
.type(manualRedactionEntry.getType())
.value(manualRedactionEntry.getValue())
.legalBasis(manualRedactionEntry.getLegalBasis() == null || manualRedactionEntry.getLegalBasis().isEmpty() ? unprocessedManualEntity.getLegalBasis() : manualRedactionEntry.getLegalBasis())
.reason(manualRedactionEntry.getReason())
.entryType(isHint ? EntryType.HINT : EntryType.ENTITY)
.state(isHint ? EntryState.SKIPPED : EntryState.APPLIED)
.imported(false)
.matchedRule("")
.section(manualRedactionEntry.getSection())
.color(unprocessedManualEntity.getColor())
.positions(unprocessedManualEntity.getPositions())
.textAfter(unprocessedManualEntity.getTextAfter())
.textBefore(unprocessedManualEntity.getTextBefore())
.startOffset(unprocessedManualEntity.getStartOffset())
.endOffset(unprocessedManualEntity.getEndOffset())
.containingNodeId(unprocessedManualEntity.getContainingNodeId())
.closestHeadline(unprocessedManualEntity.getClosestHeadline())
.imageHasTransparency(false)
.dictionaryEntry(manualRedactionEntry.isAddToDictionary())
.dossierDictionaryEntry(manualRedactionEntry.isAddToDossierDictionary())
.excluded(false)
.changes(changes)
.manualChanges(List.of(ManualChange.builder()
.manualRedactionType(ManualRedactionType.ADD_LOCALLY)
.requestedDate(manualRedactionEntry.getRequestDate())
.processedDate(null)
.userId(manualRedactionEntry.getUser())
.propertyChanges(Map.of("value", manualRedactionEntry.getValue()))
.build()))
.engines(new HashSet<>())
.reference(new HashSet<>())
.importedRedactionIntersections(new HashSet<>());
entityLog.getEntityLogEntry().add(entityLogEntryBuilder
.build());
}
private void mergeFalsePositive(EntityLog entityLog, EntityLogEntry existingEntry) {
existingEntry.setState(EntryState.REMOVED);
List<Change> falsePositiveChanges = new ArrayList<>();
falsePositiveChanges.add(Change.builder()
.analysisNumber(entityLog.getAnalysisNumber())
.dateTime(OffsetDateTime.now())
.type(ChangeType.REMOVED)
.build());
if (existingEntry.getChanges() != null && !existingEntry.getChanges().isEmpty()) {
existingEntry.getChanges().addAll(falsePositiveChanges);
} else {
existingEntry.setChanges(falsePositiveChanges);
}
}
private void mergeIdsToRemove(IdRemoval idRemoval, EntityLog entityLog) {
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(idRemoval.getAnnotationId())).findAny();
if (entity.isPresent()) {
entity.get().setState(EntryState.IGNORED);
addChanges(entity.get().getChanges(), ChangeType.REMOVED, entityLog.getAnalysisNumber());
entity.get().getManualChanges().add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.REMOVE_LOCALLY)
.requestedDate(idRemoval.getRequestDate())
.processedDate(null)
.userId(idRemoval.getUser()).build());
}
}
private void mergeResizeRedactions(ManualResizeRedaction manualResizeRedaction, EntityLog entityLog) {
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(manualResizeRedaction.getAnnotationId())).findAny();
if (entity.isPresent()) {
var newPosition = manualResizeRedaction.getPositions().get(0);
entity.get().setPositions(List.of(new Position(
newPosition.getTopLeftX(),
newPosition.getTopLeftY(),
newPosition.getWidth(),
newPosition.getHeight(),
newPosition.getPage())));
addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber());
entity.get().getManualChanges().add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.RESIZE)
.requestedDate(manualResizeRedaction.getRequestDate())
.processedDate(null)
.propertyChanges(Map.of("value", manualResizeRedaction.getValue()))
.userId(manualResizeRedaction.getUser()).build());
}
}
private void mergeLegalBasisChanges(ManualLegalBasisChange manualLegalBasisChange, EntityLog entityLog) {
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(manualLegalBasisChange.getAnnotationId())).findAny();
if (entity.isPresent()) {
entity.get().setLegalBasis(manualLegalBasisChange.getLegalBasis());
entity.get().setSection(manualLegalBasisChange.getSection());
entity.get().setValue(manualLegalBasisChange.getValue());
addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber());
entity.get().getManualChanges().add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE)
.requestedDate(manualLegalBasisChange.getRequestDate())
.processedDate(null)
.propertyChanges(Map.of("value", manualLegalBasisChange.getValue(),
"section", manualLegalBasisChange.getSection(),
"legalBasis", manualLegalBasisChange.getLegalBasis()))
.userId(manualLegalBasisChange.getUser())
.build());
}
}
private void mergeRecategorizations(ManualRecategorization recategorization, EntityLog entityLog, DossierEntity dossier) {
boolean isHint = isHint(recategorization.getType(), dossier);
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(recategorization.getAnnotationId())).findAny();
if (entity.isPresent()) {
entity.get().setType(recategorization.getType());
entity.get().setEntryType(getEntryType(isHint, recategorization.getType()));
entity.get().setState(isHint ? EntryState.SKIPPED : EntryState.APPLIED);
addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber());
entity.get().getManualChanges().add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.RECATEGORIZE)
.requestedDate(recategorization.getRequestDate())
.processedDate(recategorization.getProcessedDate())
.userId(recategorization.getUser())
.propertyChanges(Map.of("type", recategorization.getType()))
.build());
}
}
private static EntryType getEntryType(boolean isHint, String type) {
if (type.equals("image") || type.equals("logo") || type.equals("signature") || type.equals("formula")) {
return isHint ? EntryType.IMAGE_HINT : EntryType.IMAGE;
} else {
return isHint ? EntryType.HINT : EntryType.ENTITY;
}
}
private void mergeForceRedactions(ManualForceRedaction forceRedaction, EntityLog entityLog) {
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(forceRedaction.getAnnotationId())).findAny();
if (entity.isPresent()) {
entity.get().setLegalBasis(forceRedaction.getLegalBasis());
entity.get().setState(EntryState.APPLIED);
addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber());
var forceRedactManualChange = ManualChange.builder()
.manualRedactionType(ManualRedactionType.FORCE_REDACT)
.requestedDate(forceRedaction.getRequestDate())
.processedDate(forceRedaction.getProcessedDate())
.userId(forceRedaction.getUser());
if (forceRedaction.getLegalBasis() != null && !forceRedaction.getLegalBasis().isEmpty()) {
forceRedactManualChange.propertyChanges(Map.of("legalBasis", forceRedaction.getLegalBasis()));
}
entity.get().getManualChanges().add(forceRedactManualChange.build());
}
}
private void addChanges(List<Change> changes, ChangeType changeType, int analysisNumber) {
if (!changes.isEmpty()) {
changes.add(Change.builder()
.analysisNumber(analysisNumber + 1)
.dateTime(OffsetDateTime.now())
.type(changeType)
.build());
} else {
changes.add(Change.builder().analysisNumber(analysisNumber).dateTime(OffsetDateTime.now()).type(changeType).build());
}
}
private boolean isHint(String type, DossierEntity dossier) {
String typeId = toTypeId(type, dossier.getDossierTemplateId());
TypeEntity typeEntity = dictionaryPersistenceService.getType(typeId);
if (typeEntity == null) {
var optionalType = dictionaryPersistenceService.getAllTypes(false).stream().filter(typeEntity1 -> typeEntity1.getType().equals(type)).findFirst();
if (optionalType.isPresent()) {
typeEntity = optionalType.get();
} else {
throw new NotFoundException("TypeEntity could not be found for typeId: " + typeId);
}
}
return typeEntity.isHint();
}
private boolean equalPosition(Rectangle position1, Position position2) {
return position1.getTopLeftX() == position2.x()
&& position1.getTopLeftY() == position2.y()
&& position1.getWidth() == position2.w()
&& position1.getHeight() == position2.h();
}
private List<BaseAnnotation> allManualChanges(ManualRedactions manualRedactions) {
return Stream.of(manualRedactions.getEntriesToAdd(),
manualRedactions.getForceRedactions(),
manualRedactions.getResizeRedactions(),
manualRedactions.getRecategorizations(),
manualRedactions.getIdsToRemove(),
manualRedactions.getLegalBasisChanges()).flatMap(Collection::stream).map(baseAnnotation -> (BaseAnnotation) baseAnnotation).toList();
}
}

View File

@ -1,45 +1,22 @@
package com.iqser.red.service.persistence.management.v1.processor.service;
import static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.iqser.red.service.persistence.management.v1.processor.client.redactionservice.UnprocessedManualEntityClient;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionProviderService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Change;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ChangeType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.FilteredEntityLogRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualRedactionType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRecategorization;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.redaction.v1.model.UnprocessedManualEntity;
import lombok.AccessLevel;
@ -60,8 +37,7 @@ public class EntityLogService {
UnprocessedManualEntityClient unprocessedManualEntityClient;
DossierService dossierService;
CommentService commentService;
DictionaryPersistenceService dictionaryPersistenceService;
DossierTemplatePersistenceService dossierTemplatePersistenceService;
EntityLogMergeService entityLogMergeService;
public EntityLog getEntityLog(String dossierId, String fileId) {
@ -90,7 +66,7 @@ public class EntityLogService {
DossierEntity dossier = dossierService.getDossierById(dossierId);
ManualRedactions manualRedactions = manualRedactionProviderService.getManualRedactions(fileId, true);
List<UnprocessedManualEntity> unprocessedManualEntities = getUnprocessedManualEntities(dossierId, fileId, dossier.getDossierTemplateId(), manualRedactions);
mergeEntityLog(manualRedactions, unprocessedManualEntities, entityLog, dossier, fileStatus);
entityLogMergeService.mergeEntityLog(manualRedactions, unprocessedManualEntities, entityLog, dossier);
}
Map<String, Integer> commentCountPerAnnotationId = commentService.getCommentCounts(fileId);
@ -142,232 +118,4 @@ public class EntityLogService {
}
public void mergeEntityLog(ManualRedactions manualRedactions, List<UnprocessedManualEntity> unprocessedManualEntities, EntityLog entityLog, DossierEntity dossier, FileModel fileStatus) {
log.info("Merging EntityLog");
mergeManualRedactionEntries(manualRedactions.getEntriesToAdd(), unprocessedManualEntities, entityLog, dossier, fileStatus);
mergeIdsToRemove(manualRedactions.getIdsToRemove(), entityLog);
mergeResizeRedactions(manualRedactions.getResizeRedactions(), entityLog);
mergeLegalBasisChanges(manualRedactions.getLegalBasisChanges(), entityLog);
mergeRecategorizations(manualRedactions.getRecategorizations(), entityLog, dossier, fileStatus);
mergeForceRedactions(manualRedactions.getForceRedactions(), entityLog);
log.info("EntityLog merged successfully!");
}
private void mergeManualRedactionEntries(Set<ManualRedactionEntry> manualRedactionEntries, List<UnprocessedManualEntity> unprocessedManualEntities, EntityLog entityLog, DossierEntity dossier, FileModel fileStatus) {
manualRedactionEntries.forEach(manualRedactionEntry -> {
UnprocessedManualEntity unprocessedManualEntity = unprocessedManualEntities.stream()
.filter(manualEntity -> manualEntity.getAnnotationId().equals(manualRedactionEntry.getAnnotationId()))
.findFirst().orElseThrow(() -> new NotFoundException("Entry with annotationId " + manualRedactionEntry.getAnnotationId() + " not found"));
List<Change> changes = new ArrayList<>();
changes.add(Change.builder()
.analysisNumber(entityLog.getAnalysisNumber())
.dateTime(OffsetDateTime.now())
.type(ChangeType.ADDED)
.build());
boolean isHint = isHint(manualRedactionEntry.getType(), dossier, fileStatus);
entityLog.getEntityLogEntry().add(EntityLogEntry.builder()
.id(manualRedactionEntry.getAnnotationId())
.type(manualRedactionEntry.getType())
.value(manualRedactionEntry.getValue())
.legalBasis(manualRedactionEntry.getLegalBasis() == null || manualRedactionEntry.getLegalBasis().isEmpty() ? unprocessedManualEntity.getLegalBasis() : manualRedactionEntry.getLegalBasis())
.reason(manualRedactionEntry.getReason())
.entryType(isHint ? EntryType.HINT : EntryType.ENTITY)
.state(isHint ? EntryState.SKIPPED : EntryState.APPLIED)
.imported(false)
.matchedRule("")
.section(manualRedactionEntry.getSection())
.color(unprocessedManualEntity.getColor())
.positions(unprocessedManualEntity.getPositions())
.textAfter(unprocessedManualEntity.getTextAfter())
.textBefore(unprocessedManualEntity.getTextBefore())
.startOffset(unprocessedManualEntity.getStartOffset())
.endOffset(unprocessedManualEntity.getEndOffset())
.containingNodeId(unprocessedManualEntity.getContainingNodeId())
.closestHeadline(unprocessedManualEntity.getClosestHeadline())
.imageHasTransparency(false)
.dictionaryEntry(manualRedactionEntry.isAddToDictionary())
.dossierDictionaryEntry(manualRedactionEntry.isAddToDossierDictionary())
.excluded(false)
.changes(changes)
.manualChanges(List.of(ManualChange.builder()
.manualRedactionType(ManualRedactionType.ADD_LOCALLY)
.requestedDate(manualRedactionEntry.getRequestDate())
.processedDate(null)
.userId(manualRedactionEntry.getUser())
.propertyChanges(Map.of("value", manualRedactionEntry.getValue()))
.build()))
.engines(new HashSet<>())
.reference(new HashSet<>())
.importedRedactionIntersections(new HashSet<>())
.build());
});
}
private void mergeIdsToRemove(Set<IdRemoval> idRemovals, EntityLog entityLog) {
idRemovals.forEach(idRemoval -> {
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(idRemoval.getAnnotationId())).findAny();
if (entity.isPresent()) {
entity.get().setState(EntryState.IGNORED);
addChanges(entity.get().getChanges(), ChangeType.REMOVED, entityLog.getAnalysisNumber());
entity.get().getManualChanges().add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.REMOVE_LOCALLY)
.requestedDate(idRemoval.getRequestDate())
.processedDate(null)
.userId(idRemoval.getUser()).build());
}
});
}
private void mergeResizeRedactions(Set<ManualResizeRedaction> manualResizeRedactions, EntityLog entityLog) {
manualResizeRedactions.forEach(manualResizeRedaction -> {
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(manualResizeRedaction.getAnnotationId())).findAny();
if (entity.isPresent()) {
var newPosition = manualResizeRedaction.getPositions().get(0);
entity.get().setPositions(List.of(new Position(
newPosition.getTopLeftX(),
newPosition.getTopLeftY(),
newPosition.getWidth(),
newPosition.getHeight(),
newPosition.getPage())));
addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber());
entity.get().getManualChanges().add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.RESIZE)
.requestedDate(manualResizeRedaction.getRequestDate())
.processedDate(null)
.propertyChanges(Map.of("value", manualResizeRedaction.getValue()))
.userId(manualResizeRedaction.getUser()).build());
}
});
}
private void mergeLegalBasisChanges(Set<ManualLegalBasisChange> manualLegalBasisChanges, EntityLog entityLog) {
manualLegalBasisChanges.forEach(manualLegalBasisChange -> {
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(manualLegalBasisChange.getAnnotationId())).findAny();
if (entity.isPresent()) {
entity.get().setLegalBasis(manualLegalBasisChange.getLegalBasis());
entity.get().setSection(manualLegalBasisChange.getSection());
entity.get().setValue(manualLegalBasisChange.getValue());
addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber());
entity.get().getManualChanges().add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE)
.requestedDate(manualLegalBasisChange.getRequestDate())
.processedDate(null)
.propertyChanges(Map.of("value", manualLegalBasisChange.getValue(),
"section", manualLegalBasisChange.getSection(),
"legalBasis", manualLegalBasisChange.getLegalBasis()))
.userId(manualLegalBasisChange.getUser())
.build());
}
});
}
private void mergeRecategorizations(Set<ManualRecategorization> recategorizations, EntityLog entityLog, DossierEntity dossier, FileModel fileStatus) {
recategorizations.forEach(recategorization -> {
boolean isHint = isHint(recategorization.getType(), dossier, fileStatus);
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(recategorization.getAnnotationId())).findAny();
if (entity.isPresent()) {
entity.get().setType(recategorization.getType());
entity.get().setEntryType(getEntryType(isHint, recategorization.getType()));
entity.get().setState(isHint ? EntryState.SKIPPED : EntryState.APPLIED);
addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber());
entity.get().getManualChanges().add(ManualChange.builder()
.manualRedactionType(ManualRedactionType.RECATEGORIZE)
.requestedDate(recategorization.getRequestDate())
.processedDate(recategorization.getProcessedDate())
.userId(recategorization.getUser())
.propertyChanges(Map.of("type", recategorization.getType()))
.build());
}
});
}
private static EntryType getEntryType(boolean isHint, String type) {
if (type.equals("image") || type.equals("logo") || type.equals("signature") || type.equals("formula")) {
return isHint ? EntryType.IMAGE_HINT : EntryType.IMAGE;
} else {
return isHint ? EntryType.HINT : EntryType.ENTITY;
}
}
private void mergeForceRedactions(Set<ManualForceRedaction> forceRedactions, EntityLog entityLog) {
forceRedactions.forEach(forceRedaction -> {
var entity = entityLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(forceRedaction.getAnnotationId())).findAny();
if (entity.isPresent()) {
entity.get().setLegalBasis(forceRedaction.getLegalBasis());
entity.get().setState(EntryState.APPLIED);
addChanges(entity.get().getChanges(), ChangeType.CHANGED, entityLog.getAnalysisNumber());
var forceRedactManualChange = ManualChange.builder()
.manualRedactionType(ManualRedactionType.FORCE_REDACT)
.requestedDate(forceRedaction.getRequestDate())
.processedDate(forceRedaction.getProcessedDate())
.userId(forceRedaction.getUser());
if (forceRedaction.getLegalBasis() != null && !forceRedaction.getLegalBasis().isEmpty()) {
forceRedactManualChange.propertyChanges(Map.of("legalBasis", forceRedaction.getLegalBasis()));
}
entity.get().getManualChanges().add(forceRedactManualChange.build());
}
});
}
private void addChanges(List<Change> changes, ChangeType changeType, int analysisNumber) {
if (!changes.isEmpty()) {
changes.add(Change.builder()
.analysisNumber(analysisNumber + 1)
.dateTime(OffsetDateTime.now())
.type(changeType)
.build());
} else {
changes.add(Change.builder().analysisNumber(analysisNumber).dateTime(OffsetDateTime.now()).type(changeType).build());
}
}
private boolean isHint(String type, DossierEntity dossier, FileModel fileStatus) {
String typeId = toTypeId(type, dossier.getDossierTemplateId());
TypeEntity typeEntity = dictionaryPersistenceService.getType(typeId);
if (typeEntity == null) {
var optionalType = dictionaryPersistenceService.getAllTypes(false).stream().filter(typeEntity1 -> typeEntity1.getType().equals(type)).findFirst();
if (optionalType.isPresent()) {
typeEntity = optionalType.get();
} else {
throw new NotFoundException("TypeEntity could not be found for typeId: " + typeId);
}
}
if (Objects.equals(type, "CBI_address")) {
String vertebrateStudyId = "";
var fileAttributesMap = fileStatus.getFileAttributes();
var dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(dossier.getDossierTemplateId());
var fileAttributeConfigs = dossierTemplate.getFileAttributeConfigs();
var vertebrateStudy = fileAttributeConfigs.stream().filter(fileAttributeConfigEntity -> fileAttributeConfigEntity.getLabel().equals("Vertebrate Study")).findFirst();
if (vertebrateStudy.isPresent()) {
vertebrateStudyId = vertebrateStudy.get().getId();
}
if (!Objects.equals(vertebrateStudyId, "")
&& fileAttributesMap.containsKey(vertebrateStudyId)
&& (fileAttributesMap.get(vertebrateStudyId) == null || !fileAttributesMap.get(vertebrateStudyId).equals("Yes"))) {
return true;
}
}
return typeEntity.isHint();
}
}

View File

@ -18,7 +18,7 @@ import java.util.UUID;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.testcontainers.shaded.com.google.common.collect.Lists;
@ -28,6 +28,7 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.configur
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.CommentService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierService;
import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogMergeService;
import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
@ -49,10 +50,11 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;
import com.iqser.red.service.redaction.v1.model.UnprocessedManualEntity;
@ExtendWith(SpringExtension.class)
public class EntityLogTest {
public class EntityLogMergeTest {
@MockBean
private FileStatusService fileStatusService;
@ -78,12 +80,12 @@ public class EntityLogTest {
@MockBean
private DossierTemplatePersistenceService dossierTemplatePersistenceService;
private EntityLogService entityLogService;
private EntityLogMergeService entityLogMergeService;
@BeforeEach
public void setUp() {
entityLogService = new EntityLogService(fileManagementStorageService, fileStatusService, manualRedactionProviderService, unprocessedManualEntityClient, dossierService, commentService, dictionaryPersistenceService, dossierTemplatePersistenceService);
entityLogMergeService = new EntityLogMergeService(dictionaryPersistenceService);
}
@ -127,7 +129,9 @@ public class EntityLogTest {
.build());
when(dictionaryPersistenceService.getType(anyString())).thenReturn(TypeEntity.builder().isHint(false).build());
EntityLog response = entityLogService.getEntityLog(dossierId, fileId, null, true);
EntityLog response = entityLogMergeService.mergeEntityLog(manualRedactions, List.of(unprocessedManualEntity), entityLog, DossierEntity.builder()
.dossierTemplateId(dossierTemplateId)
.build());
assertNotNull(response);
assertFalse(response.getEntityLogEntry().isEmpty());
@ -250,11 +254,13 @@ public class EntityLogTest {
.addToDossierDictionary(false)
.fileId(fileId)
.requestDate(OffsetDateTime.now())
.dictionaryEntryType(DictionaryEntryType.ENTRY)
.type("manual")
.build()))
.idsToRemove(Set.of(
IdRemoval.builder()
.annotationId(entryToRemoveId)
.requestDate(OffsetDateTime.now())
.build()
))
.resizeRedactions(Set.of(
@ -263,6 +269,7 @@ public class EntityLogTest {
.value("Random")
.annotationId(entryToResizeId)
.positions(positions)
.requestDate(OffsetDateTime.now())
.build()
))
.legalBasisChanges(Set.of(
@ -271,6 +278,7 @@ public class EntityLogTest {
.value("Random")
.legalBasis("New legal basis")
.section("Section")
.requestDate(OffsetDateTime.now())
.build()
))
.forceRedactions(Set.of(
@ -278,8 +286,10 @@ public class EntityLogTest {
.annotationId(forceRedactionId)
.fileId(fileId)
.legalBasis("Force")
.requestDate(OffsetDateTime.now())
.build()
))
.build();
}
}

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -30,5 +31,6 @@ public class ManualRedactionEntry extends BaseAnnotation {
private String textBefore;
private String textAfter;
private String sourceId;
private DictionaryEntryType dictionaryEntryType;
}