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 671d00485..bfd679a24 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; @@ -96,11 +97,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); } @@ -116,6 +121,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() @@ -160,25 +173,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(); } @@ -269,7 +293,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); @@ -286,7 +310,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()); @@ -312,7 +336,7 @@ public class EntityLogMergeService { } - 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()); @@ -351,25 +375,39 @@ 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); + } 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()); + entityLogEntry.setState(isHint ? EntryState.SKIPPED : EntryState.APPLIED); + } else { + entityLogEntry.setState(EntryState.SKIPPED); } + if (!Strings.isNullOrEmpty(recategorization.getSection())) { entityLogEntry.setSection(recategorization.getSection()); } + if (!Strings.isNullOrEmpty(recategorization.getValue())) { entityLogEntry.setValue(recategorization.getValue()); } + addChanges(entityLogEntry.getChanges(), ChangeType.CHANGED, analysisNumber, recategorization.getRequestDate()); entityLogEntry.getManualChanges() .add(ManualChange.builder() @@ -383,10 +421,11 @@ public class EntityLogMergeService { 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())) { @@ -405,7 +444,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/ManualRedactionDictionaryUpdateHandler.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionDictionaryUpdateHandler.java index 224b3b4e5..945fee559 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionDictionaryUpdateHandler.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionDictionaryUpdateHandler.java @@ -52,7 +52,7 @@ public class ManualRedactionDictionaryUpdateHandler { public Set handleAddToDictionaryAndReturnModifiedTypeIds(String fileId, String value, ManualRequestWithAddToDictionary manualRequestWithAddToDictionary) { - if (!manualRequestWithAddToDictionary.isAddToDictionary()) { + if (!manualRequestWithAddToDictionary.isAddToDictionary() || value == null) { return Collections.emptySet(); } Set typeIdsOfModifiedDictionaries = new HashSet<>(); @@ -99,7 +99,7 @@ public class ManualRedactionDictionaryUpdateHandler { public Set handleRemoveFromDictionaryAndReturnModifiedTypeIds(String fileId, ManualRequestWithRemoveFromDictionary manualRequestWithRemoveFromDictionary) { - if (!manualRequestWithRemoveFromDictionary.isRemoveFromDictionary()) { + if (!manualRequestWithRemoveFromDictionary.isRemoveFromDictionary() || manualRequestWithRemoveFromDictionary.getValue() == null) { return Collections.emptySet(); } String dossierId = manualRequestWithRemoveFromDictionary.getDossierId(); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionMapper.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionMapper.java index 587688c1f..447109394 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionMapper.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionMapper.java @@ -191,13 +191,12 @@ public class ManualRedactionMapper { .addToDictionary(recategorizationRequest.isAddToDictionary()) .addToAllDossiers(recategorizationRequest.isAddToAllDossiers()) .dictionaryEntryType(getDictionaryEntryType(entityLogEntry)) - .value(entityLogEntry.getValue()) + .value(recategorizationRequest.getValue()) .typeToRemove(entityLogEntry.getType()) .dossierTemplateTypeId(toTypeId(recategorizationRequest.getType(), dossierTemplateId)) .legalBasis(Optional.ofNullable(recategorizationRequest.getLegalBasis()) .orElse("")) .section(recategorizationRequest.getSection()) - .value(recategorizationRequest.getValue()) .build(); if (!entityLogEntry.getEngines().contains(Engine.MANUAL) && !recategorizationRequest.isAddToAllDossiers() && !recategorizationRequest.isAddToDictionary()) { diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionService.java index c6fd8a3e9..4673e5014 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/manualredactions/ManualRedactionService.java @@ -240,6 +240,7 @@ public class ManualRedactionService { Set typeIdsOfDictionariesWithAdd = manualRedactionDictionaryUpdateHandler.handleAddToDictionaryAndReturnModifiedTypeIds(fileId, recategorizationRequest.getValue(), recategorizationRequest); + Set typeIdsOfDictionariesWithDelete = manualRedactionDictionaryUpdateHandler.handleRemoveFromDictionaryAndReturnModifiedTypeIds(fileId, recategorizationRequest); @@ -255,6 +256,7 @@ public class ManualRedactionService { response.add(ManualAddResponse.builder().annotationId(recategorizationRequest.getAnnotationId()).commentId(commentId).build()); } + reprocess(dossierId, fileId); fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now()); 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(); + } + }