RED-8480: don't merge recategorizations for images, create pending entry instead #411

Merged
ali.oezyetimoglu1 merged 5 commits from RED-8480-2 into master 2024-04-02 11:26:49 +02:00
5 changed files with 104 additions and 22 deletions

View File

@ -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<EntityLogEntry> pendingImageRecategorizations = mergeLocalManualChangesAndReturnNonMergeableAsPending(dossier,
allManualChanges,
entityLogEntry,
analysisNumber);
List<EntityLogEntry> 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<EntityLogEntry> concatLists(List<EntityLogEntry> pendingDictionaryEntries, List<EntityLogEntry> pendingImageRecategorizations) {
return Stream.of(pendingDictionaryEntries, pendingImageRecategorizations)
.flatMap(Collection::stream)
.toList();
}
private Stream<EntityLogEntry> buildPendingDictionaryChanges(ManualRedactions unprocessedManualRedactions) {
return unprocessedManualRedactions.getEntriesToAdd()
@ -160,25 +173,36 @@ public class EntityLogMergeService {
}
private void mergeLocalManualChanges(DossierEntity dossier, Map<String, List<BaseAnnotation>> allManualChanges, EntityLogEntry entityLogEntry, int analysisNumber) {
private List<EntityLogEntry> mergeLocalManualChangesAndReturnNonMergeableAsPending(DossierEntity dossier,
Map<String, List<BaseAnnotation>> 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<String, String> getPropertyChanges(ManualRecategorization recategorization) {
public static Map<String, String> getPropertyChanges(ManualRecategorization recategorization) {
Map<String, String> 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);

View File

@ -52,7 +52,7 @@ public class ManualRedactionDictionaryUpdateHandler {
public Set<String> handleAddToDictionaryAndReturnModifiedTypeIds(String fileId, String value, ManualRequestWithAddToDictionary manualRequestWithAddToDictionary) {
if (!manualRequestWithAddToDictionary.isAddToDictionary()) {
if (!manualRequestWithAddToDictionary.isAddToDictionary() || value == null) {
return Collections.emptySet();
}
Set<String> typeIdsOfModifiedDictionaries = new HashSet<>();
@ -99,7 +99,7 @@ public class ManualRedactionDictionaryUpdateHandler {
public Set<String> handleRemoveFromDictionaryAndReturnModifiedTypeIds(String fileId, ManualRequestWithRemoveFromDictionary manualRequestWithRemoveFromDictionary) {
if (!manualRequestWithRemoveFromDictionary.isRemoveFromDictionary()) {
if (!manualRequestWithRemoveFromDictionary.isRemoveFromDictionary() || manualRequestWithRemoveFromDictionary.getValue() == null) {
return Collections.emptySet();
}
String dossierId = manualRequestWithRemoveFromDictionary.getDossierId();

View File

@ -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()) {

View File

@ -240,6 +240,7 @@ public class ManualRedactionService {
Set<String> typeIdsOfDictionariesWithAdd = manualRedactionDictionaryUpdateHandler.handleAddToDictionaryAndReturnModifiedTypeIds(fileId,
recategorizationRequest.getValue(),
recategorizationRequest);
Set<String> 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());

View File

@ -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();
}
}