RED-7384: fix redactionLog migration services
This commit is contained in:
parent
93a0838e73
commit
b73b3a74cf
@ -1,5 +1,8 @@
|
|||||||
package com.iqser.red.service.redaction.v1.model;
|
package com.iqser.red.service.redaction.v1.model;
|
||||||
|
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -11,7 +14,9 @@ import lombok.NoArgsConstructor;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class MigrationRequest {
|
public class MigrationRequest {
|
||||||
|
|
||||||
|
String dossierTemplateId;
|
||||||
String dossierId;
|
String dossierId;
|
||||||
String fileId;
|
String fileId;
|
||||||
|
ManualRedactions manualRedactions;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,440 @@
|
|||||||
|
package com.iqser.red.service.redaction.v1.server.migration;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;
|
||||||
|
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.redactionlog.Change;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.ChangeType;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Engine;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.ManualChange;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.ManualRedactionType;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Point;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Rectangle;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLogEntry;
|
||||||
|
import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LegacyRedactionLogMergeService {
|
||||||
|
|
||||||
|
private final DictionaryService dictionaryService;
|
||||||
|
|
||||||
|
|
||||||
|
public RedactionLog mergeManualChanges(RedactionLog redactionLog, ManualRedactions manualRedactions, String dossierTemplateId) {
|
||||||
|
|
||||||
|
var skippedImportedRedactions = new HashSet<>();
|
||||||
|
log.info("Merging Redaction log with manual redactions");
|
||||||
|
if (manualRedactions != null) {
|
||||||
|
|
||||||
|
var manualRedactionLogEntries = addManualAddEntries(manualRedactions.getEntriesToAdd(), dossierTemplateId, redactionLog.getAnalysisNumber());
|
||||||
|
|
||||||
|
redactionLog.getRedactionLogEntry().addAll(manualRedactionLogEntries);
|
||||||
|
|
||||||
|
var manualRedactionWrappers = createManualRedactionWrappers(manualRedactions);
|
||||||
|
|
||||||
|
for (RedactionLogEntry entry : redactionLog.getRedactionLogEntry()) {
|
||||||
|
|
||||||
|
processRedactionLogEntry(manualRedactionWrappers.stream().filter(mr -> entry.getId().equals(mr.getId())).collect(Collectors.toList()), entry, dossierTemplateId);
|
||||||
|
|
||||||
|
if (entry.isImported() && !entry.isRedacted()) {
|
||||||
|
skippedImportedRedactions.add(entry.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> processedIds = new HashSet<>();
|
||||||
|
redactionLog.getRedactionLogEntry().removeIf(entry -> {
|
||||||
|
|
||||||
|
if (entry.getImportedRedactionIntersections() != null) {
|
||||||
|
entry.getImportedRedactionIntersections().removeAll(skippedImportedRedactions);
|
||||||
|
if (!entry.getImportedRedactionIntersections().isEmpty() && (!entry.isImage() || entry.isImage() && !(entry.getType().equals("image") || entry.getType()
|
||||||
|
.equals("ocr")))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (processedIds.contains(entry.getId())) {
|
||||||
|
log.info("Duplicate annotation found with id {}", entry.getId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
processedIds.add(entry.getId());
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return redactionLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<ManualRedactionWrapper> createManualRedactionWrappers(ManualRedactions manualRedactions) {
|
||||||
|
|
||||||
|
List<ManualRedactionWrapper> manualRedactionWrappers = new ArrayList<>();
|
||||||
|
|
||||||
|
manualRedactions.getRecategorizations().forEach(item -> {
|
||||||
|
if (item.getSoftDeletedTime() == null) {
|
||||||
|
manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
manualRedactions.getIdsToRemove().forEach(item -> {
|
||||||
|
if (item.getSoftDeletedTime() == null) {
|
||||||
|
manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
manualRedactions.getForceRedactions().forEach(item -> {
|
||||||
|
if (item.getSoftDeletedTime() == null) {
|
||||||
|
manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
manualRedactions.getLegalBasisChanges().forEach(item -> {
|
||||||
|
if (item.getSoftDeletedTime() == null) {
|
||||||
|
manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
manualRedactions.getResizeRedactions().forEach(item -> {
|
||||||
|
if (item.getSoftDeletedTime() == null) {
|
||||||
|
manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Collections.sort(manualRedactionWrappers);
|
||||||
|
|
||||||
|
return manualRedactionWrappers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void processRedactionLogEntry(List<ManualRedactionWrapper> manualRedactionWrappers, RedactionLogEntry redactionLogEntry, String dossierTemplateId) {
|
||||||
|
|
||||||
|
manualRedactionWrappers.forEach(mrw -> {
|
||||||
|
|
||||||
|
Object item = mrw.getItem();
|
||||||
|
if (item instanceof ManualRecategorization imageRecategorization) {
|
||||||
|
processManualImageRecategorization(redactionLogEntry, dossierTemplateId, imageRecategorization);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item instanceof IdRemoval manualRemoval) {
|
||||||
|
processIdRemoval(redactionLogEntry, dossierTemplateId, manualRemoval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item instanceof ManualForceRedaction manualForceRedact) {
|
||||||
|
processManualForceRedaction(redactionLogEntry, dossierTemplateId, manualForceRedact);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item instanceof ManualLegalBasisChange manualLegalBasisChange) {
|
||||||
|
processManualLegalBasisChange(redactionLogEntry, dossierTemplateId, manualLegalBasisChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item instanceof ManualResizeRedaction manualResizeRedact) {
|
||||||
|
processManualResizeRedaction(redactionLogEntry, dossierTemplateId, manualResizeRedact);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void processManualImageRecategorization(RedactionLogEntry redactionLogEntry, String dossierTemplateId, ManualRecategorization imageRecategorization) {
|
||||||
|
|
||||||
|
String manualOverrideReason = null;
|
||||||
|
if (imageRecategorization.getStatus().equals(AnnotationStatus.APPROVED)) {
|
||||||
|
|
||||||
|
redactionLogEntry.setType(imageRecategorization.getType());
|
||||||
|
redactionLogEntry.setSection("Image:" + redactionLogEntry.getType());
|
||||||
|
|
||||||
|
if (dictionaryService.isHint(imageRecategorization.getType(), dossierTemplateId)) {
|
||||||
|
redactionLogEntry.setRedacted(false);
|
||||||
|
redactionLogEntry.setHint(true);
|
||||||
|
} else {
|
||||||
|
redactionLogEntry.setHint(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", recategorized by manual override");
|
||||||
|
} else if (imageRecategorization.getStatus().equals(AnnotationStatus.REQUESTED)) {
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to recategorize");
|
||||||
|
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, false, redactionLogEntry.isRedacted()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manualOverrideReason != null) {
|
||||||
|
redactionLogEntry.setReason(manualOverrideReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
redactionLogEntry.getManualChanges()
|
||||||
|
.add(ManualChange.from(imageRecategorization).withManualRedactionType(ManualRedactionType.RECATEGORIZE).withChange("type", imageRecategorization.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String mergeReasonIfNecessary(String currentReason, String addition) {
|
||||||
|
|
||||||
|
if (currentReason != null) {
|
||||||
|
if (!currentReason.contains(addition)) {
|
||||||
|
return currentReason + addition;
|
||||||
|
}
|
||||||
|
return currentReason;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void processIdRemoval(RedactionLogEntry redactionLogEntry,String dossierTemplateId, IdRemoval manualRemoval) {
|
||||||
|
|
||||||
|
boolean isApprovedRedaction = manualRemoval.getStatus().equals(AnnotationStatus.APPROVED);
|
||||||
|
if (isApprovedRedaction && manualRemoval.isRemoveFromDictionary() && isBasedOnDictionaryOnly(redactionLogEntry)) {
|
||||||
|
log.debug("Skipping merge for dictionary-modifying entry");
|
||||||
|
} else {
|
||||||
|
String redactionLogEntryType = redactionLogEntry.getType();
|
||||||
|
String manualOverrideReason = null;
|
||||||
|
if (isApprovedRedaction) {
|
||||||
|
redactionLogEntry.setRedacted(false);
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", removed by manual override");
|
||||||
|
redactionLogEntry.setColor(getColor(redactionLogEntryType, dossierTemplateId, false, redactionLogEntry.isRedacted()));
|
||||||
|
redactionLogEntry.setHint(false);
|
||||||
|
} else if (manualRemoval.getStatus().equals(AnnotationStatus.REQUESTED)) {
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to remove");
|
||||||
|
redactionLogEntry.setColor(getColor(redactionLogEntryType, dossierTemplateId, true, redactionLogEntry.isRedacted()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manualOverrideReason != null) {
|
||||||
|
redactionLogEntry.setReason(manualOverrideReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redactionLogEntry.getManualChanges()
|
||||||
|
.add(ManualChange.from(manualRemoval)
|
||||||
|
.withManualRedactionType(manualRemoval.isRemoveFromDictionary() ? ManualRedactionType.REMOVE_FROM_DICTIONARY : ManualRedactionType.REMOVE_LOCALLY));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isBasedOnDictionaryOnly(RedactionLogEntry redactionLogEntry) {
|
||||||
|
|
||||||
|
return redactionLogEntry.getEngines().contains(Engine.DICTIONARY) && redactionLogEntry.getEngines().size() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void processManualForceRedaction(RedactionLogEntry redactionLogEntry, String dossierTemplateId, ManualForceRedaction manualForceRedact) {
|
||||||
|
|
||||||
|
String manualOverrideReason = null;
|
||||||
|
var dictionaryIsHint = dictionaryService.isHint(redactionLogEntry.getType(), dossierTemplateId);
|
||||||
|
if (manualForceRedact.getStatus().equals(AnnotationStatus.APPROVED)) {
|
||||||
|
// Forcing a skipped hint should result in a hint
|
||||||
|
if (dictionaryIsHint) {
|
||||||
|
redactionLogEntry.setHint(true);
|
||||||
|
} else {
|
||||||
|
redactionLogEntry.setRedacted(true);
|
||||||
|
}
|
||||||
|
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, false, redactionLogEntry.isRedacted()));
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", forced by manual override");
|
||||||
|
redactionLogEntry.setLegalBasis(manualForceRedact.getLegalBasis());
|
||||||
|
} else if (manualForceRedact.getStatus().equals(AnnotationStatus.REQUESTED)) {
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to force " + (dictionaryIsHint ? "hint" : "redact"));
|
||||||
|
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, true, redactionLogEntry.isRedacted()));
|
||||||
|
redactionLogEntry.setLegalBasis(manualForceRedact.getLegalBasis());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manualOverrideReason != null) {
|
||||||
|
redactionLogEntry.setReason(manualOverrideReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
var manualChange = ManualChange.from(manualForceRedact).withManualRedactionType(dictionaryIsHint ? ManualRedactionType.FORCE_HINT : ManualRedactionType.FORCE_REDACT);
|
||||||
|
|
||||||
|
redactionLogEntry.getManualChanges().add(manualChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void processManualLegalBasisChange(RedactionLogEntry redactionLogEntry, String dossierTemplateId, ManualLegalBasisChange manualLegalBasisChange) {
|
||||||
|
|
||||||
|
String manualOverrideReason = null;
|
||||||
|
if (manualLegalBasisChange.getStatus().equals(AnnotationStatus.APPROVED)) {
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", legal basis was manually changed");
|
||||||
|
redactionLogEntry.setLegalBasis(manualLegalBasisChange.getLegalBasis());
|
||||||
|
redactionLogEntry.setRedacted(true);
|
||||||
|
if (manualLegalBasisChange.getSection() != null) {
|
||||||
|
redactionLogEntry.setSection(manualLegalBasisChange.getSection());
|
||||||
|
}
|
||||||
|
if (redactionLogEntry.isRectangle() && manualLegalBasisChange.getValue() != null) {
|
||||||
|
redactionLogEntry.setValue(manualLegalBasisChange.getValue());
|
||||||
|
}
|
||||||
|
} else if (manualLegalBasisChange.getStatus().equals(AnnotationStatus.REQUESTED)) {
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", legal basis change requested");
|
||||||
|
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, true, redactionLogEntry.isRedacted()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manualOverrideReason != null) {
|
||||||
|
redactionLogEntry.setReason(manualOverrideReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
var manualChange = ManualChange.from(manualLegalBasisChange).withManualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE);
|
||||||
|
manualChange.withChange("legalBasis", manualLegalBasisChange.getLegalBasis());
|
||||||
|
if (manualLegalBasisChange.getSection() != null) {
|
||||||
|
manualChange.withChange("section", manualLegalBasisChange.getSection());
|
||||||
|
}
|
||||||
|
if (redactionLogEntry.isRectangle() && manualLegalBasisChange.getValue() != null) {
|
||||||
|
manualChange.withChange("value", manualLegalBasisChange.getValue());
|
||||||
|
}
|
||||||
|
redactionLogEntry.getManualChanges().add(manualChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void processManualResizeRedaction(RedactionLogEntry redactionLogEntry, String dossierTemplateId, ManualResizeRedaction manualResizeRedact) {
|
||||||
|
|
||||||
|
String manualOverrideReason = null;
|
||||||
|
if (manualResizeRedact.getStatus().equals(AnnotationStatus.APPROVED)) {
|
||||||
|
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, false, redactionLogEntry.isRedacted()));
|
||||||
|
redactionLogEntry.setPositions(convertPositions(manualResizeRedact.getPositions()));
|
||||||
|
if (!"signature".equalsIgnoreCase(redactionLogEntry.getType()) && !"logo".equalsIgnoreCase(redactionLogEntry.getType())) {
|
||||||
|
redactionLogEntry.setValue(manualResizeRedact.getValue());
|
||||||
|
}
|
||||||
|
// This is for backwards compatibility, now the text after/before is calculated during reanalysis because we need to find dict entries on positions where entries are resized to smaller.
|
||||||
|
if (manualResizeRedact.getTextBefore() != null || manualResizeRedact.getTextAfter() != null) {
|
||||||
|
redactionLogEntry.setTextBefore(manualResizeRedact.getTextBefore());
|
||||||
|
redactionLogEntry.setTextAfter(manualResizeRedact.getTextAfter());
|
||||||
|
}
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", resized by manual override");
|
||||||
|
} else if (manualResizeRedact.getStatus().equals(AnnotationStatus.REQUESTED)) {
|
||||||
|
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to resize redact");
|
||||||
|
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, true, redactionLogEntry.isRedacted()));
|
||||||
|
redactionLogEntry.setPositions(convertPositions(manualResizeRedact.getPositions()));
|
||||||
|
|
||||||
|
// This is for backwards compatibility, now the text after/before is calculated during reanalysis because we need to find dict entries on positions where entries are resized to smaller.
|
||||||
|
if (manualResizeRedact.getTextBefore() != null || manualResizeRedact.getTextAfter() != null) {
|
||||||
|
redactionLogEntry.setTextBefore(manualResizeRedact.getTextBefore());
|
||||||
|
redactionLogEntry.setTextAfter(manualResizeRedact.getTextAfter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redactionLogEntry.setReason(manualOverrideReason);
|
||||||
|
redactionLogEntry.getManualChanges()
|
||||||
|
.add(ManualChange.from(manualResizeRedact).withManualRedactionType(ManualRedactionType.RESIZE).withChange("value", manualResizeRedact.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<RedactionLogEntry> addManualAddEntries(Set<ManualRedactionEntry> manualAdds, String dossierTemplateId, int analysisNumber) {
|
||||||
|
|
||||||
|
List<RedactionLogEntry> redactionLogEntries = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ManualRedactionEntry manualRedactionEntry : manualAdds) {
|
||||||
|
|
||||||
|
if (shouldCreateManualEntry(manualRedactionEntry)) {
|
||||||
|
RedactionLogEntry redactionLogEntry = createRedactionLogEntry(manualRedactionEntry, manualRedactionEntry.getAnnotationId(), dossierTemplateId, analysisNumber);
|
||||||
|
redactionLogEntry.setPositions(convertPositions(manualRedactionEntry.getPositions()));
|
||||||
|
redactionLogEntry.setTextBefore(manualRedactionEntry.getTextBefore());
|
||||||
|
redactionLogEntry.setTextAfter(manualRedactionEntry.getTextAfter());
|
||||||
|
|
||||||
|
redactionLogEntries.add(redactionLogEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redactionLogEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<Rectangle> convertPositions(List<com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle> positions) {
|
||||||
|
|
||||||
|
return positions.stream()
|
||||||
|
.map(pos -> new Rectangle(new Point(pos.getTopLeftX(), pos.getTopLeftY()), pos.getWidth(), pos.getHeight(), pos.getPage()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.UselessParentheses")
|
||||||
|
private boolean shouldCreateManualEntry(ManualRedactionEntry manualRedactionEntry) {
|
||||||
|
|
||||||
|
return (!manualRedactionEntry.isAddToDictionary() && !manualRedactionEntry.isAddToDossierDictionary()) || ((manualRedactionEntry.isAddToDictionary() || manualRedactionEntry.isAddToDossierDictionary()) && manualRedactionEntry.getProcessedDate() == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private RedactionLogEntry createRedactionLogEntry(ManualRedactionEntry manualRedactionEntry, String id, String dossierTemplateId, int analysisNumber) {
|
||||||
|
|
||||||
|
var addToDictionary = manualRedactionEntry.isAddToDictionary() || manualRedactionEntry.isAddToDossierDictionary();
|
||||||
|
|
||||||
|
var change = ManualChange.from(manualRedactionEntry).withManualRedactionType(addToDictionary ? ManualRedactionType.ADD_TO_DICTIONARY : ManualRedactionType.ADD_LOCALLY);
|
||||||
|
List<ManualChange> changeList = new ArrayList<>();
|
||||||
|
changeList.add(change);
|
||||||
|
|
||||||
|
return RedactionLogEntry.builder()
|
||||||
|
.id(id)
|
||||||
|
.color(getColorForManualAdd(manualRedactionEntry.getType(), dossierTemplateId, manualRedactionEntry.getStatus()))
|
||||||
|
.reason(manualRedactionEntry.getReason())
|
||||||
|
.isDictionaryEntry(manualRedactionEntry.isAddToDictionary())
|
||||||
|
.isDossierDictionaryEntry(manualRedactionEntry.isAddToDossierDictionary())
|
||||||
|
.legalBasis(manualRedactionEntry.getLegalBasis())
|
||||||
|
.value(manualRedactionEntry.getValue())
|
||||||
|
.sourceId(manualRedactionEntry.getSourceId())
|
||||||
|
.section(manualRedactionEntry.getSection())
|
||||||
|
.type(manualRedactionEntry.getType())
|
||||||
|
.redacted(true)
|
||||||
|
.isHint(false)
|
||||||
|
.sectionNumber(-1)
|
||||||
|
.rectangle(manualRedactionEntry.isRectangle())
|
||||||
|
.manualChanges(changeList)
|
||||||
|
.changes(List.of(new Change(analysisNumber + 1, ChangeType.ADDED, manualRedactionEntry.getRequestDate())))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private float[] getColor(String type, String dossierTemplateId, boolean isRedaction, boolean skipped) {
|
||||||
|
|
||||||
|
if (skipped || !isRedaction && !isHint(dossierTemplateId, type)) {
|
||||||
|
return dictionaryService.getNotRedactedColor(dossierTemplateId);
|
||||||
|
}
|
||||||
|
return dictionaryService.getColor(type, dossierTemplateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private float[] getColorForManualAdd(String type, String dossierTemplateId, AnnotationStatus status) {
|
||||||
|
|
||||||
|
if (status.equals(AnnotationStatus.REQUESTED) || status.equals(AnnotationStatus.DECLINED)) {
|
||||||
|
return dictionaryService.getNotRedactedColor(dossierTemplateId);
|
||||||
|
}
|
||||||
|
return dictionaryService.getColor(type, dossierTemplateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean isHint(String dossierTemplateId, String type) {
|
||||||
|
|
||||||
|
return dictionaryService.isHint(type, dossierTemplateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
private static class ManualRedactionWrapper implements Comparable<ManualRedactionWrapper> {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private OffsetDateTime date;
|
||||||
|
private Object item;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ManualRedactionWrapper o) {
|
||||||
|
|
||||||
|
return this.date.compareTo(o.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
package com.iqser.red.service.redaction.v1.server.migration;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
|
||||||
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLogEntry;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class LegacyVersion0MigrationService {
|
||||||
|
|
||||||
|
public RedactionLog mergeDuplicateAnnotationIds(RedactionLog redactionLog) {
|
||||||
|
|
||||||
|
List<RedactionLogEntry> mergedEntries = new LinkedList<>();
|
||||||
|
Map<String, List<RedactionLogEntry>> entriesById = redactionLog.getRedactionLogEntry().stream().collect(Collectors.groupingBy(RedactionLogEntry::getId));
|
||||||
|
for (List<RedactionLogEntry> entries : entriesById.values()) {
|
||||||
|
|
||||||
|
if (entries.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries.size() == 1) {
|
||||||
|
mergedEntries.add(entries.get(0));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RedactionLogEntry> sortedEntries = entries.stream().sorted(Comparator.comparing(entry -> entry.getChanges().get(0).getDateTime())).toList();
|
||||||
|
|
||||||
|
RedactionLogEntry initialEntry = sortedEntries.get(0);
|
||||||
|
for (RedactionLogEntry entry : sortedEntries.subList(1, sortedEntries.size())) {
|
||||||
|
copyNonNullFields(entry, initialEntry);
|
||||||
|
}
|
||||||
|
mergedEntries.add(initialEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
redactionLog.setRedactionLogEntry(mergedEntries);
|
||||||
|
return redactionLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void copyNonNullFields(RedactionLogEntry source, RedactionLogEntry destination) {
|
||||||
|
|
||||||
|
if (source == null || destination == null) {
|
||||||
|
throw new IllegalArgumentException("Source and destination objects must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> sourceClass = source.getClass();
|
||||||
|
|
||||||
|
Field[] sourceFields = sourceClass.getDeclaredFields();
|
||||||
|
|
||||||
|
for (Field field : sourceFields) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object value = field.get(source);
|
||||||
|
if (value != null) {
|
||||||
|
field.set(destination, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.queue;
|
package com.iqser.red.service.redaction.v1.server.migration;
|
||||||
|
|
||||||
import static com.iqser.red.service.redaction.v1.model.QueueNames.MIGRATION_QUEUE;
|
import static com.iqser.red.service.redaction.v1.model.QueueNames.MIGRATION_QUEUE;
|
||||||
import static com.iqser.red.service.redaction.v1.model.QueueNames.MIGRATION_RESPONSE_QUEUE;
|
import static com.iqser.red.service.redaction.v1.model.QueueNames.MIGRATION_RESPONSE_QUEUE;
|
||||||
@ -16,7 +16,6 @@ import com.iqser.red.service.redaction.v1.model.MigrationRequest;
|
|||||||
import com.iqser.red.service.redaction.v1.model.MigrationResponse;
|
import com.iqser.red.service.redaction.v1.model.MigrationResponse;
|
||||||
import com.iqser.red.service.redaction.v1.server.model.MigratedEntityLog;
|
import com.iqser.red.service.redaction.v1.server.model.MigratedEntityLog;
|
||||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
||||||
import com.iqser.red.service.redaction.v1.server.service.RedactionLogToEntityLogMigrationService;
|
|
||||||
import com.iqser.red.service.redaction.v1.server.service.document.DocumentGraphMapper;
|
import com.iqser.red.service.redaction.v1.server.service.document.DocumentGraphMapper;
|
||||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||||
|
|
||||||
@ -35,6 +34,8 @@ public class MigrationMessageReceiver {
|
|||||||
ObjectMapper objectMapper;
|
ObjectMapper objectMapper;
|
||||||
RedactionLogToEntityLogMigrationService redactionLogToEntityLogMigrationService;
|
RedactionLogToEntityLogMigrationService redactionLogToEntityLogMigrationService;
|
||||||
RedactionStorageService redactionStorageService;
|
RedactionStorageService redactionStorageService;
|
||||||
|
LegacyRedactionLogMergeService legacyRedactionLogMergeService;
|
||||||
|
LegacyVersion0MigrationService legacyVersion0MigrationService;
|
||||||
RabbitTemplate rabbitTemplate;
|
RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
|
|
||||||
@ -45,8 +46,16 @@ public class MigrationMessageReceiver {
|
|||||||
|
|
||||||
MigrationRequest migrationRequest = objectMapper.readValue(message.getBody(), MigrationRequest.class);
|
MigrationRequest migrationRequest = objectMapper.readValue(message.getBody(), MigrationRequest.class);
|
||||||
|
|
||||||
RedactionLog redactionLog = redactionStorageService.getRedactionLog(migrationRequest.getDossierId(), migrationRequest.getFileId());
|
// TODO: if an image is not found, try to copy the old one exactly (like with TextEntities)
|
||||||
|
|
||||||
Document document = DocumentGraphMapper.toDocumentGraph(redactionStorageService.getDocumentData(migrationRequest.getDossierId(), migrationRequest.getFileId()));
|
Document document = DocumentGraphMapper.toDocumentGraph(redactionStorageService.getDocumentData(migrationRequest.getDossierId(), migrationRequest.getFileId()));
|
||||||
|
RedactionLog redactionLog = redactionStorageService.getRedactionLog(migrationRequest.getDossierId(), migrationRequest.getFileId());
|
||||||
|
|
||||||
|
if (redactionLog.getAnalysisVersion() == 0) {
|
||||||
|
redactionLog = legacyVersion0MigrationService.mergeDuplicateAnnotationIds(redactionLog);
|
||||||
|
} else {
|
||||||
|
redactionLog = legacyRedactionLogMergeService.mergeManualChanges(redactionLog, migrationRequest.getManualRedactions(), migrationRequest.getDossierTemplateId());
|
||||||
|
}
|
||||||
|
|
||||||
MigratedEntityLog migratedEntityLog = redactionLogToEntityLogMigrationService.migrate(redactionLog, document);
|
MigratedEntityLog migratedEntityLog = redactionLogToEntityLogMigrationService.migrate(redactionLog, document);
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.iqser.red.service.redaction.v1.server.service;
|
package com.iqser.red.service.redaction.v1.server.migration;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -39,7 +39,7 @@ import com.iqser.red.service.redaction.v1.server.document.graph.BuildDocumentInt
|
|||||||
import com.iqser.red.service.redaction.v1.server.model.MigratedEntityLog;
|
import com.iqser.red.service.redaction.v1.server.model.MigratedEntityLog;
|
||||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
||||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils;
|
import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils;
|
||||||
import com.iqser.red.service.redaction.v1.server.service.RedactionLogToEntityLogMigrationService;
|
import com.iqser.red.service.redaction.v1.server.migration.RedactionLogToEntityLogMigrationService;
|
||||||
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user