From b4356408b6214227e1d084ab8daff86d00936d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kilian=20Sch=C3=BCttler?= Date: Tue, 24 Oct 2023 15:12:42 +0200 Subject: [PATCH] RED-7759: Cannot resize local manual redactions --- .../v1/server/model/RectangleWithPage.java | 4 +- .../entity/ManualChangeOverwrite.java | 25 ++++++- .../v1/server/service/AnalyzeService.java | 6 +- .../service/EntityLogCreatorService.java | 25 +++++-- .../service/RedactionLogCreatorService.java | 74 +++++++++---------- .../document/ManualEntityCreationService.java | 36 ++++----- .../drools/EntityDroolsExecutionService.java | 6 +- .../v1/server/RedactionIntegrationTest.java | 52 +++++++------ .../v1/server/annotate/AnnotationService.java | 11 ++- .../document/graph/MigrationPocTest.java | 10 +-- .../ManualChangesEnd2EndTest.java | 48 +++++++++++- .../test/resources/drools/documine_flora.drl | 13 +--- 12 files changed, 186 insertions(+), 124 deletions(-) diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/RectangleWithPage.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/RectangleWithPage.java index 58879ce8..a04f9658 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/RectangleWithPage.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/RectangleWithPage.java @@ -20,13 +20,13 @@ public record RectangleWithPage(int pageNumber, Rectangle2D rectangle2D) { private static Rectangle2D toRectangle2D(Rectangle rectangle) { - return new Rectangle2D.Float(rectangle.getTopLeft().getX(), rectangle.getTopLeft().getY() + rectangle.getHeight(), rectangle.getWidth(), -rectangle.getHeight()); + return new Rectangle2D.Float(rectangle.getTopLeft().getX(), rectangle.getTopLeft().getY(), rectangle.getWidth(), rectangle.getHeight()); } private static Rectangle2D toRectangle2D(com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle rectangle) { - return new Rectangle2D.Float(rectangle.getTopLeft().getX(), rectangle.getTopLeft().getY() + rectangle.getHeight(), rectangle.getWidth(), -rectangle.getHeight()); + return new Rectangle2D.Float(rectangle.getTopLeft().getX(), rectangle.getTopLeft().getY(), rectangle.getWidth(), rectangle.getHeight()); } } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/document/entity/ManualChangeOverwrite.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/document/entity/ManualChangeOverwrite.java index a6b0ee3e..0582af73 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/document/entity/ManualChangeOverwrite.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/document/entity/ManualChangeOverwrite.java @@ -1,5 +1,6 @@ package com.iqser.red.service.redaction.v1.server.model.document.entity; +import java.awt.geom.Rectangle2D; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; @@ -10,10 +11,11 @@ import java.util.Optional; 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.ManualRecategorization; 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.redaction.v1.server.model.RectangleWithPage; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -42,6 +44,7 @@ public class ManualChangeOverwrite { String legalBasis; String section; String value; + List positions; Boolean applied; Boolean removed; Boolean ignored; @@ -96,9 +99,12 @@ public class ManualChangeOverwrite { value = manualLegalBasisChange.getValue(); } - if (manualChange instanceof ManualResizeRedaction) { - // resizing logic happens in ManualChangesApplicationService. + if (manualChange instanceof ManualResizeRedaction manualResizeRedaction) { + // resizing logic for TextEntities and Images happens in ManualChangesApplicationService. resized = true; + // This is only for not found Manual Entities. + value = manualResizeRedaction.getValue(); + positions = manualResizeRedaction.getPositions().stream().map(RectangleWithPage::fromAnnotationRectangle).toList(); } if (manualChange instanceof ManualRecategorization recategorization) { @@ -197,4 +203,17 @@ public class ManualChangeOverwrite { return descriptions == null ? Collections.emptyList() : descriptions; } + + public Optional> getPositions() { + + calculateCurrentOverride(); + return positions == null ? Optional.empty() : Optional.of(positions); + } + + + public static Rectangle2D toRectangle2D(com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle rect) { + + return new Rectangle2D.Double(rect.getTopLeftX() - rect.getWidth(), rect.getTopLeftY() - rect.getHeight(), rect.getWidth(), rect.getHeight()); + } + } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/AnalyzeService.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/AnalyzeService.java index 48fa20cc..7ac037c7 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/AnalyzeService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/AnalyzeService.java @@ -112,8 +112,7 @@ public class AnalyzeService { startTime, kieContainerCreationService.getLatestKieContainer(analyzeRequest.getDossierTemplateId(), RuleFileType.COMPONENT), entityLogCreatorService.updateVersionsAndReturnChanges(previousEntityLog, - dictionaryIncrement.getDictionaryVersion(), - analyzeRequest.getDossierTemplateId(), false), document, + dictionaryIncrement.getDictionaryVersion(), analyzeRequest.getDossierTemplateId(), false), document, previousRedactionLog, document.getNumberOfPages(), dictionaryIncrement.getDictionaryVersion(), @@ -154,8 +153,7 @@ public class AnalyzeService { dictionary.getVersion()); return finalizeAnalysis(analyzeRequest, - startTime, - kieContainerCreationService.getLatestKieContainer(analyzeRequest.getDossierTemplateId(), RuleFileType.COMPONENT), entityLogChanges, document, + startTime, kieContainerCreationService.getLatestKieContainer(analyzeRequest.getDossierTemplateId(), RuleFileType.COMPONENT), entityLogChanges, document, redactionLog, document.getNumberOfPages(), dictionaryIncrement.getDictionaryVersion(), diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/EntityLogCreatorService.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/EntityLogCreatorService.java index 8a2bbfcf..6f971f7a 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/EntityLogCreatorService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/EntityLogCreatorService.java @@ -171,14 +171,14 @@ public class EntityLogCreatorService { } processedIds.add(positionOnPage.getId()); - EntityLogEntry redactionLogEntry = createEntityLogEntry(textEntity, dossierTemplateId); - redactionLogEntry.setId(positionOnPage.getId()); + EntityLogEntry entityLogEntries = createEntityLogEntry(textEntity, dossierTemplateId); + entityLogEntries.setId(positionOnPage.getId()); List rectanglesPerLine = positionOnPage.getRectanglePerLine().stream().map(rectangle2D -> new Position(rectangle2D, positionOnPage.getPage().getNumber())) .toList(); - redactionLogEntry.setPositions(rectanglesPerLine); - redactionLogEntities.add(redactionLogEntry); + entityLogEntries.setPositions(rectanglesPerLine); + redactionLogEntities.add(entityLogEntries); } return redactionLogEntities; @@ -216,9 +216,15 @@ public class EntityLogCreatorService { String type = manualEntity.getManualOverwrite().getType().orElse(manualEntity.getType()); boolean isHint = isHint(manualEntity.getEntityType()); - return EntityLogEntry.builder().id(manualEntity.getId()).color(getColor(type, dossierTemplateId, manualEntity.applied(), isHint)) + return EntityLogEntry.builder() + .id(manualEntity.getId()) + .color(getColor(type, dossierTemplateId, manualEntity.applied(), isHint)) .reason(manualEntity.buildReasonWithManualChangeDescriptions()) - .legalBasis(manualEntity.legalBasis()).value(manualEntity.value()).type(type).state(buildEntryState(manualEntity)).entryType(buildEntryType(manualEntity)) + .legalBasis(manualEntity.legalBasis()) + .value(manualEntity.value()) + .type(type) + .state(buildEntryState(manualEntity)) + .entryType(buildEntryType(manualEntity)) .section(manualEntity.getManualOverwrite().getSection().orElse(manualEntity.getSection())) .containingNodeId(Collections.emptyList()) .closestHeadline("") @@ -229,7 +235,12 @@ public class EntityLogCreatorService { .textBefore("") .startOffset(-1) .endOffset(-1) - .positions(manualEntity.getEntityPosition().stream().map(entityPosition -> new Position(entityPosition.rectangle2D(), entityPosition.pageNumber())).toList()) + .positions(manualEntity.getManualOverwrite() + .getPositions() + .orElse(manualEntity.getEntityPosition()) + .stream() + .map(entityPosition -> new Position(entityPosition.rectangle2D(), entityPosition.pageNumber())) + .toList()) .engines(Collections.emptySet()) .reference(Collections.emptySet()) .manualChanges(manualChangeFactory.toManualChangeList(manualEntity.getManualOverwrite().getManualChangeLog(), isHint)) diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/RedactionLogCreatorService.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/RedactionLogCreatorService.java index 4087c91f..09d8ea52 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/RedactionLogCreatorService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/RedactionLogCreatorService.java @@ -53,8 +53,7 @@ public class RedactionLogCreatorService { List entries = new ArrayList<>(); Set processIds = new HashSet<>(); - document.getEntities() - .stream().filter(RedactionLogCreatorService::notFalsePositiveOrFalseRecommendation) + document.getEntities().stream().filter(RedactionLogCreatorService::notFalsePositiveOrFalseRecommendation) .filter(IEntity::active) .forEach(entityNode -> entries.addAll(toRedactionLogEntries(entityNode, dossierTemplateId, comments, processIds))); document.streamAllImages().filter(image -> !image.removed()).forEach(imageNode -> entries.add(createRedactionLogEntry(imageNode, dossierTemplateId, comments))); @@ -221,6 +220,38 @@ public class RedactionLogCreatorService { } + public RedactionLogEntry createRedactionLogEntry(Image image, String dossierTemplateId, Map> comments) { + + String imageType = image.getImageType().equals(ImageType.OTHER) ? "image" : image.getImageType().toString().toLowerCase(Locale.ENGLISH); + boolean isHint = dictionaryService.isHint(imageType, dossierTemplateId); + return RedactionLogEntry.builder().id(image.getId()).color(getColor(imageType, dossierTemplateId, image.applied(), isHint)) + .isImage(true) + .value(image.value()) + .type(imageType) + .redacted(image.applied()) + .reason(image.buildReasonWithManualChangeDescriptions()) + .legalBasis(image.legalBasis()) + .matchedRule(image.getMatchedRule().getRuleIdentifier().toString()) + .isHint(isHint) + .isDictionaryEntry(false) + .isRecommendation(false) + .positions(List.of(toRedactionLogRectangle(image.getPosition(), image.getPage().getNumber()))) + .sectionNumber(image.getTreeId().get(0)) + .section(image.getManualOverwrite().getSection().orElse(image.getParent().toString())) + .imageHasTransparency(image.isTransparent()) + .manualChanges(mapManualChanges(image.getManualOverwrite(), isHint)) + .comments(buildRedactionLogComments(comments, image.getId())) + .build(); + + } + + + private Rectangle toRedactionLogRectangle(Rectangle2D rectangle2D, int pageNumber) { + + return new Rectangle(new Point((float) rectangle2D.getMinX(), (float) rectangle2D.getMinY()), (float) rectangle2D.getWidth(), (float) rectangle2D.getHeight(), pageNumber); + } + + public RedactionLogEntry createRedactionLogEntry(ManualEntity manualEntity, String dossierTemplateId, Map> comments) { String type = manualEntity.getManualOverwrite().getType().orElse(manualEntity.getType()); @@ -245,11 +276,9 @@ public class RedactionLogCreatorService { .textAfter("") .textBefore("") .startOffset(-1) - .endOffset(-1) - .positions(manualEntity.getEntityPosition() + .endOffset(-1).positions(manualEntity.getManualOverwrite().getPositions().orElse(manualEntity.getEntityPosition()) .stream() - .map(entityPosition -> toRedactionLogRectangle(entityPosition.rectangle2D(), entityPosition.pageNumber())) - .collect(Collectors.toList())) + .map(entityPosition -> toRedactionLogRectangle(entityPosition.rectangle2D(), entityPosition.pageNumber())).toList()) .engines(Collections.emptySet()) .reference(Collections.emptySet()) .manualChanges(mapManualChanges(manualEntity.getManualOverwrite(), isHint)) @@ -257,37 +286,4 @@ public class RedactionLogCreatorService { .build(); } - - private Rectangle toRedactionLogRectangle(Rectangle2D rectangle2D, int pageNumber) { - - return new Rectangle(new Point((float) rectangle2D.getMinX(), (float) rectangle2D.getMinY()), (float) rectangle2D.getWidth(), (float) rectangle2D.getHeight(), pageNumber); - } - - - public RedactionLogEntry createRedactionLogEntry(Image image, String dossierTemplateId, Map> comments) { - - String imageType = image.getImageType().equals(ImageType.OTHER) ? "image" : image.getImageType().toString().toLowerCase(Locale.ENGLISH); - boolean isHint = dictionaryService.isHint(imageType, dossierTemplateId); - return RedactionLogEntry.builder() - .id(image.getId()).color(getColor(imageType, dossierTemplateId, image.applied(), isHint)) - .isImage(true) - .value(image.value()) - .type(imageType) - .redacted(image.applied()) - .reason(image.buildReasonWithManualChangeDescriptions()) - .legalBasis(image.legalBasis()) - .matchedRule(image.getMatchedRule().getRuleIdentifier().toString()) - .isHint(isHint) - .isDictionaryEntry(false) - .isRecommendation(false) - .positions(List.of(toRedactionLogRectangle(image.getPosition(), image.getPage().getNumber()))) - .sectionNumber(image.getTreeId().get(0)) - .section(image.getManualOverwrite().getSection().orElse(image.getParent().toString())) - .imageHasTransparency(image.isTransparent()) - .manualChanges(mapManualChanges(image.getManualOverwrite(), isHint)) - .comments(buildRedactionLogComments(comments, image.getId())) - .build(); - - } - } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/document/ManualEntityCreationService.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/document/ManualEntityCreationService.java index f2f6cb70..1cbeb321 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/document/ManualEntityCreationService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/document/ManualEntityCreationService.java @@ -53,7 +53,7 @@ public class ManualEntityCreationService { } - public List toRedactionEntity(RedactionLog redactionLog, SemanticNode node) { + public List toTextEntity(RedactionLog redactionLog, SemanticNode node) { List manualEntities = redactionLog.getRedactionLogEntry().stream().map(ManualEntity::fromRedactionLogEntry).peek(manualEntity -> { if (manualEntity.isApplied()) { @@ -62,26 +62,11 @@ public class ManualEntityCreationService { manualEntity.skip(manualEntity.getRuleIdentifier(), manualEntity.getReason()); } }).toList(); - return toRedactionEntity(manualEntities, node); + return toTextEntity(manualEntities, node); } - public List createRedactionEntitiesIfFoundAndReturnNotFoundEntries(Set manualRedactionEntries, - SemanticNode node, - String dossierTemplateId) { - - List manualEntities = manualRedactionEntries.stream() - .filter(manualRedactionEntry -> !(manualRedactionEntry.isAddToDictionary() || manualRedactionEntry.isAddToDossierDictionary())) - .map(manualRedactionEntry -> ManualEntity.fromManualRedactionEntry(manualRedactionEntry, - dictionaryService.isHint(manualRedactionEntry.getType(), dossierTemplateId))) - .peek(manualEntity -> manualEntity.apply("MAN.5.0", "manual entries are applied by default", manualEntity.getLegalBasis())) - .toList(); - - return toRedactionEntity(manualEntities, node); - } - - - private List toRedactionEntity(List manualEntities, SemanticNode node) { + private List toTextEntity(List manualEntities, SemanticNode node) { Set pageNumbers = manualEntities.stream().flatMap(entry -> entry.getEntityPosition().stream().map(RectangleWithPage::pageNumber)).collect(Collectors.toSet()); Set entryValues = manualEntities.stream().map(ManualEntity::getValue).map(String::toLowerCase).collect(Collectors.toSet()); @@ -102,6 +87,21 @@ public class ManualEntityCreationService { } + public List createRedactionEntitiesIfFoundAndReturnNotFoundEntries(Set manualRedactionEntries, + SemanticNode node, + String dossierTemplateId) { + + List manualEntities = manualRedactionEntries.stream() + .filter(manualRedactionEntry -> !(manualRedactionEntry.isAddToDictionary() || manualRedactionEntry.isAddToDossierDictionary())) + .map(manualRedactionEntry -> ManualEntity.fromManualRedactionEntry(manualRedactionEntry, + dictionaryService.isHint(manualRedactionEntry.getType(), dossierTemplateId))) + .peek(manualEntity -> manualEntity.apply("MAN.5.0", "manual entries are applied by default", manualEntity.getLegalBasis())) + .toList(); + + return toTextEntity(manualEntities, node); + } + + /** * Deletes the temp Entity and creates a RedactionEntity with correct values, based on the given parameters. * diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/EntityDroolsExecutionService.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/EntityDroolsExecutionService.java index 82b4883b..f9156cdc 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/EntityDroolsExecutionService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/EntityDroolsExecutionService.java @@ -7,8 +7,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; -import com.iqser.red.service.redaction.v1.server.utils.exception.DroolsTimeoutException; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.rule.QueryResults; @@ -16,6 +14,7 @@ import org.kie.api.runtime.rule.QueryResultsRow; import org.springframework.stereotype.Service; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; +import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions; import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings; import com.iqser.red.service.redaction.v1.server.model.NerEntities; @@ -25,6 +24,7 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNo import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService; +import com.iqser.red.service.redaction.v1.server.utils.exception.DroolsTimeoutException; import io.micrometer.core.annotation.Timed; import lombok.AccessLevel; @@ -81,7 +81,7 @@ public class EntityDroolsExecutionService { fileAttributes.stream().filter(f -> f.getValue() != null).forEach(kieSession::insert); if (manualRedactions != null) { - manualRedactions.getResizeRedactions().forEach(kieSession::insert); + manualRedactions.getResizeRedactions().stream().filter(manualResizeRedaction -> !manualResizeRedaction.getUpdateDictionary()).forEach(kieSession::insert); manualRedactions.getRecategorizations().forEach(kieSession::insert); manualRedactions.getEntriesToAdd().forEach(kieSession::insert); manualRedactions.getForceRedactions().forEach(kieSession::insert); diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/RedactionIntegrationTest.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/RedactionIntegrationTest.java index 82013e6c..406aa3b2 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/RedactionIntegrationTest.java +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/RedactionIntegrationTest.java @@ -196,6 +196,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest { } + @Test public void manualResizeTest() throws IOException { @@ -213,29 +214,27 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest { // } AnalyzeRequest request = uploadFileToStorage("files/new/crafted document.pdf"); + ManualResizeRedaction manualResizeRedaction = ManualResizeRedaction.builder() + .annotationId("c6be5277f5ee60dc3d83527798b7fe02") + .value("Dr. Alan") + .positions(List.of(new Rectangle(236.8f, 182.90005f, 40.584f, 12.642f, 7))) + .status(AnnotationStatus.APPROVED) + .requestDate(OffsetDateTime.now()) + .build(); + manualResizeRedaction.setUpdateDictionary(false); - ManualRedactions manualRedactions = ManualRedactions.builder() - .resizeRedactions(Set.of(ManualResizeRedaction.builder() - .annotationId("c6be5277f5ee60dc3d83527798b7fe02") - .value("Dr. Alan") - .positions(List.of(new Rectangle(236.8f,182.90005f,40.584f,12.642f, 7 ))) - .status(AnnotationStatus.APPROVED) - .requestDate(OffsetDateTime.now()) - .build() - )) + ManualRedactions manualRedactions = ManualRedactions.builder().resizeRedactions(Set.of()) .build(); request.setManualRedactions(manualRedactions); - - System.out.println("Start Full integration test"); analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request); System.out.println("Finished structure analysis"); AnalyzeResult result = analyzeService.analyze(request); System.out.println("Finished analysis"); - var redactionLog = redactionStorageService.getRedactionLog(TEST_DOSSIER_ID, TEST_FILE_ID); + var redactionLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID); AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder().dossierId(TEST_DOSSIER_ID).fileId(TEST_FILE_ID).build()); @@ -574,8 +573,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest { fileOutputStream.write(objectMapper.writeValueAsBytes(redactionStorageService.getDocumentData(TEST_DOSSIER_ID, TEST_FILE_ID))); } - List valuesInDocument = entityLog.getEntityLogEntry() - .stream().filter(e -> !e.getEntryType().equals(EntryType.IMAGE)) + List valuesInDocument = entityLog.getEntityLogEntry().stream().filter(e -> !e.getEntryType().equals(EntryType.IMAGE)) .map(redactionLogEntry -> new TextRange(redactionLogEntry.getStartOffset(), redactionLogEntry.getEndOffset())) .map(boundary -> documentGraph.getTextBlock().subSequence(boundary).toString()) .toList(); @@ -893,19 +891,19 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest { AnalyzeRequest request = uploadFileToStorage("files/new/S157.pdf"); ManualRedactions manualRedactions = new ManualRedactions(); - - manualRedactions.getResizeRedactions() - .add(ManualResizeRedaction.builder() - .annotationId("ca2b437e2480a4b5966cb8386020d454") - .fileId(TEST_FILE_ID) - .status(AnnotationStatus.APPROVED) - .requestDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 475479, ZoneOffset.UTC)) - .processedDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 483651, ZoneOffset.UTC)) - .value("Bera P") - .positions(List.of(new Rectangle(382.12485f, 235.94246f, 8.768621f, -21.632504f, 1))) - .textBefore("") - .textAfter("") - .build()); + ManualResizeRedaction manualResizeRedaction = ManualResizeRedaction.builder() + .annotationId("ca2b437e2480a4b5966cb8386020d454") + .fileId(TEST_FILE_ID) + .status(AnnotationStatus.APPROVED) + .requestDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 475479, ZoneOffset.UTC)) + .processedDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 483651, ZoneOffset.UTC)) + .value("Bera P") + .positions(List.of(new Rectangle(382.12485f, 235.94246f, 8.768621f, -21.632504f, 1))) + .textBefore("") + .textAfter("") + .build(); + manualResizeRedaction.setUpdateDictionary(false); + manualRedactions.getResizeRedactions().add(manualResizeRedaction); request.setManualRedactions(manualRedactions); diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/annotate/AnnotationService.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/annotate/AnnotationService.java index 036814f1..28ffbeec 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/annotate/AnnotationService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/annotate/AnnotationService.java @@ -12,10 +12,12 @@ import java.util.stream.Stream; import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; +import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentProperties; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationHighlight; import org.springframework.stereotype.Service; @@ -60,7 +62,8 @@ public class AnnotationService { public AnnotateResponse annotate(AnnotateRequest annotateRequest) { var storedObjectFile = redactionStorageService.getStoredObjectFile(RedactionStorageService.StorageIdUtils.getStorageId(annotateRequest.getDossierId(), - annotateRequest.getFileId(), FileType.VIEWER_DOCUMENT)); + annotateRequest.getFileId(), + FileType.VIEWER_DOCUMENT)); var entityLog = redactionStorageService.getEntityLog(annotateRequest.getDossierId(), annotateRequest.getFileId()); @@ -70,11 +73,14 @@ public class AnnotationService { dictionaryService.updateDictionary(annotateRequest.getDossierTemplateId(), annotateRequest.getDossierId()); annotate(pdDocument, entityLog); + PDDocumentCatalog catalog = pdDocument.getDocumentCatalog(); + PDOptionalContentProperties ocprops = catalog.getOCProperties(); + ocprops.setGroupEnabled("Layout grid", true); + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { pdDocument.save(byteArrayOutputStream); return AnnotateResponse.builder().document(byteArrayOutputStream.toByteArray()).build(); } - } } @@ -173,6 +179,7 @@ public class AnnotationService { return rectangles.stream().map(Position::toRectangle2D).flatMap(AnnotationService::toQuadPoints).toList(); } + public static Stream toQuadPoints(Rectangle2D rectangle) { double x1 = rectangle.getMinX(); diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/document/graph/MigrationPocTest.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/document/graph/MigrationPocTest.java index 2604adee..517b5c61 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/document/graph/MigrationPocTest.java +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/document/graph/MigrationPocTest.java @@ -2,7 +2,6 @@ package com.iqser.red.service.redaction.v1.server.document.graph; import static java.util.stream.Collectors.toMap; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.when; import java.io.IOException; @@ -27,12 +26,11 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSON import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type; 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.document.DocumentGraphMapper; -import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; -import com.iqser.red.service.redaction.v1.server.service.document.ManualEntityCreationService; import com.iqser.red.service.redaction.v1.server.model.ManualEntity; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.service.RedactionLogCreatorService; -import com.iqser.red.service.redaction.v1.server.utils.ExceptionProvider; +import com.iqser.red.service.redaction.v1.server.service.document.DocumentGraphMapper; +import com.iqser.red.service.redaction.v1.server.service.document.ManualEntityCreationService; import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; import com.knecon.fforesight.tenantcommons.TenantContext; @@ -94,7 +92,7 @@ public class MigrationPocTest extends BuildDocumentIntegrationTest { // IMPORTANT: always use the graph which is mapped from the DocumentData, since rounding errors occur during storage. Document document = DocumentGraphMapper.toDocumentGraph(redactionStorageService.getDocumentData(request.getDossierId(), request.getFileId())); - List notFoundManualRedactionEntries = redactionLogAdapter.toRedactionEntity(originalRedactionLog, document); + List notFoundManualRedactionEntries = redactionLogAdapter.toTextEntity(originalRedactionLog, document); var migratedRedactionLogEntries = redactionLogCreatorService.createRedactionLog(document, TEST_DOSSIER_TEMPLATE_ID, notFoundManualRedactionEntries, Collections.emptyMap()); diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/manualchanges/ManualChangesEnd2EndTest.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/manualchanges/ManualChangesEnd2EndTest.java index 14f2d524..7844895a 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/manualchanges/ManualChangesEnd2EndTest.java +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/manualchanges/ManualChangesEnd2EndTest.java @@ -38,6 +38,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileTyp 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.ManualRedactionType; 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.Comment; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions; @@ -50,6 +51,7 @@ 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.ManualResizeRedaction; import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type; +import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Point; import com.iqser.red.service.redaction.v1.server.AbstractRedactionIntegrationTest; import com.iqser.red.service.redaction.v1.server.Application; import com.iqser.red.service.redaction.v1.server.annotate.AnnotateRequest; @@ -158,8 +160,7 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest { TextEntity expandedEntity = entityCreationService.byString(expandedEntityKeyword, "PII", EntityType.ENTITY, document).findFirst().get(); String idToResize = redactionLog.getEntityLogEntry() - .stream() - .filter(entry -> entry.getValue().equals(testEntityValue1)).max(Comparator.comparingInt(EntityLogEntry::getStartOffset)) + .stream().filter(entry -> entry.getValue().equals(testEntityValue1)).max(Comparator.comparingInt(EntityLogEntry::getStartOffset)) .get() .getId(); List resizedPositions = expandedEntity.getPositionsOnPagePerPage() @@ -175,7 +176,7 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest { .requestDate(OffsetDateTime.now()) .status(AnnotationStatus.APPROVED) .build(); - + manualResizeRedaction.setUpdateDictionary(false); ManualRedactions manualRedactions = new ManualRedactions(); manualRedactions.getResizeRedactions().add(manualResizeRedaction); request.setManualRedactions(manualRedactions); @@ -335,4 +336,45 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest { assertEquals(1, oxfordUniversityPressRecategorized.getManualChanges().size()); } + + @Test + @SneakyThrows + public void testShrinkingResize() { + + String filePath = "files/new/crafted document.pdf"; + AnalyzeRequest request = uploadFileToStorage(filePath); + analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request); + AnalyzeResult result = analyzeService.analyze(request); + ManualRedactions manualRedactions = new ManualRedactions(); + String annotationId = "testAnnotationId"; + + manualRedactions.setEntriesToAdd(Set.of(ManualRedactionEntry.builder() + .annotationId(annotationId) + .requestDate(OffsetDateTime.now()) + .value("Expand to Hint Clarissa’s Donut ← not added to Dict, should be not annotated Simpson's Tower ← added to Authors-Dict, should be annotated") + .positions(List.of(// + new Rectangle(new Point(56.8f, 496.27f), 61.25f, 12.83f, 2), // + new Rectangle(new Point(56.8f, 482.26f), 303.804f, 15.408f, 2), // + new Rectangle(new Point(56.8f, 468.464f), 314.496f, 15.408f, 2))) // + .status(AnnotationStatus.APPROVED) + .build())); + ManualResizeRedaction manualResizeRedaction = ManualResizeRedaction.builder() + .annotationId(annotationId) + .requestDate(OffsetDateTime.now()) + .value("Expand to Hint") + .positions(List.of(new Rectangle(new Point(56.8f, 496.27f), 61.25f, 12.83f, 2))).status(AnnotationStatus.APPROVED) + .build(); + manualResizeRedaction.setUpdateDictionary(false); + manualRedactions.setResizeRedactions(Set.of(manualResizeRedaction)); + request.setManualRedactions(manualRedactions); + + analyzeService.reanalyze(request); + + EntityLog entityLog = redactionStorageService.getEntityLog(request.getDossierId(), request.getFileId()); + EntityLogEntry entityLogEntry = entityLog.getEntityLogEntry().stream().filter(entry -> entry.getId().equals(annotationId)).findFirst().orElseThrow(); + assertEquals("Expand to Hint", entityLogEntry.getValue()); + assertEquals(1, entityLogEntry.getPositions().size()); + assertEquals(ManualRedactionType.RESIZE, entityLogEntry.getManualChanges().get(entityLogEntry.getManualChanges().size() - 1).getManualRedactionType()); + } + } diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/documine_flora.drl b/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/documine_flora.drl index 417fa51a..056b83a4 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/documine_flora.drl +++ b/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/documine_flora.drl @@ -401,17 +401,10 @@ rule "DOC.7.1: Performing Laboratory (Country)" rule "DOC.7.2: Performing Laboratory (Country & Name) from dict" when - $section: Section( - (hasEntitiesOfType("laboratory_country") || hasEntitiesOfType("laboratory_name")) - && (containsString("PERFORMING LABORATORY:") || (containsString("PERFORMING") && containsString("LABORATORY:"))) - ) + $section: Section(containsString("PERFORMING LABORATORY:") || (containsString("PERFORMING") && containsString("LABORATORY:"))) + $countryOrNameFromDictionary: TextEntity(type == "laboratory_country" || type == "laboratory_name", $type: type, isDictionaryEntry()) then - $section.getEntitiesOfType("laboratory_country").forEach(entity -> { - entity.apply("DOC.7.2", "Performing laboratory country dictionary entry found."); - }); - $section.getEntitiesOfType("laboratory_name").forEach(entity -> { - entity.apply("DOC.7.2", "Performing laboratory name dictionary entry found."); - }); + $countryOrNameFromDictionary.apply("DOC.7.2", "Performing " + $type + " dictionary entry found."); end