Compare commits
89
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
debc014cf0 | ||
|
|
a8ad213cad | ||
|
|
436d0db23c | ||
|
|
1a5a2dbe10 | ||
|
|
f05f1fd9b3 | ||
|
|
91385de867 | ||
|
|
02fda23461 | ||
|
|
989eb2145e | ||
|
|
4b2db3126d | ||
|
|
f1deb374ba | ||
|
|
3bd1fbebae | ||
|
|
8961abc4fe | ||
|
|
b01288d85e | ||
|
|
e6c048d6df | ||
|
|
93c1a2b90a | ||
|
|
07d248c157 | ||
|
|
fa870fa856 | ||
|
|
a2e72f8a62 | ||
|
|
7a9b1c65d7 | ||
|
|
960daf6840 | ||
|
|
a1ffbab5a6 | ||
|
|
a2a3e84031 | ||
|
|
22ec044503 | ||
|
|
72f324bb11 | ||
|
|
5031b17a7d | ||
|
|
8187983944 | ||
|
|
99951dd1bb | ||
|
|
67184eb861 | ||
|
|
a72d96c646 | ||
|
|
e5641d7097 | ||
|
|
62c0a7eb09 | ||
|
|
7dbc585274 | ||
|
|
981901012c | ||
|
|
9b7e5bcca1 | ||
|
|
8e8864fdfa | ||
|
|
e3031aa716 | ||
|
|
793bbc6bde | ||
|
|
895d56c05f | ||
|
|
bbac9866d9 | ||
|
|
0e4e615d4a | ||
|
|
c5b83bf6e0 | ||
|
|
455137131c | ||
|
|
097bc34568 | ||
|
|
3deb002eb7 | ||
|
|
d8684595a3 | ||
|
|
3bfd41bd5e | ||
|
|
b9ce6f8c85 | ||
|
|
5647bc16f5 | ||
|
|
b55f33e39a | ||
|
|
82d4fd567b | ||
|
|
7868b56993 | ||
|
|
afc83db249 | ||
|
|
0d53e65a0c | ||
|
|
f641824270 | ||
|
|
07d6fea992 | ||
|
|
88888545e5 | ||
|
|
7777e74a44 | ||
|
|
187f7c95e0 | ||
|
|
69d0ab0754 | ||
|
|
9101b53970 | ||
|
|
3579c06033 | ||
|
|
dbb89321c4 | ||
|
|
1c3c632fd2 | ||
|
|
76f587aae4 | ||
|
|
dc7910cd07 | ||
|
|
d877b362ef | ||
|
|
364f994ffd | ||
|
|
4291fe56f6 | ||
|
|
ec04a902d5 | ||
|
|
a5969dbf8c | ||
|
|
0b833f2f22 | ||
|
|
bee535715d | ||
|
|
8362296edd | ||
|
|
ec6cf3efb0 | ||
|
|
cde17075e5 | ||
|
|
27ae7d6d96 | ||
|
|
62ccf46069 | ||
|
|
2bc3440a14 | ||
|
|
8820eb696c | ||
|
|
172c9dc3ee | ||
|
|
d488bee1bb | ||
|
|
b99d9ed59c | ||
|
|
8175f6d012 | ||
|
|
8f6b242c40 | ||
|
|
12a22bbb47 | ||
|
|
e620412f09 | ||
|
|
ac38a966c5 | ||
|
|
89227ec850 | ||
|
|
80fdff803d |
@@ -21,16 +21,3 @@ deploy:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
- if: $CI_COMMIT_BRANCH =~ /^release/
|
||||
- if: $CI_COMMIT_TAG
|
||||
|
||||
pages:
|
||||
stage: build
|
||||
tags:
|
||||
- dind
|
||||
script:
|
||||
- echo "Generating Javadoc..."
|
||||
- gradle generateJavaDoc -PjavadocDestinationDir="javadoc"
|
||||
- mkdir public
|
||||
- mv redaction-service-v1/redaction-service-server-v1/javadoc/* public/
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
|
||||
@@ -7,7 +7,7 @@ description = "redaction-service-api-v1"
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework:spring-web:6.0.12")
|
||||
implementation("com.iqser.red.service:persistence-service-internal-api-v1:2.351.0")
|
||||
implementation("com.iqser.red.service:persistence-service-internal-api-v1:2.349.56")
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
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 DroolsSyntaxDeprecatedWarnings {
|
||||
|
||||
Integer line;
|
||||
Integer column;
|
||||
String message;
|
||||
}
|
||||
-2
@@ -19,8 +19,6 @@ public class DroolsSyntaxValidation {
|
||||
|
||||
@Builder.Default
|
||||
List<DroolsSyntaxErrorMessage> droolsSyntaxErrorMessages = new LinkedList<>();
|
||||
@Builder.Default
|
||||
List<DroolsSyntaxDeprecatedWarnings> droolsSyntaxDeprecatedWarnings = new LinkedList<>();
|
||||
|
||||
|
||||
public void addErrorMessage(int line, int column, String message) {
|
||||
|
||||
+13
@@ -1,11 +1,15 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@@ -13,9 +17,18 @@ import lombok.NoArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
public class MigrationRequest {
|
||||
|
||||
@NonNull
|
||||
String dossierTemplateId;
|
||||
@NonNull
|
||||
String dossierId;
|
||||
@NonNull
|
||||
String fileId;
|
||||
|
||||
boolean fileIsApproved;
|
||||
@NonNull
|
||||
ManualRedactions manualRedactions;
|
||||
@NonNull
|
||||
@Builder.Default
|
||||
Set<String> entitiesWithComments = Collections.emptySet();
|
||||
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ plugins {
|
||||
description = "redaction-service-server-v1"
|
||||
|
||||
|
||||
val layoutParserVersion = "0.94.0"
|
||||
val layoutParserVersion = "0.89.11"
|
||||
val jacksonVersion = "2.15.2"
|
||||
val droolsVersion = "9.44.0.Final"
|
||||
val pdfBoxVersion = "3.0.0"
|
||||
val persistenceServiceVersion = "2.360.0"
|
||||
val persistenceServiceVersion = "2.349.56"
|
||||
val springBootStarterVersion = "3.1.5"
|
||||
|
||||
configurations {
|
||||
@@ -60,8 +60,6 @@ dependencies {
|
||||
implementation("net.logstash.logback:logstash-logback-encoder:7.4")
|
||||
implementation("ch.qos.logback:logback-classic")
|
||||
|
||||
implementation("org.reflections:reflections:0.10.2")
|
||||
|
||||
testImplementation(project(":rules-management"))
|
||||
testImplementation("org.apache.pdfbox:pdfbox:${pdfBoxVersion}")
|
||||
testImplementation("org.apache.pdfbox:pdfbox-tools:${pdfBoxVersion}")
|
||||
@@ -84,6 +82,9 @@ tasks.test {
|
||||
maxHeapSize = "1024m"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
tasks.named<BootBuildImage>("bootBuildImage") {
|
||||
|
||||
environment.put("BPE_DELIM_JAVA_TOOL_OPTIONS", " ")
|
||||
@@ -110,40 +111,3 @@ tasks.named<BootBuildImage>("bootBuildImage") {
|
||||
tags.set(listOf(dockerTag))
|
||||
}
|
||||
}
|
||||
|
||||
fun parseDroolsImports(droolsFilePath: String): List<String> {
|
||||
val imports = mutableListOf<String>()
|
||||
val importPattern = Regex("^import\\s+(com\\.iqser\\.red\\.service\\.redaction\\.v1\\.[\\w.]+);")
|
||||
val desiredPrefix = "com.iqser.red.service.redaction.v1"
|
||||
|
||||
File(droolsFilePath).forEachLine { line ->
|
||||
importPattern.find(line)?.let { matchResult ->
|
||||
val importPath = matchResult.groupValues[1].trim()
|
||||
if (importPath.startsWith(desiredPrefix)) {
|
||||
val formattedPath = importPath.replace('.', '/')
|
||||
imports.add("$formattedPath.java")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imports
|
||||
}
|
||||
|
||||
val droolsImports = parseDroolsImports("redaction-service-v1/redaction-service-server-v1/src/main/resources/drools/all_rules_documine.drl")
|
||||
|
||||
tasks.register("generateJavaDoc", Javadoc::class) {
|
||||
dependsOn("compileJava")
|
||||
dependsOn("delombok")
|
||||
classpath = project.sourceSets["main"].runtimeClasspath
|
||||
source = fileTree("${buildDir}/generated/sources/delombok/java/main") {
|
||||
include(droolsImports)
|
||||
}
|
||||
destinationDir = file(project.findProperty("javadocDestinationDir")?.toString() ?: "")
|
||||
|
||||
options.memberLevel = JavadocMemberLevel.PUBLIC
|
||||
(options as StandardJavadocDocletOptions).apply {
|
||||
header = "Redaction Service ${project.version}"
|
||||
footer = "Redaction Service ${project.version}"
|
||||
title = "API Documentation for Redaction Service ${project.version}"
|
||||
}
|
||||
}
|
||||
|
||||
-8
@@ -46,18 +46,10 @@ public class Application {
|
||||
return new TimedAspect(registry);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public DictionaryMergeService dictionaryMergeService() {
|
||||
|
||||
return new DictionaryMergeService();
|
||||
}
|
||||
|
||||
|
||||
@Bean(initMethod = "findDeprecatedMethods")
|
||||
public DeprecatedElementsFinder deprecatedElementsFinder() {
|
||||
|
||||
return new DeprecatedElementsFinder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
package com.iqser.red.service.redaction.v1.server;
|
||||
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
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.reflections.Reflections;
|
||||
import org.reflections.scanners.Scanners;
|
||||
import org.reflections.util.ConfigurationBuilder;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.model.dictionary.SearchImplementation;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class DeprecatedElementsFinder {
|
||||
|
||||
public static final String PACKAGE_NAME = "com.iqser.red.service.redaction.v1.server";
|
||||
private Set<Method> deprecatedMethods;
|
||||
@Getter
|
||||
private Map<String, String> deprecatedMethodsSignaturesMap;
|
||||
private Set<Class<?>> deprecatedClasses;
|
||||
@Getter
|
||||
private SearchImplementation classesSearchImplementation;
|
||||
@Getter
|
||||
private SearchImplementation methodsSearchImplementation;
|
||||
|
||||
|
||||
public void findDeprecatedMethods() {
|
||||
|
||||
deprecatedMethods = new HashSet<>();
|
||||
deprecatedClasses = new HashSet<>();
|
||||
deprecatedMethodsSignaturesMap = new HashMap<>();
|
||||
|
||||
Reflections reflections = new Reflections(new ConfigurationBuilder().forPackage(PACKAGE_NAME)
|
||||
.setExpandSuperTypes(true)
|
||||
.setScanners(Scanners.MethodsAnnotated, Scanners.TypesAnnotated, Scanners.SubTypes));
|
||||
|
||||
deprecatedMethods = reflections.get(Scanners.MethodsAnnotated.with(Deprecated.class).as(Method.class));
|
||||
|
||||
deprecatedClasses = reflections.get(Scanners.TypesAnnotated.with(Deprecated.class).asClass());
|
||||
|
||||
if (!deprecatedClasses.isEmpty()) {
|
||||
deprecatedClasses.forEach(deprecatedType -> deprecatedMethods.addAll(Arrays.stream(deprecatedType.getDeclaredMethods())
|
||||
.collect(Collectors.toSet())));
|
||||
classesSearchImplementation = new SearchImplementation(deprecatedClasses.stream()
|
||||
.map(Class::getName)
|
||||
.collect(Collectors.toSet()), false);
|
||||
}
|
||||
|
||||
if (!deprecatedMethods.isEmpty()) {
|
||||
methodsSearchImplementation = new SearchImplementation(deprecatedMethods.stream()
|
||||
.map(Method::getName)
|
||||
.collect(Collectors.toSet()), false);
|
||||
deprecatedMethodsSignaturesMap = new HashMap<>();
|
||||
Map<String, List<Method>> deprecatedMethodsNameMap = deprecatedMethods.stream()
|
||||
.collect(groupingBy(Method::getName));
|
||||
for (String deprecatedMethod : deprecatedMethodsNameMap.keySet()) {
|
||||
deprecatedMethodsSignaturesMap.computeIfAbsent(deprecatedMethod,
|
||||
key -> deprecatedMethodsNameMap.get(deprecatedMethod)
|
||||
.stream()
|
||||
.map(this::getMethodSignature)
|
||||
.collect(Collectors.joining("\n")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log.info("For package: {} found deprecated classes {} deprecated methods {}", PACKAGE_NAME, deprecatedClasses.size(), deprecatedMethods.size());
|
||||
}
|
||||
|
||||
|
||||
public Set<Method> getDeprecatedMethods() {
|
||||
|
||||
if (this.deprecatedMethods == null) {
|
||||
findDeprecatedMethods();
|
||||
}
|
||||
return this.deprecatedMethods;
|
||||
}
|
||||
|
||||
|
||||
public Set<Class<?>> getDeprecatedClasses() {
|
||||
|
||||
if (this.deprecatedClasses == null) {
|
||||
findDeprecatedMethods();
|
||||
}
|
||||
return this.deprecatedClasses;
|
||||
}
|
||||
|
||||
private String getMethodSignature(Method method) {
|
||||
|
||||
String methodName = method.getName();
|
||||
String parameterTypes = Arrays.stream(method.getGenericParameterTypes())
|
||||
.map(Type::getTypeName)
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
return method.getGenericReturnType() + " " + methodName + "(" + parameterTypes + ")";
|
||||
}
|
||||
|
||||
}
|
||||
+6
@@ -30,4 +30,10 @@ public class RedactionServiceSettings {
|
||||
|
||||
private int droolsExecutionTimeoutSecs = 300;
|
||||
|
||||
|
||||
public int getDroolsExecutionTimeoutSecs(int numberOfPages) {
|
||||
|
||||
return (int) Math.max(getDroolsExecutionTimeoutSecs(), getDroolsExecutionTimeoutSecs() * ((float) numberOfPages / 1000));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-6
@@ -18,6 +18,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.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.dossiertemplate.type.DictionaryEntryType;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Change;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.ChangeType;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Engine;
|
||||
@@ -92,11 +93,16 @@ public class LegacyRedactionLogMergeService {
|
||||
return redactionLog;
|
||||
}
|
||||
|
||||
|
||||
public long getNumberOfAffectedAnnotations(ManualRedactions manualRedactions) {
|
||||
|
||||
return createManualRedactionWrappers(manualRedactions).stream().map(ManualRedactionWrapper::getId).distinct().count();
|
||||
return createManualRedactionWrappers(manualRedactions).stream()
|
||||
.map(ManualRedactionWrapper::getId)
|
||||
.distinct()
|
||||
.count();
|
||||
}
|
||||
|
||||
|
||||
private List<ManualRedactionWrapper> createManualRedactionWrappers(ManualRedactions manualRedactions) {
|
||||
|
||||
List<ManualRedactionWrapper> manualRedactionWrappers = new ArrayList<>();
|
||||
@@ -197,7 +203,12 @@ public class LegacyRedactionLogMergeService {
|
||||
}
|
||||
|
||||
redactionLogEntry.getManualChanges()
|
||||
.add(ManualChange.from(imageRecategorization).withManualRedactionType(ManualRedactionType.RECATEGORIZE).withChange("type", imageRecategorization.getType()));
|
||||
.add(ManualChange.from(imageRecategorization)
|
||||
.withManualRedactionType(ManualRedactionType.RECATEGORIZE)
|
||||
.withChange("type", imageRecategorization.getType())
|
||||
.withChange("section", imageRecategorization.getSection())
|
||||
.withChange("legalBasis", imageRecategorization.getLegalBasis())
|
||||
.withChange("value", imageRecategorization.getValue()));
|
||||
}
|
||||
|
||||
|
||||
@@ -369,13 +380,17 @@ public class LegacyRedactionLogMergeService {
|
||||
@SuppressWarnings("PMD.UselessParentheses")
|
||||
private boolean shouldCreateManualEntry(ManualRedactionEntry manualRedactionEntry) {
|
||||
|
||||
if (!manualRedactionEntry.isApproved()) {
|
||||
if (manualRedactionEntry.getDictionaryEntryType() != null //
|
||||
&& (manualRedactionEntry.getDictionaryEntryType().equals(DictionaryEntryType.FALSE_POSITIVE) //
|
||||
|| manualRedactionEntry.getDictionaryEntryType().equals(DictionaryEntryType.FALSE_RECOMMENDATION))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (!manualRedactionEntry.isAddToDictionary() && !manualRedactionEntry.isAddToDossierDictionary()) || ((manualRedactionEntry.isAddToDictionary()
|
||||
|| manualRedactionEntry.isAddToDossierDictionary())
|
||||
&& manualRedactionEntry.getProcessedDate() == null);
|
||||
if (manualRedactionEntry.getProcessedDate() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (!manualRedactionEntry.isAddToDictionary() && !manualRedactionEntry.isAddToDossierDictionary());
|
||||
}
|
||||
|
||||
|
||||
|
||||
+13
-5
@@ -1,9 +1,9 @@
|
||||
package com.iqser.red.service.redaction.v1.server.migration;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.ManualChange;
|
||||
@@ -70,13 +70,21 @@ public class MigrationMapper {
|
||||
|
||||
public static Set<com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine> getMigratedEngines(RedactionLogEntry entry) {
|
||||
|
||||
if (entry.getEngines() == null) {
|
||||
return Collections.emptySet();
|
||||
Set<com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine> engines = new HashSet<>();
|
||||
|
||||
if (entry.isImported()) {
|
||||
engines.add(com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine.IMPORTED);
|
||||
}
|
||||
return entry.getEngines()
|
||||
|
||||
if (entry.getEngines() == null) {
|
||||
return engines;
|
||||
}
|
||||
entry.getEngines()
|
||||
.stream()
|
||||
.map(MigrationMapper::toEntityLogEngine)
|
||||
.collect(Collectors.toSet());
|
||||
.forEach(engines::add);
|
||||
|
||||
return engines;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-2
@@ -57,7 +57,7 @@ public class MigrationMessageReceiver {
|
||||
|
||||
if (redactionLog.getAnalysisVersion() == 0) {
|
||||
redactionLog = legacyVersion0MigrationService.mergeDuplicateAnnotationIds(redactionLog);
|
||||
} else if (migrationRequest.getManualRedactions() != null) {
|
||||
} else {
|
||||
redactionLog = legacyRedactionLogMergeService.addManualAddEntriesAndRemoveSkippedImported(redactionLog,
|
||||
migrationRequest.getManualRedactions(),
|
||||
migrationRequest.getDossierTemplateId());
|
||||
@@ -67,7 +67,9 @@ public class MigrationMessageReceiver {
|
||||
document,
|
||||
migrationRequest.getDossierTemplateId(),
|
||||
migrationRequest.getManualRedactions(),
|
||||
migrationRequest.getFileId());
|
||||
migrationRequest.getFileId(),
|
||||
migrationRequest.getEntitiesWithComments(),
|
||||
migrationRequest.isFileIsApproved());
|
||||
|
||||
log.info("Storing migrated entityLog and ids to migrate in DB for file {}", migrationRequest.getFileId());
|
||||
redactionStorageService.storeObject(migrationRequest.getDossierId(), migrationRequest.getFileId(), FileType.ENTITY_LOG, migratedEntityLog.getEntityLog());
|
||||
|
||||
+85
-21
@@ -8,6 +8,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -19,7 +20,9 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.migration.MigratedIds;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.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.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.Rectangle;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLogEntry;
|
||||
@@ -34,7 +37,6 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;
|
||||
import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
|
||||
import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService;
|
||||
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
|
||||
import com.iqser.red.service.redaction.v1.server.service.document.EntityFindingUtility;
|
||||
import com.iqser.red.service.redaction.v1.server.service.document.EntityFromPrecursorCreationService;
|
||||
import com.iqser.red.service.redaction.v1.server.utils.IdBuilder;
|
||||
@@ -54,12 +56,17 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
|
||||
private static final double MATCH_THRESHOLD = 10;
|
||||
EntityFindingUtility entityFindingUtility;
|
||||
EntityEnrichmentService entityEnrichmentService;
|
||||
DictionaryService dictionaryService;
|
||||
ManualChangesApplicationService manualChangesApplicationService;
|
||||
|
||||
|
||||
public MigratedEntityLog migrate(RedactionLog redactionLog, Document document, String dossierTemplateId, ManualRedactions manualRedactions, String fileId) {
|
||||
public MigratedEntityLog migrate(RedactionLog redactionLog,
|
||||
Document document,
|
||||
String dossierTemplateId,
|
||||
ManualRedactions manualRedactions,
|
||||
String fileId,
|
||||
Set<String> entitiesWithComments,
|
||||
boolean fileIsApproved) {
|
||||
|
||||
log.info("Migrating entities for file {}", fileId);
|
||||
List<MigrationEntity> entitiesToMigrate = calculateMigrationEntitiesFromRedactionLog(redactionLog, document, dossierTemplateId, fileId);
|
||||
@@ -67,8 +74,8 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
MigratedIds migratedIds = entitiesToMigrate.stream()
|
||||
.collect(new MigratedIdsCollector());
|
||||
|
||||
applyManualChanges(entitiesToMigrate, manualRedactions);
|
||||
log.info("applying manual changes to migrated entities for file {}", fileId);
|
||||
applyLocalProcessedManualChanges(entitiesToMigrate, manualRedactions, fileIsApproved);
|
||||
|
||||
EntityLog entityLog = new EntityLog();
|
||||
entityLog.setAnalysisNumber(redactionLog.getAnalysisNumber());
|
||||
@@ -89,7 +96,7 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
.map(migrationEntity -> migrationEntity.toEntityLogEntry(oldToNewIDMapping))
|
||||
.toList());
|
||||
|
||||
if (getNumberOfApprovedEntries(redactionLog) != entityLog.getEntityLogEntry().size()) {
|
||||
if (getNumberOfApprovedEntries(redactionLog, document.getNumberOfPages()) != entityLog.getEntityLogEntry().size()) {
|
||||
String message = String.format("Not all entities have been found during the migration redactionLog has %d entries and new entityLog %d",
|
||||
redactionLog.getRedactionLogEntry().size(),
|
||||
entityLog.getEntityLogEntry().size());
|
||||
@@ -97,8 +104,14 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
throw new AssertionError(message);
|
||||
}
|
||||
|
||||
Set<String> entitiesWithUnprocessedChanges = manualRedactions.buildAll()
|
||||
.stream()
|
||||
.filter(manualRedaction -> manualRedaction.getProcessedDate() == null)
|
||||
.map(BaseAnnotation::getAnnotationId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
MigratedIds idsToMigrateInDb = entitiesToMigrate.stream()
|
||||
.filter(MigrationEntity::hasManualChangesOrComments)
|
||||
.filter(migrationEntity -> migrationEntity.hasManualChangesOrComments(entitiesWithComments, entitiesWithUnprocessedChanges))
|
||||
.filter(m -> !m.getOldId().equals(m.getNewId()))
|
||||
.collect(new MigratedIdsCollector());
|
||||
|
||||
@@ -113,20 +126,29 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
}
|
||||
|
||||
|
||||
private void applyManualChanges(List<MigrationEntity> entitiesToMigrate, ManualRedactions manualRedactions) {
|
||||
private void applyLocalProcessedManualChanges(List<MigrationEntity> entitiesToMigrate, ManualRedactions manualRedactions, boolean fileIsApproved) {
|
||||
|
||||
if (manualRedactions == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<BaseAnnotation>> manualChangesPerAnnotationId;
|
||||
|
||||
Map<String, List<BaseAnnotation>> manualChangesPerAnnotationId = Stream.of(manualRedactions.getIdsToRemove(),
|
||||
manualRedactions.getEntriesToAdd(),
|
||||
manualRedactions.getForceRedactions(),
|
||||
manualRedactions.getResizeRedactions(),
|
||||
manualRedactions.getLegalBasisChanges(),
|
||||
manualRedactions.getRecategorizations())
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.groupingBy(BaseAnnotation::getAnnotationId));
|
||||
if (fileIsApproved) {
|
||||
manualChangesPerAnnotationId = manualRedactions.buildAll()
|
||||
.stream()
|
||||
.filter(manualChange -> (manualChange.getProcessedDate() != null && manualChange.isLocal()) //
|
||||
// unprocessed dict change of type IdRemoval or ManualResize must be applied for approved documents
|
||||
|| (manualChange.getProcessedDate() == null && !manualChange.isLocal() //
|
||||
&& (manualChange instanceof IdRemoval || manualChange instanceof ManualResizeRedaction)))
|
||||
.map(this::convertPendingDictChangesToLocal)
|
||||
.collect(Collectors.groupingBy(BaseAnnotation::getAnnotationId));
|
||||
} else {
|
||||
manualChangesPerAnnotationId = manualRedactions.buildAll()
|
||||
.stream()
|
||||
.filter(manualChange -> manualChange.getProcessedDate() != null)
|
||||
.filter(BaseAnnotation::isLocal)
|
||||
.collect(Collectors.groupingBy(BaseAnnotation::getAnnotationId));
|
||||
}
|
||||
|
||||
entitiesToMigrate.forEach(migrationEntity -> migrationEntity.applyManualChanges(manualChangesPerAnnotationId.getOrDefault(migrationEntity.getOldId(),
|
||||
Collections.emptyList()),
|
||||
@@ -135,15 +157,40 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
}
|
||||
|
||||
|
||||
private static long getNumberOfApprovedEntries(RedactionLog redactionLog) {
|
||||
private BaseAnnotation convertPendingDictChangesToLocal(BaseAnnotation baseAnnotation) {
|
||||
|
||||
return redactionLog.getRedactionLogEntry().size();
|
||||
if (baseAnnotation.getProcessedDate() != null) {
|
||||
return baseAnnotation;
|
||||
}
|
||||
|
||||
if (baseAnnotation.isLocal()) {
|
||||
return baseAnnotation;
|
||||
}
|
||||
|
||||
if (baseAnnotation instanceof ManualResizeRedaction manualResizeRedaction) {
|
||||
manualResizeRedaction.setAddToAllDossiers(false);
|
||||
manualResizeRedaction.setUpdateDictionary(false);
|
||||
} else if (baseAnnotation instanceof IdRemoval idRemoval) {
|
||||
idRemoval.setRemoveFromAllDossiers(false);
|
||||
idRemoval.setRemoveFromDictionary(false);
|
||||
}
|
||||
|
||||
return baseAnnotation;
|
||||
}
|
||||
|
||||
|
||||
private long getNumberOfApprovedEntries(RedactionLog redactionLog, int numberOfPages) {
|
||||
|
||||
return redactionLog.getRedactionLogEntry()
|
||||
.stream()
|
||||
.filter(redactionLogEntry -> isOnExistingPage(redactionLogEntry, numberOfPages))
|
||||
.count();
|
||||
}
|
||||
|
||||
|
||||
private List<MigrationEntity> calculateMigrationEntitiesFromRedactionLog(RedactionLog redactionLog, Document document, String dossierTemplateId, String fileId) {
|
||||
|
||||
List<MigrationEntity> images = getImageBasedMigrationEntities(redactionLog, document, fileId);
|
||||
List<MigrationEntity> images = getImageBasedMigrationEntities(redactionLog, document, fileId, dossierTemplateId);
|
||||
List<MigrationEntity> textMigrationEntities = getTextBasedMigrationEntities(redactionLog, document, dossierTemplateId, fileId);
|
||||
return Stream.of(textMigrationEntities.stream(), images.stream())
|
||||
.flatMap(Function.identity())
|
||||
@@ -157,7 +204,7 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
}
|
||||
|
||||
|
||||
private List<MigrationEntity> getImageBasedMigrationEntities(RedactionLog redactionLog, Document document, String fileId) {
|
||||
private List<MigrationEntity> getImageBasedMigrationEntities(RedactionLog redactionLog, Document document, String fileId, String dossierTemplateId) {
|
||||
|
||||
List<Image> images = document.streamAllImages()
|
||||
.collect(Collectors.toList());
|
||||
@@ -204,7 +251,7 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
} else {
|
||||
closestImage.skip(ruleIdentifier, reason);
|
||||
}
|
||||
migrationEntities.add(MigrationEntity.fromRedactionLogImage(redactionLogImage, closestImage, fileId));
|
||||
migrationEntities.add(MigrationEntity.fromRedactionLogImage(redactionLogImage, closestImage, fileId, dictionaryService, dossierTemplateId));
|
||||
}
|
||||
return migrationEntities;
|
||||
}
|
||||
@@ -250,7 +297,8 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
List<MigrationEntity> entitiesToMigrate = redactionLog.getRedactionLogEntry()
|
||||
.stream()
|
||||
.filter(redactionLogEntry -> !redactionLogEntry.isImage())
|
||||
.map(entry -> MigrationEntity.fromRedactionLogEntry(entry, dictionaryService.isHint(entry.getType(), dossierTemplateId), fileId))
|
||||
.filter(redactionLogEntry -> isOnExistingPage(redactionLogEntry, document.getNumberOfPages()))
|
||||
.map(entry -> MigrationEntity.fromRedactionLogEntry(entry, fileId, dictionaryService, dossierTemplateId))
|
||||
.toList();
|
||||
|
||||
List<PrecursorEntity> precursorEntities = entitiesToMigrate.stream()
|
||||
@@ -287,4 +335,20 @@ public class RedactionLogToEntityLogMigrationService {
|
||||
return entitiesToMigrate;
|
||||
}
|
||||
|
||||
|
||||
private boolean isOnExistingPage(RedactionLogEntry redactionLogEntry, int numberOfPages) {
|
||||
|
||||
var pages = redactionLogEntry.getPositions()
|
||||
.stream()
|
||||
.map(Rectangle::getPage)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (int page : pages) {
|
||||
if (page > numberOfPages) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+71
-43
@@ -4,22 +4,29 @@ import static com.iqser.red.service.redaction.v1.server.service.EntityLogCreator
|
||||
import static com.iqser.red.service.redaction.v1.server.service.EntityLogCreatorService.buildEntryType;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.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.Position;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualChangeFactory;
|
||||
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.BaseAnnotation;
|
||||
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.dossiertemplate.type.DictionaryEntryType;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.ManualRedactionType;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLogEntry;
|
||||
import com.iqser.red.service.redaction.v1.server.migration.MigrationMapper;
|
||||
@@ -28,7 +35,8 @@ 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.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
|
||||
import com.iqser.red.service.redaction.v1.server.service.ManualChangeFactory;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;
|
||||
import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
|
||||
import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -46,6 +54,8 @@ public final class MigrationEntity {
|
||||
|
||||
private final PrecursorEntity precursorEntity;
|
||||
private final RedactionLogEntry redactionLogEntry;
|
||||
private final DictionaryService dictionaryService;
|
||||
private final String dossierTemplateId;
|
||||
private IEntity migratedEntity;
|
||||
private String oldId;
|
||||
private String newId;
|
||||
@@ -55,10 +65,10 @@ public final class MigrationEntity {
|
||||
List<BaseAnnotation> manualChanges = new LinkedList<>();
|
||||
|
||||
|
||||
public static MigrationEntity fromRedactionLogEntry(RedactionLogEntry redactionLogEntry, boolean hint, String fileId) {
|
||||
public static MigrationEntity fromRedactionLogEntry(RedactionLogEntry redactionLogEntry, String fileId, DictionaryService dictionaryService, String dossierTemplateId) {
|
||||
|
||||
boolean hint = dictionaryService.isHint(redactionLogEntry.getType(), dossierTemplateId);
|
||||
PrecursorEntity precursorEntity = createPrecursorEntity(redactionLogEntry, hint);
|
||||
|
||||
if (precursorEntity.getEntityType().equals(EntityType.HINT) && !redactionLogEntry.isHint() && !redactionLogEntry.isRedacted()) {
|
||||
precursorEntity.ignore(precursorEntity.getRuleIdentifier(), precursorEntity.getReason());
|
||||
} else if (redactionLogEntry.lastChangeIsRemoved()) {
|
||||
@@ -73,13 +83,32 @@ public final class MigrationEntity {
|
||||
precursorEntity.skip(precursorEntity.getRuleIdentifier(), precursorEntity.getReason());
|
||||
}
|
||||
|
||||
return MigrationEntity.builder().precursorEntity(precursorEntity).redactionLogEntry(redactionLogEntry).oldId(redactionLogEntry.getId()).fileId(fileId).build();
|
||||
return MigrationEntity.builder()
|
||||
.precursorEntity(precursorEntity)
|
||||
.redactionLogEntry(redactionLogEntry)
|
||||
.oldId(redactionLogEntry.getId())
|
||||
.fileId(fileId)
|
||||
.dictionaryService(dictionaryService)
|
||||
.dossierTemplateId(dossierTemplateId)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
public static MigrationEntity fromRedactionLogImage(RedactionLogEntry redactionLogImage, Image image, String fileId) {
|
||||
public static MigrationEntity fromRedactionLogImage(RedactionLogEntry redactionLogImage,
|
||||
Image image,
|
||||
String fileId,
|
||||
DictionaryService dictionaryService,
|
||||
String dossierTemplateId) {
|
||||
|
||||
return MigrationEntity.builder().redactionLogEntry(redactionLogImage).migratedEntity(image).oldId(redactionLogImage.getId()).newId(image.getId()).fileId(fileId).build();
|
||||
return MigrationEntity.builder()
|
||||
.redactionLogEntry(redactionLogImage)
|
||||
.migratedEntity(image)
|
||||
.oldId(redactionLogImage.getId())
|
||||
.newId(image.getId())
|
||||
.fileId(fileId)
|
||||
.dictionaryService(dictionaryService)
|
||||
.dossierTemplateId(dossierTemplateId)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -156,18 +185,6 @@ public final class MigrationEntity {
|
||||
}
|
||||
|
||||
|
||||
private static EntryType getEntryType(EntityType entityType) {
|
||||
|
||||
return switch (entityType) {
|
||||
case ENTITY -> EntryType.ENTITY;
|
||||
case HINT -> EntryType.HINT;
|
||||
case FALSE_POSITIVE -> EntryType.FALSE_POSITIVE;
|
||||
case RECOMMENDATION -> EntryType.RECOMMENDATION;
|
||||
case FALSE_RECOMMENDATION -> EntryType.FALSE_RECOMMENDATION;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public EntityLogEntry toEntityLogEntry(Map<String, String> oldToNewIdMapping) {
|
||||
|
||||
EntityLogEntry entityLogEntry;
|
||||
@@ -181,7 +198,7 @@ public final class MigrationEntity {
|
||||
throw new UnsupportedOperationException("Unknown subclass " + migratedEntity.getClass());
|
||||
}
|
||||
|
||||
entityLogEntry.setManualChanges(ManualChangeFactory.toManualChangeList(migratedEntity.getManualOverwrite().getManualChangeLog(), redactionLogEntry.isHint()));
|
||||
entityLogEntry.setManualChanges(ManualChangeFactory.toLocalManualChangeList(migratedEntity.getManualOverwrite().getManualChangeLog(), true));
|
||||
entityLogEntry.setColor(redactionLogEntry.getColor());
|
||||
entityLogEntry.setChanges(redactionLogEntry.getChanges()
|
||||
.stream()
|
||||
@@ -190,14 +207,21 @@ public final class MigrationEntity {
|
||||
entityLogEntry.setReference(migrateSetOfIds(redactionLogEntry.getReference(), oldToNewIdMapping));
|
||||
entityLogEntry.setImportedRedactionIntersections(migrateSetOfIds(redactionLogEntry.getImportedRedactionIntersections(), oldToNewIdMapping));
|
||||
entityLogEntry.setEngines(MigrationMapper.getMigratedEngines(redactionLogEntry));
|
||||
if (redactionLogEntry.getLegalBasis() != null) {
|
||||
entityLogEntry.setLegalBasis(redactionLogEntry.getLegalBasis());
|
||||
}
|
||||
|
||||
if (entityLogEntry.getEntryType().equals(EntryType.HINT) && lastManualChangeIsRemoveLocally(entityLogEntry)) {
|
||||
entityLogEntry.setState(EntryState.IGNORED);
|
||||
}
|
||||
|
||||
if (redactionLogEntry.isImported() && redactionLogEntry.getValue() == null) {
|
||||
entityLogEntry.setValue("Imported Redaction");
|
||||
}
|
||||
|
||||
if(entityLogEntry.getChanges() != null
|
||||
&& !entityLogEntry.getChanges().isEmpty()
|
||||
&& entityLogEntry.getChanges().stream().map(Change::getType).toList().get(entityLogEntry.getChanges().size() - 1).equals(ChangeType.REMOVED)) {
|
||||
entityLogEntry.setState(EntryState.REMOVED);
|
||||
}
|
||||
|
||||
return entityLogEntry;
|
||||
}
|
||||
|
||||
@@ -225,13 +249,15 @@ public final class MigrationEntity {
|
||||
|
||||
public EntityLogEntry createEntityLogEntry(Image image) {
|
||||
|
||||
String imageType = image.getImageType().equals(ImageType.OTHER) ? "image" : image.getImageType().toString().toLowerCase(Locale.ENGLISH);
|
||||
List<Position> positions = getPositionsFromOverride(image).orElse(List.of(new Position(image.getPosition(), image.getPage().getNumber())));
|
||||
return EntityLogEntry.builder()
|
||||
.id(image.getId())
|
||||
.value(image.value())
|
||||
.type(image.type())
|
||||
.value(image.getValue())
|
||||
.type(imageType)
|
||||
.reason(image.buildReasonWithManualChangeDescriptions())
|
||||
.legalBasis(image.legalBasis())
|
||||
.legalBasis(image.getManualOverwrite().getLegalBasis()
|
||||
.orElse(redactionLogEntry.getLegalBasis()))
|
||||
.matchedRule(image.getMatchedRule().getRuleIdentifier().toString())
|
||||
.dictionaryEntry(false)
|
||||
.positions(positions)
|
||||
@@ -243,7 +269,7 @@ public final class MigrationEntity {
|
||||
.textBefore(redactionLogEntry.getTextBefore())
|
||||
.imageHasTransparency(image.isTransparent())
|
||||
.state(buildEntryState(image))
|
||||
.entryType(redactionLogEntry.isHint() ? EntryType.IMAGE_HINT : EntryType.IMAGE)
|
||||
.entryType(dictionaryService.isHint(imageType, dossierTemplateId) ? EntryType.IMAGE_HINT : EntryType.IMAGE)
|
||||
.build();
|
||||
|
||||
}
|
||||
@@ -254,7 +280,8 @@ public final class MigrationEntity {
|
||||
return EntityLogEntry.builder()
|
||||
.id(precursorEntity.getId())
|
||||
.reason(precursorEntity.buildReasonWithManualChangeDescriptions())
|
||||
.legalBasis(precursorEntity.legalBasis())
|
||||
.legalBasis(precursorEntity.getManualOverwrite().getLegalBasis()
|
||||
.orElse(redactionLogEntry.getLegalBasis()))
|
||||
.value(precursorEntity.value())
|
||||
.type(precursorEntity.type())
|
||||
.state(buildEntryState(precursorEntity))
|
||||
@@ -288,7 +315,8 @@ public final class MigrationEntity {
|
||||
.id(entity.getId())
|
||||
.positions(rectanglesPerLine)
|
||||
.reason(entity.buildReasonWithManualChangeDescriptions())
|
||||
.legalBasis(entity.legalBasis())
|
||||
.legalBasis(entity.getManualOverwrite().getLegalBasis()
|
||||
.orElse(redactionLogEntry.getLegalBasis()))
|
||||
.value(entity.getManualOverwrite().getValue()
|
||||
.orElse(entity.getMatchedRule().isWriteValueWithLineBreaks() ? entity.getValueWithLineBreaks() : entity.getValue()))
|
||||
.type(entity.type())
|
||||
@@ -331,11 +359,11 @@ public final class MigrationEntity {
|
||||
}
|
||||
|
||||
|
||||
public boolean hasManualChangesOrComments() {
|
||||
public boolean hasManualChangesOrComments(Set<String> entitiesWithComments, Set<String> entitiesWithUnprocessedChanges) {
|
||||
|
||||
return !(redactionLogEntry.getManualChanges() == null || redactionLogEntry.getManualChanges().isEmpty()) || //
|
||||
!(redactionLogEntry.getComments() == null || redactionLogEntry.getComments().isEmpty()) //
|
||||
|| hasManualChanges();
|
||||
|| hasManualChanges() || entitiesWithComments.contains(oldId) || entitiesWithUnprocessedChanges.contains(oldId);
|
||||
}
|
||||
|
||||
|
||||
@@ -350,17 +378,11 @@ public final class MigrationEntity {
|
||||
manualChanges.addAll(manualChangesToApply);
|
||||
manualChangesToApply.forEach(manualChange -> {
|
||||
if (manualChange instanceof ManualResizeRedaction manualResizeRedaction && migratedEntity instanceof TextEntity textEntity) {
|
||||
// Due to the value in the old redaction log already being resized, there is no way to find the original entity ID and therefore to migrate the resize annotation correctly.
|
||||
// Instead, we add an add_locally change to the db.
|
||||
ManualResizeRedaction migratedManualResizeRedaction = ManualResizeRedaction.builder()
|
||||
.positions(manualResizeRedaction.getPositions())
|
||||
.annotationId(getNewId())
|
||||
.updateDictionary(manualResizeRedaction.getUpdateDictionary())
|
||||
.addToAllDossiers(manualResizeRedaction.isAddToAllDossiers())
|
||||
.textAfter(manualResizeRedaction.getTextAfter())
|
||||
.textBefore(manualResizeRedaction.getTextBefore())
|
||||
.build();
|
||||
manualChangesApplicationService.resize(textEntity, migratedManualResizeRedaction);
|
||||
manualResizeRedaction.setAnnotationId(newId);
|
||||
manualChangesApplicationService.resize(textEntity, manualResizeRedaction);
|
||||
} else if (manualChange instanceof ManualRecategorization manualRecategorization && migratedEntity instanceof Image image) {
|
||||
image.setImageType(ImageType.fromString(manualRecategorization.getType()));
|
||||
migratedEntity.getManualOverwrite().addChange(manualChange);
|
||||
} else {
|
||||
migratedEntity.getManualOverwrite().addChange(manualChange);
|
||||
}
|
||||
@@ -378,19 +400,25 @@ public final class MigrationEntity {
|
||||
.findFirst()
|
||||
.orElse(manualChanges.get(0)).getUser();
|
||||
|
||||
OffsetDateTime requestDate = manualChanges.get(0).getRequestDate();
|
||||
|
||||
return ManualRedactionEntry.builder()
|
||||
.annotationId(newId)
|
||||
.fileId(fileId)
|
||||
.user(user)
|
||||
.requestDate(requestDate)
|
||||
.type(redactionLogEntry.getType())
|
||||
.value(redactionLogEntry.getValue())
|
||||
.reason(redactionLogEntry.getReason())
|
||||
.legalBasis(redactionLogEntry.getLegalBasis())
|
||||
.section(redactionLogEntry.getSection())
|
||||
.rectangle(false)
|
||||
.addToDictionary(false)
|
||||
.addToDossierDictionary(false)
|
||||
.rectangle(false)
|
||||
.positions(buildPositions(migratedEntity))
|
||||
.user(user)
|
||||
.textAfter(redactionLogEntry.getTextAfter())
|
||||
.textBefore(redactionLogEntry.getTextBefore())
|
||||
.dictionaryEntryType(DictionaryEntryType.ENTRY)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
+85
-10
@@ -2,6 +2,7 @@ package com.iqser.red.service.redaction.v1.server.model.document;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -40,7 +41,10 @@ public class DocumentTree {
|
||||
|
||||
public TextBlock buildTextBlock() {
|
||||
|
||||
return allEntriesInOrder().map(Entry::getNode).filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
|
||||
return allEntriesInOrder().map(Entry::getNode)
|
||||
.filter(SemanticNode::isLeaf)
|
||||
.map(SemanticNode::getLeafTextBlock)
|
||||
.collect(new TextBlockCollector());
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +93,8 @@ public class DocumentTree {
|
||||
if (treeId.isEmpty()) {
|
||||
return root != null;
|
||||
}
|
||||
Entry entry = root.children.get(treeId.get(0));
|
||||
for (int id : treeId.subList(1, treeId.size())) {
|
||||
Entry entry = root;
|
||||
for (int id : treeId) {
|
||||
if (id >= entry.children.size() || 0 > id) {
|
||||
return false;
|
||||
}
|
||||
@@ -114,13 +118,78 @@ public class DocumentTree {
|
||||
|
||||
public Stream<SemanticNode> childNodes(List<Integer> treeId) {
|
||||
|
||||
return getEntryById(treeId).children.stream().map(Entry::getNode);
|
||||
return getEntryById(treeId).children.stream()
|
||||
.map(Entry::getNode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all child nodes of the specified entry, whose nodes textRange intersects the given textRange. It achieves this by finding the first entry, whose textRange contains the start idx of the TextRange using a binary search.
|
||||
* It then iterates over the remaining children adding them to the intersections, until one does not contain the end of the TextRange. All intersected Entries are returned as SemanticNodes.
|
||||
*
|
||||
* @param treeId the treeId of the Entry whose children shall be checked.
|
||||
* @param textRange The TextRange to find intersecting childNodes for.
|
||||
* @return A list of all SemanticNodes, that are direct children of the specified Entry, whose TextRange intersects the given TextRange
|
||||
*/
|
||||
public List<SemanticNode> findIntersectingChildNodes(List<Integer> treeId, TextRange textRange) {
|
||||
|
||||
List<Entry> childEntries = getEntryById(treeId).getChildren();
|
||||
List<SemanticNode> intersectingChildEntries = new LinkedList<>();
|
||||
int startIdx = findFirstIdxOfContainingChildBinarySearch(childEntries, textRange.start());
|
||||
if (startIdx < 0) {
|
||||
return intersectingChildEntries;
|
||||
}
|
||||
for (int i = startIdx; i < childEntries.size(); i++) {
|
||||
if (childEntries.get(i).getNode().getTextRange().start() < textRange.end()) {
|
||||
intersectingChildEntries.add(childEntries.get(i).getNode());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return intersectingChildEntries;
|
||||
}
|
||||
|
||||
|
||||
public Optional<SemanticNode> findFirstContainingChild(List<Integer> treeId, TextRange textRange) {
|
||||
|
||||
List<Entry> childEntries = getEntryById(treeId).getChildren();
|
||||
int startIdx = findFirstIdxOfContainingChildBinarySearch(childEntries, textRange.start());
|
||||
if (startIdx < 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (childEntries.get(startIdx).getNode().getTextRange().contains(textRange.end())) {
|
||||
return Optional.of(childEntries.get(startIdx).getNode());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
private int findFirstIdxOfContainingChildBinarySearch(List<Entry> childNodes, int start) {
|
||||
|
||||
int low = 0;
|
||||
int high = childNodes.size() - 1;
|
||||
while (low <= high) {
|
||||
int mid = low + (high - low) / 2;
|
||||
TextRange range = childNodes.get(mid).getNode().getTextRange();
|
||||
if (range.start() > start) {
|
||||
high = mid - 1;
|
||||
} else if (range.end() <= start) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
public Stream<SemanticNode> childNodesOfType(List<Integer> treeId, NodeType nodeType) {
|
||||
|
||||
return getEntryById(treeId).children.stream().filter(entry -> entry.node.getType().equals(nodeType)).map(Entry::getNode);
|
||||
return getEntryById(treeId).children.stream()
|
||||
.filter(entry -> entry.node.getType().equals(nodeType))
|
||||
.map(Entry::getNode);
|
||||
}
|
||||
|
||||
|
||||
@@ -199,26 +268,32 @@ public class DocumentTree {
|
||||
|
||||
public Stream<Entry> allEntriesInOrder() {
|
||||
|
||||
return Stream.of(root).flatMap(DocumentTree::flatten);
|
||||
return Stream.of(root)
|
||||
.flatMap(DocumentTree::flatten);
|
||||
}
|
||||
|
||||
|
||||
public Stream<Entry> allSubEntriesInOrder(List<Integer> parentId) {
|
||||
|
||||
return getEntryById(parentId).children.stream().flatMap(DocumentTree::flatten);
|
||||
return getEntryById(parentId).children.stream()
|
||||
.flatMap(DocumentTree::flatten);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return String.join("\n", allEntriesInOrder().map(Entry::toString).toList());
|
||||
return String.join("\n",
|
||||
allEntriesInOrder().map(Entry::toString)
|
||||
.toList());
|
||||
}
|
||||
|
||||
|
||||
private static Stream<Entry> flatten(Entry entry) {
|
||||
|
||||
return Stream.concat(Stream.of(entry), entry.children.stream().flatMap(DocumentTree::flatten));
|
||||
return Stream.concat(Stream.of(entry),
|
||||
entry.children.stream()
|
||||
.flatMap(DocumentTree::flatten));
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +315,7 @@ public class DocumentTree {
|
||||
List<Integer> treeId;
|
||||
SemanticNode node;
|
||||
@Builder.Default
|
||||
List<Entry> children = new LinkedList<>();
|
||||
List<Entry> children = new ArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
+15
-5
@@ -92,12 +92,18 @@ public class TextRange implements Comparable<TextRange> {
|
||||
|
||||
public List<TextRange> split(List<Integer> splitIndices) {
|
||||
|
||||
if (splitIndices.stream().anyMatch(idx -> !this.contains(idx))) {
|
||||
throw new IndexOutOfBoundsException(format("%s splitting indices are out of range for %s", splitIndices.stream().filter(idx -> !this.contains(idx)).toList(), this));
|
||||
if (splitIndices.stream()
|
||||
.anyMatch(idx -> !this.contains(idx))) {
|
||||
throw new IndexOutOfBoundsException(format("%s splitting indices are out of range for %s",
|
||||
splitIndices.stream()
|
||||
.filter(idx -> !this.contains(idx))
|
||||
.toList(),
|
||||
this));
|
||||
}
|
||||
List<TextRange> splitBoundaries = new LinkedList<>();
|
||||
int previousIndex = start;
|
||||
for (int splitIndex : splitIndices) {
|
||||
for (int i = 0, splitIndicesSize = splitIndices.size(); i < splitIndicesSize; i++) {
|
||||
int splitIndex = splitIndices.get(i);
|
||||
|
||||
// skip split if it would produce a boundary of length 0
|
||||
if (splitIndex == previousIndex) {
|
||||
@@ -113,8 +119,12 @@ public class TextRange implements Comparable<TextRange> {
|
||||
|
||||
public static TextRange merge(Collection<TextRange> boundaries) {
|
||||
|
||||
int minStart = boundaries.stream().mapToInt(TextRange::start).min().orElseThrow(IllegalArgumentException::new);
|
||||
int maxEnd = boundaries.stream().mapToInt(TextRange::end).max().orElseThrow(IllegalArgumentException::new);
|
||||
int minStart = boundaries.stream()
|
||||
.mapToInt(TextRange::start)
|
||||
.min().orElseThrow(IllegalArgumentException::new);
|
||||
int maxEnd = boundaries.stream()
|
||||
.mapToInt(TextRange::end)
|
||||
.max().orElseThrow(IllegalArgumentException::new);
|
||||
return new TextRange(minStart, maxEnd);
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -129,9 +129,19 @@ public class ManualChangeOverwrite {
|
||||
}
|
||||
|
||||
if (manualChange instanceof ManualRecategorization recategorization) {
|
||||
// recategorization logic happens in ManualChangesApplicationService.
|
||||
recategorized = true;
|
||||
type = recategorization.getType();
|
||||
if (recategorization.getLegalBasis() != null && !recategorization.getLegalBasis().isEmpty()) {
|
||||
// this is only relevant for ManualEntities. Image and TextEntity is recategorized in the ManualChangesApplicationService.
|
||||
if (recategorization.getType() != null) {
|
||||
type = recategorization.getType();
|
||||
}
|
||||
if (recategorization.getSection() != null) {
|
||||
section = recategorization.getSection();
|
||||
}
|
||||
if (recategorization.getValue() != null) {
|
||||
value = recategorization.getValue();
|
||||
}
|
||||
if (recategorization.getLegalBasis() != null) {
|
||||
legalBasis = recategorization.getLegalBasis();
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -14,7 +14,6 @@ import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -34,8 +33,6 @@ public class Document implements GenericSemanticNode {
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId = Collections.emptyList();
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
|
||||
Set<Page> pages;
|
||||
DocumentTree documentTree;
|
||||
|
||||
-3
@@ -9,7 +9,6 @@ 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.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -28,8 +27,6 @@ import lombok.experimental.FieldDefaults;
|
||||
public class Footer implements GenericSemanticNode {
|
||||
|
||||
final static SectionIdentifier sectionIdentifier = SectionIdentifier.empty();
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId;
|
||||
|
||||
-3
@@ -9,7 +9,6 @@ import java.util.Set;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -28,8 +27,6 @@ import lombok.experimental.FieldDefaults;
|
||||
public class Header implements GenericSemanticNode {
|
||||
|
||||
final static SectionIdentifier sectionIdentifier = SectionIdentifier.empty();
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId;
|
||||
|
||||
-3
@@ -10,7 +10,6 @@ import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.AtomicTextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -28,8 +27,6 @@ import lombok.experimental.FieldDefaults;
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class Headline implements GenericSemanticNode {
|
||||
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId;
|
||||
TextBlock leafTextBlock;
|
||||
|
||||
+12
-46
@@ -17,8 +17,6 @@ import com.iqser.red.service.redaction.v1.server.model.document.entity.ManualCha
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -36,13 +34,12 @@ import lombok.experimental.FieldDefaults;
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class Image implements GenericSemanticNode, IEntity {
|
||||
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId;
|
||||
String id;
|
||||
|
||||
TextBlock leafTextBlock;
|
||||
|
||||
ImageType imageType;
|
||||
boolean transparent;
|
||||
Rectangle2D position;
|
||||
@@ -53,14 +50,11 @@ public class Image implements GenericSemanticNode, IEntity {
|
||||
@Builder.Default
|
||||
ManualChangeOverwrite manualOverwrite = new ManualChangeOverwrite();
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
Page page;
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
DocumentTree documentTree;
|
||||
|
||||
@Builder.Default
|
||||
@EqualsAndHashCode.Exclude
|
||||
Set<TextEntity> entities = new HashSet<>();
|
||||
|
||||
|
||||
@@ -74,9 +68,7 @@ public class Image implements GenericSemanticNode, IEntity {
|
||||
@Override
|
||||
public TextBlock getTextBlock() {
|
||||
|
||||
return streamAllSubNodes().filter(SemanticNode::isLeaf)
|
||||
.map(SemanticNode::getLeafTextBlock)
|
||||
.collect(new TextBlockCollector());
|
||||
return leafTextBlock;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,15 +82,21 @@ public class Image implements GenericSemanticNode, IEntity {
|
||||
@Override
|
||||
public TextRange getTextRange() {
|
||||
|
||||
return GenericSemanticNode.super.getTextRange();
|
||||
return leafTextBlock.getTextRange();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
|
||||
return getTextRange().length();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
|
||||
return getManualOverwrite().getType()
|
||||
.orElse(imageType.toString());
|
||||
return getManualOverwrite().getType().orElse(imageType.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
|
||||
@@ -130,36 +128,4 @@ public class Image implements GenericSemanticNode, IEntity {
|
||||
return name.charAt(0) + name.substring(1).toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
|
||||
public boolean mostlyContainedBy(Image image, double containmentThreshold) {
|
||||
|
||||
Map<Page, Rectangle2D> bboxImage = image.getBBox();
|
||||
Map<Page, Rectangle2D> bbox = this.getBBox();
|
||||
//image needs to be on the same page
|
||||
if(bboxImage.get(this.page) != null) {
|
||||
Rectangle2D intersection = bboxImage.get(this.page).createIntersection(bbox.get(this.page));
|
||||
double calculatedIntersection = intersection.getWidth() * intersection.getHeight();
|
||||
double area = bbox.get(this.page).getWidth() * bbox.get(this.page).getHeight();
|
||||
return (calculatedIntersection / area) > containmentThreshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean mostlyContains(Image image, double containmentThreshold) {
|
||||
|
||||
Map<Page, Rectangle2D> bboxImage = image.getBBox();
|
||||
Map<Page, Rectangle2D> bbox = this.getBBox();
|
||||
Rectangle2D intersection = bbox.get(this.page).createIntersection(bboxImage.get(this.page));
|
||||
double calculatedIntersection = intersection.getWidth() * intersection.getHeight();
|
||||
double area = bbox.get(this.page).getWidth() * bbox.get(this.page).getHeight();
|
||||
return (area / calculatedIntersection) > containmentThreshold;
|
||||
}
|
||||
|
||||
|
||||
public int length() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-13
@@ -6,22 +6,10 @@ public enum ImageType {
|
||||
LOGO,
|
||||
FORMULA,
|
||||
SIGNATURE,
|
||||
OTHER {
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "image";
|
||||
}
|
||||
},
|
||||
OTHER,
|
||||
OCR;
|
||||
|
||||
|
||||
public String toString() {
|
||||
|
||||
return name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
|
||||
public static ImageType fromString(String imageType) {
|
||||
|
||||
return switch (imageType.toLowerCase(Locale.ROOT)) {
|
||||
|
||||
-4
@@ -9,7 +9,6 @@ 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.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -25,9 +24,6 @@ import lombok.experimental.FieldDefaults;
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class Paragraph implements GenericSemanticNode {
|
||||
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId;
|
||||
TextBlock leafTextBlock;
|
||||
|
||||
-6
@@ -10,14 +10,12 @@ import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntit
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -28,7 +26,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class Section implements GenericSemanticNode {
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId;
|
||||
|
||||
@@ -40,9 +37,6 @@ public class Section implements GenericSemanticNode {
|
||||
|
||||
Map<Page, Rectangle2D> bBoxCache;
|
||||
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
|
||||
|
||||
@Override
|
||||
public NodeType getType() {
|
||||
|
||||
+91
-35
@@ -22,7 +22,6 @@ import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBl
|
||||
import com.iqser.red.service.redaction.v1.server.service.document.NodeVisitor;
|
||||
import com.iqser.red.service.redaction.v1.server.utils.RectangleTransformations;
|
||||
import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
public interface SemanticNode {
|
||||
|
||||
@@ -71,7 +70,9 @@ public interface SemanticNode {
|
||||
*/
|
||||
default Page getFirstPage() {
|
||||
|
||||
return getTextBlock().getPages().stream().min(Comparator.comparingInt(Page::getNumber)).orElseThrow();
|
||||
return getTextBlock().getPages()
|
||||
.stream()
|
||||
.min(Comparator.comparingInt(Page::getNumber)).orElseThrow();
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +98,8 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean onPage(int pageNumber) {
|
||||
|
||||
return getPages().stream().anyMatch(page -> page.getNumber() == pageNumber);
|
||||
return getPages().stream()
|
||||
.anyMatch(page -> page.getNumber() == pageNumber);
|
||||
}
|
||||
|
||||
|
||||
@@ -249,7 +251,9 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean hasEntitiesOfType(String type) {
|
||||
|
||||
return getEntities().stream().filter(TextEntity::active).anyMatch(redactionEntity -> redactionEntity.type().equals(type));
|
||||
return getEntities().stream()
|
||||
.filter(TextEntity::active)
|
||||
.anyMatch(redactionEntity -> redactionEntity.type().equals(type));
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +266,10 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean hasEntitiesOfAnyType(String... types) {
|
||||
|
||||
return getEntities().stream().filter(TextEntity::active).anyMatch(redactionEntity -> Arrays.stream(types).anyMatch(type -> redactionEntity.type().equals(type)));
|
||||
return getEntities().stream()
|
||||
.filter(TextEntity::active)
|
||||
.anyMatch(redactionEntity -> Arrays.stream(types)
|
||||
.anyMatch(type -> redactionEntity.type().equals(type)));
|
||||
}
|
||||
|
||||
|
||||
@@ -275,7 +282,12 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean hasEntitiesOfAllTypes(String... types) {
|
||||
|
||||
return getEntities().stream().filter(TextEntity::active).map(TextEntity::type).collect(Collectors.toUnmodifiableSet()).containsAll(Arrays.stream(types).toList());
|
||||
return getEntities().stream()
|
||||
.filter(TextEntity::active)
|
||||
.map(TextEntity::type)
|
||||
.collect(Collectors.toUnmodifiableSet())
|
||||
.containsAll(Arrays.stream(types)
|
||||
.toList());
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +300,10 @@ public interface SemanticNode {
|
||||
*/
|
||||
default List<TextEntity> getEntitiesOfType(String type) {
|
||||
|
||||
return getEntities().stream().filter(TextEntity::active).filter(redactionEntity -> redactionEntity.type().equals(type)).toList();
|
||||
return getEntities().stream()
|
||||
.filter(TextEntity::active)
|
||||
.filter(redactionEntity -> redactionEntity.type().equals(type))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@@ -301,7 +316,10 @@ public interface SemanticNode {
|
||||
*/
|
||||
default List<TextEntity> getEntitiesOfType(List<String> types) {
|
||||
|
||||
return getEntities().stream().filter(TextEntity::active).filter(redactionEntity -> redactionEntity.isAnyType(types)).toList();
|
||||
return getEntities().stream()
|
||||
.filter(TextEntity::active)
|
||||
.filter(redactionEntity -> redactionEntity.isAnyType(types))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@@ -314,7 +332,11 @@ public interface SemanticNode {
|
||||
*/
|
||||
default List<TextEntity> getEntitiesOfType(String... types) {
|
||||
|
||||
return getEntities().stream().filter(TextEntity::active).filter(redactionEntity -> redactionEntity.isAnyType(Arrays.stream(types).toList())).toList();
|
||||
return getEntities().stream()
|
||||
.filter(TextEntity::active)
|
||||
.filter(redactionEntity -> redactionEntity.isAnyType(Arrays.stream(types)
|
||||
.toList()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@@ -357,13 +379,6 @@ public interface SemanticNode {
|
||||
return getTextBlock().getSearchText().contains(string);
|
||||
}
|
||||
|
||||
Set<LayoutEngine> getEngines();
|
||||
|
||||
default void addEngine(LayoutEngine engine) {
|
||||
getEngines().add(engine);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether this SemanticNode contains all the provided Strings.
|
||||
@@ -373,7 +388,8 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAllStrings(String... strings) {
|
||||
|
||||
return Arrays.stream(strings).allMatch(this::containsString);
|
||||
return Arrays.stream(strings)
|
||||
.allMatch(this::containsString);
|
||||
}
|
||||
|
||||
|
||||
@@ -385,7 +401,8 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAnyString(String... strings) {
|
||||
|
||||
return Arrays.stream(strings).anyMatch(this::containsString);
|
||||
return Arrays.stream(strings)
|
||||
.anyMatch(this::containsString);
|
||||
}
|
||||
|
||||
|
||||
@@ -397,7 +414,8 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAnyString(List<String> strings) {
|
||||
|
||||
return strings.stream().anyMatch(this::containsString);
|
||||
return strings.stream()
|
||||
.anyMatch(this::containsString);
|
||||
}
|
||||
|
||||
|
||||
@@ -421,7 +439,8 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAnyStringIgnoreCase(String... strings) {
|
||||
|
||||
return Arrays.stream(strings).anyMatch(this::containsStringIgnoreCase);
|
||||
return Arrays.stream(strings)
|
||||
.anyMatch(this::containsStringIgnoreCase);
|
||||
}
|
||||
|
||||
|
||||
@@ -433,7 +452,8 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAllStringsIgnoreCase(String... strings) {
|
||||
|
||||
return Arrays.stream(strings).allMatch(this::containsStringIgnoreCase);
|
||||
return Arrays.stream(strings)
|
||||
.allMatch(this::containsStringIgnoreCase);
|
||||
}
|
||||
|
||||
|
||||
@@ -445,7 +465,9 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsWord(String word) {
|
||||
|
||||
return getTextBlock().getWords().stream().anyMatch(s -> s.equals(word));
|
||||
return getTextBlock().getWords()
|
||||
.stream()
|
||||
.anyMatch(s -> s.equals(word));
|
||||
}
|
||||
|
||||
|
||||
@@ -457,7 +479,10 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsWordIgnoreCase(String word) {
|
||||
|
||||
return getTextBlock().getWords().stream().map(String::toLowerCase).anyMatch(s -> s.equals(word.toLowerCase(Locale.ENGLISH)));
|
||||
return getTextBlock().getWords()
|
||||
.stream()
|
||||
.map(String::toLowerCase)
|
||||
.anyMatch(s -> s.equals(word.toLowerCase(Locale.ENGLISH)));
|
||||
}
|
||||
|
||||
|
||||
@@ -469,7 +494,10 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAnyWord(String... words) {
|
||||
|
||||
return Arrays.stream(words).anyMatch(word -> getTextBlock().getWords().stream().anyMatch(word::equals));
|
||||
return Arrays.stream(words)
|
||||
.anyMatch(word -> getTextBlock().getWords()
|
||||
.stream()
|
||||
.anyMatch(word::equals));
|
||||
}
|
||||
|
||||
|
||||
@@ -481,7 +509,12 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAnyWordIgnoreCase(String... words) {
|
||||
|
||||
return Arrays.stream(words).map(String::toLowerCase).anyMatch(word -> getTextBlock().getWords().stream().map(String::toLowerCase).anyMatch(word::equals));
|
||||
return Arrays.stream(words)
|
||||
.map(String::toLowerCase)
|
||||
.anyMatch(word -> getTextBlock().getWords()
|
||||
.stream()
|
||||
.map(String::toLowerCase)
|
||||
.anyMatch(word::equals));
|
||||
}
|
||||
|
||||
|
||||
@@ -493,7 +526,10 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAllWords(String... words) {
|
||||
|
||||
return Arrays.stream(words).allMatch(word -> getTextBlock().getWords().stream().anyMatch(word::equals));
|
||||
return Arrays.stream(words)
|
||||
.allMatch(word -> getTextBlock().getWords()
|
||||
.stream()
|
||||
.anyMatch(word::equals));
|
||||
}
|
||||
|
||||
|
||||
@@ -505,7 +541,12 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean containsAllWordsIgnoreCase(String... words) {
|
||||
|
||||
return Arrays.stream(words).map(String::toLowerCase).allMatch(word -> getTextBlock().getWords().stream().map(String::toLowerCase).anyMatch(word::equals));
|
||||
return Arrays.stream(words)
|
||||
.map(String::toLowerCase)
|
||||
.allMatch(word -> getTextBlock().getWords()
|
||||
.stream()
|
||||
.map(String::toLowerCase)
|
||||
.anyMatch(word::equals));
|
||||
}
|
||||
|
||||
|
||||
@@ -545,7 +586,11 @@ public interface SemanticNode {
|
||||
*/
|
||||
default boolean intersectsRectangle(int x, int y, int w, int h, int pageNumber) {
|
||||
|
||||
return getBBox().entrySet().stream().filter(entry -> entry.getKey().getNumber() == pageNumber).map(Map.Entry::getValue).anyMatch(rect -> rect.intersects(x, y, w, h));
|
||||
return getBBox().entrySet()
|
||||
.stream()
|
||||
.filter(entry -> entry.getKey().getNumber() == pageNumber)
|
||||
.map(Map.Entry::getValue)
|
||||
.anyMatch(rect -> rect.intersects(x, y, w, h));
|
||||
}
|
||||
|
||||
|
||||
@@ -562,8 +607,9 @@ public interface SemanticNode {
|
||||
if (textBlock.containsTextRange(textEntity.getTextRange())) {
|
||||
textEntity.setDeepestFullyContainingNode(this);
|
||||
}
|
||||
|
||||
textEntity.addIntersectingNode(this);
|
||||
streamChildren().filter(semanticNode -> semanticNode.getTextRange().intersects(textEntity.getTextRange()))
|
||||
getDocumentTree().findIntersectingChildNodes(getTreeId(), textEntity.getTextRange())
|
||||
.forEach(node -> node.addThisToEntityIfIntersects(textEntity));
|
||||
}
|
||||
}
|
||||
@@ -598,7 +644,8 @@ public interface SemanticNode {
|
||||
*/
|
||||
default Stream<SemanticNode> streamAllSubNodes() {
|
||||
|
||||
return getDocumentTree().allSubEntriesInOrder(getTreeId()).map(DocumentTree.Entry::getNode);
|
||||
return getDocumentTree().allSubEntriesInOrder(getTreeId())
|
||||
.map(DocumentTree.Entry::getNode);
|
||||
}
|
||||
|
||||
|
||||
@@ -609,7 +656,9 @@ public interface SemanticNode {
|
||||
*/
|
||||
default Stream<SemanticNode> streamAllSubNodesOfType(NodeType nodeType) {
|
||||
|
||||
return getDocumentTree().allSubEntriesInOrder(getTreeId()).filter(entry -> entry.getType().equals(nodeType)).map(DocumentTree.Entry::getNode);
|
||||
return getDocumentTree().allSubEntriesInOrder(getTreeId())
|
||||
.filter(entry -> entry.getType().equals(nodeType))
|
||||
.map(DocumentTree.Entry::getNode);
|
||||
}
|
||||
|
||||
|
||||
@@ -648,7 +697,8 @@ public interface SemanticNode {
|
||||
if (isLeaf()) {
|
||||
return getTextBlock().getPositionsPerPage(textRange);
|
||||
}
|
||||
Optional<SemanticNode> containingChildNode = streamChildren().filter(child -> child.getTextRange().contains(textRange)).findFirst();
|
||||
Optional<SemanticNode> containingChildNode = getDocumentTree().findFirstContainingChild(getTreeId(), textRange);
|
||||
|
||||
if (containingChildNode.isEmpty()) {
|
||||
return getTextBlock().getPositionsPerPage(textRange);
|
||||
}
|
||||
@@ -698,8 +748,12 @@ public interface SemanticNode {
|
||||
private Map<Page, Rectangle2D> getBBoxFromChildren() {
|
||||
|
||||
Map<Page, Rectangle2D> bBoxPerPage = new HashMap<>();
|
||||
List<Map<Page, Rectangle2D>> childrenBBoxes = streamChildren().map(SemanticNode::getBBox).toList();
|
||||
Set<Page> pages = childrenBBoxes.stream().flatMap(map -> map.keySet().stream()).collect(Collectors.toSet());
|
||||
List<Map<Page, Rectangle2D>> childrenBBoxes = streamChildren().map(SemanticNode::getBBox)
|
||||
.toList();
|
||||
Set<Page> pages = childrenBBoxes.stream()
|
||||
.flatMap(map -> map.keySet()
|
||||
.stream())
|
||||
.collect(Collectors.toSet());
|
||||
for (Page page : pages) {
|
||||
Rectangle2D bBoxOnPage = childrenBBoxes.stream()
|
||||
.filter(childBboxPerPage -> childBboxPerPage.containsKey(page))
|
||||
@@ -717,7 +771,9 @@ public interface SemanticNode {
|
||||
private Map<Page, Rectangle2D> getBBoxFromLeafTextBlock() {
|
||||
|
||||
Map<Page, Rectangle2D> bBoxPerPage = new HashMap<>();
|
||||
Map<Page, List<AtomicTextBlock>> atomicTextBlockPerPage = getTextBlock().getAtomicTextBlocks().stream().collect(Collectors.groupingBy(AtomicTextBlock::getPage));
|
||||
Map<Page, List<AtomicTextBlock>> atomicTextBlockPerPage = getTextBlock().getAtomicTextBlocks()
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(AtomicTextBlock::getPage));
|
||||
atomicTextBlockPerPage.forEach((page, atomicTextBlocks) -> bBoxPerPage.put(page, RectangleTransformations.atomicTextBlockBBox(atomicTextBlocks)));
|
||||
return bBoxPerPage;
|
||||
}
|
||||
|
||||
-3
@@ -17,7 +17,6 @@ import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -33,8 +32,6 @@ import lombok.experimental.FieldDefaults;
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class Table implements SemanticNode {
|
||||
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId;
|
||||
DocumentTree documentTree;
|
||||
|
||||
-4
@@ -11,7 +11,6 @@ import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntit
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -27,9 +26,6 @@ import lombok.experimental.FieldDefaults;
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class TableCell implements GenericSemanticNode {
|
||||
|
||||
@Builder.Default
|
||||
Set<LayoutEngine> engines = new HashSet<>(Set.of(LayoutEngine.ALGORITHM));
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
List<Integer> treeId;
|
||||
int row;
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class MessageReceiver {
|
||||
|
||||
|
||||
@RabbitHandler
|
||||
@RabbitListener(queues = REDACTION_QUEUE)
|
||||
@RabbitListener(queues = REDACTION_QUEUE, concurrency = "1")
|
||||
public void receiveAnalyzeRequest(Message message) {
|
||||
|
||||
redactionMessageReceiver.receiveAnalyzeRequest(message, false);
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class PriorityMessageReceiver {
|
||||
|
||||
|
||||
@RabbitHandler
|
||||
@RabbitListener(queues = REDACTION_PRIORITY_QUEUE)
|
||||
@RabbitListener(queues = REDACTION_PRIORITY_QUEUE, concurrency = "1")
|
||||
public void receiveAnalyzeRequest(Message message) {
|
||||
|
||||
redactionMessageReceiver.receiveAnalyzeRequest(message, true);
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ public class RedactionMessageReceiver {
|
||||
|
||||
private void sendAnalysisFailed(AnalyzeRequest analyzeRequest, boolean priority, Exception e) {
|
||||
|
||||
log.warn("Failed to process analyze request: {}", analyzeRequest, e);
|
||||
log.error("Failed to process analyze request: {}", analyzeRequest, e);
|
||||
var timestamp = OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS);
|
||||
fileStatusProcessingUpdateClient.analysisFailed(analyzeRequest.getDossierId(),
|
||||
analyzeRequest.getFileId(),
|
||||
|
||||
+13
-26
@@ -1,7 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.server.service;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -13,9 +12,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.ChangeType;
|
||||
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.ManualChange;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.AccessLevel;
|
||||
@@ -41,9 +38,11 @@ public class EntityChangeLogService {
|
||||
boolean hasChanges = false;
|
||||
|
||||
for (EntityLogEntry entityLogEntry : newEntityLogEntries) {
|
||||
|
||||
Optional<EntityLogEntry> optionalPreviousEntity = previousEntityLogEntries.stream()
|
||||
.filter(entry -> entry.getId().equals(entityLogEntry.getId()))
|
||||
.findAny();
|
||||
|
||||
if (optionalPreviousEntity.isEmpty()) {
|
||||
hasChanges = true;
|
||||
entityLogEntry.getChanges().add(new Change(analysisNumber, ChangeType.ADDED, now));
|
||||
@@ -51,14 +50,18 @@ public class EntityChangeLogService {
|
||||
}
|
||||
|
||||
EntityLogEntry previousEntity = optionalPreviousEntity.get();
|
||||
|
||||
entityLogEntry.getChanges().addAll(previousEntity.getChanges());
|
||||
|
||||
if (!previousEntity.getState().equals(entityLogEntry.getState())) {
|
||||
hasChanges = true;
|
||||
ChangeType changeType = calculateChangeType(entityLogEntry.getState(), previousEntity.getState());
|
||||
entityLogEntry.getChanges().add(new Change(analysisNumber, changeType, now));
|
||||
}
|
||||
}
|
||||
|
||||
addRemovedEntriesAsRemoved(previousEntityLogEntries, newEntityLogEntries, manualRedactions, analysisNumber, now);
|
||||
|
||||
return hasChanges;
|
||||
}
|
||||
|
||||
@@ -72,36 +75,20 @@ public class EntityChangeLogService {
|
||||
Set<String> existingIds = newEntityLogEntries.stream()
|
||||
.map(EntityLogEntry::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<EntityLogEntry> removedEntries = previousEntityLogEntries.stream()
|
||||
.filter(entry -> !existingIds.contains(entry.getId()))
|
||||
.collect(Collectors.toList());
|
||||
List<EntityLogEntry> removedDossierRedaction = removedEntries.stream()
|
||||
.filter(e -> e.getState() == EntryState.REMOVED && e.getType().equals("dossier_redaction"))
|
||||
.toList();
|
||||
previousEntityLogEntries.removeAll(removedDossierRedaction);
|
||||
removedEntries.removeAll(removedDossierRedaction);
|
||||
removedEntries.forEach(entry -> entry.getChanges().add(new Change(analysisNumber, ChangeType.REMOVED, now)));
|
||||
removedEntries.forEach(entry -> entry.setState(EntryState.REMOVED));
|
||||
removedEntries.forEach(entry -> addManualChangeForDictionaryRemovals(entry, manualRedactions));
|
||||
|
||||
removedEntries.stream()
|
||||
.filter(entry -> !entry.getState().equals(EntryState.REMOVED))
|
||||
.peek(entry -> entry.getChanges().add(new Change(analysisNumber, ChangeType.REMOVED, now)))
|
||||
.forEach(entry -> entry.setState(EntryState.REMOVED));
|
||||
|
||||
newEntityLogEntries.addAll(removedEntries);
|
||||
}
|
||||
|
||||
|
||||
private void addManualChangeForDictionaryRemovals(EntityLogEntry entry, ManualRedactions manualRedactions) {
|
||||
|
||||
if (manualRedactions == null || manualRedactions.getIdsToRemove().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
manualRedactions.getIdsToRemove()
|
||||
.stream()
|
||||
.filter(IdRemoval::isRemoveFromDictionary)//
|
||||
.filter(removed -> removed.getAnnotationId().equals(entry.getId()))//
|
||||
.findFirst()//
|
||||
.ifPresent(idRemove -> entry.getManualChanges().add(ManualChangeFactory.toManualChange(idRemove, false)));
|
||||
}
|
||||
|
||||
|
||||
private ChangeType calculateChangeType(EntryState state, EntryState previousState) {
|
||||
|
||||
if (state.equals(previousState)) {
|
||||
|
||||
+17
-8
@@ -19,6 +19,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.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.Position;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualChangeFactory;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.legalbasis.LegalBasis;
|
||||
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
|
||||
import com.iqser.red.service.redaction.v1.server.client.LegalBasisClient;
|
||||
@@ -32,6 +33,7 @@ import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntit
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
@@ -186,11 +188,12 @@ public class EntityLogCreatorService {
|
||||
|
||||
private EntityLogEntry createEntityLogEntry(Image image, String dossierTemplateId) {
|
||||
|
||||
boolean isHint = dictionaryService.isHint(image.type(), dossierTemplateId);
|
||||
String imageType = image.getImageType().equals(ImageType.OTHER) ? "image" : image.getImageType().toString().toLowerCase(Locale.ENGLISH);
|
||||
boolean isHint = dictionaryService.isHint(imageType, dossierTemplateId);
|
||||
return EntityLogEntry.builder()
|
||||
.id(image.getId())
|
||||
.value(image.value())
|
||||
.type(image.type())
|
||||
.value(image.getValue())
|
||||
.type(imageType)
|
||||
.reason(image.buildReasonWithManualChangeDescriptions())
|
||||
.legalBasis(image.legalBasis())
|
||||
.matchedRule(image.getMatchedRule().getRuleIdentifier().toString())
|
||||
@@ -199,9 +202,9 @@ public class EntityLogCreatorService {
|
||||
.containingNodeId(image.getTreeId())
|
||||
.closestHeadline(image.getHeadline().getTextBlock().getSearchText())
|
||||
.section(image.getManualOverwrite().getSection()
|
||||
.orElse(image.getParent().toString()))
|
||||
.orElse(this.buildSectionString(image.getParent())))
|
||||
.imageHasTransparency(image.isTransparent())
|
||||
.manualChanges(ManualChangeFactory.toManualChangeList(image.getManualOverwrite().getManualChangeLog(), isHint))
|
||||
.manualChanges(ManualChangeFactory.toLocalManualChangeList(image.getManualOverwrite().getManualChangeLog(), true))
|
||||
.state(buildEntryState(image))
|
||||
.entryType(isHint ? EntryType.IMAGE_HINT : EntryType.IMAGE)
|
||||
.engines(getEngines(null, image.getManualOverwrite()))
|
||||
@@ -244,7 +247,7 @@ public class EntityLogCreatorService {
|
||||
//(was .imported(precursorEntity.getEngines() != null && precursorEntity.getEngines().contains(Engine.IMPORTED)))
|
||||
.imported(false)
|
||||
.reference(Collections.emptySet())
|
||||
.manualChanges(ManualChangeFactory.toManualChangeList(precursorEntity.getManualOverwrite().getManualChangeLog(), isHint))
|
||||
.manualChanges(ManualChangeFactory.toLocalManualChangeList(precursorEntity.getManualOverwrite().getManualChangeLog(), true))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -265,7 +268,7 @@ public class EntityLogCreatorService {
|
||||
.orElse(entity.getMatchedRule().isWriteValueWithLineBreaks() ? entity.getValueWithLineBreaks() : entity.getValue()))
|
||||
.type(entity.type())
|
||||
.section(entity.getManualOverwrite().getSection()
|
||||
.orElse(entity.getDeepestFullyContainingNode().toString()))
|
||||
.orElse(this.buildSectionString(entity.getDeepestFullyContainingNode())))
|
||||
.containingNodeId(entity.getDeepestFullyContainingNode().getTreeId())
|
||||
.closestHeadline(entity.getDeepestFullyContainingNode().getHeadline().getTextBlock().getSearchText())
|
||||
.matchedRule(entity.getMatchedRule().getRuleIdentifier().toString())
|
||||
@@ -280,7 +283,7 @@ public class EntityLogCreatorService {
|
||||
//(was .imported(entity.getEngines() != null && entity.getEngines().contains(Engine.IMPORTED)))
|
||||
.imported(false)
|
||||
.reference(referenceIds)
|
||||
.manualChanges(ManualChangeFactory.toManualChangeList(entity.getManualOverwrite().getManualChangeLog(), isHint))
|
||||
.manualChanges(ManualChangeFactory.toLocalManualChangeList(entity.getManualOverwrite().getManualChangeLog(), true))
|
||||
.state(buildEntryState(entity))
|
||||
.entryType(buildEntryType(entity))
|
||||
.build();
|
||||
@@ -353,4 +356,10 @@ public class EntityLogCreatorService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
private String buildSectionString(SemanticNode node) {
|
||||
|
||||
return node.getType().toString() + ": " + node.getTextBlock().buildSummary();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
package com.iqser.red.service.redaction.v1.server.service;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.ManualRedactionType;
|
||||
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.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 lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class ManualChangeFactory {
|
||||
|
||||
public List<ManualChange> toManualChangeList(List<BaseAnnotation> manualChanges, boolean isHint) {
|
||||
|
||||
return manualChanges.stream()
|
||||
.map(baseAnnotation -> toManualChange(baseAnnotation, isHint))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public ManualChange toManualChange(BaseAnnotation baseAnnotation, boolean isHint) {
|
||||
|
||||
ManualChange manualChange = ManualChange.from(baseAnnotation);
|
||||
if (baseAnnotation instanceof ManualRecategorization imageRecategorization) {
|
||||
manualChange.withManualRedactionType(ManualRedactionType.RECATEGORIZE).withChange("type", imageRecategorization.getType());
|
||||
} else if (baseAnnotation instanceof IdRemoval manualRemoval) {
|
||||
manualChange.withManualRedactionType(manualRemoval.isRemoveFromDictionary() ? ManualRedactionType.REMOVE_FROM_DICTIONARY : ManualRedactionType.REMOVE);
|
||||
} else if (baseAnnotation instanceof ManualForceRedaction manualForceRedaction) {
|
||||
manualChange.withManualRedactionType(ManualRedactionType.FORCE).withChange("legalBasis", manualForceRedaction.getLegalBasis());
|
||||
} else if (baseAnnotation instanceof ManualResizeRedaction manualResizeRedact) {
|
||||
manualChange.withManualRedactionType(manualResizeRedact.getUpdateDictionary() ? ManualRedactionType.RESIZE_IN_DICTIONARY : ManualRedactionType.RESIZE)
|
||||
.withChange("value", manualResizeRedact.getValue());
|
||||
} else if (baseAnnotation instanceof ManualRedactionEntry manualRedactionEntry) {
|
||||
manualChange.withManualRedactionType(manualRedactionEntry.isAddToDictionary() ? ManualRedactionType.ADD_TO_DICTIONARY : ManualRedactionType.ADD)
|
||||
.withChange("value", manualRedactionEntry.getValue());
|
||||
} else if (baseAnnotation instanceof ManualLegalBasisChange manualLegalBasisChange) {
|
||||
manualChange.withManualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE)
|
||||
.withChange("section", manualLegalBasisChange.getSection())
|
||||
.withChange("value", manualLegalBasisChange.getValue())
|
||||
.withChange("legalBasis", manualLegalBasisChange.getLegalBasis());
|
||||
}
|
||||
manualChange.setProcessedDate(OffsetDateTime.now());
|
||||
return manualChange;
|
||||
}
|
||||
|
||||
}
|
||||
+27
-10
@@ -47,6 +47,10 @@ public class ManualChangesApplicationService {
|
||||
entityToBeReCategorized.getMatchedRuleList().clear();
|
||||
entityToBeReCategorized.getManualOverwrite().addChange(manualRecategorization);
|
||||
|
||||
if (manualRecategorization.getType() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entityToBeReCategorized instanceof Image image) {
|
||||
image.setImageType(ImageType.fromString(manualRecategorization.getType()));
|
||||
return;
|
||||
@@ -74,9 +78,9 @@ public class ManualChangesApplicationService {
|
||||
.orElseThrow(() -> new NoSuchElementException("No redaction position with matching annotation id found!"));
|
||||
|
||||
positionOnPageToBeResized.setRectanglePerLine(manualResizeRedaction.getPositions()
|
||||
.stream()
|
||||
.map(ManualChangesApplicationService::toRectangle2D)
|
||||
.collect(Collectors.toList()));
|
||||
.stream()
|
||||
.map(ManualChangesApplicationService::toRectangle2D)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
entityToBeResized.getManualOverwrite().addChange(manualResizeRedaction);
|
||||
|
||||
@@ -90,11 +94,17 @@ public class ManualChangesApplicationService {
|
||||
|
||||
if (closestEntity.isPresent()) {
|
||||
copyValuesFromClosestEntity(entityToBeResized, manualResizeRedaction, closestEntity.get());
|
||||
possibleEntities.values().stream().flatMap(Collection::stream).forEach(TextEntity::removeFromGraph);
|
||||
possibleEntities.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.forEach(TextEntity::removeFromGraph);
|
||||
return;
|
||||
}
|
||||
|
||||
possibleEntities.values().stream().flatMap(Collection::stream).forEach(TextEntity::removeFromGraph);
|
||||
possibleEntities.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.forEach(TextEntity::removeFromGraph);
|
||||
|
||||
if (node.hasParent()) {
|
||||
node = node.getParent();
|
||||
@@ -110,14 +120,18 @@ public class ManualChangesApplicationService {
|
||||
Set<SemanticNode> currentIntersectingNodes = new HashSet<>(entityToBeResized.getIntersectingNodes());
|
||||
Set<SemanticNode> newIntersectingNodes = new HashSet<>(closestEntity.getIntersectingNodes());
|
||||
|
||||
Sets.difference(currentIntersectingNodes, newIntersectingNodes).forEach(removedNode -> removedNode.getEntities().remove(entityToBeResized));
|
||||
Sets.difference(newIntersectingNodes, currentIntersectingNodes).forEach(addedNode -> addedNode.getEntities().add(entityToBeResized));
|
||||
Sets.difference(currentIntersectingNodes, newIntersectingNodes)
|
||||
.forEach(removedNode -> removedNode.getEntities().remove(entityToBeResized));
|
||||
Sets.difference(newIntersectingNodes, currentIntersectingNodes)
|
||||
.forEach(addedNode -> addedNode.getEntities().add(entityToBeResized));
|
||||
|
||||
Set<Page> currentIntersectingPages = new HashSet<>(entityToBeResized.getPages());
|
||||
Set<Page> newIntersectingPages = new HashSet<>(closestEntity.getPages());
|
||||
|
||||
Sets.difference(currentIntersectingPages, newIntersectingPages).forEach(removedPage -> removedPage.getEntities().remove(entityToBeResized));
|
||||
Sets.difference(newIntersectingPages, currentIntersectingPages).forEach(addedPage -> addedPage.getEntities().add(entityToBeResized));
|
||||
Sets.difference(currentIntersectingPages, newIntersectingPages)
|
||||
.forEach(removedPage -> removedPage.getEntities().remove(entityToBeResized));
|
||||
Sets.difference(newIntersectingPages, currentIntersectingPages)
|
||||
.forEach(addedPage -> addedPage.getEntities().add(entityToBeResized));
|
||||
|
||||
entityToBeResized.setDeepestFullyContainingNode(closestEntity.getDeepestFullyContainingNode());
|
||||
entityToBeResized.setIntersectingNodes(new ArrayList<>(newIntersectingNodes));
|
||||
@@ -135,7 +149,10 @@ public class ManualChangesApplicationService {
|
||||
if (manualResizeRedaction.getPositions().isEmpty() || manualResizeRedaction.getPositions() == null) {
|
||||
return;
|
||||
}
|
||||
var bBox = RectangleTransformations.rectangle2DBBox(manualResizeRedaction.getPositions().stream().map(ManualChangesApplicationService::toRectangle2D).toList());
|
||||
var bBox = RectangleTransformations.rectangle2DBBox(manualResizeRedaction.getPositions()
|
||||
.stream()
|
||||
.map(ManualChangesApplicationService::toRectangle2D)
|
||||
.toList());
|
||||
image.setPosition(bBox);
|
||||
image.getManualOverwrite().addChange(manualResizeRedaction);
|
||||
}
|
||||
|
||||
+25
-18
@@ -1,29 +1,29 @@
|
||||
package com.iqser.red.service.redaction.v1.server.service.document;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.DocumentData;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Footer;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Header;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Paragraph;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.AtomicTextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.DocumentData;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPage;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionData;
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure;
|
||||
@@ -40,7 +40,9 @@ public class DocumentGraphMapper {
|
||||
DocumentTree documentTree = new DocumentTree(document);
|
||||
Context context = new Context(documentData, documentTree);
|
||||
|
||||
context.pageData.addAll(Arrays.stream(documentData.getDocumentPages()).map(DocumentGraphMapper::buildPage).toList());
|
||||
context.pageData.addAll(Arrays.stream(documentData.getDocumentPages())
|
||||
.map(DocumentGraphMapper::buildPage)
|
||||
.toList());
|
||||
|
||||
context.documentTree.getRoot().getChildren().addAll(buildEntries(documentData.getDocumentStructure().getRoot().getChildren(), context));
|
||||
|
||||
@@ -55,10 +57,12 @@ public class DocumentGraphMapper {
|
||||
|
||||
private List<DocumentTree.Entry> buildEntries(List<DocumentStructure.EntryData> entries, Context context) {
|
||||
|
||||
List<DocumentTree.Entry> newEntries = new LinkedList<>();
|
||||
List<DocumentTree.Entry> newEntries = new ArrayList<>(entries.size());
|
||||
for (DocumentStructure.EntryData entryData : entries) {
|
||||
|
||||
List<Page> pages = Arrays.stream(entryData.getPageNumbers()).map(pageNumber -> getPage(pageNumber, context)).toList();
|
||||
List<Page> pages = Arrays.stream(entryData.getPageNumbers())
|
||||
.map(pageNumber -> getPage(pageNumber, context))
|
||||
.toList();
|
||||
|
||||
SemanticNode node = switch (entryData.getType()) {
|
||||
case SECTION -> buildSection(context);
|
||||
@@ -76,8 +80,8 @@ public class DocumentGraphMapper {
|
||||
TextBlock textBlock = toTextBlock(entryData.getAtomicBlockIds(), context, node);
|
||||
node.setLeafTextBlock(textBlock);
|
||||
}
|
||||
List<Integer> treeId = Arrays.stream(entryData.getTreeId()).boxed().toList();
|
||||
entryData.getEngines().forEach(engine -> node.addEngine(engine));
|
||||
List<Integer> treeId = Arrays.stream(entryData.getTreeId()).boxed()
|
||||
.toList();
|
||||
node.setTreeId(treeId);
|
||||
|
||||
switch (entryData.getType()) {
|
||||
@@ -150,16 +154,18 @@ public class DocumentGraphMapper {
|
||||
|
||||
private TextBlock toTextBlock(Long[] atomicTextBlockIds, Context context, SemanticNode parent) {
|
||||
|
||||
return Arrays.stream(atomicTextBlockIds).map(atomicTextBlockId -> getAtomicTextBlock(context, parent, atomicTextBlockId)).collect(new TextBlockCollector());
|
||||
return Arrays.stream(atomicTextBlockIds)
|
||||
.map(atomicTextBlockId -> getAtomicTextBlock(context, parent, atomicTextBlockId))
|
||||
.collect(new TextBlockCollector());
|
||||
}
|
||||
|
||||
|
||||
private AtomicTextBlock getAtomicTextBlock(Context context, SemanticNode parent, Long atomicTextBlockId) {
|
||||
|
||||
return AtomicTextBlock.fromAtomicTextBlockData(context.documentTextData.get(Math.toIntExact(atomicTextBlockId)),
|
||||
context.documentPositionData.get(Math.toIntExact(atomicTextBlockId)),
|
||||
parent,
|
||||
getPage(context.documentTextData.get(Math.toIntExact(atomicTextBlockId)).getPage(), context));
|
||||
context.documentPositionData.get(Math.toIntExact(atomicTextBlockId)),
|
||||
parent,
|
||||
getPage(context.documentTextData.get(Math.toIntExact(atomicTextBlockId)).getPage(), context));
|
||||
}
|
||||
|
||||
|
||||
@@ -173,8 +179,7 @@ public class DocumentGraphMapper {
|
||||
|
||||
return context.pageData.stream()
|
||||
.filter(page -> page.getNumber() == Math.toIntExact(pageIndex))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NoSuchElementException(String.format("ClassificationPage with number %d not found", pageIndex)));
|
||||
.findFirst().orElseThrow(() -> new NoSuchElementException(String.format("ClassificationPage with number %d not found", pageIndex)));
|
||||
}
|
||||
|
||||
|
||||
@@ -190,8 +195,10 @@ public class DocumentGraphMapper {
|
||||
|
||||
this.documentTree = documentTree;
|
||||
this.pageData = new LinkedList<>();
|
||||
this.documentTextData = Arrays.stream(documentData.getDocumentTextData()).toList();
|
||||
this.documentPositionData = Arrays.stream(documentData.getDocumentPositionData()).toList();
|
||||
this.documentTextData = Arrays.stream(documentData.getDocumentTextData())
|
||||
.toList();
|
||||
this.documentPositionData = Arrays.stream(documentData.getDocumentPositionData())
|
||||
.toList();
|
||||
|
||||
}
|
||||
|
||||
|
||||
+76
-52
@@ -1,14 +1,18 @@
|
||||
package com.iqser.red.service.redaction.v1.server.service.document;
|
||||
|
||||
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.*;
|
||||
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.addEntityToNodeEntitySets;
|
||||
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.addToPages;
|
||||
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.allEntitiesIntersectAndHaveSameTypes;
|
||||
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.checkIfBothStartAndEndAreEmpty;
|
||||
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.findIntersectingSubNodes;
|
||||
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.toLineAfterTextRange;
|
||||
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.truncateEndIfLineBreakIsBetween;
|
||||
import static com.iqser.red.service.redaction.v1.server.utils.SeparatorUtils.boundaryIsSurroundedBySeparators;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -276,7 +280,8 @@ public class EntityCreationService {
|
||||
"this is some text. a here is more text" and "here is more text". We only want to keep the latter.
|
||||
*/
|
||||
return entityTextRanges.stream()
|
||||
.filter(boundary -> entityTextRanges.stream().noneMatch(innerBoundary -> !innerBoundary.equals(boundary) && innerBoundary.containedBy(boundary)))
|
||||
.filter(boundary -> entityTextRanges.stream()
|
||||
.noneMatch(innerBoundary -> !innerBoundary.equals(boundary) && innerBoundary.containedBy(boundary)))
|
||||
.toList();
|
||||
|
||||
}
|
||||
@@ -351,10 +356,10 @@ public class EntityCreationService {
|
||||
|
||||
return tableNode.streamTableCells()
|
||||
.flatMap(tableCell -> lineAfterBoundariesAcrossColumns(RedactionSearchUtility.findTextRangesByString(string, tableCell.getTextBlock()),
|
||||
tableCell,
|
||||
type,
|
||||
entityType,
|
||||
tableNode));
|
||||
tableCell,
|
||||
type,
|
||||
entityType,
|
||||
tableNode));
|
||||
}
|
||||
|
||||
|
||||
@@ -362,10 +367,10 @@ public class EntityCreationService {
|
||||
|
||||
return tableNode.streamTableCells()
|
||||
.flatMap(tableCell -> lineAfterBoundariesAcrossColumns(RedactionSearchUtility.findTextRangesByStringIgnoreCase(string, tableCell.getTextBlock()),
|
||||
tableCell,
|
||||
type,
|
||||
entityType,
|
||||
tableNode));
|
||||
tableCell,
|
||||
type,
|
||||
entityType,
|
||||
tableNode));
|
||||
}
|
||||
|
||||
|
||||
@@ -500,7 +505,10 @@ public class EntityCreationService {
|
||||
|
||||
public Stream<TextEntity> bySemanticNodeParagraphsOnly(SemanticNode node, String type, EntityType entityType) {
|
||||
|
||||
return node.streamAllSubNodesOfType(NodeType.PARAGRAPH).map(semanticNode -> bySemanticNode(semanticNode, type, entityType)).filter(Optional::isPresent).map(Optional::get);
|
||||
return node.streamAllSubNodesOfType(NodeType.PARAGRAPH)
|
||||
.map(semanticNode -> bySemanticNode(semanticNode, type, entityType))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get);
|
||||
}
|
||||
|
||||
|
||||
@@ -590,11 +598,18 @@ public class EntityCreationService {
|
||||
throw new IllegalArgumentException(String.format("%s is not in the %s of the provided semantic node %s", textRange, node.getTextRange(), node));
|
||||
}
|
||||
TextRange trimmedTextRange = textRange.trim(node.getTextBlock());
|
||||
if (trimmedTextRange.length() == 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
TextEntity entity = TextEntity.initialEntityNode(trimmedTextRange, type, entityType, node);
|
||||
if (node.getEntities().contains(entity)) {
|
||||
Optional<TextEntity> optionalTextEntity = node.getEntities().stream().filter(e -> e.equals(entity) && e.type().equals(type)).peek(e -> e.addEngines(engines)).findAny();
|
||||
Optional<TextEntity> optionalTextEntity = node.getEntities()
|
||||
.stream()
|
||||
.filter(e -> e.equals(entity) && e.type().equals(type))
|
||||
.peek(e -> e.addEngines(engines))
|
||||
.findAny();
|
||||
if (optionalTextEntity.isEmpty()) {
|
||||
return optionalTextEntity; // Entity has been recategorized and should not be created at all.
|
||||
return Optional.empty(); // Entity has been recategorized and should not be created at all.
|
||||
}
|
||||
TextEntity existingEntity = optionalTextEntity.get();
|
||||
if (existingEntity.getTextRange().equals(textRange)) {
|
||||
@@ -606,7 +621,7 @@ public class EntityCreationService {
|
||||
}
|
||||
return Optional.empty(); // Entity has been resized, if there are duplicates they should be treated there
|
||||
}
|
||||
addEntityToGraph(entity, node);
|
||||
addEntityToGraph(entity, node.getDocumentTree());
|
||||
entity.addEngines(engines);
|
||||
insertToKieSession(entity);
|
||||
return Optional.of(entity);
|
||||
@@ -635,6 +650,8 @@ public class EntityCreationService {
|
||||
}
|
||||
|
||||
|
||||
// Do not use anymore. This might not work correctly due to duplicate textranges not being taken into account here.
|
||||
@Deprecated(forRemoval = true)
|
||||
public TextEntity mergeEntitiesOfSameType(List<TextEntity> entitiesToMerge, String type, EntityType entityType, SemanticNode node) {
|
||||
|
||||
if (!allEntitiesIntersectAndHaveSameTypes(entitiesToMerge)) {
|
||||
@@ -647,27 +664,44 @@ public class EntityCreationService {
|
||||
return entitiesToMerge.get(0);
|
||||
}
|
||||
|
||||
TextEntity mergedEntity = TextEntity.initialEntityNode(TextRange.merge(entitiesToMerge.stream().map(TextEntity::getTextRange).toList()), type, entityType, node);
|
||||
mergedEntity.addEngines(entitiesToMerge.stream().flatMap(entityNode -> entityNode.getEngines().stream()).collect(Collectors.toSet()));
|
||||
entitiesToMerge.stream().map(TextEntity::getMatchedRuleList).flatMap(Collection::stream).forEach(matchedRule -> mergedEntity.getMatchedRuleList().add(matchedRule));
|
||||
TextEntity mergedEntity = TextEntity.initialEntityNode(TextRange.merge(entitiesToMerge.stream()
|
||||
.map(TextEntity::getTextRange)
|
||||
.toList()), type, entityType, node);
|
||||
mergedEntity.addEngines(entitiesToMerge.stream()
|
||||
.flatMap(entityNode -> entityNode.getEngines()
|
||||
.stream())
|
||||
.collect(Collectors.toSet()));
|
||||
entitiesToMerge.stream()
|
||||
.map(TextEntity::getMatchedRuleList)
|
||||
.flatMap(Collection::stream)
|
||||
.forEach(matchedRule -> mergedEntity.getMatchedRuleList().add(matchedRule));
|
||||
entitiesToMerge.stream()
|
||||
.map(TextEntity::getManualOverwrite)
|
||||
.map(ManualChangeOverwrite::getManualChangeLog)
|
||||
.flatMap(Collection::stream)
|
||||
.forEach(manualChange -> mergedEntity.getManualOverwrite().addChange(manualChange));
|
||||
|
||||
mergedEntity.setDictionaryEntry(entitiesToMerge.stream().anyMatch(TextEntity::isDictionaryEntry));
|
||||
mergedEntity.setDossierDictionaryEntry(entitiesToMerge.stream().anyMatch(TextEntity::isDossierDictionaryEntry));
|
||||
mergedEntity.setDictionaryEntry(entitiesToMerge.stream()
|
||||
.anyMatch(TextEntity::isDictionaryEntry));
|
||||
mergedEntity.setDossierDictionaryEntry(entitiesToMerge.stream()
|
||||
.anyMatch(TextEntity::isDossierDictionaryEntry));
|
||||
|
||||
entityEnrichmentService.enrichEntity(mergedEntity, node.getTextBlock());
|
||||
|
||||
addEntityToGraph(mergedEntity, node);
|
||||
insertToKieSession(mergedEntity);
|
||||
|
||||
entitiesToMerge.stream()
|
||||
.filter(e -> !e.equals(mergedEntity))
|
||||
.forEach(node.getEntities()::remove);
|
||||
return mergedEntity;
|
||||
}
|
||||
|
||||
|
||||
public Stream<TextEntity> copyEntities(List<TextEntity> entities, String type, EntityType entityType, SemanticNode node) {
|
||||
|
||||
return entities.stream().map(entity -> copyEntity(entity, type, entityType, node));
|
||||
return entities.stream()
|
||||
.map(entity -> copyEntity(entity, type, entityType, node));
|
||||
}
|
||||
|
||||
|
||||
@@ -741,25 +775,19 @@ public class EntityCreationService {
|
||||
public void addEntityToGraph(TextEntity entity, SemanticNode node) {
|
||||
|
||||
DocumentTree documentTree = node.getDocumentTree();
|
||||
try {
|
||||
if (node.getEntities().contains(entity)) {
|
||||
// If entity already exists and it has a different text range, we add the text range to the list of duplicated text ranges
|
||||
node.getEntities().stream()//
|
||||
.filter(e -> e.equals(entity))//
|
||||
.filter(e -> !e.getTextRange().equals(entity.getTextRange()))//
|
||||
.findAny()//
|
||||
.ifPresent(entityToDuplicate -> addDuplicateEntityToGraph(entityToDuplicate, entity.getTextRange(), node));
|
||||
} else {
|
||||
entity.addIntersectingNode(documentTree.getRoot().getNode());
|
||||
addEntityToGraph(entity, documentTree);
|
||||
}
|
||||
} catch (NoSuchElementException e) {
|
||||
entity.setDeepestFullyContainingNode(documentTree.getRoot().getNode());
|
||||
entityEnrichmentService.enrichEntity(entity, entity.getDeepestFullyContainingNode().getTextBlock());
|
||||
entity.addIntersectingNode(documentTree.getRoot().getNode());
|
||||
addToPages(entity);
|
||||
addEntityToNodeEntitySets(entity);
|
||||
if (node.getEntities().contains(entity)) {
|
||||
// If entity already exists and it has a different text range, we add the text range to the list of duplicated text ranges
|
||||
node.getEntities()
|
||||
.stream()//
|
||||
.filter(e -> e.equals(entity))//
|
||||
.filter(e -> !e.getTextRange().equals(entity.getTextRange()))//
|
||||
.findAny()
|
||||
.ifPresent(e -> addDuplicateEntityToGraph(e, entity.getTextRange(), node));
|
||||
|
||||
} else {
|
||||
addEntityToGraph(entity, documentTree);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -770,10 +798,11 @@ public class EntityCreationService {
|
||||
SemanticNode deepestSharedNode = entityToDuplicate.getIntersectingNodes()
|
||||
.stream()
|
||||
.sorted(Comparator.comparingInt(n -> -n.getTreeId().size()))
|
||||
.filter(intersectingNode -> entityToDuplicate.getDuplicateTextRanges().stream().allMatch(tr -> intersectingNode.getTextRange().contains(tr)) && //
|
||||
intersectingNode.getTextRange().contains(entityToDuplicate.getTextRange()))
|
||||
.findFirst()
|
||||
.orElse(node.getDocumentTree().getRoot().getNode());
|
||||
.filter(intersectingNode -> entityToDuplicate.getDuplicateTextRanges()
|
||||
.stream()
|
||||
.allMatch(tr -> intersectingNode.getTextRange().contains(tr)) && //
|
||||
intersectingNode.getTextRange().contains(entityToDuplicate.getTextRange()))
|
||||
.findFirst().orElse(node.getDocumentTree().getRoot().getNode());
|
||||
|
||||
entityToDuplicate.setDeepestFullyContainingNode(deepestSharedNode);
|
||||
|
||||
@@ -784,7 +813,8 @@ public class EntityCreationService {
|
||||
return;
|
||||
}
|
||||
additionalIntersectingNode.getEntities().add(entityToDuplicate);
|
||||
additionalIntersectingNode.getPages(newTextRange).forEach(page -> page.getEntities().add(entityToDuplicate));
|
||||
additionalIntersectingNode.getPages(newTextRange)
|
||||
.forEach(page -> page.getEntities().add(entityToDuplicate));
|
||||
entityToDuplicate.addIntersectingNode(additionalIntersectingNode);
|
||||
});
|
||||
}
|
||||
@@ -792,12 +822,7 @@ public class EntityCreationService {
|
||||
|
||||
private void addEntityToGraph(TextEntity entity, DocumentTree documentTree) {
|
||||
|
||||
SemanticNode containingNode = documentTree.childNodes(Collections.emptyList())
|
||||
.filter(node -> node.getTextBlock().containsTextRange(entity.getTextRange()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NoSuchElementException("No containing Node found!"));
|
||||
|
||||
containingNode.addThisToEntityIfIntersects(entity);
|
||||
documentTree.getRoot().getNode().addThisToEntityIfIntersects(entity);
|
||||
|
||||
TextBlock textBlock = entity.getDeepestFullyContainingNode().getTextBlock();
|
||||
entityEnrichmentService.enrichEntity(entity, textBlock);
|
||||
@@ -806,5 +831,4 @@ public class EntityCreationService {
|
||||
addEntityToNodeEntitySets(entity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+29
-15
@@ -47,7 +47,9 @@ public class EntityFindingUtility {
|
||||
}
|
||||
|
||||
|
||||
public Optional<TextEntity> findClosestEntityAndReturnEmptyIfNotFound(PrecursorEntity precursorEntity, Map<String, List<TextEntity>> entitiesWithSameValue, double matchThreshold) {
|
||||
public Optional<TextEntity> findClosestEntityAndReturnEmptyIfNotFound(PrecursorEntity precursorEntity,
|
||||
Map<String, List<TextEntity>> entitiesWithSameValue,
|
||||
double matchThreshold) {
|
||||
|
||||
if (precursorEntity.getValue() == null) {
|
||||
return Optional.empty();
|
||||
@@ -56,7 +58,7 @@ public class EntityFindingUtility {
|
||||
List<TextEntity> possibleEntities = entitiesWithSameValue.get(precursorEntity.getValue().toLowerCase(Locale.ENGLISH));
|
||||
|
||||
if (entityIdentifierValueNotFound(possibleEntities)) {
|
||||
log.warn("Entity could not be created with precursorEntity: {}, due to the value {} not being found anywhere.", precursorEntity, precursorEntity.getValue());
|
||||
log.info("Entity could not be created with precursorEntity: {}, due to the value {} not being found anywhere.", precursorEntity, precursorEntity.getValue());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -66,18 +68,22 @@ public class EntityFindingUtility {
|
||||
.min(Comparator.comparingDouble(ClosestEntity::getDistance));
|
||||
|
||||
if (optionalClosestEntity.isEmpty()) {
|
||||
log.warn("No Entity with value {} found on page {}", precursorEntity.getValue(), precursorEntity.getEntityPosition());
|
||||
log.info("No Entity with value {} found on page {}", precursorEntity.getValue(), precursorEntity.getEntityPosition());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ClosestEntity closestEntity = optionalClosestEntity.get();
|
||||
if (closestEntity.getDistance() > matchThreshold) {
|
||||
log.warn("For entity {} on page {} with positions {} distance to closest found entity is {} and therefore higher than the threshold of {}",
|
||||
precursorEntity.getValue(),
|
||||
precursorEntity.getEntityPosition().get(0).pageNumber(),
|
||||
precursorEntity.getEntityPosition().stream().map(RectangleWithPage::rectangle2D).toList(),
|
||||
closestEntity.getDistance(),
|
||||
matchThreshold);
|
||||
log.info("For entity {} on page {} with positions {} distance to closest found entity is {} and therefore higher than the threshold of {}",
|
||||
precursorEntity.getValue(),
|
||||
precursorEntity.getEntityPosition()
|
||||
.get(0).pageNumber(),
|
||||
precursorEntity.getEntityPosition()
|
||||
.stream()
|
||||
.map(RectangleWithPage::rectangle2D)
|
||||
.toList(),
|
||||
closestEntity.getDistance(),
|
||||
matchThreshold);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -93,8 +99,14 @@ public class EntityFindingUtility {
|
||||
|
||||
private static boolean pagesMatch(TextEntity entity, List<RectangleWithPage> originalPositions) {
|
||||
|
||||
Set<Integer> entityPageNumbers = entity.getPositionsOnPagePerPage().stream().map(PositionOnPage::getPage).map(Page::getNumber).collect(Collectors.toSet());
|
||||
Set<Integer> originalPageNumbers = originalPositions.stream().map(RectangleWithPage::pageNumber).collect(Collectors.toSet());
|
||||
Set<Integer> entityPageNumbers = entity.getPositionsOnPagePerPage()
|
||||
.stream()
|
||||
.map(PositionOnPage::getPage)
|
||||
.map(Page::getNumber)
|
||||
.collect(Collectors.toSet());
|
||||
Set<Integer> originalPageNumbers = originalPositions.stream()
|
||||
.map(RectangleWithPage::pageNumber)
|
||||
.collect(Collectors.toSet());
|
||||
return entityPageNumbers.containsAll(originalPageNumbers);
|
||||
}
|
||||
|
||||
@@ -105,15 +117,16 @@ public class EntityFindingUtility {
|
||||
return Double.MAX_VALUE;
|
||||
}
|
||||
return originalPositions.stream()
|
||||
.mapToDouble(rectangleWithPage -> calculateMinDistancePerRectangle(entity, rectangleWithPage.pageNumber(), rectangleWithPage.rectangle2D()))
|
||||
.average()
|
||||
.mapToDouble(rectangleWithPage -> calculateMinDistancePerRectangle(entity, rectangleWithPage.pageNumber(), rectangleWithPage.rectangle2D())).average()
|
||||
.orElse(Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
private static long countRectangles(TextEntity entity) {
|
||||
|
||||
return entity.getPositionsOnPagePerPage().stream().mapToLong(redactionPosition -> redactionPosition.getRectanglePerLine().size()).sum();
|
||||
return entity.getPositionsOnPagePerPage()
|
||||
.stream()
|
||||
.mapToLong(redactionPosition -> redactionPosition.getRectanglePerLine().size()).sum();
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +174,8 @@ public class EntityFindingUtility {
|
||||
pageNumbers.stream().filter(pageNumber -> !node.onPage(pageNumber)).toList(),
|
||||
node.getPages()));
|
||||
}
|
||||
SearchImplementation searchImplementation = new SearchImplementation(entryValues, true);
|
||||
|
||||
SearchImplementation searchImplementation = new SearchImplementation(entryValues.stream().map(String::trim).collect(Collectors.toSet()), true);
|
||||
|
||||
return searchImplementation.getBoundaries(node.getTextBlock(), node.getTextRange())
|
||||
.stream()
|
||||
|
||||
+3
-12
@@ -9,7 +9,6 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedactions;
|
||||
@@ -23,29 +22,21 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNo
|
||||
import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||
public class EntityFromPrecursorCreationService {
|
||||
|
||||
static double MATCH_THRESHOLD = 10; // Is compared to the average sum of distances in pdf coordinates for each corner of the bounding box of the entities
|
||||
EntityFindingUtility entityFindingUtility;
|
||||
EntityCreationService entityCreationService;
|
||||
DictionaryService dictionaryService;
|
||||
|
||||
|
||||
@Autowired
|
||||
public EntityFromPrecursorCreationService(EntityEnrichmentService entityEnrichmentService, DictionaryService dictionaryService, EntityFindingUtility entityFindingUtility) {
|
||||
|
||||
this.entityFindingUtility = entityFindingUtility;
|
||||
entityCreationService = new EntityCreationService(entityEnrichmentService);
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
|
||||
public List<PrecursorEntity> createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions manualRedactions, SemanticNode node, String dossierTemplateId) {
|
||||
|
||||
Set<IdRemoval> idRemovals = manualRedactions.getIdsToRemove();
|
||||
@@ -54,7 +45,7 @@ public class EntityFromPrecursorCreationService {
|
||||
.filter(BaseAnnotation::isLocal)
|
||||
.filter(manualRedactionEntry -> idRemovals.stream()
|
||||
.filter(idRemoval -> idRemoval.getAnnotationId().equals(manualRedactionEntry.getAnnotationId()))
|
||||
.filter(idRemoval -> idRemoval.getRequestDate().isBefore(manualRedactionEntry.getRequestDate()))
|
||||
.filter(idRemoval -> idRemoval.getRequestDate().isAfter(manualRedactionEntry.getRequestDate()))
|
||||
.findAny()//
|
||||
.isEmpty())
|
||||
.map(manualRedactionEntry -> //
|
||||
|
||||
+17
-35
@@ -57,23 +57,18 @@ public class SectionFinderService {
|
||||
if (entry.getContainingNodeId().isEmpty()) {
|
||||
continue; // Empty list means either Entity has not been found or it is between main sections. Thus, this might lead to wrong reanalysis.
|
||||
}
|
||||
sectionsToReanalyse.add(entry.getContainingNodeId()
|
||||
.get(0));
|
||||
sectionsToReanalyse.add(entry.getContainingNodeId().get(0));
|
||||
}
|
||||
}
|
||||
|
||||
var dictionaryIncrementsSearch = new SearchImplementation(dictionaryIncrement.getValues()
|
||||
.stream()
|
||||
.map(DictionaryIncrementValue::getValue)
|
||||
.toList(), true);
|
||||
var dictionaryIncrementsSearch = new SearchImplementation(dictionaryIncrement.getValues().stream().map(DictionaryIncrementValue::getValue).collect(Collectors.toList()),
|
||||
true);
|
||||
|
||||
document.streamChildren()
|
||||
.forEach(mainNode -> {
|
||||
if (dictionaryIncrementsSearch.atLeastOneMatches(mainNode.getTextBlock().getSearchText())) {
|
||||
sectionsToReanalyse.add(mainNode.getTreeId()
|
||||
.get(0));
|
||||
}
|
||||
});
|
||||
document.streamChildren().forEach(mainNode -> {
|
||||
if (dictionaryIncrementsSearch.atLeastOneMatches(mainNode.getTextBlock().getSearchText())) {
|
||||
sectionsToReanalyse.add(mainNode.getTreeId().get(0));
|
||||
}
|
||||
});
|
||||
|
||||
Set<Integer> relevantPagesForReanalysis = getRelevantPageNumbersForAddRedactions(analyzeRequest);
|
||||
relevantPagesForReanalysis.addAll(getRelevantPageNumbersForImportedRedactions(importedRedactions, relevantManuallyModifiedAnnotationIds));
|
||||
@@ -92,11 +87,8 @@ public class SectionFinderService {
|
||||
return document.getPages()
|
||||
.stream()
|
||||
.filter(page -> relevantPagesForReanalysis.contains(page.getNumber()))
|
||||
.flatMap(page -> Stream.concat(page.getMainBody()
|
||||
.stream()
|
||||
.filter(node -> node.getType().equals(NodeType.SECTION)), Stream.of(page.getHeader(), page.getFooter())))
|
||||
.map(node -> node.getTreeId()
|
||||
.get(0))
|
||||
.flatMap(page -> Stream.concat(page.getMainBody().stream().filter(node -> node.getType().equals(NodeType.SECTION)), Stream.of(page.getHeader(), page.getFooter())))
|
||||
.map(node -> node.getTreeId().get(0))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -107,7 +99,8 @@ public class SectionFinderService {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return analyzeRequest.getManualRedactions().getEntriesToAdd()
|
||||
return analyzeRequest.getManualRedactions()
|
||||
.getEntriesToAdd()
|
||||
.stream()
|
||||
.filter(addRedaction -> !(addRedaction.isAddToDictionary() || addRedaction.isAddToDossierDictionary()))
|
||||
.map(ManualRedactionEntry::getPositions)
|
||||
@@ -139,22 +132,11 @@ public class SectionFinderService {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
return Stream.concat(manualRedactions.getResizeRedactions()
|
||||
.stream()
|
||||
.map(ManualResizeRedaction::getAnnotationId),
|
||||
Stream.concat(manualRedactions.getLegalBasisChanges()
|
||||
.stream()
|
||||
.map(ManualLegalBasisChange::getAnnotationId),
|
||||
Stream.concat(manualRedactions.getRecategorizations()
|
||||
.stream()
|
||||
.map(ManualRecategorization::getAnnotationId),
|
||||
Stream.concat(manualRedactions.getIdsToRemove()
|
||||
.stream()
|
||||
.map(IdRemoval::getAnnotationId),
|
||||
manualRedactions.getForceRedactions()
|
||||
.stream()
|
||||
.map(ManualForceRedaction::getAnnotationId)))))
|
||||
.collect(Collectors.toSet());
|
||||
return Stream.concat(manualRedactions.getResizeRedactions().stream().map(ManualResizeRedaction::getAnnotationId),
|
||||
Stream.concat(manualRedactions.getLegalBasisChanges().stream().map(ManualLegalBasisChange::getAnnotationId),
|
||||
Stream.concat(manualRedactions.getRecategorizations().stream().map(ManualRecategorization::getAnnotationId),
|
||||
Stream.concat(manualRedactions.getIdsToRemove().stream().map(IdRemoval::getAnnotationId),
|
||||
manualRedactions.getForceRedactions().stream().map(ManualForceRedaction::getAnnotationId))))).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-3
@@ -50,7 +50,9 @@ public class ComponentDroolsExecutionService {
|
||||
.filter(entityLogEntry -> entityLogEntry.getState().equals(EntryState.APPLIED))
|
||||
.map(entry -> Entity.fromEntityLogEntry(entry, document))
|
||||
.forEach(kieSession::insert);
|
||||
fileAttributes.stream().filter(f -> f.getValue() != null).forEach(kieSession::insert);
|
||||
fileAttributes.stream()
|
||||
.filter(f -> f.getValue() != null)
|
||||
.forEach(kieSession::insert);
|
||||
|
||||
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
|
||||
kieSession.fireAllRules();
|
||||
@@ -58,7 +60,8 @@ public class ComponentDroolsExecutionService {
|
||||
});
|
||||
|
||||
try {
|
||||
completableFuture.orTimeout(settings.getDroolsExecutionTimeoutSecs(), TimeUnit.SECONDS).get();
|
||||
completableFuture.orTimeout(settings.getDroolsExecutionTimeoutSecs(document.getNumberOfPages()), TimeUnit.SECONDS)
|
||||
.get();
|
||||
} catch (ExecutionException e) {
|
||||
kieSession.dispose();
|
||||
if (e.getCause() instanceof TimeoutException) {
|
||||
@@ -71,7 +74,9 @@ public class ComponentDroolsExecutionService {
|
||||
}
|
||||
|
||||
List<FileAttribute> resultingFileAttributes = getFileAttributes(kieSession);
|
||||
List<Component> components = getComponents(kieSession).stream().sorted(ComponentComparator.first()).toList();
|
||||
List<Component> components = getComponents(kieSession).stream()
|
||||
.sorted(ComponentComparator.first())
|
||||
.toList();
|
||||
kieSession.dispose();
|
||||
return components;
|
||||
}
|
||||
|
||||
+31
-112
@@ -1,9 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.server.service.drools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -15,34 +12,24 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
|
||||
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxDeprecatedWarnings;
|
||||
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxErrorMessage;
|
||||
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation;
|
||||
import com.iqser.red.service.redaction.v1.model.RuleValidationModel;
|
||||
import com.iqser.red.service.redaction.v1.server.DeprecatedElementsFinder;
|
||||
import com.iqser.red.service.redaction.v1.server.model.dictionary.SearchImplementation;
|
||||
import com.iqser.red.service.redaction.v1.server.model.drools.BasicQuery;
|
||||
import com.iqser.red.service.redaction.v1.server.model.drools.BasicRule;
|
||||
import com.iqser.red.service.redaction.v1.server.model.drools.RuleClass;
|
||||
import com.iqser.red.service.redaction.v1.server.model.drools.RuleFileBluePrint;
|
||||
import com.iqser.red.service.redaction.v1.server.model.drools.RuleUnit;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RuleManagementResources;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DroolsSyntaxValidationService {
|
||||
|
||||
|
||||
private static final Pattern allowedImportsPattern = Pattern.compile("^(?:import\\s+static\\s+)?(?:import\\s+)?(?:com\\.knecon\\.fforesight|com\\.iqser\\.red)\\..*;$");
|
||||
|
||||
private final KieContainerCreationService kieContainerCreationService;
|
||||
|
||||
private final DeprecatedElementsFinder deprecatedElementsFinder;
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public DroolsSyntaxValidation testRules(RuleValidationModel rules) {
|
||||
@@ -56,74 +43,15 @@ public class DroolsSyntaxValidationService {
|
||||
}
|
||||
DroolsSyntaxValidation droolsCompilerSyntaxValidation = buildDroolsCompilerSyntaxValidation(rules);
|
||||
droolsCompilerSyntaxValidation.getDroolsSyntaxErrorMessages().addAll(customDroolsSyntaxValidation.getDroolsSyntaxErrorMessages());
|
||||
droolsCompilerSyntaxValidation.getDroolsSyntaxDeprecatedWarnings().addAll(customDroolsSyntaxValidation.getDroolsSyntaxDeprecatedWarnings());
|
||||
return droolsCompilerSyntaxValidation;
|
||||
}
|
||||
|
||||
private DroolsSyntaxDeprecatedWarnings getWarningsForDeprecatedImports(RuleFileBluePrint ruleFileBluePrint) {
|
||||
|
||||
if (!deprecatedElementsFinder.getDeprecatedClasses().isEmpty()) {
|
||||
String imports = ruleFileBluePrint.getImports();
|
||||
SearchImplementation classesSearchImplementation = deprecatedElementsFinder.getClassesSearchImplementation();
|
||||
List<SearchImplementation.MatchPosition> matches = classesSearchImplementation.getMatches(imports);
|
||||
if (!matches.isEmpty()) {
|
||||
String sb = "Following imports are deprecated: \n" + matches.stream()
|
||||
.map(m -> imports.substring(m.startIndex(), m.endIndex()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
return DroolsSyntaxDeprecatedWarnings.builder().line(ruleFileBluePrint.getImportLine()).column(0).message(sb)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<DroolsSyntaxDeprecatedWarnings> getWarningsForDeprecatedRules(RuleFileBluePrint ruleFileBluePrint) {
|
||||
|
||||
List<DroolsSyntaxDeprecatedWarnings> warningMessages = new ArrayList<>();
|
||||
|
||||
// checks the rules for deprecateMethods
|
||||
if (!deprecatedElementsFinder.getDeprecatedMethods().isEmpty()) {
|
||||
SearchImplementation methodsSearchImplementation = deprecatedElementsFinder.getMethodsSearchImplementation();
|
||||
Map<String, String> deprecatedMethodsSignatureMap = deprecatedElementsFinder.getDeprecatedMethodsSignaturesMap();
|
||||
|
||||
for (RuleClass ruleClass : ruleFileBluePrint.getRuleClasses()) {
|
||||
for (RuleUnit ruleUnit : ruleClass.ruleUnits()) {
|
||||
for (BasicRule basicRule : ruleUnit.rules()) {
|
||||
List<SearchImplementation.MatchPosition> matches = methodsSearchImplementation.getMatches(basicRule.getCode());
|
||||
if (!matches.isEmpty()) {
|
||||
String warningMessage = matches.stream()
|
||||
.map(m -> basicRule.getCode().substring(m.startIndex(), m.endIndex()))
|
||||
.distinct()
|
||||
.map(dm -> String.format("Method %s might be deprecated because of \n %s \n", dm, deprecatedMethodsSignatureMap.get(dm)))
|
||||
.collect(Collectors.joining("\n"));
|
||||
warningMessages.add(DroolsSyntaxDeprecatedWarnings.builder().line(basicRule.getLine()).column(0).message(warningMessage)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return warningMessages.stream()
|
||||
.sorted(Comparator.comparingInt(DroolsSyntaxDeprecatedWarnings::getLine))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
private DroolsSyntaxValidation buildCustomDroolsSyntaxValidation(String ruleString, RuleFileType ruleFileType) throws DroolsParserException {
|
||||
|
||||
RuleFileBluePrint ruleFileBluePrint = RuleFileParser.buildBluePrintFromRulesString(ruleString);
|
||||
|
||||
DroolsSyntaxValidation customSyntaxValidation = ruleFileBluePrint.getDroolsSyntaxValidation();
|
||||
|
||||
// find deprecated elements in the ruleFileBluePrint
|
||||
DroolsSyntaxDeprecatedWarnings warningMessageForImports = getWarningsForDeprecatedImports(ruleFileBluePrint);
|
||||
if (warningMessageForImports != null) {
|
||||
customSyntaxValidation.getDroolsSyntaxDeprecatedWarnings().add(warningMessageForImports);
|
||||
}
|
||||
customSyntaxValidation.getDroolsSyntaxDeprecatedWarnings().addAll(getWarningsForDeprecatedRules(ruleFileBluePrint));
|
||||
|
||||
RuleFileBluePrint baseRuleFileBluePrint = switch (ruleFileType) {
|
||||
case ENTITY -> RuleFileParser.buildBluePrintFromRulesString(RuleManagementResources.getBaseRuleFileString());
|
||||
case COMPONENT -> RuleFileParser.buildBluePrintFromRulesString(RuleManagementResources.getBaseComponentRuleFileString());
|
||||
@@ -132,36 +60,35 @@ public class DroolsSyntaxValidationService {
|
||||
if (!importsAreValid(baseRuleFileBluePrint, ruleFileBluePrint)) {
|
||||
customSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.add(DroolsSyntaxErrorMessage.builder()
|
||||
.line(ruleFileBluePrint.getImportLine())
|
||||
.column(0)
|
||||
.message(String.format("Changing the imports is not allowed! Must be: %n%s", baseRuleFileBluePrint.getImports()))
|
||||
.build());
|
||||
.line(ruleFileBluePrint.getImportLine())
|
||||
.column(0)
|
||||
.message(String.format("Changing the imports is not allowed! Must be: %n%s", baseRuleFileBluePrint.getImports()))
|
||||
.build());
|
||||
}
|
||||
if (!ruleFileBluePrint.getGlobals().equals(baseRuleFileBluePrint.getGlobals())) {
|
||||
customSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.add(DroolsSyntaxErrorMessage.builder()
|
||||
.line(ruleFileBluePrint.getGlobalsLine())
|
||||
.column(0)
|
||||
.message(String.format("Changing the globals is not allowed! Must be: %n%s", baseRuleFileBluePrint.getGlobals()))
|
||||
.build());
|
||||
.line(ruleFileBluePrint.getGlobalsLine())
|
||||
.column(0)
|
||||
.message(String.format("Changing the globals is not allowed! Must be: %n%s", baseRuleFileBluePrint.getGlobals()))
|
||||
.build());
|
||||
}
|
||||
baseRuleFileBluePrint.getQueries()
|
||||
.forEach(basicQuery -> {
|
||||
if (!validateQueryIsPresent(basicQuery, ruleFileBluePrint)) {
|
||||
customSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.add(DroolsSyntaxErrorMessage.builder()
|
||||
.line(basicQuery.getLine())
|
||||
.column(0)
|
||||
.message(String.format("Changing or removing the query %s is not allowed! Must be: %n%s", basicQuery.getName(), basicQuery.getCode()))
|
||||
.build());
|
||||
}
|
||||
});
|
||||
baseRuleFileBluePrint.getQueries().forEach(basicQuery -> {
|
||||
if (!validateQueryIsPresent(basicQuery, ruleFileBluePrint)) {
|
||||
customSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.add(DroolsSyntaxErrorMessage.builder()
|
||||
.line(basicQuery.getLine())
|
||||
.column(0)
|
||||
.message(String.format("Changing or removing the query %s is not allowed! Must be: %n%s", basicQuery.getName(), basicQuery.getCode()))
|
||||
.build());
|
||||
}
|
||||
});
|
||||
if (ruleFileType.equals(RuleFileType.ENTITY)) {
|
||||
String requiredAgendaGroup = "LOCAL_DICTIONARY_ADDS";
|
||||
if (!validateAgendaGroupIsPresent(ruleFileBluePrint, requiredAgendaGroup)) {
|
||||
customSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.add(DroolsSyntaxErrorMessage.builder().line(0).column(0).message(String.format("At least one rule with Agenda-Group '%s' required!", requiredAgendaGroup))
|
||||
.build());
|
||||
customSyntaxValidation.getDroolsSyntaxErrorMessages().add(DroolsSyntaxErrorMessage.builder().line(0)
|
||||
.column(0).message(String.format("At least one rule with Agenda-Group '%s' required!", requiredAgendaGroup))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
return customSyntaxValidation;
|
||||
@@ -170,8 +97,7 @@ public class DroolsSyntaxValidationService {
|
||||
|
||||
private boolean validateAgendaGroupIsPresent(RuleFileBluePrint ruleFileBluePrint, String agendaGroupName) {
|
||||
|
||||
return ruleFileBluePrint.streamAllRules()
|
||||
.anyMatch(basicRule -> basicRule.getAgendaGroup().equals(agendaGroupName));
|
||||
return ruleFileBluePrint.streamAllRules().anyMatch(basicRule -> basicRule.getAgendaGroup().equals(agendaGroupName));
|
||||
}
|
||||
|
||||
|
||||
@@ -183,16 +109,13 @@ public class DroolsSyntaxValidationService {
|
||||
|
||||
Set<String> additionalImports = Sets.difference(imports, baseImports);
|
||||
|
||||
return additionalImports.stream()
|
||||
.allMatch(additionalImport -> allowedImportsPattern.matcher(additionalImport).matches());
|
||||
return additionalImports.stream().allMatch(additionalImport -> allowedImportsPattern.matcher(additionalImport).matches());
|
||||
}
|
||||
|
||||
|
||||
private static boolean validateQueryIsPresent(BasicQuery queryToCheckFor, RuleFileBluePrint ruleFileBluePrint) {
|
||||
|
||||
return ruleFileBluePrint.getQueries()
|
||||
.stream()
|
||||
.anyMatch(query -> query.getName().equals(queryToCheckFor.getName()) && query.getCode().equals(queryToCheckFor.getCode()));
|
||||
return ruleFileBluePrint.getQueries().stream().anyMatch(query -> query.getName().equals(queryToCheckFor.getName()) && query.getCode().equals(queryToCheckFor.getCode()));
|
||||
}
|
||||
|
||||
|
||||
@@ -201,9 +124,9 @@ public class DroolsSyntaxValidationService {
|
||||
var versionId = System.currentTimeMillis();
|
||||
var testRules = "test-rules";
|
||||
KieBuilder kieBuilder = kieContainerCreationService.registerNewKieContainerVersion(testRules,
|
||||
versionId,
|
||||
rules.getRulesString(),
|
||||
RuleFileType.valueOf(rules.getRuleFileType()));
|
||||
versionId,
|
||||
rules.getRulesString(),
|
||||
RuleFileType.valueOf(rules.getRuleFileType()));
|
||||
return buildDroolsCompilerSyntaxValidation(kieBuilder);
|
||||
}
|
||||
|
||||
@@ -211,18 +134,14 @@ public class DroolsSyntaxValidationService {
|
||||
private DroolsSyntaxValidation buildDroolsCompilerSyntaxValidation(KieBuilder kieBuilder) {
|
||||
|
||||
List<Message> errorMessages = kieBuilder.getResults().getMessages(Message.Level.ERROR);
|
||||
List<DroolsSyntaxErrorMessage> droolsSyntaxErrorMessages = errorMessages.stream()
|
||||
.map(this::buildDroolsSyntaxErrorMessage)
|
||||
.collect(Collectors.toList());
|
||||
return DroolsSyntaxValidation.builder().droolsSyntaxErrorMessages(droolsSyntaxErrorMessages)
|
||||
.build();
|
||||
List<DroolsSyntaxErrorMessage> droolsSyntaxErrorMessages = errorMessages.stream().map(this::buildDroolsSyntaxErrorMessage).collect(Collectors.toList());
|
||||
return DroolsSyntaxValidation.builder().droolsSyntaxErrorMessages(droolsSyntaxErrorMessages).build();
|
||||
}
|
||||
|
||||
|
||||
private DroolsSyntaxErrorMessage buildDroolsSyntaxErrorMessage(Message message) {
|
||||
|
||||
return DroolsSyntaxErrorMessage.builder().line(message.getLine()).column(message.getColumn()).message(message.getText())
|
||||
.build();
|
||||
return DroolsSyntaxErrorMessage.builder().line(message.getLine()).column(message.getColumn()).message(message.getText()).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ public class EntityDroolsExecutionService {
|
||||
});
|
||||
|
||||
try {
|
||||
completableFuture.orTimeout(settings.getDroolsExecutionTimeoutSecs(), TimeUnit.SECONDS)
|
||||
completableFuture.orTimeout(settings.getDroolsExecutionTimeoutSecs(document.getNumberOfPages()), TimeUnit.SECONDS)
|
||||
.get();
|
||||
} catch (ExecutionException e) {
|
||||
kieSession.dispose();
|
||||
|
||||
+1
-3
@@ -30,8 +30,7 @@ public class KieContainerCreationService {
|
||||
private final RulesClient rulesClient;
|
||||
|
||||
|
||||
@Observed(name = "KieContainerCreationService",
|
||||
contextualName = "get-kie-container")
|
||||
@Observed(name = "KieContainerCreationService", contextualName = "get-kie-container")
|
||||
public KieWrapper getLatestKieContainer(String dossierTemplateId, RuleFileType ruleFileType) {
|
||||
|
||||
try {
|
||||
@@ -65,7 +64,6 @@ public class KieContainerCreationService {
|
||||
try {
|
||||
return kieServices.newKieContainer(getReleaseId(dossierTemplateId, version, ruleFileType));
|
||||
} catch (Exception e) {
|
||||
|
||||
registerNewKieContainerVersion(dossierTemplateId, version, ruleFileType);
|
||||
return kieServices.newKieContainer(getReleaseId(dossierTemplateId, version, ruleFileType));
|
||||
}
|
||||
|
||||
-1613
File diff suppressed because it is too large
Load Diff
+327
@@ -0,0 +1,327 @@
|
||||
package com.iqser.red.service.redaction.v1.server;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.iqser.red.commons.jackson.ObjectMapperFactory;
|
||||
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntryModel;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeRequest;
|
||||
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.persistence.service.v1.api.shared.model.common.JSONPrimitive;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
|
||||
import com.iqser.red.service.redaction.v1.server.client.DictionaryClient;
|
||||
import com.iqser.red.service.redaction.v1.server.client.LegalBasisClient;
|
||||
import com.iqser.red.service.redaction.v1.server.client.RulesClient;
|
||||
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.DictionaryModel;
|
||||
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryVersion;
|
||||
import com.iqser.red.service.redaction.v1.server.service.AnalyzeService;
|
||||
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.exception.NotFoundException;
|
||||
import com.iqser.red.storage.commons.service.StorageService;
|
||||
import com.knecon.fforesight.tenantcommons.TenantsClient;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@Import(RedactionIntegrationTest.RedactionIntegrationTestConfiguration.class)
|
||||
@Disabled
|
||||
/*
|
||||
* This test is meant to be used directly with a download from blob storage (e.g. minio). You need to define the dossier template you want to use by supplying an absolute path.
|
||||
* The dossier template will then be parsed for dictionaries, colors, entities, and rules. This is defined for the all tests once.
|
||||
* Inside a test you supply a path to your minio download folder. The files should still be zipped in this folder.
|
||||
* The files will then be checked for completeness and uploaded to the FileSystemBackedStorageService.
|
||||
* This way you can recreate what is happening on the stack almost exactly.
|
||||
*/ public class AnalysisEnd2EndTest {
|
||||
|
||||
Path dossierTemplateToUse = Path.of("/home/kschuettler/iqser/business-logic/redactmanager/prod-cp-eu-reg/EFSA_sanitisation_GFL_v1"); // Add your dossier-template here
|
||||
ObjectMapper mapper = ObjectMapperFactory.create();
|
||||
final String TENANT_ID = "tenant";
|
||||
|
||||
@Autowired
|
||||
StorageService storageService;
|
||||
|
||||
@Autowired
|
||||
protected AnalyzeService analyzeService;
|
||||
|
||||
@MockBean
|
||||
DictionaryService dictionaryService;
|
||||
|
||||
@MockBean
|
||||
RabbitTemplate rabbitTemplate;
|
||||
|
||||
TestDossierTemplate testDossierTemplate;
|
||||
@MockBean
|
||||
protected LegalBasisClient legalBasisClient;
|
||||
|
||||
@MockBean
|
||||
private TenantsClient tenantsClient;
|
||||
|
||||
@MockBean
|
||||
protected RulesClient rulesClient;
|
||||
|
||||
@MockBean
|
||||
protected DictionaryClient dictionaryClient;
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void runAnalysisEnd2End() {
|
||||
|
||||
String folder = "files/end2end/file0"; // Should contain all files from minio directly, still zipped. Can contain multiple fileIds.
|
||||
|
||||
Path absoluteFolderPath;
|
||||
if (folder.startsWith("files")) { // if it starts with "files" it is most likely in the resources folder, else it should be an absolute path
|
||||
ClassPathResource classPathResource = new ClassPathResource(folder);
|
||||
absoluteFolderPath = classPathResource.getFile().toPath();
|
||||
} else {
|
||||
absoluteFolderPath = Path.of(folder);
|
||||
}
|
||||
|
||||
log.info("Starting end2end analyses for all distinct filenames in folder: {}", folder);
|
||||
List<AnalyzeRequest> analyzeRequests = prepareStorageForFolder(absoluteFolderPath);
|
||||
log.info("Found {} distinct fileIds", analyzeRequests.size());
|
||||
for (int i = 0; i < analyzeRequests.size(); i++) {
|
||||
AnalyzeRequest analyzeRequest = analyzeRequests.get(i);
|
||||
log.info("{}/{}: Starting analysis for file {}", i + 1, analyzeRequests.size(), analyzeRequest.getFileId());
|
||||
analyzeService.analyze(analyzeRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
|
||||
testDossierTemplate = new TestDossierTemplate(dossierTemplateToUse);
|
||||
when(dictionaryService.updateDictionary(any(), any())).thenReturn(new DictionaryVersion(0, 0));
|
||||
when(dictionaryService.getDeepCopyDictionary(any(), any())).thenReturn(testDossierTemplate.testDictionary);
|
||||
when(dictionaryService.getDictionaryIncrements(any(), any(), any())).thenReturn(new DictionaryIncrement(Collections.emptySet(), new DictionaryVersion(0, 0)));
|
||||
when(dictionaryService.isHint(any(String.class), any())).thenAnswer(invocation -> {
|
||||
String type = invocation.getArgument(0);
|
||||
return testDossierTemplate.testDictionary.getType(type).isHint();
|
||||
});
|
||||
when(dictionaryService.getColor(any(String.class), any())).thenAnswer(invocation -> {
|
||||
String type = invocation.getArgument(0);
|
||||
return testDossierTemplate.testDictionary.getType(type).getColor();
|
||||
});
|
||||
when(dictionaryService.getNotRedactedColor(any())).thenReturn(new float[]{0.2f, 0.2f, 0.2f});
|
||||
|
||||
when(rulesClient.getVersion(testDossierTemplate.id, RuleFileType.ENTITY)).thenReturn(System.currentTimeMillis());
|
||||
when(rulesClient.getRules(testDossierTemplate.id, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(testDossierTemplate.rules));
|
||||
when(rulesClient.getVersion(testDossierTemplate.id, RuleFileType.COMPONENT)).thenReturn(testDossierTemplate.componentRules != null ? System.currentTimeMillis() : -1);
|
||||
when(rulesClient.getRules(testDossierTemplate.id, RuleFileType.COMPONENT)).thenReturn(JSONPrimitive.of(testDossierTemplate.componentRules));
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
private List<AnalyzeRequest> prepareStorageForFolder(Path folder) {
|
||||
|
||||
return Files.list(folder)
|
||||
.map(this::parseFileId)
|
||||
.distinct()
|
||||
.map(fileId -> prepareStorageForFile(fileId, folder))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
private String parseFileId(Path path) {
|
||||
|
||||
return path.getFileName().toString().split("\\.")[0];
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
private AnalyzeRequest prepareStorageForFile(String fileId, Path folder) {
|
||||
|
||||
AnalyzeRequest request = new AnalyzeRequest();
|
||||
request.setDossierId(UUID.randomUUID().toString());
|
||||
request.setFileId(UUID.randomUUID().toString());
|
||||
request.setDossierTemplateId(testDossierTemplate.id);
|
||||
request.setManualRedactions(new ManualRedactions());
|
||||
request.setAnalysisNumber(-1);
|
||||
|
||||
Set<FileType> endingsToUpload = Set.of("ORIGIN",
|
||||
"DOCUMENT_PAGES",
|
||||
"DOCUMENT_POSITION",
|
||||
"DOCUMENT_STRUCTURE",
|
||||
"DOCUMENT_TEXT",
|
||||
"IMAGE_INFO",
|
||||
"NER_ENTITIES",
|
||||
"TABLES",
|
||||
"IMPORTED_REDACTIONS")
|
||||
.stream()
|
||||
.map(FileType::valueOf)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<FileType> uploadedFileTypes = Files.walk(folder)
|
||||
.filter(path -> path.toFile().isFile())
|
||||
.filter(path -> endingsToUpload.contains(parseFileTypeFromPath(path)))
|
||||
.map(filePath -> uploadFile(filePath, request))
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
Set<FileType> missingFileTypes = Sets.difference(endingsToUpload, uploadedFileTypes);
|
||||
if (!missingFileTypes.isEmpty()) {
|
||||
log.error("Folder {} is missing files of type {}",
|
||||
folder.toFile(),
|
||||
missingFileTypes.stream()
|
||||
.map(Enum::toString)
|
||||
.collect(Collectors.joining(", ")));
|
||||
throw new NotFoundException("Not all required file types are present.");
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
private static FileType parseFileTypeFromPath(Path path) {
|
||||
|
||||
return FileType.valueOf(path.getFileName().toString().split("\\.")[1]);
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
private FileType uploadFile(Path path, AnalyzeRequest request) {
|
||||
|
||||
FileType fileType = parseFileTypeFromPath(path);
|
||||
try (var fis = new FileInputStream(path.toFile()); var in = new GZIPInputStream(fis);) {
|
||||
storageService.storeObject(TENANT_ID, RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), fileType), in);
|
||||
|
||||
}
|
||||
return fileType;
|
||||
}
|
||||
|
||||
|
||||
private class TestDossierTemplate {
|
||||
|
||||
String id;
|
||||
Dictionary testDictionary;
|
||||
AtomicInteger dictEntryIdCounter = new AtomicInteger(0);
|
||||
String rules;
|
||||
String componentRules;
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
TestDossierTemplate(Path dossierTemplateToUse) {
|
||||
|
||||
Map<String, Object> dossierTemplate = mapper.readValue(dossierTemplateToUse.resolve("dossierTemplate.json").toFile(), HashMap.class);
|
||||
this.id = (String) dossierTemplate.get("dossierTemplateId");
|
||||
List<DictionaryModel> dictionaries = Files.walk(dossierTemplateToUse)
|
||||
.filter(path -> path.getFileName().toString().equals("dossierType.json"))
|
||||
.map(this::loadDictionaryModel)
|
||||
.toList();
|
||||
File ruleFile = dossierTemplateToUse.resolve("rules.drl").toFile();
|
||||
rules = new String(Files.readAllBytes(ruleFile.toPath()));
|
||||
|
||||
File componentRuleFile = dossierTemplateToUse.resolve("componentRules.drl").toFile();
|
||||
if (componentRuleFile.exists()) {
|
||||
componentRules = new String(Files.readAllBytes(componentRuleFile.toPath()));
|
||||
}
|
||||
|
||||
testDictionary = new Dictionary(dictionaries, new DictionaryVersion(0, 0));
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
private DictionaryModel loadDictionaryModel(Path path) {
|
||||
|
||||
Map<String, Object> model = mapper.readValue(path.toFile(), HashMap.class);
|
||||
Set<DictionaryEntryModel> entries = new HashSet<>();
|
||||
Set<DictionaryEntryModel> falsePositives = new HashSet<>();
|
||||
Set<DictionaryEntryModel> falseRecommendations = new HashSet<>();
|
||||
|
||||
String type = (String) model.get("type");
|
||||
Integer rank = (Integer) model.get("rank");
|
||||
float[] color = hexToFloatArr((String) model.get("hexColor"));
|
||||
Boolean caseInsensitive = (Boolean) model.get("caseInsensitive");
|
||||
Boolean hint = (Boolean) model.get("hint");
|
||||
Boolean hasDictionary = (Boolean) model.get("hasDictionary");
|
||||
|
||||
boolean isDossierDictionary;
|
||||
if (model.containsKey("dossierDictionaryOnly")) {
|
||||
isDossierDictionary = true;
|
||||
} else {
|
||||
isDossierDictionary = ((String) model.get("id")).split(":").length == 3;
|
||||
}
|
||||
|
||||
if (hasDictionary) {
|
||||
try (var in = new FileInputStream(path.getParent().resolve("entries.txt").toFile())) {
|
||||
entries.addAll(parseDictionaryEntryModelFromFile(new String(in.readAllBytes()), dictEntryIdCounter, (String) model.get("typeId")));
|
||||
}
|
||||
try (var in = new FileInputStream(path.getParent().resolve("falsePositives.txt").toFile())) {
|
||||
falsePositives.addAll(parseDictionaryEntryModelFromFile(new String(in.readAllBytes()), dictEntryIdCounter, (String) model.get("typeId")));
|
||||
}
|
||||
try (var in = new FileInputStream(path.getParent().resolve("falseRecommendations.txt").toFile())) {
|
||||
falseRecommendations.addAll(parseDictionaryEntryModelFromFile(new String(in.readAllBytes()), dictEntryIdCounter, (String) model.get("typeId")));
|
||||
}
|
||||
}
|
||||
|
||||
return new DictionaryModel(type, rank, color, caseInsensitive, hint, entries, falsePositives, falseRecommendations, isDossierDictionary);
|
||||
}
|
||||
|
||||
|
||||
private Set<DictionaryEntryModel> parseDictionaryEntryModelFromFile(String s, AtomicInteger dictEntryIdCounter, String typeId) {
|
||||
|
||||
String[] values = s.split("\n");
|
||||
return Arrays.stream(values)
|
||||
.map(value -> new DictionaryEntryModel(dictEntryIdCounter.getAndIncrement(), value, 0L, false, typeId))
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
|
||||
private float[] hexToFloatArr(String hexColor) {
|
||||
|
||||
// Remove # symbol if present
|
||||
String cleanHexColor = hexColor.replace("#", "");
|
||||
|
||||
// Parse hex string into RGB components
|
||||
int r = Integer.parseInt(cleanHexColor.substring(0, 2), 16);
|
||||
int g = Integer.parseInt(cleanHexColor.substring(2, 4), 16);
|
||||
int b = Integer.parseInt(cleanHexColor.substring(4, 6), 16);
|
||||
|
||||
// Normalize RGB values to floats between 0 and 1
|
||||
float[] rgbFloat = new float[3];
|
||||
rgbFloat[0] = r / 255.0f;
|
||||
rgbFloat[1] = g / 255.0f;
|
||||
rgbFloat[2] = b / 255.0f;
|
||||
|
||||
return rgbFloat;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+3
-1
@@ -177,7 +177,9 @@ public class MigrationIntegrationTest extends BuildDocumentIntegrationTest {
|
||||
document,
|
||||
TEST_DOSSIER_TEMPLATE_ID,
|
||||
manualRedactions,
|
||||
TEST_FILE_ID);
|
||||
TEST_FILE_ID,
|
||||
Collections.emptySet(),
|
||||
false);
|
||||
|
||||
redactionStorageService.storeObject(TEST_DOSSIER_ID, TEST_FILE_ID, FileType.ENTITY_LOG, migratedEntityLog.getEntityLog());
|
||||
assertEquals(mergedRedactionLog.getRedactionLogEntry().size(), migratedEntityLog.getEntityLog().getEntityLogEntry().size());
|
||||
|
||||
+34
-17
@@ -87,15 +87,15 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
|
||||
|
||||
when(dictionaryClient.getVersion(TEST_DOSSIER_TEMPLATE_ID)).thenReturn(0L);
|
||||
when(dictionaryClient.getAllTypesForDossier(TEST_DOSSIER_ID, true)).thenReturn(List.of(Type.builder()
|
||||
.id(DOSSIER_REDACTIONS_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID)
|
||||
.type(DOSSIER_REDACTIONS_INDICATOR)
|
||||
.dossierTemplateId(TEST_DOSSIER_ID)
|
||||
.hexColor("#ffe187")
|
||||
.isHint(hintTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isCaseInsensitive(caseInSensitiveMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isRecommendation(recommendationTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.rank(rankTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.build()));
|
||||
.id(DOSSIER_REDACTIONS_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID)
|
||||
.type(DOSSIER_REDACTIONS_INDICATOR)
|
||||
.dossierTemplateId(TEST_DOSSIER_ID)
|
||||
.hexColor("#ffe187")
|
||||
.isHint(hintTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isCaseInsensitive(caseInSensitiveMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isRecommendation(recommendationTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.rank(rankTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.build()));
|
||||
|
||||
mockDictionaryCalls(null);
|
||||
|
||||
@@ -122,9 +122,12 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
|
||||
assertThat(recommendations).containsExactlyInAnyOrder("Michael N.", "Funnarie B.", "Feuer A.");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void acceptanceTests() throws IOException {
|
||||
|
||||
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
|
||||
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
|
||||
AnalyzeRequest request = uploadFileToStorage("files/new/SYNGENTA_EFSA_sanitisation_GFL_v1_moreSections.pdf");
|
||||
System.out.println("Start Full integration test");
|
||||
analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request);
|
||||
@@ -133,8 +136,12 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
|
||||
System.out.println("Finished analysis");
|
||||
EntityLog entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
|
||||
var publishedInformationEntry1 = findEntityByTypeAndValue(entityLog, "published_information", "Oxford University Press").findFirst().orElseThrow();
|
||||
var asyaLyon1 = findEntityByTypeAndValueAndSectionNumber(entityLog, "CBI_author", "Asya Lyon", publishedInformationEntry1.getContainingNodeId()).findFirst().orElseThrow();
|
||||
var publishedInformationEntry1 = findEntityByTypeAndValue(entityLog, "published_information", "Oxford University Press").findFirst()
|
||||
.orElseThrow();
|
||||
assertThat(publishedInformationEntry1.getSection().startsWith("Paragraph:"));
|
||||
var asyaLyon1 = findEntityByTypeAndValueAndSectionNumber(entityLog, "CBI_author", "Asya Lyon", publishedInformationEntry1.getContainingNodeId()).findFirst()
|
||||
.orElseThrow();
|
||||
assertThat(publishedInformationEntry1.getSection().startsWith("Paragraph:"));
|
||||
|
||||
assertEquals(EntryState.SKIPPED, asyaLyon1.getState());
|
||||
|
||||
@@ -146,8 +153,10 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
|
||||
|
||||
entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
|
||||
var publishedInformationEntry2 = findEntityByTypeAndValue(entityLog, "published_information", "Oxford University Press").findFirst().orElseThrow();
|
||||
var asyaLyon2 = findEntityByTypeAndValueAndSectionNumber(entityLog, "CBI_author", "Asya Lyon", publishedInformationEntry2.getContainingNodeId()).findFirst().orElseThrow();
|
||||
var publishedInformationEntry2 = findEntityByTypeAndValue(entityLog, "published_information", "Oxford University Press").findFirst()
|
||||
.orElseThrow();
|
||||
var asyaLyon2 = findEntityByTypeAndValueAndSectionNumber(entityLog, "CBI_author", "Asya Lyon", publishedInformationEntry2.getContainingNodeId()).findFirst()
|
||||
.orElseThrow();
|
||||
|
||||
assertEquals(EntryState.APPLIED, asyaLyon2.getState());
|
||||
|
||||
@@ -168,19 +177,25 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
|
||||
.stream()
|
||||
.filter(entry -> entry.getType().equals(type))
|
||||
.filter(entry -> entry.getValue().equals(value))
|
||||
.filter(entry -> entry.getContainingNodeId().get(0).equals(sectionNumber.get(0)));
|
||||
.filter(entry -> entry.getContainingNodeId()
|
||||
.get(0).equals(sectionNumber.get(0)));
|
||||
}
|
||||
|
||||
|
||||
private static Stream<EntityLogEntry> findEntityByTypeAndValue(EntityLog redactionLog, String type, String value) {
|
||||
|
||||
return redactionLog.getEntityLogEntry().stream().filter(entry -> entry.getType().equals(type)).filter(entry -> entry.getValue().equals(value));
|
||||
return redactionLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entry -> entry.getType().equals(type))
|
||||
.filter(entry -> entry.getValue().equals(value));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void noEndlessLoopsTest() {
|
||||
|
||||
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
|
||||
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
|
||||
AnalyzeRequest request = uploadFileToStorage("files/new/SYNGENTA_EFSA_sanitisation_GFL_v1_moreSections.pdf");
|
||||
System.out.println("Start Full integration test");
|
||||
analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request);
|
||||
@@ -201,13 +216,15 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
|
||||
|
||||
var redactionLog2 = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
assertEquals(EntryState.IGNORED,
|
||||
findEntityByTypeAndValue(redactionLog2, "CBI_author", "Desiree").filter(entry -> entry.getEntryType().equals(EntryType.ENTITY)).findFirst().get().getState());
|
||||
findEntityByTypeAndValue(redactionLog2, "CBI_author", "Desiree").filter(entry -> entry.getEntryType().equals(EntryType.ENTITY))
|
||||
.findFirst()
|
||||
.get().getState());
|
||||
}
|
||||
|
||||
|
||||
private static IdRemoval buildIdRemoval(String id) {
|
||||
|
||||
return IdRemoval.builder().annotationId(id).requestDate(OffsetDateTime.now()).fileId(TEST_FILE_ID).build();
|
||||
return IdRemoval.builder().annotationId(id).user("user").requestDate(OffsetDateTime.now()).fileId(TEST_FILE_ID).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+97
-2
@@ -218,10 +218,12 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
ManualRedactions manualRedactions = ManualRedactions.builder()
|
||||
.resizeRedactions(Set.of(ManualResizeRedaction.builder()
|
||||
.annotationId("c6be5277f5ee60dc3d83527798b7fe02")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.value("Dr. Alan")
|
||||
.positions(List.of(new Rectangle(236.8f, 182.90005f, 40.584f, 12.642f, 7)))
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.updateDictionary(false)
|
||||
.user("user")
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
@@ -583,6 +585,8 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
@Test
|
||||
public void redactionTest() throws IOException {
|
||||
|
||||
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
|
||||
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
|
||||
String fileName = "files/new/crafted document.pdf";
|
||||
String outputFileName = OsUtils.getTemporaryDirectory() + "/Annotated.pdf";
|
||||
|
||||
@@ -656,6 +660,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
.fileId("fileId")
|
||||
.type("signature")
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.user("user")
|
||||
.build()));
|
||||
|
||||
request.setManualRedactions(manualRedactions);
|
||||
@@ -710,6 +715,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
.annotationId("308dab9015bfafd911568cffe0a7f7de")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.requestDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 475479, ZoneOffset.UTC))
|
||||
.user("user")
|
||||
.processedDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 483651, ZoneOffset.UTC))
|
||||
.build());
|
||||
|
||||
@@ -718,6 +724,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
.annotationId("0b56ea1a87c83f351df177315af94f0d")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.legalBasis("Something")
|
||||
.user("user")
|
||||
.requestDate(OffsetDateTime.of(2022, 05, 23, 9, 30, 15, 4653, ZoneOffset.UTC))
|
||||
.processedDate(OffsetDateTime.of(2022, 05, 23, 9, 30, 15, 794, ZoneOffset.UTC))
|
||||
.build());
|
||||
@@ -726,6 +733,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
.add(IdRemoval.builder()
|
||||
.annotationId("0b56ea1a87c83f351df177315af94f0d")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.requestDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 23, 961721, ZoneOffset.UTC))
|
||||
.processedDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 23, 96528, ZoneOffset.UTC))
|
||||
.build());
|
||||
@@ -945,6 +953,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
.textBefore("")
|
||||
.updateDictionary(false)
|
||||
.textAfter("")
|
||||
.user("user")
|
||||
.build();
|
||||
manualRedactions.getResizeRedactions().add(manualResizeRedaction);
|
||||
|
||||
@@ -1032,12 +1041,14 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
manualRedactions.setIdsToRemove(Set.of(IdRemoval.builder()
|
||||
.annotationId("5b940b2cb401ed9f5be6fc24f6e77bcf")
|
||||
.fileId("fileId")
|
||||
.user("user")
|
||||
.processedDate(OffsetDateTime.now())
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.build()));
|
||||
manualRedactions.setForceRedactions(Set.of(ManualForceRedaction.builder()
|
||||
.annotationId("675eba69b0c2917de55462c817adaa05")
|
||||
.fileId("fileId")
|
||||
.user("user")
|
||||
.legalBasis("Something")
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.processedDate(OffsetDateTime.now())
|
||||
@@ -1227,6 +1238,8 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
@Test
|
||||
public void signaturesAreRedactionAfterReanalyse() throws IOException {
|
||||
|
||||
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
|
||||
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
|
||||
AnalyzeRequest request = uploadFileToStorage("files/new/SYNGENTA_EFSA_sanitisation_GFL_v1 (1).pdf");
|
||||
ClassPathResource imageServiceResponseFileResource = new ClassPathResource("files/new/SYNGENTA_EFSA_sanitisation_GFL_v1 (1).IMAGE_INFO.json");
|
||||
|
||||
@@ -1248,6 +1261,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.fileId(TEST_FILE_ID)
|
||||
.legalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002")
|
||||
.user("user")
|
||||
.build()))
|
||||
.build());
|
||||
|
||||
@@ -1267,6 +1281,8 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
@Test
|
||||
public void entityIsAppliedAfterRecateorize() throws IOException {
|
||||
|
||||
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
|
||||
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
|
||||
AnalyzeRequest request = uploadFileToStorage("files/new/SYNGENTA_EFSA_sanitisation_GFL_v1 (1).pdf");
|
||||
ClassPathResource imageServiceResponseFileResource = new ClassPathResource("files/new/SYNGENTA_EFSA_sanitisation_GFL_v1 (1).IMAGE_INFO.json");
|
||||
|
||||
@@ -1288,13 +1304,14 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.fileId(TEST_FILE_ID)
|
||||
.legalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002")
|
||||
.user("user")
|
||||
.build()))
|
||||
.recategorizations(Set.of(ManualRecategorization.builder()
|
||||
.annotationId("3029651d0842a625f2d23f8375c23600")
|
||||
.type("CBI_author")
|
||||
.legalBasis("")
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.build()))
|
||||
.build());
|
||||
|
||||
@@ -1434,10 +1451,78 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testReAddingSameManualRedaction() {
|
||||
|
||||
String pdfFile = "files/new/test1S1T1.pdf";
|
||||
|
||||
String manualAddId = UUID.randomUUID().toString();
|
||||
List<Rectangle> positions = List.of(Rectangle.builder().topLeftX(305.35f).topLeftY(332.5033f).width(71.40744f).height(13.645125f).page(1).build());
|
||||
ManualRedactionEntry manualRedactionEntry = getManualRedactionEntry(manualAddId,
|
||||
positions,
|
||||
"the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety");
|
||||
|
||||
IdRemoval idRemoval = getIdRemoval(manualAddId);
|
||||
|
||||
AnalyzeRequest request = uploadFileToStorage(pdfFile);
|
||||
request.setManualRedactions(ManualRedactions.builder().entriesToAdd(Set.of(manualRedactionEntry)).build());
|
||||
analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request);
|
||||
request.setAnalysisNumber(1);
|
||||
analyzeService.analyze(request);
|
||||
|
||||
var entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
|
||||
assertTrue(entityLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.anyMatch(entityLogEntry -> entityLogEntry.getId().equals(manualAddId)));
|
||||
assertEquals(entityLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entityLogEntry -> entityLogEntry.getId().equals(manualAddId))
|
||||
.findFirst()
|
||||
.get().getState(), EntryState.APPLIED);
|
||||
|
||||
manualRedactionEntry.setProcessedDate(OffsetDateTime.now());
|
||||
request.setManualRedactions(ManualRedactions.builder().entriesToAdd(Set.of(manualRedactionEntry)).idsToRemove(Set.of(idRemoval)).build());
|
||||
request.setAnalysisNumber(2);
|
||||
analyzeService.reanalyze(request);
|
||||
|
||||
entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
|
||||
assertTrue(entityLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.anyMatch(entityLogEntry -> entityLogEntry.getId().equals(manualAddId)));
|
||||
assertEquals(entityLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entityLogEntry -> entityLogEntry.getId().equals(manualAddId))
|
||||
.findFirst()
|
||||
.get().getState(), EntryState.REMOVED);
|
||||
|
||||
|
||||
manualRedactionEntry.setRequestDate(OffsetDateTime.now());
|
||||
idRemoval.setProcessedDate(OffsetDateTime.now());
|
||||
request.setManualRedactions(ManualRedactions.builder().entriesToAdd(Set.of(manualRedactionEntry)).idsToRemove(Set.of(idRemoval)).build());
|
||||
request.setAnalysisNumber(3);
|
||||
analyzeService.reanalyze(request);
|
||||
|
||||
entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
|
||||
assertTrue(entityLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.anyMatch(entityLogEntry -> entityLogEntry.getId().equals(manualAddId)));
|
||||
assertEquals(entityLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entityLogEntry -> entityLogEntry.getId().equals(manualAddId))
|
||||
.findFirst()
|
||||
.get().getState(), EntryState.APPLIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testResizeWithUpdateDictionaryTrue() {
|
||||
|
||||
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
|
||||
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
|
||||
String pdfFile = "files/new/crafted document.pdf";
|
||||
|
||||
AnalyzeRequest request = uploadFileToStorage(pdfFile);
|
||||
@@ -1455,6 +1540,8 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
.resizeRedactions(Set.of(ManualResizeRedaction.builder()
|
||||
.updateDictionary(true)
|
||||
.annotationId(david.getId())
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.value("David Ksenia")
|
||||
.positions(List.of(Rectangle.builder()
|
||||
@@ -1482,7 +1569,14 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
|
||||
private IdRemoval getIdRemoval(String id) {
|
||||
|
||||
return IdRemoval.builder().annotationId(id).removeFromAllDossiers(false).removeFromDictionary(false).requestDate(OffsetDateTime.now()).build();
|
||||
return IdRemoval.builder()
|
||||
.annotationId(id)
|
||||
.removeFromAllDossiers(false)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.removeFromDictionary(false)
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1491,6 +1585,7 @@ public class RedactionIntegrationTest extends AbstractRedactionIntegrationTest {
|
||||
ManualRedactionEntry manualRedactionEntry2 = new ManualRedactionEntry();
|
||||
manualRedactionEntry2.setAnnotationId(id);
|
||||
manualRedactionEntry2.setFileId("fileId");
|
||||
manualRedactionEntry2.setUser("test");
|
||||
manualRedactionEntry2.setType("manual");
|
||||
manualRedactionEntry2.setRectangle(false);
|
||||
manualRedactionEntry2.setRequestDate(OffsetDateTime.now());
|
||||
|
||||
-2
@@ -5,14 +5,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.drools.io.ClassPathResource;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
+99
-150
@@ -1,6 +1,5 @@
|
||||
package com.iqser.red.service.redaction.v1.server.drools.files.management.services;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@@ -19,7 +18,6 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
|
||||
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation;
|
||||
import com.iqser.red.service.redaction.v1.model.RuleValidationModel;
|
||||
import com.iqser.red.service.redaction.v1.server.DeprecatedElementsFinder;
|
||||
import com.iqser.red.service.redaction.v1.server.client.RulesClient;
|
||||
import com.iqser.red.service.redaction.v1.server.model.drools.RuleFileBluePrint;
|
||||
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
|
||||
@@ -49,15 +47,13 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@SneakyThrows
|
||||
void testRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
@@ -66,16 +62,14 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@SneakyThrows
|
||||
void testRulesWithRemovedImportsAndChangedName() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
rulesString = rulesString.replaceAll("import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;", "");
|
||||
rulesString = rulesString.replaceAll("rule \"LDS.0.0: Run local dictionary search\"", "rule \"LDS.0.0: run local dictionary search\"");
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
@@ -83,32 +77,102 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@SneakyThrows
|
||||
void testRulesWithAddedImports() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
rulesString = rulesString.replaceAll("import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;",
|
||||
"import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;\nimport com.iqser.red.service.redaction.v1.server.service.document.EntityComparators;");
|
||||
rulesString = rulesString.replaceAll("import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;", "import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;\nimport com.iqser.red.service.redaction.v1.server.service.document.EntityComparators;");
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testRulesWithUnallowedAddedImports() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
rulesString = rulesString.replaceAll("import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;", "import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;\nimport com.google.common.collect.Sets;");
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertFalse(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testAllRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/all_redact_manager_rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testAcceptanceRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/acceptance_rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testManualRedactionRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/manual_redaction_rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testRulesV2() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/rules_v2.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testDocumineRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/documine_flora.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
@@ -117,8 +181,7 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@SneakyThrows
|
||||
void testCorruptedRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
@@ -133,13 +196,12 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@SneakyThrows
|
||||
void testCorruptedImports() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
String corruptedRules = rulesString.replaceAll("import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;",
|
||||
"import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.SomethingElse;");
|
||||
"import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.SomethingElse;");
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), corruptedRules));
|
||||
assertFalse(droolsSyntaxValidation.isCompiled());
|
||||
@@ -150,16 +212,14 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@SneakyThrows
|
||||
void testRemoveQuery() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
String corruptedRules = rulesString.replaceAll("query \"getFileAttributes\"\n.*\n *end", "");
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), corruptedRules));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertFalse(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
@@ -168,15 +228,13 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@SneakyThrows
|
||||
void testComponentDrools() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/documine_flora_components.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.COMPONENT.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
@@ -185,15 +243,13 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@SneakyThrows
|
||||
void testComponentDroolsAsEntityDrools() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
var rulesFile = new ClassPathResource("drools/documine_flora_components.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages().forEach(System.out::println);
|
||||
assertFalse(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
@@ -203,15 +259,13 @@ class DroolsSyntaxValidationServiceTest {
|
||||
@Disabled
|
||||
void attemptImportsFixToAllRuleFiles() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient));
|
||||
|
||||
List<String> ruleFiles = List.of("drools/rules.drl",
|
||||
"drools/all_redact_manager_rules.drl",
|
||||
"drools/documine_flora.drl",
|
||||
"drools/manual_redaction_rules.drl",
|
||||
"drools/acceptance_rules.drl",
|
||||
"drools/rules_v2.drl");
|
||||
List<String> ruleFiles = List.of("drools/rules.drl", "drools/all_redact_manager_rules.drl",
|
||||
"drools/documine_flora.drl",
|
||||
"drools/manual_redaction_rules.drl",
|
||||
"drools/acceptance_rules.drl",
|
||||
"drools/rules_v2.drl");
|
||||
|
||||
for (String ruleFile : ruleFiles) {
|
||||
var rulesFile = new ClassPathResource(ruleFile);
|
||||
@@ -234,109 +288,4 @@ class DroolsSyntaxValidationServiceTest {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testRulesWithUnallowedAddedImports() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
rulesString = rulesString.replaceAll("import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;",
|
||||
"import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;\nimport com.google.common.collect.Sets;");
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
assertFalse(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testAllRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
var rulesFile = new ClassPathResource("drools/all_redact_manager_rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testAcceptanceRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
var rulesFile = new ClassPathResource("drools/acceptance_rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testManualRedactionRules() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
var rulesFile = new ClassPathResource("drools/manual_redaction_rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testRulesV2() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
var rulesFile = new ClassPathResource("drools/rules_v2.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testRulesWithDeprecatedFunctions() {
|
||||
|
||||
DroolsSyntaxValidationService droolsSyntaxValidationService = new DroolsSyntaxValidationService(new KieContainerCreationService(rulesClient),
|
||||
new DeprecatedElementsFinder());
|
||||
var rulesFile = new ClassPathResource("drools/rules.drl");
|
||||
|
||||
String rulesString = new String(rulesFile.getInputStream().readAllBytes());
|
||||
rulesString = rulesString.replaceAll(".resize\\(", ".resizeEntityAndReinsert(");
|
||||
|
||||
DroolsSyntaxValidation droolsSyntaxValidation = droolsSyntaxValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), rulesString));
|
||||
droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
|
||||
.forEach(System.out::println);
|
||||
assertTrue(droolsSyntaxValidation.isCompiled());
|
||||
assertEquals(droolsSyntaxValidation.getDroolsSyntaxDeprecatedWarnings().size(), 2);
|
||||
}
|
||||
|
||||
}
|
||||
+78
-53
@@ -127,15 +127,15 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
|
||||
when(dictionaryClient.getVersion(TEST_DOSSIER_TEMPLATE_ID)).thenReturn(0L);
|
||||
when(dictionaryClient.getAllTypesForDossier(TEST_DOSSIER_ID, false)).thenReturn(List.of(Type.builder()
|
||||
.id(DOSSIER_REDACTIONS_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID)
|
||||
.type(DOSSIER_REDACTIONS_INDICATOR)
|
||||
.dossierTemplateId(TEST_DOSSIER_ID)
|
||||
.hexColor("#ffe187")
|
||||
.isHint(hintTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isCaseInsensitive(caseInSensitiveMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isRecommendation(recommendationTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.rank(rankTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.build()));
|
||||
.id(DOSSIER_REDACTIONS_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID)
|
||||
.type(DOSSIER_REDACTIONS_INDICATOR)
|
||||
.dossierTemplateId(TEST_DOSSIER_ID)
|
||||
.hexColor("#ffe187")
|
||||
.isHint(hintTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isCaseInsensitive(caseInSensitiveMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isRecommendation(recommendationTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.rank(rankTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.build()));
|
||||
|
||||
mockDictionaryCalls(null);
|
||||
|
||||
@@ -155,29 +155,40 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
String testEntityValue1 = "Desiree";
|
||||
String testEntityValue2 = "Melanie";
|
||||
EntityLog redactionLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
assertEquals(2, redactionLog.getEntityLogEntry().stream().filter(entry -> entry.getValue().equals(testEntityValue1)).count());
|
||||
assertEquals(2, redactionLog.getEntityLogEntry().stream().filter(entry -> entry.getValue().equals(testEntityValue2)).count());
|
||||
assertEquals(2,
|
||||
redactionLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue().equals(testEntityValue1))
|
||||
.count());
|
||||
assertEquals(2,
|
||||
redactionLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue().equals(testEntityValue2))
|
||||
.count());
|
||||
|
||||
Document document = DocumentGraphMapper.toDocumentGraph(redactionStorageService.getDocumentData(TEST_DOSSIER_ID, TEST_FILE_ID));
|
||||
String expandedEntityKeyword = "Lorem ipsum dolor sit amet, consectetur adipiscing elit Desiree et al sed do eiusmod tempor incididunt ut labore et dolore magna aliqua Melanie et al. Reference No 12345 Lorem ipsum.";
|
||||
entityCreationService.byString(expandedEntityKeyword, "PII", EntityType.ENTITY, document).findFirst().get();
|
||||
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))
|
||||
.get()
|
||||
.getId();
|
||||
.get().getId();
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
manualRedactions.getResizeRedactions().add(ManualResizeRedaction.builder()
|
||||
.annotationId(idToResize)
|
||||
.value(expandedEntityKeyword)
|
||||
.positions(List.of(Rectangle.builder().topLeftX(56.8f).topLeftY(454.664f).height(15.408f).width(493.62f).page(3).build(),
|
||||
Rectangle.builder().topLeftX(56.8f).topLeftY(440.864f).height(15.408f).width(396f).page(3).build()))
|
||||
.addToAllDossiers(false)
|
||||
.updateDictionary(false)
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.build());
|
||||
manualRedactions.getResizeRedactions()
|
||||
.add(ManualResizeRedaction.builder()
|
||||
.annotationId(idToResize)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.value(expandedEntityKeyword)
|
||||
.positions(List.of(Rectangle.builder().topLeftX(56.8f).topLeftY(454.664f).height(15.408f).width(493.62f).page(3).build(),
|
||||
Rectangle.builder().topLeftX(56.8f).topLeftY(440.864f).height(15.408f).width(396f).page(3).build()))
|
||||
.addToAllDossiers(false)
|
||||
.updateDictionary(false)
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.build());
|
||||
request.setManualRedactions(manualRedactions);
|
||||
analyzeService.reanalyze(request);
|
||||
|
||||
@@ -188,21 +199,32 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile)) {
|
||||
fileOutputStream.write(annotateResponse.getDocument());
|
||||
}
|
||||
EntityLogEntry resizedEntry = redactionLog.getEntityLogEntry().stream().filter(entry -> entry.getValue().equals(expandedEntityKeyword)).findFirst().get();
|
||||
EntityLogEntry resizedEntry = redactionLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue().equals(expandedEntityKeyword))
|
||||
.findFirst()
|
||||
.get();
|
||||
assertEquals(idToResize, resizedEntry.getId());
|
||||
assertEquals(1, redactionLog.getEntityLogEntry().stream().filter(entry -> entry.getValue().equals(testEntityValue1)).count());
|
||||
assertEquals(1,
|
||||
redactionLog.getEntityLogEntry().stream().filter(entry -> entry.getValue().equals(testEntityValue2) && !entry.getState().equals(EntryState.REMOVED)).count());
|
||||
redactionLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue().equals(testEntityValue1))
|
||||
.count());
|
||||
assertEquals(1,
|
||||
redactionLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue().equals(testEntityValue2) && !entry.getState().equals(EntryState.REMOVED))
|
||||
.count());
|
||||
}
|
||||
|
||||
|
||||
private static com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle toAnnotationRectangle(Rectangle2D rectangle2D, int pageNumber) {
|
||||
|
||||
return new com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle((float) rectangle2D.getMaxX(),
|
||||
(float) rectangle2D.getMaxY() - (float) rectangle2D.getHeight(),
|
||||
(float) rectangle2D.getWidth(),
|
||||
-(float) rectangle2D.getHeight(),
|
||||
pageNumber);
|
||||
(float) rectangle2D.getMaxY() - (float) rectangle2D.getHeight(),
|
||||
(float) rectangle2D.getWidth(),
|
||||
-(float) rectangle2D.getHeight(),
|
||||
pageNumber);
|
||||
}
|
||||
|
||||
|
||||
@@ -219,10 +241,10 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
|
||||
manualRedactions.setIdsToRemove(Set.of(IdRemoval.builder().annotationId("5b940b2cb401ed9f5be6fc24f6e77bcf").fileId("fileId").build()));
|
||||
manualRedactions.setForceRedactions(Set.of(ManualForceRedaction.builder()
|
||||
.annotationId("675eba69b0c2917de55462c817adaa05")
|
||||
.fileId("fileId")
|
||||
.legalBasis("Something")
|
||||
.build()));
|
||||
.annotationId("675eba69b0c2917de55462c817adaa05")
|
||||
.fileId("fileId")
|
||||
.legalBasis("Something")
|
||||
.build()));
|
||||
|
||||
ManualRedactionEntry manualRedactionEntry = new ManualRedactionEntry();
|
||||
manualRedactionEntry.setAnnotationId(manualAddId);
|
||||
@@ -232,7 +254,7 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
manualRedactionEntry.setValue("O'Loughlin C.K.");
|
||||
manualRedactionEntry.setReason("Manual Redaction");
|
||||
manualRedactionEntry.setPositions(List.of(Rectangle.builder().topLeftX(375.61096f).topLeftY(241.282f).width(7.648041f).height(43.72262f).page(1).build(),
|
||||
Rectangle.builder().topLeftX(384.83517f).topLeftY(241.282f).width(7.648041f).height(17.043358f).page(1).build()));
|
||||
Rectangle.builder().topLeftX(384.83517f).topLeftY(241.282f).width(7.648041f).height(17.043358f).page(1).build()));
|
||||
|
||||
AnalyzeRequest request = uploadFileToStorage(pdfFile);
|
||||
request.setManualRedactions(manualRedactions);
|
||||
@@ -242,11 +264,11 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
manualRedactions.getEntriesToAdd().add(manualRedactionEntry);
|
||||
manualRedactions.setIdsToRemove(Set.of(IdRemoval.builder().annotationId("5b940b2cb401ed9f5be6fc24f6e77bcf").fileId("fileId").build()));
|
||||
manualRedactions.setLegalBasisChanges((Set.of(ManualLegalBasisChange.builder()
|
||||
.annotationId("675eba69b0c2917de55462c817adaa05")
|
||||
.fileId("fileId")
|
||||
.legalBasis("Manual Legal Basis Change")
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.build())));
|
||||
.annotationId("675eba69b0c2917de55462c817adaa05")
|
||||
.fileId("fileId")
|
||||
.legalBasis("Manual Legal Basis Change")
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.build())));
|
||||
|
||||
analyzeService.reanalyze(request);
|
||||
|
||||
@@ -295,7 +317,10 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
fileOutputStream.write(annotateResponse.getDocument());
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
var optionalEntry = redactionLog.getEntityLogEntry().stream().filter(entityLogEntry -> entityLogEntry.getId().equals(manualAddId)).findAny();
|
||||
var optionalEntry = redactionLog.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entityLogEntry -> entityLogEntry.getId().equals(manualAddId))
|
||||
.findAny();
|
||||
assertTrue(optionalEntry.isPresent());
|
||||
assertEquals(2, optionalEntry.get().getContainingNodeId().size()); // 2 is the depth of the table instead of the table cell
|
||||
System.out.println("duration: " + (end - start));
|
||||
@@ -345,9 +370,9 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
|
||||
EntityLog redactionLog2 = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
assertFalse(redactionLog2.getEntityLogEntry()
|
||||
.stream()
|
||||
.filter(entry -> entry.getType().equals("published_information"))
|
||||
.anyMatch(entry -> entry.getValue().equals("Oxford University Press")));
|
||||
.stream()
|
||||
.filter(entry -> entry.getType().equals("published_information"))
|
||||
.anyMatch(entry -> entry.getValue().equals("Oxford University Press")));
|
||||
|
||||
var oxfordUniversityPressRecategorized = redactionLog2.getEntityLogEntry()
|
||||
.stream()
|
||||
@@ -381,15 +406,15 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
|
||||
String annotationId = "testAnnotationId";
|
||||
|
||||
manualRedactions.setEntriesToAdd(Set.of(ManualRedactionEntry.builder()
|
||||
.annotationId(annotationId)
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.type("manual")
|
||||
.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))) //
|
||||
.build()));
|
||||
.annotationId(annotationId)
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.type("manual")
|
||||
.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))) //
|
||||
.build()));
|
||||
ManualResizeRedaction manualResizeRedaction = ManualResizeRedaction.builder()
|
||||
.annotationId(annotationId)
|
||||
.requestDate(OffsetDateTime.now())
|
||||
|
||||
+94
-29
@@ -32,18 +32,33 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
public void manualResizeRedactionTest() {
|
||||
|
||||
Document document = buildGraph("files/new/crafted document");
|
||||
Set<TextEntity> entities = entityCreationService.byString("David Ksenia", "CBI_author", EntityType.ENTITY, document).collect(Collectors.toUnmodifiableSet());
|
||||
Set<TextEntity> entities = entityCreationService.byString("David Ksenia", "CBI_author", EntityType.ENTITY, document)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
Set<TextEntity> biggerEntities = entityCreationService.byString("David Ksenia Max Mustermann", "CBI_author", EntityType.ENTITY, document)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
TextEntity entity = entities.stream().filter(e -> e.getPages().stream().anyMatch(p -> p.getNumber() == 1)).findFirst().get();
|
||||
TextEntity biggerEntity = biggerEntities.stream().filter(e -> e.getPages().stream().anyMatch(p -> p.getNumber() == 1)).findFirst().get();
|
||||
TextEntity entity = entities.stream()
|
||||
.filter(e -> e.getPages()
|
||||
.stream()
|
||||
.anyMatch(p -> p.getNumber() == 1))
|
||||
.findFirst()
|
||||
.get();
|
||||
TextEntity biggerEntity = biggerEntities.stream()
|
||||
.filter(e -> e.getPages()
|
||||
.stream()
|
||||
.anyMatch(p -> p.getNumber() == 1))
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
String initialId = entity.getPositionsOnPagePerPage().get(0).getId();
|
||||
String initialId = entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId();
|
||||
ManualResizeRedaction manualResizeRedaction = ManualResizeRedaction.builder()
|
||||
.annotationId(initialId)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.value(biggerEntity.getValue())
|
||||
.positions(toAnnotationRectangles(biggerEntity.getPositionsOnPagePerPage().get(0)))
|
||||
.positions(toAnnotationRectangles(biggerEntity.getPositionsOnPagePerPage()
|
||||
.get(0)))
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.updateDictionary(false)
|
||||
.build();
|
||||
@@ -55,8 +70,13 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
assertTrue(Sets.difference(new HashSet<>(biggerEntity.getIntersectingNodes()), new HashSet<>(entity.getIntersectingNodes())).isEmpty());
|
||||
assertEquals(biggerEntity.getPages(), entity.getPages());
|
||||
assertEquals(biggerEntity.getValue(), entity.getValue());
|
||||
assertEquals(initialId, entity.getPositionsOnPagePerPage().get(0).getId());
|
||||
assertRectanglesAlmostEqual(biggerEntity.getPositionsOnPagePerPage().get(0).getRectanglePerLine(), entity.getPositionsOnPagePerPage().get(0).getRectanglePerLine());
|
||||
assertEquals(initialId,
|
||||
entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId());
|
||||
assertRectanglesAlmostEqual(biggerEntity.getPositionsOnPagePerPage()
|
||||
.get(0).getRectanglePerLine(),
|
||||
entity.getPositionsOnPagePerPage()
|
||||
.get(0).getRectanglePerLine());
|
||||
assertTrue(entity.resized());
|
||||
}
|
||||
|
||||
@@ -65,12 +85,25 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
public void manualForceRedactionTest() {
|
||||
|
||||
Document document = buildGraph("files/new/crafted document");
|
||||
Set<TextEntity> entities = entityCreationService.byString("David Ksenia", "CBI_author", EntityType.ENTITY, document).collect(Collectors.toUnmodifiableSet());
|
||||
Set<TextEntity> entities = entityCreationService.byString("David Ksenia", "CBI_author", EntityType.ENTITY, document)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
TextEntity entity = entities.stream().filter(e -> e.getPages().stream().anyMatch(p -> p.getNumber() == 1)).findFirst().get();
|
||||
TextEntity entity = entities.stream()
|
||||
.filter(e -> e.getPages()
|
||||
.stream()
|
||||
.anyMatch(p -> p.getNumber() == 1))
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
String initialId = entity.getPositionsOnPagePerPage().get(0).getId();
|
||||
ManualForceRedaction manualForceRedaction = ManualForceRedaction.builder().annotationId(initialId).legalBasis("Something").requestDate(OffsetDateTime.now()).build();
|
||||
String initialId = entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId();
|
||||
ManualForceRedaction manualForceRedaction = ManualForceRedaction.builder()
|
||||
.annotationId(initialId)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.legalBasis("Something")
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.build();
|
||||
|
||||
doAnalysis(document, List.of(manualForceRedaction));
|
||||
|
||||
@@ -78,8 +111,12 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
assertFalse(entity.getIntersectingNodes().isEmpty());
|
||||
assertEquals(1, entity.getPages().size());
|
||||
assertEquals("David Ksenia", entity.getValue());
|
||||
assertEquals("Something", entity.getManualOverwrite().getLegalBasis().orElse(entity.getMatchedRule().getLegalBasis()));
|
||||
assertEquals(initialId, entity.getPositionsOnPagePerPage().get(0).getId());
|
||||
assertEquals("Something",
|
||||
entity.getManualOverwrite().getLegalBasis()
|
||||
.orElse(entity.getMatchedRule().getLegalBasis()));
|
||||
assertEquals(initialId,
|
||||
entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId());
|
||||
assertFalse(entity.removed());
|
||||
assertTrue(entity.hasManualChanges());
|
||||
assertTrue(entity.applied());
|
||||
@@ -90,17 +127,26 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
public void manualIDRemovalTest() {
|
||||
|
||||
Document document = buildGraph("files/new/crafted document");
|
||||
Set<TextEntity> entities = entityCreationService.byString("David Ksenia", "CBI_author", EntityType.ENTITY, document).collect(Collectors.toUnmodifiableSet());
|
||||
Set<TextEntity> entities = entityCreationService.byString("David Ksenia", "CBI_author", EntityType.ENTITY, document)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
TextEntity entity = entities.stream().filter(e -> e.getPages().stream().anyMatch(p -> p.getNumber() == 1)).findFirst().get();
|
||||
TextEntity entity = entities.stream()
|
||||
.filter(e -> e.getPages()
|
||||
.stream()
|
||||
.anyMatch(p -> p.getNumber() == 1))
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
String initialId = entity.getPositionsOnPagePerPage().get(0).getId();
|
||||
IdRemoval idRemoval = IdRemoval.builder().annotationId(initialId).requestDate(OffsetDateTime.now()).build();
|
||||
String initialId = entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId();
|
||||
IdRemoval idRemoval = IdRemoval.builder().annotationId(initialId).requestDate(OffsetDateTime.now()).fileId(TEST_FILE_ID).user("user").build();
|
||||
|
||||
doAnalysis(document, List.of(idRemoval));
|
||||
|
||||
assertEquals("David Ksenia", entity.getValue());
|
||||
assertEquals(initialId, entity.getPositionsOnPagePerPage().get(0).getId());
|
||||
assertEquals(initialId,
|
||||
entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId());
|
||||
assertTrue(entity.ignored());
|
||||
}
|
||||
|
||||
@@ -109,13 +155,25 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
public void manualIDRemovalButAlsoForceRedactionTest() {
|
||||
|
||||
Document document = buildGraph("files/new/crafted document");
|
||||
Set<TextEntity> entities = entityCreationService.byString("David Ksenia", "CBI_author", EntityType.ENTITY, document).collect(Collectors.toUnmodifiableSet());
|
||||
Set<TextEntity> entities = entityCreationService.byString("David Ksenia", "CBI_author", EntityType.ENTITY, document)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
TextEntity entity = entities.stream().filter(e -> e.getPages().stream().anyMatch(p -> p.getNumber() == 1)).findFirst().get();
|
||||
TextEntity entity = entities.stream()
|
||||
.filter(e -> e.getPages()
|
||||
.stream()
|
||||
.anyMatch(p -> p.getNumber() == 1))
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
String initialId = entity.getPositionsOnPagePerPage().get(0).getId();
|
||||
IdRemoval idRemoval = IdRemoval.builder().annotationId(initialId).requestDate(OffsetDateTime.now()).build();
|
||||
ManualForceRedaction manualForceRedaction = ManualForceRedaction.builder().annotationId(initialId).legalBasis("Something").requestDate(OffsetDateTime.now()).build();
|
||||
String initialId = entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId();
|
||||
ManualForceRedaction manualForceRedaction = ManualForceRedaction.builder()
|
||||
.annotationId(initialId)
|
||||
.legalBasis("Something")
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.build();
|
||||
|
||||
doAnalysis(document, List.of(manualForceRedaction));
|
||||
|
||||
@@ -123,7 +181,9 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
assertFalse(entity.getIntersectingNodes().isEmpty());
|
||||
assertEquals(1, entity.getPages().size());
|
||||
assertEquals("David Ksenia", entity.getValue());
|
||||
assertEquals(initialId, entity.getPositionsOnPagePerPage().get(0).getId());
|
||||
assertEquals(initialId,
|
||||
entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId());
|
||||
assertFalse(entity.removed());
|
||||
assertFalse(entity.ignored());
|
||||
}
|
||||
@@ -131,7 +191,9 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
|
||||
private void assertRectanglesAlmostEqual(Collection<Rectangle2D> rects1, Collection<Rectangle2D> rects2) {
|
||||
|
||||
if (rects1.stream().allMatch(rect1 -> rects2.stream().anyMatch(rect2 -> rectanglesAlmostEqual(rect1, rect2)))) {
|
||||
if (rects1.stream()
|
||||
.allMatch(rect1 -> rects2.stream()
|
||||
.anyMatch(rect2 -> rectanglesAlmostEqual(rect1, rect2)))) {
|
||||
return;
|
||||
}
|
||||
// use this for nice formatting of error message
|
||||
@@ -143,15 +205,18 @@ public class ManualChangesIntegrationTest extends RulesIntegrationTest {
|
||||
|
||||
double tolerance = 1e-1;
|
||||
return Math.abs(r1.getX() - r2.getX()) < tolerance &&//
|
||||
Math.abs(r1.getY() - r2.getY()) < tolerance &&//
|
||||
Math.abs(r1.getWidth() - r2.getWidth()) < tolerance &&//
|
||||
Math.abs(r1.getHeight() - r2.getHeight()) < tolerance;
|
||||
Math.abs(r1.getY() - r2.getY()) < tolerance &&//
|
||||
Math.abs(r1.getWidth() - r2.getWidth()) < tolerance &&//
|
||||
Math.abs(r1.getHeight() - r2.getHeight()) < tolerance;
|
||||
}
|
||||
|
||||
|
||||
private static List<Rectangle> toAnnotationRectangles(PositionOnPage positionsOnPage) {
|
||||
|
||||
return positionsOnPage.getRectanglePerLine().stream().map(rectangle2D -> toAnnotationRectangle(rectangle2D, positionsOnPage.getPage().getNumber())).toList();
|
||||
return positionsOnPage.getRectanglePerLine()
|
||||
.stream()
|
||||
.map(rectangle2D -> toAnnotationRectangle(rectangle2D, positionsOnPage.getPage().getNumber()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+43
-18
@@ -43,7 +43,9 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
OffsetDateTime start = OffsetDateTime.now();
|
||||
String reason = "whatever";
|
||||
Document document = buildGraphNoImages("files/new/crafted document.pdf");
|
||||
List<TextEntity> entities = entityCreationService.byString("David Ksenia", "test", EntityType.ENTITY, document).peek(e -> e.apply("T.0.0", reason)).toList();
|
||||
List<TextEntity> entities = entityCreationService.byString("David Ksenia", "test", EntityType.ENTITY, document)
|
||||
.peek(e -> e.apply("T.0.0", reason))
|
||||
.toList();
|
||||
assertFalse(entities.isEmpty());
|
||||
TextEntity entity = entities.get(0);
|
||||
assertTrue(entity.active());
|
||||
@@ -52,10 +54,11 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
assertFalse(entity.resized());
|
||||
assertFalse(entity.ignored());
|
||||
assertEquals("n-a", entity.getMatchedRule().getLegalBasis());
|
||||
String annotationId = entity.getPositionsOnPagePerPage().get(0).getId();
|
||||
String annotationId = entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId();
|
||||
|
||||
// remove first
|
||||
IdRemoval removal = IdRemoval.builder().requestDate(start).fileId(TEST_FILE_ID).annotationId(annotationId).build();
|
||||
IdRemoval removal = IdRemoval.builder().requestDate(start).fileId(TEST_FILE_ID).user("user").annotationId(annotationId).build();
|
||||
entity.getManualOverwrite().addChange(removal);
|
||||
assertTrue(entity.ignored());
|
||||
assertFalse(entity.applied());
|
||||
@@ -65,6 +68,7 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
ManualForceRedaction forceRedaction = ManualForceRedaction.builder()
|
||||
.requestDate(start.plusSeconds(1))
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.annotationId(annotationId)
|
||||
.legalBasis("coolio")
|
||||
.build();
|
||||
@@ -73,10 +77,12 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
assertFalse(entity.ignored());
|
||||
assertFalse(entity.removed());
|
||||
assertEquals(reason + ", removed by manual override, forced by manual override", entity.buildReasonWithManualChangeDescriptions());
|
||||
assertEquals("coolio", entity.getManualOverwrite().getLegalBasis().orElse(entity.getMatchedRule().getLegalBasis()));
|
||||
assertEquals("coolio",
|
||||
entity.getManualOverwrite().getLegalBasis()
|
||||
.orElse(entity.getMatchedRule().getLegalBasis()));
|
||||
|
||||
// remove again
|
||||
IdRemoval removal2 = IdRemoval.builder().requestDate(start.plusSeconds(3)).fileId(TEST_FILE_ID).annotationId(annotationId).build();
|
||||
IdRemoval removal2 = IdRemoval.builder().requestDate(start.plusSeconds(3)).fileId(TEST_FILE_ID).annotationId(annotationId).user("user").build();
|
||||
entity.getManualOverwrite().addChange(removal2);
|
||||
assertTrue(entity.ignored());
|
||||
assertFalse(entity.applied());
|
||||
@@ -86,6 +92,7 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
ManualForceRedaction forceRedaction2 = ManualForceRedaction.builder()
|
||||
.requestDate(start.plusSeconds(2))
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.annotationId(annotationId)
|
||||
.legalBasis("coolio")
|
||||
.build();
|
||||
@@ -93,7 +100,7 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
assertTrue(entity.ignored());
|
||||
assertFalse(entity.applied());
|
||||
assertEquals(reason + ", removed by manual override, forced by manual override, forced by manual override, removed by manual override",
|
||||
entity.buildReasonWithManualChangeDescriptions());
|
||||
entity.buildReasonWithManualChangeDescriptions());
|
||||
|
||||
String legalBasis = "Yeah";
|
||||
String section = "Some random section!";
|
||||
@@ -103,6 +110,7 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
.annotationId(annotationId)
|
||||
.requestDate(start.plusSeconds(4))
|
||||
.section(section)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("peter")
|
||||
.value(value)
|
||||
.build();
|
||||
@@ -110,21 +118,32 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
assertTrue(entity.ignored());
|
||||
assertFalse(entity.applied());
|
||||
assertEquals(reason + ", removed by manual override, forced by manual override, forced by manual override, removed by manual override, legal basis was manually changed",
|
||||
entity.buildReasonWithManualChangeDescriptions());
|
||||
assertEquals(value, entity.getManualOverwrite().getValue().orElse(entity.getValue()));
|
||||
assertEquals(legalBasis, entity.getManualOverwrite().getLegalBasis().orElse(entity.getMatchedRule().getLegalBasis()));
|
||||
assertEquals(section, entity.getManualOverwrite().getSection().orElse(entity.getDeepestFullyContainingNode().toString()));
|
||||
entity.buildReasonWithManualChangeDescriptions());
|
||||
assertEquals(value,
|
||||
entity.getManualOverwrite().getValue()
|
||||
.orElse(entity.getValue()));
|
||||
assertEquals(legalBasis,
|
||||
entity.getManualOverwrite().getLegalBasis()
|
||||
.orElse(entity.getMatchedRule().getLegalBasis()));
|
||||
assertEquals(section,
|
||||
entity.getManualOverwrite().getSection()
|
||||
.orElse(entity.getDeepestFullyContainingNode().toString()));
|
||||
|
||||
ManualRecategorization imageRecategorizationRequest = ManualRecategorization.builder()
|
||||
.type("type")
|
||||
.requestDate(start.plusSeconds(5))
|
||||
.fileId(TEST_FILE_ID)
|
||||
.user("user")
|
||||
.annotationId(annotationId)
|
||||
.legalBasis("")
|
||||
.build();
|
||||
entity.getManualOverwrite().addChange(imageRecategorizationRequest);
|
||||
assertTrue(entity.getManualOverwrite().getRecategorized().isPresent());
|
||||
assertTrue(entity.getManualOverwrite().getRecategorized().get());
|
||||
assertEquals("type", entity.getManualOverwrite().getType().orElse(entity.type()));
|
||||
assertTrue(entity.getManualOverwrite().getRecategorized()
|
||||
.isPresent());
|
||||
assertTrue(entity.getManualOverwrite().getRecategorized()
|
||||
.get());
|
||||
assertEquals("type",
|
||||
entity.getManualOverwrite().getType()
|
||||
.orElse(entity.type()));
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +153,9 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
OffsetDateTime start = OffsetDateTime.now();
|
||||
String reason = "whatever";
|
||||
Document document = buildGraphNoImages("files/new/crafted document.pdf");
|
||||
List<TextEntity> entities = entityCreationService.byString("David Ksenia", "test", EntityType.HINT, document).peek(e -> e.apply("T.0.0", reason)).toList();
|
||||
List<TextEntity> entities = entityCreationService.byString("David Ksenia", "test", EntityType.HINT, document)
|
||||
.peek(e -> e.apply("T.0.0", reason))
|
||||
.toList();
|
||||
assertFalse(entities.isEmpty());
|
||||
TextEntity entity = entities.get(0);
|
||||
assertTrue(entity.active());
|
||||
@@ -143,10 +164,11 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
assertFalse(entity.resized());
|
||||
assertFalse(entity.ignored());
|
||||
assertEquals("n-a", entity.getMatchedRule().getLegalBasis());
|
||||
String annotationId = entity.getPositionsOnPagePerPage().get(0).getId();
|
||||
String annotationId = entity.getPositionsOnPagePerPage()
|
||||
.get(0).getId();
|
||||
|
||||
// remove first
|
||||
IdRemoval removal = IdRemoval.builder().requestDate(start).fileId(TEST_FILE_ID).annotationId(annotationId).build();
|
||||
IdRemoval removal = IdRemoval.builder().requestDate(start).fileId(TEST_FILE_ID).annotationId(annotationId).user("user").build();
|
||||
entity.getManualOverwrite().addChange(removal);
|
||||
assertTrue(entity.ignored());
|
||||
assertFalse(entity.applied());
|
||||
@@ -157,6 +179,7 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
.requestDate(start.plusSeconds(1))
|
||||
.fileId(TEST_FILE_ID)
|
||||
.annotationId(annotationId)
|
||||
.user("user")
|
||||
.legalBasis("coolio")
|
||||
.build();
|
||||
entity.getManualOverwrite().addChange(forceRedaction);
|
||||
@@ -164,7 +187,9 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
|
||||
assertFalse(entity.ignored());
|
||||
assertFalse(entity.removed());
|
||||
assertEquals(reason + ", removed by manual override, forced by manual override", entity.buildReasonWithManualChangeDescriptions());
|
||||
assertEquals("coolio", entity.getManualOverwrite().getLegalBasis().orElse(entity.getMatchedRule().getLegalBasis()));
|
||||
assertEquals("coolio",
|
||||
entity.getManualOverwrite().getLegalBasis()
|
||||
.orElse(entity.getMatchedRule().getLegalBasis()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
+34
-17
@@ -84,7 +84,7 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
|
||||
public void testFoundManualAddRedactionAndRemovedHasStateRemoved() {
|
||||
|
||||
DocumentAndEntity context = createFoundManualRedaction();
|
||||
IdRemoval removal = IdRemoval.builder().requestDate(OffsetDateTime.now()).build();
|
||||
IdRemoval removal = IdRemoval.builder().annotationId("123").user("user").fileId(TEST_FILE_ID).requestDate(OffsetDateTime.now()).build();
|
||||
context.entity().getManualOverwrite().addChange(removal);
|
||||
assertTrue(context.entity().removed());
|
||||
}
|
||||
@@ -95,7 +95,7 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
|
||||
public void testNotFoundManualAddRedactionAndRemovedHasStateRemoved() {
|
||||
|
||||
DocumentAndEntity context = createNotFoundManualRedaction();
|
||||
IdRemoval removal = IdRemoval.builder().requestDate(OffsetDateTime.now()).build();
|
||||
IdRemoval removal = IdRemoval.builder().fileId(TEST_FILE_ID).user("user").annotationId("123").requestDate(OffsetDateTime.now()).build();
|
||||
context.entity().getManualOverwrite().addChange(removal);
|
||||
assertTrue(context.entity().removed());
|
||||
}
|
||||
@@ -108,8 +108,11 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
|
||||
String value = "To: Syngenta Ltd. Jealott’s Hill";
|
||||
String type = DICTIONARY_AUTHOR;
|
||||
ManualRedactionEntry manualRedactionEntry = ManualRedactionEntry.builder()
|
||||
.annotationId("123")
|
||||
.type(type)
|
||||
.value(value)
|
||||
.user("user")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.reason("reason")
|
||||
.legalBasis("n-a")
|
||||
.section("n-a")
|
||||
@@ -122,17 +125,20 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
|
||||
|
||||
assertTrue(document.getEntities().isEmpty());
|
||||
|
||||
List<PrecursorEntity> notFoundManualEntities = entityFromPrecursorCreationService.createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions.builder().entriesToAdd(Set.of(manualRedactionEntry)).build(),
|
||||
document,
|
||||
TEST_DOSSIER_TEMPLATE_ID);
|
||||
List<PrecursorEntity> notFoundManualEntities = entityFromPrecursorCreationService.createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions.builder()
|
||||
.entriesToAdd(Set.of(
|
||||
manualRedactionEntry))
|
||||
.build(),
|
||||
document,
|
||||
TEST_DOSSIER_TEMPLATE_ID);
|
||||
assertEquals(1, notFoundManualEntities.size());
|
||||
assertTrue(document.getEntities().isEmpty());
|
||||
|
||||
List<EntityLogEntry> redactionLogEntries = entityLogCreatorService.createInitialEntityLog(new AnalyzeRequest(),
|
||||
document,
|
||||
notFoundManualEntities,
|
||||
new DictionaryVersion(),
|
||||
0L).getEntityLogEntry();
|
||||
document,
|
||||
notFoundManualEntities,
|
||||
new DictionaryVersion(),
|
||||
0L).getEntityLogEntry();
|
||||
|
||||
assertEquals(1, redactionLogEntries.size());
|
||||
assertEquals(value, redactionLogEntries.get(0).getValue());
|
||||
@@ -146,7 +152,8 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
|
||||
Document document = buildGraph("files/new/VV-919901.pdf");
|
||||
EntityCreationService entityCreationService = new EntityCreationService(entityEnrichmentService);
|
||||
|
||||
List<TextEntity> tempEntities = entityCreationService.byString("To: Syngenta Ltd.", "temp", EntityType.ENTITY, document).toList();
|
||||
List<TextEntity> tempEntities = entityCreationService.byString("To: Syngenta Ltd.", "temp", EntityType.ENTITY, document)
|
||||
.toList();
|
||||
assertFalse(tempEntities.isEmpty());
|
||||
var tempEntity = tempEntities.get(0);
|
||||
List<Rectangle> positions = tempEntity.getPositionsOnPagePerPage()
|
||||
@@ -158,8 +165,11 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
|
||||
|
||||
ManualRedactionEntry manualRedactionEntry = ManualRedactionEntry.builder()
|
||||
.type("manual")
|
||||
.annotationId("123")
|
||||
.value(tempEntity.getValue())
|
||||
.reason("reason")
|
||||
.user("user")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.legalBasis("n-a")
|
||||
.section(tempEntity.getDeepestFullyContainingNode().toString())
|
||||
.rectangle(false)
|
||||
@@ -172,21 +182,28 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
|
||||
tempEntity.removeFromGraph();
|
||||
assertTrue(document.getEntities().isEmpty());
|
||||
|
||||
List<PrecursorEntity> notFoundManualEntities = entityFromPrecursorCreationService.createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions.builder().entriesToAdd(Set.of(manualRedactionEntry)).build(),
|
||||
document,
|
||||
TEST_DOSSIER_TEMPLATE_ID);
|
||||
List<PrecursorEntity> notFoundManualEntities = entityFromPrecursorCreationService.createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions.builder()
|
||||
.entriesToAdd(Set.of(
|
||||
manualRedactionEntry))
|
||||
.build(),
|
||||
document,
|
||||
TEST_DOSSIER_TEMPLATE_ID);
|
||||
assertTrue(notFoundManualEntities.isEmpty());
|
||||
assertEquals(1, document.getEntities().size());
|
||||
return new DocumentAndEntity(document, document.getEntities().stream().findFirst().get());
|
||||
return new DocumentAndEntity(document,
|
||||
document.getEntities()
|
||||
.stream()
|
||||
.findFirst()
|
||||
.get());
|
||||
}
|
||||
|
||||
|
||||
public static Rectangle toAnnotationRectangle(Rectangle2D rectangle2D, int pageNumber) {
|
||||
|
||||
return new Rectangle(new Point((float) rectangle2D.getMinX(), (float) (rectangle2D.getMinY() + rectangle2D.getHeight())),
|
||||
(float) rectangle2D.getWidth(),
|
||||
-(float) rectangle2D.getHeight(),
|
||||
pageNumber);
|
||||
(float) rectangle2D.getWidth(),
|
||||
-(float) rectangle2D.getHeight(),
|
||||
pageNumber);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+7
@@ -3,6 +3,7 @@ package com.iqser.red.service.redaction.v1.server.rules;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.kie.api.KieServices;
|
||||
import org.kie.api.builder.KieBuilder;
|
||||
import org.kie.api.builder.KieFileSystem;
|
||||
@@ -10,6 +11,8 @@ import org.kie.api.builder.KieModule;
|
||||
import org.kie.api.runtime.KieContainer;
|
||||
import org.kie.api.runtime.KieSession;
|
||||
import org.kie.internal.io.ResourceFactory;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -17,11 +20,13 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.document.graph.BuildDocumentIntegrationTest;
|
||||
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
||||
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;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class RulesIntegrationTest extends BuildDocumentIntegrationTest {
|
||||
|
||||
protected static final String RULES = "drools/rules.drl";
|
||||
@@ -72,10 +77,12 @@ public class RulesIntegrationTest extends BuildDocumentIntegrationTest {
|
||||
@BeforeEach
|
||||
public void createServices() {
|
||||
|
||||
Dictionary dict = Mockito.mock(Dictionary.class);
|
||||
kieSession = kieContainer.newKieSession();
|
||||
entityCreationService = new EntityCreationService(entityEnrichmentService, kieSession);
|
||||
kieSession.setGlobal("manualChangesApplicationService", manualChangesApplicationService);
|
||||
kieSession.setGlobal("entityCreationService", entityCreationService);
|
||||
kieSession.setGlobal("dictionary", dict);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-8
@@ -16,8 +16,8 @@ class SemanticNodeComparatorsTest {
|
||||
@Test
|
||||
public void testFirstSemanticNode() {
|
||||
|
||||
var node = new Section(List.of(0, 1), null, null, null, null,null);
|
||||
var otherNode = new Section(List.of(0, 2), null, null, null, null,null);
|
||||
var node = new Section(List.of(0, 1), null, null, null, null);
|
||||
var otherNode = new Section(List.of(0, 2), null, null, null, null);
|
||||
List<SemanticNode> list = new ArrayList<>();
|
||||
list.add(otherNode);
|
||||
list.add(node);
|
||||
@@ -29,8 +29,8 @@ class SemanticNodeComparatorsTest {
|
||||
@Test
|
||||
public void testFirstSemanticNode2() {
|
||||
|
||||
var node = new Section(Collections.emptyList(), null, null, null, null, null);
|
||||
var otherNode = new Section(List.of(0, 2), null, null, null, null, null);
|
||||
var node = new Section(Collections.emptyList(), null, null, null, null);
|
||||
var otherNode = new Section(List.of(0, 2), null, null, null, null);
|
||||
List<SemanticNode> list = new ArrayList<>();
|
||||
list.add(otherNode);
|
||||
list.add(node);
|
||||
@@ -42,8 +42,8 @@ class SemanticNodeComparatorsTest {
|
||||
@Test
|
||||
public void testFirstSemanticNode3() {
|
||||
|
||||
var node = new Section(List.of(1, 5, 8), null, null, null, null, null);
|
||||
var otherNode = new Section(List.of(0, 2), null, null, null, null, null);
|
||||
var node = new Section(List.of(1, 5, 8), null, null, null, null);
|
||||
var otherNode = new Section(List.of(0, 2), null, null, null, null);
|
||||
List<SemanticNode> list = new ArrayList<>();
|
||||
list.add(otherNode);
|
||||
list.add(node);
|
||||
@@ -55,8 +55,8 @@ class SemanticNodeComparatorsTest {
|
||||
@Test
|
||||
public void testFirstSemanticNode4() {
|
||||
|
||||
var node = new Section(List.of(1, 5, 8), null, null, null, null,null);
|
||||
var otherNode = new Section(List.of(1, 5, 9), null, null, null, null,null);
|
||||
var node = new Section(List.of(1, 5, 8), null, null, null, null);
|
||||
var otherNode = new Section(List.of(1, 5, 9), null, null, null, null);
|
||||
List<SemanticNode> list = new ArrayList<>();
|
||||
list.add(otherNode);
|
||||
list.add(node);
|
||||
|
||||
+181
-71
@@ -8,6 +8,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -34,7 +35,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import com.iqser.red.commons.jackson.ObjectMapperFactory;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
|
||||
@@ -84,6 +84,7 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
@SpyBean
|
||||
RabbitTemplate rabbitTemplate;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void stubClients() {
|
||||
|
||||
@@ -101,21 +102,22 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
|
||||
when(dictionaryClient.getVersion(TEST_DOSSIER_TEMPLATE_ID)).thenReturn(0L);
|
||||
when(dictionaryClient.getAllTypesForDossier(TEST_DOSSIER_ID, true)).thenReturn(List.of(Type.builder()
|
||||
.id(DOSSIER_REDACTIONS_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID)
|
||||
.type(DOSSIER_REDACTIONS_INDICATOR)
|
||||
.dossierTemplateId(TEST_DOSSIER_ID)
|
||||
.hexColor("#ffe187")
|
||||
.isHint(hintTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isCaseInsensitive(caseInSensitiveMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isRecommendation(recommendationTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.rank(rankTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.build()));
|
||||
.id(DOSSIER_REDACTIONS_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID)
|
||||
.type(DOSSIER_REDACTIONS_INDICATOR)
|
||||
.dossierTemplateId(TEST_DOSSIER_ID)
|
||||
.hexColor("#ffe187")
|
||||
.isHint(hintTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isCaseInsensitive(caseInSensitiveMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.isRecommendation(recommendationTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.rank(rankTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
|
||||
.build()));
|
||||
|
||||
mockDictionaryCalls(null);
|
||||
|
||||
when(dictionaryClient.getColors(TEST_DOSSIER_TEMPLATE_ID)).thenReturn(colors);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testManualSurroundingText() {
|
||||
@@ -125,10 +127,20 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
|
||||
var aoelId = UUID.randomUUID().toString();
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId, List.of(Rectangle.builder().topLeftX(355.53775f).topLeftY(266.1895f).width(29.32224f).height(10.048125f).page(1).build()), "AOEL");
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId,
|
||||
List.of(Rectangle.builder()
|
||||
.topLeftX(355.53775f)
|
||||
.topLeftY(266.1895f)
|
||||
.width(29.32224f)
|
||||
.height(10.048125f)
|
||||
.page(1)
|
||||
.build()),
|
||||
"AOEL");
|
||||
|
||||
var notFoundId = UUID.randomUUID().toString();
|
||||
ManualRedactionEntry manualRedactionEntry2 = prepareManualRedactionEntry(notFoundId, List.of(Rectangle.builder().topLeftX(1f).topLeftY(1f).width(1f).height(1f).page(1).build()), "Random");
|
||||
ManualRedactionEntry manualRedactionEntry2 = prepareManualRedactionEntry(notFoundId,
|
||||
List.of(Rectangle.builder().topLeftX(1f).topLeftY(1f).width(1f).height(1f).page(1).build()),
|
||||
"Random");
|
||||
|
||||
manualRedactions.getEntriesToAdd().add(manualRedactionEntry);
|
||||
manualRedactions.getEntriesToAdd().add(manualRedactionEntry2);
|
||||
@@ -147,30 +159,43 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
assertFalse(unprocessedManualEntities.isEmpty());
|
||||
assertEquals(unprocessedManualEntities.size(), 2);
|
||||
|
||||
Optional<UnprocessedManualEntity> optionalUnprocessedManualEntity = unprocessedManualEntities.stream().filter(manualEntity -> manualEntity.getAnnotationId().equals(aoelId)).findFirst();
|
||||
Optional<UnprocessedManualEntity> optionalUnprocessedManualEntity = unprocessedManualEntities.stream()
|
||||
.filter(manualEntity -> manualEntity.getAnnotationId().equals(aoelId))
|
||||
.findFirst();
|
||||
assertTrue(optionalUnprocessedManualEntity.isPresent());
|
||||
|
||||
UnprocessedManualEntity unprocessedManualEntity = optionalUnprocessedManualEntity.get();
|
||||
assertEquals(unprocessedManualEntity.getTextBefore(), "was above the ");
|
||||
assertEquals(unprocessedManualEntity.getTextAfter(), " without PPE (34%");
|
||||
assertEquals(unprocessedManualEntity.getSection(), "[1, 1]: Paragraph: A9396G containing 960 g/L");
|
||||
assertEquals(unprocessedManualEntity.getPositions().get(0).x(), 355.53775f);
|
||||
assertEquals(unprocessedManualEntity.getPositions().get(0).y(), 266.49002f);
|
||||
assertEquals(unprocessedManualEntity.getPositions().get(0).w(), 29.322266f);
|
||||
assertEquals(unprocessedManualEntity.getPositions().get(0).h(), 11.017679f);
|
||||
assertEquals(unprocessedManualEntity.getPositions()
|
||||
.get(0).x(), 355.53775f);
|
||||
assertEquals(unprocessedManualEntity.getPositions()
|
||||
.get(0).y(), 266.49002f);
|
||||
assertEquals(unprocessedManualEntity.getPositions()
|
||||
.get(0).w(), 29.322266f);
|
||||
assertEquals(unprocessedManualEntity.getPositions()
|
||||
.get(0).h(), 11.017679f);
|
||||
|
||||
Optional<UnprocessedManualEntity> optionalNotFoundUnprocessedManualEntity = unprocessedManualEntities.stream().filter(manualEntity -> manualEntity.getAnnotationId().equals(notFoundId)).findFirst();
|
||||
Optional<UnprocessedManualEntity> optionalNotFoundUnprocessedManualEntity = unprocessedManualEntities.stream()
|
||||
.filter(manualEntity -> manualEntity.getAnnotationId().equals(notFoundId))
|
||||
.findFirst();
|
||||
assertTrue(optionalNotFoundUnprocessedManualEntity.isPresent());
|
||||
|
||||
UnprocessedManualEntity unprocessedNotFoundManualEntity = optionalNotFoundUnprocessedManualEntity.get();
|
||||
assertEquals(unprocessedNotFoundManualEntity.getTextBefore(), "");
|
||||
assertEquals(unprocessedNotFoundManualEntity.getTextAfter(), "");
|
||||
assertEquals(unprocessedNotFoundManualEntity.getSection(), "");
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions().get(0).getPageNumber(), 1);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions().get(0).getRectangle()[0], 1f);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions().get(0).getRectangle()[1], 1f);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions().get(0).getRectangle()[2], 1f);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions().get(0).getRectangle()[3], 1f);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions()
|
||||
.get(0).getPageNumber(), 1);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions()
|
||||
.get(0).getRectangle()[0], 1f);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions()
|
||||
.get(0).getRectangle()[1], 1f);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions()
|
||||
.get(0).getRectangle()[2], 1f);
|
||||
assertEquals(unprocessedNotFoundManualEntity.getPositions()
|
||||
.get(0).getRectangle()[3], 1f);
|
||||
|
||||
analyzeService.reanalyze(request);
|
||||
|
||||
@@ -190,10 +215,14 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
assertEquals(unprocessedManualEntities.get(0).getTextAfter(), " without PPE (34%");
|
||||
assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "to EFSA guidance ");
|
||||
assertEquals(unprocessedManualEntities.get(0).getSection(), "[1, 1]: Paragraph: A9396G containing 960 g/L");
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).x(), positions.get(0).getTopLeftX());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).y(), positions.get(0).getTopLeftY());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).w(), positions.get(0).getWidth());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).h(), positions.get(0).getHeight());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).x(), positions.get(0).getTopLeftX());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).y(), positions.get(0).getTopLeftY());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).w(), positions.get(0).getWidth());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).h(), positions.get(0).getHeight());
|
||||
}
|
||||
|
||||
|
||||
@@ -205,13 +234,37 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
|
||||
var aoelId = UUID.randomUUID().toString();
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId, List.of(Rectangle.builder().topLeftX(384.85536f).topLeftY(240.8695f).width(13.49088f).height(10.048125f).page(1).build()), "EL");
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId,
|
||||
List.of(Rectangle.builder()
|
||||
.topLeftX(384.85536f)
|
||||
.topLeftY(240.8695f)
|
||||
.width(13.49088f)
|
||||
.height(10.048125f)
|
||||
.page(1)
|
||||
.build()),
|
||||
"EL");
|
||||
|
||||
var cormsId = UUID.randomUUID().toString();
|
||||
ManualRedactionEntry manualRedactionEntry2 = prepareManualRedactionEntry(cormsId, List.of(Rectangle.builder().topLeftX(129.86f).topLeftY(505.7295f).width(35.9904f).height(10.048125f).page(1).build()), "CoRMS");
|
||||
ManualRedactionEntry manualRedactionEntry2 = prepareManualRedactionEntry(cormsId,
|
||||
List.of(Rectangle.builder()
|
||||
.topLeftX(129.86f)
|
||||
.topLeftY(505.7295f)
|
||||
.width(35.9904f)
|
||||
.height(10.048125f)
|
||||
.page(1)
|
||||
.build()),
|
||||
"CoRMS");
|
||||
|
||||
var a9Id = UUID.randomUUID().toString();
|
||||
ManualRedactionEntry manualRedactionEntry3 = prepareManualRedactionEntry(a9Id, List.of(Rectangle.builder().topLeftX(140.1096f).topLeftY(291.5095f).width(37.84512f).height(10.048125f).page(1).build()), "A9396G");
|
||||
ManualRedactionEntry manualRedactionEntry3 = prepareManualRedactionEntry(a9Id,
|
||||
List.of(Rectangle.builder()
|
||||
.topLeftX(140.1096f)
|
||||
.topLeftY(291.5095f)
|
||||
.width(37.84512f)
|
||||
.height(10.048125f)
|
||||
.page(1)
|
||||
.build()),
|
||||
"A9396G");
|
||||
|
||||
manualRedactions.getEntriesToAdd().add(manualRedactionEntry3);
|
||||
manualRedactions.getEntriesToAdd().add(manualRedactionEntry2);
|
||||
@@ -238,35 +291,53 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
assertFalse(unprocessedManualEntities.isEmpty());
|
||||
assertEquals(unprocessedManualEntities.size(), 3);
|
||||
|
||||
var resizedAoel = unprocessedManualEntities.stream().filter(unprocessedManualEntity -> unprocessedManualEntity.getAnnotationId().equals(aoelId)).findFirst();
|
||||
var resizedAoel = unprocessedManualEntities.stream()
|
||||
.filter(unprocessedManualEntity -> unprocessedManualEntity.getAnnotationId().equals(aoelId))
|
||||
.findFirst();
|
||||
assertTrue(resizedAoel.isPresent());
|
||||
assertEquals(resizedAoel.get().getTextAfter(), " (max. 43% of");
|
||||
assertEquals(resizedAoel.get().getTextBefore(), "is below the ");
|
||||
assertEquals(resizedAoel.get().getSection(), "[1, 1]: Paragraph: A9396G containing 960 g/L");
|
||||
assertEquals(resizedAoel.get().getPositions().get(0).x(), positions.get(0).getTopLeftX());
|
||||
assertEquals(resizedAoel.get().getPositions().get(0).y(), positions.get(0).getTopLeftY());
|
||||
assertEquals(resizedAoel.get().getPositions().get(0).w(), positions.get(0).getWidth());
|
||||
assertEquals(resizedAoel.get().getPositions().get(0).h(), positions.get(0).getHeight());
|
||||
assertEquals(resizedAoel.get().getPositions()
|
||||
.get(0).x(), positions.get(0).getTopLeftX());
|
||||
assertEquals(resizedAoel.get().getPositions()
|
||||
.get(0).y(), positions.get(0).getTopLeftY());
|
||||
assertEquals(resizedAoel.get().getPositions()
|
||||
.get(0).w(), positions.get(0).getWidth());
|
||||
assertEquals(resizedAoel.get().getPositions()
|
||||
.get(0).h(), positions.get(0).getHeight());
|
||||
|
||||
var cormsResized = unprocessedManualEntities.stream().filter(unprocessedManualEntity -> unprocessedManualEntity.getAnnotationId().equals(cormsId)).findFirst();
|
||||
var cormsResized = unprocessedManualEntities.stream()
|
||||
.filter(unprocessedManualEntity -> unprocessedManualEntity.getAnnotationId().equals(cormsId))
|
||||
.findFirst();
|
||||
assertTrue(cormsResized.isPresent());
|
||||
assertEquals(cormsResized.get().getTextAfter(), " a NOAEL of");
|
||||
assertEquals(cormsResized.get().getTextBefore(), "mg/kg bw/d. Furthermore ");
|
||||
assertEquals(cormsResized.get().getSection(), "[0, 3]: Paragraph: The Co-RMS indicated the");
|
||||
assertEquals(cormsResized.get().getPositions().get(0).x(), positions2.get(0).getTopLeftX());
|
||||
assertEquals(cormsResized.get().getPositions().get(0).y(), positions2.get(0).getTopLeftY());
|
||||
assertEquals(cormsResized.get().getPositions().get(0).w(), positions2.get(0).getWidth());
|
||||
assertEquals(cormsResized.get().getPositions().get(0).h(), positions2.get(0).getHeight());
|
||||
assertEquals(cormsResized.get().getPositions()
|
||||
.get(0).x(), positions2.get(0).getTopLeftX());
|
||||
assertEquals(cormsResized.get().getPositions()
|
||||
.get(0).y(), positions2.get(0).getTopLeftY());
|
||||
assertEquals(cormsResized.get().getPositions()
|
||||
.get(0).w(), positions2.get(0).getWidth());
|
||||
assertEquals(cormsResized.get().getPositions()
|
||||
.get(0).h(), positions2.get(0).getHeight());
|
||||
|
||||
var a9Resized = unprocessedManualEntities.stream().filter(unprocessedManualEntity -> unprocessedManualEntity.getAnnotationId().equals(a9Id)).findFirst();
|
||||
var a9Resized = unprocessedManualEntities.stream()
|
||||
.filter(unprocessedManualEntity -> unprocessedManualEntity.getAnnotationId().equals(a9Id))
|
||||
.findFirst();
|
||||
assertTrue(a9Resized.isPresent());
|
||||
assertEquals(a9Resized.get().getTextAfter(), " were obtained from");
|
||||
assertEquals(a9Resized.get().getTextBefore(), "data for S");
|
||||
assertEquals(a9Resized.get().getSection(), "[1, 1]: Paragraph: A9396G containing 960 g/L");
|
||||
assertEquals(a9Resized.get().getPositions().get(0).x(), positions3.get(0).getTopLeftX());
|
||||
assertEquals(a9Resized.get().getPositions().get(0).y(), positions3.get(0).getTopLeftY());
|
||||
assertEquals(a9Resized.get().getPositions().get(0).w(), positions3.get(0).getWidth());
|
||||
assertEquals(a9Resized.get().getPositions().get(0).h(), positions3.get(0).getHeight());
|
||||
assertEquals(a9Resized.get().getPositions()
|
||||
.get(0).x(), positions3.get(0).getTopLeftX());
|
||||
assertEquals(a9Resized.get().getPositions()
|
||||
.get(0).y(), positions3.get(0).getTopLeftY());
|
||||
assertEquals(a9Resized.get().getPositions()
|
||||
.get(0).w(), positions3.get(0).getWidth());
|
||||
assertEquals(a9Resized.get().getPositions()
|
||||
.get(0).h(), positions3.get(0).getHeight());
|
||||
}
|
||||
|
||||
|
||||
@@ -277,7 +348,15 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
var aoelId = UUID.randomUUID().toString();
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId, List.of(Rectangle.builder().topLeftX(384.85536f).topLeftY(240.8695f).width(13.49088f).height(10.048125f).page(1).build()), "EL");
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId,
|
||||
List.of(Rectangle.builder()
|
||||
.topLeftX(384.85536f)
|
||||
.topLeftY(240.8695f)
|
||||
.width(13.49088f)
|
||||
.height(10.048125f)
|
||||
.page(1)
|
||||
.build()),
|
||||
"EL");
|
||||
|
||||
manualRedactions.getEntriesToAdd().add(manualRedactionEntry);
|
||||
AnalyzeRequest request = uploadFileToStorage(pdfFile);
|
||||
@@ -301,10 +380,14 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
assertEquals(unprocessedManualEntities.get(0).getTextAfter(), " (max. 43% of");
|
||||
assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "is below the ");
|
||||
assertEquals(unprocessedManualEntities.get(0).getSection(), "[1, 1]: Paragraph: A9396G containing 960 g/L");
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).x(), positions.get(0).getTopLeftX());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).y(), positions.get(0).getTopLeftY());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).w(), positions.get(0).getWidth());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).h(), positions.get(0).getHeight());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).x(), positions.get(0).getTopLeftX());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).y(), positions.get(0).getTopLeftY());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).w(), positions.get(0).getWidth());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).h(), positions.get(0).getHeight());
|
||||
}
|
||||
|
||||
|
||||
@@ -315,7 +398,15 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
var aoelId = UUID.randomUUID().toString();
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId, List.of(Rectangle.builder().topLeftX(384.85536f).topLeftY(240.8695f).width(13.49088f).height(10.048125f).page(1).build()), "EL");
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId,
|
||||
List.of(Rectangle.builder()
|
||||
.topLeftX(384.85536f)
|
||||
.topLeftY(240.8695f)
|
||||
.width(13.49088f)
|
||||
.height(10.048125f)
|
||||
.page(1)
|
||||
.build()),
|
||||
"EL");
|
||||
|
||||
manualRedactions.getEntriesToAdd().add(manualRedactionEntry);
|
||||
AnalyzeRequest request = uploadFileToStorage(pdfFile);
|
||||
@@ -339,10 +430,14 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
assertEquals(unprocessedManualEntities.get(0).getTextAfter(), ", the same");
|
||||
assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "to set an ");
|
||||
assertEquals(unprocessedManualEntities.get(0).getSection(), "[0, 4]: Paragraph: With respect to the");
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).x(), positions.get(0).getTopLeftX());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).y(), positions.get(0).getTopLeftY());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).w(), positions.get(0).getWidth());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions().get(0).h(), positions.get(0).getHeight());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).x(), positions.get(0).getTopLeftX());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).y(), positions.get(0).getTopLeftY());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).w(), positions.get(0).getWidth());
|
||||
assertEquals(unprocessedManualEntities.get(0).getPositions()
|
||||
.get(0).h(), positions.get(0).getHeight());
|
||||
}
|
||||
|
||||
|
||||
@@ -353,7 +448,15 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
var aoelId = UUID.randomUUID().toString();
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId, List.of(Rectangle.builder().topLeftX(384.85536f).topLeftY(240.8695f).width(13.49088f).height(10.048125f).page(1).build()), "EL");
|
||||
ManualRedactionEntry manualRedactionEntry = prepareManualRedactionEntry(aoelId,
|
||||
List.of(Rectangle.builder()
|
||||
.topLeftX(384.85536f)
|
||||
.topLeftY(240.8695f)
|
||||
.width(13.49088f)
|
||||
.height(10.048125f)
|
||||
.page(1)
|
||||
.build()),
|
||||
"EL");
|
||||
|
||||
manualRedactions.getEntriesToAdd().add(manualRedactionEntry);
|
||||
AnalyzeRequest request = uploadFileToStorage(pdfFile);
|
||||
@@ -377,25 +480,32 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
|
||||
|
||||
private static ManualResizeRedaction prepareManualSizeRedaction(String id, List<Rectangle> positions, String value) {
|
||||
|
||||
ManualResizeRedaction manualResizeRedaction = new ManualResizeRedaction();
|
||||
manualResizeRedaction.setAnnotationId(id);
|
||||
manualResizeRedaction.setPositions(positions);
|
||||
manualResizeRedaction.setUpdateDictionary(false);
|
||||
manualResizeRedaction.setAddToAllDossiers(false);
|
||||
manualResizeRedaction.setValue(value);
|
||||
return manualResizeRedaction;
|
||||
return ManualResizeRedaction.builder()
|
||||
.annotationId(id)
|
||||
.fileId("fileId")
|
||||
.user("user")
|
||||
.positions(positions)
|
||||
.updateDictionary(false)
|
||||
.addToAllDossiers(false)
|
||||
.value(value)
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
private static ManualRedactionEntry prepareManualRedactionEntry(String id, List<Rectangle> positions, String value) {
|
||||
|
||||
ManualRedactionEntry manualRedactionEntry = new ManualRedactionEntry();
|
||||
manualRedactionEntry.setAnnotationId(id);
|
||||
manualRedactionEntry.setFileId("fileId");
|
||||
manualRedactionEntry.setType("CBI_author");
|
||||
manualRedactionEntry.setValue(value);
|
||||
manualRedactionEntry.setReason("Manual Redaction");
|
||||
manualRedactionEntry.setPositions(positions);
|
||||
return manualRedactionEntry;
|
||||
return ManualRedactionEntry.builder()
|
||||
.annotationId(id)
|
||||
.fileId("fileId")
|
||||
.user("user")
|
||||
.type("CBI_author")
|
||||
.value(value)
|
||||
.reason("Manual Redaction")
|
||||
.processedDate(OffsetDateTime.now())
|
||||
.requestDate(OffsetDateTime.now())
|
||||
.positions(positions)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-3
@@ -16,7 +16,6 @@ public class LayoutParsingRequestProvider {
|
||||
var originFileStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.ORIGIN);
|
||||
var tablesFileStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.TABLES);
|
||||
var imagesFileStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.IMAGE_INFO);
|
||||
var sectionGridStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.SECTION_GRID);
|
||||
var structureFileStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.DOCUMENT_STRUCTURE);
|
||||
var textBlockFileStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.DOCUMENT_TEXT);
|
||||
var positionBlockFileStorageId = RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.DOCUMENT_POSITION);
|
||||
@@ -29,14 +28,12 @@ public class LayoutParsingRequestProvider {
|
||||
.originFileStorageId(originFileStorageId)
|
||||
.tablesFileStorageId(Optional.of(tablesFileStorageId))
|
||||
.imagesFileStorageId(Optional.of(imagesFileStorageId))
|
||||
.visualLayoutParsingFileId(Optional.empty())
|
||||
.structureFileStorageId(structureFileStorageId)
|
||||
.textBlockFileStorageId(textBlockFileStorageId)
|
||||
.positionBlockFileStorageId(positionBlockFileStorageId)
|
||||
.pageFileStorageId(pageFileStorageId)
|
||||
.simplifiedTextStorageId(simplifiedTextStorageId)
|
||||
.viewerDocumentStorageId(viewerDocumentStorageId)
|
||||
.visualLayoutParsingFileId(Optional.empty())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ redaction-service:
|
||||
application:
|
||||
type: "RedactManager"
|
||||
|
||||
logging.type: "CONSOLE"
|
||||
|
||||
storage:
|
||||
backend: 's3'
|
||||
|
||||
|
||||
+406
-95
@@ -54,8 +54,6 @@ 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.ManualLegalBasisChange;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
@@ -155,6 +153,32 @@ rule "CBI.7.1: Do not redact Names and Addresses if published information found
|
||||
$authorOrAddress.skipWithReferences("CBI.7.1", "Published Information found in row", $table.getEntitiesOfTypeInSameRow("published_information", $authorOrAddress));
|
||||
end
|
||||
|
||||
rule "CBI.7.2: Do not redact PII if published information found in Section without tables"
|
||||
when
|
||||
$section: Section(!hasTables(),
|
||||
hasEntitiesOfType("published_information"),
|
||||
hasEntitiesOfType("PII"))
|
||||
then
|
||||
$section.getEntitiesOfType("PII")
|
||||
.forEach(redactionEntity -> {
|
||||
redactionEntity.skipWithReferences(
|
||||
"CBI.7.2",
|
||||
"Published Information found in section",
|
||||
$section.getEntitiesOfType("published_information")
|
||||
);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.7.3: Do not redact PII if published information found in same table row"
|
||||
when
|
||||
$table: Table(hasEntitiesOfType("published_information"), hasEntitiesOfType("PII"))
|
||||
$cellsWithPublishedInformation: TableCell() from $table.streamTableCellsWhichContainType("published_information").toList()
|
||||
$tableCell: TableCell(row == $cellsWithPublishedInformation.row) from $table.streamTableCells().toList()
|
||||
$pii: TextEntity(type() == "PII", active()) from $tableCell.getEntities()
|
||||
then
|
||||
$pii.skipWithReferences("CBI.7.3", "Published Information found in row", $table.getEntitiesOfTypeInSameRow("published_information", $pii));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.9
|
||||
rule "CBI.9.0: Redact all cells with Header Author(s) as CBI_author (non vertebrate study)"
|
||||
@@ -183,6 +207,19 @@ rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "CBI.9.2: Redact all cells with Header Author(s) as CBI_author (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$table: Table(hasHeader("Author(s)"))
|
||||
then
|
||||
$table.streamTableCellsWithHeader("Author(s)")
|
||||
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.2", "Author(s) found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.10
|
||||
rule "CBI.10.0: Redact all cells with Header Author(s) as CBI_author (vertebrate study)"
|
||||
@@ -211,6 +248,32 @@ rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate st
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.1", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "CBI.10.2: Redact all cells with Header Author(s) as CBI_author (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$table: Table(hasHeader("Author(s)"))
|
||||
then
|
||||
$table.streamTableCellsWithHeader("Author(s)")
|
||||
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.2", "Author(s) found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
rule "CBI.10.3: Redact all cells with Header Author as CBI_author (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$table: Table(hasHeader("Author"))
|
||||
then
|
||||
$table.streamTableCellsWithHeader("Author")
|
||||
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.3", "Author found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.11
|
||||
rule "CBI.11.0: Recommend all CBI_author entities in Table with Vertebrate Study Y/N Header"
|
||||
@@ -224,7 +287,19 @@ rule "CBI.11.0: Recommend all CBI_author entities in Table with Vertebrate Study
|
||||
|
||||
|
||||
// Rule unit: CBI.16
|
||||
rule "CBI.16.0: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
|
||||
rule "CBI.16.0: Add CBI_author with \"et al.\" RegEx"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$section: Section(containsString("et al."))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.0", "Author found by \"et al\" regex", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.16.1: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -232,12 +307,12 @@ rule "CBI.16.0: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.0", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
entity.redact("CBI.16.1", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.16.1: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
|
||||
rule "CBI.16.2: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -245,7 +320,19 @@ rule "CBI.16.1: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.1", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
entity.redact("CBI.16.2", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.16.3: Add CBI_author with \"et al.\" RegEx"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$section: Section(containsString("et al."))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.3", "Author found by \"et al\" regex", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
@@ -270,7 +357,19 @@ rule "CBI.17.1: Add recommendation for Addresses in Test Organism sections, with
|
||||
|
||||
|
||||
// Rule unit: CBI.20
|
||||
rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (non vertebrate study)"
|
||||
rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\")"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$section: Section(!hasTables(), containsString("PERFORMING LABORATORY:"), containsString("LABORATORY PROJECT ID:"))
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.redact("CBI.20.0", "PERFORMING LABORATORY was found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.20.1: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -278,12 +377,12 @@ rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.skip("CBI.20.0", "PERFORMING LABORATORY was found for non vertebrate study");
|
||||
laboratoryEntity.skip("CBI.20.1", "PERFORMING LABORATORY was found for non vertebrate study");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.20.1: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (vertebrate study)"
|
||||
rule "CBI.20.2: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -291,54 +390,133 @@ rule "CBI.20.1: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.redact("CBI.20.1", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.20.3: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\""
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$section: Section(!hasTables(), containsString("PERFORMING LABORATORY:"), containsString("LABORATORY PROJECT ID:"))
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.redact("CBI.20.3", "PERFORMING LABORATORY was found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.23
|
||||
rule "CBI.23.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ PII rules ------------------------------------
|
||||
|
||||
// Rule unit: PII.0
|
||||
rule "PII.0.0: Redact all PII (non vertebrate study)"
|
||||
rule "PII.0.0: Redact all PII"
|
||||
when
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.0", "Personal Information found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
end
|
||||
|
||||
rule "PII.0.1: Redact all PII (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.0", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
$pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "PII.0.1: Redact all PII (vertebrate study)"
|
||||
rule "PII.0.2: Redact all PII (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
$pii.redact("PII.0.2", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "PII.0.3: Redact all PII"
|
||||
when
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.3", "Personal Information found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.1
|
||||
rule "PII.1.0: Redact Emails by RegEx (Non vertebrate study)"
|
||||
rule "PII.1.0: Redact Emails by RegEx"
|
||||
when
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.0", "Found by Email Regex", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.0", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.1.1: Redact Emails by RegEx (vertebrate study)"
|
||||
rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.1.5: Redact Emails by RegEx"
|
||||
when
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.5", "Found by Email Regex", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.2
|
||||
rule "PII.2.0: Redact Phone and Fax by RegEx (non vertebrate study)"
|
||||
rule "PII.2.0: Redact Phone and Fax by RegEx"
|
||||
when
|
||||
$section: Section(containsString("Contact") ||
|
||||
containsString("Telephone") ||
|
||||
containsString("Phone") ||
|
||||
containsString("Ph.") ||
|
||||
containsString("Fax") ||
|
||||
containsString("Tel") ||
|
||||
containsString("Ter") ||
|
||||
containsString("Mobile") ||
|
||||
containsString("Fel") ||
|
||||
containsString("Fer"))
|
||||
then
|
||||
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.2.0", "Found by Phone and Fax Regex", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.2.1: Redact Phone and Fax by RegEx (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("Contact") ||
|
||||
@@ -352,11 +530,11 @@ rule "PII.2.0: Redact Phone and Fax by RegEx (non vertebrate study)"
|
||||
containsString("Fel") ||
|
||||
containsString("Fer"))
|
||||
then
|
||||
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.2.0", "Found by Phone and Fax Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^m]|mobile|fel|fer)[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.2.1: Redact Phone and Fax by RegEx (vertebrate study)"
|
||||
rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("Contact") ||
|
||||
@@ -370,35 +548,42 @@ rule "PII.2.1: Redact Phone and Fax by RegEx (vertebrate study)"
|
||||
containsString("Fel") ||
|
||||
containsString("Fer"))
|
||||
then
|
||||
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^m]|mobile|fel|fer)[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.2.2", "Found by Phone and Fax Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.3
|
||||
rule "PII.3.0: Redact telephone numbers by RegEx (Non vertebrate study)"
|
||||
rule "PII.3.0: Redact telephone numbers by RegEx"
|
||||
when
|
||||
$section: Section(matchesRegex("[+]\\d{1,}"))
|
||||
then
|
||||
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> entity.redact("PII.3.0", "Telephone number found by regex", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.3.1: Redact telephone numbers by RegEx (Non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(matchesRegex("[+]\\d{1,}"))
|
||||
then
|
||||
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> entity.redact("PII.3.0", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.3.1: Redact telephone numbers by RegEx (vertebrate study)"
|
||||
rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(matchesRegex("[+]\\d{1,}"))
|
||||
then
|
||||
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(entity -> entity.redact("PII.3.2", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.4
|
||||
rule "PII.4.0: Redact line after contact information keywords (non vertebrate study)"
|
||||
rule "PII.4.0: Redact line after contact information keywords"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
"Contact:",
|
||||
"Alternative contact:",
|
||||
@@ -424,7 +609,62 @@ rule "PII.4.0: Redact line after contact information keywords (non vertebrate st
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.4.1: Redact line after contact information keywords (vertebrate study)"
|
||||
rule "PII.4.1: Redact line after contact information keywords"
|
||||
when
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
"Contact:",
|
||||
"Alternative contact:",
|
||||
"European contact:",
|
||||
"No:",
|
||||
"Contact:",
|
||||
"Tel.:",
|
||||
"Tel:",
|
||||
"Telephone number:",
|
||||
"Telephone No:",
|
||||
"Telephone:",
|
||||
"Phone No.",
|
||||
"Phone:",
|
||||
"Fax number:",
|
||||
"Fax:",
|
||||
"E-mail:",
|
||||
"Email:",
|
||||
"e-mail:",
|
||||
"E-mail address:")
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
rule "PII.4.2: Redact line after contact information keywords (Non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
"Contact:",
|
||||
"Alternative contact:",
|
||||
"European contact:",
|
||||
"No:",
|
||||
"Contact:",
|
||||
"Tel.:",
|
||||
"Tel:",
|
||||
"Telephone number:",
|
||||
"Telephone No:",
|
||||
"Telephone:",
|
||||
"Phone No.",
|
||||
"Phone:",
|
||||
"Fax number:",
|
||||
"Fax:",
|
||||
"E-mail:",
|
||||
"Email:",
|
||||
"e-mail:",
|
||||
"E-mail address:")
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.2", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.4.3: Redact line after contact information keywords (Vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
@@ -449,12 +689,24 @@ rule "PII.4.1: Redact line after contact information keywords (vertebrate study)
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.3", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.5
|
||||
rule "PII.5.0: Redact line after contact information keywords reduced (non vertebrate study)"
|
||||
rule "PII.5.0: Redact line after contact information keywords reduced"
|
||||
when
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
"Contact:",
|
||||
"Alternative contact:",
|
||||
"European contact:")
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.5.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.5.1: Redact line after contact information keywords reduced (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
@@ -464,10 +716,10 @@ rule "PII.5.0: Redact line after contact information keywords reduced (non verte
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.5.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.5.1: Redact line after contact information keywords reduced (Vertebrate study)"
|
||||
rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
@@ -477,12 +729,23 @@ rule "PII.5.1: Redact line after contact information keywords reduced (Vertebrat
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.5.2", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.6
|
||||
rule "PII.6.0: Redact line between contact keywords (non vertebrate study)"
|
||||
rule "PII.6.0: Redact line between contact keywords"
|
||||
when
|
||||
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
||||
then
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.0", "Found between contact keywords", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.6.1: Redact line between contact keywords (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
||||
@@ -491,10 +754,10 @@ rule "PII.6.0: Redact line between contact keywords (non vertebrate study)"
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.0", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.6.1: Redact line between contact keywords (vertebrate study)"
|
||||
rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
||||
@@ -503,12 +766,41 @@ rule "PII.6.1: Redact line between contact keywords (vertebrate study)"
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.6.3: Redact line between contact keywords (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
||||
then
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.3", "Found between contact keywords", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.7
|
||||
rule "PII.7.0: Redact contact information if applicant is found (non vertebrate study)"
|
||||
rule "PII.7.0: Redact contact information if applicant is found"
|
||||
when
|
||||
$section: Section(getHeadline().containsString("applicant") ||
|
||||
getHeadline().containsString("Primary contact") ||
|
||||
getHeadline().containsString("Alternative contact") ||
|
||||
containsString("Applicant") ||
|
||||
containsString("Telephone number:"))
|
||||
then
|
||||
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "Contact:", "Tel.:", "Tel:", "Telephone number:",
|
||||
"Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section),
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.7.0", "Applicant information was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.7.1: Redact contact information if applicant is found (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(getHeadline().containsString("applicant") ||
|
||||
@@ -523,10 +815,10 @@ rule "PII.7.0: Redact contact information if applicant is found (non vertebrate
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.7.0", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.7.1: Redact contact information if applicant is found (vertebrate study)"
|
||||
rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(getHeadline().containsString("applicant") ||
|
||||
@@ -541,14 +833,13 @@ rule "PII.7.1: Redact contact information if applicant is found (vertebrate stud
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.8
|
||||
rule "PII.8.0: Redact contact information if producer is found (non vertebrate study)"
|
||||
rule "PII.8.0: Redact contact information if producer is found"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
||||
containsStringIgnoreCase("producer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer of the active substance") ||
|
||||
@@ -564,7 +855,25 @@ rule "PII.8.0: Redact contact information if producer is found (non vertebrate s
|
||||
.forEach(entity -> entity.redact("PII.8.0", "Producer was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.8.1: Redact contact information if producer is found (vertebrate study)"
|
||||
rule "PII.8.1: Redact contact information if producer is found (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
||||
containsStringIgnoreCase("producer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer:") ||
|
||||
containsStringIgnoreCase("Producer or producers of the active substance"))
|
||||
then
|
||||
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "Contact:", "Tel.:", "Tel:", "Telephone number:",
|
||||
"Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section),
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.8.2: Redact contact information if producer is found (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
||||
@@ -579,27 +888,25 @@ rule "PII.8.1: Redact contact information if producer is found (vertebrate study
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(entity -> entity.redact("PII.8.2", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.9
|
||||
rule "PII.9.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (non vertebrate study)"
|
||||
rule "PII.9.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\""
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.9.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.9.0", "AUTHOR(S) was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.9.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
|
||||
rule "PII.9.3: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\""
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.9.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.9.3", "AUTHOR(S) was found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
@@ -656,38 +963,66 @@ rule "ETC.0.0: Purity Hint"
|
||||
|
||||
|
||||
// Rule unit: ETC.2
|
||||
rule "ETC.2.0: Redact signatures (non vertebrate study)"
|
||||
rule "ETC.2.0: Redact signatures"
|
||||
when
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.0", "Signature Found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
end
|
||||
|
||||
rule "ETC.2.1: Redact signatures (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.0", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
$signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "ETC.2.1: Redact signatures (vertebrate study)"
|
||||
rule "ETC.2.2: Redact signatures (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
$signature.redact("ETC.2.2", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "ETC.2.3: Redact signatures"
|
||||
when
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.3", "Signature Found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: ETC.3
|
||||
rule "ETC.3.0: Skip logos (non vertebrate study)"
|
||||
rule "ETC.3.0: Redact logos"
|
||||
when
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.redact("ETC.3.0", "Logo Found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
end
|
||||
|
||||
rule "ETC.3.1: Skip logos (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.skip("ETC.3.0", "Logo Found");
|
||||
$logo.skip("ETC.3.1", "Logo Found");
|
||||
end
|
||||
|
||||
rule "ETC.3.1: Redact logos (vertebrate study)"
|
||||
rule "ETC.3.2: Redact logos (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.redact("ETC.3.1", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
$logo.redact("ETC.3.2", "Logo Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "ETC.3.3: Redact logos"
|
||||
when
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.redact("ETC.3.3", "Logo Found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
end
|
||||
|
||||
|
||||
@@ -702,6 +1037,7 @@ rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidentia
|
||||
end
|
||||
|
||||
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
|
||||
salience 256
|
||||
when
|
||||
not FileAttribute(label == "Confidentiality", value == "confidential")
|
||||
$dossierRedaction: TextEntity(type() == "dossier_redaction")
|
||||
@@ -893,7 +1229,7 @@ rule "MAN.4.1: Apply legal basis change"
|
||||
rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
salience 65
|
||||
when
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, active())
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, !removed())
|
||||
$contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
|
||||
then
|
||||
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
||||
@@ -901,22 +1237,6 @@ rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.1
|
||||
rule "X.1.0: Merge intersecting Entities of same type"
|
||||
salience 64
|
||||
when
|
||||
$first: TextEntity($type: type(), $entityType: entityType, !resized(), active())
|
||||
$second: TextEntity(intersects($first), type() == $type, entityType == $entityType, this != $first, !hasManualChanges(), active())
|
||||
then
|
||||
TextEntity mergedEntity = entityCreationService.mergeEntitiesOfSameType(List.of($first, $second), $type, $entityType, document);
|
||||
$first.remove("X.1.0", "merge intersecting Entities of same type");
|
||||
$second.remove("X.1.0", "merge intersecting Entities of same type");
|
||||
retract($first);
|
||||
retract($second);
|
||||
mergedEntity.getIntersectingNodes().forEach(node -> update(node));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.2
|
||||
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
|
||||
salience 64
|
||||
@@ -1024,24 +1344,15 @@ rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.9
|
||||
rule "X.9.0: Merge mostly contained signatures"
|
||||
// Rule unit: X.11
|
||||
rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
|
||||
salience 64
|
||||
when
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI)
|
||||
$signature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.ALGORITHM, mostlyContains($aiSignature, 0.8))
|
||||
$manualEntity: TextEntity(engines contains Engine.MANUAL, active())
|
||||
$dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
|
||||
then
|
||||
$aiSignature.remove("X.9.0", "removed because already contained by alogrithm signature");
|
||||
$signature.addEngine(LayoutEngine.AI);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.10
|
||||
rule "X.10.0: remove false positives of ai"
|
||||
when
|
||||
$anyImage: Image(engines contains LayoutEngine.ALGORITHM)
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI, !mostlyContainedBy($anyImage, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.10.0", "Removed because false positive");
|
||||
$dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
|
||||
retract($dictionaryEntity);
|
||||
end
|
||||
|
||||
|
||||
|
||||
+524
-142
File diff suppressed because it is too large
Load Diff
+8
-19
@@ -54,8 +54,6 @@ 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.ManualLegalBasisChange;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
@@ -1314,7 +1312,7 @@ rule "MAN.4.1: Apply legal basis change"
|
||||
rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
salience 65
|
||||
when
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, active())
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, !removed())
|
||||
$contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
|
||||
then
|
||||
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
||||
@@ -1416,24 +1414,15 @@ rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.9
|
||||
rule "X.9.0: Merge mostly contained signatures"
|
||||
// Rule unit: X.11
|
||||
rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
|
||||
salience 64
|
||||
when
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI)
|
||||
$signature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.ALGORITHM, mostlyContains($aiSignature, 0.8))
|
||||
$manualEntity: TextEntity(engines contains Engine.MANUAL, active())
|
||||
$dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
|
||||
then
|
||||
$aiSignature.remove("X.9.0", "removed because already contained by alogrithm signature");
|
||||
$signature.addEngine(LayoutEngine.AI);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.10
|
||||
rule "X.10.0: remove false positives of ai"
|
||||
when
|
||||
$anyImage: Image(engines contains LayoutEngine.ALGORITHM)
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI, !mostlyContainedBy($anyImage, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.10.0", "Removed because false positive");
|
||||
$dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
|
||||
retract($dictionaryEntity);
|
||||
end
|
||||
|
||||
|
||||
|
||||
+953
@@ -0,0 +1,953 @@
|
||||
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.document.*;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Paragraph;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SectionIdentifier;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Footer;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Header;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.*;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.AtomicTextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.model.document.textblock.ConcatenatedTextBlock;
|
||||
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.DictionaryModel;
|
||||
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
|
||||
import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService;
|
||||
import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;
|
||||
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.annotations.entitymapped.ManualResizeRedaction;
|
||||
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;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
global Dictionary dictionary
|
||||
|
||||
//------------------------------------ queries ------------------------------------
|
||||
|
||||
query "getFileAttributes"
|
||||
$fileAttribute: FileAttribute()
|
||||
end
|
||||
|
||||
//------------------------------------ Syngenta specific rules ------------------------------------
|
||||
|
||||
// Rule unit: SYN.1
|
||||
rule "SYN.1.0: Recommend CTL/BL laboratory that start with BL or CTL"
|
||||
when
|
||||
$section: Section(containsString("CT") || containsString("BL"))
|
||||
then
|
||||
/* Regular expression: ((\b((([Cc]T(([1ILli\/])| L|~P))|(BL))[\. ]?([\dA-Ziltphz~\/.:!]| ?[\(',][Ppi](\(e)?|([\(-?']\/))+( ?[\(\/\dA-Znasieg]+)?)\b( ?\/? ?\d+)?)|(\bCT[L1i]\b)) */
|
||||
entityCreationService.byRegexIgnoreCase("((\\b((([Cc]T(([1ILli\\/])| L|~P))|(BL))[\\. ]?([\\dA-Ziltphz~\\/.:!]| ?[\\(',][Ppi](\\(e)?|([\\(-?']\\/))+( ?[\\(\\/\\dA-Znasieg]+)?)\\b( ?\\/? ?\\d+)?)|(\\bCT[L1i]\\b))", "CBI_address", EntityType.RECOMMENDATION, $section)
|
||||
.forEach(entity -> entity.skip("SYN.1.0", ""));
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ CBI rules ------------------------------------
|
||||
|
||||
// Rule unit: CBI.0
|
||||
rule "CBI.0.0: Redact CBI Authors (non vertebrate Study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$entity: TextEntity(type() == "CBI_author", dictionaryEntry)
|
||||
then
|
||||
$entity.redact("CBI.0.0", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "CBI.0.1: Redact CBI Authors (vertebrate Study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$entity: TextEntity(type() == "CBI_author", dictionaryEntry)
|
||||
then
|
||||
$entity.redact("CBI.0.1", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.1
|
||||
rule "CBI.1.0: Do not redact CBI Address (non vertebrate Study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$entity: TextEntity(type() == "CBI_address", dictionaryEntry)
|
||||
then
|
||||
$entity.skip("CBI.1.0", "Address found for Non Vertebrate Study");
|
||||
end
|
||||
|
||||
rule "CBI.1.1: Redact CBI Address (vertebrate Study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$entity: TextEntity(type() == "CBI_address", dictionaryEntry)
|
||||
then
|
||||
$entity.redact("CBI.1.1", "Address found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.2
|
||||
rule "CBI.2.0: Do not redact genitive CBI Author"
|
||||
when
|
||||
$entity: TextEntity(type() == "CBI_author", anyMatch(textAfter, "['’’'ʼˈ´`‘′ʻ’']s"))
|
||||
then
|
||||
entityCreationService.byTextRange($entity.getTextRange(), "CBI_author", EntityType.FALSE_POSITIVE, document)
|
||||
.ifPresent(falsePositive -> falsePositive.skip("CBI.2.0", "Genitive Author found"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.7
|
||||
rule "CBI.7.0: Do not redact Names and Addresses if published information found in Section without tables"
|
||||
when
|
||||
$section: Section(!hasTables(),
|
||||
hasEntitiesOfType("published_information"),
|
||||
(hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
||||
then
|
||||
$section.getEntitiesOfType(List.of("CBI_author", "CBI_address"))
|
||||
.forEach(redactionEntity -> {
|
||||
redactionEntity.skipWithReferences(
|
||||
"CBI.7.0",
|
||||
"Published Information found in section",
|
||||
$section.getEntitiesOfType("published_information")
|
||||
);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.7.1: Do not redact Names and Addresses if published information found in same table row"
|
||||
when
|
||||
$table: Table(hasEntitiesOfType("published_information"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))
|
||||
$cellsWithPublishedInformation: TableCell() from $table.streamTableCellsWhichContainType("published_information").toList()
|
||||
$tableCell: TableCell(row == $cellsWithPublishedInformation.row) from $table.streamTableCells().toList()
|
||||
$authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities()
|
||||
then
|
||||
$authorOrAddress.skipWithReferences("CBI.7.1", "Published Information found in row", $table.getEntitiesOfTypeInSameRow("published_information", $authorOrAddress));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.9
|
||||
rule "CBI.9.0: Redact all cells with Header Author(s) as CBI_author (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$table: Table(hasHeader("Author(s)"))
|
||||
then
|
||||
$table.streamTableCellsWithHeader("Author(s)")
|
||||
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$table: Table(hasHeader("Author"))
|
||||
then
|
||||
$table.streamTableCellsWithHeader("Author")
|
||||
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.10
|
||||
rule "CBI.10.0: Redact all cells with Header Author(s) as CBI_author (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$table: Table(hasHeader("Author(s)"))
|
||||
then
|
||||
$table.streamTableCellsWithHeader("Author(s)")
|
||||
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.0", "Author(s) found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$table: Table(hasHeader("Author"))
|
||||
then
|
||||
$table.streamTableCellsWithHeader("Author")
|
||||
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.1", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.11
|
||||
rule "CBI.11.0: Recommend all CBI_author entities in Table with Vertebrate Study Y/N Header"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
salience -1
|
||||
when
|
||||
$table: Table(hasHeader("Author(s)") && hasHeader("Vertebrate Study Y/N"))
|
||||
then
|
||||
$table.getEntitiesOfType("CBI_author").stream().filter(IEntity::applied).forEach(entity -> dictionary.addMultipleAuthorsAsRecommendation(entity));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.16
|
||||
rule "CBI.16.1: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("et al."))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.1", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.16.2: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("et al."))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.2", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.17
|
||||
rule "CBI.17.0: Add recommendation for Addresses in Test Organism sections, without colon"
|
||||
when
|
||||
$section: Section(!hasTables(), containsString("Species") && containsString("Source") && !containsString("Species:") && !containsString("Source:"))
|
||||
then
|
||||
entityCreationService.lineAfterString("Source", "CBI_address", EntityType.RECOMMENDATION, $section)
|
||||
.forEach(entity -> entity.skip("CBI.17.0", "Line after \"Source\" in Test Organism Section"));
|
||||
end
|
||||
|
||||
rule "CBI.17.1: Add recommendation for Addresses in Test Organism sections, with colon"
|
||||
when
|
||||
$section: Section(!hasTables(), containsString("Species:"), containsString("Source:"))
|
||||
then
|
||||
entityCreationService.lineAfterString("Source:", "CBI_address", EntityType.RECOMMENDATION, $section)
|
||||
.forEach(entity -> entity.skip("CBI.17.1", "Line after \"Source:\" in Test Animals Section"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.20
|
||||
rule "CBI.20.1: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(!hasTables(), containsString("PERFORMING LABORATORY:"), containsString("LABORATORY PROJECT ID:"))
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.skip("CBI.20.1", "PERFORMING LABORATORY was found for non vertebrate study");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.20.2: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(!hasTables(), containsString("PERFORMING LABORATORY:"), containsString("LABORATORY PROJECT ID:"))
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.23
|
||||
rule "CBI.23.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ PII rules ------------------------------------
|
||||
|
||||
// Rule unit: PII.0
|
||||
rule "PII.0.1: Redact all PII (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "PII.0.2: Redact all PII (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.2", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.1
|
||||
rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.1.3: Redact typoed Emails with indicator"
|
||||
when
|
||||
$section: Section(containsString("@") || containsStringIgnoreCase("mail"))
|
||||
then
|
||||
entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.3", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.2
|
||||
rule "PII.2.1: Redact Phone and Fax by RegEx (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("Contact") ||
|
||||
containsString("Telephone") ||
|
||||
containsString("Phone") ||
|
||||
containsString("Ph.") ||
|
||||
containsString("Fax") ||
|
||||
containsString("Tel") ||
|
||||
containsString("Ter") ||
|
||||
containsString("Mobile") ||
|
||||
containsString("Fel") ||
|
||||
containsString("Fer"))
|
||||
then
|
||||
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^m]|mobile|fel|fer)[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("Contact") ||
|
||||
containsString("Telephone") ||
|
||||
containsString("Phone") ||
|
||||
containsString("Ph.") ||
|
||||
containsString("Fax") ||
|
||||
containsString("Tel") ||
|
||||
containsString("Ter") ||
|
||||
containsString("Mobile") ||
|
||||
containsString("Fel") ||
|
||||
containsString("Fer"))
|
||||
then
|
||||
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^m]|mobile|fel|fer)[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.2.2", "Found by Phone and Fax Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.2.3: Redact phone numbers without indicators"
|
||||
when
|
||||
$section: Section(containsString("+"))
|
||||
then
|
||||
entityCreationService.byRegex("(\\+[\\dO]{1,2} )(\\([\\dO]{1,3}\\))?[\\d\\-O ]{8,15}", "PII", EntityType.ENTITY, $section)
|
||||
.forEach(entity -> entity.redact("PII.2.3", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.3
|
||||
rule "PII.3.1: Redact telephone numbers by RegEx (Non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(matchesRegex("[+]\\d{1,}"))
|
||||
then
|
||||
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(matchesRegex("[+]\\d{1,}"))
|
||||
then
|
||||
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> entity.redact("PII.3.2", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.7
|
||||
rule "PII.7.1: Redact contact information if applicant is found (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(getHeadline().containsString("applicant") ||
|
||||
getHeadline().containsString("Primary contact") ||
|
||||
getHeadline().containsString("Alternative contact") ||
|
||||
containsString("Applicant") ||
|
||||
containsString("Telephone number:"))
|
||||
then
|
||||
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "Contact:", "Tel.:", "Tel:", "Telephone number:",
|
||||
"Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section),
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(getHeadline().containsString("applicant") ||
|
||||
getHeadline().containsString("Primary contact") ||
|
||||
getHeadline().containsString("Alternative contact") ||
|
||||
containsString("Applicant") ||
|
||||
containsString("Telephone number:"))
|
||||
then
|
||||
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "Contact:", "Tel.:", "Tel:", "Telephone number:",
|
||||
"Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section),
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.8
|
||||
rule "PII.8.1: Redact contact information if producer is found (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
||||
containsStringIgnoreCase("producer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer:") ||
|
||||
containsStringIgnoreCase("Producer or producers of the active substance"))
|
||||
then
|
||||
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "Contact:", "Tel.:", "Tel:", "Telephone number:",
|
||||
"Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section),
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.8.2: Redact contact information if producer is found (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
||||
containsStringIgnoreCase("producer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer:") ||
|
||||
containsStringIgnoreCase("Producer or producers of the active substance"))
|
||||
then
|
||||
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "Contact:", "Tel.:", "Tel:", "Telephone number:",
|
||||
"Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section),
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.8.2", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.10
|
||||
rule "PII.10.0: Redact study director abbreviation"
|
||||
when
|
||||
$section: Section(containsString("KATH") || containsString("BECH") || containsString("KML"))
|
||||
then
|
||||
entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> entity.redact("PII.10.0", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.11
|
||||
rule "PII.11.0: Redact On behalf of Sequani Ltd.:"
|
||||
when
|
||||
$section: Section(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title"))
|
||||
then
|
||||
entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section)
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.12
|
||||
rule "PII.12.0: Expand PII entities with salutation prefix"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
|
||||
then
|
||||
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
|
||||
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.12.1: Expand PII entities with salutation prefix"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
|
||||
then
|
||||
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
|
||||
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ Other rules ------------------------------------
|
||||
|
||||
// Rule unit: ETC.0
|
||||
rule "ETC.0.0: Purity Hint"
|
||||
when
|
||||
$section: Section(containsStringIgnoreCase("purity"))
|
||||
then
|
||||
entityCreationService.byRegexIgnoreCase("(purity ?( of|\\(.{1,20}\\))?( ?:)?) .{0,5}[\\d\\.]+( .{0,4}\\.)? ?%", "hint_only", EntityType.HINT, 1, $section)
|
||||
.forEach(hint -> hint.skip("ETC.0.0", "hint only"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: ETC.2
|
||||
rule "ETC.2.1: Redact signatures (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "ETC.2.2: Redact signatures (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.2", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: ETC.3
|
||||
rule "ETC.3.1: Skip logos (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.skip("ETC.3.1", "Logo Found");
|
||||
end
|
||||
|
||||
rule "ETC.3.2: Redact logos (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.redact("ETC.3.2", "Logo Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: ETC.5
|
||||
rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidential'"
|
||||
when
|
||||
FileAttribute(label == "Confidentiality", value == "confidential")
|
||||
$dossierRedaction: TextEntity(type() == "dossier_redaction")
|
||||
then
|
||||
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
|
||||
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
|
||||
end
|
||||
|
||||
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
|
||||
salience 256
|
||||
when
|
||||
not FileAttribute(label == "Confidentiality", value == "confidential")
|
||||
$dossierRedaction: TextEntity(type() == "dossier_redaction")
|
||||
then
|
||||
$dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
|
||||
retract($dossierRedaction);
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ AI rules ------------------------------------
|
||||
|
||||
// Rule unit: AI.0
|
||||
rule "AI.0.0: Add all NER Entities of type CBI_author"
|
||||
salience 999
|
||||
when
|
||||
nerEntities: NerEntities(hasEntitiesOfType("CBI_author"))
|
||||
then
|
||||
nerEntities.streamEntitiesOfType("CBI_author")
|
||||
.forEach(nerEntity -> entityCreationService.optionalByNerEntity(nerEntity, EntityType.RECOMMENDATION, document));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: AI.1
|
||||
rule "AI.1.0: Combine and add NER Entities as CBI_address"
|
||||
salience 999
|
||||
when
|
||||
nerEntities: NerEntities(hasEntitiesOfType("ORG") || hasEntitiesOfType("STREET") || hasEntitiesOfType("CITY"))
|
||||
then
|
||||
entityCreationService.combineNerEntitiesToCbiAddressDefaults(nerEntities, "CBI_address", EntityType.RECOMMENDATION, document).toList();
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ Manual changes rules ------------------------------------
|
||||
|
||||
// Rule unit: MAN.0
|
||||
rule "MAN.0.0: Apply manual resize redaction"
|
||||
salience 128
|
||||
when
|
||||
$resizeRedaction: ManualResizeRedaction($id: annotationId, $requestDate: requestDate)
|
||||
not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate))
|
||||
$entityToBeResized: TextEntity(matchesAnnotationId($id))
|
||||
then
|
||||
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
|
||||
retract($resizeRedaction);
|
||||
update($entityToBeResized);
|
||||
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
|
||||
end
|
||||
|
||||
rule "MAN.0.1: Apply manual resize redaction"
|
||||
salience 128
|
||||
when
|
||||
$resizeRedaction: ManualResizeRedaction($id: annotationId, $requestDate: requestDate)
|
||||
not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate))
|
||||
$imageToBeResized: Image(id == $id)
|
||||
then
|
||||
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
|
||||
retract($resizeRedaction);
|
||||
update($imageToBeResized);
|
||||
update($imageToBeResized.getParent());
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: MAN.1
|
||||
rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to Entity"
|
||||
salience 128
|
||||
when
|
||||
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
|
||||
$entityToBeRemoved: TextEntity(matchesAnnotationId($id))
|
||||
then
|
||||
$entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
|
||||
update($entityToBeRemoved);
|
||||
retract($idRemoval);
|
||||
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
|
||||
end
|
||||
|
||||
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
|
||||
salience 128
|
||||
when
|
||||
$idRemoval: IdRemoval($id: annotationId)
|
||||
$imageEntityToBeRemoved: Image($id == id)
|
||||
then
|
||||
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
|
||||
update($imageEntityToBeRemoved);
|
||||
retract($idRemoval);
|
||||
update($imageEntityToBeRemoved.getParent());
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: MAN.2
|
||||
rule "MAN.2.0: Apply force redaction"
|
||||
salience 128
|
||||
when
|
||||
$force: ManualForceRedaction($id: annotationId)
|
||||
$entityToForce: TextEntity(matchesAnnotationId($id))
|
||||
then
|
||||
$entityToForce.getManualOverwrite().addChange($force);
|
||||
update($entityToForce);
|
||||
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
|
||||
retract($force);
|
||||
end
|
||||
|
||||
rule "MAN.2.1: Apply force redaction to images"
|
||||
salience 128
|
||||
when
|
||||
$force: ManualForceRedaction($id: annotationId)
|
||||
$imageToForce: Image(id == $id)
|
||||
then
|
||||
$imageToForce.getManualOverwrite().addChange($force);
|
||||
update($imageToForce);
|
||||
update($imageToForce.getParent());
|
||||
retract($force);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: MAN.3
|
||||
rule "MAN.3.0: Apply entity recategorization"
|
||||
salience 128
|
||||
when
|
||||
$recategorization: ManualRecategorization($id: annotationId, $type: type, $requestDate: requestDate)
|
||||
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
|
||||
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
|
||||
then
|
||||
$entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
|
||||
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
|
||||
update($entityToBeRecategorized);
|
||||
retract($recategorization);
|
||||
end
|
||||
|
||||
rule "MAN.3.1: Apply entity recategorization of same type"
|
||||
salience 128
|
||||
when
|
||||
$recategorization: ManualRecategorization($id: annotationId, $type: type, $requestDate: requestDate)
|
||||
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
|
||||
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
|
||||
then
|
||||
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
|
||||
retract($recategorization);
|
||||
end
|
||||
|
||||
rule "MAN.3.2: Apply image recategorization"
|
||||
salience 128
|
||||
when
|
||||
$recategorization: ManualRecategorization($id: annotationId, $requestDate: requestDate)
|
||||
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
|
||||
$imageToBeRecategorized: Image($id == id)
|
||||
then
|
||||
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
|
||||
update($imageToBeRecategorized);
|
||||
update($imageToBeRecategorized.getParent());
|
||||
retract($recategorization);
|
||||
end
|
||||
|
||||
rule "MAN.3.3: Apply recategorization entities by default"
|
||||
salience 128
|
||||
when
|
||||
$entity: TextEntity(getManualOverwrite().getRecategorized().orElse(false), !dictionary.isHint(type()))
|
||||
then
|
||||
$entity.apply("MAN.3.3", "Recategorized entities are applied by default.", $entity.legalBasis());
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: MAN.4
|
||||
rule "MAN.4.0: Apply legal basis change"
|
||||
salience 128
|
||||
when
|
||||
$legalBasisChange: ManualLegalBasisChange($id: annotationId)
|
||||
$imageToBeRecategorized: Image($id == id)
|
||||
then
|
||||
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
|
||||
update($imageToBeRecategorized)
|
||||
retract($legalBasisChange)
|
||||
end
|
||||
|
||||
rule "MAN.4.1: Apply legal basis change"
|
||||
salience 128
|
||||
when
|
||||
$legalBasisChange: ManualLegalBasisChange($id: annotationId)
|
||||
$entityToBeChanged: TextEntity(matchesAnnotationId($id))
|
||||
then
|
||||
$entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
|
||||
update($entityToBeChanged)
|
||||
retract($legalBasisChange)
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ Entity merging rules ------------------------------------
|
||||
|
||||
// Rule unit: X.0
|
||||
rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
salience 65
|
||||
when
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, !removed())
|
||||
$contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
|
||||
then
|
||||
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
||||
retract($contained);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.2
|
||||
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
|
||||
salience 64
|
||||
when
|
||||
$falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
|
||||
$entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
|
||||
then
|
||||
$entity.getIntersectingNodes().forEach(node -> update(node));
|
||||
$entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
|
||||
retract($entity)
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.3
|
||||
rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
|
||||
salience 64
|
||||
when
|
||||
$falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
|
||||
$recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
|
||||
then
|
||||
$recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
|
||||
retract($recommendation);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.4
|
||||
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
|
||||
salience 256
|
||||
when
|
||||
$entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
|
||||
$recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
|
||||
then
|
||||
$entity.addEngines($recommendation.getEngines());
|
||||
$recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
|
||||
retract($recommendation);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.5
|
||||
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
|
||||
salience 256
|
||||
when
|
||||
$entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
|
||||
$recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
|
||||
then
|
||||
$recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
|
||||
retract($recommendation);
|
||||
end
|
||||
|
||||
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
|
||||
salience 256
|
||||
when
|
||||
$entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
|
||||
$recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
|
||||
then
|
||||
$recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
|
||||
retract($recommendation);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.6
|
||||
rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
|
||||
salience 32
|
||||
when
|
||||
$higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
|
||||
$lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
|
||||
then
|
||||
$lowerRank.getIntersectingNodes().forEach(node -> update(node));
|
||||
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
|
||||
retract($lowerRank);
|
||||
end
|
||||
|
||||
rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
|
||||
salience 32
|
||||
when
|
||||
$outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
|
||||
$inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
|
||||
then
|
||||
$inner.getIntersectingNodes().forEach(node -> update(node));
|
||||
$inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
|
||||
retract($inner);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.8
|
||||
rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
|
||||
salience 257
|
||||
when
|
||||
$entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
|
||||
$other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
|
||||
then
|
||||
$other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
|
||||
$entity.addEngines($other.getEngines());
|
||||
retract($other);
|
||||
end
|
||||
|
||||
rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
salience 256
|
||||
when
|
||||
$entity: TextEntity(engines contains Engine.IMPORTED, active())
|
||||
$other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
|
||||
then
|
||||
$other.remove("X.8.1", "remove Entity when intersected by imported Entity");
|
||||
retract($other);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.11
|
||||
rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
|
||||
salience 64
|
||||
when
|
||||
$manualEntity: TextEntity(engines contains Engine.MANUAL, active())
|
||||
$dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
|
||||
then
|
||||
$dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
|
||||
retract($dictionaryEntity);
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ File attributes rules ------------------------------------
|
||||
|
||||
// Rule unit: FA.1
|
||||
rule "FA.1.0: Remove duplicate FileAttributes"
|
||||
salience 64
|
||||
when
|
||||
$fileAttribute: FileAttribute($label: label, $value: value)
|
||||
$duplicate: FileAttribute(this != $fileAttribute, label == $label, value == $value)
|
||||
then
|
||||
retract($duplicate);
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ Local dictionary search rules ------------------------------------
|
||||
|
||||
// Rule unit: LDS.0
|
||||
rule "LDS.0.0: Run local dictionary search"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
salience -999
|
||||
when
|
||||
$dictionaryModel: DictionaryModel(!localEntriesWithMatchedRules.isEmpty()) from dictionary.getDictionaryModels()
|
||||
then
|
||||
entityCreationService.bySearchImplementation($dictionaryModel.getLocalSearch(), $dictionaryModel.getType(), EntityType.RECOMMENDATION, document)
|
||||
.forEach(entity -> {
|
||||
Collection<MatchedRule> matchedRules = $dictionaryModel.getMatchedRulesForLocalDictionaryEntry(entity.getValue());
|
||||
matchedRules.forEach(matchedRule -> entity.addMatchedRule(matchedRule.asSkippedIfApplied()));
|
||||
});
|
||||
end
|
||||
+7
-18
@@ -54,8 +54,6 @@ 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.ManualLegalBasisChange;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
@@ -257,24 +255,15 @@ rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.9
|
||||
rule "X.9.0: Merge mostly contained signatures"
|
||||
// Rule unit: X.11
|
||||
rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
|
||||
salience 64
|
||||
when
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI)
|
||||
$signature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.ALGORITHM, mostlyContains($aiSignature, 0.8))
|
||||
$manualEntity: TextEntity(engines contains Engine.MANUAL, active())
|
||||
$dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
|
||||
then
|
||||
$aiSignature.remove("X.9.0", "removed because already contained by alogrithm signature");
|
||||
$signature.addEngine(LayoutEngine.AI);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.10
|
||||
rule "X.10.0: remove false positives of ai"
|
||||
when
|
||||
$anyImage: Image(engines contains LayoutEngine.ALGORITHM)
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI, !mostlyContainedBy($anyImage, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.10.0", "Removed because false positive");
|
||||
$dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
|
||||
retract($dictionaryEntity);
|
||||
end
|
||||
|
||||
|
||||
|
||||
+372
-126
@@ -54,8 +54,6 @@ 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.ManualLegalBasisChange;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
@@ -102,17 +100,18 @@ rule "CBI.3.0: Redacted because Section contains a vertebrate"
|
||||
|
||||
rule "CBI.3.1: Redacted because table row contains a vertebrate"
|
||||
when
|
||||
$table: Table(hasEntitiesOfType("vertebrate"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))
|
||||
$cellsWithvertebrate: TableCell() from $table.streamTableCellsWhichContainType("vertebrate").toList()
|
||||
$tableCell: TableCell(row == $cellsWithvertebrate.row) from $table.streamTableCells().toList()
|
||||
$authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities()
|
||||
$table: Table(hasEntitiesOfType("vertebrate"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
||||
then
|
||||
$authorOrAddress.applyWithReferences(
|
||||
$table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate"))
|
||||
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
||||
.forEach(entity -> {
|
||||
entity.applyWithReferences(
|
||||
"CBI.3.1",
|
||||
"Vertebrate found",
|
||||
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
||||
$table.getEntitiesOfTypeInSameRow("vertebrate", $authorOrAddress)
|
||||
$table.getEntitiesOfTypeInSameRow("vertebrate", entity)
|
||||
);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.3.2: Do not redact because Section does not contain a vertebrate"
|
||||
@@ -151,21 +150,23 @@ rule "CBI.4.0: Do not redact Names and Addresses if no_redaction_indicator is fo
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.4.1: Redacted because table row contains a vertebrate"
|
||||
rule "CBI.4.1: Don't redact authors or addresses which appear in the same row as a vertebrate and a no_redaction_indicator"
|
||||
when
|
||||
$table: Table(hasEntitiesOfType("no_redaction_indicator"), hasEntitiesOfType("vertebrate"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))
|
||||
TableCell($row: row) from $table.streamTableCellsWhichContainType("vertebrate").toList()
|
||||
TableCell(row == $row) from $table.streamTableCellsWhichContainType("no_redaction_indicator").toList()
|
||||
$tableCell: TableCell(row == $row) from $table.streamTableCells().toList()
|
||||
$authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities()
|
||||
$table: Table(hasEntitiesOfType("no_redaction_indicator"),
|
||||
hasEntitiesOfType("vertebrate"),
|
||||
(hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
||||
then
|
||||
$authorOrAddress.skipWithReferences(
|
||||
$table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate", "no-redaction_indicator"))
|
||||
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
||||
.forEach(entity -> {
|
||||
entity.skipWithReferences(
|
||||
"CBI.4.1",
|
||||
"Vertebrate but a no redaction indicator found",
|
||||
Stream.concat(
|
||||
$table.getEntitiesOfTypeInSameRow("vertebrate", $authorOrAddress).stream(),
|
||||
$table.getEntitiesOfTypeInSameRow("no_redaction_indicator", $authorOrAddress).stream()).toList()
|
||||
$table.getEntitiesOfTypeInSameRow("vertebrate", entity).stream(),
|
||||
$table.getEntitiesOfTypeInSameRow("no_redaction_indicator", entity).stream()).toList()
|
||||
);
|
||||
});
|
||||
end
|
||||
|
||||
|
||||
@@ -184,7 +185,7 @@ rule "CBI.5.0: Redact Names and Addresses if no_redaction_indicator but also red
|
||||
"no_redaction_indicator but also redaction_indicator found",
|
||||
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
||||
Stream.concat(
|
||||
$section.getEntitiesOfType("vertebrate").stream(),
|
||||
$section.getEntitiesOfType("redaction_indicator").stream(),
|
||||
$section.getEntitiesOfType("no_redaction_indicator").stream()).toList()
|
||||
);
|
||||
});
|
||||
@@ -192,20 +193,22 @@ rule "CBI.5.0: Redact Names and Addresses if no_redaction_indicator but also red
|
||||
|
||||
rule "CBI.5.1: Redact Names and Addresses if no_redaction_indicator but also redaction_indicator is found in table row"
|
||||
when
|
||||
$table: Table(hasEntitiesOfType("no_redaction_indicator"), hasEntitiesOfType("redaction_indicator"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))
|
||||
TableCell($row: row) from $table.streamTableCellsWhichContainType("redaction_indicator").toList()
|
||||
TableCell(row == $row) from $table.streamTableCellsWhichContainType("no_redaction_indicator").toList()
|
||||
$tableCell: TableCell(row == $row) from $table.streamTableCells().toList()
|
||||
$entity: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities()
|
||||
$table: Table(hasEntitiesOfType("no_redaction_indicator"),
|
||||
hasEntitiesOfType("redaction_indicator"),
|
||||
(hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
||||
then
|
||||
$entity.applyWithReferences(
|
||||
$table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("redaction_indicator", "no_redaction_indicator"))
|
||||
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
||||
.forEach(entity -> {
|
||||
entity.applyWithReferences(
|
||||
"CBI.5.1",
|
||||
"no_redaction_indicator but also redaction_indicator found",
|
||||
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
||||
Stream.concat(
|
||||
$table.getEntitiesOfTypeInSameRow("redaction_indicator", $entity).stream(),
|
||||
$table.getEntitiesOfTypeInSameRow("no_redaction_indicator", $entity).stream()).toList()
|
||||
$table.getEntitiesOfTypeInSameRow("redaction_indicator", entity).stream(),
|
||||
$table.getEntitiesOfTypeInSameRow("no_redaction_indicator", entity).stream()).toList()
|
||||
);
|
||||
});
|
||||
end
|
||||
|
||||
|
||||
@@ -227,17 +230,18 @@ rule "CBI.8.0: Redacted because Section contains must_redact entity"
|
||||
|
||||
rule "CBI.8.1: Redacted because table row contains must_redact entity"
|
||||
when
|
||||
$table: Table(hasEntitiesOfType("must_redact"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))
|
||||
$cellsWithMustRedact: TableCell() from $table.streamTableCellsWhichContainType("must_redact").toList()
|
||||
$tableCell: TableCell(row == $cellsWithMustRedact.row) from $table.streamTableCells().toList()
|
||||
$authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities()
|
||||
$table: Table(hasEntitiesOfType("must_redact"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
||||
then
|
||||
$authorOrAddress.applyWithReferences(
|
||||
"CBI.8.1",
|
||||
"Must_redact found",
|
||||
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
||||
$table.getEntitiesOfTypeInSameRow("must_redact", $authorOrAddress)
|
||||
);
|
||||
$table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("must_redact"))
|
||||
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
||||
.forEach(entity -> {
|
||||
entity.applyWithReferences(
|
||||
"CBI.8.1",
|
||||
"Must_redact found",
|
||||
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
||||
$table.getEntitiesOfTypeInSameRow("must_redact", entity)
|
||||
);
|
||||
});
|
||||
end
|
||||
|
||||
|
||||
@@ -268,6 +272,19 @@ rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "CBI.9.2: Redact all cells with Header Author(s) as CBI_author (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$table: Table(hasHeader("Author(s)"))
|
||||
then
|
||||
$table.streamTableCellsWithHeader("Author(s)")
|
||||
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.2", "Author(s) found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.11
|
||||
rule "CBI.11.0: Recommend all CBI_author entities in Table with Vertebrate Study Y/N Header"
|
||||
@@ -281,7 +298,22 @@ rule "CBI.11.0: Recommend all CBI_author entities in Table with Vertebrate Study
|
||||
|
||||
|
||||
// Rule unit: CBI.12
|
||||
rule "CBI.12.0: Redact and recommend TableCell with header 'Author' or 'Author(s)' and header 'Vertebrate study Y/N' with value 'Yes' (non vertebrate study)"
|
||||
rule "CBI.12.0: Redact and recommend TableCell with header 'Author' or 'Author(s)' and header 'Vertebrate study Y/N' with value 'Yes'"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$table: Table(hasHeader("Author(s)") || hasHeader("Author"), hasHeaderIgnoreCase("Vertebrate Study Y/N"))
|
||||
TableCell(header, containsAnyStringIgnoreCase("Author", "Author(s)"), $authorCol: col) from $table.streamHeaders().toList()
|
||||
TableCell(header, containsStringIgnoreCase("Vertebrate study Y/N"), $vertebrateCol: col) from $table.streamHeaders().toList()
|
||||
$rowCell: TableCell(!header, containsAnyString("Yes", "Y"), $rowWithYes: row) from $table.streamCol($vertebrateCol).toList()
|
||||
TableCell(row == $rowWithYes) from $table.streamCol($authorCol).toList()
|
||||
then
|
||||
entityCreationService.bySemanticNode($rowCell, "must_redact", EntityType.HINT)
|
||||
.ifPresent(yesEntity -> {
|
||||
yesEntity.skip("CBI.12.0", "must_redact");
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.12.1: Redact and recommend TableCell with header 'Author' or 'Author(s)' and header 'Vertebrate study Y/N' with value 'Yes' (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -293,13 +325,13 @@ rule "CBI.12.0: Redact and recommend TableCell with header 'Author' or 'Author(s
|
||||
then
|
||||
entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY)
|
||||
.ifPresent(authorEntity -> {
|
||||
authorEntity.redact("CBI.12.0", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
authorEntity.redact("CBI.12.1", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
dictionary.addMultipleAuthorsAsRecommendation(authorEntity);
|
||||
});
|
||||
|
||||
end
|
||||
|
||||
rule "CBI.12.1: Redact and recommend TableCell with header 'Author' or 'Author(s)' and header 'Vertebrate study Y/N' with value 'Yes' (vertebrate study)"
|
||||
rule "CBI.12.2: Redact and recommend TableCell with header 'Author' or 'Author(s)' and header 'Vertebrate study Y/N' with value 'Yes' (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -312,13 +344,13 @@ rule "CBI.12.1: Redact and recommend TableCell with header 'Author' or 'Author(s
|
||||
|
||||
entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY)
|
||||
.ifPresent(authorEntity -> {
|
||||
authorEntity.redact("CBI.12.1", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
authorEntity.redact("CBI.12.2", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
dictionary.addMultipleAuthorsAsRecommendation(authorEntity);
|
||||
});
|
||||
|
||||
end
|
||||
|
||||
rule "CBI.12.2: Skip TableCell with header 'Author' or 'Author(s)' and header 'Vertebrate study Y/N' with value 'No'"
|
||||
rule "CBI.12.3: Skip TableCell with header 'Author' or 'Author(s)' and header 'Vertebrate study Y/N' with value 'No'"
|
||||
when
|
||||
$table: Table(hasHeader("Author(s)") || hasHeader("Author"), hasHeaderIgnoreCase("Vertebrate Study Y/N"))
|
||||
TableCell(header, containsAnyStringIgnoreCase("Author", "Author(s)"), $authorCol: col) from $table.streamHeaders().toList()
|
||||
@@ -327,16 +359,19 @@ rule "CBI.12.2: Skip TableCell with header 'Author' or 'Author(s)' and header 'V
|
||||
$authorCell: TableCell(row == $rowWithNo) from $table.streamCol($authorCol).toList()
|
||||
then
|
||||
entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY)
|
||||
.ifPresent(authorEntity -> authorEntity.skip("CBI.12.2", "Not redacted because it's row does not belong to a vertebrate study"));
|
||||
.ifPresent(authorEntity -> authorEntity.skip("CBI.12.3", "Not redacted because it's row does not belong to a vertebrate study"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.14
|
||||
rule "CBI.14.0: Redact CBI_sponsor entities if preceded by \"batches produced at\""
|
||||
when
|
||||
$section: Section(containsStringIgnoreCase("batches produced at"))
|
||||
$sponsorEntity: TextEntity(type() == "CBI_sponsor", textBefore.contains("batches produced at"))
|
||||
then
|
||||
$sponsorEntity.redact("CBI.14.0", "Redacted because it represents a sponsor company", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
entityCreationService.byString("batches produced at", "must_redact", EntityType.HINT, $section)
|
||||
.forEach(entity -> entity.skip("CBI.14.0", "must_redact"));
|
||||
end
|
||||
|
||||
|
||||
@@ -357,10 +392,10 @@ rule "CBI.15.0: Redact row if row contains \"determination of residues\" and liv
|
||||
containsStringIgnoreCase($residueKeyword),
|
||||
containsStringIgnoreCase($keyword))
|
||||
then
|
||||
entityCreationService.byString($keyword, "must_redact", EntityType.ENTITY, $section)
|
||||
.toList();
|
||||
entityCreationService.byString($keyword, "must_redact", EntityType.HINT, $section)
|
||||
.forEach(entity -> entity.skip("CBI.15.0", "must_redact"));
|
||||
|
||||
$section.getEntitiesOfType(List.of($keyword, $residueKeyword))
|
||||
$section.getEntitiesOfType(List.of("CBI_author", "CBI_address"))
|
||||
.forEach(redactionEntity -> redactionEntity.redact("CBI.15.0", "Determination of residues and keyword \"" + $keyword + "\" was found.", "Reg (EC) No 1107/2009 Art. 63 (2g)"));
|
||||
end
|
||||
|
||||
@@ -378,8 +413,8 @@ rule "CBI.15.1: Redact CBI_author and CBI_address if row contains \"determinatio
|
||||
$residueKeyword: String() from List.of("determination of residues", "determination of total residues")
|
||||
$table: Table(containsStringIgnoreCase($residueKeyword), containsStringIgnoreCase($keyword))
|
||||
then
|
||||
entityCreationService.byString($keyword, "must_redact", EntityType.ENTITY, $table)
|
||||
.toList();
|
||||
entityCreationService.byString($keyword, "must_redact", EntityType.HINT, $table)
|
||||
.forEach(entity -> entity.skip("CBI.15.1", "must_redact"));
|
||||
|
||||
$table.streamEntitiesWhereRowContainsStringsIgnoreCase(List.of($keyword, $residueKeyword))
|
||||
.filter(redactionEntity -> redactionEntity.isAnyType(List.of("CBI_author", "CBI_address")))
|
||||
@@ -388,7 +423,19 @@ rule "CBI.15.1: Redact CBI_author and CBI_address if row contains \"determinatio
|
||||
|
||||
|
||||
// Rule unit: CBI.16
|
||||
rule "CBI.16.0: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
|
||||
rule "CBI.16.0: Add CBI_author with \"et al.\" RegEx"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$section: Section(containsString("et al."))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.0", "Author found by \"et al\" regex", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.16.1: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -396,12 +443,12 @@ rule "CBI.16.0: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.0", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
entity.redact("CBI.16.1", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.16.1: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
|
||||
rule "CBI.16.2: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -409,7 +456,19 @@ rule "CBI.16.1: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.1", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
entity.redact("CBI.16.2", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.16.3: Add CBI_author with \"et al.\" RegEx"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$section: Section(containsString("et al."))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
||||
.forEach(entity -> {
|
||||
entity.redact("CBI.16.3", "Author found by \"et al\" regex", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
dictionary.recommendEverywhere(entity);
|
||||
});
|
||||
end
|
||||
@@ -467,7 +526,19 @@ rule "CBI.19.0: Expand CBI_author entities with salutation prefix"
|
||||
|
||||
|
||||
// Rule unit: CBI.20
|
||||
rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (non vertebrate study)"
|
||||
rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\")"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$section: Section(!hasTables(), containsString("PERFORMING LABORATORY:"), containsString("LABORATORY PROJECT ID:"))
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.redact("CBI.20.0", "PERFORMING LABORATORY was found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.20.1: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (non vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -475,12 +546,12 @@ rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.skip("CBI.20.0", "PERFORMING LABORATORY was found for non vertebrate study");
|
||||
laboratoryEntity.skip("CBI.20.1", "PERFORMING LABORATORY was found for non vertebrate study");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.20.1: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (vertebrate study)"
|
||||
rule "CBI.20.2: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (vertebrate study)"
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
@@ -488,56 +559,117 @@ rule "CBI.20.1: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.redact("CBI.20.1", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
rule "CBI.20.3: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\""
|
||||
agenda-group "LOCAL_DICTIONARY_ADDS"
|
||||
when
|
||||
$section: Section(!hasTables(), containsString("PERFORMING LABORATORY:"), containsString("LABORATORY PROJECT ID:"))
|
||||
then
|
||||
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
||||
.forEach(laboratoryEntity -> {
|
||||
laboratoryEntity.redact("CBI.20.3", "PERFORMING LABORATORY was found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
dictionary.recommendEverywhere(laboratoryEntity);
|
||||
});
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: CBI.23
|
||||
rule "CBI.23.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ PII rules ------------------------------------
|
||||
|
||||
// Rule unit: PII.0
|
||||
rule "PII.0.0: Redact all PII (non vertebrate study)"
|
||||
rule "PII.0.0: Redact all PII"
|
||||
when
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.0", "Personal Information found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
end
|
||||
|
||||
rule "PII.0.1: Redact all PII (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.0", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
$pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "PII.0.1: Redact all PII (vertebrate study)"
|
||||
rule "PII.0.2: Redact all PII (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
$pii.redact("PII.0.2", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "PII.0.3: Redact all PII"
|
||||
when
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.3", "Personal Information found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.1
|
||||
rule "PII.1.0: Redact Emails by RegEx (Non vertebrate study)"
|
||||
rule "PII.1.0: Redact Emails by RegEx"
|
||||
when
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.0", "Found by Email Regex", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.0", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.1.1: Redact Emails by RegEx (vertebrate study)"
|
||||
rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.1.5: Redact Emails by RegEx"
|
||||
when
|
||||
$section: Section(containsString("@"))
|
||||
then
|
||||
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
|
||||
.forEach(emailEntity -> emailEntity.redact("PII.1.5", "Found by Email Regex", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.4
|
||||
rule "PII.4.0: Redact line after contact information keywords (non vertebrate study)"
|
||||
rule "PII.4.0: Redact line after contact information keywords"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
"Contact:",
|
||||
"Alternative contact:",
|
||||
@@ -563,7 +695,62 @@ rule "PII.4.0: Redact line after contact information keywords (non vertebrate st
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.4.1: Redact line after contact information keywords (vertebrate study)"
|
||||
rule "PII.4.1: Redact line after contact information keywords"
|
||||
when
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
"Contact:",
|
||||
"Alternative contact:",
|
||||
"European contact:",
|
||||
"No:",
|
||||
"Contact:",
|
||||
"Tel.:",
|
||||
"Tel:",
|
||||
"Telephone number:",
|
||||
"Telephone No:",
|
||||
"Telephone:",
|
||||
"Phone No.",
|
||||
"Phone:",
|
||||
"Fax number:",
|
||||
"Fax:",
|
||||
"E-mail:",
|
||||
"Email:",
|
||||
"e-mail:",
|
||||
"E-mail address:")
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
rule "PII.4.2: Redact line after contact information keywords (Non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
"Contact:",
|
||||
"Alternative contact:",
|
||||
"European contact:",
|
||||
"No:",
|
||||
"Contact:",
|
||||
"Tel.:",
|
||||
"Tel:",
|
||||
"Telephone number:",
|
||||
"Telephone No:",
|
||||
"Telephone:",
|
||||
"Phone No.",
|
||||
"Phone:",
|
||||
"Fax number:",
|
||||
"Fax:",
|
||||
"E-mail:",
|
||||
"Email:",
|
||||
"e-mail:",
|
||||
"E-mail address:")
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.2", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.4.3: Redact line after contact information keywords (Vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$contactKeyword: String() from List.of("Contact point:",
|
||||
@@ -588,12 +775,23 @@ rule "PII.4.1: Redact line after contact information keywords (vertebrate study)
|
||||
$section: Section(containsString($contactKeyword))
|
||||
then
|
||||
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.4.3", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.6
|
||||
rule "PII.6.0: Redact line between contact keywords (non vertebrate study)"
|
||||
rule "PII.6.0: Redact line between contact keywords"
|
||||
when
|
||||
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
||||
then
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.0", "Found between contact keywords", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.6.1: Redact line between contact keywords (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
||||
@@ -602,10 +800,10 @@ rule "PII.6.0: Redact line between contact keywords (non vertebrate study)"
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.0", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.6.1: Redact line between contact keywords (vertebrate study)"
|
||||
rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
||||
@@ -614,12 +812,41 @@ rule "PII.6.1: Redact line between contact keywords (vertebrate study)"
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.6.3: Redact line between contact keywords (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
||||
then
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
)
|
||||
.forEach(contactEntity -> contactEntity.redact("PII.6.3", "Found between contact keywords", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.7
|
||||
rule "PII.7.0: Redact contact information if applicant is found (non vertebrate study)"
|
||||
rule "PII.7.0: Redact contact information if applicant is found"
|
||||
when
|
||||
$section: Section(getHeadline().containsString("applicant") ||
|
||||
getHeadline().containsString("Primary contact") ||
|
||||
getHeadline().containsString("Alternative contact") ||
|
||||
containsString("Applicant") ||
|
||||
containsString("Telephone number:"))
|
||||
then
|
||||
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "Contact:", "Tel.:", "Tel:", "Telephone number:",
|
||||
"Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section),
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.7.0", "Applicant information was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.7.1: Redact contact information if applicant is found (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(getHeadline().containsString("applicant") ||
|
||||
@@ -634,10 +861,10 @@ rule "PII.7.0: Redact contact information if applicant is found (non vertebrate
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.7.0", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.7.1: Redact contact information if applicant is found (vertebrate study)"
|
||||
rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(getHeadline().containsString("applicant") ||
|
||||
@@ -652,14 +879,13 @@ rule "PII.7.1: Redact contact information if applicant is found (vertebrate stud
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.8
|
||||
rule "PII.8.0: Redact contact information if producer is found (non vertebrate study)"
|
||||
rule "PII.8.0: Redact contact information if producer is found"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
||||
containsStringIgnoreCase("producer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer of the active substance") ||
|
||||
@@ -675,7 +901,25 @@ rule "PII.8.0: Redact contact information if producer is found (non vertebrate s
|
||||
.forEach(entity -> entity.redact("PII.8.0", "Producer was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.8.1: Redact contact information if producer is found (vertebrate study)"
|
||||
rule "PII.8.1: Redact contact information if producer is found (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
||||
containsStringIgnoreCase("producer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer of the active substance") ||
|
||||
containsStringIgnoreCase("manufacturer:") ||
|
||||
containsStringIgnoreCase("Producer or producers of the active substance"))
|
||||
then
|
||||
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "Contact:", "Tel.:", "Tel:", "Telephone number:",
|
||||
"Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section),
|
||||
Stream.concat(
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
rule "PII.8.2: Redact contact information if producer is found (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
||||
@@ -690,27 +934,25 @@ rule "PII.8.1: Redact contact information if producer is found (vertebrate study
|
||||
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
||||
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
||||
))
|
||||
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(entity -> entity.redact("PII.8.2", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: PII.9
|
||||
rule "PII.9.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (non vertebrate study)"
|
||||
rule "PII.9.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\""
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.9.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.9.0", "AUTHOR(S) was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
||||
end
|
||||
|
||||
rule "PII.9.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
|
||||
rule "PII.9.3: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\""
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
|
||||
then
|
||||
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document)
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.9.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
||||
.forEach(authorEntity -> authorEntity.redact("PII.9.3", "AUTHOR(S) was found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
|
||||
end
|
||||
|
||||
|
||||
@@ -757,38 +999,66 @@ rule "ETC.1.0: Redact Purity"
|
||||
|
||||
|
||||
// Rule unit: ETC.2
|
||||
rule "ETC.2.0: Redact signatures (non vertebrate study)"
|
||||
rule "ETC.2.0: Redact signatures"
|
||||
when
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.0", "Signature Found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
end
|
||||
|
||||
rule "ETC.2.1: Redact signatures (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.0", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
$signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "ETC.2.1: Redact signatures (vertebrate study)"
|
||||
rule "ETC.2.2: Redact signatures (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
$signature.redact("ETC.2.2", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "ETC.2.3: Redact signatures"
|
||||
when
|
||||
$signature: Image(imageType == ImageType.SIGNATURE)
|
||||
then
|
||||
$signature.redact("ETC.2.3", "Signature Found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: ETC.3
|
||||
rule "ETC.3.0: Skip logos (non vertebrate study)"
|
||||
rule "ETC.3.0: Redact logos"
|
||||
when
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.redact("ETC.3.0", "Logo Found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
||||
end
|
||||
|
||||
rule "ETC.3.1: Skip logos (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.skip("ETC.3.0", "Logo Found");
|
||||
$logo.skip("ETC.3.1", "Logo Found");
|
||||
end
|
||||
|
||||
rule "ETC.3.1: Redact logos (vertebrate study)"
|
||||
rule "ETC.3.2: Redact logos (vertebrate study)"
|
||||
when
|
||||
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.redact("ETC.3.1", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
||||
$logo.redact("ETC.3.2", "Logo Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
rule "ETC.3.3: Redact logos"
|
||||
when
|
||||
$logo: Image(imageType == ImageType.LOGO)
|
||||
then
|
||||
$logo.redact("ETC.3.3", "Logo Found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
|
||||
end
|
||||
|
||||
|
||||
@@ -812,6 +1082,7 @@ rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidentia
|
||||
end
|
||||
|
||||
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
|
||||
salience 256
|
||||
when
|
||||
not FileAttribute(label == "Confidentiality", value == "confidential")
|
||||
$dossierRedaction: TextEntity(type() == "dossier_redaction")
|
||||
@@ -1046,7 +1317,7 @@ rule "MAN.4.1: Apply legal basis change"
|
||||
rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
salience 65
|
||||
when
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, active())
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, !removed())
|
||||
$contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
|
||||
then
|
||||
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
||||
@@ -1054,22 +1325,6 @@ rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.1
|
||||
rule "X.1.0: Merge intersecting Entities of same type"
|
||||
salience 64
|
||||
when
|
||||
$first: TextEntity($type: type(), $entityType: entityType, !resized(), active())
|
||||
$second: TextEntity(intersects($first), type() == $type, entityType == $entityType, this != $first, !hasManualChanges(), active())
|
||||
then
|
||||
TextEntity mergedEntity = entityCreationService.mergeEntitiesOfSameType(List.of($first, $second), $type, $entityType, document);
|
||||
$first.remove("X.1.0", "merge intersecting Entities of same type");
|
||||
$second.remove("X.1.0", "merge intersecting Entities of same type");
|
||||
retract($first);
|
||||
retract($second);
|
||||
mergedEntity.getIntersectingNodes().forEach(node -> update(node));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.2
|
||||
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
|
||||
salience 64
|
||||
@@ -1177,24 +1432,15 @@ rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.9
|
||||
rule "X.9.0: Merge mostly contained signatures"
|
||||
// Rule unit: X.11
|
||||
rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
|
||||
salience 64
|
||||
when
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI)
|
||||
$signature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.ALGORITHM, mostlyContains($aiSignature, 0.8))
|
||||
$manualEntity: TextEntity(engines contains Engine.MANUAL, active())
|
||||
$dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
|
||||
then
|
||||
$aiSignature.remove("X.9.0", "removed because already contained by alogrithm signature");
|
||||
$signature.addEngine(LayoutEngine.AI);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.10
|
||||
rule "X.10.0: remove false positives of ai"
|
||||
when
|
||||
$anyImage: Image(engines contains LayoutEngine.ALGORITHM)
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI, !mostlyContainedBy($anyImage, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.10.0", "Removed because false positive");
|
||||
$dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
|
||||
retract($dictionaryEntity);
|
||||
end
|
||||
|
||||
|
||||
|
||||
+10
-37
@@ -54,8 +54,6 @@ 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.ManualLegalBasisChange;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
@@ -82,12 +80,12 @@ rule "CBI.0.0: Redact CBI Authors (non vertebrate Study)"
|
||||
//------------------------------------ PII rules ------------------------------------
|
||||
|
||||
// Rule unit: PII.0
|
||||
rule "PII.0.0: Redact all PII (non vertebrate study)"
|
||||
rule "PII.0.1: Redact all PII (non vertebrate study)"
|
||||
when
|
||||
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
|
||||
$pii: TextEntity(type() == "PII", dictionaryEntry)
|
||||
then
|
||||
$pii.redact("PII.0.0", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
$pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
||||
end
|
||||
|
||||
|
||||
@@ -250,7 +248,7 @@ rule "MAN.4.1: Apply legal basis change"
|
||||
rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
salience 65
|
||||
when
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, active())
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, !removed())
|
||||
$contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
|
||||
then
|
||||
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
||||
@@ -258,22 +256,6 @@ rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.1
|
||||
rule "X.1.0: Merge intersecting Entities of same type"
|
||||
salience 64
|
||||
when
|
||||
$first: TextEntity($type: type(), $entityType: entityType, !resized(), active())
|
||||
$second: TextEntity(intersects($first), type() == $type, entityType == $entityType, this != $first, !hasManualChanges(), active())
|
||||
then
|
||||
TextEntity mergedEntity = entityCreationService.mergeEntitiesOfSameType(List.of($first, $second), $type, $entityType, document);
|
||||
$first.remove("X.1.0", "merge intersecting Entities of same type");
|
||||
$second.remove("X.1.0", "merge intersecting Entities of same type");
|
||||
retract($first);
|
||||
retract($second);
|
||||
mergedEntity.getIntersectingNodes().forEach(node -> update(node));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.2
|
||||
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
|
||||
salience 64
|
||||
@@ -381,24 +363,15 @@ rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.9
|
||||
rule "X.9.0: Merge mostly contained signatures"
|
||||
// Rule unit: X.11
|
||||
rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
|
||||
salience 64
|
||||
when
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI)
|
||||
$signature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.ALGORITHM, mostlyContains($aiSignature, 0.8))
|
||||
$manualEntity: TextEntity(engines contains Engine.MANUAL, active())
|
||||
$dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
|
||||
then
|
||||
$aiSignature.remove("X.9.0", "removed because already contained by alogrithm signature");
|
||||
$signature.addEngine(LayoutEngine.AI);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.10
|
||||
rule "X.10.0: remove false positives of ai"
|
||||
when
|
||||
$anyImage: Image(engines contains LayoutEngine.ALGORITHM)
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI, !mostlyContainedBy($anyImage, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.10.0", "Removed because false positive");
|
||||
$dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
|
||||
retract($dictionaryEntity);
|
||||
end
|
||||
|
||||
|
||||
|
||||
+8
-19
@@ -54,8 +54,6 @@ 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.ManualLegalBasisChange;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
@@ -376,7 +374,7 @@ rule "MAN.4.1: Apply legal basis change"
|
||||
rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
salience 65
|
||||
when
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, active())
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, !removed())
|
||||
$contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
|
||||
then
|
||||
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
||||
@@ -502,24 +500,15 @@ rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.9
|
||||
rule "X.9.0: Merge mostly contained signatures"
|
||||
// Rule unit: X.11
|
||||
rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
|
||||
salience 64
|
||||
when
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI)
|
||||
$signature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.ALGORITHM, mostlyContains($aiSignature, 0.8))
|
||||
$manualEntity: TextEntity(engines contains Engine.MANUAL, active())
|
||||
$dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
|
||||
then
|
||||
$aiSignature.remove("X.9.0", "removed because already contained by alogrithm signature");
|
||||
$signature.addEngine(LayoutEngine.AI);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.10
|
||||
rule "X.10.0: remove false positives of ai"
|
||||
when
|
||||
$anyImage: Image(engines contains LayoutEngine.ALGORITHM)
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI, !mostlyContainedBy($anyImage, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.10.0", "Removed because false positive");
|
||||
$dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
|
||||
retract($dictionaryEntity);
|
||||
end
|
||||
|
||||
|
||||
|
||||
+1
-40
@@ -54,8 +54,6 @@ 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.ManualLegalBasisChange;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
@@ -276,7 +274,7 @@ rule "MAN.4.1: Apply legal basis change"
|
||||
rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
salience 65
|
||||
when
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, active())
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, !removed())
|
||||
$contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
|
||||
then
|
||||
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
||||
@@ -284,22 +282,6 @@ rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.1
|
||||
rule "X.1.0: Merge intersecting Entities of same type"
|
||||
salience 64
|
||||
when
|
||||
$first: TextEntity($type: type(), $entityType: entityType, !resized(), active())
|
||||
$second: TextEntity(intersects($first), type() == $type, entityType == $entityType, this != $first, !hasManualChanges(), active())
|
||||
then
|
||||
TextEntity mergedEntity = entityCreationService.mergeEntitiesOfSameType(List.of($first, $second), $type, $entityType, document);
|
||||
$first.remove("X.1.0", "merge intersecting Entities of same type");
|
||||
$second.remove("X.1.0", "merge intersecting Entities of same type");
|
||||
retract($first);
|
||||
retract($second);
|
||||
mergedEntity.getIntersectingNodes().forEach(node -> update(node));
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.2
|
||||
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
|
||||
salience 64
|
||||
@@ -418,27 +400,6 @@ rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.9
|
||||
rule "X.9.0: Merge mostly contained signatures"
|
||||
when
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI)
|
||||
$signature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.ALGORITHM, mostlyContains($aiSignature, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.9.0", "removed because already contained by alogrithm signature");
|
||||
$signature.addEngine(LayoutEngine.AI);
|
||||
end
|
||||
|
||||
|
||||
// Rule unit: X.10
|
||||
rule "X.10.0: remove false positives of ai"
|
||||
when
|
||||
$anyImage: Image(engines contains LayoutEngine.ALGORITHM)
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI, !mostlyContainedBy($anyImage, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.10.0", "Removed because false positive");
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ File attributes rules ------------------------------------
|
||||
|
||||
// Rule unit: FA.1
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<Configuration>
|
||||
|
||||
<Appenders>
|
||||
<Console name="CONSOLE" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Root level="warn">
|
||||
<AppenderRef ref="CONSOLE"/>
|
||||
</Root>
|
||||
<Logger name="com.iqser" level="info"/>
|
||||
</Loggers>
|
||||
|
||||
</Configuration>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<configuration>
|
||||
|
||||
<springProperty scope="configuration" name="logType" source="logging.type"/>
|
||||
<springProperty scope="context" name="application.name" source="spring.application.name"/>
|
||||
<springProperty scope="context" name="version" source="project.version"/>
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
|
||||
|
||||
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="${logType}"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
+1
@@ -459,6 +459,7 @@ rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidentia
|
||||
end
|
||||
|
||||
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
|
||||
salience 256
|
||||
when
|
||||
not FileAttribute(label == "Confidentiality", value == "confidential")
|
||||
$dossierRedaction: TextEntity(type() == "dossier_redaction")
|
||||
|
||||
+1
-5
@@ -33,11 +33,7 @@ public class RuleFileMigrator {
|
||||
rulesToAdd.forEach(ruleFileBluePrint::addRule);
|
||||
}
|
||||
|
||||
RuleFileBluePrint newBluePrint = new RuleFileBluePrint(combinedBluePrint.imports(),
|
||||
combinedBluePrint.globals(),
|
||||
combinedBluePrint.queries(),
|
||||
ruleFileBluePrint.ruleClasses());
|
||||
String migratedRulesString = RuleFileFactory.buildRuleString(newBluePrint);
|
||||
String migratedRulesString = RuleFileFactory.buildRuleString(ruleFileBluePrint);
|
||||
String migratedFilePath = ruleFile.getAbsolutePath();
|
||||
try (var out = new FileOutputStream(migratedFilePath)) {
|
||||
out.write(migratedRulesString.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
+526
-139
File diff suppressed because it is too large
Load Diff
@@ -54,8 +54,6 @@ 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.ManualLegalBasisChange;
|
||||
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.LayoutEngine;
|
||||
|
||||
global Document document
|
||||
global EntityCreationService entityCreationService
|
||||
global ManualChangesApplicationService manualChangesApplicationService
|
||||
@@ -1301,6 +1299,7 @@ rule "TAB.7.0: Indicator (Species)"
|
||||
.ifPresent(redactionEntity -> redactionEntity.apply("TAB.7.0", "Vertebrate study found"));
|
||||
end
|
||||
|
||||
|
||||
//------------------------------------ Manual redaction rules ------------------------------------
|
||||
|
||||
// Rule unit: MAN.0
|
||||
@@ -1459,7 +1458,7 @@ rule "MAN.4.1: Apply legal basis change"
|
||||
rule "X.0.0: Remove Entity contained by Entity of same type"
|
||||
salience 65
|
||||
when
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, active())
|
||||
$larger: TextEntity($type: type(), $entityType: entityType, !removed())
|
||||
$contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
|
||||
then
|
||||
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
||||
@@ -1562,27 +1561,19 @@ rule "X.8.1: Remove Entity when intersected by imported Entity"
|
||||
retract($other);
|
||||
end
|
||||
|
||||
// Rule unit: X.9.0
|
||||
rule "X.9.0: Merge mostly contained signatures"
|
||||
when
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI)
|
||||
$signature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.ALGORITHM, mostlyContains($aiSignature, 0.8))
|
||||
then
|
||||
$aiSignature.remove("X.9.0", "removed because already contained by alogrithm signature");
|
||||
$signature.addEngine(LayoutEngine.AI);
|
||||
end
|
||||
|
||||
// Rule unit: X.10.0
|
||||
rule "X.10.0: remove false positives of ai"
|
||||
// Rule unit: X.11
|
||||
rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
|
||||
salience 64
|
||||
when
|
||||
$anyImage: Image(engines contains LayoutEngine.ALGORITHM)
|
||||
$aiSignature: Image(imageType == ImageType.SIGNATURE, engines contains LayoutEngine.AI, !mostlyContainedBy($anyImage, 0.8))
|
||||
$manualEntity: TextEntity(engines contains Engine.MANUAL, active())
|
||||
$dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
|
||||
then
|
||||
$aiSignature.remove("X.10.0", "Removed because false positive");
|
||||
$dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
|
||||
retract($dictionaryEntity);
|
||||
end
|
||||
|
||||
|
||||
|
||||
//------------------------------------ File attributes rules ------------------------------------
|
||||
|
||||
// Rule unit: FA.1
|
||||
|
||||
+8
-4
@@ -25,9 +25,9 @@ public class RuleFileMigrationTest {
|
||||
|
||||
// Put your redaction service drools paths and dossier-templates paths both RM and DM here
|
||||
static final List<String> ruleFileDirs = List.of(
|
||||
"/Users/maverickstuder/Documents/RedactManager/redaction-service/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools",
|
||||
"/Users/maverickstuder/Documents/DocuMine/dossier-templates-v2/",
|
||||
"/Users/maverickstuder/Documents/RedactManager/dossier-templates-v2/");
|
||||
"/home/kschuettler/iqser/redaction/redaction-service/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools",
|
||||
"/home/kschuettler/iqser/fforesight/dossier-templates-v2",
|
||||
"/home/kschuettler/iqser/redaction/dossier-templates-v2");
|
||||
|
||||
|
||||
@Test
|
||||
@@ -36,7 +36,11 @@ public class RuleFileMigrationTest {
|
||||
void migrateAllEntityRules() {
|
||||
|
||||
for (String ruleFileDir : ruleFileDirs) {
|
||||
Files.walk(Path.of(ruleFileDir)).filter(this::isEntityRuleFile).map(Path::toFile).peek(System.out::println).forEach(RuleFileMigrator::migrateFile);
|
||||
Files.walk(Path.of(ruleFileDir))
|
||||
.filter(this::isEntityRuleFile)
|
||||
.map(Path::toFile)
|
||||
.peek(System.out::println)
|
||||
.forEach(RuleFileMigrator::migrateFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user