diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogMergeService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogMergeService.java index d971f251f..716747f7b 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogMergeService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogMergeService.java @@ -4,6 +4,7 @@ import static com.iqser.red.service.persistence.management.v1.processor.utils.Ty import java.time.OffsetDateTime; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -97,11 +98,15 @@ public class EntityLogMergeService { if (allManualChanges.containsKey(entityLogEntry.getId())) { - mergeLocalManualChanges(dossier, allManualChanges, entityLogEntry, analysisNumber); + List pendingImageRecategorizations = mergeLocalManualChangesAndReturnNonMergeableAsPending(dossier, + allManualChanges, + entityLogEntry, + analysisNumber); List pendingDictionaryEntries = buildPendingDictionaryEntries(allManualChanges, entityLogEntry); + // insert pending entries directly after the associated entry to enable performant linking in UI - for (EntityLogEntry pendingDictionaryEntry : pendingDictionaryEntries) { + for (EntityLogEntry pendingDictionaryEntry : concatLists(pendingDictionaryEntries, pendingImageRecategorizations)) { numberOfAddedPendingEntries++; entityLogEntries.add(i + numberOfAddedPendingEntries, pendingDictionaryEntry); } @@ -117,6 +122,14 @@ public class EntityLogMergeService { } + private static List concatLists(List pendingDictionaryEntries, List pendingImageRecategorizations) { + + return Stream.of(pendingDictionaryEntries, pendingImageRecategorizations) + .flatMap(Collection::stream) + .toList(); + } + + private Stream buildPendingDictionaryChanges(ManualRedactions unprocessedManualRedactions) { return unprocessedManualRedactions.getEntriesToAdd() @@ -161,25 +174,36 @@ public class EntityLogMergeService { } - private void mergeLocalManualChanges(DossierEntity dossier, Map> allManualChanges, EntityLogEntry entityLogEntry, int analysisNumber) { + private List mergeLocalManualChangesAndReturnNonMergeableAsPending(DossierEntity dossier, + Map> allManualChanges, + EntityLogEntry entityLogEntry, + int analysisNumber) { - allManualChanges.getOrDefault(entityLogEntry.getId(), Collections.emptyList()) + return allManualChanges.getOrDefault(entityLogEntry.getId(), Collections.emptyList()) .stream() .filter(BaseAnnotation::isLocal) .sorted(Comparator.comparing(BaseAnnotation::getRequestDate)) - .forEach(localChange -> { + .map(localChange -> { if (localChange instanceof IdRemoval idRemoval) { - mergeIdsToRemove(idRemoval, entityLogEntry, analysisNumber); + mergeIdToRemove(idRemoval, entityLogEntry, analysisNumber); + return null; } else if (localChange instanceof ManualResizeRedaction manualResizeRedaction) { - mergeResizeRedactions(manualResizeRedaction, entityLogEntry, analysisNumber); + mergeResizeRedaction(manualResizeRedaction, entityLogEntry, analysisNumber); + return null; } else if (localChange instanceof ManualLegalBasisChange manualLegalBasisChange) { - mergeLegalBasisChanges(manualLegalBasisChange, entityLogEntry, analysisNumber); + mergeLegalBasisChange(manualLegalBasisChange, entityLogEntry, analysisNumber); + return null; } else if (localChange instanceof ManualRecategorization manualRecategorization) { - mergeRecategorizations(manualRecategorization, entityLogEntry, dossier, analysisNumber); + return mergeRecategorization(manualRecategorization, entityLogEntry, dossier, analysisNumber); } else if (localChange instanceof ManualForceRedaction manualForceRedaction) { - mergeForceRedactions(manualForceRedaction, entityLogEntry, analysisNumber); + mergeForceRedaction(manualForceRedaction, entityLogEntry, analysisNumber); + return null; + } else { + return null; } - }); + }) + .filter(Objects::nonNull) + .toList(); } @@ -268,7 +292,7 @@ public class EntityLogMergeService { } - private void mergeIdsToRemove(IdRemoval idRemoval, EntityLogEntry entityLogEntry, int analysisNumber) { + private void mergeIdToRemove(IdRemoval idRemoval, EntityLogEntry entityLogEntry, int analysisNumber) { entityLogEntry.setState(EntryState.IGNORED); entityLogEntry.getEngines().add(Engine.MANUAL); @@ -285,7 +309,7 @@ public class EntityLogMergeService { } - private void mergeResizeRedactions(ManualResizeRedaction manualResizeRedaction, EntityLogEntry entityLogEntry, int analysisNumber) { + private void mergeResizeRedaction(ManualResizeRedaction manualResizeRedaction, EntityLogEntry entityLogEntry, int analysisNumber) { entityLogEntry.setTextAfter(manualResizeRedaction.getTextAfter()); entityLogEntry.setTextBefore(manualResizeRedaction.getTextBefore()); @@ -306,7 +330,7 @@ public class EntityLogMergeService { @Deprecated(forRemoval = true) - private void mergeLegalBasisChanges(ManualLegalBasisChange manualLegalBasisChange, EntityLogEntry entityLogEntry, int analysisNumber) { + private void mergeLegalBasisChange(ManualLegalBasisChange manualLegalBasisChange, EntityLogEntry entityLogEntry, int analysisNumber) { entityLogEntry.setLegalBasis(manualLegalBasisChange.getLegalBasis()); entityLogEntry.setSection(manualLegalBasisChange.getSection()); @@ -343,16 +367,24 @@ public class EntityLogMergeService { } - private void mergeRecategorizations(ManualRecategorization recategorization, EntityLogEntry entityLogEntry, DossierEntity dossier, int analysisNumber) { + private EntityLogEntry mergeRecategorization(ManualRecategorization recategorization, EntityLogEntry entityLogEntry, DossierEntity dossier, int analysisNumber) { + + if (entityLogEntry.getEntryType().equals(EntryType.IMAGE) || entityLogEntry.getEntryType().equals(EntryType.IMAGE_HINT)) { + return pendingDictionaryEntryFactory.buildPendingImageRecategorizationEntry(recategorization, entityLogEntry, analysisNumber); + } boolean isHint = isHint(recategorization.getType(), dossier); if (!Strings.isNullOrEmpty(recategorization.getType())) { entityLogEntry.setType(recategorization.getType()); } + entityLogEntry.setEntryType(getEntryType(isHint, recategorization.getType())); - entityLogEntry.setState(isHint ? EntryState.SKIPPED : EntryState.APPLIED); + + entityLogEntry.setState(isHint ? EntryState.SKIPPED : EntryState.APPLIED); // TODO: only set applied if legalBasis is set by recategorization + entityLogEntry.getEngines().add(Engine.MANUAL); + if (!Strings.isNullOrEmpty(recategorization.getLegalBasis())) { entityLogEntry.setLegalBasis(recategorization.getLegalBasis()); } @@ -372,10 +404,15 @@ public class EntityLogMergeService { .userId(recategorization.getUser()) .propertyChanges(getPropertyChanges(recategorization)) .build()); + + if ((entityLogEntry.isDictionaryEntry() || entityLogEntry.isDossierDictionaryEntry()) && !recategorization.isAddToDictionary() && !recategorization.isAddToAllDossiers()) { + entityLogEntry.setState(EntryState.REMOVED); + } + return null; } - private Map getPropertyChanges(ManualRecategorization recategorization) { + public static Map getPropertyChanges(ManualRecategorization recategorization) { Map propertyChanges = new HashMap<>(); if (!Strings.isNullOrEmpty(recategorization.getType())) { @@ -394,7 +431,7 @@ public class EntityLogMergeService { } - private void mergeForceRedactions(ManualForceRedaction forceRedaction, EntityLogEntry entityLogEntry, int analysisNumber) { + private void mergeForceRedaction(ManualForceRedaction forceRedaction, EntityLogEntry entityLogEntry, int analysisNumber) { entityLogEntry.setLegalBasis(forceRedaction.getLegalBasis()); entityLogEntry.setState(entityLogEntry.getEntryType().equals(EntryType.HINT) ? EntryState.SKIPPED : EntryState.APPLIED); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/PendingDictionaryEntryFactory.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/PendingDictionaryEntryFactory.java index 105676a67..2991bce52 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/PendingDictionaryEntryFactory.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/PendingDictionaryEntryFactory.java @@ -9,6 +9,7 @@ import java.util.stream.Collectors; import org.springframework.stereotype.Service; +import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogMergeService; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine; 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; @@ -204,4 +205,45 @@ public class PendingDictionaryEntryFactory { .build(); } + + + public EntityLogEntry buildPendingImageRecategorizationEntry(ManualRecategorization manualChange, EntityLogEntry originalEntry) { + + var manualChanges = List.of(ManualChange.builder() + .manualRedactionType(ManualRedactionType.RECATEGORIZE) + .requestedDate(manualChange.getRequestDate()) + .processedDate(manualChange.getProcessedDate()) + .userId(manualChange.getUser()) + .propertyChanges(EntityLogMergeService.getPropertyChanges(manualChange)) + .build()); + + String reason = String.format("Image has been recategorized from %s to %s", originalEntry.getType(), manualChange.getType()); + + return EntityLogEntry.builder() + .id(originalEntry.getId()) + .value(originalEntry.getValue()) + .type(manualChange.getType()) + .entryType(originalEntry.getEntryType()) + .state(EntryState.PENDING) + .dictionaryEntry(manualChange.isAddToDictionary()) + .dossierDictionaryEntry(manualChange.isAddToAllDossiers()) + .reason(reason) + .legalBasis(reason) + .matchedRule("") + .containingNodeId(Collections.emptyList()) + .closestHeadline("") + .section("") + .positions(originalEntry.getPositions()) + .textAfter("") + .textBefore("") + .startOffset(-1) + .endOffset(-1) + .changes(Collections.emptyList()) + .manualChanges(manualChanges) + .engines(Set.of(Engine.MANUAL)) + .reference(Collections.emptySet()) + .importedRedactionIntersections(Collections.emptySet()) + .build(); + } + }