RED-7784 - Apply unprocessed manual changes in reports and redactions
* Add endpoint used to calculate information aboout unprocessed manual entities like textBefore/textAfter/offsets etc.
This commit is contained in:
parent
db2b663547
commit
6930d94274
@ -0,0 +1,24 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode
|
||||
public class UnprocessedManualEntity {
|
||||
|
||||
private String annotationId;
|
||||
private String textBefore;
|
||||
private String textAfter;
|
||||
private int startOffset;
|
||||
private int endOffset;
|
||||
private String closestHeadline;
|
||||
private float[] color;
|
||||
private List<Integer> containingNodeId;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.iqser.red.service.redaction.v1.resources;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.redaction.v1.model.UnprocessedManualEntity;
|
||||
|
||||
public interface UnprocessedManualEntityResource {
|
||||
|
||||
@GetMapping(value = "/mergeUnprocessedManualEntities", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<UnprocessedManualEntity> mergeUnprocessedManualEntities(@RequestParam(value = "fileId") String fileId,
|
||||
@RequestParam(value = "dossierId") String dossierId,
|
||||
@RequestParam(value = "dossierTemplateId") String dosserTemplateId,
|
||||
@RequestBody ManualRedactions manualRedactions);
|
||||
}
|
||||
@ -16,7 +16,7 @@ val layoutParserVersion = "0.74.0"
|
||||
val jacksonVersion = "2.15.2"
|
||||
val droolsVersion = "9.44.0.Final"
|
||||
val pdfBoxVersion = "3.0.0"
|
||||
val persistenceServiceVersion = "2.210.0"
|
||||
val persistenceServiceVersion = "2.223.0"
|
||||
|
||||
configurations {
|
||||
all {
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
package com.iqser.red.service.redaction.v1.server.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.redaction.v1.model.UnprocessedManualEntity;
|
||||
import com.iqser.red.service.redaction.v1.resources.UnprocessedManualEntityResource;
|
||||
import com.iqser.red.service.redaction.v1.server.service.UnprocessedChangesService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class UnprocessedManualEntityController implements UnprocessedManualEntityResource {
|
||||
|
||||
private final UnprocessedChangesService unprocessedChangesService;
|
||||
|
||||
|
||||
@Override
|
||||
public List<UnprocessedManualEntity> mergeUnprocessedManualEntities(String fileId, String dossierId, String dossierTemplateId, ManualRedactions manualRedactions) {
|
||||
|
||||
return unprocessedChangesService.addInformationToUnprocessedManualEntities(fileId, dossierId, dossierTemplateId, manualRedactions);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
package com.iqser.red.service.redaction.v1.server.service;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
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.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
|
||||
import com.iqser.red.service.redaction.v1.model.UnprocessedManualEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.PositionOnPage;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
|
||||
import com.iqser.red.service.redaction.v1.server.service.document.DocumentGraphMapper;
|
||||
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.storage.RedactionStorageService;
|
||||
import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility;
|
||||
import com.iqser.red.service.redaction.v1.server.utils.exception.NotFoundException;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UnprocessedChangesService {
|
||||
|
||||
private final RedactionStorageService redactionStorageService;
|
||||
private final EntityEnrichmentService entityEnrichmentService;
|
||||
private final DictionaryService dictionaryService;
|
||||
|
||||
|
||||
public List<UnprocessedManualEntity> addInformationToUnprocessedManualEntities(String fileId, String dossierId, String dossierTemplateId, ManualRedactions manualRedactions) {
|
||||
|
||||
Document document = DocumentGraphMapper.toDocumentGraph(redactionStorageService.getDocumentData(dossierId, fileId));
|
||||
List<UnprocessedManualEntity> unprocessedManualEntities = new ArrayList<>();
|
||||
|
||||
for (SemanticNode node : document.streamChildren().toList()) {
|
||||
|
||||
if (manualRedactions.getEntriesToAdd().isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
var iterator = manualRedactions.getEntriesToAdd().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
var manualAddRedaction = iterator.next();
|
||||
if (sectionContainsEntry(node, manualAddRedaction.getPositions())) {
|
||||
TextEntity surroundingText = findSurroundingText(node, manualAddRedaction.getValue(), manualAddRedaction.getPositions());
|
||||
unprocessedManualEntities.add(UnprocessedManualEntity.builder()
|
||||
.annotationId(manualAddRedaction.getAnnotationId())
|
||||
.closestHeadline(surroundingText.getDeepestFullyContainingNode().getHeadline().getTextBlock().getSearchText())
|
||||
.color(getColor(surroundingText.getType(), dossierTemplateId, surroundingText.applied(), surroundingText.getEntityType().equals(EntityType.HINT)))
|
||||
.startOffset(surroundingText.getTextRange().start())
|
||||
.endOffset(surroundingText.getTextRange().end())
|
||||
.containingNodeId(surroundingText.getDeepestFullyContainingNode().getTreeId())
|
||||
.textBefore(surroundingText.getTextBefore())
|
||||
.textAfter(surroundingText.getTextAfter())
|
||||
.build());
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unprocessedManualEntities;
|
||||
}
|
||||
|
||||
|
||||
private TextEntity findSurroundingText(SemanticNode node, String value, List<Rectangle> toFindPositions) {
|
||||
|
||||
EntityCreationService entityCreationService = new EntityCreationService(entityEnrichmentService);
|
||||
Set<TextEntity> entities = RedactionSearchUtility.findTextRangesByString(value, node.getTextBlock())
|
||||
.stream()
|
||||
.map(boundary -> entityCreationService.forceByTextRange(boundary, "searchHelper", EntityType.RECOMMENDATION, node))
|
||||
.collect(Collectors.toSet());
|
||||
return getEntityOnCorrectPosition(entities, toFindPositions);
|
||||
}
|
||||
|
||||
|
||||
private boolean sectionContainsEntry(SemanticNode semanticNode, List<Rectangle> positions) {
|
||||
|
||||
for (Rectangle position : positions) {
|
||||
if (semanticNode.containsRectangle(UnprocessedChangesService.toRectangle2D(position), position.getPage())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private TextEntity getEntityOnCorrectPosition(Set<TextEntity> entities, List<Rectangle> toFindPositions) {
|
||||
|
||||
return entities.stream()
|
||||
.filter(entity -> entity.getPositionsOnPagePerPage()
|
||||
.stream()
|
||||
.map(PositionOnPage::getRectanglePerLine)
|
||||
.flatMap(Collection::stream)
|
||||
.allMatch(rectangle2D -> toFindPositionsIntersectRectangle(toFindPositions, rectangle2D)))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NotFoundException("No matching Entity could be found for positions" + toFindPositions));
|
||||
}
|
||||
|
||||
|
||||
private static boolean toFindPositionsIntersectRectangle(List<Rectangle> toFindPositions, Rectangle2D rectangle2D) {
|
||||
|
||||
return toFindPositions.stream().map(UnprocessedChangesService::toRectangle2D).anyMatch(toFindRectangle -> toFindRectangle.intersects(rectangle2D));
|
||||
}
|
||||
|
||||
|
||||
private static Rectangle2D toRectangle2D(Rectangle rect) {
|
||||
|
||||
return new Rectangle2D.Double(rect.getTopLeftX() - rect.getWidth(), rect.getTopLeftY() - rect.getHeight(), rect.getWidth(), rect.getHeight());
|
||||
}
|
||||
|
||||
private float[] getColor(String type, String dossierTemplateId, boolean isApplied, boolean isHint) {
|
||||
|
||||
if (!isApplied && !isHint) {
|
||||
return dictionaryService.getNotRedactedColor(dossierTemplateId);
|
||||
}
|
||||
return dictionaryService.getColor(type, dossierTemplateId);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user