DM-285: Entity to component mappings with new ruleset

* refactoring structure
* added custom syntax validation, such that rule files may not be corrupted
* added component drools preparations
* deprecated redactionLog
* integrated entityLog
* integrated new getRules with ruleFileType
* renamed service.graph package -> service.document
* fixed tests
* update layout parser version
* initial component drools file - wip

Signed-off-by: Kilian Schuettler <kilian.schuettler@knecon.com>
This commit is contained in:
Kilian Schuettler 2023-09-08 17:11:39 +02:00
parent ee46e73c99
commit a8ce7ab1f3
32 changed files with 1346 additions and 194 deletions

View File

@ -12,11 +12,11 @@ plugins {
description = "redaction-service-server-v1"
val layoutParserVersion = "0.25.0"
val layoutParserVersion = "0.70.0"
val jacksonVersion = "2.15.2"
val droolsVersion = "8.43.0.Final"
val pdfBoxVersion = "3.0.0-alpha2"
val persistenceServiceVersion = "DM-285-3"
val pdfBoxVersion = "3.0.0"
val persistenceServiceVersion = "DM-285-5"
configurations {
all {

View File

@ -5,7 +5,8 @@ import java.util.PriorityQueue;
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.redactionlog.RedactionLogEntry;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Entity;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.ManualChangeOverwrite;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
@ -20,7 +21,7 @@ import lombok.experimental.FieldDefaults;
@Builder
@AllArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class ManualEntity implements Entity {
public class ManualEntity implements IEntity {
// The id must be mapped into a TextEntity as is for comments to work correctly
String id;
@ -88,4 +89,18 @@ public class ManualEntity implements Entity {
.build();
}
@Override
public TextRange getTextRange() {
return new TextRange(-1, -1);
}
@Override
public String type() {
return getManualOverwrite().getType().orElse(type);
}
}

View File

@ -0,0 +1,41 @@
package com.iqser.red.service.redaction.v1.server.model.component;
import java.util.Collection;
import java.util.List;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleIdentifier;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Component {
RuleIdentifier matchedRule;
String category;
String value;
String transformation;
List<Entity> references;
public boolean addReference(Entity entity) {
return references.add(entity);
}
public boolean addReferences(Collection<Entity> entities) {
return references.addAll(entities);
}
}

View File

@ -0,0 +1,106 @@
package com.iqser.red.service.redaction.v1.server.model.component;
import java.util.List;
import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Change;
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;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@Getter
@Builder
@AllArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class Entity {
String id;
String type;
EntryType entryType;
EntryState state;
String value;
String reason;
String matchedRule;
boolean rectangle;
String legalBasis;
boolean imported;
String section;
float[] color;
List<Position> positions;
int sectionNumber;
String textBefore;
String textAfter;
int startOffset;
int endOffset;
int length;
boolean imageHasTransparency;
boolean isDictionaryEntry;
boolean isDossierDictionaryEntry;
boolean excluded;
String sourceId;
List<Change> changes;
List<ManualChange> manualChanges;
Set<Engine> engines;
Set<String> reference;
Set<String> importedRedactionIntersections;
public static Entity fromEntityLogEntry(EntityLogEntry e) {
return Entity.builder()
.id(e.getId())
.type(e.getType())
.entryType(e.getEntryType())
.state(e.getState())
.value(e.getValue())
.reason(e.getReason())
.matchedRule(e.getMatchedRule())
.rectangle(e.isRectangle())
.legalBasis(e.getLegalBasis())
.imported(e.isImported())
.section(e.getSection())
.color(e.getColor())
.positions(e.getPositions())
.sectionNumber(e.getSectionNumber())
.textBefore(e.getTextBefore())
.textAfter(e.getTextAfter())
.startOffset(e.getStartOffset())
.endOffset(e.getEndOffset())
.length(e.getValue().length())
.imageHasTransparency(e.isImageHasTransparency())
.isDictionaryEntry(e.isDictionaryEntry())
.isDossierDictionaryEntry(e.isDossierDictionaryEntry())
.excluded(e.isExcluded())
.sourceId(e.getSourceId())
.changes(e.getChanges())
.manualChanges(e.getManualChanges())
.engines(e.getEngines())
.reference(e.getReference())
.importedRedactionIntersections(e.getImportedRedactionIntersections())
.build();
}
}

View File

@ -1,24 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.document.entity;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Component {
String value;
String originalValue;
String transformation;
List<Entity> references;
}

View File

@ -5,11 +5,12 @@ import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleIdentifier;
import lombok.NonNull;
public interface Entity {
public interface IEntity {
PriorityQueue<MatchedRule> getMatchedRuleList();
@ -17,6 +18,27 @@ public interface Entity {
ManualChangeOverwrite getManualOverwrite();
String getValue();
TextRange getTextRange();
String type();
default int length() {
return value().length();
}
default String value() {
return getManualOverwrite().getValue().orElse(getValue());
}
// Don't use default accessor pattern (e.g. isApplied()), as it might lead to errors in drools due to property-specific optimization of the drools planner.
default boolean applied() {

View File

@ -28,7 +28,7 @@ import lombok.experimental.FieldDefaults;
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class TextEntity implements Entity {
public class TextEntity implements IEntity {
// primary key
@EqualsAndHashCode.Include
@ -201,5 +201,10 @@ public class TextEntity implements Entity {
}
@Override
public String type() {
return getManualOverwrite().getType().orElse(type);
}
}

View File

@ -5,12 +5,14 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Entity;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.ManualChangeOverwrite;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
@ -30,7 +32,7 @@ import lombok.experimental.FieldDefaults;
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Image implements GenericSemanticNode, Entity {
public class Image implements GenericSemanticNode, IEntity {
List<Integer> treeId;
String id;
@ -77,6 +79,20 @@ public class Image implements GenericSemanticNode, Entity {
}
@Override
public TextRange getTextRange() {
return GenericSemanticNode.super.getTextRange();
}
@Override
public String type() {
return getManualOverwrite().getType().orElse(imageType.toString());
}
@Override
public String toString() {
@ -92,4 +108,23 @@ public class Image implements GenericSemanticNode, Entity {
return bBoxPerPage;
}
@Override
public String getValue() {
return NodeType.IMAGE + ":" + camelCase(imageType.toString());
}
private String camelCase(String name) {
return name.charAt(0) + name.substring(1).toLowerCase(Locale.ENGLISH);
}
public int length() {
return 0;
}
}

View File

@ -16,6 +16,6 @@ public enum NodeType {
public String toString() {
return this.name().charAt(0) + this.name().substring(1).toLowerCase(Locale.ROOT);
return this.name().charAt(0) + this.name().substring(1).toLowerCase(Locale.ENGLISH);
}
}

View File

@ -17,6 +17,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribu
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
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.EntityLogChanges;
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.EntityLogLegalBasis;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Comment;
@ -35,15 +36,16 @@ import com.iqser.red.service.redaction.v1.server.model.NerEntities;
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrement;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryVersion;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Component;
import com.iqser.red.service.redaction.v1.server.model.component.Component;
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.drools.ComponentDroolsExecutionService;
import com.iqser.red.service.redaction.v1.server.service.drools.EntityDroolsExecutionService;
import com.iqser.red.service.redaction.v1.server.service.drools.KieContainerCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.DocumentGraphMapper;
import com.iqser.red.service.redaction.v1.server.service.document.ManualRedactionEntryService;
import com.iqser.red.service.redaction.v1.server.service.document.NerEntitiesAdapter;
import com.iqser.red.service.redaction.v1.server.service.document.SectionFinderService;
import com.iqser.red.service.redaction.v1.server.service.drools.ComponentDroolsExecutionService;
import com.iqser.red.service.redaction.v1.server.service.drools.EntityDroolsExecutionService;
import com.iqser.red.service.redaction.v1.server.service.drools.KieContainerCreationService;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import io.micrometer.core.annotation.Timed;
@ -71,6 +73,7 @@ public class AnalyzeService {
ComponentLogCreatorService componentLogCreatorService;
RedactionStorageService redactionStorageService;
RedactionChangeLogService redactionChangeLogService;
EntityChangeLogService entityChangeLogService;
LegalBasisClient legalBasisClient;
RedactionServiceSettings redactionServiceSettings;
ImportedRedactionService importedRedactionService;
@ -114,27 +117,14 @@ public class AnalyzeService {
nerEntities);
log.info("Finished entity rule execution for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
ComponentLog componentLog = new ComponentLog();
if (kieWrapperComponentRules.isPresent()) {
List<Component> components = componentDroolsExecutionService.executeRules(document.getEntities(),
allFileAttributes,
notFoundManualRedactionEntries,
document.streamAllImages().toList());
componentLog = componentLogCreatorService.buildComponentLog(kieWrapperComponentRules.container(), analyzeRequest.getAnalysisNumber(), components);
log.info("Finished component rule execution for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
}
RedactionLog redactionLog = createRedactionLog(analyzeRequest, document, notFoundManualRedactionEntries, dictionary, kieWrapperEntityRules);
EntityLog entityLog = createEntityLog(analyzeRequest, document, notFoundManualRedactionEntries, dictionary, kieWrapperEntityRules);
return finalizeAnalysis(analyzeRequest,
startTime,
entityLog, componentLog,
startTime, kieWrapperComponentRules, entityLog,
redactionLog,
document.getNumberOfPages(),
dictionary.getVersion(),
false, new HashSet<>(allFileAttributes));
dictionary.getVersion(), false, new HashSet<>(allFileAttributes));
}
@ -184,16 +174,6 @@ public class AnalyzeService {
log.info("Loaded previous entity log for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
ComponentLog previousComponentLog = new ComponentLog();
KieWrapper kieWrapperComponentRules = kieContainerCreationService.getLatestKieContainer(analyzeRequest.getDossierTemplateId(), RuleFileType.COMPONENT);
if (kieWrapperComponentRules.isPresent()) {
log.info("Updated component rules to version {} for file {} in dossier {}",
kieWrapperComponentRules.rulesVersion(),
analyzeRequest.getFileId(),
analyzeRequest.getDossierId());
previousComponentLog = redactionStorageService.getComponentLog(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
log.info("Loaded previous component log for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
}
Document document = DocumentGraphMapper.toDocumentGraph(redactionStorageService.getDocumentData(analyzeRequest.getDossierId(), analyzeRequest.getFileId()));
log.info("Loaded Document Graph for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
@ -203,30 +183,28 @@ public class AnalyzeService {
}
DictionaryIncrement dictionaryIncrement = dictionaryService.getDictionaryIncrements(analyzeRequest.getDossierTemplateId(),
new DictionaryVersion(previousRedactionLog.getDictionaryVersion(), previousRedactionLog.getDossierDictionaryVersion()),
new DictionaryVersion(previousEntityLog.getDictionaryVersion(), previousEntityLog.getDossierDictionaryVersion()),
analyzeRequest.getDossierId());
Set<Integer> sectionsToReanalyseIds = getSectionsToReanalyseIds(analyzeRequest, previousRedactionLog, document, dictionaryIncrement);
Set<Integer> sectionsToReanalyseIds = getSectionsToReanalyseIds(analyzeRequest, previousEntityLog, document, dictionaryIncrement);
List<SemanticNode> sectionsToReAnalyse = getSectionsToReAnalyse(document, sectionsToReanalyseIds);
log.info("{} Sections to reanalyze found for file {} in dossier {}", sectionsToReanalyseIds.size(), analyzeRequest.getFileId(), analyzeRequest.getDossierId());
if (sectionsToReAnalyse.isEmpty()) {
return finalizeAnalysis(analyzeRequest,
startTime,
previousEntityLog, previousComponentLog,
startTime, kieContainerCreationService.getLatestKieContainer(analyzeRequest.getDossierTemplateId(), RuleFileType.COMPONENT), previousEntityLog,
previousRedactionLog,
document.getNumberOfPages(),
dictionaryIncrement.getDictionaryVersion(),
true,
Collections.emptySet());
}
KieWrapper kieWrapperEntityRules = kieContainerCreationService.getLatestKieContainer(analyzeRequest.getDossierTemplateId(), RuleFileType.ENTITY);
log.info("Updated entity rules to version {} for file {} in dossier {}", kieWrapperEntityRules.rulesVersion(), analyzeRequest.getFileId(), analyzeRequest.getDossierId());
NerEntities nerEntities = getEntityRecognitionEntitiesFilteredBySectionIds(analyzeRequest, document, sectionsToReanalyseIds);
log.info("Loaded Ner Entities for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
KieWrapper kieWrapperEntityRules = kieContainerCreationService.getLatestKieContainer(analyzeRequest.getDossierTemplateId(), RuleFileType.ENTITY);
log.info("Updated entity rules to version {} for file {} in dossier {}", kieWrapperEntityRules.rulesVersion(), analyzeRequest.getFileId(), analyzeRequest.getDossierId());
List<ManualEntity> notFoundManualRedactionEntries = manualRedactionEntryService.addManualRedactionEntriesAndReturnNotFoundEntries(analyzeRequest, document);
Dictionary dictionary = dictionaryService.getDeepCopyDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
@ -244,23 +222,11 @@ public class AnalyzeService {
nerEntities);
log.info("Finished entity rule execution for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
ComponentLog componentLog = new ComponentLog();
if (kieWrapperComponentRules.isPresent()) {
List<Component> components = componentDroolsExecutionService.executeRules(document.getEntities(),
allFileAttributes,
notFoundManualRedactionEntries,
document.streamAllImages().toList());
componentLog = componentLogCreatorService.buildComponentLog(kieWrapperComponentRules.container(), analyzeRequest.getAnalysisNumber(), components);
log.info("Finished component rule execution for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
}
RedactionLog redactionLog = updatePreviousRedactionLog(analyzeRequest, document, notFoundManualRedactionEntries, previousRedactionLog, sectionsToReanalyseIds);
EntityLog entityLog = updatePreviousEntityLog(analyzeRequest, document, notFoundManualRedactionEntries, previousEntityLog, sectionsToReanalyseIds);
return finalizeAnalysis(analyzeRequest,
startTime,
entityLog, componentLog,
startTime, kieContainerCreationService.getLatestKieContainer(analyzeRequest.getDossierTemplateId(), RuleFileType.COMPONENT), entityLog,
redactionLog,
document.getNumberOfPages(),
dictionaryIncrement.getDictionaryVersion(),
@ -316,26 +282,20 @@ public class AnalyzeService {
private AnalyzeResult finalizeAnalysis(AnalyzeRequest analyzeRequest,
long startTime,
EntityLog entityLog, ComponentLog componentLog,
long startTime, KieWrapper kieWrapperComponentRules, EntityLog entityLog,
RedactionLog redactionLog,
int numberOfPages,
DictionaryVersion dictionaryVersion,
boolean isReanalysis,
Set<FileAttribute> addedFileAttributes) {
redactionLog.setDictionaryVersion(dictionaryVersion.getDossierTemplateVersion());
redactionLog.setDossierDictionaryVersion(dictionaryVersion.getDossierVersion());
excludeExcludedPages(redactionLog, analyzeRequest.getExcludedPages());
EntityLogChanges entityLogChanges = finalizeEntityLog(analyzeRequest, entityLog, redactionLog, dictionaryVersion);
RedactionLogChanges redactionLogChange = redactionChangeLogService.computeChanges(analyzeRequest.getDossierId(),
analyzeRequest.getFileId(),
redactionLog,
analyzeRequest.getAnalysisNumber());
log.info("Created Redaction Log for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
if (entityLogChanges.isHasChanges()) {
computeComponentsWhenRulesArePresent(analyzeRequest, kieWrapperComponentRules, addedFileAttributes, entityLogChanges);
}
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.REDACTION_LOG, redactionLogChange.getRedactionLog());
log.info("Stored Redaction Log for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
log.info("Stored analysis logs for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
long duration = System.currentTimeMillis() - startTime;
@ -346,13 +306,13 @@ public class AnalyzeService {
.fileId(analyzeRequest.getFileId())
.duration(duration)
.numberOfPages(numberOfPages)
.hasUpdates(redactionLogChange.isHasChanges())
.hasUpdates(entityLogChanges.isHasChanges())
.analysisVersion(redactionServiceSettings.getAnalysisVersion())
.analysisNumber(analyzeRequest.getAnalysisNumber())
.rulesVersion(redactionLog.getRulesVersion())
.dictionaryVersion(redactionLog.getDictionaryVersion())
.legalBasisVersion(redactionLog.getLegalBasisVersion())
.dossierDictionaryVersion(redactionLog.getDossierDictionaryVersion())
.rulesVersion(entityLog.getRulesVersion())
.dictionaryVersion(entityLog.getDictionaryVersion())
.legalBasisVersion(entityLog.getLegalBasisVersion())
.dossierDictionaryVersion(entityLog.getDossierDictionaryVersion())
.wasReanalyzed(isReanalysis)
.manualRedactions(analyzeRequest.getManualRedactions())
.addedFileAttributes(addedFileAttributes)
@ -360,16 +320,58 @@ public class AnalyzeService {
}
private void computeComponentsWhenRulesArePresent(AnalyzeRequest analyzeRequest,
KieWrapper kieWrapperComponentRules,
Set<FileAttribute> addedFileAttributes,
EntityLogChanges entityLogChanges) {
if (!kieWrapperComponentRules.isPresent()) {
return;
}
List<Component> components = componentDroolsExecutionService.executeRules(kieWrapperComponentRules.container(),
entityLogChanges.getEntityLog(),
addedFileAttributes.stream().toList());
log.info("Finished component rule execution for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
ComponentLog componentLog = componentLogCreatorService.buildComponentLog(analyzeRequest.getAnalysisNumber(), components);
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.COMPONENT_LOG, componentLog);
}
private EntityLogChanges finalizeEntityLog(AnalyzeRequest analyzeRequest, EntityLog entityLog, RedactionLog redactionLog, DictionaryVersion dictionaryVersion) {
redactionLog.setDictionaryVersion(dictionaryVersion.getDossierTemplateVersion());
redactionLog.setDossierDictionaryVersion(dictionaryVersion.getDossierVersion());
excludeExcludedPages(redactionLog, analyzeRequest.getExcludedPages());
EntityLogChanges entityLogChanges = entityChangeLogService.computeChanges(analyzeRequest.getDossierId(),
analyzeRequest.getFileId(),
entityLog,
analyzeRequest.getAnalysisNumber());
RedactionLogChanges redactionLogChange = redactionChangeLogService.computeChanges(analyzeRequest.getDossierId(),
analyzeRequest.getFileId(),
redactionLog,
analyzeRequest.getAnalysisNumber());
log.info("Created entity log for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.REDACTION_LOG, redactionLogChange.getRedactionLog());
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.ENTITY_LOG, entityLogChanges.getEntityLog());
return entityLogChanges;
}
private static List<SemanticNode> getSectionsToReAnalyse(Document document, Set<Integer> sectionsToReanalyseIds) {
return document.streamChildren().filter(section -> sectionsToReanalyseIds.contains(section.getTreeId().get(0))).collect(Collectors.toList());
}
private Set<Integer> getSectionsToReanalyseIds(AnalyzeRequest analyzeRequest, RedactionLog redactionLog, Document document, DictionaryIncrement dictionaryIncrement) {
private Set<Integer> getSectionsToReanalyseIds(AnalyzeRequest analyzeRequest, EntityLog entityLog, Document document, DictionaryIncrement dictionaryIncrement) {
return analyzeRequest.getSectionsToReanalyse().isEmpty() //
? sectionFinderService.findSectionsToReanalyse(dictionaryIncrement, redactionLog, document, analyzeRequest) //
? sectionFinderService.findSectionsToReanalyse(dictionaryIncrement, entityLog, document, analyzeRequest) //
: analyzeRequest.getSectionsToReanalyse();
}

View File

@ -2,28 +2,30 @@ package com.iqser.red.service.redaction.v1.server.service;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.kie.api.runtime.KieContainer;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentEntityReference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.redaction.v1.server.model.ManualEntity;
import com.iqser.red.service.redaction.v1.server.model.RectangleWithPage;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Component;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Entity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.redaction.v1.server.model.component.Component;
import com.iqser.red.service.redaction.v1.server.model.component.Entity;
import com.iqser.red.service.redaction.v1.server.service.document.EntityComparators;
@Service
public class ComponentLogCreatorService {
public ComponentLog buildComponentLog(KieContainer container, int analysisNumber, List<Component> components) {
public ComponentLog buildComponentLog(int analysisNumber, List<Component> components) {
List<ComponentLogEntry> componentLogEntries = components.stream().map(this::buildComponentLogEntry).toList();
return new ComponentLog(analysisNumber, componentLogEntries, -1, -1, -1);
List<ComponentLogCategory> componentLogCategories = components.stream()
.collect(Collectors.groupingBy(Component::getCategory, Collectors.mapping(this::buildComponentLogEntry, Collectors.toList())))
.entrySet()
.stream()
.map(entry -> new ComponentLogCategory(entry.getKey(), entry.getValue()))
.toList();
return new ComponentLog(analysisNumber, componentLogCategories, -1, -1, -1);
}
@ -31,8 +33,7 @@ public class ComponentLogCreatorService {
return ComponentLogEntry.builder()
.value(component.getValue())
.transformation(component.getTransformation())
.originalValue(component.getOriginalValue())
.transformation(component.getTransformation()).originalValue(component.getReferences().stream().sorted(EntityComparators.start()).map(Entity::getValue).toList())
.componentEntityReferences(toComponentEntityReferences(component.getReferences()))
.build();
}
@ -40,45 +41,19 @@ public class ComponentLogCreatorService {
private List<ComponentEntityReference> toComponentEntityReferences(List<Entity> references) {
return references.stream().flatMap(this::toComponentEntityReference).toList();
return references.stream().map(this::toComponentEntityReference).toList();
}
private Stream<ComponentEntityReference> toComponentEntityReference(Entity entity) {
private ComponentEntityReference toComponentEntityReference(Entity entity) {
if (entity instanceof TextEntity textEntity) {
textEntity.getPositionsOnPagePerPage()
.stream()
.map(positionOnPage -> ComponentEntityReference.builder()
.page(positionOnPage.getPage().getNumber())
.type(textEntity.getType())
.id(positionOnPage.getId())
.ruleIdentifier(textEntity.getMatchedRule().getRuleIdentifier().toString())
.reason(textEntity.buildReasonWithManualChangeDescriptions())
.build());
} else if (entity instanceof Image image) {
return Stream.of(ComponentEntityReference.builder()
.id(image.getId())
.reason(image.buildReasonWithManualChangeDescriptions())
.type(image.getImageType().toString())
.ruleIdentifier(image.getMatchedRule().getRuleIdentifier().toString())
.page(image.getFirstPage().getNumber())
.build());
} else if (entity instanceof ManualEntity manualEntity) {
return manualEntity.getEntityPosition()
.stream()
.collect(Collectors.groupingBy(RectangleWithPage::pageNumber))
.keySet()
.stream()
.map(pageNumber -> ComponentEntityReference.builder()
.id(manualEntity.getId())
.reason(manualEntity.buildReasonWithManualChangeDescriptions())
.type(manualEntity.getType())
.ruleIdentifier(manualEntity.getRuleIdentifier())
.page(pageNumber)
.build());
}
throw new UnsupportedOperationException(String.format("Entity with class %s is not implemented yet", entity.getClass()));
return ComponentEntityReference.builder()
.id(entity.getId())
.page(entity.getPositions().stream().findFirst().map(Position::getPageNumber).orElse(0))
.reason(entity.getReason())
.ruleIdentifier(entity.getMatchedRule())
.type(entity.getType())
.build();
}
}

View File

@ -0,0 +1,116 @@
package com.iqser.red.service.redaction.v1.server.service;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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.analysislog.entitylog.Change;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ChangeType;
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.EntityLogChanges;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import io.micrometer.core.annotation.Timed;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class EntityChangeLogService {
private final RedactionStorageService redactionStorageService;
@Timed("redactmanager_computeChanges")
public EntityLogChanges computeChanges(String dossierId, String fileId, EntityLog currentEntityLog, int analysisNumber) {
long start = System.currentTimeMillis();
EntityLog previousEntityLog = redactionStorageService.getEntityLog(dossierId, fileId);
if (previousEntityLog == null) {
currentEntityLog.getEntityLogEntry().forEach(entry -> {
entry.getChanges().add(new Change(analysisNumber, ChangeType.ADDED, OffsetDateTime.now()));
});
return new EntityLogChanges(currentEntityLog, false);
}
List<EntityLogEntry> previouslyExistingEntries = previousEntityLog.getEntityLogEntry().stream().filter(entry -> !entry.lastChangeIsRemoved()).toList();
Map<String, EntityLogEntry> addedEntryIds = getEntriesThatExistInCurrentButNotInPreviousEntityLog(currentEntityLog, previouslyExistingEntries);
Set<String> removedIds = getEntryIdsThatExistInPreviousButNotInCurrentEntityLog(currentEntityLog, previouslyExistingEntries);
List<EntityLogEntry> newEntityLogEntries = previousEntityLog.getEntityLogEntry();
List<EntityLogEntry> toRemove = new ArrayList<>();
newEntityLogEntries.forEach(entry -> {
if (removedIds.contains(entry.getId()) && addedEntryIds.containsKey(entry.getId())) {
List<Change> changes = entry.getChanges();
changes.add(new Change(analysisNumber, ChangeType.CHANGED, OffsetDateTime.now()));
var newEntry = addedEntryIds.get(entry.getId());
newEntry.setChanges(changes);
addedEntryIds.put(entry.getId(), newEntry);
toRemove.add(entry);
} else if (removedIds.contains(entry.getId())) {
entry.getChanges().add(new Change(analysisNumber, ChangeType.REMOVED, OffsetDateTime.now()));
} else if (addedEntryIds.containsKey(entry.getId())) {
List<Change> changes = entry.getChanges();
changes.add(new Change(analysisNumber, ChangeType.ADDED, OffsetDateTime.now()));
var newEntry = addedEntryIds.get(entry.getId());
newEntry.setChanges(changes);
addedEntryIds.put(entry.getId(), newEntry);
toRemove.add(entry);
}
});
newEntityLogEntries.removeAll(toRemove);
addedEntryIds.forEach((k, v) -> {
if (v.getChanges().isEmpty()) {
v.getChanges().add(new Change(analysisNumber, ChangeType.ADDED, OffsetDateTime.now()));
}
newEntityLogEntries.add(v);
});
currentEntityLog.setEntityLogEntry(newEntityLogEntries);
log.debug("Change computation took: {}", System.currentTimeMillis() - start);
return new EntityLogChanges(currentEntityLog, !addedEntryIds.isEmpty() || !removedIds.isEmpty());
}
private static Set<String> getEntryIdsThatExistInPreviousButNotInCurrentEntityLog(EntityLog currentEntityLog, List<EntityLogEntry> previouslyExistingEntries) {
Set<EntityLogEntry> removed = new HashSet<>(previouslyExistingEntries);
currentEntityLog.getEntityLogEntry().forEach(removed::remove);
Set<String> removedIds = removed.stream().map(EntityLogEntry::getId).collect(Collectors.toSet());
return removedIds;
}
private static Map<String, EntityLogEntry> getEntriesThatExistInCurrentButNotInPreviousEntityLog(EntityLog currentEntityLog, List<EntityLogEntry> previouslyExistingEntries) {
Set<EntityLogEntry> currentExistingEntries = currentEntityLog.getEntityLogEntry()
.stream()
.filter(entry -> entry.getChanges().isEmpty() || !entry.lastChangeIsRemoved())
.collect(Collectors.toSet());
previouslyExistingEntries.forEach(currentExistingEntries::remove);
Map<String, EntityLogEntry> addedIds = new HashMap<>();
currentExistingEntries.forEach(entry -> {
addedIds.put(entry.getId(), entry);
});
return addedIds;
}
}

View File

@ -16,7 +16,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.redaction.v1.server.model.ManualEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Entity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
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;
@ -188,13 +188,13 @@ public class EntityLogCreatorService {
}
private EntryState buildEntryState(Entity entity) {
private EntryState buildEntryState(IEntity IEntity) {
if (entity.applied()) {
if (IEntity.applied()) {
return EntryState.APPLIED;
} else if (entity.skipped()) {
} else if (IEntity.skipped()) {
return EntryState.SKIPPED;
} else if (entity.ignored()) {
} else if (IEntity.ignored()) {
return EntryState.IGNORED;
} else {
return EntryState.REMOVED;
@ -202,13 +202,13 @@ public class EntityLogCreatorService {
}
private EntryType buildEntryType(Entity entity, boolean isHint) {
private EntryType buildEntryType(IEntity IEntity, boolean isHint) {
if (entity instanceof TextEntity textEntity) {
if (IEntity instanceof TextEntity textEntity) {
return getEntryType(isHint, textEntity.getEntityType());
} else if (entity instanceof ManualEntity manualEntity) {
} else if (IEntity instanceof ManualEntity manualEntity) {
return getEntryType(isHint, manualEntity.getEntityType());
} else if (entity instanceof Image) {
} else if (IEntity instanceof Image) {
return EntryType.IMAGE;
}
throw new UnsupportedOperationException("Entity subclass %s not implemented!");

View File

@ -7,7 +7,7 @@ import java.util.NoSuchElementException;
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.ManualResizeRedaction;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Entity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
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.Image;
@ -24,14 +24,14 @@ public class ManualChangesApplicationService {
private final EntityCreationService entityCreationService;
public void recategorize(Entity entityToBeReCategorized, ManualRecategorization manualRecategorization) {
public void recategorize(IEntity IEntityToBeReCategorized, ManualRecategorization manualRecategorization) {
if (entityToBeReCategorized instanceof Image image) {
if (IEntityToBeReCategorized instanceof Image image) {
image.setImageType(ImageType.fromString(manualRecategorization.getType()));
return;
}
// need to create a new entity and copy over all values, since type is part of the primary key for entities and should never be changed!
if (entityToBeReCategorized instanceof TextEntity textEntity) {
if (IEntityToBeReCategorized instanceof TextEntity textEntity) {
TextEntity recategorizedEntity = entityCreationService.copyEntity(textEntity, manualRecategorization.getType(), textEntity.getEntityType(), textEntity.getDeepestFullyContainingNode());
recategorizedEntity.setPositionsOnPagePerPage(textEntity.getPositionsOnPagePerPage());
recategorizedEntity.getManualOverwrite().addChange(manualRecategorization);

View File

@ -1,5 +1,131 @@
package com.iqser.red.service.redaction.v1.server.service.document;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.kie.api.runtime.KieSession;
import com.iqser.red.service.redaction.v1.server.model.component.Component;
import com.iqser.red.service.redaction.v1.server.model.component.Entity;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleIdentifier;
import com.iqser.red.service.redaction.v1.server.utils.DateConverter;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class ComponentCreationService {
KieSession kieSession;
Set<Entity> referencedEntities = new HashSet<>();
public void firstOrElse(String ruleIdentifier, String category, Collection<Entity> entities, String fallback) {
String transformation = String.format("First found value or else '%s'", fallback);
String value = entities.stream().min(EntityComparators.start()).map(Entity::getValue).orElse(fallback);
createComponent(ruleIdentifier, category, value, transformation, entities);
}
public void joining(String ruleIdentifier, String category, Collection<Entity> entities) {
joining(ruleIdentifier, category, entities, ", ");
}
public void joining(String ruleIdentifier, String category, Collection<Entity> entities, String delimiter) {
String transformation = String.format("Joining all values with '%s'", delimiter);
String value = entities.stream().sorted(EntityComparators.start()).map(Entity::getValue).collect(Collectors.joining(delimiter));
createComponent(ruleIdentifier, category, value, transformation, entities);
}
public void joiningUnique(String ruleIdentifier, String category, Collection<Entity> entities) {
joining(ruleIdentifier, category, entities, ", ");
}
public void joiningUnique(String ruleIdentifier, String category, Collection<Entity> entities, String delimiter) {
String transformation = String.format("Joining all values with '%s'", delimiter);
String value = entities.stream().sorted(EntityComparators.start()).map(Entity::getValue).distinct().collect(Collectors.joining(delimiter));
createComponent(ruleIdentifier, category, value, transformation, entities);
}
public void convertDates(String ruleIdentifier, String category, Collection<Entity> entities) {
convertDates(ruleIdentifier, category, entities, "dd/MM/yyyy");
}
public void convertDates(String ruleIdentifier, String category, Collection<Entity> entities, String resultFormat) {
String transformation = "Convert values of type to dd/MM/yyyy joined with ', '";
String date = entities.stream().map(Entity::getValue).map(value -> DateConverter.convertDate(value, resultFormat)).collect(Collectors.joining(", "));
createComponent(ruleIdentifier, category, date, transformation, entities);
}
public void createComponentsForUnMappedEntities(String ruleIdentifier, Collection<Entity> entities) {
entities.forEach(entity -> createComponent(ruleIdentifier, entity.getType(), entity.getValue(), "Unmapped Entity", List.of(entity)));
}
public void createComponent(String ruleIdentifier, String category, String value, String transformation, Collection<Entity> references) {
referencedEntities.addAll(references);
kieSession.insert(Component.builder()
.matchedRule(RuleIdentifier.fromString(ruleIdentifier))
.category(category)
.value(value)
.transformation(transformation)
.references(new LinkedList<>(references)));
}
public void createComponent(String ruleIdentifier, String category, String value, String transformation, Entity reference) {
referencedEntities.add(reference);
List<Entity> referenceList = new LinkedList<>();
referenceList.add(reference);
kieSession.insert(Component.builder()
.matchedRule(RuleIdentifier.fromString(ruleIdentifier))
.category(category)
.value(value)
.transformation(transformation)
.references(referenceList)
.build());
}
public void createComponent(String ruleIdentifier, String category, String value, String transformation) {
createComponent(ruleIdentifier, category, value, transformation, Collections.emptyList());
}
public void createComponent(String ruleIdentifier, String category, String value) {
kieSession.insert(Component.builder()
.matchedRule(RuleIdentifier.fromString(ruleIdentifier))
.category(category)
.value(value)
.transformation("")
.references(Collections.emptyList())
.build());
}
}

View File

@ -0,0 +1,41 @@
package com.iqser.red.service.redaction.v1.server.service.document;
import java.util.Comparator;
import com.iqser.red.service.redaction.v1.server.model.component.Entity;
public abstract class EntityComparators implements Comparator<Entity> {
private static class LongestEntity implements Comparator<Entity> {
@Override
public int compare(Entity Entity, Entity otherEntity) {
return Integer.compare(Entity.getLength(), otherEntity.getLength());
}
}
private static class FirstEntity implements Comparator<Entity> {
@Override
public int compare(Entity Entity, Entity otherEntity) {
return Integer.compare(Entity.getStartOffset(), otherEntity.getStartOffset());
}
}
public static Comparator<Entity> length() {
return new LongestEntity();
}
public static Comparator<Entity> start() {
return new FirstEntity();
}
}

View File

@ -1,4 +1,4 @@
package com.iqser.red.service.redaction.v1.server.service;
package com.iqser.red.service.redaction.v1.server.service.document;
import java.util.Collection;
import java.util.Collections;
@ -8,9 +8,11 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeRequest;
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.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
@ -19,13 +21,11 @@ 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.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.RedactionLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLogEntry;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrement;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrementValue;
import com.iqser.red.service.redaction.v1.server.model.dictionary.SearchImplementation;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType;
import io.micrometer.core.annotation.Timed;
import lombok.AccessLevel;
@ -34,18 +34,18 @@ import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@Service
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
class SectionFinderService {
public class SectionFinderService {
@Timed("redactmanager_findSectionsToReanalyse")
public Set<Integer> findSectionsToReanalyse(DictionaryIncrement dictionaryIncrement, RedactionLog redactionLog, Document document, AnalyzeRequest analyzeRequest) {
public Set<Integer> findSectionsToReanalyse(DictionaryIncrement dictionaryIncrement, EntityLog entityLog, Document document, AnalyzeRequest analyzeRequest) {
long start = System.currentTimeMillis();
Set<String> relevantManuallyModifiedAnnotationIds = getRelevantManuallyModifiedAnnotationIds(analyzeRequest.getManualRedactions());
Set<Integer> sectionsToReanalyse = new HashSet<>();
for (RedactionLogEntry entry : redactionLog.getRedactionLogEntry()) {
for (EntityLogEntry entry : entityLog.getEntityLogEntry()) {
if (relevantManuallyModifiedAnnotationIds.contains(entry.getId())) {
sectionsToReanalyse.add(entry.getSectionNumber());
}

View File

@ -1,16 +1,26 @@
package com.iqser.red.service.redaction.v1.server.service.drools;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.QueryResults;
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.redaction.v1.server.model.ManualEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Component;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.model.component.Component;
import com.iqser.red.service.redaction.v1.server.model.component.Entity;
import com.iqser.red.service.redaction.v1.server.service.document.ComponentCreationService;
import com.iqser.red.service.redaction.v1.server.utils.exception.DroolsTimeoutException;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@ -23,9 +33,61 @@ import lombok.extern.slf4j.Slf4j;
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class ComponentDroolsExecutionService {
public List<Component> executeRules(Set<TextEntity> entities, List<FileAttribute> allFileAttributes, List<ManualEntity> notFoundManualRedactionEntries, List<Image> list) {
RedactionServiceSettings settings;
return Collections.emptyList();
public List<Component> executeRules(KieContainer kieContainer, EntityLog entityLog, List<FileAttribute> fileAttributes) {
KieSession kieSession = kieContainer.newKieSession();
ComponentCreationService componentCreationService = new ComponentCreationService(kieSession);
kieSession.setGlobal("componentCreationService", componentCreationService);
entityLog.getEntityLogEntry().stream().map(Entity::fromEntityLogEntry).forEach(kieSession::insert);
fileAttributes.stream().filter(f -> f.getValue() != null).forEach(kieSession::insert);
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
kieSession.fireAllRules();
return null;
});
try {
completableFuture.orTimeout(settings.getDroolsExecutionTimeoutSecs(), TimeUnit.SECONDS).get();
} catch (ExecutionException e) {
kieSession.dispose();
if (e.getCause() instanceof TimeoutException) {
throw new DroolsTimeoutException(e, false, RuleFileType.COMPONENT);
}
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
List<FileAttribute> resultingFileAttributes = getFileAttributes(kieSession);
List<Component> components = getComponents(kieSession);
kieSession.dispose();
return components;
}
public List<FileAttribute> getFileAttributes(KieSession kieSession) {
List<FileAttribute> fileAttributes = new LinkedList<>();
QueryResults entitiesResult = kieSession.getQueryResults("getFileAttributes");
for (QueryResultsRow resultsRow : entitiesResult) {
fileAttributes.add((FileAttribute) resultsRow.get("$fileAttribute"));
}
return fileAttributes;
}
public List<Component> getComponents(KieSession kieSession) {
List<Component> components = new LinkedList<>();
QueryResults entitiesResult = kieSession.getQueryResults("getComponents");
for (QueryResultsRow resultsRow : entitiesResult) {
components.add((Component) resultsRow.get("$component"));
}
return components;
}
}

View File

@ -47,6 +47,16 @@ public class RedactionStorageService {
}
@SneakyThrows
public File getStoredObjectFile(String storageId) {
File tempFile = File.createTempFile("temp", "data");
tempFile.deleteOnExit();
storageService.downloadTo(TenantContext.getTenantId(), storageId, tempFile);
return tempFile;
}
@SneakyThrows
public void storeObject(String dossierId, String fileId, FileType fileType, InputStream inputStream) {

View File

@ -0,0 +1,49 @@
package com.iqser.red.service.redaction.v1.server.utils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@UtilityClass
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class DateConverter {
static List<SimpleDateFormat> formats = List.of(new SimpleDateFormat("dd MMM yy", Locale.ENGLISH),
new SimpleDateFormat("dd MM yyyy", Locale.ENGLISH),
new SimpleDateFormat("dd MM yyyy.", Locale.ENGLISH),
new SimpleDateFormat("dd MMMM yyyy", Locale.ENGLISH),
new SimpleDateFormat("MMMM dd, yyyy", Locale.ENGLISH),
new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH));
public String convertDate(String dateAsString, String resultFormat) {
DateFormat resultDateFormat = new SimpleDateFormat(resultFormat, Locale.ENGLISH);
Date date = null;
for (SimpleDateFormat format : formats) {
try {
date = format.parse(dateAsString);
break;
} catch (Exception e) {
log.warn("Failed to parse date from string {}. \n{}", dateAsString, e.getMessage());
// ignore, try next...
}
}
if (date == null) {
return dateAsString;
}
return resultDateFormat.format(date);
}
}

View File

@ -43,6 +43,7 @@ import com.knecon.fforesight.tenantcommons.TenantContext;
public class DocumineFloraTest extends AbstractRedactionIntegrationTest {
private static final String RULES = loadFromClassPath("drools/documine_flora.drl");
private static final String COMPONENT_RULES = loadFromClassPath("drools/documine_flora_components.drl");
@Test
@ -148,7 +149,8 @@ public class DocumineFloraTest extends AbstractRedactionIntegrationTest {
when(rulesClient.getVersion(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(System.currentTimeMillis());
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(RULES));
doThrow(ExceptionProvider.getMockNotFoundFeignException()).when(rulesClient).getVersion(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.COMPONENT);
when(rulesClient.getVersion(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.COMPONENT)).thenReturn(System.currentTimeMillis());
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.COMPONENT)).thenReturn(JSONPrimitive.of(COMPONENT_RULES));
loadTypeForTest();
loadNerForTest();

View File

@ -13,7 +13,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
@ -36,10 +35,10 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlo
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.CellRectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionGrid;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionRectangle;
import com.iqser.red.service.redaction.v1.server.utils.RectangleTransformations;
import com.iqser.red.service.redaction.v1.server.utils.exception.RedactionException;
import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import com.iqser.red.service.redaction.v1.server.utils.RectangleTransformations;
import com.iqser.red.service.redaction.v1.server.utils.exception.RedactionException;
import lombok.RequiredArgsConstructor;
@ -67,14 +66,14 @@ public class AnnotationService {
*/
public AnnotateResponse annotate(AnnotateRequest annotateRequest) {
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(annotateRequest.getDossierId(),
var storedObjectFile = redactionStorageService.getStoredObjectFile(RedactionStorageService.StorageIdUtils.getStorageId(annotateRequest.getDossierId(),
annotateRequest.getFileId(),
FileType.ORIGIN));
var redactionLog = redactionStorageService.getRedactionLog(annotateRequest.getDossierId(), annotateRequest.getFileId());
var sectionsGrid = redactionStorageService.getSectionGrid(annotateRequest.getDossierId(), annotateRequest.getFileId());
try (PDDocument pdDocument = Loader.loadPDF(storedObjectStream, MemoryUsageSetting.setupTempFileOnly())) {
try (PDDocument pdDocument = Loader.loadPDF(storedObjectFile)) {
pdDocument.setAllSecurityToBeRemoved(true);
dictionaryService.updateDictionary(annotateRequest.getDossierTemplateId(), annotateRequest.getDossierId());

View File

@ -32,7 +32,7 @@ import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBl
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
public class DocumentEntityInsertionIntegrationTest extends BuildDocumentIntegrationTest {
public class DocumentIEntityInsertionIntegrationTest extends BuildDocumentIntegrationTest {
@Autowired
private EntityEnrichmentService entityEnrichmentService;

View File

@ -255,7 +255,7 @@ public class DocumentPerformanceIntegrationTest extends BuildDocumentIntegration
var tmpFileName = "/tmp/" + filename.split("/")[2] + "_ENTITY_BBOX.pdf";
var fileResource = new ClassPathResource(filename + ".pdf");
try (var fileStream = fileResource.getInputStream(); PDDocument pdDocument = Loader.loadPDF(fileStream)) {
try (PDDocument pdDocument = Loader.loadPDF(fileResource.getFile())) {
for (Page page : document.getPages()) {
List<Rectangle2D> entityPositionsOnPage = page.getEntities()

View File

@ -79,7 +79,7 @@ public class DocumentVisualizationIntegrationTest extends BuildDocumentIntegrati
var tmpFileName = "/tmp/" + filename.split("/")[2] + "_SEMANTIC_NODES_BBOX.pdf";
var fileResource = new ClassPathResource(filename + ".pdf");
try (var fileStream = fileResource.getInputStream(); PDDocument pdDocument = Loader.loadPDF(fileStream)) {
try (PDDocument pdDocument = Loader.loadPDF(fileResource.getFile())) {
PdfVisualisationUtility.drawDocumentGraph(pdDocument, document);
PdfVisualisationUtility.drawTextBlock(pdDocument,

View File

@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.mock.mockito.MockBean;
@ -154,4 +155,12 @@ class DroolsSyntaxValidationServiceTest {
assertFalse(droolsSyntaxValidation.isCompiled());
}
@Test
@SneakyThrows
@Disabled
void fixAllRuleFiles() {
}
}

View File

@ -71,7 +71,7 @@ class NerEntitiesAdapterTest extends BuildDocumentIntegrationTest {
assertTrue(entityRecognitionEntities.stream().allMatch(entity -> entity.textRange().start() < entity.textRange().end()));
ClassPathResource resource = new ClassPathResource(filePath);
try (PDDocument pdDocument = Loader.loadPDF(resource.getInputStream())) {
try (PDDocument pdDocument = Loader.loadPDF(resource.getFile())) {
Stream<NerEntities.NerEntity> unchangedAddressParts = NerEntitiesAdapter.toNerEntities(parseNerEntities(nerEntitiesFilePath), document)
.getNerEntityList()
@ -120,7 +120,7 @@ class NerEntitiesAdapterTest extends BuildDocumentIntegrationTest {
assertTrue(cbiAddressEntities.stream().allMatch(entity -> entity.getTextRange().start() < entity.getTextRange().end()));
ClassPathResource resource = new ClassPathResource(filePath);
try (PDDocument pdDocument = Loader.loadPDF(resource.getInputStream())) {
try (PDDocument pdDocument = Loader.loadPDF(resource.getFile())) {
List<TextEntity> validatedEntities = NerEntitiesAdapter.toNerEntities(parseNerEntities(nerEntitiesFilePath), document)
.getNerEntityList()

View File

@ -21,6 +21,7 @@ public class LayoutParsingRequestProvider {
var positionBlockFileStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.DOCUMENT_POSITION);
var pageFileStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.DOCUMENT_PAGES);
var simplifiedTextStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.SIMPLIFIED_TEXT);
var viewerDocumentStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.VIEWER_DOCUMENT);
return LayoutParsingRequest.builder()
.layoutParsingType(layoutParsingType)
.originFileStorageId(originFileStorageId)
@ -31,7 +32,7 @@ public class LayoutParsingRequestProvider {
.positionBlockFileStorageId(positionBlockFileStorageId)
.pageFileStorageId(pageFileStorageId)
.sectionGridStorageId(sectionGridStorageId)
.simplifiedTextStorageId(simplifiedTextStorageId)
.simplifiedTextStorageId(simplifiedTextStorageId).viewerDocumentStorageId(viewerDocumentStorageId)
.build();
}

View File

@ -0,0 +1,558 @@
package drools
import static java.lang.String.format;
import static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.anyMatch;
import static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.exactMatch;
import java.util.List;
import java.util.LinkedList;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.Collection;
import java.util.stream.Stream;
import java.util.Optional;
import com.iqser.red.service.redaction.v1.server.model.component.Component;
import com.iqser.red.service.redaction.v1.server.model.component.Entity;
import com.iqser.red.service.redaction.v1.server.service.document.ComponentCreationService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Change;
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.EntryState;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;
global ComponentCreationService componentCreationService
//------------------------------------ queries ------------------------------------
query "getFileAttributes"
$fileAttribute: FileAttribute()
end
query "getComponents"
$component: Component()
end
//------------------------------------ Default Components rules ------------------------------------
rule "StudyTitle.0.0: Study Title"
when
$titleCandidates: List() from collect (Entity(type == "study_title"))
then
componentCreationService.firstOrElse("StudyTitle.0.0", "Study_Title", $titleCandidates, "No study title found!");
end
rule "PerformingLaboratory.0.0: Performing Laboratory"
when
$laboratoryName: Entity(type == "laboratory_name")
$laboratoryCountry: Entity(type == "laboratory_country", $distance: Math.abs($laboratoryName.startOffset - startOffset))
not Entity(type == "laboratory_country", Math.abs($laboratoryName.startOffset - startOffset) < $distance)
eval($distance < 80)
then
componentCreationService.createComponent("PerformingLaboratory.0.0", "Performing_Laboratory", $laboratoryName.getValue() + ", " + $laboratoryCountry.getValue(), "Laboratory name and country found!");
end
rule "PerformingLaboratory.0.1: Performing Laboratory"
when
$laboratoryName: Entity(type == "laboratory_name")
not Entity(type == "laboratory_country", Math.abs($laboratoryName.startOffset - startOffset) < 80)
then
componentCreationService.createComponent("DefaultComponents.0.1", "Performing_Laboratory", $laboratoryName.getValue(), "Only laboratory name found!");
end
rule "PerformingLaboratory.0.2: Performing Laboratory"
salience -1
when
not Component(category == "Performing_Laboratory")
then
componentCreationService.createComponent("PerformingLaboratory.0.2", "Performing_Laboratory", "n-a", "fallback");
end
rule "ReportNumber.0.0: Report number"
when
$reportNumberCandidates: List() from collect (Entity(type == "report_number"))
then
componentCreationService.firstOrElse("ReportNumber.0.0", "Report_number", $reportNumberCandidates, "No report number found!");
end
rule "GLPStudy.0.0: GLP Study"
when
$glpStudy: Entity(type() == "glp_study")
then
componentCreationService.createComponent("GLPStudy.0.0", "GLP_study", "Yes", "Yes if present, No if not", $glpStudy);
end
rule "GLPStudy.1.0: GLP Study"
when
not Entity(type() == "glp_study")
then
componentCreationService.createComponent("GLPStudy.1.0", "GLP_study", "No", "Yes if present, No if not");
end
rule "GLPStudy.2.0: GLP Study"
when
$first: Component(category == "GLP_study")
$second: Component(category == "GLP_study", value == $first.value, this != $first)
then
$first.addReferences($second.getReferences());
retract($second);
end
rule "DefaultComponents.4.0: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number")
not Entity(type == "oecd_guideline_year")
then
componentCreationService.createComponent("DefaultComponents.4.0", "Test_Guidelines_1", $guidelineNumber.getValue(), "Only OECD Number found!");
end
rule "DefaultComponents.4.1: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2015")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "404")
then
componentCreationService.createComponent(
"DefaultComponents.4.1",
"Test_Guidelines_1",
"Nº 404: Acute Dermal Irritation/Corrosion (28/07/2015)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.2: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "1981")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "403")
then
componentCreationService.createComponent(
"DefaultComponents.4.2",
"Test_Guidelines_1",
"Nº 403: Acute Inhalation Toxicity (12/05/1981)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.3: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2018")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "442B")
then
componentCreationService.createComponent(
"DefaultComponents.4.3",
"Test_Guidelines_1",
"Nº 442B: Skin Sensitization (27/06/2018)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.4: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2016")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "487")
then
componentCreationService.createComponent(
"DefaultComponents.4.4",
"Test_Guidelines_1",
"Nº 487: Micronucleus Human Lymphocytes (2016)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.5: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2004")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "428")
then
componentCreationService.createComponent(
"DefaultComponents.4.5",
"Test_Guidelines_1",
"Nº 428: Split-Thickness Skin test (2004)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.6: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2018")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "442A")
then
componentCreationService.createComponent(
"DefaultComponents.4.6",
"Test_Guidelines_1",
"Nº 442A: Skin Sensitization (23/07/2018)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.7: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2002")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "404")
then
componentCreationService.createComponent(
"DefaultComponents.4.7",
"Test_Guidelines_1",
"Nº 404: Acute Dermal Irritation/Corrosion (24/04/2002)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.8: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "1992")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "406")
then
componentCreationService.createComponent(
"DefaultComponents.4.8",
"Test_Guidelines_1",
"Nº 406: Skin Sensitisation (1992)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.9: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2018")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "438")
then
componentCreationService.createComponent(
"DefaultComponents.4.9",
"Test_Guidelines_1",
"Nº 438: Eye Irritation (26/06/2018)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.10: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2009")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "436")
then
componentCreationService.createComponent(
"DefaultComponents.4.10",
"Test_Guidelines_1",
"Nº 436: Acute Inhalation Toxicity Acute Toxic Class Method (08/09/2009)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.11: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2002")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "429")
then
componentCreationService.createComponent(
"DefaultComponents.4.11",
"Test_Guidelines_1",
"Nº 429: Skin Sensitisation: Local Lymph Node Assay (24/04/2002)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.12: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2020")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "471")
then
componentCreationService.createComponent(
"DefaultComponents.4.12",
"Test_Guidelines_1",
"Nº 471: Bacterial Reverse Mutation Test (26/06/2020)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.13: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "1987")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "405")
then
componentCreationService.createComponent(
"DefaultComponents.4.13",
"Test_Guidelines_1",
"Nº 405: Acute Eye Irritation/Corrosion (24/02/1987)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.14: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "1997")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "471")
then
componentCreationService.createComponent(
"DefaultComponents.4.14",
"Test_Guidelines_1",
"Nº 471: Bacterial Reverse Mutation Test (21/07/1997)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.15: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2009")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "403")
then
componentCreationService.createComponent(
"DefaultComponents.4.15",
"Test_Guidelines_1",
"Nº 403: Acute Inhalation Toxicity (08/09/2009)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.16: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2018")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "433")
then
componentCreationService.createComponent(
"DefaultComponents.4.16",
"Test_Guidelines_1",
"Nº 433: Acute Inhalation Toxicity: Fixed Concentration Procedure (27/06/2018)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.17: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2019")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "439")
then
componentCreationService.createComponent(
"DefaultComponents.4.17",
"Test_Guidelines_1",
"Nº 439: Skin Irritation (2019)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.18: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2001")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "425")
then
componentCreationService.createComponent(
"DefaultComponents.4.18",
"Test_Guidelines_1",
"Nº 425: Acute oral Toxicity - Up-and-Down Procedure (17/12/2001)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.19: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2017")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "402")
then
componentCreationService.createComponent(
"DefaultComponents.4.19",
"Test_Guidelines_1",
"Nº 402: Acute Dermal Toxicity (09/10/2017)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.20: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "1981")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "404")
then
componentCreationService.createComponent(
"DefaultComponents.4.20",
"Test_Guidelines_1",
"Nº 404: Acute Dermal Irritation/Corrosion (12/05/1981)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.21: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2016")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "474")
then
componentCreationService.createComponent(
"DefaultComponents.4.21",
"Test_Guidelines_1",
"Nº 474: Micronucleus Bone Marrow Cells Rat (2016)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.22: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2017")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "433")
then
componentCreationService.createComponent(
"DefaultComponents.4.22",
"Test_Guidelines_1",
"Nº 433: Acute Inhalation Toxicity: Fixed Concentration Procedure (09/10/2017)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.23: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "1992")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "404")
then
componentCreationService.createComponent(
"DefaultComponents.4.23",
"Test_Guidelines_1",
"Nº 404: Acute Dermal Irritation/Corrosion (17/07/1992)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.24: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2010")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "429")
then
componentCreationService.createComponent(
"DefaultComponents.4.24",
"Test_Guidelines_1",
"Nº 429: Skin Sensitisation (23/07/2010)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.25: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2017")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "405")
then
componentCreationService.createComponent(
"DefaultComponents.4.25",
"Test_Guidelines_1",
"Nº 405: Acute Eye Irritation/Corrosion (09/10/2017)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.26: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "1987")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "402")
then
componentCreationService.createComponent(
"DefaultComponents.4.26",
"Test_Guidelines_1",
"Nº 402: Acute Dermal Toxicity (24/02/1987)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.27: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2012")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "405")
then
componentCreationService.createComponent(
"DefaultComponents.4.27",
"Test_Guidelines_1",
"Nº 405: Acute Eye Irritation/Corrosion (02/10/2012)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.28: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2002")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "405")
then
componentCreationService.createComponent(
"DefaultComponents.4.28",
"Test_Guidelines_1",
"Nº 405: Acute Eye Irritation/Corrosion (24/04/2002)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.4.29: Test Guideline 1"
when
$guidelineNumber: Entity(type == "oecd_guideline_number", value == "2008")
$guidelineYear: Entity(type == "oecd_guideline_year", value == "425")
then
componentCreationService.createComponent(
"DefaultComponents.4.29",
"Test_Guidelines_1",
"Nº 425: Acute oral Toxicity - Up-and-Down Procedure (03/10/2008)",
"OECD Number and guideline year mapped!"
);
end
rule "DefaultComponents.5.0: Test Guideline 2"
when
$epaGuideLines: List() from collect (Entity(type() == "epa_guideline"))
$ecGuideLines: List() from collect (Entity(type() == "ec_guideline"))
then
componentCreationService.joining("DefaultComponents.5.0",
"Test_Guideline_2",
Stream.of(
$epaGuideLines.stream(),
$ecGuideLines.stream())
.flatMap(a -> a)
.toList()
);
end
rule "DefaultComponents.6.0: Experimental Starting Date"
when
$startDates: List(!isEmpty()) from collect (Entity(type() == "experimental_start_date"))
then
componentCreationService.convertDates("DefaultComponents.6.0", "Experimental_Starting_Date", $startDates);
end
rule "DefaultComponents.7.0: Experimental Completion Date"
when
$endDates: List(!isEmpty()) from collect (Entity(type() == "experimental_end_date"))
then
componentCreationService.convertDates("DefaultComponents.7.0", "Experimental_Completion_Date", $endDates);
end
rule "DefaultComponents.8.0: Certificate of analysis batch identification"
when
$batchNumbers: List(!isEmpty()) from collect (Entity(type() == "batch_number"))
then
componentCreationService.joiningUnique("DefaultComponents.8.0", "Batch_Number", $batchNumbers);
end
rule "DefaultComponents.999.0: Create components for all unmapped entities."
salience -999
when
$allEntities: List(!isEmpty()) from collect (Entity()
then
componentCreationService.createComponentsForUnMappedEntities("DefaultComponents.999.0", $allEntities);
end
//------------------------------------ Component merging rules ------------------------------------
rule "X.0.0: merge duplicate component references"
when
$first: Component()
$duplicate: Component(this != $first, category == category, value == value)
then
$first.getReferences().addAll($duplicate.getReferences());
retract($duplicate);
end

View File

@ -58,6 +58,7 @@ global Document document
global EntityCreationService entityCreationService
global ManualChangesApplicationService manualChangesApplicationService
global Dictionary dictionary
//------------------------------------ queries ------------------------------------
query "getFileAttributes"

View File

@ -58,6 +58,7 @@ global Document document
global EntityCreationService entityCreationService
global ManualChangesApplicationService manualChangesApplicationService
global Dictionary dictionary
// --------------------------------------- queries -------------------------------------------------------------------
query "getFileAttributes"

View File

@ -47,7 +47,7 @@ import com.iqser.red.service.redaction.v1.server.document.graph.nodes.ImageType
import com.iqser.red.service.redaction.v1.server.document.graph.entity.EntityType
import com.iqser.red.service.redaction.v1.server.document.graph.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType
global Document document
global EntityCreationService entityCreationService