Compare commits
67
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c8ff214e4 | ||
|
|
2a6ee02f9f | ||
|
|
7dad091e19 | ||
|
|
f7b584617b | ||
|
|
02a264d586 | ||
|
|
dacd7b725d | ||
|
|
7ee338a067 | ||
|
|
19d48e207b | ||
|
|
e22b625e9c | ||
|
|
95a2d71af0 | ||
|
|
6fec4cc209 | ||
|
|
fa3cbc938c | ||
|
|
f6b4d2a067 | ||
|
|
0967e8e64a | ||
|
|
20c7e19343 | ||
|
|
6fb7600de2 | ||
|
|
fbd55c15ef | ||
|
|
ffa251643a | ||
|
|
ac19defa1c | ||
|
|
21ca25f330 | ||
|
|
e9b9fbb91b | ||
|
|
3f76401c7d | ||
|
|
a16f9c6d6e | ||
|
|
7ca3416d1d | ||
|
|
85db826f89 | ||
|
|
6fac40fd8d | ||
|
|
23fe93630b | ||
|
|
778c975649 | ||
|
|
50b2908763 | ||
|
|
c1192ceefd | ||
|
|
77584c9a5a | ||
|
|
21d717f083 | ||
|
|
c85ce25ed4 | ||
|
|
f608d991a3 | ||
|
|
e914b09d1e | ||
|
|
87d64b6d12 | ||
|
|
eb71373152 | ||
|
|
a65e23e45d | ||
|
|
4aaef260d7 | ||
|
|
c669f9a09a | ||
|
|
b1bad03090 | ||
|
|
0a0d9fe31a | ||
|
|
fd37e3deab | ||
|
|
7fb04cfa48 | ||
|
|
0f5f7974cc | ||
|
|
0601ec4862 | ||
|
|
d2807e0ff1 | ||
|
|
e49bbfca88 | ||
|
|
7967453c67 | ||
|
|
a3d002929d | ||
|
|
8a44d6299c | ||
|
|
78de7371a0 | ||
|
|
924f791e4d | ||
|
|
542bbb294a | ||
|
|
df2fcd2e5e | ||
|
|
8e819a0d26 | ||
|
|
01e7115d30 | ||
|
|
747323f882 | ||
|
|
729a7334d9 | ||
|
|
36967cab2c | ||
|
|
cfd9d7665b | ||
|
|
23d84980cd | ||
|
|
314f7ebdd9 | ||
|
|
f666a03301 | ||
|
|
313ec32d30 | ||
|
|
76dca41299 | ||
|
|
f21e48e3d7 |
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.iqser.red</groupId>
|
||||
<artifactId>platform-docker-dependency</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<version>1.2.0</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -42,7 +42,7 @@
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@@ -95,4 +95,4 @@
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -23,20 +23,24 @@
|
||||
|
||||
<properties>
|
||||
<pdfbox.version>2.0.24</pdfbox.version>
|
||||
<dsljson.version>1.9.9</dsljson.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.dslplatform</groupId>
|
||||
<artifactId>dsl-json-java8</artifactId>
|
||||
<version>${dsljson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.iqser.red</groupId>
|
||||
<artifactId>platform-commons-dependency</artifactId>
|
||||
<version>1.15.0</version>
|
||||
<version>1.13.0</version>
|
||||
<scope>import</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.pdfbox</groupId>
|
||||
<artifactId>pdfbox</artifactId>
|
||||
@@ -47,9 +51,7 @@
|
||||
<artifactId>pdfbox-tools</artifactId>
|
||||
<version>${pdfbox.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
@@ -58,12 +60,10 @@
|
||||
<plugin>
|
||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||
<artifactId>sonar-maven-plugin</artifactId>
|
||||
<version>3.9.0.2155</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-maven</artifactId>
|
||||
<version>6.3.1</version>
|
||||
<configuration>
|
||||
<format>ALL</format>
|
||||
</configuration>
|
||||
@@ -88,27 +88,5 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-agent</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<goals>
|
||||
<goal>report-aggregate</goal>
|
||||
</goals>
|
||||
<phase>verify</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -12,15 +12,24 @@
|
||||
<artifactId>redaction-service-api-v1</artifactId>
|
||||
|
||||
<properties>
|
||||
<persistence-service.version>1.156.20</persistence-service.version>
|
||||
<persistence-service.version>1.166.0</persistence-service.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.dslplatform/dsl-json-java8 -->
|
||||
<dependency>
|
||||
<groupId>com.dslplatform</groupId>
|
||||
<artifactId>dsl-json-java8</artifactId>
|
||||
<version>${dsljson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.service</groupId>
|
||||
<artifactId>persistence-service-api-v1</artifactId>
|
||||
@@ -32,5 +41,21 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessors>
|
||||
<annotationProcessor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</annotationProcessor>
|
||||
<annotationProcessor>com.dslplatform.json.processor.CompiledJsonAnnotationProcessor</annotationProcessor>
|
||||
</annotationProcessors>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
+6
-7
@@ -1,18 +1,17 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
-1
@@ -1,7 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
+4
-4
@@ -1,14 +1,14 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
+6
-6
@@ -22,11 +22,7 @@ public class ManualChange {
|
||||
private OffsetDateTime processedDate;
|
||||
private OffsetDateTime requestedDate;
|
||||
private String userId;
|
||||
private Map<String, Object> propertyChanges = new HashMap<>();
|
||||
|
||||
public boolean isProcessed() {
|
||||
return processedDate != null;
|
||||
}
|
||||
private Map<String, String> propertyChanges = new HashMap<>();
|
||||
|
||||
public static ManualChange from(BaseAnnotation baseAnnotation) {
|
||||
ManualChange manualChange = new ManualChange();
|
||||
@@ -37,12 +33,16 @@ public class ManualChange {
|
||||
return manualChange;
|
||||
}
|
||||
|
||||
public boolean isProcessed() {
|
||||
return processedDate != null;
|
||||
}
|
||||
|
||||
public ManualChange withManualRedactionType(ManualRedactionType manualRedactionType) {
|
||||
this.manualRedactionType = manualRedactionType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManualChange withChange(String property, Object value) {
|
||||
public ManualChange withChange(String property, String value) {
|
||||
this.propertyChanges.put(property, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
+7
-3
@@ -1,14 +1,18 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.legalbasis.LegalBasis;
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RedactionLog {
|
||||
|
||||
|
||||
@@ -23,8 +27,8 @@ public class RedactionLog {
|
||||
*/
|
||||
private int analysisNumber;
|
||||
|
||||
private List<RedactionLogEntry> redactionLogEntry;
|
||||
private List<LegalBasis> legalBasis;
|
||||
private List<RedactionLogEntry> redactionLogEntry = new ArrayList<>();
|
||||
private List<RedactionLogLegalBasis> legalBasis = new ArrayList<>();
|
||||
|
||||
private long dictionaryVersion = -1;
|
||||
private long dossierDictionaryVersion = -1;
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RedactionLogComment {
|
||||
|
||||
private long id;
|
||||
private String user;
|
||||
private String text;
|
||||
private String annotationId;
|
||||
private String fileId;
|
||||
private OffsetDateTime date;
|
||||
private OffsetDateTime softDeletedTime;
|
||||
|
||||
}
|
||||
+3
-2
@@ -1,7 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.Comment;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.*;
|
||||
@@ -41,7 +40,7 @@ public class RedactionLogEntry {
|
||||
private String textAfter;
|
||||
|
||||
@Builder.Default
|
||||
private List<Comment> comments = new ArrayList<>();
|
||||
private List<RedactionLogComment> comments = new ArrayList<>();
|
||||
|
||||
private int startOffset;
|
||||
private int endOffset;
|
||||
@@ -54,6 +53,8 @@ public class RedactionLogEntry {
|
||||
|
||||
private boolean excluded;
|
||||
|
||||
private String sourceId;
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
@Builder.Default
|
||||
private List<Change> changes = new ArrayList<>();
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RedactionLogLegalBasis {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private String reason;
|
||||
|
||||
}
|
||||
+4
-7
@@ -1,20 +1,17 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
-11
@@ -3,27 +3,16 @@ package com.iqser.red.service.redaction.v1.model;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SectionArea {
|
||||
|
||||
@NonNull
|
||||
private Point topLeft;
|
||||
|
||||
@NonNull
|
||||
private float width;
|
||||
|
||||
@NonNull
|
||||
private float height;
|
||||
|
||||
@NonNull
|
||||
private int page;
|
||||
|
||||
private String header;
|
||||
|
||||
public boolean contains(Rectangle other) {
|
||||
|
||||
+8
-6
@@ -1,13 +1,14 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SectionGrid {
|
||||
@@ -17,13 +18,14 @@ public class SectionGrid {
|
||||
private List<SectionGridSection> sections = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class SectionGridSection {
|
||||
|
||||
private final int sectionNumber;
|
||||
private final String headline;
|
||||
private final Set<Integer> pages;
|
||||
private final List<SectionArea> sectionAreas;
|
||||
private int sectionNumber;
|
||||
private String headline;
|
||||
private Set<Integer> pages;
|
||||
private List<SectionArea> sectionAreas;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
-12
@@ -3,30 +3,18 @@ package com.iqser.red.service.redaction.v1.model;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class SectionRectangle {
|
||||
|
||||
@NonNull
|
||||
private Point topLeft;
|
||||
|
||||
@NonNull
|
||||
private float width;
|
||||
|
||||
@NonNull
|
||||
private float height;
|
||||
|
||||
@NonNull
|
||||
private int part;
|
||||
|
||||
@NonNull
|
||||
private int numberOfParts;
|
||||
|
||||
private List<CellRectangle> tableCells;
|
||||
|
||||
+3
-7
@@ -1,20 +1,16 @@
|
||||
package com.iqser.red.service.redaction.v1.resources;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.redaction.v1.model.*;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.model.RedactionLog;
|
||||
import com.iqser.red.service.redaction.v1.model.RedactionRequest;
|
||||
import com.iqser.red.service.redaction.v1.model.RedactionResult;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
public interface RedactionResource {
|
||||
|
||||
@PostMapping(value = "/annotate", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
AnnotateResponse annotate(@RequestBody AnnotateRequest annotateRequest);
|
||||
|
||||
|
||||
@PostMapping(value = "/debug/classifications", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
RedactionResult classify(@RequestBody RedactionRequest redactionRequest);
|
||||
|
||||
|
||||
@@ -12,23 +12,44 @@
|
||||
<artifactId>redaction-service-server-v1</artifactId>
|
||||
|
||||
<properties>
|
||||
<drools.version>7.59.0.Final</drools.version>
|
||||
<kie.version>7.59.0.Final</kie.version>
|
||||
<locationtech.version>1.16.1</locationtech.version>
|
||||
<pdfbox.jbig2-imageio.version>3.0.3</pdfbox.jbig2-imageio.version>
|
||||
<jai-imageio.version>1.4.0</jai-imageio.version>
|
||||
<drools.version>7.68.0.Final</drools.version>
|
||||
<kie.version>7.68.0.Final</kie.version>
|
||||
<locationtech.version>1.18.2</locationtech.version>
|
||||
<javaassist.version>3.28.0-GA</javaassist.version>
|
||||
<ahocorasick.version>0.6.3</ahocorasick.version>
|
||||
<jackson.version>2.13.2</jackson.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.commons</groupId>
|
||||
<artifactId>jackson-commons</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.commons</groupId>
|
||||
<artifactId>storage-commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-afterburner</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ahocorasick</groupId>
|
||||
<artifactId>ahocorasick</artifactId>
|
||||
<version>${ahocorasick.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>${javaassist.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.service</groupId>
|
||||
<artifactId>redaction-service-api-v1</artifactId>
|
||||
@@ -54,22 +75,6 @@
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.pdfbox</groupId>
|
||||
<artifactId>jbig2-imageio</artifactId>
|
||||
<version>${pdfbox.jbig2-imageio.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jai-imageio</groupId>
|
||||
<artifactId>jai-imageio-core</artifactId>
|
||||
<version>${jai-imageio.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jai-imageio</groupId>
|
||||
<artifactId>jai-imageio-jpeg2000</artifactId>
|
||||
<version>${jai-imageio.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- commons -->
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.commons</groupId>
|
||||
@@ -118,6 +123,18 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessors>
|
||||
<annotationProcessor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</annotationProcessor>
|
||||
<annotationProcessor>com.dslplatform.json.processor.CompiledJsonAnnotationProcessor</annotationProcessor>
|
||||
</annotationProcessors>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<!-- generate git.properties for exposure in /info -->
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
|
||||
+9
@@ -9,8 +9,12 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import io.micrometer.core.aop.TimedAspect;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
|
||||
@Import({DefaultWebMvcConfiguration.class})
|
||||
@EnableFeignClients(basePackageClasses = RulesClient.class)
|
||||
@EnableConfigurationProperties(RedactionServiceSettings.class)
|
||||
@@ -22,5 +26,10 @@ public class Application {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TimedAspect timedAspect(MeterRegistry registry) {
|
||||
return new TimedAspect(registry);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -14,6 +15,7 @@ public class Footer {
|
||||
private List<TextBlock> textBlocks;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public SearchableText getSearchableText() {
|
||||
|
||||
SearchableText searchableText = new SearchableText();
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -14,6 +15,7 @@ public class Header {
|
||||
private List<TextBlock> textBlocks;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public SearchableText getSearchableText() {
|
||||
|
||||
SearchableText searchableText = new SearchableText();
|
||||
|
||||
+4
@@ -1,5 +1,7 @@
|
||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.iqser.red.service.redaction.v1.model.SectionArea;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.CellValue;
|
||||
@@ -14,6 +16,7 @@ import java.util.*;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@CompiledJson
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SectionText {
|
||||
@@ -38,6 +41,7 @@ public class SectionText {
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public SearchableText getSearchableText() {
|
||||
|
||||
SearchableText searchableText = new SearchableText();
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SimplifiedSectionText {
|
||||
|
||||
private int sectionNumber;
|
||||
private String text;
|
||||
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SimplifiedText {
|
||||
|
||||
private int numberOfPages;
|
||||
private List<SimplifiedSectionText> sectionTexts = new ArrayList<>();
|
||||
|
||||
}
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -8,6 +9,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Text {
|
||||
|
||||
+4
@@ -1,5 +1,7 @@
|
||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.TextNormalizationUtilities;
|
||||
@@ -15,6 +17,7 @@ import java.util.List;
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Data
|
||||
@CompiledJson
|
||||
@NoArgsConstructor
|
||||
public class TextBlock extends AbstractTextContainer {
|
||||
|
||||
@@ -121,6 +124,7 @@ public class TextBlock extends AbstractTextContainer {
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public String getText() {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.server.classification.model;
|
||||
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -14,6 +15,7 @@ public class UnclassifiedText {
|
||||
private List<TextBlock> textBlocks;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public SearchableText getSearchableText() {
|
||||
|
||||
SearchableText searchableText = new SearchableText();
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.server.client.model;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -7,6 +8,7 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@CompiledJson
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class EntityRecogintionEntity {
|
||||
|
||||
+3
-3
@@ -4,18 +4,18 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@CompiledJson
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class NerEntities {
|
||||
|
||||
@Builder.Default
|
||||
private Map<Integer, List<EntityRecogintionEntity>> data = new HashMap<>();
|
||||
|
||||
}
|
||||
|
||||
+1
-64
@@ -16,7 +16,6 @@ import com.iqser.red.service.redaction.v1.server.tableextraction.model.Table;
|
||||
import com.iqser.red.service.redaction.v1.server.visualization.service.PdfVisualisationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -33,42 +32,11 @@ public class RedactionController implements RedactionResource {
|
||||
|
||||
private final PdfVisualisationService pdfVisualisationService;
|
||||
private final DroolsExecutionService droolsExecutionService;
|
||||
private final DictionaryService dictionaryService;
|
||||
private final AnnotationService annotationService;
|
||||
private final PdfSegmentationService pdfSegmentationService;
|
||||
private final RedactionStorageService redactionStorageService;
|
||||
private final RedactionLogMergeService redactionLogMergeService;
|
||||
private final ManualRedactionSurroundingTextService manualRedactionSurroundingTextService;
|
||||
|
||||
|
||||
public AnnotateResponse annotate(@RequestBody AnnotateRequest annotateRequest) {
|
||||
|
||||
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(annotateRequest.getDossierId(), annotateRequest.getFileId(), FileType.ORIGIN));
|
||||
var mergedRedactionLog = getRedactionLog(RedactionRequest.builder()
|
||||
.fileId(annotateRequest.getFileId())
|
||||
.manualRedactions(annotateRequest.getManualRedactions())
|
||||
.dossierId(annotateRequest.getDossierId())
|
||||
.dossierTemplateId(annotateRequest.getDossierTemplateId())
|
||||
.build());
|
||||
var sectionsGrid = redactionStorageService.getSectionGrid(annotateRequest.getDossierId(), annotateRequest.getFileId());
|
||||
|
||||
try (PDDocument pdDocument = PDDocument.load(storedObjectStream, MemoryUsageSetting.setupTempFileOnly())) {
|
||||
pdDocument.setAllSecurityToBeRemoved(true);
|
||||
|
||||
dictionaryService.updateDictionary(annotateRequest.getDossierTemplateId(), annotateRequest.getDossierId());
|
||||
annotationService.annotate(pdDocument, mergedRedactionLog, sectionsGrid);
|
||||
|
||||
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
|
||||
pdDocument.save(byteArrayOutputStream);
|
||||
return AnnotateResponse.builder().document(byteArrayOutputStream.toByteArray()).build();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RedactionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RedactionResult classify(@RequestBody RedactionRequest redactionRequest) {
|
||||
|
||||
@@ -157,39 +125,8 @@ public class RedactionController implements RedactionResource {
|
||||
@Override
|
||||
public RedactionLog getRedactionLog(RedactionRequest redactionRequest) {
|
||||
|
||||
log.debug("Requested preview for: {}", redactionRequest);
|
||||
|
||||
var redactionLog = redactionStorageService.getRedactionLog(redactionRequest.getDossierId(), redactionRequest.getFileId());
|
||||
|
||||
if (redactionLog == null) {
|
||||
throw new NotFoundException("RedactionLog not present");
|
||||
}
|
||||
|
||||
log.info("Loaded redaction log with computationalVersion: {}", redactionLog.getAnalysisVersion());
|
||||
|
||||
SectionGrid sectionGrid = redactionStorageService.getSectionGrid(redactionRequest.getDossierId(), redactionRequest.getFileId());
|
||||
if (sectionGrid.getSections().isEmpty()) {
|
||||
|
||||
log.info("SectionGrid does not have headlines set. Computing headlines now!");
|
||||
var text = redactionStorageService.getText(redactionRequest.getDossierId(), redactionRequest.getFileId());
|
||||
|
||||
// enhance section grid with headline data
|
||||
for (var sectionText : text.getSectionTexts()) {
|
||||
sectionGrid.getSections()
|
||||
.add(new SectionGrid.SectionGridSection(sectionText.getSectionNumber(), sectionText.getHeadline(), sectionText.getSectionAreas()
|
||||
.stream()
|
||||
.map(SectionArea::getPage)
|
||||
.collect(Collectors.toSet()), sectionText.getSectionAreas()));
|
||||
}
|
||||
redactionStorageService.storeObject(redactionRequest.getDossierId(), redactionRequest.getFileId(), FileType.SECTION_GRID, sectionGrid);
|
||||
}
|
||||
|
||||
log.info("Loaded redaction log with computationalVersion: {}", redactionLog.getAnalysisVersion());
|
||||
var merged = redactionLogMergeService.mergeRedactionLogData(redactionLog, sectionGrid, redactionRequest.getManualRedactions(), redactionRequest.getExcludedPages(), redactionRequest.getTypes(), redactionRequest.getColors());
|
||||
|
||||
merged.getRedactionLogEntry().removeIf(e -> e.isFalsePositive() && !redactionRequest.isIncludeFalsePositives());
|
||||
|
||||
return merged;
|
||||
return redactionLogMergeService.provideRedactionLog(redactionRequest);
|
||||
}
|
||||
|
||||
|
||||
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package com.iqser.red.service.redaction.v1.server.memory;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
|
||||
@Slf4j
|
||||
public class MemoryStats {
|
||||
|
||||
|
||||
public static void printMemoryStats() {
|
||||
log.info("\n\n ------------------------------ \n" +
|
||||
" Used Memory: " + humanReadableByteCountBin(getUsedMemory()) + "\n" +
|
||||
" Free Memory: " + humanReadableByteCountBin(getFreeMemory()) + "\n" +
|
||||
" Total Memory: " + humanReadableByteCountBin(getTotalMemory()) + "\n" +
|
||||
" Max Memory: " + humanReadableByteCountBin(getMaxMemory()) + "\n" +
|
||||
"\n ------------------------------ \n");
|
||||
}
|
||||
|
||||
|
||||
public static String humanReadableByteCountBin(long bytes) {
|
||||
long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
|
||||
if (absB < 1024) {
|
||||
return bytes + " B";
|
||||
}
|
||||
long value = absB;
|
||||
CharacterIterator ci = new StringCharacterIterator("KMGTPE");
|
||||
for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
|
||||
value >>= 10;
|
||||
ci.next();
|
||||
}
|
||||
value *= Long.signum(bytes);
|
||||
return String.format("%.1f %ciB", value / 1024.0, ci.current());
|
||||
}
|
||||
|
||||
private static long getMaxMemory() {
|
||||
return Runtime.getRuntime().maxMemory();
|
||||
}
|
||||
|
||||
private static long getUsedMemory() {
|
||||
return getMaxMemory() - getFreeMemory();
|
||||
}
|
||||
|
||||
private static long getTotalMemory() {
|
||||
return Runtime.getRuntime().totalMemory();
|
||||
}
|
||||
|
||||
private static long getFreeMemory() {
|
||||
return Runtime.getRuntime().freeMemory();
|
||||
}
|
||||
}
|
||||
+16
-74
@@ -1,52 +1,26 @@
|
||||
package com.iqser.red.service.redaction.v1.server.parsing;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.apache.pdfbox.contentstream.operator.Operator;
|
||||
import org.apache.pdfbox.contentstream.operator.OperatorName;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColor;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColorN;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingColorSpace;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceCMYKColor;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceGrayColor;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetNonStrokingDeviceRGBColor;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetStrokingColor;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetStrokingColorN;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetStrokingColorSpace;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceCMYKColor;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceGrayColor;
|
||||
import org.apache.pdfbox.contentstream.operator.color.SetStrokingDeviceRGBColor;
|
||||
import org.apache.pdfbox.contentstream.operator.state.SetFlatness;
|
||||
import org.apache.pdfbox.contentstream.operator.state.SetLineCapStyle;
|
||||
import org.apache.pdfbox.contentstream.operator.state.SetLineDashPattern;
|
||||
import org.apache.pdfbox.contentstream.operator.state.SetLineJoinStyle;
|
||||
import org.apache.pdfbox.contentstream.operator.state.SetLineMiterLimit;
|
||||
import org.apache.pdfbox.contentstream.operator.state.SetLineWidth;
|
||||
import org.apache.pdfbox.contentstream.operator.state.SetRenderingIntent;
|
||||
import org.apache.pdfbox.contentstream.operator.text.SetFontAndSize;
|
||||
import org.apache.pdfbox.cos.COSBase;
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.cos.COSNumber;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.apache.pdfbox.text.TextPosition;
|
||||
import org.apache.pdfbox.util.Matrix;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.RedTextPosition;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Ruling;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.pdfbox.contentstream.operator.Operator;
|
||||
import org.apache.pdfbox.contentstream.operator.OperatorName;
|
||||
import org.apache.pdfbox.contentstream.operator.color.*;
|
||||
import org.apache.pdfbox.contentstream.operator.state.*;
|
||||
import org.apache.pdfbox.contentstream.operator.text.SetFontAndSize;
|
||||
import org.apache.pdfbox.cos.COSBase;
|
||||
import org.apache.pdfbox.cos.COSNumber;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.text.TextPosition;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
@@ -66,8 +40,6 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
private int minCharHeight;
|
||||
@Getter
|
||||
private int maxCharHeight;
|
||||
@Getter
|
||||
private List<PdfImage> images = new ArrayList<>();
|
||||
|
||||
private float path_x;
|
||||
private float path_y;
|
||||
@@ -183,9 +155,6 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
graphicsPath.clear();
|
||||
break;
|
||||
|
||||
// case OperatorName.DRAW_OBJECT:
|
||||
// processImageOperation(arguments);
|
||||
// break;
|
||||
|
||||
}
|
||||
|
||||
@@ -193,32 +162,6 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
}
|
||||
|
||||
|
||||
protected void processImageOperation(List<COSBase> arguments) {
|
||||
|
||||
try {
|
||||
COSName objectName = (COSName) arguments.get(0);
|
||||
PDXObject xobject = getResources().getXObject(objectName);
|
||||
if (xobject instanceof PDImageXObject) {
|
||||
PDImageXObject image = (PDImageXObject) xobject;
|
||||
Matrix ctmNew = getGraphicsState().getCurrentTransformationMatrix();
|
||||
|
||||
Rectangle2D rect = new Rectangle2D.Float(ctmNew.getTranslateX(), ctmNew.getTranslateY(), ctmNew.getScaleX(), ctmNew.getScaleY());
|
||||
|
||||
// Memory Hack - sofReference kills me
|
||||
FieldUtils.writeField(image, "cachedImageSubsampling", -1, true);
|
||||
|
||||
if (rect.getHeight() > 2 && rect.getWidth() > 2) {
|
||||
this.images.add(new PdfImage(image.getImage(), rect, pageNumber, image.getImage()
|
||||
.getColorModel()
|
||||
.hasAlpha()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Problem during image extraction: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private float floatValue(COSBase value) {
|
||||
|
||||
if (value instanceof COSNumber) {
|
||||
@@ -244,7 +187,7 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
rulings.addAll(path);
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
log.error("UnsupportedOperationException: " + getGraphicsState().getStrokingColor()
|
||||
log.debug("UnsupportedOperationException: " + getGraphicsState().getStrokingColor()
|
||||
.getColorSpace()
|
||||
.getName() + " or " + getGraphicsState().getNonStrokingColor()
|
||||
.getColorSpace()
|
||||
@@ -375,7 +318,6 @@ public class PDFLinesTextStripper extends PDFTextStripper {
|
||||
minCharHeight = Integer.MAX_VALUE;
|
||||
maxCharHeight = 0;
|
||||
textPositionSequences.clear();
|
||||
images = new ArrayList<>();
|
||||
rulings.clear();
|
||||
graphicsPath.clear();
|
||||
path_x = 0.0f;
|
||||
|
||||
+1
-1
@@ -1871,7 +1871,7 @@ public class PDFTextStripper extends LegacyPDFStreamEngine
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.error("Could not close BidiMirroring.txt ", e);
|
||||
LOG.debug("Could not close BidiMirroring.txt ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-4
@@ -1,14 +1,23 @@
|
||||
package com.iqser.red.service.redaction.v1.server.parsing.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.pdfbox.text.TextPosition;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@CompiledJson
|
||||
public class RedTextPosition {
|
||||
|
||||
private String textMatrix;
|
||||
@@ -26,14 +35,17 @@ public class RedTextPosition {
|
||||
|
||||
// not used in reanalysis
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
private float widthOfSpace;
|
||||
|
||||
// not used in reanalysis
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
private float fontSizeInPt;
|
||||
|
||||
// not used in reanalysis
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
private String fontName;
|
||||
|
||||
|
||||
|
||||
+118
-25
@@ -6,18 +6,25 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.pdfbox.text.TextPosition;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.iqser.red.service.redaction.v1.model.Point;
|
||||
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Data
|
||||
@Builder
|
||||
@CompiledJson
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonIgnoreProperties({"empty"})
|
||||
public class TextPositionSequence implements CharSequence {
|
||||
|
||||
@@ -112,6 +119,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getX1() {
|
||||
|
||||
if (textPositions.get(0).getRotation() == 90) {
|
||||
@@ -123,6 +131,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getX2() {
|
||||
|
||||
if (textPositions.get(0).getRotation() == 90) {
|
||||
@@ -135,6 +144,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getRotationAdjustedY() {
|
||||
|
||||
return textPositions.get(0).getY();
|
||||
@@ -142,6 +152,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getRotationAdjustedX() {
|
||||
|
||||
return textPositions.get(0).getXDirAdj();
|
||||
@@ -149,6 +160,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getY1() {
|
||||
|
||||
if (textPositions.get(0).getRotation() == 90) {
|
||||
@@ -160,6 +172,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getY2() {
|
||||
|
||||
if (textPositions.get(0).getRotation() == 90) {
|
||||
@@ -171,6 +184,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getTextHeight() {
|
||||
|
||||
return textPositions.get(0).getHeightDir() + 2;
|
||||
@@ -178,6 +192,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getHeight() {
|
||||
|
||||
return getY2() - getY1();
|
||||
@@ -185,6 +200,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getWidth() {
|
||||
|
||||
return getX2() - getX1();
|
||||
@@ -192,6 +208,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public String getFont() {
|
||||
|
||||
return textPositions.get(0).getFontName().toLowerCase().replaceAll(",bold", "").replaceAll(",italic", "");
|
||||
@@ -199,6 +216,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public String getFontStyle() {
|
||||
|
||||
String lowercaseFontName = textPositions.get(0).getFontName().toLowerCase();
|
||||
@@ -217,6 +235,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getFontSize() {
|
||||
|
||||
return textPositions.get(0).getFontSizeInPt();
|
||||
@@ -224,6 +243,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getSpaceWidth() {
|
||||
|
||||
return textPositions.get(0).getWidthOfSpace();
|
||||
@@ -231,6 +251,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public int getRotation() {
|
||||
|
||||
return textPositions.get(0).getRotation();
|
||||
@@ -238,9 +259,11 @@ public class TextPositionSequence implements CharSequence {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public Rectangle getRectangle() {
|
||||
|
||||
log.debug("Page: '{}', Word: '{}', Rotation: '{}', textRotation {}", page, toString(), textPositions.get(0).getRotation(), textPositions.get(0).getDir());
|
||||
log.debug("Page: '{}', Word: '{}', Rotation: '{}', textRotation {}", page, toString(), textPositions.get(0)
|
||||
.getRotation(), textPositions.get(0).getDir());
|
||||
|
||||
float height = getTextHeight();
|
||||
|
||||
@@ -249,26 +272,24 @@ public class TextPositionSequence implements CharSequence {
|
||||
float posYInit;
|
||||
float posYEnd;
|
||||
|
||||
if (textPositions.get(0).getRotation() == 90 && textPositions.get(0).getDir() != 0.0f) {
|
||||
posXEnd = textPositions.get(0).getYDirAdj() + 2;
|
||||
posYInit = getY1();
|
||||
posYEnd = textPositions.get(textPositions.size() - 1).getXDirAdj() - height + 4;
|
||||
if (textPositions.get(0).getRotation() == 0 && textPositions.get(0).getDir() == 90f) {
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 270 && textPositions.get(0).getDir() == 270.0f) {
|
||||
posYInit = textPositions.get(0).getPageHeight() - getX1();
|
||||
posYEnd = textPositions.get(0).getPageHeight() - getX2() - textPositions.get(0)
|
||||
.getWidth() - textPositions.get(textPositions.size() - 1).getWidth() - 1;
|
||||
posXInit = textPositions.get(0).getPageWidth() - textPositions.get(0).getYDirAdj() - 2;
|
||||
posXEnd = textPositions.get(0).getPageWidth() - textPositions.get(textPositions.size() - 1)
|
||||
.getYDirAdj() + height;
|
||||
posYInit = getX1();
|
||||
posYEnd = getX2() + textPositions.get(0).getWidthDirAdj() - textPositions.get(textPositions.size() - 1)
|
||||
.getWidthDirAdj() - 3;
|
||||
posXInit = textPositions.get(0).getYDirAdj() + 2;
|
||||
posXEnd = textPositions.get(textPositions.size() - 1).getYDirAdj() - height;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 270 && textPositions.get(0).getDir() == 0.0f) {
|
||||
posYInit = textPositions.get(0).getPageHeight() - textPositions.get(0).getYDirAdj() - 2;
|
||||
posYEnd = posYInit + 1;
|
||||
posXInit = textPositions.get(0).getXDirAdj();
|
||||
posXEnd = textPositions.get(textPositions.size() - 1).getXDirAdj() + textPositions.get(textPositions.size() - 1).getWidthDirAdj() + 0.1f;
|
||||
} else if (textPositions.get(0).getRotation() == 0 && textPositions.get(0).getDir() == 180f) {
|
||||
|
||||
posXInit = textPositions.get(0).getPageWidth() - getX1() + 1;
|
||||
posXEnd = textPositions.get(0).getPageWidth() - getX2() + textPositions.get(0)
|
||||
.getWidthDirAdj() - textPositions.get(textPositions.size() - 1).getWidthDirAdj() - 3;
|
||||
posYInit = textPositions.get(0).getYDirAdj() - height + 2;
|
||||
posYEnd = textPositions.get(textPositions.size() - 1).getYDirAdj() - height + 2;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 0 && textPositions.get(0).getDir() == 270f) {
|
||||
|
||||
posYInit = textPositions.get(0).getPageHeight() - getX1();
|
||||
posYEnd = textPositions.get(0).getPageHeight() - getX2() - textPositions.get(0)
|
||||
.getWidthDirAdj() - textPositions.get(textPositions.size() - 1).getWidthDirAdj() - 3;
|
||||
@@ -277,6 +298,7 @@ public class TextPositionSequence implements CharSequence {
|
||||
.getYDirAdj() + height;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 90 && textPositions.get(0).getDir() == 0.0f) {
|
||||
|
||||
posXInit = textPositions.get(textPositions.size() - 1)
|
||||
.getXDirAdj() + textPositions.get(textPositions.size() - 1).getHeightDir();
|
||||
posXEnd = textPositions.get(0).getXDirAdj();
|
||||
@@ -284,22 +306,93 @@ public class TextPositionSequence implements CharSequence {
|
||||
posYEnd = textPositions.get(0).getPageHeight() - textPositions.get(textPositions.size() - 1)
|
||||
.getYDirAdj() + 2;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 0 && textPositions.get(0).getDir() == 90f) {
|
||||
} else if (textPositions.get(0).getRotation() == 90 && textPositions.get(0).getDir() == 90.0f) {
|
||||
posXEnd = textPositions.get(0).getYDirAdj() + 2;
|
||||
posYInit = getY1();
|
||||
posYEnd = textPositions.get(textPositions.size() - 1).getXDirAdj() - height + 4;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 90 && textPositions.get(0).getDir() == 180.0f) {
|
||||
|
||||
posXInit = textPositions.get(0).getPageWidth() - textPositions.get(textPositions.size() - 1)
|
||||
.getXDirAdj() - 4;
|
||||
posXEnd = textPositions.get(0).getPageWidth() - textPositions.get(0).getXDirAdj();
|
||||
posYInit = textPositions.get(0).getYDirAdj() - 2 - textPositions.get(textPositions.size() - 1)
|
||||
.getHeightDir();
|
||||
posYEnd = textPositions.get(textPositions.size() - 1)
|
||||
.getYDirAdj() - textPositions.get(textPositions.size() - 1).getHeightDir();
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 90 && textPositions.get(0).getDir() == 270.0f) {
|
||||
|
||||
posXInit = textPositions.get(0).getPageWidth() - getX1();
|
||||
posXEnd = textPositions.get(0).getPageWidth() - textPositions.get(0).getYDirAdj() - 2;
|
||||
posYInit = textPositions.get(0).getPageHeight() - getY1();
|
||||
posYEnd = textPositions.get(0).getPageHeight() - textPositions.get(textPositions.size() - 1)
|
||||
.getXDirAdj() - height - 4;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 180 && textPositions.get(0).getDir() == 0f) {
|
||||
|
||||
posXEnd = textPositions.get(textPositions.size() - 1)
|
||||
.getXDirAdj() + textPositions.get(textPositions.size() - 1).getWidthDirAdj() + 1;
|
||||
posYInit = textPositions.get(0).getPageHeight() - textPositions.get(0).getYDirAdj() - 2;
|
||||
posYEnd = textPositions.get(0).getPageHeight() - textPositions.get(textPositions.size() - 1)
|
||||
.getYDirAdj() + 2;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 180 && textPositions.get(0).getDir() == 90f) {
|
||||
|
||||
posYInit = getX1();
|
||||
posYEnd = getX2() + textPositions.get(0).getWidthDirAdj() - textPositions.get(textPositions.size() - 1)
|
||||
.getWidthDirAdj() - 3;
|
||||
posYEnd = getX2() - 3;
|
||||
posXInit = textPositions.get(0).getYDirAdj() + 2;
|
||||
posXEnd = textPositions.get(textPositions.size() - 1).getYDirAdj() - height;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 180 && textPositions.get(0).getDir() == 180f) {
|
||||
posXInit = textPositions.get(0).getPageWidth() - getX1() +1;
|
||||
posXEnd = textPositions.get(0).getPageWidth() - getX2() + textPositions.get(0).getWidthDirAdj() - textPositions.get(textPositions.size() - 1)
|
||||
.getWidthDirAdj() - 3;
|
||||
|
||||
posXInit = textPositions.get(0).getPageWidth() - getX1() + 1;
|
||||
posXEnd = textPositions.get(0).getPageWidth() - getX2() + textPositions.get(0)
|
||||
.getWidthDirAdj() - textPositions.get(textPositions.size() - 1).getWidthDirAdj() - 3;
|
||||
posYInit = textPositions.get(0).getYDirAdj() - height + 2;
|
||||
posYEnd = textPositions.get(textPositions.size() - 1)
|
||||
.getYDirAdj() - height + 2;
|
||||
posYEnd = textPositions.get(textPositions.size() - 1).getYDirAdj() - height + 2;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 180 && textPositions.get(0).getDir() == 270.0f) {
|
||||
|
||||
posYInit = textPositions.get(0).getPageHeight() - getX1();
|
||||
posYEnd = textPositions.get(0).getPageHeight() - getX2() - textPositions.get(0)
|
||||
.getWidthDirAdj() - textPositions.get(textPositions.size() - 1).getWidthDirAdj();
|
||||
posXInit = textPositions.get(0).getPageWidth() - textPositions.get(0).getYDirAdj() - 2;
|
||||
posXEnd = textPositions.get(0).getPageWidth() - textPositions.get(textPositions.size() - 1)
|
||||
.getYDirAdj() + height;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 270 && textPositions.get(0).getDir() == 0.0f) {
|
||||
|
||||
posYInit = textPositions.get(0).getPageHeight() - textPositions.get(0).getYDirAdj() - 2;
|
||||
posYEnd = posYInit + 1;
|
||||
posXInit = textPositions.get(0).getXDirAdj();
|
||||
posXEnd = textPositions.get(textPositions.size() - 1)
|
||||
.getXDirAdj() + textPositions.get(textPositions.size() - 1).getWidthDirAdj() + 0.1f;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 270 && textPositions.get(0).getDir() == 90.0f) {
|
||||
|
||||
posYInit = getX1();
|
||||
posYEnd = getX2() - height;
|
||||
posXInit = textPositions.get(0).getYDirAdj() + 2;
|
||||
posXEnd = textPositions.get(textPositions.size() - 1).getYDirAdj() - height;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 270 && textPositions.get(0).getDir() == 180.0f) {
|
||||
|
||||
posXInit = textPositions.get(0).getPageWidth() - getX1() + 1;
|
||||
posXEnd = textPositions.get(0).getPageWidth() - getX2() - 4;
|
||||
posYInit = textPositions.get(0).getYDirAdj() - height + 2;
|
||||
posYEnd = textPositions.get(textPositions.size() - 1).getYDirAdj() - height + 2;
|
||||
|
||||
} else if (textPositions.get(0).getRotation() == 270 && textPositions.get(0).getDir() == 270.0f) {
|
||||
|
||||
posYInit = textPositions.get(0).getPageHeight() - getX1();
|
||||
posYEnd = textPositions.get(0).getPageHeight() - getX2() - height;
|
||||
posXInit = textPositions.get(0).getPageWidth() - textPositions.get(0).getYDirAdj() - 2;
|
||||
posXEnd = textPositions.get(0).getPageWidth() - textPositions.get(textPositions.size() - 1)
|
||||
.getYDirAdj() + height;
|
||||
|
||||
} else {
|
||||
// page rotation = 0 and text direction = 0
|
||||
posXEnd = textPositions.get(textPositions.size() - 1)
|
||||
.getXDirAdj() + textPositions.get(textPositions.size() - 1).getWidthDirAdj() + 1;
|
||||
posYInit = textPositions.get(0).getPageHeight() - textPositions.get(0).getYDirAdj() - 2;
|
||||
|
||||
-10
@@ -54,16 +54,6 @@ public class Dictionary {
|
||||
}
|
||||
|
||||
|
||||
public boolean containsValue(String type, String value) {
|
||||
|
||||
return localAccessMap.containsKey(type) && localAccessMap.get(type)
|
||||
.getValues(false)
|
||||
.contains(value) || localAccessMap.containsKey(type) && localAccessMap.get(type)
|
||||
.getValues(true)
|
||||
.contains(value);
|
||||
}
|
||||
|
||||
|
||||
public boolean isHint(String type) {
|
||||
|
||||
DictionaryModel model = localAccessMap.get(type);
|
||||
|
||||
+1
-1
@@ -23,4 +23,4 @@ public class DictionaryEntries {
|
||||
@Builder.Default
|
||||
Set<DictionaryEntry> falseRecommendations = new HashSet<>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+67
-19
@@ -2,10 +2,12 @@ package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntry;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.SearchImplementation;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -14,30 +16,76 @@ import java.util.stream.Collectors;
|
||||
@AllArgsConstructor
|
||||
public class DictionaryModel implements Serializable {
|
||||
|
||||
private String type;
|
||||
private int rank;
|
||||
private float[] color;
|
||||
private boolean caseInsensitive;
|
||||
private boolean hint;
|
||||
private Set<DictionaryEntry> entries;
|
||||
private Set<DictionaryEntry> falsePositives;
|
||||
private Set<DictionaryEntry> falseRecommendations;
|
||||
private Set<String> localEntries;
|
||||
private boolean isDossierDictionary;
|
||||
private final String type;
|
||||
private final int rank;
|
||||
private final float[] color;
|
||||
private final boolean caseInsensitive;
|
||||
private final boolean hint;
|
||||
private final boolean isDossierDictionary;
|
||||
private final Set<DictionaryEntry> entries;
|
||||
private final Set<DictionaryEntry> falsePositives;
|
||||
private final Set<DictionaryEntry> falseRecommendations;
|
||||
|
||||
private transient SearchImplementation entriesSearch;
|
||||
private transient SearchImplementation falsePositiveSearch;
|
||||
private transient SearchImplementation falseRecommendationsSearch;
|
||||
private transient SearchImplementation localSearch;
|
||||
|
||||
private final Set<String> localEntries = new HashSet<>();
|
||||
|
||||
public DictionaryModel(String type,
|
||||
int rank,
|
||||
float[] color,
|
||||
boolean caseInsensitive,
|
||||
boolean hint,
|
||||
Set<DictionaryEntry> entries,
|
||||
Set<DictionaryEntry> falsePositives,
|
||||
Set<DictionaryEntry> falseRecommendations,
|
||||
boolean isDossierDictionary) {
|
||||
this.type = type;
|
||||
this.rank = rank;
|
||||
this.color = color;
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
this.hint = hint;
|
||||
this.isDossierDictionary = isDossierDictionary;
|
||||
|
||||
this.entries = entries;
|
||||
this.falsePositives = falsePositives;
|
||||
this.falseRecommendations = falseRecommendations;
|
||||
|
||||
this.entriesSearch = new SearchImplementation(this.entries.stream().filter(e -> !e.isDeleted()).map(DictionaryEntry::getValue).collect(Collectors.toList()), caseInsensitive);
|
||||
this.falsePositiveSearch = new SearchImplementation(this.falsePositives.stream().filter(e -> !e.isDeleted()).map(DictionaryEntry::getValue).collect(Collectors.toList()), caseInsensitive);
|
||||
this.falseRecommendationsSearch = new SearchImplementation(this.falseRecommendations.stream().filter(e -> !e.isDeleted()).map(DictionaryEntry::getValue).collect(Collectors.toList()), caseInsensitive);
|
||||
|
||||
public Set<String> getValues(boolean local) {
|
||||
return local ? localEntries : entries.stream().filter(e -> !e.isDeleted()).map(e -> e.getValue()).collect(Collectors
|
||||
.toSet());
|
||||
}
|
||||
|
||||
public Set<String> getFalsePositiveValues() {
|
||||
return falsePositives.stream().filter(e -> !e.isDeleted()).map(e -> e.getValue()).collect(Collectors
|
||||
.toSet());
|
||||
public SearchImplementation getLocalSearch() {
|
||||
if (this.localSearch == null) {
|
||||
this.localSearch = new SearchImplementation(this.localEntries, caseInsensitive);
|
||||
}
|
||||
return this.localSearch;
|
||||
}
|
||||
|
||||
public Set<String> getFalseRecommendationValues() {
|
||||
return falseRecommendations.stream().filter(e -> !e.isDeleted()).map(e -> e.getValue()).collect(Collectors
|
||||
.toSet());
|
||||
public SearchImplementation getEntriesSearch() {
|
||||
if (entriesSearch == null) {
|
||||
this.entriesSearch = new SearchImplementation(this.entries.stream().filter(e -> !e.isDeleted()).map(DictionaryEntry::getValue).collect(Collectors.toList()), caseInsensitive);
|
||||
}
|
||||
return entriesSearch;
|
||||
}
|
||||
|
||||
public SearchImplementation getFalsePositiveSearch() {
|
||||
if (falsePositiveSearch == null) {
|
||||
this.falsePositiveSearch = new SearchImplementation(this.falsePositives.stream().filter(e -> !e.isDeleted()).map(DictionaryEntry::getValue).collect(Collectors.toList()), caseInsensitive);
|
||||
}
|
||||
return falsePositiveSearch;
|
||||
}
|
||||
|
||||
public SearchImplementation getFalseRecommendationsSearch() {
|
||||
if (falseRecommendationsSearch == null) {
|
||||
this.falseRecommendationsSearch = new SearchImplementation(this.falseRecommendations.stream().filter(e -> !e.isDeleted()).map(DictionaryEntry::getValue).collect(Collectors.toList()), caseInsensitive);
|
||||
}
|
||||
return falseRecommendationsSearch;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+14
-6
@@ -1,17 +1,23 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.model.Engine;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.model.Engine;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class Entity implements ReasonHolder {
|
||||
|
||||
@@ -50,6 +56,8 @@ public class Entity implements ReasonHolder {
|
||||
|
||||
private EntityType entityType;
|
||||
|
||||
private boolean resized;
|
||||
|
||||
|
||||
public Entity(String word, String type, boolean redaction, String redactionReason,
|
||||
List<EntityPositionSequence> positionSequences, String headline, int matchedRule, int sectionNumber,
|
||||
|
||||
+9
-6
@@ -1,15 +1,18 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@RequiredArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -7,6 +8,7 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@CompiledJson
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Image implements ReasonHolder {
|
||||
|
||||
+1
-14
@@ -1,34 +1,21 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
public class PdfImage {
|
||||
|
||||
@JsonIgnore
|
||||
private BufferedImage image;
|
||||
@NonNull
|
||||
private RedRectangle2D position;
|
||||
@NonNull
|
||||
private ImageType imageType;
|
||||
private boolean isAppendedToParagraph;
|
||||
@NonNull
|
||||
private boolean hasTransparency;
|
||||
|
||||
@NonNull
|
||||
private int page;
|
||||
|
||||
public PdfImage(BufferedImage image, Rectangle2D position, int page, boolean hasTransparency) {
|
||||
this.image = image;
|
||||
this.position = new RedRectangle2D(position.getX(), position.getY(), position.getWidth(), position.getHeight());
|
||||
this.page = page;
|
||||
this.hasTransparency = hasTransparency;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-1
@@ -1,12 +1,17 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@CompiledJson
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RedRectangle2D {
|
||||
@@ -19,6 +24,7 @@ public class RedRectangle2D {
|
||||
private double height;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public boolean isEmpty() {
|
||||
return width <= 0.0f || height <= 0.0f;
|
||||
}
|
||||
|
||||
+18
-12
@@ -1,16 +1,22 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.IdBuilder;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.SeparatorUtils;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.TextNormalizationUtilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SearchableText {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
private transient String stringRepresentation;
|
||||
private final List<TextPositionSequence> sequences = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -88,8 +94,8 @@ public class SearchableText {
|
||||
.get(i)
|
||||
.charAt(j, caseInsensitive) == '-') {
|
||||
|
||||
if (counter != 0 || i == 0 && j == 0 || j != 0 && isSeparator(searchSpace.get(i)
|
||||
.charAt(j - 1, caseInsensitive)) || j == 0 && i != 0 && isSeparator(searchSpace.get(i - 1)
|
||||
if (counter != 0 || i == 0 && j == 0 || j != 0 && SeparatorUtils.isSeparator(searchSpace.get(i)
|
||||
.charAt(j - 1, caseInsensitive)) || j == 0 && i != 0 && SeparatorUtils.isSeparator(searchSpace.get(i - 1)
|
||||
.charAt(searchSpace.get(i - 1)
|
||||
.length() - 1, caseInsensitive)) || j == 0 && i != 0 && searchSpace.get(i - 1)
|
||||
.charAt(searchSpace.get(i - 1).length() - 1, caseInsensitive) != ' ' && searchSpace.get(i)
|
||||
@@ -105,9 +111,9 @@ public class SearchableText {
|
||||
crossSequenceParts.add(partMatch);
|
||||
|
||||
if (i == searchSpace.size() - 1 && j == searchSpace.get(i)
|
||||
.length() - 1 || j != searchSpace.get(i).length() - 1 && isSeparator(searchSpace.get(i)
|
||||
.length() - 1 || j != searchSpace.get(i).length() - 1 && SeparatorUtils.isSeparator(searchSpace.get(i)
|
||||
.charAt(j + 1, caseInsensitive)) || j == searchSpace.get(i)
|
||||
.length() - 1 && isSeparator(searchSpace.get(i + 1)
|
||||
.length() - 1 && SeparatorUtils.isSeparator(searchSpace.get(i + 1)
|
||||
.charAt(0, caseInsensitive)) || j == searchSpace.get(i)
|
||||
.length() - 1 && searchSpace.get(i)
|
||||
.charAt(j, caseInsensitive) != ' ' && searchSpace.get(i + 1)
|
||||
@@ -175,18 +181,18 @@ public class SearchableText {
|
||||
}
|
||||
|
||||
|
||||
private boolean isSeparator(char c) {
|
||||
|
||||
return Character.isWhitespace(c) || Pattern.matches("\\p{Punct}", String.valueOf(c)) || c == '\"' || c == '‘' || c == '’';
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return buildString(sequences);
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
if (stringRepresentation == null) {
|
||||
stringRepresentation = buildString(sequences);
|
||||
}
|
||||
return stringRepresentation;
|
||||
}
|
||||
|
||||
|
||||
public String buildString(List<TextPositionSequence> sequences) {
|
||||
|
||||
|
||||
+300
-100
@@ -1,6 +1,5 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -19,12 +18,16 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.redaction.v1.model.ArgumentType;
|
||||
import com.iqser.red.service.redaction.v1.model.Engine;
|
||||
import com.iqser.red.service.redaction.v1.model.FileAttribute;
|
||||
import com.iqser.red.service.redaction.v1.model.SectionArea;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.EntitySearchUtils;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.FindEntityDetails;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.Patterns;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.SearchImplementation;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -68,13 +71,22 @@ public class Section {
|
||||
@Builder.Default
|
||||
private List<FileAttribute> fileAttributes = new ArrayList<>();
|
||||
|
||||
@Builder.Default
|
||||
private List<SectionArea> sectionAreas = new ArrayList<>();
|
||||
|
||||
public void addAiEntities(String type, String asType) {
|
||||
private ManualRedactions manualRedactions;
|
||||
|
||||
Set<Entity> entitiesOfType = nerEntities.stream().filter(nerEntity -> nerEntity.getType().equals(type)).collect(Collectors.toSet());
|
||||
Set<String> values = entitiesOfType.stream().map(Entity::getWord).collect(Collectors.toSet());
|
||||
Set<Entity> found = EntitySearchUtils.findEntities(searchText, values, dictionary.getType(asType), headline, sectionNumber, false, false, Engine.NER, true, true);
|
||||
EntitySearchUtils.clearAndFindPositions(found, searchableText, dictionary);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public void addAiEntities(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.TYPE) String asType) {
|
||||
|
||||
Set<Entity> entitiesOfType = nerEntities.stream()
|
||||
.filter(nerEntity -> nerEntity.getType().equals(type))
|
||||
.collect(Collectors.toSet());
|
||||
List<String> values = entitiesOfType.stream().map(Entity::getWord).collect(Collectors.toList());
|
||||
Set<Entity> found = EntitySearchUtils.findEntities(searchText, new SearchImplementation(values, dictionary.isCaseInsensitiveDictionary(asType)), dictionary.getType(asType), new FindEntityDetails(asType, headline, sectionNumber, false, false, Engine.NER, EntityType.RECOMMENDATION));
|
||||
EntitySearchUtils.clearAndFindPositions(found, searchableText, dictionary, manualRedactions);
|
||||
|
||||
Set<Entity> finalResult = new HashSet<>();
|
||||
|
||||
@@ -99,11 +111,20 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
public void combineAiTypes(String startType, String combineTypes, int maxDistanceBetween, String asType, int minPartMatches, boolean allowDuplicateTypes) {
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public void combineAiTypes(@Argument(ArgumentType.TYPE) String startType,
|
||||
@Argument(ArgumentType.TYPE) String combineTypes,
|
||||
@Argument(ArgumentType.INTEGER) int maxDistanceBetween,
|
||||
@Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.INTEGER) int minPartMatches,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean allowDuplicateTypes) {
|
||||
|
||||
Set<String> combineSet = Set.of(combineTypes.split(","));
|
||||
|
||||
List<Entity> sorted = nerEntities.stream().sorted(Comparator.comparing(Entity::getStart)).collect(Collectors.toList());
|
||||
List<Entity> sorted = nerEntities.stream()
|
||||
.sorted(Comparator.comparing(Entity::getStart))
|
||||
.collect(Collectors.toList());
|
||||
Set<Entity> found = new HashSet<>();
|
||||
int start = -1;
|
||||
int lastEnd = -1;
|
||||
@@ -161,49 +182,67 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean fileAttributeByIdEquals(@Argument(ArgumentType.FILE_ATTRIBUTE) String id, @Argument(ArgumentType.STRING) String value) {
|
||||
public boolean fileAttributeByIdEquals(@Argument(ArgumentType.FILE_ATTRIBUTE) String id,
|
||||
@Argument(ArgumentType.STRING) String value) {
|
||||
|
||||
return fileAttributes != null && fileAttributes.stream().anyMatch(attribute -> id.equals(attribute.getId()) && value.equals(attribute.getValue()));
|
||||
return fileAttributes != null && fileAttributes.stream()
|
||||
.anyMatch(attribute -> id.equals(attribute.getId()) && value.equals(attribute.getValue()));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean fileAttributeByPlaceholderEquals(@Argument(ArgumentType.FILE_ATTRIBUTE) String placeholder, @Argument(ArgumentType.STRING) String value) {
|
||||
public boolean fileAttributeByPlaceholderEquals(@Argument(ArgumentType.FILE_ATTRIBUTE) String placeholder,
|
||||
@Argument(ArgumentType.STRING) String value) {
|
||||
|
||||
return fileAttributes != null && fileAttributes.stream().anyMatch(attribute -> placeholder.equals(attribute.getPlaceholder()) && value.equals(attribute.getValue()));
|
||||
return fileAttributes != null && fileAttributes.stream()
|
||||
.anyMatch(attribute -> placeholder.equals(attribute.getPlaceholder()) && value.equals(attribute.getValue()));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean fileAttributeByLabelEquals(@Argument(ArgumentType.FILE_ATTRIBUTE) String label, @Argument(ArgumentType.STRING) String value) {
|
||||
public boolean fileAttributeByLabelEquals(@Argument(ArgumentType.FILE_ATTRIBUTE) String label,
|
||||
@Argument(ArgumentType.STRING) String value) {
|
||||
|
||||
return fileAttributes != null && fileAttributes.stream().anyMatch(attribute -> label.equals(attribute.getLabel()) && value.equals(attribute.getValue()));
|
||||
return fileAttributes != null && fileAttributes.stream()
|
||||
.anyMatch(attribute -> label.equals(attribute.getLabel()) && value.equals(attribute.getValue()));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean fileAttributeByIdEqualsIgnoreCase(@Argument(ArgumentType.FILE_ATTRIBUTE) String id, @Argument(ArgumentType.STRING) String value) {
|
||||
public boolean fileAttributeByIdEqualsIgnoreCase(@Argument(ArgumentType.FILE_ATTRIBUTE) String id,
|
||||
@Argument(ArgumentType.STRING) String value) {
|
||||
|
||||
return fileAttributes != null && fileAttributes.stream().anyMatch(attribute -> id.equals(attribute.getId()) && value.equalsIgnoreCase(attribute.getValue()));
|
||||
return fileAttributes != null && fileAttributes.stream()
|
||||
.anyMatch(attribute -> id.equals(attribute.getId()) && value.equalsIgnoreCase(attribute.getValue()));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean fileAttributeByPlaceholderEqualsIgnoreCase(@Argument(ArgumentType.FILE_ATTRIBUTE) String placeholder, @Argument(ArgumentType.STRING) String value) {
|
||||
public boolean fileAttributeByPlaceholderEqualsIgnoreCase(@Argument(ArgumentType.FILE_ATTRIBUTE) String placeholder,
|
||||
@Argument(ArgumentType.STRING) String value) {
|
||||
|
||||
return fileAttributes != null && fileAttributes.stream()
|
||||
.anyMatch(attribute -> placeholder.equals(attribute.getPlaceholder()) && value.equalsIgnoreCase(attribute.getValue()));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean fileAttributeByLabelEqualsIgnoreCase(@Argument(ArgumentType.FILE_ATTRIBUTE) String label, @Argument(ArgumentType.STRING) String value) {
|
||||
public boolean fileAttributeByLabelEqualsIgnoreCase(@Argument(ArgumentType.FILE_ATTRIBUTE) String label,
|
||||
@Argument(ArgumentType.STRING) String value) {
|
||||
|
||||
return fileAttributes != null && fileAttributes.stream().anyMatch(attribute -> label.equals(attribute.getLabel()) && value.equalsIgnoreCase(attribute.getValue()));
|
||||
return fileAttributes != null && fileAttributes.stream()
|
||||
.anyMatch(attribute -> label.equals(attribute.getLabel()) && value.equalsIgnoreCase(attribute.getValue()));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean hasTableHeader(@Argument(ArgumentType.STRING) String headerName) {
|
||||
|
||||
@@ -212,6 +251,7 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean aiMatchesType(@Argument(ArgumentType.TYPE) String type) {
|
||||
|
||||
@@ -219,6 +259,7 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean matchesType(@Argument(ArgumentType.TYPE) String type) {
|
||||
|
||||
@@ -226,6 +267,7 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean matchesImageType(@Argument(ArgumentType.TYPE) String type) {
|
||||
|
||||
@@ -233,6 +275,7 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean headlineContainsWord(@Argument(ArgumentType.STRING) String word) {
|
||||
|
||||
@@ -240,8 +283,10 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean containsRegEx(@Argument(ArgumentType.STRING) String regEx, @Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive){
|
||||
public boolean containsRegEx(@Argument(ArgumentType.STRING) String regEx,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive) {
|
||||
|
||||
var compiledPattern = Patterns.getCompiledPattern(regEx, patternCaseInsensitive);
|
||||
|
||||
@@ -251,34 +296,48 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@WhenCondition
|
||||
public boolean rowEquals(@Argument(ArgumentType.STRING) String headerName, @Argument(ArgumentType.STRING) String value) {
|
||||
public boolean rowEquals(@Argument(ArgumentType.STRING) String headerName,
|
||||
@Argument(ArgumentType.STRING) String value) {
|
||||
|
||||
String cleanHeaderName = headerName.replaceAll("\n", "").replaceAll(" ", "").replaceAll("-", "");
|
||||
|
||||
return tabularData != null && tabularData.containsKey(cleanHeaderName) && tabularData.get(cleanHeaderName).toString().equals(value);
|
||||
return tabularData != null && tabularData.containsKey(cleanHeaderName) && tabularData.get(cleanHeaderName)
|
||||
.toString()
|
||||
.equals(value);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void expandByPrefixRegEx(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.REGEX) String prefixPattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive, @Argument(ArgumentType.INTEGER) int group) {
|
||||
@SuppressWarnings("unused")
|
||||
public void expandByPrefixRegEx(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.REGEX) String prefixPattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group) {
|
||||
|
||||
expandByPrefixRegEx(type, prefixPattern, patternCaseInsensitive, group, null);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void expandByPrefixRegEx(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.REGEX) String prefixPattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive, @Argument(ArgumentType.INTEGER) int group,
|
||||
@Argument(ArgumentType.REGEX) String valuePattern) {
|
||||
@SuppressWarnings("unused")
|
||||
public void expandByPrefixRegEx(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.REGEX) String prefixPattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group,
|
||||
@Argument(ArgumentType.REGEX) String valuePattern) {
|
||||
|
||||
if (StringUtils.isEmpty(prefixPattern)) return;
|
||||
if (StringUtils.isEmpty(prefixPattern)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var compiledValuePattern = valuePattern == null ? null : Patterns.getCompiledPattern(valuePattern, patternCaseInsensitive);
|
||||
var compiledPrefixPattern = Patterns.getCompiledPattern(prefixPattern, patternCaseInsensitive);
|
||||
|
||||
Set<Entity> expanded = new HashSet<>();
|
||||
for (var entity : entities) {
|
||||
System.out.println(entity.getWord());
|
||||
|
||||
if (!entity.getType().equals(type) || entity.getTextBefore() == null) {
|
||||
continue;
|
||||
@@ -315,19 +374,27 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void expandByRegEx(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.REGEX) String suffixPattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive, @Argument(ArgumentType.INTEGER) int group) {
|
||||
@SuppressWarnings("unused")
|
||||
public void expandByRegEx(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.REGEX) String suffixPattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group) {
|
||||
|
||||
expandByRegEx(type, suffixPattern, patternCaseInsensitive, group, null);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void expandByRegEx(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.REGEX) String suffixPattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive, @Argument(ArgumentType.INTEGER) int group,
|
||||
@SuppressWarnings("unused")
|
||||
public void expandByRegEx(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.REGEX) String suffixPattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group,
|
||||
@Argument(ArgumentType.REGEX) String valuePattern) {
|
||||
|
||||
if (StringUtils.isEmpty(suffixPattern)) return;
|
||||
if (StringUtils.isEmpty(suffixPattern)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var compiledValuePattern = valuePattern == null ? null : Patterns.getCompiledPattern(valuePattern, patternCaseInsensitive);
|
||||
var compiledSuffixPattern = Patterns.getCompiledPattern(suffixPattern, patternCaseInsensitive);
|
||||
@@ -370,7 +437,10 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactImage(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.STRING) String reason,
|
||||
@SuppressWarnings("unused")
|
||||
public void redactImage(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
redactImage(type, ruleNumber, reason, legalBasis, true);
|
||||
@@ -378,14 +448,19 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNotImage(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.STRING) String reason) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNotImage(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
redactImage(type, ruleNumber, reason, null, false);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redact(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.STRING) String reason,
|
||||
@SuppressWarnings("unused")
|
||||
public void redact(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
redact(type, ruleNumber, reason, legalBasis, true);
|
||||
@@ -393,15 +468,20 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNot(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.STRING) String reason) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNot(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
redact(type, ruleNumber, reason, null, false);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactLineAfter(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.TYPE) String asType, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean redactEverywhere, @Argument(ArgumentType.STRING) String reason,
|
||||
@SuppressWarnings("unused")
|
||||
public void redactLineAfter(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
redactLineAfter(start, asType, ruleNumber, redactEverywhere, reason, legalBasis, true);
|
||||
@@ -409,8 +489,12 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNotLineAfter(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.TYPE) String asType, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean redactEverywhere, @Argument(ArgumentType.STRING) String reason) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNotLineAfter(@Argument(ArgumentType.STRING) String start,
|
||||
@Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
redactLineAfter(start, asType, ruleNumber, redactEverywhere, reason, null, false);
|
||||
|
||||
@@ -418,17 +502,24 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactByRegEx(@Argument(ArgumentType.REGEX) String pattern, @Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group, @Argument(ArgumentType.TYPE) String asType, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason, @Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactByRegEx(@Argument(ArgumentType.REGEX) String pattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
redactByRegEx(pattern, patternCaseInsensitive, group, asType, ruleNumber, reason, legalBasis, true);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNotByRegEx(@Argument(ArgumentType.REGEX) String pattern, @Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group, @Argument(ArgumentType.TYPE) String asType, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNotByRegEx(@Argument(ArgumentType.REGEX) String pattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
redactByRegEx(pattern, patternCaseInsensitive, group, asType, ruleNumber, reason, null, false);
|
||||
@@ -436,17 +527,24 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactBetween(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.STRING) String stop, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@Argument(ArgumentType.STRING) String reason, @Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactBetween(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.STRING) String stop,
|
||||
@Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
redactBetween(start, stop, asType, ruleNumber, redactEverywhere, reason, legalBasis, true);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNotBetween(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.STRING) String stop, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNotBetween(@Argument(ArgumentType.STRING) String start,
|
||||
@Argument(ArgumentType.STRING) String stop, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
redactBetween(start, stop, asType, ruleNumber, redactEverywhere, reason, null, false);
|
||||
@@ -454,17 +552,26 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactLinesBetween(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.STRING) String stop, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@Argument(ArgumentType.STRING) String reason, @Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactLinesBetween(@Argument(ArgumentType.STRING) String start,
|
||||
@Argument(ArgumentType.STRING) String stop,
|
||||
@Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
redactLinesBetween(start, stop, asType, ruleNumber, redactEverywhere, reason, legalBasis, true);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNotLinesBetween(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.STRING) String stop, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNotLinesBetween(@Argument(ArgumentType.STRING) String start,
|
||||
@Argument(ArgumentType.STRING) String stop,
|
||||
@Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean redactEverywhere,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
redactLinesBetween(start, stop, asType, ruleNumber, redactEverywhere, reason, null, false);
|
||||
@@ -472,8 +579,11 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactCell(@Argument(ArgumentType.STRING) String cellHeader, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean addAsRecommendations, @Argument(ArgumentType.STRING) String reason,
|
||||
@SuppressWarnings("unused")
|
||||
public void redactCell(@Argument(ArgumentType.STRING) String cellHeader,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean addAsRecommendations,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
annotateCell(cellHeader, ruleNumber, type, true, addAsRecommendations, reason, legalBasis);
|
||||
@@ -481,34 +591,50 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNotCell(@Argument(ArgumentType.STRING) String cellHeader, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean addAsRecommendations, @Argument(ArgumentType.STRING) String reason) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNotCell(@Argument(ArgumentType.STRING) String cellHeader,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean addAsRecommendations,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
annotateCell(cellHeader, ruleNumber, type, false, addAsRecommendations, reason, null);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactAndRecommendByRegEx(@Argument(ArgumentType.REGEX) String pattern, @Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group, @Argument(ArgumentType.TYPE) String asType, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason, @Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactAndRecommendByRegEx(@Argument(ArgumentType.REGEX) String pattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group,
|
||||
@Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
redactAndRecommendByRegEx(pattern, patternCaseInsensitive, group, asType, ruleNumber, reason, legalBasis, true);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNotAndRecommendByRegEx(@Argument(ArgumentType.REGEX) String pattern, @Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.STRING) String reason) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNotAndRecommendByRegEx(@Argument(ArgumentType.REGEX) String pattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group,
|
||||
@Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
redactAndRecommendByRegEx(pattern, patternCaseInsensitive, group, asType, ruleNumber, reason, null, false);
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void addRecommendationByRegEx(@Argument(ArgumentType.REGEX) String pattern, @Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group, @Argument(ArgumentType.TYPE) String asType) {
|
||||
@SuppressWarnings("unused")
|
||||
public void addRecommendationByRegEx(@Argument(ArgumentType.REGEX) String pattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group,
|
||||
@Argument(ArgumentType.TYPE) String asType) {
|
||||
|
||||
Pattern compiledPattern = Patterns.getCompiledPattern(pattern, patternCaseInsensitive);
|
||||
|
||||
@@ -524,11 +650,15 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactNotAndReference(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.REFERENCE_TYPE) String referenceType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.STRING) String reason) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactNotAndReference(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.REFERENCE_TYPE) String referenceType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason) {
|
||||
|
||||
|
||||
Set<Entity> references = entities.stream().filter(entity -> entity.getType().equals(referenceType)).collect(Collectors.toSet());
|
||||
Set<Entity> references = entities.stream()
|
||||
.filter(entity -> entity.getType().equals(referenceType))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
entities.forEach(entity -> {
|
||||
if (entity.getType().equals(type)) {
|
||||
@@ -542,8 +672,12 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void redactIfPrecededBy(@Argument(ArgumentType.STRING) String prefix, @Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason, @Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
@SuppressWarnings("unused")
|
||||
public void redactIfPrecededBy(@Argument(ArgumentType.STRING) String prefix,
|
||||
@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
entities.forEach(entity -> {
|
||||
if (entity.getType().equals(type) && searchText.indexOf(prefix + entity.getWord()) != 1) {
|
||||
@@ -557,29 +691,40 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void addRedaction(@Argument(ArgumentType.STRING) String value, @Argument(ArgumentType.TYPE) String asType, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason, @Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
@SuppressWarnings("unused")
|
||||
public void addRedaction(@Argument(ArgumentType.STRING) String value, @Argument(ArgumentType.TYPE) String asType,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
Set<Entity> found = findEntities(value.trim(), asType, true, true, ruleNumber, reason, legalBasis, Engine.RULE, false);
|
||||
EntitySearchUtils.addEntitiesIgnoreRank(entities, found);
|
||||
}
|
||||
|
||||
|
||||
public void ignore(String type) {
|
||||
@ThenAction
|
||||
@SuppressWarnings("unused")
|
||||
public void ignore(@Argument(ArgumentType.TYPE) String type) {
|
||||
|
||||
entities.removeIf(entity -> entity.getType().equals(type) && entity.getEntityType().equals(EntityType.ENTITY));
|
||||
}
|
||||
|
||||
|
||||
public void ignoreRecommendations(String type) {
|
||||
@ThenAction
|
||||
@SuppressWarnings("unused")
|
||||
public void ignoreRecommendations(@Argument(ArgumentType.TYPE) String type) {
|
||||
|
||||
entities.removeIf(entity -> entity.getType().equals(type) && entity.getEntityType().equals(EntityType.RECOMMENDATION));
|
||||
entities.removeIf(entity -> entity.getType().equals(type) && entity.getEntityType()
|
||||
.equals(EntityType.RECOMMENDATION));
|
||||
}
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void expandToFalsePositiveByRegEx(@Argument(ArgumentType.TYPE) String type, @Argument(ArgumentType.STRING) String pattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive, @Argument(ArgumentType.INTEGER) int group) {
|
||||
@SuppressWarnings("unused")
|
||||
public void expandToFalsePositiveByRegEx(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.STRING) String pattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group) {
|
||||
|
||||
Pattern compiledPattern = Patterns.getCompiledPattern(pattern, patternCaseInsensitive);
|
||||
|
||||
@@ -607,8 +752,11 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void addHintAnnotationByRegEx(@Argument(ArgumentType.REGEX) String pattern, @Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group, @Argument(ArgumentType.TYPE) String asType) {
|
||||
@SuppressWarnings("unused")
|
||||
public void addHintAnnotationByRegEx(@Argument(ArgumentType.REGEX) String pattern,
|
||||
@Argument(ArgumentType.BOOLEAN) boolean patternCaseInsensitive,
|
||||
@Argument(ArgumentType.INTEGER) int group,
|
||||
@Argument(ArgumentType.TYPE) String asType) {
|
||||
|
||||
Pattern compiledPattern = Patterns.getCompiledPattern(pattern, patternCaseInsensitive);
|
||||
|
||||
@@ -625,7 +773,9 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void addHintAnnotation(@Argument(ArgumentType.STRING) String value, @Argument(ArgumentType.TYPE) String asType) {
|
||||
@SuppressWarnings("unused")
|
||||
public void addHintAnnotation(@Argument(ArgumentType.STRING) String value,
|
||||
@Argument(ArgumentType.TYPE) String asType) {
|
||||
|
||||
Set<Entity> found = findEntities(value.trim(), asType, true, false, 0, null, null, Engine.RULE, false);
|
||||
EntitySearchUtils.addEntitiesIgnoreRank(entities, found);
|
||||
@@ -633,7 +783,9 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void recommendLineAfter(@Argument(ArgumentType.STRING) String start, @Argument(ArgumentType.TYPE) String asType) {
|
||||
@SuppressWarnings("unused")
|
||||
public void recommendLineAfter(@Argument(ArgumentType.STRING) String start,
|
||||
@Argument(ArgumentType.TYPE) String asType) {
|
||||
|
||||
String[] values = StringUtils.substringsBetween(text, start, "\n");
|
||||
|
||||
@@ -657,14 +809,54 @@ public class Section {
|
||||
|
||||
|
||||
@ThenAction
|
||||
public void highlightCell(@Argument(ArgumentType.STRING) String cellHeader, @Argument(ArgumentType.RULE_NUMBER) int ruleNumber, @Argument(ArgumentType.TYPE) String type) {
|
||||
@SuppressWarnings("unused")
|
||||
public void highlightCell(@Argument(ArgumentType.STRING) String cellHeader,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.TYPE) String type) {
|
||||
|
||||
annotateCell(cellHeader, ruleNumber, type, false, false, null, null);
|
||||
}
|
||||
|
||||
|
||||
private void redactAndRecommendByRegEx(String pattern, boolean patternCaseInsensitive, int group, String asType, int ruleNumber, String reason, String legalBasis,
|
||||
boolean redaction) {
|
||||
@ThenAction
|
||||
@SuppressWarnings("unused")
|
||||
public void redactSection(@Argument(ArgumentType.TYPE) String type,
|
||||
@Argument(ArgumentType.RULE_NUMBER) int ruleNumber,
|
||||
@Argument(ArgumentType.STRING) String reason,
|
||||
@Argument(ArgumentType.LEGAL_BASIS) String legalBasis) {
|
||||
|
||||
for (SectionArea sectionArea : sectionAreas) {
|
||||
|
||||
RedRectangle2D position = RedRectangle2D.builder()
|
||||
.height(sectionArea.getHeight() + 4)
|
||||
.width(sectionArea.getWidth() + 4)
|
||||
.x(sectionArea.getTopLeft().getX() - 2)
|
||||
.y(sectionArea.getTopLeft().getY() - 2)
|
||||
.build();
|
||||
|
||||
log.debug("SectionArea: {}", sectionArea);
|
||||
log.debug("Position {}", position.toString());
|
||||
|
||||
Image image = Image.builder()
|
||||
.page(sectionArea.getPage())
|
||||
.position(position)
|
||||
.redaction(true)
|
||||
.hasTransparency(false)
|
||||
.sectionNumber(sectionNumber)
|
||||
.section(headline)
|
||||
.matchedRule(ruleNumber)
|
||||
.legalBasis(legalBasis)
|
||||
.redactionReason(reason)
|
||||
.type(type)
|
||||
.build();
|
||||
|
||||
images.add(image);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void redactAndRecommendByRegEx(String pattern, boolean patternCaseInsensitive, int group, String asType,
|
||||
int ruleNumber, String reason, String legalBasis, boolean redaction) {
|
||||
|
||||
Pattern compiledPattern = Patterns.getCompiledPattern(pattern, patternCaseInsensitive);
|
||||
Matcher matcher = compiledPattern.matcher(searchText);
|
||||
@@ -679,13 +871,12 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
private Set<Entity> findEntities(String value, String asType, boolean caseInsensitive, boolean redacted, int ruleNumber, String reason, String legalBasis, Engine engine, boolean asRecommendation) {
|
||||
private Set<Entity> findEntities(String value, String asType, boolean caseInsensitive, boolean redacted,
|
||||
int ruleNumber, String reason, String legalBasis, Engine engine,
|
||||
boolean asRecommendation) {
|
||||
|
||||
String text = caseInsensitive ? searchText.toLowerCase() : searchText;
|
||||
String searchValue = caseInsensitive ? value.toLowerCase() : value;
|
||||
|
||||
Set<Entity> found = EntitySearchUtils.findEntities(text, Set.of(searchValue), dictionary.getType(asType), headline, sectionNumber, false, false, engine, false, asRecommendation);
|
||||
|
||||
Set<Entity> found = EntitySearchUtils.findEntities(text, new SearchImplementation(value, caseInsensitive), dictionary.getType(asType), new FindEntityDetails(asType, headline, sectionNumber, false, false, engine, asRecommendation ? EntityType.RECOMMENDATION : EntityType.ENTITY));
|
||||
found.forEach(entity -> {
|
||||
if (redacted) {
|
||||
entity.setRedaction(true);
|
||||
@@ -695,10 +886,13 @@ public class Section {
|
||||
}
|
||||
});
|
||||
|
||||
return EntitySearchUtils.clearAndFindPositions(found, searchableText, dictionary);
|
||||
return EntitySearchUtils.clearAndFindPositions(found, searchableText, dictionary, manualRedactions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void redact(String type, int ruleNumber, String reason, String legalBasis, boolean redaction) {
|
||||
|
||||
entities.forEach(entity -> {
|
||||
@@ -725,7 +919,8 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
private void annotateCell(String cellHeader, int ruleNumber, String type, boolean redact, boolean addAsRecommendations, String reason, String legalBasis) {
|
||||
private void annotateCell(String cellHeader, int ruleNumber, String type, boolean redact,
|
||||
boolean addAsRecommendations, String reason, String legalBasis) {
|
||||
|
||||
String cleanHeaderName = cellHeader.replaceAll("\n", "").replaceAll(" ", "").replaceAll("-", "");
|
||||
|
||||
@@ -748,7 +943,8 @@ public class Section {
|
||||
|
||||
Set<Entity> singleEntitySet = new HashSet<>();
|
||||
singleEntitySet.add(entity);
|
||||
EntitySearchUtils.clearAndFindPositions(singleEntitySet, searchableText, dictionary);
|
||||
|
||||
EntitySearchUtils.clearAndFindPositions(singleEntitySet, searchableText, dictionary, manualRedactions);
|
||||
|
||||
EntitySearchUtils.addEntitiesWithHigherRank(entities, entity, dictionary);
|
||||
|
||||
@@ -772,7 +968,8 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
private void redactLineAfter(String start, String asType, int ruleNumber, boolean redactEverywhere, String reason, String legalBasis, boolean redaction) {
|
||||
private void redactLineAfter(String start, String asType, int ruleNumber, boolean redactEverywhere, String reason,
|
||||
String legalBasis, boolean redaction) {
|
||||
|
||||
String[] values = StringUtils.substringsBetween(text, start, "\n");
|
||||
|
||||
@@ -791,7 +988,8 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
private void redactByRegEx(String pattern, boolean patternCaseInsensitive, int group, String asType, int ruleNumber, String reason, String legalBasis, boolean redaction) {
|
||||
private void redactByRegEx(String pattern, boolean patternCaseInsensitive, int group, String asType, int ruleNumber,
|
||||
String reason, String legalBasis, boolean redaction) {
|
||||
|
||||
Pattern compiledPattern = Patterns.getCompiledPattern(pattern, patternCaseInsensitive);
|
||||
|
||||
@@ -807,7 +1005,8 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
private void redactBetween(String start, String stop, String asType, int ruleNumber, boolean redactEverywhere, String reason, String legalBasis, boolean redaction) {
|
||||
private void redactBetween(String start, String stop, String asType, int ruleNumber, boolean redactEverywhere,
|
||||
String reason, String legalBasis, boolean redaction) {
|
||||
|
||||
String[] values = StringUtils.substringsBetween(searchText, start, stop);
|
||||
|
||||
@@ -827,7 +1026,8 @@ public class Section {
|
||||
}
|
||||
|
||||
|
||||
private void redactLinesBetween(String start, String stop, String asType, int ruleNumber, boolean redactEverywhere, String reason, String legalBasis, boolean redaction) {
|
||||
private void redactLinesBetween(String start, String stop, String asType, int ruleNumber, boolean redactEverywhere,
|
||||
String reason, String legalBasis, boolean redaction) {
|
||||
|
||||
String[] values = StringUtils.substringsBetween(text, start, stop);
|
||||
|
||||
|
||||
+3
@@ -1,5 +1,7 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -9,5 +11,6 @@ public class SectionSearchableTextPair {
|
||||
|
||||
private Section section;
|
||||
private SearchableText searchableText;
|
||||
private List<Integer> cellStarts;
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -3,9 +3,11 @@ package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class Classification {
|
||||
|
||||
private Map<String, Float> probabilities = new HashMap<>();
|
||||
|
||||
+2
@@ -1,8 +1,10 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class FilterGeometry {
|
||||
|
||||
private ImageSize imageSize;
|
||||
|
||||
+2
@@ -1,8 +1,10 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class Filters {
|
||||
|
||||
private FilterGeometry geometry;
|
||||
|
||||
+3
-1
@@ -1,9 +1,11 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class Geometry {
|
||||
private float width;
|
||||
private float height;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -1,8 +1,10 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class ImageFormat {
|
||||
|
||||
private float quotient;
|
||||
|
||||
+3
@@ -1,12 +1,15 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class ImageMetadata {
|
||||
|
||||
private Classification classification;
|
||||
private Position position;
|
||||
private Geometry geometry;
|
||||
private Filters filters;
|
||||
private boolean alpha;
|
||||
}
|
||||
|
||||
+8
-5
@@ -1,14 +1,16 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class ImageServiceResponse {
|
||||
|
||||
private String dossierId;
|
||||
@@ -16,6 +18,7 @@ public class ImageServiceResponse {
|
||||
|
||||
@JsonProperty(value = "imageMetadata")
|
||||
@JsonAlias("data")
|
||||
@JsonAttribute(alternativeNames = {"imageMetadata"})
|
||||
private List<ImageMetadata> data = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -1,8 +1,10 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class ImageSize {
|
||||
|
||||
private float quotient;
|
||||
|
||||
+3
-1
@@ -1,12 +1,14 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class Position {
|
||||
private float x1;
|
||||
private float x2;
|
||||
private float y1;
|
||||
private float y2;
|
||||
private int pageNumber;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -1,8 +1,10 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.model.image;
|
||||
|
||||
import com.dslplatform.json.CompiledJson;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@CompiledJson
|
||||
public class Probability {
|
||||
|
||||
private boolean unconfident;
|
||||
|
||||
+37
-52
@@ -1,59 +1,42 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.service;
|
||||
|
||||
import static com.iqser.red.service.redaction.v1.server.redaction.service.ImportedRedactionService.IMPORTED_REDACTION_TYPE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.kie.api.runtime.KieContainer;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.IdRemoval;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualForceRedaction;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualImageRecategorization;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualLegalBasisChange;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualResizeRedaction;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
|
||||
import com.iqser.red.service.redaction.v1.model.AnalyzeRequest;
|
||||
import com.iqser.red.service.redaction.v1.model.AnalyzeResult;
|
||||
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||
import com.iqser.red.service.redaction.v1.model.RedactionLog;
|
||||
import com.iqser.red.service.redaction.v1.model.RedactionLogEntry;
|
||||
import com.iqser.red.service.redaction.v1.model.SectionArea;
|
||||
import com.iqser.red.service.redaction.v1.model.SectionGrid;
|
||||
import com.iqser.red.service.redaction.v1.model.StructureAnalyzeRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.legalbasis.LegalBasis;
|
||||
import com.iqser.red.service.redaction.v1.model.*;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.SectionText;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.SimplifiedSectionText;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.SimplifiedText;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Text;
|
||||
import com.iqser.red.service.redaction.v1.server.client.LegalBasisClient;
|
||||
import com.iqser.red.service.redaction.v1.server.client.model.NerEntities;
|
||||
import com.iqser.red.service.redaction.v1.server.exception.RedactionException;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Dictionary;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryIncrement;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryVersion;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Image;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.PageEntities;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.RedRectangle2D;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.*;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.EntitySearchUtils;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.SearchImplementation;
|
||||
import com.iqser.red.service.redaction.v1.server.segmentation.ImageService;
|
||||
import com.iqser.red.service.redaction.v1.server.segmentation.PdfSegmentationService;
|
||||
import com.iqser.red.service.redaction.v1.server.settings.RedactionServiceSettings;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.kie.api.runtime.KieContainer;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.iqser.red.service.redaction.v1.server.redaction.service.ImportedRedactionService.IMPORTED_REDACTION_TYPE;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -75,6 +58,7 @@ public class AnalyzeService {
|
||||
private final ImportedRedactionService importedRedactionService;
|
||||
|
||||
|
||||
@Timed("redactmanager_analyzeDocumentStructure")
|
||||
public AnalyzeResult analyzeDocumentStructure(StructureAnalyzeRequest analyzeRequest) {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
@@ -96,6 +80,7 @@ public class AnalyzeService {
|
||||
}
|
||||
|
||||
List<SectionText> sectionTexts = sectionTextBuilderService.buildSectionText(classifiedDoc);
|
||||
|
||||
sectionGridCreatorService.createSectionGrid(classifiedDoc, pageCount);
|
||||
|
||||
Text text = new Text(pageCount, sectionTexts);
|
||||
@@ -108,9 +93,8 @@ public class AnalyzeService {
|
||||
.map(SectionArea::getPage)
|
||||
.collect(Collectors.toSet()), sectionText.getSectionAreas())));
|
||||
|
||||
log.info("Store text, simplified text and section grid for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
|
||||
log.info("Store text and section grid for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
|
||||
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.TEXT, text);
|
||||
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.SIMPLIFIED_TEXT, convert(text));
|
||||
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.SECTION_GRID, classifiedDoc.getSectionGrid());
|
||||
|
||||
return AnalyzeResult.builder()
|
||||
@@ -123,6 +107,7 @@ public class AnalyzeService {
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_reanalyze")
|
||||
@SneakyThrows
|
||||
public AnalyzeResult reanalyze(@RequestBody AnalyzeRequest analyzeRequest) {
|
||||
|
||||
@@ -149,7 +134,7 @@ public class AnalyzeService {
|
||||
if (redactionServiceSettings.isNerServiceEnabled()) {
|
||||
nerEntities = redactionStorageService.getNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
|
||||
} else {
|
||||
nerEntities = NerEntities.builder().build();
|
||||
nerEntities = new NerEntities();
|
||||
}
|
||||
|
||||
List<SectionText> reanalysisSections = text.getSectionTexts()
|
||||
@@ -162,6 +147,7 @@ public class AnalyzeService {
|
||||
Dictionary dictionary = dictionaryService.getDeepCopyDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
|
||||
|
||||
PageEntities pageEntities = entityRedactionService.findEntities(dictionary, reanalysisSections, kieContainer, analyzeRequest, nerEntities);
|
||||
|
||||
var newRedactionLogEntries = redactionLogCreatorService.createRedactionLog(pageEntities, text.getNumberOfPages(), analyzeRequest.getDossierTemplateId());
|
||||
|
||||
var importedRedactionFilteredEntries = importedRedactionService.processImportedRedactions(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId(), analyzeRequest.getFileId(), newRedactionLogEntries, false);
|
||||
@@ -175,6 +161,7 @@ public class AnalyzeService {
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_analyze")
|
||||
public AnalyzeResult analyze(AnalyzeRequest analyzeRequest) {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
@@ -184,7 +171,7 @@ public class AnalyzeService {
|
||||
if (redactionServiceSettings.isNerServiceEnabled()) {
|
||||
nerEntities = redactionStorageService.getNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
|
||||
} else {
|
||||
nerEntities = NerEntities.builder().build();
|
||||
nerEntities = new NerEntities();
|
||||
}
|
||||
dictionaryService.updateDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
|
||||
KieContainer kieContainer = droolsExecutionService.updateRules(analyzeRequest.getDossierTemplateId());
|
||||
@@ -196,7 +183,7 @@ public class AnalyzeService {
|
||||
List<RedactionLogEntry> redactionLogEntries = redactionLogCreatorService.createRedactionLog(pageEntities, text.getNumberOfPages(), analyzeRequest.getDossierTemplateId());
|
||||
|
||||
var legalBasis = legalBasisClient.getLegalBasisMapping(analyzeRequest.getDossierTemplateId());
|
||||
var redactionLog = new RedactionLog(redactionServiceSettings.getAnalysisVersion(), analyzeRequest.getAnalysisNumber(), redactionLogEntries, legalBasis, dictionary.getVersion()
|
||||
var redactionLog = new RedactionLog(redactionServiceSettings.getAnalysisVersion(), analyzeRequest.getAnalysisNumber(), redactionLogEntries, convert(legalBasis), dictionary.getVersion()
|
||||
.getDossierTemplateVersion(), dictionary.getVersion()
|
||||
.getDossierVersion(), rulesVersion, legalBasisClient.getVersion(analyzeRequest.getDossierTemplateId()));
|
||||
|
||||
@@ -207,6 +194,7 @@ public class AnalyzeService {
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_findSectionsToReanalyse")
|
||||
private Set<Integer> findSectionsToReanalyse(DictionaryIncrement dictionaryIncrement, RedactionLog redactionLog,
|
||||
Text text, AnalyzeRequest analyzeRequest) {
|
||||
|
||||
@@ -224,9 +212,13 @@ public class AnalyzeService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var dictionaryIncrementsSearch = new SearchImplementation(dictionaryIncrement.getValues().stream()
|
||||
.map(DictionaryIncrementValue::getValue).collect(Collectors.toList()), true);
|
||||
|
||||
for (SectionText sectionText : text.getSectionTexts()) {
|
||||
|
||||
if (EntitySearchUtils.sectionContainsAny(sectionText.getText(), dictionaryIncrement.getValues())) {
|
||||
if (EntitySearchUtils.sectionContainsAny(sectionText.getText(), dictionaryIncrementsSearch)) {
|
||||
sectionsToReanalyse.add(sectionText.getSectionNumber());
|
||||
}
|
||||
|
||||
@@ -276,7 +268,9 @@ public class AnalyzeService {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
return Stream.concat(manualRedactions.getLegalBasisChanges()
|
||||
return Stream.concat(manualRedactions.getResizeRedactions()
|
||||
.stream()
|
||||
.map(ManualResizeRedaction::getAnnotationId), Stream.concat(manualRedactions.getLegalBasisChanges()
|
||||
.stream()
|
||||
.map(ManualLegalBasisChange::getAnnotationId), Stream.concat(manualRedactions.getImageRecategorization()
|
||||
.stream()
|
||||
@@ -284,9 +278,12 @@ public class AnalyzeService {
|
||||
.stream()
|
||||
.map(IdRemoval::getAnnotationId), manualRedactions.getForceRedactions()
|
||||
.stream()
|
||||
.map(ManualForceRedaction::getAnnotationId)))).collect(Collectors.toSet());
|
||||
.map(ManualForceRedaction::getAnnotationId))))).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public List<RedactionLogLegalBasis> convert(List<LegalBasis> legalBasis) {
|
||||
return legalBasis.stream().map(l -> new RedactionLogLegalBasis(l.getName(), l.getDescription(), l.getReason())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Image convert(RedactionLogEntry entry) {
|
||||
|
||||
@@ -315,16 +312,4 @@ public class AnalyzeService {
|
||||
}
|
||||
}
|
||||
|
||||
private SimplifiedText convert(Text text) {
|
||||
|
||||
List<SimplifiedSectionText> sectionTexts = new ArrayList<>();
|
||||
int numberOfPages = 0;
|
||||
|
||||
if (text != null) {
|
||||
numberOfPages = text.getNumberOfPages();
|
||||
text.getSectionTexts().stream().filter(Objects::nonNull).forEach(st -> sectionTexts.add(new SimplifiedSectionText(st.getSectionNumber(), st.getText())));
|
||||
}
|
||||
return SimplifiedText.builder().numberOfPages(numberOfPages).sectionTexts(sectionTexts).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+75
-64
@@ -5,9 +5,12 @@ import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ty
|
||||
import com.iqser.red.service.redaction.v1.server.client.DictionaryClient;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Dictionary;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.*;
|
||||
|
||||
import feign.FeignException;
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -17,7 +20,6 @@ import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -29,6 +31,7 @@ public class DictionaryService {
|
||||
private final Map<String, DictionaryRepresentation> dictionariesByDossier = new HashMap<>();
|
||||
|
||||
|
||||
@Timed("redactmanager_updateDictionary")
|
||||
public DictionaryVersion updateDictionary(String dossierTemplateId, String dossierId) {
|
||||
|
||||
log.info("Updating dictionary data for dossierTemplate {} and dossier {}", dossierTemplateId, dossierId);
|
||||
@@ -44,11 +47,16 @@ public class DictionaryService {
|
||||
updateDictionaryEntry(dossierTemplateId, dossierDictionaryVersion, getVersion(dossierDictionary), dossierId);
|
||||
}
|
||||
|
||||
return DictionaryVersion.builder().dossierTemplateVersion(dossierTemplateDictionaryVersion).dossierVersion(dossierDictionaryVersion).build();
|
||||
return DictionaryVersion.builder()
|
||||
.dossierTemplateVersion(dossierTemplateDictionaryVersion)
|
||||
.dossierVersion(dossierDictionaryVersion)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
public DictionaryIncrement getDictionaryIncrements(String dossierTemplateId, DictionaryVersion fromVersion, String dossierId) {
|
||||
@Timed("redactmanager_getDictionaryIncrements")
|
||||
public DictionaryIncrement getDictionaryIncrements(String dossierTemplateId, DictionaryVersion fromVersion,
|
||||
String dossierId) {
|
||||
|
||||
DictionaryVersion version = updateDictionary(dossierTemplateId, dossierId);
|
||||
|
||||
@@ -102,54 +110,69 @@ public class DictionaryService {
|
||||
try {
|
||||
DictionaryRepresentation dictionaryRepresentation = new DictionaryRepresentation();
|
||||
|
||||
var typeResponse = dossierId == null ? dictionaryClient.getAllTypesForDossierTemplate(dossierTemplateId, false)
|
||||
: dictionaryClient.getAllTypesForDossier(dossierId, false);
|
||||
var typeResponse = dossierId == null ? dictionaryClient.getAllTypesForDossierTemplate(dossierTemplateId, false) : dictionaryClient.getAllTypesForDossier(dossierId, false);
|
||||
if (CollectionUtils.isNotEmpty(typeResponse)) {
|
||||
|
||||
List<DictionaryModel> dictionary = typeResponse
|
||||
.stream()
|
||||
.map(t -> {
|
||||
List<DictionaryModel> dictionary = typeResponse.stream().map(t -> {
|
||||
|
||||
Optional<DictionaryModel> oldModel;
|
||||
if (dossierId == null) {
|
||||
var representation = dictionariesByDossierTemplate.get(dossierTemplateId);
|
||||
oldModel = representation != null ? representation.getDictionary().stream().filter(f -> f.getType().equals(t.getType())).findAny() : Optional.empty();
|
||||
} else {
|
||||
var representation = dictionariesByDossier.get(dossierId);
|
||||
oldModel = representation != null ? representation.getDictionary().stream().filter(f -> f.getType().equals(t.getType())).findAny() : Optional.empty();
|
||||
}
|
||||
Optional<DictionaryModel> oldModel;
|
||||
if (dossierId == null) {
|
||||
var representation = dictionariesByDossierTemplate.get(dossierTemplateId);
|
||||
oldModel = representation != null ? representation.getDictionary()
|
||||
.stream()
|
||||
.filter(f -> f.getType().equals(t.getType()))
|
||||
.findAny() : Optional.empty();
|
||||
} else {
|
||||
var representation = dictionariesByDossier.get(dossierId);
|
||||
oldModel = representation != null ? representation.getDictionary()
|
||||
.stream()
|
||||
.filter(f -> f.getType().equals(t.getType()))
|
||||
.findAny() : Optional.empty();
|
||||
}
|
||||
|
||||
Set<DictionaryEntry> entries = new HashSet<>();
|
||||
Set<DictionaryEntry> falsePositives = new HashSet<>();
|
||||
Set<DictionaryEntry> falseRecommendations = new HashSet<>();
|
||||
Set<DictionaryEntry> entries = new HashSet<>();
|
||||
Set<DictionaryEntry> falsePositives = new HashSet<>();
|
||||
Set<DictionaryEntry> falseRecommendations = new HashSet<>();
|
||||
|
||||
DictionaryEntries newEntries = getEntries(t.getId(), currentVersion);
|
||||
DictionaryEntries newEntries = getEntries(t.getId(), currentVersion);
|
||||
|
||||
var newValues = newEntries.getEntries().stream().map(v -> v.getValue()).collect(Collectors.toSet());
|
||||
var newFalsePositivesValues = newEntries.getFalsePositives().stream().map(v -> v.getValue()).collect(Collectors.toSet());
|
||||
var newFalseRecommendationsValues = newEntries.getFalseRecommendations().stream().map(v -> v.getValue()).collect(Collectors.toSet());
|
||||
var newValues = newEntries.getEntries()
|
||||
.stream()
|
||||
.map(DictionaryEntry::getValue)
|
||||
.collect(Collectors.toSet());
|
||||
var newFalsePositivesValues = newEntries.getFalsePositives()
|
||||
.stream()
|
||||
.map(DictionaryEntry::getValue)
|
||||
.collect(Collectors.toSet());
|
||||
var newFalseRecommendationsValues = newEntries.getFalseRecommendations()
|
||||
.stream()
|
||||
.map(DictionaryEntry::getValue)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// add old entries from existing DictionaryModel
|
||||
oldModel.ifPresent(dictionaryModel -> entries.addAll(dictionaryModel.getEntries().stream().filter(
|
||||
f -> !newValues.contains(f.getValue())).collect(Collectors.toList())
|
||||
));
|
||||
oldModel.ifPresent(dictionaryModel -> falsePositives.addAll(dictionaryModel.getFalsePositives().stream().filter(
|
||||
f -> !newFalsePositivesValues.contains(f.getValue())).collect(Collectors.toList())
|
||||
));
|
||||
oldModel.ifPresent(dictionaryModel -> falseRecommendations.addAll(dictionaryModel.getFalseRecommendations().stream().filter(
|
||||
f -> !newFalseRecommendationsValues.contains(f.getValue())).collect(Collectors.toList())
|
||||
));
|
||||
oldModel.ifPresent(oldDictionaryModel -> {
|
||||
|
||||
// Add Increments
|
||||
entries.addAll(newEntries.getEntries());
|
||||
falsePositives.addAll(newEntries.getFalsePositives());
|
||||
falseRecommendations.addAll(newEntries.getFalseRecommendations());
|
||||
});
|
||||
// add old entries from existing DictionaryModel
|
||||
oldModel.ifPresent(dictionaryModel -> entries.addAll(dictionaryModel.getEntries()
|
||||
.stream()
|
||||
.filter(f -> !newValues.contains(f.getValue()))
|
||||
.collect(Collectors.toList())));
|
||||
oldModel.ifPresent(dictionaryModel -> falsePositives.addAll(dictionaryModel.getFalsePositives()
|
||||
.stream()
|
||||
.filter(f -> !newFalsePositivesValues.contains(f.getValue()))
|
||||
.collect(Collectors.toList())));
|
||||
oldModel.ifPresent(dictionaryModel -> falseRecommendations.addAll(dictionaryModel.getFalseRecommendations()
|
||||
.stream()
|
||||
.filter(f -> !newFalseRecommendationsValues.contains(f.getValue()))
|
||||
.collect(Collectors.toList())));
|
||||
|
||||
return new DictionaryModel(t.getType(), t.getRank(), convertColor(t.getHexColor()), t.isCaseInsensitive(), t
|
||||
.isHint(), entries, falsePositives, falseRecommendations, new HashSet<>(), dossierId != null);
|
||||
})
|
||||
.sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed())
|
||||
.collect(Collectors.toList());
|
||||
// Add Increments
|
||||
entries.addAll(newEntries.getEntries());
|
||||
falsePositives.addAll(newEntries.getFalsePositives());
|
||||
falseRecommendations.addAll(newEntries.getFalseRecommendations());
|
||||
|
||||
return new DictionaryModel(t.getType(), t.getRank(), convertColor(t.getHexColor()), t.isCaseInsensitive(), t.isHint(), entries, falsePositives, falseRecommendations, dossierId != null);
|
||||
}).sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed()).collect(Collectors.toList());
|
||||
|
||||
dictionary.forEach(dm -> dictionaryRepresentation.getLocalAccessMap().put(dm.getType(), dm));
|
||||
|
||||
@@ -189,6 +212,7 @@ public class DictionaryService {
|
||||
falsePositives.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT)));
|
||||
falseRecommendations.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
log.info("Dictionary update returned {} entries {} falsePositives and {} falseRecommendations for type {}", entries.size(), falsePositives.size(), falseRecommendations.size(), type.getType());
|
||||
return new DictionaryEntries(entries, falsePositives, falseRecommendations);
|
||||
}
|
||||
|
||||
@@ -200,16 +224,6 @@ public class DictionaryService {
|
||||
}
|
||||
|
||||
|
||||
public boolean isCaseInsensitiveDictionary(String type, String dossierTemplateId) {
|
||||
|
||||
DictionaryModel dictionaryModel = dictionariesByDossierTemplate.get(dossierTemplateId).getLocalAccessMap().get(type);
|
||||
if (dictionaryModel != null) {
|
||||
return dictionaryModel.isCaseInsensitive();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public float[] getColor(String type, String dossierTemplateId) {
|
||||
|
||||
DictionaryModel model = dictionariesByDossierTemplate.get(dossierTemplateId).getLocalAccessMap().get(type);
|
||||
@@ -230,6 +244,7 @@ public class DictionaryService {
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_getDeepCopyDictionary")
|
||||
public Dictionary getDeepCopyDictionary(String dossierTemplateId, String dossierId) {
|
||||
|
||||
List<DictionaryModel> copy = new ArrayList<>();
|
||||
@@ -249,13 +264,12 @@ public class DictionaryService {
|
||||
dossierDictionaryVersion = dossierRepresentation.getDictionaryVersion();
|
||||
}
|
||||
|
||||
return new Dictionary(copy.stream().sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed()).collect(Collectors.toList()), DictionaryVersion.builder().dossierTemplateVersion(dossierTemplateRepresentation.getDictionaryVersion()).dossierVersion(dossierDictionaryVersion).build());
|
||||
}
|
||||
|
||||
|
||||
public float[] getRequestRemoveColor(String dossierTemplateId) {
|
||||
|
||||
return dictionariesByDossierTemplate.get(dossierTemplateId).getRequestRemoveColor();
|
||||
return new Dictionary(copy.stream()
|
||||
.sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed())
|
||||
.collect(Collectors.toList()), DictionaryVersion.builder()
|
||||
.dossierTemplateVersion(dossierTemplateRepresentation.getDictionaryVersion())
|
||||
.dossierVersion(dossierDictionaryVersion)
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@@ -265,16 +279,13 @@ public class DictionaryService {
|
||||
}
|
||||
|
||||
|
||||
public float[] getRequestAddColor(String dossierTemplateId) {
|
||||
|
||||
return dictionariesByDossierTemplate.get(dossierTemplateId).getRequestAddColor();
|
||||
}
|
||||
|
||||
private Long getVersion(DictionaryRepresentation dictionaryRepresentation) {
|
||||
|
||||
if (dictionaryRepresentation == null) {
|
||||
return null;
|
||||
} else {
|
||||
return dictionaryRepresentation.getDictionaryVersion();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -3,6 +3,8 @@ package com.iqser.red.service.redaction.v1.server.redaction.service;
|
||||
import com.iqser.red.service.redaction.v1.server.client.RulesClient;
|
||||
import com.iqser.red.service.redaction.v1.server.exception.RulesValidationException;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Section;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kie.api.KieServices;
|
||||
@@ -41,6 +43,7 @@ public class DroolsExecutionService {
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_executeRules")
|
||||
public Section executeRules(KieContainer kieContainer, Section section) {
|
||||
|
||||
KieSession kieSession = kieContainer.newKieSession();
|
||||
|
||||
+56
-32
@@ -14,6 +14,7 @@ import org.kie.api.runtime.KieContainer;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.IdRemoval;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualImageRecategorization;
|
||||
import com.iqser.red.service.redaction.v1.model.AnalyzeRequest;
|
||||
@@ -32,9 +33,11 @@ import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Section;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.SectionSearchableTextPair;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.EntitySearchUtils;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.FindEntityDetails;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.IdBuilder;
|
||||
import com.iqser.red.service.redaction.v1.server.settings.RedactionServiceSettings;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -48,6 +51,7 @@ public class EntityRedactionService {
|
||||
private final SurroundingWordsService surroundingWordsService;
|
||||
|
||||
|
||||
|
||||
public PageEntities findEntities(Dictionary dictionary, List<SectionText> sectionTexts, KieContainer kieContainer,
|
||||
AnalyzeRequest analyzeRequest, NerEntities nerEntities) {
|
||||
|
||||
@@ -76,8 +80,7 @@ public class EntityRedactionService {
|
||||
List<SectionSearchableTextPair> sectionSearchableTextPairs = new ArrayList<>();
|
||||
for (SectionText reanalysisSection : reanalysisSections) {
|
||||
|
||||
Entities entities = findEntities(reanalysisSection.getSearchableText(), reanalysisSection.getHeadline(), reanalysisSection
|
||||
.getSectionNumber(), dictionary, local, nerEntities, reanalysisSection.getCellStarts());
|
||||
Entities entities = findEntities(reanalysisSection.getSearchableText(), reanalysisSection.getHeadline(), reanalysisSection.getSectionNumber(), dictionary, local, nerEntities, reanalysisSection.getCellStarts(), analyzeRequest.getManualRedactions());
|
||||
|
||||
if (reanalysisSection.getCellStarts() != null && !reanalysisSection.getCellStarts().isEmpty()) {
|
||||
surroundingWordsService.addSurroundingText(entities.getEntities(), reanalysisSection.getSearchableText(), dictionary, reanalysisSection
|
||||
@@ -88,16 +91,24 @@ public class EntityRedactionService {
|
||||
|
||||
if (!local && analyzeRequest.getManualRedactions() != null) {
|
||||
|
||||
var approvedForceRedactions = analyzeRequest.getManualRedactions().getForceRedactions().stream()
|
||||
var approvedForceRedactions = analyzeRequest.getManualRedactions()
|
||||
.getForceRedactions()
|
||||
.stream()
|
||||
.filter(fr -> fr.getStatus() == AnnotationStatus.APPROVED)
|
||||
.filter(fr -> fr.getRequestDate() != null)
|
||||
.collect(Collectors.toList());
|
||||
// only approved id removals, that haven't been forced back afterwards
|
||||
var idsToRemove = analyzeRequest.getManualRedactions().getIdsToRemove().stream()
|
||||
var idsToRemove = analyzeRequest.getManualRedactions()
|
||||
.getIdsToRemove()
|
||||
.stream()
|
||||
.filter(idr -> idr.getStatus() == AnnotationStatus.APPROVED && !idr.isRemoveFromDictionary())
|
||||
.filter(idr -> idr.getRequestDate() != null)
|
||||
.filter(idr -> approvedForceRedactions.stream().noneMatch(forceRedact -> forceRedact.getRequestDate().isAfter(idr.getRequestDate())))
|
||||
.map(IdRemoval::getAnnotationId).collect(Collectors.toSet());
|
||||
.filter(idr -> approvedForceRedactions.stream()
|
||||
.noneMatch(forceRedact -> forceRedact.getAnnotationId()
|
||||
.equals(idr.getAnnotationId()) && forceRedact.getRequestDate()
|
||||
.isAfter(idr.getRequestDate())))
|
||||
.map(IdRemoval::getAnnotationId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (reanalysisSection.getImages() != null && !reanalysisSection.getImages()
|
||||
.isEmpty() && analyzeRequest.getManualRedactions().getImageRecategorization() != null) {
|
||||
@@ -124,12 +135,13 @@ public class EntityRedactionService {
|
||||
}));
|
||||
}
|
||||
|
||||
log.debug("Section {}, Images: {}", reanalysisSection.getSectionNumber(), reanalysisSection.getImages());
|
||||
|
||||
sectionSearchableTextPairs.add(new SectionSearchableTextPair(Section.builder()
|
||||
.isLocal(false)
|
||||
.dictionaryTypes(dictionary.getTypes())
|
||||
.entities(hintsPerSectionNumber != null && hintsPerSectionNumber.containsKey(reanalysisSection.getSectionNumber()) ? Stream
|
||||
.concat(entities.getEntities().stream(), hintsPerSectionNumber.get(reanalysisSection.getSectionNumber())
|
||||
.stream())
|
||||
.entities(hintsPerSectionNumber != null && hintsPerSectionNumber.containsKey(reanalysisSection.getSectionNumber()) ? Stream.concat(entities.getEntities()
|
||||
.stream(), hintsPerSectionNumber.get(reanalysisSection.getSectionNumber()).stream())
|
||||
.collect(Collectors.toSet()) : entities.getEntities())
|
||||
.nerEntities(entities.getNerEntities())
|
||||
.text(reanalysisSection.getSearchableText().getAsStringWithLinebreaks())
|
||||
@@ -140,14 +152,27 @@ public class EntityRedactionService {
|
||||
.searchableText(reanalysisSection.getSearchableText())
|
||||
.dictionary(dictionary)
|
||||
.images(reanalysisSection.getImages())
|
||||
.sectionAreas(reanalysisSection.getSectionAreas())
|
||||
.fileAttributes(analyzeRequest.getFileAttributes())
|
||||
.build(), reanalysisSection.getSearchableText()));
|
||||
.manualRedactions(analyzeRequest.getManualRedactions())
|
||||
.build(), reanalysisSection.getSearchableText(), reanalysisSection.getCellStarts()));
|
||||
|
||||
}
|
||||
|
||||
Set<Entity> entities = new HashSet<>();
|
||||
sectionSearchableTextPairs.forEach(sectionSearchableTextPair -> {
|
||||
Section analysedSection = droolsExecutionService.executeRules(kieContainer, sectionSearchableTextPair.getSection());
|
||||
EntitySearchUtils.removeEntitiesContainedInLarger(analysedSection.getEntities());
|
||||
|
||||
var entriesWithoutSurroundingText = analysedSection.getEntities().stream().filter(e -> e.getTextAfter() == null && e.getTextBefore() == null).collect(Collectors.toSet());
|
||||
|
||||
if (sectionSearchableTextPair.getCellStarts() != null && !sectionSearchableTextPair.getCellStarts()
|
||||
.isEmpty()) {
|
||||
surroundingWordsService.addSurroundingText(entriesWithoutSurroundingText, sectionSearchableTextPair.getSearchableText(), dictionary, sectionSearchableTextPair.getCellStarts());
|
||||
} else {
|
||||
surroundingWordsService.addSurroundingText(entriesWithoutSurroundingText, sectionSearchableTextPair.getSearchableText(), dictionary);
|
||||
}
|
||||
|
||||
entities.addAll(analysedSection.getEntities());
|
||||
|
||||
if (!local) {
|
||||
@@ -156,6 +181,7 @@ public class EntityRedactionService {
|
||||
}
|
||||
addLocalValuesToDictionary(analysedSection, dictionary);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return entities;
|
||||
@@ -174,11 +200,7 @@ public class EntityRedactionService {
|
||||
|
||||
for (Map.Entry<Integer, List<EntityPositionSequence>> entry : sequenceOnPage.entrySet()) {
|
||||
entitiesPerPage.computeIfAbsent(entry.getKey(), (x) -> new ArrayList<>())
|
||||
.add(new Entity(entity.getWord(), entity.getType(), entity.isRedaction(), entity.getRedactionReason(), entry
|
||||
.getValue(), entity.getHeadline(), entity.getMatchedRule(), entity.getSectionNumber(), entity
|
||||
.getLegalBasis(), entity.isDictionaryEntry(), entity.getTextBefore(), entity.getTextAfter(), entity
|
||||
.getStart(), entity.getEnd(), entity.isDossierDictionaryEntry(), entity.getEngines(), entity
|
||||
.getReferences(), entity.getEntityType()));
|
||||
.add(new Entity(entity.getWord(), entity.getType(), entity.isRedaction(), entity.getRedactionReason(), entry.getValue(), entity.getHeadline(), entity.getMatchedRule(), entity.getSectionNumber(), entity.getLegalBasis(), entity.isDictionaryEntry(), entity.getTextBefore(), entity.getTextAfter(), entity.getStart(), entity.getEnd(), entity.isDossierDictionaryEntry(), entity.getEngines(), entity.getReferences(), entity.getEntityType()));
|
||||
}
|
||||
}
|
||||
return entitiesPerPage;
|
||||
@@ -200,39 +222,41 @@ public class EntityRedactionService {
|
||||
private void addLocalValuesToDictionary(Section analysedSection, Dictionary dictionary) {
|
||||
|
||||
analysedSection.getLocalDictionaryAdds().keySet().forEach(key -> {
|
||||
analysedSection.getLocalDictionaryAdds().get(key).forEach(value -> {
|
||||
analysedSection.getLocalDictionaryAdds().get(key).forEach(value -> {
|
||||
|
||||
if (dictionary.getLocalAccessMap().get(key) == null) {
|
||||
log.warn("Dictionary {} is null", key);
|
||||
}
|
||||
if (dictionary.getLocalAccessMap().get(key) == null) {
|
||||
log.warn("Dictionary {} is null", key);
|
||||
}
|
||||
|
||||
if (dictionary.getLocalAccessMap().get(key).getLocalEntries() == null) {
|
||||
log.warn("Dictionary {} localEntries is null", key);
|
||||
}
|
||||
if (dictionary.getLocalAccessMap().get(key).getLocalEntries() == null) {
|
||||
log.warn("Dictionary {} localEntries is null", key);
|
||||
}
|
||||
|
||||
dictionary.getLocalAccessMap().get(key).getLocalEntries().add(value);
|
||||
});
|
||||
dictionary.getLocalAccessMap().get(key).getLocalEntries().add(value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_findEntities")
|
||||
private Entities findEntities(SearchableText searchableText, String headline, int sectionNumber,
|
||||
Dictionary dictionary, boolean local, NerEntities nerEntities,
|
||||
List<Integer> cellStarts) {
|
||||
List<Integer> cellStarts, ManualRedactions manualRedactions) {
|
||||
|
||||
Set<Entity> found = new HashSet<>();
|
||||
String searchableString = searchableText.toString();
|
||||
String searchableString = searchableText.asString();
|
||||
|
||||
if (StringUtils.isEmpty(searchableString)) {
|
||||
return new Entities(new HashSet<>(), new HashSet<>());
|
||||
}
|
||||
|
||||
String lowercaseInputString = searchableString.toLowerCase();
|
||||
for (DictionaryModel model : dictionary.getDictionaryModels()) {
|
||||
if (model.isCaseInsensitive()) {
|
||||
EntitySearchUtils.addOrAddEngine(found, EntitySearchUtils.findEntities(lowercaseInputString, model.getValues(local), model, headline, sectionNumber, !local, model.isDossierDictionary(), local ? Engine.RULE : Engine.DICTIONARY, false, local ? true : false));
|
||||
} else {
|
||||
EntitySearchUtils.addOrAddEngine(found, EntitySearchUtils.findEntities(searchableString, model.getValues(local), model, headline, sectionNumber, !local, model.isDossierDictionary(), local ? Engine.RULE : Engine.DICTIONARY, false, local ? true : false));
|
||||
}
|
||||
|
||||
var searchImplementation = local ? model.getLocalSearch() : model.getEntriesSearch();
|
||||
var entities = EntitySearchUtils.findEntities(model.isCaseInsensitive() ? lowercaseInputString : searchableString, searchImplementation, model, new FindEntityDetails(model.getType(), headline, sectionNumber, !local, model.isDossierDictionary(), local ? Engine.RULE : Engine.DICTIONARY, local ? EntityType.RECOMMENDATION : EntityType.ENTITY));
|
||||
|
||||
EntitySearchUtils.addOrAddEngine(found, entities);
|
||||
}
|
||||
|
||||
Set<Entity> nerFound = new HashSet<>();
|
||||
@@ -240,7 +264,7 @@ public class EntityRedactionService {
|
||||
nerFound.addAll(getNerValues(sectionNumber, nerEntities, cellStarts, headline));
|
||||
}
|
||||
|
||||
return new Entities(EntitySearchUtils.clearAndFindPositions(found, searchableText, dictionary), nerFound);
|
||||
return new Entities(EntitySearchUtils.clearAndFindPositions(found, searchableText, dictionary, manualRedactions), nerFound);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+55
-10
@@ -7,10 +7,12 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.model.ImportedRedaction;
|
||||
import com.iqser.red.service.redaction.v1.model.ImportedRedactions;
|
||||
import com.iqser.red.service.redaction.v1.model.Point;
|
||||
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||
import com.iqser.red.service.redaction.v1.model.RedactionLogEntry;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Service
|
||||
@@ -23,6 +25,7 @@ public class ImportedRedactionService {
|
||||
private final RedactionStorageService redactionStorageService;
|
||||
|
||||
|
||||
@Timed("redactmanager_processImportedRedactions")
|
||||
public List<RedactionLogEntry> processImportedRedactions(String dossierTemplateId, String dossierId, String fileId,
|
||||
List<RedactionLogEntry> redactionLogEntries,
|
||||
boolean addImportedRedactions) {
|
||||
@@ -68,12 +71,13 @@ public class ImportedRedactionService {
|
||||
private void addIntersections(RedactionLogEntry redactionLogEntry, ImportedRedactions importedRedactions) {
|
||||
|
||||
for (Rectangle rectangle : redactionLogEntry.getPositions()) {
|
||||
var normalizedRectangle = normalize(rectangle);
|
||||
if (importedRedactions.getImportedRedactions().containsKey(rectangle.getPage())) {
|
||||
var importedRedactionsOnPage = importedRedactions.getImportedRedactions().get(rectangle.getPage());
|
||||
for (ImportedRedaction importedRedaction : importedRedactionsOnPage) {
|
||||
for (Rectangle importedRedactionPosition : importedRedaction.getPositions()) {
|
||||
if (intersects(importedRedactionPosition, rectangle)) {
|
||||
if(redactionLogEntry.getImportedRedactionIntersections() == null){
|
||||
if (rectOverlap(normalizedRectangle, normalize(importedRedactionPosition))) {
|
||||
if (redactionLogEntry.getImportedRedactionIntersections() == null) {
|
||||
redactionLogEntry.setImportedRedactionIntersections(new HashSet<>());
|
||||
}
|
||||
redactionLogEntry.getImportedRedactionIntersections().add(importedRedaction.getId());
|
||||
@@ -85,15 +89,56 @@ public class ImportedRedactionService {
|
||||
}
|
||||
|
||||
|
||||
private boolean intersects(Rectangle importedPosition, Rectangle redactionLogPosition) {
|
||||
boolean valueInRange(float value, float min, float max) {
|
||||
|
||||
return redactionLogPosition.getTopLeft()
|
||||
.getX() + redactionLogPosition.getWidth() > importedPosition.getTopLeft()
|
||||
.getX() && redactionLogPosition.getTopLeft()
|
||||
.getY() + redactionLogPosition.getHeight() > importedPosition.getTopLeft()
|
||||
.getY() && redactionLogPosition.getTopLeft().getX() < importedPosition.getTopLeft()
|
||||
.getX() + importedPosition.getWidth() && redactionLogPosition.getTopLeft()
|
||||
.getY() < importedPosition.getTopLeft().getY() + importedPosition.getHeight();
|
||||
return round(value) >= round(min) && round(value) <= round(max);
|
||||
}
|
||||
|
||||
|
||||
boolean rectOverlap(Rectangle a, Rectangle b) {
|
||||
|
||||
boolean xOverlap = valueInRange(a.getTopLeft().getX(), b.getTopLeft().getX(), b.getTopLeft()
|
||||
.getX() + b.getWidth()) || valueInRange(b.getTopLeft().getX(), a.getTopLeft().getX(), a.getTopLeft()
|
||||
.getX() + a.getWidth());
|
||||
|
||||
boolean yOverlap = valueInRange(a.getTopLeft().getY(), b.getTopLeft().getY(), b.getTopLeft()
|
||||
.getY() + b.getHeight()) || valueInRange(b.getTopLeft().getY(), a.getTopLeft().getY(), a.getTopLeft()
|
||||
.getY() + a.getHeight());
|
||||
|
||||
return xOverlap && yOverlap;
|
||||
}
|
||||
|
||||
|
||||
private Rectangle normalize(Rectangle rectangle) {
|
||||
|
||||
Rectangle r = new Rectangle();
|
||||
Point p = new Point();
|
||||
|
||||
if (rectangle.getWidth() < 0) {
|
||||
p.setX(rectangle.getTopLeft().getX() - rectangle.getWidth());
|
||||
r.setWidth(Math.abs(rectangle.getWidth()));
|
||||
} else {
|
||||
p.setX(rectangle.getTopLeft().getX());
|
||||
r.setWidth(rectangle.getWidth());
|
||||
}
|
||||
|
||||
if (rectangle.getHeight() < 0) {
|
||||
p.setY(rectangle.getTopLeft().getY() + rectangle.getHeight());
|
||||
r.setHeight(Math.abs(rectangle.getHeight()));
|
||||
} else {
|
||||
p.setY(rectangle.getTopLeft().getY());
|
||||
}
|
||||
|
||||
r.setTopLeft(p);
|
||||
r.setPage(rectangle.getPage());
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
private float round(float value) {
|
||||
|
||||
double d = Math.pow(10, 0);
|
||||
return (float) (Math.round(value * d) / d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+7
-19
@@ -10,7 +10,6 @@ import org.springframework.stereotype.Service;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.Rectangle;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualRedactionEntry;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualResizeRedaction;
|
||||
import com.iqser.red.service.redaction.v1.model.AnalyzeResult;
|
||||
import com.iqser.red.service.redaction.v1.model.Engine;
|
||||
import com.iqser.red.service.redaction.v1.model.SectionArea;
|
||||
@@ -21,8 +20,11 @@ import com.iqser.red.service.redaction.v1.server.redaction.model.Entity;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityPositionSequence;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityType;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.EntitySearchUtils;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.FindEntityDetails;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.SearchImplementation;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -34,17 +36,16 @@ public class ManualRedactionSurroundingTextService {
|
||||
private final RedactionStorageService redactionStorageService;
|
||||
private final SurroundingWordsService surroundingWordsService;
|
||||
|
||||
|
||||
@Timed("redactmanager_SurroundingTextAnalysis")
|
||||
public AnalyzeResult addSurroundingText(String dossierId, String fileId, ManualRedactions manualRedactions) {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
Text text = redactionStorageService.getText(dossierId, fileId);
|
||||
List<ManualRedactionEntry> processedAddRedactions = new ArrayList<>();
|
||||
List<ManualResizeRedaction> processedResizeRedactions = new ArrayList<>();
|
||||
|
||||
for (SectionText sectionText : text.getSectionTexts()) {
|
||||
|
||||
if (manualRedactions.getEntriesToAdd().isEmpty() && manualRedactions.getResizeRedactions().isEmpty()) {
|
||||
if (manualRedactions.getEntriesToAdd().isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -61,23 +62,10 @@ public class ManualRedactionSurroundingTextService {
|
||||
addItty.remove();
|
||||
}
|
||||
}
|
||||
|
||||
var resizeItty = manualRedactions.getResizeRedactions().iterator();
|
||||
while (resizeItty.hasNext()) {
|
||||
var manualResizeRedaction = resizeItty.next();
|
||||
if (manualResizeRedaction.getValue() != null && sectionContainsEntry(sectionArea, manualResizeRedaction.getPositions())) {
|
||||
var surroundingText = findSurroundingText(sectionText, manualResizeRedaction.getValue(), manualResizeRedaction.getPositions());
|
||||
manualResizeRedaction.setTextBefore(surroundingText.getLeft());
|
||||
manualResizeRedaction.setTextAfter(surroundingText.getRight());
|
||||
processedResizeRedactions.add(manualResizeRedaction);
|
||||
resizeItty.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manualRedactions.getEntriesToAdd().addAll(processedAddRedactions);
|
||||
manualRedactions.getResizeRedactions().addAll(processedResizeRedactions);
|
||||
|
||||
return AnalyzeResult.builder()
|
||||
.dossierId(dossierId)
|
||||
@@ -91,8 +79,8 @@ public class ManualRedactionSurroundingTextService {
|
||||
private Pair<String, String> findSurroundingText(SectionText sectionText, String value,
|
||||
List<Rectangle> toFindPositions) {
|
||||
|
||||
Set<Entity> entities = EntitySearchUtils.find(sectionText.getText(), Set.of(value), "dummy", sectionText.getHeadline(), sectionText.getSectionNumber(), false, false, Engine.DICTIONARY, false, EntityType.ENTITY);
|
||||
Set<Entity> entitiesWithPositions = EntitySearchUtils.clearAndFindPositions(entities, sectionText.getSearchableText(), null);
|
||||
Set<Entity> entities = EntitySearchUtils.find(sectionText.getText(), new SearchImplementation(value, false), new FindEntityDetails("dummy", sectionText.getHeadline(), sectionText.getSectionNumber(), false, false, Engine.DICTIONARY, EntityType.ENTITY));
|
||||
Set<Entity> entitiesWithPositions = EntitySearchUtils.clearAndFindPositions(entities, sectionText.getSearchableText(), null, null);
|
||||
|
||||
Entity correctEntity = getEntityOnCorrectPosition(entitiesWithPositions, toFindPositions);
|
||||
|
||||
|
||||
+3
@@ -2,6 +2,8 @@ package com.iqser.red.service.redaction.v1.server.redaction.service;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.model.*;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -18,6 +20,7 @@ public class RedactionChangeLogService {
|
||||
private final RedactionStorageService redactionStorageService;
|
||||
|
||||
|
||||
@Timed("redactmanager_computeChanges")
|
||||
public RedactionLogChanges computeChanges(String dossierId, String fileId, RedactionLog currentRedactionLog, int analysisNumber) {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import com.iqser.red.service.redaction.v1.server.redaction.model.Image;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.PageEntities;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.IdBuilder;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -33,6 +34,7 @@ public class RedactionLogCreatorService {
|
||||
private final DictionaryService dictionaryService;
|
||||
|
||||
|
||||
@Timed("redactmanager_createRedactionLog")
|
||||
public List<RedactionLogEntry> createRedactionLog(PageEntities pageEntities, int numberOfPages,
|
||||
String dossierTemplateId) {
|
||||
|
||||
|
||||
+64
-8
@@ -5,10 +5,15 @@ import com.iqser.red.service.persistence.service.v1.api.model.annotations.Commen
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.*;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
|
||||
import com.iqser.red.service.redaction.v1.model.Point;
|
||||
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||
import com.iqser.red.service.redaction.v1.model.*;
|
||||
import com.iqser.red.service.redaction.v1.server.exception.NotFoundException;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -29,9 +34,46 @@ public class RedactionLogMergeService {
|
||||
|
||||
private final SectionTextService sectionTextService;
|
||||
|
||||
private final RedactionStorageService redactionStorageService;
|
||||
|
||||
public RedactionLog mergeRedactionLogData(RedactionLog redactionLog, SectionGrid sectionGrid, ManualRedactions manualRedactions,
|
||||
Set<Integer> excludedPages, List<Type> types, Colors colors) {
|
||||
|
||||
@Timed("redactmanager_getMergedRedactionLog")
|
||||
public RedactionLog provideRedactionLog(RedactionRequest redactionRequest) {
|
||||
log.debug("Requested preview for: {}", redactionRequest);
|
||||
|
||||
var redactionLog = redactionStorageService.getRedactionLog(redactionRequest.getDossierId(), redactionRequest.getFileId());
|
||||
|
||||
if (redactionLog == null) {
|
||||
throw new NotFoundException("RedactionLog not present");
|
||||
}
|
||||
|
||||
SectionGrid sectionGrid = redactionStorageService.getSectionGrid(redactionRequest.getDossierId(), redactionRequest.getFileId());
|
||||
if (sectionGrid.getSections().isEmpty()) {
|
||||
|
||||
log.info("SectionGrid does not have headlines set. Computing headlines now!");
|
||||
var text = redactionStorageService.getText(redactionRequest.getDossierId(), redactionRequest.getFileId());
|
||||
|
||||
// enhance section grid with headline data
|
||||
for (var sectionText : text.getSectionTexts()) {
|
||||
sectionGrid.getSections()
|
||||
.add(new SectionGrid.SectionGridSection(sectionText.getSectionNumber(), sectionText.getHeadline(), sectionText.getSectionAreas()
|
||||
.stream()
|
||||
.map(SectionArea::getPage)
|
||||
.collect(Collectors.toSet()), sectionText.getSectionAreas()));
|
||||
}
|
||||
redactionStorageService.storeObject(redactionRequest.getDossierId(), redactionRequest.getFileId(), FileType.SECTION_GRID, sectionGrid);
|
||||
}
|
||||
|
||||
log.debug("Loaded redaction log with computationalVersion: {}", redactionLog.getAnalysisVersion());
|
||||
var merged = mergeRedactionLogData(redactionLog, sectionGrid, redactionRequest.getManualRedactions(), redactionRequest.getExcludedPages(), redactionRequest.getTypes(), redactionRequest.getColors());
|
||||
|
||||
merged.getRedactionLogEntry().removeIf(e -> e.isFalsePositive() && !redactionRequest.isIncludeFalsePositives());
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private RedactionLog mergeRedactionLogData(RedactionLog redactionLog, SectionGrid sectionGrid, ManualRedactions manualRedactions,
|
||||
Set<Integer> excludedPages, List<Type> types, Colors colors) {
|
||||
|
||||
var skippedImportedRedactions = new HashSet<>();
|
||||
|
||||
@@ -55,7 +97,7 @@ public class RedactionLogMergeService {
|
||||
skippedImportedRedactions.add(entry.getId());
|
||||
}
|
||||
|
||||
entry.setComments(manualRedactions.getComments().get(entry.getId()));
|
||||
entry.setComments(convert(manualRedactions.getComments().get(entry.getId())));
|
||||
|
||||
if (excludedPages != null && !excludedPages.isEmpty()) {
|
||||
entry.getPositions().forEach(pos -> {
|
||||
@@ -252,15 +294,23 @@ public class RedactionLogMergeService {
|
||||
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, false, redactionLogEntry.isRedacted(), false, types));
|
||||
redactionLogEntry.setPositions(convertPositions(manualResizeRedact.getPositions()));
|
||||
redactionLogEntry.setValue(manualResizeRedact.getValue());
|
||||
redactionLogEntry.setTextBefore(manualResizeRedact.getTextBefore());
|
||||
redactionLogEntry.setTextAfter(manualResizeRedact.getTextAfter());
|
||||
|
||||
// This is for backwards compatibility, now the text after/before is calculated during reanalysis because we need to find dict entries on positions where entries are resized to smaller.
|
||||
if(manualResizeRedact.getTextBefore() != null || manualResizeRedact.getTextAfter() != null) {
|
||||
redactionLogEntry.setTextBefore(manualResizeRedact.getTextBefore());
|
||||
redactionLogEntry.setTextAfter(manualResizeRedact.getTextAfter());
|
||||
}
|
||||
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", resized by manual override");
|
||||
} else if (manualResizeRedact.getStatus().equals(AnnotationStatus.REQUESTED)) {
|
||||
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to resize redact");
|
||||
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, true, redactionLogEntry.isRedacted(), false, types));
|
||||
redactionLogEntry.setPositions(convertPositions(manualResizeRedact.getPositions()));
|
||||
redactionLogEntry.setTextBefore(manualResizeRedact.getTextBefore());
|
||||
redactionLogEntry.setTextAfter(manualResizeRedact.getTextAfter());
|
||||
|
||||
// This is for backwards compatibility, now the text after/before is calculated during reanalysis because we need to find dict entries on positions where entries are resized to smaller.
|
||||
if(manualResizeRedact.getTextBefore() != null || manualResizeRedact.getTextAfter() != null) {
|
||||
redactionLogEntry.setTextBefore(manualResizeRedact.getTextBefore());
|
||||
redactionLogEntry.setTextAfter(manualResizeRedact.getTextAfter());
|
||||
}
|
||||
}
|
||||
|
||||
redactionLogEntry.setReason(manualOverrideReason);
|
||||
@@ -297,7 +347,7 @@ public class RedactionLogMergeService {
|
||||
if (shouldCreateManualEntry(manualRedactionEntry)) {
|
||||
RedactionLogEntry redactionLogEntry = createRedactionLogEntry(manualRedactionEntry, manualRedactionEntry.getAnnotationId(), colors, types);
|
||||
redactionLogEntry.setPositions(convertPositions(manualRedactionEntry.getPositions()));
|
||||
redactionLogEntry.setComments(comments.get(manualRedactionEntry.getAnnotationId()));
|
||||
redactionLogEntry.setComments(convert(comments.get(manualRedactionEntry.getAnnotationId())));
|
||||
redactionLogEntry.setTextBefore(manualRedactionEntry.getTextBefore());
|
||||
redactionLogEntry.setTextAfter(manualRedactionEntry.getTextAfter());
|
||||
|
||||
@@ -310,6 +360,11 @@ public class RedactionLogMergeService {
|
||||
return redactionLogEntries;
|
||||
}
|
||||
|
||||
private List<RedactionLogComment> convert(List<Comment> comments) {
|
||||
return comments == null ? null :comments.stream().map(c -> new RedactionLogComment(c.getId(), c.getUser(), c.getText(), c.getAnnotationId(),
|
||||
c.getFileId(), c.getDate(), c.getSoftDeletedTime())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
private List<Rectangle> convertPositions(
|
||||
List<com.iqser.red.service.persistence.service.v1.api.model.annotations.Rectangle> positions) {
|
||||
@@ -345,6 +400,7 @@ public class RedactionLogMergeService {
|
||||
.isDossierDictionaryEntry(manualRedactionEntry.isAddToDossierDictionary())
|
||||
.legalBasis(manualRedactionEntry.getLegalBasis())
|
||||
.value(manualRedactionEntry.getValue())
|
||||
.sourceId(manualRedactionEntry.getSourceId())
|
||||
.section(manualRedactionEntry.getSection())
|
||||
.type(manualRedactionEntry.getType())
|
||||
.redacted(true)
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ public class SectionGridCreatorService {
|
||||
.getRectanglesPerPage()
|
||||
.computeIfAbsent(page, (x) -> new ArrayList<>())
|
||||
.add(new SectionRectangle(new Point(textBlock.getMinX(), textBlock.getMinY()), textBlock.getWidth(), textBlock
|
||||
.getHeight(), i + 1, paragraph.getPageBlocks().size()));
|
||||
.getHeight(), i + 1, paragraph.getPageBlocks().size(),null));
|
||||
|
||||
} else if (textBlock instanceof Table) {
|
||||
|
||||
|
||||
+4
-4
@@ -79,7 +79,7 @@ public class SectionTextBuilderService {
|
||||
.get(0)
|
||||
.getSequences()
|
||||
.get(0)
|
||||
.getPage());
|
||||
.getPage(), null);
|
||||
sectionText.getSectionAreas().add(sectionArea);
|
||||
sectionText.getTextBlocks().addAll(cell.getTextBlocks());
|
||||
|
||||
@@ -141,7 +141,7 @@ public class SectionTextBuilderService {
|
||||
.get(0)
|
||||
.getSequences()
|
||||
.get(0)
|
||||
.getPage());
|
||||
.getPage(), null);
|
||||
sectionText.getTextBlocks().addAll(cell.getTextBlocks());
|
||||
sectionText.getSectionAreas().add(sectionArea);
|
||||
|
||||
@@ -171,11 +171,11 @@ public class SectionTextBuilderService {
|
||||
SectionText sectionText = new SectionText();
|
||||
for (TextBlock paragraphTextBlock : paragraphTextBlocks) {
|
||||
SectionArea sectionArea = new SectionArea(new Point(paragraphTextBlock.getMinX(), paragraphTextBlock.getMinY()), paragraphTextBlock
|
||||
.getWidth(), paragraphTextBlock.getHeight(), paragraphTextBlock.getPage());
|
||||
.getWidth(), paragraphTextBlock.getHeight(), paragraphTextBlock.getPage(), null);
|
||||
sectionText.getSectionAreas().add(sectionArea);
|
||||
}
|
||||
|
||||
sectionText.setText(searchableText.toString());
|
||||
sectionText.setText(searchableText.asString());
|
||||
sectionText.setHeadline(headline);
|
||||
sectionText.setSectionNumber(sectionNumber.intValue());
|
||||
sectionText.setTable(false);
|
||||
|
||||
+6
-2
@@ -4,6 +4,8 @@ import com.iqser.red.service.redaction.v1.server.redaction.model.Dictionary;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Entity;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
|
||||
import com.iqser.red.service.redaction.v1.server.settings.RedactionServiceSettings;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -19,6 +21,7 @@ public class SurroundingWordsService {
|
||||
private final RedactionServiceSettings redactionServiceSettings;
|
||||
|
||||
|
||||
@Timed("redactmanager_addSurroundingText")
|
||||
public void addSurroundingText(Set<Entity> entities, SearchableText searchableText, Dictionary dictionary) {
|
||||
|
||||
if (entities.isEmpty()) {
|
||||
@@ -31,7 +34,7 @@ public class SurroundingWordsService {
|
||||
if (dictionary != null && dictionary.isHint(entity.getType())) {
|
||||
continue;
|
||||
}
|
||||
findSurroundingWords(entity, searchableText.toString(), entity.getStart(), entity.getEnd());
|
||||
findSurroundingWords(entity, searchableText.asString(), entity.getStart(), entity.getEnd());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not get surrounding text!");
|
||||
@@ -39,6 +42,7 @@ public class SurroundingWordsService {
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_addSurroundingText_tables")
|
||||
public void addSurroundingText(Set<Entity> entities, SearchableText searchableText, Dictionary dictionary,
|
||||
List<Integer> cellstarts) {
|
||||
|
||||
@@ -47,7 +51,7 @@ public class SurroundingWordsService {
|
||||
}
|
||||
|
||||
try {
|
||||
String searchableString = searchableText.toString();
|
||||
String searchableString = searchableText.asString();
|
||||
|
||||
if (cellstarts != null) {
|
||||
for (int i = 0; i < cellstarts.size(); i++) {
|
||||
|
||||
+82
-108
@@ -1,118 +1,65 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.model.Engine;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Dictionary;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryIncrementValue;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryModel;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Entity;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityPositionSequence;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityType;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.Image;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.*;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
@SuppressWarnings("PMD")
|
||||
public class EntitySearchUtils {
|
||||
|
||||
public boolean sectionContainsAny(String sectionText, Set<DictionaryIncrementValue> values) {
|
||||
|
||||
String inputString = sectionText.toLowerCase(Locale.ROOT);
|
||||
|
||||
for (DictionaryIncrementValue value : values) {
|
||||
|
||||
String cleanValue = value.getValue().toLowerCase(Locale.ROOT).trim();
|
||||
|
||||
if (cleanValue.length() <= 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int startIndex;
|
||||
int stopIndex = 0;
|
||||
do {
|
||||
startIndex = inputString.indexOf(cleanValue, stopIndex);
|
||||
stopIndex = startIndex + cleanValue.length();
|
||||
|
||||
if (startIndex > -1 && (startIndex == 0 || Character.isWhitespace(inputString.charAt(startIndex - 1)) || isSeparator(inputString.charAt(startIndex - 1))) && (stopIndex == inputString.length() || isSeparator(inputString.charAt(stopIndex)))) {
|
||||
return true;
|
||||
}
|
||||
} while (startIndex > -1);
|
||||
}
|
||||
return false;
|
||||
public boolean sectionContainsAny(String sectionText, SearchImplementation searchImplementation) {
|
||||
return searchImplementation.atLeastOneMatches(sectionText);
|
||||
}
|
||||
|
||||
public Set<Entity> findEntities(String inputString, SearchImplementation searchImplementation, DictionaryModel type, FindEntityDetails details) {
|
||||
|
||||
public Set<Entity> findEntities(String inputString, Set<String> values, DictionaryModel type, String headline, int sectionNumber, boolean isDictionaryEntry, boolean isDossierDictionary,
|
||||
Engine engine, boolean ignoreMinLength, boolean asRecommendation) {
|
||||
Set<Entity> found = find(inputString, searchImplementation, details);
|
||||
|
||||
Set<Entity> found = find(inputString, values, type.getType(), headline, sectionNumber, isDictionaryEntry, isDossierDictionary, engine, ignoreMinLength, asRecommendation ? EntityType.RECOMMENDATION : EntityType.ENTITY);
|
||||
|
||||
if (details.getEntityType() == EntityType.RECOMMENDATION) {
|
||||
Set<Entity> falseRecommendations = find(inputString, type.getFalseRecommendationsSearch(), details.withEntityType(EntityType.FALSE_RECOMMENDATION));
|
||||
removeFalsePositives(found, falseRecommendations);
|
||||
// RED-4799 - false positives are no longer in redaction log
|
||||
// found.addAll(falseRecommendations);
|
||||
} else {
|
||||
Set<Entity> falsePositives = find(inputString, type.getFalsePositiveSearch(), details.withEntityType(EntityType.FALSE_POSITIVE));
|
||||
removeFalsePositives(found, falsePositives);
|
||||
// RED-4799 - false positives are no longer in redaction log
|
||||
// found.addAll(falsePositives);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
public Set<Entity> find(String inputString, Set<String> values, String type, String headline, int sectionNumber, boolean isDictionaryEntry, boolean isDossierDictionary,
|
||||
Engine engine, boolean ignoreMinLength, EntityType entityType) {
|
||||
Set<Entity> found = new HashSet<>();
|
||||
|
||||
for (String value : values) {
|
||||
|
||||
String cleanValue = value.trim();
|
||||
|
||||
if (!ignoreMinLength && cleanValue.length() <= 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int startIndex;
|
||||
int stopIndex = 0;
|
||||
do {
|
||||
startIndex = inputString.indexOf(cleanValue, stopIndex);
|
||||
stopIndex = startIndex + cleanValue.length();
|
||||
|
||||
if (startIndex > -1 && (startIndex == 0 || Character.isWhitespace(inputString.charAt(startIndex - 1)) || isSeparator(inputString.charAt(startIndex - 1))) && (stopIndex == inputString.length() || isSeparator(inputString.charAt(stopIndex)))) {
|
||||
found.add(new Entity(inputString.substring(startIndex, stopIndex), type, startIndex, stopIndex, headline, sectionNumber, isDictionaryEntry, isDossierDictionary, engine, entityType));
|
||||
}
|
||||
} while (startIndex > -1);
|
||||
if (details.getEntityType() == EntityType.RECOMMENDATION) {
|
||||
Set<Entity> falseRecommendations = find(inputString, type.getFalseRecommendationsSearch(), details.withEntityType(EntityType.FALSE_RECOMMENDATION));
|
||||
removeFalsePositives(found, falseRecommendations);
|
||||
found.addAll(falseRecommendations);
|
||||
} else {
|
||||
Set<Entity> falsePositives = find(inputString, type.getFalsePositiveSearch(), details.withEntityType(EntityType.FALSE_POSITIVE));
|
||||
removeFalsePositives(found, falsePositives);
|
||||
found.addAll(falsePositives);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public Set<Entity> find(String inputString, SearchImplementation searchImplementation, FindEntityDetails findEntityDetails) {
|
||||
Set<Entity> entities = new HashSet<>();
|
||||
|
||||
searchImplementation.getMatches(inputString).forEach(match -> validateAndAddEntity(entities, findEntityDetails, inputString, match.getStartIndex(), match.getEndIndex()));
|
||||
|
||||
private boolean isSeparator(char c) {
|
||||
return entities;
|
||||
}
|
||||
|
||||
return Character.isWhitespace(c) || Pattern.matches("\\p{Punct}", String.valueOf(c)) || c == '\"' || c == '‘' || c == '’';
|
||||
private void validateAndAddEntity(Set<Entity> entities, FindEntityDetails findEntityDetails, String inputString, int startIndex, int stopIndex) {
|
||||
|
||||
if ((startIndex == 0 || SeparatorUtils.isSeparator(inputString.charAt(startIndex - 1)))
|
||||
&& (stopIndex == inputString.length() || SeparatorUtils.isSeparator(inputString.charAt(stopIndex)))) {
|
||||
entities.add(new Entity(inputString.substring(startIndex, stopIndex), findEntityDetails.getType(), startIndex, stopIndex,
|
||||
findEntityDetails.getHeadline(), findEntityDetails.getSectionNumber(), findEntityDetails.isDictionaryEntry(),
|
||||
findEntityDetails.isDossierDictionary(), findEntityDetails.getEngine(), findEntityDetails.getEntityType()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Set<Entity> clearAndFindPositions(Set<Entity> entities, SearchableText text, Dictionary dictionary) {
|
||||
public Set<Entity> clearAndFindPositions(Set<Entity> entities, SearchableText text, Dictionary dictionary, ManualRedactions manualRedactions) {
|
||||
|
||||
Map<String, List<Entity>> entitiesByWord = new HashMap<>();
|
||||
|
||||
@@ -136,12 +83,45 @@ public class EntitySearchUtils {
|
||||
}
|
||||
}
|
||||
|
||||
if (manualRedactions != null && manualRedactions.getResizeRedactions() != null && !manualRedactions.getResizeRedactions().isEmpty()){
|
||||
applyResizeRedactions(entities, manualRedactions);
|
||||
}
|
||||
|
||||
removeEntitiesContainedInLarger(entities);
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
|
||||
private Set<Entity> applyResizeRedactions(Set<Entity> entitiesWithPositions, ManualRedactions manualRedactions) {
|
||||
|
||||
if (manualRedactions == null || manualRedactions.getResizeRedactions() == null || manualRedactions.getResizeRedactions().isEmpty()){
|
||||
return entitiesWithPositions;
|
||||
}
|
||||
|
||||
entitiesWithPositions.forEach(e -> e.getPositionSequences().forEach(pos -> {
|
||||
manualRedactions.getResizeRedactions().stream().filter(resize -> resize.getStatus().equals(AnnotationStatus.APPROVED)).forEach(resize -> {
|
||||
if (resize.getAnnotationId().equals(pos.getId())) {
|
||||
if (resize.getValue().length() < e.getWord().length() && e.getWord().contains(resize.getValue())) {
|
||||
int start = e.getWord().indexOf(resize.getValue());
|
||||
e.setStart(e.getStart() + start);
|
||||
e.setEnd(e.getStart() + resize.getValue().length());
|
||||
e.setResized(true);
|
||||
e.setWord(resize.getValue());
|
||||
} else if(resize.getValue().length() > e.getWord().length() && resize.getValue().contains(e.getWord())){
|
||||
int start = resize.getValue().indexOf(e.getWord());
|
||||
e.setStart(e.getStart() - start);
|
||||
e.setEnd(e.getStart() + resize.getValue().length());
|
||||
e.setResized(true);
|
||||
e.setWord(resize.getValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
return entitiesWithPositions;
|
||||
}
|
||||
|
||||
|
||||
public void removeFalsePositives(Set<Entity> entities, Set<Entity> falsePositives) {
|
||||
|
||||
@@ -150,37 +130,31 @@ public class EntitySearchUtils {
|
||||
for (Entity inner : entities) {
|
||||
if (inner.getWord().length() < word.getWord()
|
||||
.length() && inner.getStart() >= word.getStart() && inner.getEnd() <= word.getEnd() && word != inner && word.getSectionNumber() == inner.getSectionNumber()) {
|
||||
wordsToRemove.add(inner);
|
||||
wordsToRemove.add(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
entities.removeAll(wordsToRemove);
|
||||
wordsToRemove.forEach(entities::remove);
|
||||
entities.removeAll(falsePositives);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void removeEntitiesContainedInLarger(Set<Entity> entities) {
|
||||
|
||||
List<Entity> wordsToRemove = new ArrayList<>();
|
||||
for (Entity word : entities) {
|
||||
for (Entity inner : entities) {
|
||||
// skip cross-type false positives
|
||||
if(word.getEntityType() == EntityType.FALSE_POSITIVE && inner.getEntityType() == EntityType.ENTITY && !inner.getType().equals(word.getType())){
|
||||
continue;
|
||||
}
|
||||
// skip cross-type false reccommendations
|
||||
if(word.getEntityType() == EntityType.FALSE_RECOMMENDATION && inner.getEntityType() == EntityType.RECOMMENDATION && !inner.getType().equals(word.getType())){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inner.getWord().length() < word.getWord()
|
||||
.length() && inner.getStart() >= word.getStart() && inner.getEnd() <= word.getEnd() && word != inner && word.getSectionNumber() == inner.getSectionNumber()) {
|
||||
if (word.getEntityType().equals(EntityType.RECOMMENDATION) && inner.getEntityType().equals(EntityType.ENTITY)) {
|
||||
wordsToRemove.add(word);
|
||||
} else {
|
||||
wordsToRemove.add(inner);
|
||||
} else if(!(inner.getEntityType() == EntityType.FALSE_RECOMMENDATION && word.getEntityType() == EntityType.ENTITY ||
|
||||
inner.getEntityType() == EntityType.ENTITY && word.getEntityType() == EntityType.FALSE_RECOMMENDATION)) {
|
||||
if(inner.isResized()){
|
||||
wordsToRemove.add(word);
|
||||
} else {
|
||||
wordsToRemove.add(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,7 +201,7 @@ public class EntitySearchUtils {
|
||||
|
||||
if (entities.contains(found)) {
|
||||
Optional<Entity> existingOptional = entities.stream().filter(entity -> entity.equals(found)).findFirst();
|
||||
if (!existingOptional.isPresent()) {
|
||||
if (existingOptional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
var existing = existingOptional.get();
|
||||
@@ -237,10 +211,10 @@ public class EntitySearchUtils {
|
||||
existing.setLegalBasis(found.getLegalBasis());
|
||||
existing.setMatchedRule(found.getMatchedRule());
|
||||
existing.setRedactionReason(found.getRedactionReason());
|
||||
if(existing.getEntityType().equals(EntityType.RECOMMENDATION) && found.getEntityType().equals(EntityType.ENTITY)
|
||||
|| existing.getEntityType().equals(EntityType.ENTITY) && found.getEntityType().equals(EntityType.RECOMMENDATION)){
|
||||
if (existing.getEntityType().equals(EntityType.RECOMMENDATION) && found.getEntityType().equals(EntityType.ENTITY)
|
||||
|| existing.getEntityType().equals(EntityType.ENTITY) && found.getEntityType().equals(EntityType.RECOMMENDATION)) {
|
||||
existing.setEntityType(EntityType.ENTITY);
|
||||
if(found.isRedaction()){
|
||||
if (found.isRedaction()) {
|
||||
existing.setRedaction(true);
|
||||
}
|
||||
}
|
||||
@@ -296,7 +270,7 @@ public class EntitySearchUtils {
|
||||
for (Entity toAdd : toBeAdded) {
|
||||
if (existing.contains(toAdd)) {
|
||||
Optional<Entity> existingOptional = existing.stream().filter(entity -> entity.equals(toAdd)).findFirst();
|
||||
if (!existingOptional.isPresent()) {
|
||||
if (existingOptional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
var existingEntity = existingOptional.get();
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.utils;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.model.Engine;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class FindEntityDetails {
|
||||
|
||||
private String type;
|
||||
private String headline;
|
||||
private int sectionNumber;
|
||||
private boolean isDictionaryEntry;
|
||||
private boolean isDossierDictionary;
|
||||
private Engine engine;
|
||||
private EntityType entityType;
|
||||
|
||||
public FindEntityDetails withEntityType(EntityType entityType) {
|
||||
this.entityType = entityType;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.utils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.ahocorasick.trie.Trie;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Data
|
||||
public class SearchImplementation {
|
||||
|
||||
private boolean ignoreCase;
|
||||
private List<String> values;
|
||||
|
||||
private Pattern pattern;
|
||||
private Trie trie;
|
||||
|
||||
public SearchImplementation(String value, boolean ignoreCase) {
|
||||
this.values = List.of(value);
|
||||
this.ignoreCase = ignoreCase;
|
||||
this.createSearchImplementation();
|
||||
}
|
||||
|
||||
public SearchImplementation(Collection<String> values, boolean ignoreCase) {
|
||||
this.values = new ArrayList<>(values);
|
||||
this.ignoreCase = ignoreCase;
|
||||
this.createSearchImplementation();
|
||||
}
|
||||
|
||||
private void createSearchImplementation() {
|
||||
if (this.values.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.values.size() == 1) {
|
||||
var text = this.values.iterator().next();
|
||||
this.pattern = Pattern.compile(Pattern.quote(ignoreCase ? text.toLowerCase(Locale.ROOT) : text));
|
||||
} else {
|
||||
var builder = Trie.builder();
|
||||
if (this.ignoreCase) {
|
||||
builder.ignoreCase();
|
||||
}
|
||||
|
||||
builder.addKeywords(this.values);
|
||||
|
||||
this.trie = builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean atLeastOneMatches(String text) {
|
||||
String textToCheck = text;
|
||||
if (this.values.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (this.pattern != null) {
|
||||
if (ignoreCase) {
|
||||
textToCheck = textToCheck.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
return this.pattern.matcher(textToCheck).results().findAny().isPresent();
|
||||
} else {
|
||||
return this.trie.containsMatch(textToCheck);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<MatchPosition> getMatches(String text) {
|
||||
String textToCheck = text;
|
||||
if (this.values.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
if (this.pattern != null) {
|
||||
if (ignoreCase) {
|
||||
textToCheck = textToCheck.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
return this.pattern.matcher(textToCheck).results().map(r -> new MatchPosition(r.start(), r.end())).collect(Collectors.toList());
|
||||
} else {
|
||||
return this.trie.parseText(textToCheck).stream().map(r -> new MatchPosition(r.getStart(), r.getEnd() + 1)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class MatchPosition {
|
||||
private int startIndex;
|
||||
private int endIndex;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.utils;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SeparatorUtils {
|
||||
|
||||
private final static Set<Character> quotes = Set.of('\'', '\u0022', '\u00AB', '\u00BB', '\u2018', '\u2019', '\u201A', '\u201C', '\u201D', '\u201E'
|
||||
, '\u2039', '\u203A');
|
||||
|
||||
|
||||
public static boolean isSeparator(char c) {
|
||||
return Character.isWhitespace(c) || Pattern.matches("\\p{Punct}", String.valueOf(c)) || quotes.contains(c);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -36,10 +36,10 @@ public class ImageService {
|
||||
ImageServiceResponse imageServiceResponse = objectMapper.readValue(imageClassificationStream, ImageServiceResponse.class);
|
||||
|
||||
Map<Integer, List<PdfImage>> images = new HashMap<>();
|
||||
imageServiceResponse.getData().stream().forEach(imageMetadata -> {
|
||||
imageServiceResponse.getData().forEach(imageMetadata -> {
|
||||
var classification = imageMetadata.getFilters().isAllPassed() ? ImageType.valueOf(imageMetadata.getClassification().getLabel().toUpperCase(Locale.ROOT)) : ImageType.OTHER;
|
||||
images.computeIfAbsent(imageMetadata.getPosition().getPageNumber() ,x -> new ArrayList<>())
|
||||
.add(new PdfImage(new RedRectangle2D(imageMetadata.getPosition().getX1(), imageMetadata.getPosition().getY1(), imageMetadata.getGeometry().getWidth(), imageMetadata.getGeometry().getHeight()), classification, imageMetadata.getPosition().getPageNumber()));
|
||||
.add(new PdfImage(new RedRectangle2D(imageMetadata.getPosition().getX1(), imageMetadata.getPosition().getY1(), imageMetadata.getGeometry().getWidth(), imageMetadata.getGeometry().getHeight()), classification,imageMetadata.isAlpha(), imageMetadata.getPosition().getPageNumber()));
|
||||
});
|
||||
|
||||
return images;
|
||||
|
||||
+28
-54
@@ -1,5 +1,27 @@
|
||||
package com.iqser.red.service.redaction.v1.server.segmentation;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.service.BlockificationService;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.service.ClassificationService;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.PDFLinesTextStripper;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.CleanRulings;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.service.RulingCleaningService;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.service.TableExtractionService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -13,38 +35,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.service.BlockificationService;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.service.ClassificationService;
|
||||
import com.iqser.red.service.redaction.v1.server.memory.MemoryStats;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.PDFLinesTextStripper;
|
||||
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.CleanRulings;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.service.RulingCleaningService;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.service.TableExtractionService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PdfSegmentationService {
|
||||
|
||||
private final static int MAX_PAGES_BEFORE_GC = 250;
|
||||
|
||||
private final RulingCleaningService rulingCleaningService;
|
||||
private final TableExtractionService tableExtractionService;
|
||||
private final BlockificationService blockificationService;
|
||||
@@ -60,11 +55,10 @@ public class PdfSegmentationService {
|
||||
//create tempFile
|
||||
File tempFile;
|
||||
|
||||
if(SystemUtils.IS_OS_UNIX) {
|
||||
if (SystemUtils.IS_OS_UNIX) {
|
||||
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));
|
||||
tempFile = Files.createTempFile("document", ".pdf", attr).toFile();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
tempFile = Files.createTempFile("document", ".pdf").toFile();
|
||||
tempFile.setReadable(true, true);
|
||||
tempFile.setWritable(true, true);
|
||||
@@ -78,15 +72,12 @@ public class PdfSegmentationService {
|
||||
Document document = new Document();
|
||||
List<Page> pages = new ArrayList<>();
|
||||
|
||||
pdDocument = reinitializePDDocument(tempFile, null);
|
||||
pdDocument = PDDocument.load(tempFile, MemoryUsageSetting.setupMixed(1024 * 1024 * 64));
|
||||
pdDocument.setAllSecurityToBeRemoved(true);
|
||||
long pageCount = pdDocument.getNumberOfPages();
|
||||
|
||||
for (int pageNumber = 1; pageNumber <= pageCount; pageNumber++) {
|
||||
|
||||
if (pageNumber % MAX_PAGES_BEFORE_GC == 0) {
|
||||
pdDocument = reinitializePDDocument(tempFile, pdDocument);
|
||||
}
|
||||
|
||||
PDFLinesTextStripper stripper = new PDFLinesTextStripper();
|
||||
PDPage pdPage = pdDocument.getPage(pageNumber - 1);
|
||||
stripper.setPageNumber(pageNumber);
|
||||
@@ -107,6 +98,7 @@ public class PdfSegmentationService {
|
||||
Page page = blockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings.getHorizontal(), cleanRulings
|
||||
.getVertical());
|
||||
|
||||
|
||||
PDRectangle cropbox = pdPage.getCropBox();
|
||||
float cropboxArea = cropbox.getHeight() * cropbox.getWidth();
|
||||
page.setCropBoxArea(cropboxArea);
|
||||
@@ -134,11 +126,10 @@ public class PdfSegmentationService {
|
||||
sectionsBuilderService.buildSections(document);
|
||||
sectionsBuilderService.addImagesToSections(document);
|
||||
|
||||
pdDocument = reinitializePDDocument(tempFile, pdDocument);
|
||||
|
||||
IOUtils.close(pdDocument);
|
||||
|
||||
if(!tempFile.delete()){
|
||||
if (!tempFile.delete()) {
|
||||
log.warn("Could not delete tmp file");
|
||||
}
|
||||
|
||||
@@ -152,23 +143,6 @@ public class PdfSegmentationService {
|
||||
}
|
||||
|
||||
|
||||
private PDDocument reinitializePDDocument(File tempFile, PDDocument pdDocument) throws IOException {
|
||||
|
||||
if (pdDocument != null) {
|
||||
pdDocument.close();
|
||||
}
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
|
||||
MemoryStats.printMemoryStats();
|
||||
|
||||
var newPDDocument = PDDocument.load(tempFile, MemoryUsageSetting.setupTempFileOnly());
|
||||
newPDDocument.setAllSecurityToBeRemoved(true);
|
||||
|
||||
return newPDDocument;
|
||||
}
|
||||
|
||||
|
||||
private void increaseDocumentStatistics(Page page, Document document) {
|
||||
|
||||
if (!page.isLandscape()) {
|
||||
|
||||
+79
-38
@@ -1,16 +1,31 @@
|
||||
package com.iqser.red.service.redaction.v1.server.segmentation;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Footer;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Header;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Page;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Paragraph;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.UnclassifiedText;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Cell;
|
||||
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Table;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SectionsBuilderService {
|
||||
|
||||
@@ -53,8 +68,7 @@ public class SectionsBuilderService {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev != null && current.getClassification().startsWith("H ") && !prev.getClassification()
|
||||
.startsWith("H ") || !document.isHeadlines()) {
|
||||
if (prev != null && current.getClassification().startsWith("H ") && !prev.getClassification().startsWith("H ") || !document.isHeadlines()) {
|
||||
Paragraph chunkBlock = buildTextBlock(chunkWords, lastHeadline);
|
||||
chunkBlock.setHeadline(lastHeadline);
|
||||
if (document.isHeadlines()) {
|
||||
@@ -100,17 +114,19 @@ public class SectionsBuilderService {
|
||||
|
||||
public void addImagesToSections(Document document) {
|
||||
|
||||
Map<Integer, SortedSet<Paragraph>> paragraphMap = new HashMap<>();
|
||||
Map<Integer, List<Paragraph>> paragraphMap = new HashMap<>();
|
||||
for (Paragraph paragraph : document.getParagraphs()) {
|
||||
for (AbstractTextContainer container : paragraph.getPageBlocks()) {
|
||||
paragraphMap.computeIfAbsent(container.getPage(), x -> new TreeSet<>()).add(paragraph);
|
||||
|
||||
paragraphMap.computeIfAbsent(container.getPage(), c -> new ArrayList<>()).add(paragraph);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (paragraphMap.isEmpty()) {
|
||||
Paragraph paragraph = new Paragraph();
|
||||
document.getParagraphs().add(paragraph);
|
||||
paragraphMap.computeIfAbsent(1, x -> new TreeSet<>()).add(paragraph);
|
||||
paragraphMap.computeIfAbsent(1, x -> new ArrayList<>()).add(paragraph);
|
||||
}
|
||||
|
||||
// first page is always a paragraph, else we can't process pages 1..N,
|
||||
@@ -118,12 +134,12 @@ public class SectionsBuilderService {
|
||||
if (paragraphMap.get(1) == null) {
|
||||
Paragraph paragraph = new Paragraph();
|
||||
document.getParagraphs().add(paragraph);
|
||||
paragraphMap.computeIfAbsent(1, x -> new TreeSet<>()).add(paragraph);
|
||||
paragraphMap.computeIfAbsent(1, x -> new ArrayList<>()).add(paragraph);
|
||||
}
|
||||
|
||||
for (Page page : document.getPages()) {
|
||||
for (PdfImage image : page.getImages()) {
|
||||
SortedSet<Paragraph> paragraphsOnPage = paragraphMap.get(page.getPageNumber());
|
||||
List<Paragraph> paragraphsOnPage = paragraphMap.get(page.getPageNumber());
|
||||
if (paragraphsOnPage == null) {
|
||||
int i = page.getPageNumber();
|
||||
while (paragraphsOnPage == null) {
|
||||
@@ -131,27 +147,63 @@ public class SectionsBuilderService {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
Float perviousEnd = 0f;
|
||||
for (Paragraph paragraph : paragraphsOnPage) {
|
||||
Float currentEnd = 0f;
|
||||
Float xMin = null;
|
||||
Float yMin = null;
|
||||
Float xMax = null;
|
||||
Float yMax = null;
|
||||
|
||||
for (AbstractTextContainer abs : paragraph.getPageBlocks()) {
|
||||
if (abs.getPage() != page.getPageNumber()) {
|
||||
continue;
|
||||
}
|
||||
if (abs.getMaxY() > currentEnd) {
|
||||
currentEnd = abs.getMaxY();
|
||||
|
||||
if (abs.getMinX() < abs.getMaxX()) {
|
||||
if (xMin == null || abs.getMinX() < xMin) {
|
||||
xMin = abs.getMinX();
|
||||
}
|
||||
if (xMax == null || abs.getMaxX() > xMax) {
|
||||
xMax = abs.getMaxX();
|
||||
}
|
||||
} else {
|
||||
if (xMin == null || abs.getMaxX() < xMin) {
|
||||
xMin = abs.getMaxX();
|
||||
}
|
||||
if (xMax == null || abs.getMinX() > xMax) {
|
||||
xMax = abs.getMinX();
|
||||
}
|
||||
}
|
||||
|
||||
if (abs.getMinY() < abs.getMaxY()) {
|
||||
if (yMin == null || abs.getMinY() < yMin) {
|
||||
yMin = abs.getMinY();
|
||||
}
|
||||
if (yMax == null || abs.getMaxY() > yMax) {
|
||||
yMax = abs.getMaxY();
|
||||
}
|
||||
} else {
|
||||
if (yMin == null || abs.getMaxY() < yMin) {
|
||||
yMin = abs.getMaxY();
|
||||
}
|
||||
if (yMax == null || abs.getMinY() > yMax) {
|
||||
yMax = abs.getMinY();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (image.getPosition().getY() >= perviousEnd && image.getPosition().getY() <= currentEnd) {
|
||||
log.debug("Image position x: {}, y: {}", image.getPosition().getX(), image.getPosition().getY());
|
||||
log.debug("Paragraph position xMin: {}, xMax: {}, yMin: {}, yMax: {}", xMin, xMax, yMin, yMax);
|
||||
|
||||
if (xMin != null && xMax != null && yMin != null && yMax != null && image.getPosition().getX() >= xMin && image.getPosition()
|
||||
.getX() <= xMax && image.getPosition().getY() >= yMin && image.getPosition().getY() <= yMax) {
|
||||
paragraph.getImages().add(image);
|
||||
image.setAppendedToParagraph(true);
|
||||
}
|
||||
perviousEnd = currentEnd;
|
||||
}
|
||||
if (!image.isAppendedToParagraph()) {
|
||||
paragraphsOnPage.first().getImages().add(image);
|
||||
log.debug("Image uses first paragraph");
|
||||
paragraphsOnPage.get(0).getImages().add(image);
|
||||
image.setAppendedToParagraph(true);
|
||||
}
|
||||
}
|
||||
@@ -166,9 +218,7 @@ public class SectionsBuilderService {
|
||||
List<Cell> previousTableNonHeaderRow = getRowWithNonHeaderCells(previousTable);
|
||||
List<Cell> tableNonHeaderRow = getRowWithNonHeaderCells(currentTable);
|
||||
// Allow merging of tables if header row is separated from first logical non-header row
|
||||
if (previousTableNonHeaderRow.isEmpty() && previousTable.getRowCount() == 1 && previousTable.getRows()
|
||||
.get(0)
|
||||
.size() == tableNonHeaderRow.size()) {
|
||||
if (previousTableNonHeaderRow.isEmpty() && previousTable.getRowCount() == 1 && previousTable.getRows().get(0).size() == tableNonHeaderRow.size()) {
|
||||
previousTableNonHeaderRow = previousTable.getRows().get(0).stream().map(cell -> {
|
||||
Cell fakeCell = new Cell(cell.getPoints()[0], cell.getPoints()[2]);
|
||||
fakeCell.setHeaderCells(Collections.singletonList(cell));
|
||||
@@ -178,8 +228,7 @@ public class SectionsBuilderService {
|
||||
if (previousTableNonHeaderRow.size() == tableNonHeaderRow.size()) {
|
||||
for (int i = currentTable.getRowCount() - 1; i >= 0; i--) { // Non header rows are most likely at bottom of table
|
||||
List<Cell> row = currentTable.getRows().get(i);
|
||||
if (row.size() == tableNonHeaderRow.size() && row.stream()
|
||||
.allMatch(cell -> cell.getHeaderCells().isEmpty())) {
|
||||
if (row.size() == tableNonHeaderRow.size() && row.stream().allMatch(cell -> cell.getHeaderCells().isEmpty())) {
|
||||
for (int j = 0; j < row.size(); j++) {
|
||||
row.get(j).setHeaderCells(previousTableNonHeaderRow.get(j).getHeaderCells());
|
||||
}
|
||||
@@ -229,24 +278,20 @@ public class SectionsBuilderService {
|
||||
TextBlock wordBlock = (TextBlock) container;
|
||||
|
||||
if (textBlock == null) {
|
||||
textBlock = new TextBlock(wordBlock.getMinX(), wordBlock.getMaxX(), wordBlock.getMinY(), wordBlock.getMaxY(), wordBlock
|
||||
.getSequences(), wordBlock.getRotation());
|
||||
textBlock = new TextBlock(wordBlock.getMinX(), wordBlock.getMaxX(), wordBlock.getMinY(), wordBlock.getMaxY(), wordBlock.getSequences(), wordBlock.getRotation());
|
||||
textBlock.setPage(wordBlock.getPage());
|
||||
} else if (splitByTable) {
|
||||
textBlock = new TextBlock(wordBlock.getMinX(), wordBlock.getMaxX(), wordBlock.getMinY(), wordBlock.getMaxY(), wordBlock
|
||||
.getSequences(), wordBlock.getRotation());
|
||||
textBlock = new TextBlock(wordBlock.getMinX(), wordBlock.getMaxX(), wordBlock.getMinY(), wordBlock.getMaxY(), wordBlock.getSequences(), wordBlock.getRotation());
|
||||
textBlock.setPage(wordBlock.getPage());
|
||||
alreadyAdded = false;
|
||||
} else if (pageBefore != -1 && wordBlock.getPage() != pageBefore) {
|
||||
textBlock.setPage(pageBefore);
|
||||
paragraph.getPageBlocks().add(textBlock);
|
||||
textBlock = new TextBlock(wordBlock.getMinX(), wordBlock.getMaxX(), wordBlock.getMinY(), wordBlock.getMaxY(), wordBlock
|
||||
.getSequences(), wordBlock.getRotation());
|
||||
textBlock = new TextBlock(wordBlock.getMinX(), wordBlock.getMaxX(), wordBlock.getMinY(), wordBlock.getMaxY(), wordBlock.getSequences(), wordBlock.getRotation());
|
||||
textBlock.setPage(wordBlock.getPage());
|
||||
} else {
|
||||
TextBlock spatialEntity = textBlock.union(wordBlock);
|
||||
textBlock.resize(spatialEntity.getMinX(), spatialEntity.getMinY(), spatialEntity.getWidth(), spatialEntity
|
||||
.getHeight());
|
||||
textBlock.resize(spatialEntity.getMinX(), spatialEntity.getMinY(), spatialEntity.getWidth(), spatialEntity.getHeight());
|
||||
}
|
||||
pageBefore = wordBlock.getPage();
|
||||
splitByTable = false;
|
||||
@@ -268,11 +313,7 @@ public class SectionsBuilderService {
|
||||
|
||||
private boolean hasInvalidHeaderInformation(Table table) {
|
||||
|
||||
return table.getRows()
|
||||
.stream()
|
||||
.flatMap(row -> row.stream().filter(cell -> CollectionUtils.isNotEmpty(cell.getHeaderCells())))
|
||||
.findAny()
|
||||
.isEmpty();
|
||||
return table.getRows().stream().flatMap(row -> row.stream().filter(cell -> CollectionUtils.isNotEmpty(cell.getHeaderCells()))).findAny().isEmpty();
|
||||
|
||||
}
|
||||
|
||||
|
||||
+86
-20
@@ -1,12 +1,7 @@
|
||||
package com.iqser.red.service.redaction.v1.server.storage;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.dslplatform.json.DslJson;
|
||||
import com.dslplatform.json.runtime.Settings;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
|
||||
import com.iqser.red.service.redaction.v1.model.ImportedRedactions;
|
||||
@@ -18,18 +13,28 @@ import com.iqser.red.service.redaction.v1.server.exception.NotFoundException;
|
||||
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
|
||||
import com.iqser.red.storage.commons.service.StorageService;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RedactionStorageService {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final StorageService storageService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final DslJson<Object> dslJson = new DslJson<>(Settings.basicSetup());
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@@ -40,15 +45,33 @@ public class RedactionStorageService {
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public void storeObject(String dossierId, String fileId, FileType fileType, Object any) {
|
||||
public void storeObject(String dossierId, String fileId, FileType fileType, InputStream inputStream) {
|
||||
|
||||
try (var baos = new ByteArrayOutputStream()){
|
||||
objectMapper.writeValue(baos, any);
|
||||
storageService.storeObject(StorageIdUtils.getStorageId(dossierId, fileId, fileType), baos);
|
||||
}
|
||||
storageService.storeObject(StorageIdUtils.getStorageId(dossierId, fileId, fileType), inputStream);
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@Timed("redactmanager_storeObject")
|
||||
public void storeObject(String dossierId, String fileId, FileType fileType, Object any) {
|
||||
|
||||
var bytes = serializeObject(any);
|
||||
storageService.storeObject(StorageIdUtils.getStorageId(dossierId, fileId, fileType), bytes);
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@Timed("redactmanager_serializeObject")
|
||||
private byte[] serializeObject(Object any) {
|
||||
|
||||
var baos = new ByteArrayOutputStream();
|
||||
dslJson.serialize(any, baos);
|
||||
return baos.toByteArray();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_getImportedRedactions")
|
||||
public ImportedRedactions getImportedRedactions(String dossierId, String fileId) {
|
||||
|
||||
InputStreamResource inputStreamResource;
|
||||
@@ -59,14 +82,22 @@ public class RedactionStorageService {
|
||||
return null;
|
||||
}
|
||||
|
||||
return deserializeImportedRedactions(inputStreamResource);
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_deserializeImportedRedactions")
|
||||
private ImportedRedactions deserializeImportedRedactions(InputStreamResource inputStreamResource) {
|
||||
|
||||
try {
|
||||
return objectMapper.readValue(inputStreamResource.getInputStream(), ImportedRedactions.class);
|
||||
return dslJson.deserialize(ImportedRedactions.class, inputStreamResource.getInputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not imported redactions", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_getRedactionLog")
|
||||
public RedactionLog getRedactionLog(String dossierId, String fileId) {
|
||||
|
||||
InputStreamResource inputStreamResource;
|
||||
@@ -77,14 +108,22 @@ public class RedactionStorageService {
|
||||
return null;
|
||||
}
|
||||
|
||||
return deserializeRedactionLog(inputStreamResource);
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_deserializeRedactionLog")
|
||||
private RedactionLog deserializeRedactionLog(InputStreamResource inputStreamResource) {
|
||||
|
||||
try {
|
||||
return objectMapper.readValue(inputStreamResource.getInputStream(), RedactionLog.class);
|
||||
return dslJson.deserialize(RedactionLog.class, inputStreamResource.getInputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not convert RedactionLog", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_getText")
|
||||
public Text getText(String dossierId, String fileId) {
|
||||
|
||||
InputStreamResource inputStreamResource;
|
||||
@@ -95,14 +134,22 @@ public class RedactionStorageService {
|
||||
return null;
|
||||
}
|
||||
|
||||
return deserializeText(inputStreamResource);
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_deserializeText")
|
||||
private Text deserializeText(InputStreamResource inputStreamResource) {
|
||||
|
||||
try {
|
||||
return objectMapper.readValue(inputStreamResource.getInputStream(), Text.class);
|
||||
return dslJson.deserialize(Text.class, inputStreamResource.getInputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not convert Text", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_getNerEntities")
|
||||
public NerEntities getNerEntities(String dossierId, String fileId) {
|
||||
|
||||
InputStreamResource inputStreamResource;
|
||||
@@ -112,23 +159,42 @@ public class RedactionStorageService {
|
||||
throw new NotFoundException("NER Entities are not available.");
|
||||
}
|
||||
|
||||
return deserializeNerEntities(inputStreamResource);
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_deserializeNerEntities")
|
||||
private NerEntities deserializeNerEntities(InputStreamResource inputStreamResource) {
|
||||
|
||||
try {
|
||||
return objectMapper.readValue(inputStreamResource.getInputStream(), NerEntities.class);
|
||||
return dslJson.deserialize(NerEntities.class, inputStreamResource.getInputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not convert NER Entities", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_getSectionGrid")
|
||||
public SectionGrid getSectionGrid(String dossierId, String fileId) {
|
||||
|
||||
InputStreamResource inputStreamResource;
|
||||
try {
|
||||
var sectionGrid = storageService.getObject(StorageIdUtils.getStorageId(dossierId, fileId, FileType.SECTION_GRID));
|
||||
return objectMapper.readValue(sectionGrid.getInputStream(), SectionGrid.class);
|
||||
inputStreamResource = storageService.getObject(StorageIdUtils.getStorageId(dossierId, fileId, FileType.SECTION_GRID));
|
||||
} catch (StorageObjectDoesNotExist e) {
|
||||
throw new NotFoundException("Section Grid is not available.");
|
||||
}
|
||||
|
||||
return deserializeSectionGrid(inputStreamResource);
|
||||
}
|
||||
|
||||
|
||||
@Timed("redactmanager_deserializeSectionGrid")
|
||||
private SectionGrid deserializeSectionGrid(InputStreamResource inputStreamResource) {
|
||||
|
||||
try {
|
||||
return dslJson.deserialize(SectionGrid.class, inputStreamResource.getInputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not convert RedactionLog", e);
|
||||
throw new RuntimeException("Could not convert SectionGrid", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -1,9 +1,9 @@
|
||||
package com.iqser.red.service.redaction.v1.server.tableextraction.model;
|
||||
|
||||
import com.dslplatform.json.JsonAttribute;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.Orientation;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -33,11 +33,13 @@ public abstract class AbstractTextContainer {
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getHeight() {
|
||||
return maxY - minY;
|
||||
}
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
@JsonAttribute(ignore = true)
|
||||
public float getWidth() {
|
||||
return maxX - minX;
|
||||
}
|
||||
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package com.iqser.red.service.redaction.v1.server;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntry;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
|
||||
import com.iqser.red.service.redaction.v1.server.client.DictionaryClient;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryVersion;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.service.DictionaryService;
|
||||
import com.iqser.red.storage.commons.StorageAutoConfiguration;
|
||||
import com.iqser.red.storage.commons.service.StorageService;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.kie.api.runtime.KieContainer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@Import(RedactionIntegrationTest.RedactionIntegrationTestConfiguration.class)
|
||||
public class DictionaryServiceTest {
|
||||
|
||||
@MockBean
|
||||
protected KieContainer kieContainer;
|
||||
|
||||
@MockBean
|
||||
protected DictionaryClient dictionaryClient;
|
||||
|
||||
@Autowired
|
||||
protected DictionaryService dictionaryService;
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration(exclude = {RabbitAutoConfiguration.class, StorageAutoConfiguration.class})
|
||||
public static class RedactionIntegrationTestConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public StorageService inmemoryStorage() {
|
||||
|
||||
return new FileSystemBackedStorageService();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDictionaryServiceConsistency() {
|
||||
|
||||
when(dictionaryClient.getVersion(anyString())).thenReturn(0L);
|
||||
when(dictionaryClient.getColors(anyString())).thenReturn(new Colors("dossierTemplateId", "#cccccc",
|
||||
"#cccccc", "#cccccc", "#cccccc", "#cccccc", "#cccccc",
|
||||
"#cccccc", "#cccccc", "#cccccc", "#cccccc"));
|
||||
|
||||
var type1 = new Type();
|
||||
type1.setType("type1");
|
||||
type1.setId("type1");
|
||||
type1.setVersion(1L);
|
||||
type1.setHexColor("#cccccc");
|
||||
type1.setHasDictionary(true);
|
||||
type1.setEntries(Stream.of("a", "b", "c").map(t -> new DictionaryEntry(1, t, 1L, false, "type1")).collect(Collectors.toList()));
|
||||
|
||||
|
||||
var type2 = new Type();
|
||||
type2.setType("type2");
|
||||
type2.setId("type2");
|
||||
type2.setVersion(1L);
|
||||
type2.setHexColor("#cccccc");
|
||||
type2.setHasDictionary(true);
|
||||
type2.setEntries(Stream.of("d", "e", "f").map(t -> new DictionaryEntry(1, t, 1L, false, "type2")).collect(Collectors.toList()));
|
||||
|
||||
|
||||
var type1Updated = new Type();
|
||||
type1Updated.setType("type1");
|
||||
type1Updated.setId("type1");
|
||||
type1Updated.setVersion(2L);
|
||||
type1Updated.setHexColor("#cccccc");
|
||||
type1Updated.setHasDictionary(true);
|
||||
type1Updated.setEntries(Stream.of("z", "q", "x").map(t -> new DictionaryEntry(1, t, 2L, false, "type1")).collect(Collectors.toList()));
|
||||
|
||||
|
||||
when(dictionaryClient.getAllTypesForDossierTemplate(anyString(), anyBoolean())).thenReturn(List.of(type1, type2));
|
||||
when(dictionaryClient.getDictionaryForType("type1", null)).thenReturn(type1);
|
||||
when(dictionaryClient.getDictionaryForType("type2", null)).thenReturn(type2);
|
||||
when(dictionaryClient.getDictionaryForType("type1", 0L)).thenReturn(type1);
|
||||
when(dictionaryClient.getDictionaryForType("type2", 0L)).thenReturn(type2);
|
||||
|
||||
dictionaryService.updateDictionary("dossierTemplateId", null);
|
||||
var increments = dictionaryService.getDictionaryIncrements("dossierTemplateId",
|
||||
DictionaryVersion.builder().dossierTemplateVersion(0L).build(), null);
|
||||
|
||||
|
||||
when(dictionaryClient.getVersion(any())).thenReturn(1L);
|
||||
when(dictionaryClient.getAllTypesForDossierTemplate(anyString(), anyBoolean())).thenReturn(List.of(type1Updated));
|
||||
when(dictionaryClient.getDictionaryForType("type1", 1L)).thenReturn(type1Updated);
|
||||
|
||||
dictionaryService.updateDictionary("dossierTemplateId", null);
|
||||
var updatedIncrements = dictionaryService.getDictionaryIncrements("dossierTemplateId",
|
||||
DictionaryVersion.builder().dossierTemplateVersion(0L).build(), null);
|
||||
|
||||
}
|
||||
}
|
||||
+3
-23
@@ -7,14 +7,12 @@ import lombok.SneakyThrows;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class FileSystemBackedStorageService implements StorageService{
|
||||
|
||||
@@ -65,9 +63,7 @@ public class FileSystemBackedStorageService implements StorageService{
|
||||
public void storeObject(String objectId, byte[] data) {
|
||||
File tempFile = File.createTempFile("storage", objectId.replace("/","-"));
|
||||
|
||||
try(var fileOutputStream = new FileOutputStream(tempFile)) {
|
||||
IOUtils.write(data, fileOutputStream);
|
||||
}
|
||||
IOUtils.write(data, new FileOutputStream(tempFile));
|
||||
|
||||
dataMap.put(objectId, tempFile);
|
||||
}
|
||||
@@ -77,31 +73,15 @@ public class FileSystemBackedStorageService implements StorageService{
|
||||
public void storeObject(String objectId, InputStream stream) {
|
||||
File tempFile = File.createTempFile("test", ".tmp");
|
||||
|
||||
try(var fileOutputStream = new FileOutputStream(tempFile)) {
|
||||
IOUtils.copy(stream, fileOutputStream);
|
||||
}
|
||||
IOUtils.copy(stream, new FileOutputStream(tempFile));
|
||||
|
||||
dataMap.put(objectId, tempFile);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void storeObject(String objectId, ByteArrayOutputStream stream) {
|
||||
|
||||
File tempFile = File.createTempFile("test", ".tmp");
|
||||
try(var fileOutputStream = new FileOutputStream(tempFile)){
|
||||
stream.writeTo(fileOutputStream);
|
||||
}
|
||||
|
||||
dataMap.put(objectId, tempFile);
|
||||
}
|
||||
|
||||
|
||||
public void clearStorage() {
|
||||
this.dataMap.forEach((k, v) -> {
|
||||
v.delete();
|
||||
});
|
||||
this.dataMap.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+194
-27
@@ -1,6 +1,7 @@
|
||||
package com.iqser.red.service.redaction.v1.server;
|
||||
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.Comment;
|
||||
@@ -13,12 +14,14 @@ import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.do
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntry;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
|
||||
import com.iqser.red.service.redaction.v1.model.*;
|
||||
import com.iqser.red.service.redaction.v1.server.annotate.AnnotateRequest;
|
||||
import com.iqser.red.service.redaction.v1.server.annotate.AnnotateResponse;
|
||||
import com.iqser.red.service.redaction.v1.server.annotate.AnnotationService;
|
||||
import com.iqser.red.service.redaction.v1.server.classification.model.SectionText;
|
||||
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.controller.RedactionController;
|
||||
import com.iqser.red.service.redaction.v1.server.memory.MemoryStats;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.service.AnalyzeService;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.service.ManualRedactionSurroundingTextService;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils;
|
||||
@@ -27,7 +30,9 @@ import com.iqser.red.service.redaction.v1.server.redaction.utils.TextNormalizati
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
import com.iqser.red.storage.commons.StorageAutoConfiguration;
|
||||
import com.iqser.red.storage.commons.service.StorageService;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -56,6 +61,7 @@ import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -91,6 +97,9 @@ public class RedactionIntegrationTest {
|
||||
@Autowired
|
||||
private RedactionController redactionController;
|
||||
|
||||
@Autowired
|
||||
private AnnotationService annotationService;
|
||||
|
||||
@Autowired
|
||||
private AnalyzeService analyzeService;
|
||||
|
||||
@@ -209,16 +218,16 @@ public class RedactionIntegrationTest {
|
||||
}
|
||||
|
||||
|
||||
private void mockDictionaryCalls(Long version){
|
||||
private void mockDictionaryCalls(Long version) {
|
||||
|
||||
when(dictionaryClient.getDictionaryForType(VERTEBRATE + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(VERTEBRATE, false));
|
||||
when(dictionaryClient.getDictionaryForType(ADDRESS + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(ADDRESS, false));
|
||||
when(dictionaryClient.getDictionaryForType(AUTHOR + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(AUTHOR, false));
|
||||
when(dictionaryClient.getDictionaryForType(SPONSOR + ":" + TEST_DOSSIER_TEMPLATE_ID,version)).thenReturn(getDictionaryResponse(SPONSOR, false));
|
||||
when(dictionaryClient.getDictionaryForType(SPONSOR + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(SPONSOR, false));
|
||||
when(dictionaryClient.getDictionaryForType(NO_REDACTION_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(NO_REDACTION_INDICATOR, false));
|
||||
when(dictionaryClient.getDictionaryForType(REDACTION_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(REDACTION_INDICATOR, false));
|
||||
when(dictionaryClient.getDictionaryForType(HINT_ONLY + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(HINT_ONLY, false));
|
||||
when(dictionaryClient.getDictionaryForType(MUST_REDACT + ":" + TEST_DOSSIER_TEMPLATE_ID,version)).thenReturn(getDictionaryResponse(MUST_REDACT, false));
|
||||
when(dictionaryClient.getDictionaryForType(MUST_REDACT + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(MUST_REDACT, false));
|
||||
when(dictionaryClient.getDictionaryForType(PUBLISHED_INFORMATION + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(PUBLISHED_INFORMATION, false));
|
||||
when(dictionaryClient.getDictionaryForType(TEST_METHOD + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(TEST_METHOD, false));
|
||||
when(dictionaryClient.getDictionaryForType(PII + ":" + TEST_DOSSIER_TEMPLATE_ID, version)).thenReturn(getDictionaryResponse(PII, false));
|
||||
@@ -233,11 +242,11 @@ public class RedactionIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test270Rotated() {
|
||||
|
||||
AnalyzeRequest request = prepareStorage("files/Minimal Examples/270Rotated.pdf");
|
||||
MemoryStats.printMemoryStats();
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
assertThat(result).isNotNull();
|
||||
@@ -249,7 +258,6 @@ public class RedactionIntegrationTest {
|
||||
public void testLargeScannedFileOOM() {
|
||||
|
||||
AnalyzeRequest request = prepareStorage("scanned/VV-377031.pdf");
|
||||
MemoryStats.printMemoryStats();
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
assertThat(result).isNotNull();
|
||||
@@ -279,7 +287,7 @@ public class RedactionIntegrationTest {
|
||||
dictionary.get(AUTHOR).add("Drinking water");
|
||||
when(dictionaryClient.getVersion(TEST_DOSSIER_TEMPLATE_ID)).thenReturn(1L);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -488,14 +496,12 @@ public class RedactionIntegrationTest {
|
||||
deleted.add("David Chubb");
|
||||
deleted.add("mouse");
|
||||
|
||||
|
||||
reanlysisVersions.put("mouse", 3L);
|
||||
|
||||
when(dictionaryClient.getVersion(TEST_DOSSIER_TEMPLATE_ID)).thenReturn(3L);
|
||||
|
||||
when(dictionaryClient.getDictionaryForType(VERTEBRATE, null)).thenReturn(getDictionaryResponse(VERTEBRATE, false));
|
||||
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
@@ -516,7 +522,7 @@ public class RedactionIntegrationTest {
|
||||
end = System.currentTimeMillis();
|
||||
System.out.println("reanalysis analysis duration: " + (end - start));
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -580,7 +586,7 @@ public class RedactionIntegrationTest {
|
||||
|
||||
assertThat(changes.size()).isEqualTo(2);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -666,7 +672,6 @@ public class RedactionIntegrationTest {
|
||||
|
||||
when(dictionaryClient.getDictionaryForType(VERTEBRATE, null)).thenReturn(getDictionaryResponse(VERTEBRATE, false));
|
||||
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
@@ -687,7 +692,7 @@ public class RedactionIntegrationTest {
|
||||
end = System.currentTimeMillis();
|
||||
System.out.println("reanalysis analysis duration: " + (end - start));
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -712,6 +717,71 @@ public class RedactionIntegrationTest {
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRemovePublishedInformations() throws IOException {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
ClassPathResource colorsResource = new ClassPathResource("colors/colors.json");
|
||||
var colors = objectMapper.readValue(colorsResource.getInputStream(), Colors.class);
|
||||
|
||||
ClassPathResource typeResource = new ClassPathResource("colors/types.json");
|
||||
TypeReference<List<Type>> typeRefForTypes = new TypeReference<>() {
|
||||
};
|
||||
List<Type> types = objectMapper.readValue(typeResource.getInputStream(), typeRefForTypes);
|
||||
|
||||
AnalyzeRequest request = prepareStorage("files/new/PublishedInformationTest.pdf");
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
manualRedactions.getIdsToRemove()
|
||||
.add(IdRemoval.builder()
|
||||
.annotationId("308dab9015bfafd911568cffe0a7f7de")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.status(AnnotationStatus.APPROVED)
|
||||
.requestDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 475479, ZoneOffset.UTC))
|
||||
.processedDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 483651, ZoneOffset.UTC))
|
||||
.build());
|
||||
|
||||
manualRedactions.getForceRedactions()
|
||||
.add(ManualForceRedaction.builder()
|
||||
.annotationId("0b56ea1a87c83f351df177315af94f0d")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.status(AnnotationStatus.APPROVED)
|
||||
.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());
|
||||
|
||||
manualRedactions.getIdsToRemove()
|
||||
.add(IdRemoval.builder()
|
||||
.annotationId("0b56ea1a87c83f351df177315af94f0d")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.status(AnnotationStatus.APPROVED)
|
||||
.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());
|
||||
|
||||
request.setManualRedactions(manualRedactions);
|
||||
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.manualRedactions(manualRedactions)
|
||||
.colors(colors)
|
||||
.types(types)
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(OsUtils.getTemporaryDirectory() + "/Annotated.pdf")) {
|
||||
fileOutputStream.write(annotateResponse.getDocument());
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println("duration: " + (end - start));
|
||||
System.out.println("numberOfPages: " + result.getNumberOfPages());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTableRedaction() throws IOException {
|
||||
|
||||
@@ -722,7 +792,90 @@ public class RedactionIntegrationTest {
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(OsUtils.getTemporaryDirectory() + "/Annotated.pdf")) {
|
||||
fileOutputStream.write(annotateResponse.getDocument());
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println("duration: " + (end - start));
|
||||
System.out.println("numberOfPages: " + result.getNumberOfPages());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRotations() throws IOException {
|
||||
|
||||
System.out.println("testTableRedaction");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
AnalyzeRequest request = prepareStorage("files/new/RotateTestFile.pdf");
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(OsUtils.getTemporaryDirectory() + "/Annotated.pdf")) {
|
||||
fileOutputStream.write(annotateResponse.getDocument());
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println("duration: " + (end - start));
|
||||
System.out.println("numberOfPages: " + result.getNumberOfPages());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFindDictionaryEntryInResizedEntryPosition() throws IOException {
|
||||
|
||||
System.out.println("testResizeDictFound");
|
||||
|
||||
ClassPathResource colorsResource = new ClassPathResource("colors/colors.json");
|
||||
var colors = objectMapper.readValue(colorsResource.getInputStream(), Colors.class);
|
||||
|
||||
ClassPathResource typeResource = new ClassPathResource("colors/types.json");
|
||||
TypeReference<List<Type>> typeRefForTypes = new TypeReference<>() {
|
||||
};
|
||||
List<Type> types = objectMapper.readValue(typeResource.getInputStream(), typeRefForTypes);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
AnalyzeRequest request = prepareStorage("files/new/S157.pdf");
|
||||
|
||||
ManualRedactions manualRedactions = new ManualRedactions();
|
||||
|
||||
|
||||
manualRedactions.getResizeRedactions()
|
||||
.add(ManualResizeRedaction.builder()
|
||||
.annotationId("ca2b437e2480a4b5966cb8386020d454")
|
||||
.fileId(TEST_FILE_ID)
|
||||
.status(AnnotationStatus.APPROVED)
|
||||
.requestDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 475479, ZoneOffset.UTC))
|
||||
.processedDate(OffsetDateTime.of(2022, 05, 23, 8, 30, 07, 483651, ZoneOffset.UTC))
|
||||
.value("Bera P")
|
||||
.positions(List.of(new Rectangle(382.12485f, 235.94246f, 8.768621f, -21.632504f, 1)))
|
||||
.textBefore("")
|
||||
.textAfter("")
|
||||
.build());
|
||||
|
||||
request.setManualRedactions(manualRedactions);
|
||||
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
|
||||
var redactionLog = redactionStorageService.getRedactionLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.manualRedactions(manualRedactions)
|
||||
.colors(colors)
|
||||
.types(types)
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -812,7 +965,7 @@ public class RedactionIntegrationTest {
|
||||
|
||||
var redactionLog = redactionStorageService.getRedactionLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -946,7 +1099,7 @@ public class RedactionIntegrationTest {
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -1078,7 +1231,7 @@ public class RedactionIntegrationTest {
|
||||
|
||||
analyzeService.reanalyze(request);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.dossierTemplateId(TEST_DOSSIER_TEMPLATE_ID)
|
||||
@@ -1102,7 +1255,7 @@ public class RedactionIntegrationTest {
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -1179,7 +1332,7 @@ public class RedactionIntegrationTest {
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
AnalyzeResult result = analyzeService.analyze(request);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.dossierTemplateId(TEST_DOSSIER_TEMPLATE_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
@@ -1358,7 +1511,7 @@ public class RedactionIntegrationTest {
|
||||
typeColorMap.put(LOGO, "#ffe187");
|
||||
typeColorMap.put(FORMULA, "#ffe187");
|
||||
typeColorMap.put(SIGNATURE, "#ffe187");
|
||||
typeColorMap.put(IMPORTED_REDACTION, "#32a852");
|
||||
typeColorMap.put(IMPORTED_REDACTION, "#fcfbe6");
|
||||
|
||||
hintTypeMap.put(VERTEBRATE, true);
|
||||
hintTypeMap.put(ADDRESS, false);
|
||||
@@ -1515,8 +1668,8 @@ public class RedactionIntegrationTest {
|
||||
public void testImportedRedactions() throws IOException {
|
||||
|
||||
String outputFileName = OsUtils.getTemporaryDirectory() + "/Annotated.pdf";
|
||||
ClassPathResource pdfFileResource = new ClassPathResource("files/ImportedRedactions/ImportedRedactions.pdf");
|
||||
ClassPathResource importedRedactions = new ClassPathResource("files/ImportedRedactions/ImportedRedactions.json");
|
||||
ClassPathResource pdfFileResource = new ClassPathResource("files/ImportedRedactions/RotateTestFile_without_highlights.pdf");
|
||||
ClassPathResource importedRedactions = new ClassPathResource("files/ImportedRedactions/RotateTestFile_without_highlights.IMPORTED_REDACTIONS.json");
|
||||
|
||||
AnalyzeRequest request = prepareStorage(pdfFileResource.getInputStream());
|
||||
storageService.storeObject(RedactionStorageService.StorageIdUtils.getStorageId(TEST_DOSSIER_ID, TEST_FILE_ID, FileType.IMPORTED_REDACTIONS), IOUtils.toByteArray(importedRedactions.getInputStream()));
|
||||
@@ -1526,25 +1679,38 @@ public class RedactionIntegrationTest {
|
||||
|
||||
var redactionLog = redactionStorageService.getRedactionLog(TEST_DOSSIER_ID, TEST_FILE_ID);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
|
||||
redactionLog.getRedactionLogEntry().forEach(entry -> {
|
||||
if (entry.getValue() == null) {
|
||||
return;
|
||||
}
|
||||
if (entry.getValue().equals("David")){
|
||||
assertThat(entry.getImportedRedactionIntersections().size()).isEqualTo(1);
|
||||
}
|
||||
if (entry.getValue().equals("annotation")){
|
||||
assertThat(entry.getImportedRedactionIntersections().size()).isEqualTo(0);
|
||||
}
|
||||
});
|
||||
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(outputFileName)) {
|
||||
fileOutputStream.write(annotateResponse.getDocument());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandByPrefixRegEx() throws IOException {
|
||||
|
||||
assertThat(dictionary.get(AUTHOR).contains("Robinson"));
|
||||
assertThat(! dictionary.get(AUTHOR).contains("Mrs. Robinson"));
|
||||
assertThat(!dictionary.get(AUTHOR).contains("Mrs. Robinson"));
|
||||
assertThat(dictionary.get(AUTHOR).contains("Bojangles"));
|
||||
assertThat(! dictionary.get(AUTHOR).contains("Mr. Bojangles"));
|
||||
assertThat(!dictionary.get(AUTHOR).contains("Mr. Bojangles"));
|
||||
assertThat(dictionary.get(AUTHOR).contains("Tambourine Man"));
|
||||
assertThat(! dictionary.get(AUTHOR).contains("Mr. Tambourine Man"));
|
||||
assertThat(!dictionary.get(AUTHOR).contains("Mr. Tambourine Man"));
|
||||
|
||||
String fileName = "files/mr-mrs.pdf";
|
||||
String outputFileName = OsUtils.getTemporaryDirectory() + "/Annotated.pdf";
|
||||
@@ -1555,7 +1721,7 @@ public class RedactionIntegrationTest {
|
||||
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
|
||||
analyzeService.analyze(request);
|
||||
|
||||
AnnotateResponse annotateResponse = redactionController.annotate(AnnotateRequest.builder()
|
||||
AnnotateResponse annotateResponse = annotationService.annotate(AnnotateRequest.builder()
|
||||
.dossierId(TEST_DOSSIER_ID)
|
||||
.fileId(TEST_FILE_ID)
|
||||
.build());
|
||||
@@ -1575,6 +1741,7 @@ public class RedactionIntegrationTest {
|
||||
assertThat(values).contains("Mr. Tambourine Man");
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
private AnalyzeRequest prepareStorage(InputStream stream) {
|
||||
|
||||
|
||||
+7
-1
@@ -1,6 +1,10 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
package com.iqser.red.service.redaction.v1.server.annotate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -17,4 +21,6 @@ public class AnnotateRequest {
|
||||
private String dossierTemplateId;
|
||||
private String fileId;
|
||||
private ManualRedactions manualRedactions;
|
||||
private Colors colors;
|
||||
private List<Type> types;
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.iqser.red.service.redaction.v1.model;
|
||||
package com.iqser.red.service.redaction.v1.server.annotate;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
+44
-4
@@ -1,9 +1,15 @@
|
||||
package com.iqser.red.service.redaction.v1.server.redaction.service;
|
||||
package com.iqser.red.service.redaction.v1.server.annotate;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.annotations.Comment;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
|
||||
import com.iqser.red.service.redaction.v1.model.Rectangle;
|
||||
import com.iqser.red.service.redaction.v1.model.*;
|
||||
import com.iqser.red.service.redaction.v1.server.exception.RedactionException;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.service.DictionaryService;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.service.RedactionLogMergeService;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
@@ -17,6 +23,7 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
@@ -26,8 +33,41 @@ import java.util.stream.Collectors;
|
||||
@RequiredArgsConstructor
|
||||
public class AnnotationService {
|
||||
|
||||
private final RedactionStorageService redactionStorageService;
|
||||
private final DictionaryService dictionaryService;
|
||||
|
||||
public void annotate(PDDocument document, RedactionLog redactionLog, SectionGrid sectionGrid) throws IOException {
|
||||
private final RedactionLogMergeService redactionLogMergeService;
|
||||
|
||||
public AnnotateResponse annotate(AnnotateRequest annotateRequest) {
|
||||
|
||||
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(annotateRequest.getDossierId(), annotateRequest.getFileId(), FileType.ORIGIN));
|
||||
var mergedRedactionLog = redactionLogMergeService.provideRedactionLog(RedactionRequest.builder()
|
||||
.fileId(annotateRequest.getFileId())
|
||||
.manualRedactions(annotateRequest.getManualRedactions())
|
||||
.dossierId(annotateRequest.getDossierId())
|
||||
.dossierTemplateId(annotateRequest.getDossierTemplateId())
|
||||
.colors(annotateRequest.getColors())
|
||||
.types(annotateRequest.getTypes())
|
||||
.build());
|
||||
var sectionsGrid = redactionStorageService.getSectionGrid(annotateRequest.getDossierId(), annotateRequest.getFileId());
|
||||
|
||||
try (PDDocument pdDocument = PDDocument.load(storedObjectStream, MemoryUsageSetting.setupTempFileOnly())) {
|
||||
pdDocument.setAllSecurityToBeRemoved(true);
|
||||
|
||||
dictionaryService.updateDictionary(annotateRequest.getDossierTemplateId(), annotateRequest.getDossierId());
|
||||
annotate(pdDocument, mergedRedactionLog, sectionsGrid);
|
||||
|
||||
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
|
||||
pdDocument.save(byteArrayOutputStream);
|
||||
return AnnotateResponse.builder().document(byteArrayOutputStream.toByteArray()).build();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RedactionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void annotate(PDDocument document, RedactionLog redactionLog, SectionGrid sectionGrid) throws IOException {
|
||||
|
||||
Map<Integer, List<RedactionLogEntry>> redactionLogPerPage = convertRedactionLog(redactionLog);
|
||||
|
||||
@@ -78,7 +118,7 @@ public class AnnotationService {
|
||||
annotation.setRectangle(pdRectangle);
|
||||
annotation.setQuadPoints(toQuadPoints(rectangles, mediaBox, cropBox));
|
||||
if (!redactionLogEntry.isHint()) {
|
||||
annotation.setContents(createAnnotationContent(redactionLogEntry));
|
||||
annotation.setContents(redactionLogEntry.getValue() + " " +createAnnotationContent(redactionLogEntry));
|
||||
}
|
||||
annotation.setTitlePopup(redactionLogEntry.getId());
|
||||
annotation.setAnnotationName(redactionLogEntry.getId());
|
||||
@@ -86,7 +126,7 @@ public class AnnotationService {
|
||||
annotations.add(annotation);
|
||||
|
||||
if (redactionLogEntry.getComments() != null) {
|
||||
for (Comment comment : redactionLogEntry.getComments()) {
|
||||
for (RedactionLogComment comment : redactionLogEntry.getComments()) {
|
||||
PDAnnotationText txtAnnot = new PDAnnotationText();
|
||||
txtAnnot.setAnnotationName(String.valueOf(comment.getId()));
|
||||
txtAnnot.setInReplyTo(annotation); // Reference to highlight annotation
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.iqser.red.service.redaction.v1.server.realdata;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
|
||||
import com.iqser.red.service.redaction.v1.model.AnalyzeRequest;
|
||||
import com.iqser.red.service.redaction.v1.model.MessageType;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
public class AnalyseFileRealDataIntegrationTest extends LiveDataIntegrationTest {
|
||||
|
||||
public static final String FILE_NAME = "test-file";
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testFile() {
|
||||
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
om.registerModule(new JavaTimeModule());
|
||||
|
||||
var file = new ClassPathResource(BASE_DIR + "data/" + FILE_NAME + ".pdf").getInputStream();
|
||||
redactionStorageService.storeObject("dossierId", "fileId", FileType.ORIGIN, file);
|
||||
|
||||
|
||||
try {
|
||||
var nerData = new ClassPathResource(BASE_DIR + "data/" + FILE_NAME + ".ner.json").getInputStream();
|
||||
redactionStorageService.storeObject("dossierId", "fileId", FileType.NER_ENTITIES, nerData);
|
||||
} catch (Exception e) {
|
||||
log.warn("No NER File Provided");
|
||||
redactionServiceSettings.setNerServiceEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
AnalyzeRequest ar = AnalyzeRequest.builder()
|
||||
.fileId("fileId")
|
||||
.dossierId("dossierId")
|
||||
.analysisNumber(1)
|
||||
.dossierTemplateId("dossierTemplateId")
|
||||
.lastProcessed(OffsetDateTime.now())
|
||||
.excludedPages(Set.of())
|
||||
.fileAttributes(List.of())
|
||||
.build();
|
||||
|
||||
|
||||
try {
|
||||
var text = new ClassPathResource(BASE_DIR + "data/" + FILE_NAME + ".text.json").getInputStream();
|
||||
var sectionText = new ClassPathResource(BASE_DIR + "data/" + FILE_NAME + ".section-grid.json").getInputStream();
|
||||
redactionStorageService.storeObject("dossierId", "fileId", FileType.TEXT, text);
|
||||
redactionStorageService.storeObject("dossierId", "fileId", FileType.SECTION_GRID, sectionText);
|
||||
} catch (Exception e) {
|
||||
log.info("No text file provided, Performing Structure analysis");
|
||||
|
||||
ar.setMessageType(MessageType.STRUCTURE_ANALYSE);
|
||||
String in = om.writeValueAsString(ar);
|
||||
redactionMessageReceiver.receiveAnalyzeRequest(in, false);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
var redactionLog = new ClassPathResource(BASE_DIR + "data/" + FILE_NAME + ".redaction-log.json").getInputStream();
|
||||
} catch (Exception e) {
|
||||
log.info("No redaction log provided, Performing full analysis");
|
||||
|
||||
ar.setMessageType(MessageType.ANALYSE);
|
||||
String in = om.writeValueAsString(ar);
|
||||
redactionMessageReceiver.receiveAnalyzeRequest(in, false);
|
||||
}
|
||||
|
||||
|
||||
simulateIncrement(List.of("Desiree"), "PII", 3L);
|
||||
ar.setMessageType(MessageType.REANALYSE);
|
||||
String in = om.writeValueAsString(ar);
|
||||
redactionMessageReceiver.receiveAnalyzeRequest(in, false);
|
||||
|
||||
|
||||
// var log = redactionStorageService.getRedactionLog("dossierId","fileId");
|
||||
}
|
||||
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
package com.iqser.red.service.redaction.v1.server.realdata;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntry;
|
||||
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
|
||||
import com.iqser.red.service.redaction.v1.server.Application;
|
||||
import com.iqser.red.service.redaction.v1.server.FileSystemBackedStorageService;
|
||||
import com.iqser.red.service.redaction.v1.server.client.*;
|
||||
import com.iqser.red.service.redaction.v1.server.queue.RedactionMessageReceiver;
|
||||
import com.iqser.red.service.redaction.v1.server.redaction.service.DictionaryService;
|
||||
import com.iqser.red.service.redaction.v1.server.settings.RedactionServiceSettings;
|
||||
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
|
||||
import com.iqser.red.storage.commons.StorageAutoConfiguration;
|
||||
import com.iqser.red.storage.commons.service.StorageService;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@Import(LiveDataIntegrationTest.RedactionIntegrationTestConfiguration.class)
|
||||
public class LiveDataIntegrationTest {
|
||||
|
||||
protected static String BASE_DIR = "performance/";
|
||||
|
||||
protected static String EFSA_SANITISATION_GFL_V1 = "dictionaries/EFSA_sanitisation_GFL_v1/";
|
||||
|
||||
@MockBean
|
||||
protected DictionaryClient dictionaryClient;
|
||||
|
||||
@MockBean
|
||||
protected EntityRecognitionClient entityRecognitionClient;
|
||||
|
||||
@MockBean
|
||||
private FileStatusProcessingUpdateClient fileStatusProcessingUpdateClient;
|
||||
|
||||
@MockBean
|
||||
private LegalBasisClient legalBasisClient;
|
||||
|
||||
@Autowired
|
||||
private ResourcePatternResolver resourcePatternResolver;
|
||||
|
||||
@MockBean
|
||||
private RulesClient rulesClient;
|
||||
|
||||
@Autowired
|
||||
protected DictionaryService dictionaryService;
|
||||
|
||||
@Autowired
|
||||
protected RedactionStorageService redactionStorageService;
|
||||
|
||||
@Autowired
|
||||
protected RedactionMessageReceiver redactionMessageReceiver;
|
||||
|
||||
@Autowired
|
||||
protected FileSystemBackedStorageService fileSystemBackedStorageService;
|
||||
|
||||
@Autowired
|
||||
protected RedactionServiceSettings redactionServiceSettings;
|
||||
|
||||
private List<Type> types;
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration(exclude = {RabbitAutoConfiguration.class, StorageAutoConfiguration.class})
|
||||
public static class RedactionIntegrationTestConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public StorageService inmemoryStorage() {
|
||||
|
||||
return new FileSystemBackedStorageService();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@Before
|
||||
public void prepareTest() {
|
||||
|
||||
when(dictionaryClient.getVersion(anyString())).thenReturn(1L);
|
||||
when(dictionaryClient.getVersionForDossier(anyString())).thenReturn(1L);
|
||||
|
||||
var rules = IOUtils.toString(new ClassPathResource(BASE_DIR + EFSA_SANITISATION_GFL_V1 + "rules.drl").getInputStream());
|
||||
when(rulesClient.getRules(any())).thenReturn(JSONPrimitive.of(rules));
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
var jsonNode = objectMapper.readTree(new ClassPathResource(BASE_DIR + EFSA_SANITISATION_GFL_V1 + "types.json").getInputStream());
|
||||
types = objectMapper.treeToValue(jsonNode.get("types"), objectMapper.getTypeFactory().constructType(
|
||||
new TypeReference<List<Type>>() {
|
||||
}));
|
||||
|
||||
types.forEach(t -> {
|
||||
t.setId(t.getType());
|
||||
t.setVersion(0L);
|
||||
});
|
||||
|
||||
when(dictionaryClient.getAllTypesForDossierTemplate(anyString(), anyBoolean())).thenReturn(types);
|
||||
when(dictionaryClient.getAllTypesForDossier(anyString(), anyBoolean())).thenReturn(new ArrayList<>());
|
||||
|
||||
|
||||
when(dictionaryClient.getColors(anyString())).thenReturn(objectMapper.readValue(new ClassPathResource(BASE_DIR + EFSA_SANITISATION_GFL_V1 + "colors.json").getInputStream(), Colors.class));
|
||||
|
||||
when(dictionaryClient.getDictionaryForType(anyString(), nullable(Long.class))).then(answer -> {
|
||||
String typeName = answer.getArgument(0);
|
||||
|
||||
var found = types.stream().filter(t -> t.getType().equalsIgnoreCase(typeName)).findFirst();
|
||||
if(found.isPresent()) {
|
||||
var type = types.stream().filter(t -> t.getType().equalsIgnoreCase(typeName)).findFirst().get();
|
||||
type.setEntries(getEntries(typeName, type.getTypeId()));
|
||||
|
||||
return type;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
dictionaryService.updateDictionary("dossierTemplateId","dossierId");
|
||||
}
|
||||
|
||||
public void simulateIncrement(List<String> values, String deltaTypeName, long version) {
|
||||
when(dictionaryClient.getVersion(anyString())).thenReturn(version);
|
||||
when(dictionaryClient.getVersionForDossier(anyString())).thenReturn(version);
|
||||
|
||||
when(dictionaryClient.getDictionaryForType(anyString(), nullable(Long.class))).then(answer -> {
|
||||
String typeName = answer.getArgument(0);
|
||||
|
||||
var found = types.stream().filter(t -> t.getType().equalsIgnoreCase(typeName)).findFirst();
|
||||
if(found.isPresent()) {
|
||||
|
||||
var type = types.stream().filter(t -> t.getType().equalsIgnoreCase(typeName)).findFirst().get();
|
||||
|
||||
if (deltaTypeName.equalsIgnoreCase(typeName)) {
|
||||
type.setEntries(values.stream().map(l -> new DictionaryEntry(0, l, version, false, type.getTypeId())).collect(Collectors.toList()));
|
||||
} else {
|
||||
type.setEntries(new ArrayList<>());
|
||||
}
|
||||
|
||||
return type;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUpdateDictionary() {
|
||||
dictionaryService.updateDictionary("dossierTemplateId", "dossierId");
|
||||
|
||||
var dict = dictionaryService.getDeepCopyDictionary("dossierTemplateId", "dossierId");
|
||||
assertThat(dict.getLocalAccessMap().size()).isEqualTo(12);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private List<DictionaryEntry> getEntries(String typeName, String typeId) {
|
||||
Resource[] dictionaryResources = resourcePatternResolver.getResources("classpath:" + BASE_DIR + EFSA_SANITISATION_GFL_V1 + "dictionaries/**");
|
||||
for (var resource : dictionaryResources) {
|
||||
if (resource.getFilename().contains(typeName)) {
|
||||
|
||||
List<String> lines = IOUtils.readLines(resource.getInputStream());
|
||||
return lines.stream().map(l -> new DictionaryEntry(0, l, 0L, false, typeId)).collect(Collectors.toList());
|
||||
|
||||
}
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -96,7 +96,7 @@ public class PdfSegmentationServiceTest {
|
||||
Map<Integer, List<PdfImage>> images = new HashMap<>();
|
||||
imageServiceResponse.getData().stream().forEach(imageMetadata -> {
|
||||
images.computeIfAbsent(imageMetadata.getPosition().getPageNumber() ,x -> new ArrayList<>())
|
||||
.add(new PdfImage(new RedRectangle2D(imageMetadata.getPosition().getX1(), imageMetadata.getPosition().getY1(), imageMetadata.getGeometry().getWidth(), imageMetadata.getGeometry().getHeight()), ImageType.valueOf(imageMetadata.getClassification().getLabel().toUpperCase(Locale.ROOT)), imageMetadata.getPosition().getPageNumber()));
|
||||
.add(new PdfImage(new RedRectangle2D(imageMetadata.getPosition().getX1(), imageMetadata.getPosition().getY1(), imageMetadata.getGeometry().getWidth(), imageMetadata.getGeometry().getHeight()), ImageType.valueOf(imageMetadata.getClassification().getLabel().toUpperCase(Locale.ROOT)), imageMetadata.isAlpha(), imageMetadata.getPosition().getPageNumber()));
|
||||
});
|
||||
|
||||
System.out.println("object");
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.iqser.red.service.redaction.v1.server.stringmatching;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.SneakyThrows;
|
||||
import org.ahocorasick.trie.Trie;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
public class StringMatchingPerformanceTest {
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testStringPerformance() {
|
||||
|
||||
String text = IOUtils.toString(new ClassPathResource("stringmatching/hamlet.txt").getInputStream()).toLowerCase();
|
||||
Set<String> dictionary = IOUtils.readLines(new ClassPathResource("stringmatching/names.txt").getInputStream())
|
||||
.stream()
|
||||
.map(String::toLowerCase).collect(Collectors.toSet());
|
||||
|
||||
System.out.println("Loaded text has a length of " + text.length() + " symbols");
|
||||
System.out.println("Dictionary has " + dictionary.size() + " entries");
|
||||
|
||||
var patterns = dictionary.stream()
|
||||
.map(p -> Pattern.compile(Pattern.quote(p))).collect(Collectors.toList());
|
||||
var trie = Trie.builder().ignoreCase().addKeywords(dictionary).build();
|
||||
|
||||
|
||||
// 1. Naive approach
|
||||
long t1 = System.currentTimeMillis();
|
||||
var naiveIndexes = new HashSet<Index>();
|
||||
for (var entry : dictionary) {
|
||||
var startIndex = 0;
|
||||
do {
|
||||
startIndex = text.indexOf(entry, startIndex + 1);
|
||||
if (startIndex != -1) {
|
||||
naiveIndexes.add(new Index(startIndex, startIndex + entry.length()));
|
||||
}
|
||||
} while (startIndex != -1);
|
||||
}
|
||||
long t2 = System.currentTimeMillis();
|
||||
System.out.println("Naive approach found " + naiveIndexes.size() + " entries in " + (t2 - t1) + "ms");
|
||||
|
||||
|
||||
// 2. Boyer Moore
|
||||
t1 = System.currentTimeMillis();
|
||||
var boyerMooreIndexes = new HashSet<Index>();
|
||||
for (var pattern : patterns) {
|
||||
boyerMooreIndexes.addAll(pattern.matcher(text).results().map(r -> new Index(r.start(), r.end())).collect(Collectors.toList()));
|
||||
}
|
||||
t2 = System.currentTimeMillis();
|
||||
System.out.println("Boyer Moore found " + boyerMooreIndexes.size() + " entries in " + (t2 - t1) + "ms");
|
||||
|
||||
|
||||
// 3. Aho Corasick
|
||||
t1 = System.currentTimeMillis();
|
||||
var result = trie.parseText(text);
|
||||
var ahoCorasickIndexes = result.stream().map(r -> new Index(r.getStart(), r.getEnd() + 1)).collect(Collectors.toSet());
|
||||
t2 = System.currentTimeMillis();
|
||||
System.out.println("Aho Corasick found " + ahoCorasickIndexes.size() + " entries in " + (t2 - t1) + "ms");
|
||||
|
||||
|
||||
// Assert that all algorithms are equal
|
||||
assertThat(naiveIndexes).isEqualTo(boyerMooreIndexes).isEqualTo(ahoCorasickIndexes);
|
||||
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(of = {"start", "end"})
|
||||
public static class Index {
|
||||
int start;
|
||||
int end;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"defaultColor": "#9398a0",
|
||||
"requestAdd": "#04b093",
|
||||
"requestRemove": "#04b093",
|
||||
"notRedacted": "#c498fa",
|
||||
"analysisColor": "#dd4d50",
|
||||
"updatedColor": "#fdbd00",
|
||||
"dictionaryRequestColor": "#5b97db",
|
||||
"manualRedactionColor": "#9398a0",
|
||||
"previewColor": "#9398a0",
|
||||
"ignoredHintColor": "#e7d4ff"
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
[
|
||||
{
|
||||
"id": "CBI_address:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "CBI_address",
|
||||
"hexColor": "#9398a0",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 140,
|
||||
"isHint": false,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": false,
|
||||
"isRecommendation": false,
|
||||
"description": "All site names and addresses, and location (e.g. Syngenta, Monthey, GPS Co-ordinates, Mr Smith of … providing the…). Except addresses in published literature and the applicant address.",
|
||||
"addToDictionaryAction": true,
|
||||
"label": "CBI Address",
|
||||
"hasDictionary": true,
|
||||
"systemManaged": false,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "CBI_author:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "CBI_author",
|
||||
"hexColor": "#9398a0",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 130,
|
||||
"isHint": false,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": false,
|
||||
"isRecommendation": false,
|
||||
"description": "All authors named in the study documentation. Except names in published literature.",
|
||||
"addToDictionaryAction": true,
|
||||
"label": "CBI Author",
|
||||
"hasDictionary": true,
|
||||
"systemManaged": false,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "PII:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "PII",
|
||||
"hexColor": "#9398a0",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 150,
|
||||
"isHint": false,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": false,
|
||||
"isRecommendation": false,
|
||||
"description": "Not authors but listed in the document: Names, signatures, telephone, email etc.; e.g. Reg Manager, QA Manager",
|
||||
"addToDictionaryAction": true,
|
||||
"label": "PII",
|
||||
"hasDictionary": true,
|
||||
"systemManaged": false,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "formula:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "formula",
|
||||
"hexColor": "#036ffc",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 1002,
|
||||
"isHint": true,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": true,
|
||||
"isRecommendation": false,
|
||||
"description": "Empty dictionary used to configure formula colors.",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "Formula",
|
||||
"hasDictionary": false,
|
||||
"systemManaged": true,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "isHint_only:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "isHint_only",
|
||||
"hexColor": "#fa98f7",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 50,
|
||||
"isHint": true,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": true,
|
||||
"isRecommendation": false,
|
||||
"description": "Entries of this dictionary will be highlighted only",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "isHint Only",
|
||||
"hasDictionary": true,
|
||||
"systemManaged": false,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "image:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "image",
|
||||
"hexColor": "#bdd6ff",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 999,
|
||||
"isHint": true,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": true,
|
||||
"isRecommendation": false,
|
||||
"description": "Empty dictionary used to configure image colors.",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "Image",
|
||||
"hasDictionary": false,
|
||||
"systemManaged": true,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "logo:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "logo",
|
||||
"hexColor": "#9398a0",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 1001,
|
||||
"isHint": false,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": true,
|
||||
"isRecommendation": false,
|
||||
"description": "Empty dictionary used to configure logo colors.",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "Logo",
|
||||
"hasDictionary": false,
|
||||
"systemManaged": true,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "must_redact:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "must_redact",
|
||||
"hexColor": "#9398a0",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 100,
|
||||
"isHint": false,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": true,
|
||||
"isRecommendation": false,
|
||||
"description": "Entries of this dictionary get redacted wherever found.",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "Must Redact",
|
||||
"hasDictionary": true,
|
||||
"systemManaged": false,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "ocr:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "ocr",
|
||||
"hexColor": "#bdd6ff",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 1000,
|
||||
"isHint": true,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": true,
|
||||
"isRecommendation": false,
|
||||
"description": "Empty dictionary used to configure ocr colors.",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "Ocr",
|
||||
"hasDictionary": false,
|
||||
"systemManaged": true,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "signature:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "signature",
|
||||
"hexColor": "#9398a0",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 1003,
|
||||
"isHint": false,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": true,
|
||||
"isRecommendation": false,
|
||||
"description": "Empty dictionary used to configure signature colors.",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "Signature",
|
||||
"hasDictionary": false,
|
||||
"systemManaged": true,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "imported_redaction:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "imported_redaction",
|
||||
"hexColor": "#f0f0c0",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 9999,
|
||||
"isHint": false,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": false,
|
||||
"isRecommendation": false,
|
||||
"description": "Redaction Annotations that were imported from documents",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "Imported Redaction",
|
||||
"hasDictionary": false,
|
||||
"systemManaged": true,
|
||||
"autoHideSkipped": true
|
||||
},
|
||||
{
|
||||
"id": "published_information:31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"type": "published_information",
|
||||
"hexColor": "#85ebff",
|
||||
"recommendationHexColor": "#8df06c",
|
||||
"rank": 70,
|
||||
"isHint": true,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": false,
|
||||
"isRecommendation": false,
|
||||
"description": "Manual managed list of public journals and papers that need no redaction",
|
||||
"addToDictionaryAction": true,
|
||||
"label": "Published Information",
|
||||
"hasDictionary": true,
|
||||
"systemManaged": false,
|
||||
"autoHideSkipped": false
|
||||
},
|
||||
{
|
||||
"id": "dossier_redaction:31039447-9040-4376-9ca7-614e56b284b9:5dfb2724-74a4-4a1a-a1eb-165e7943ffcd",
|
||||
"type": "dossier_redaction",
|
||||
"hexColor": "#9398a0",
|
||||
"recommendationHexColor": null,
|
||||
"rank": 1500,
|
||||
"isHint": false,
|
||||
"dossierTemplateId": "31039447-9040-4376-9ca7-614e56b284b9",
|
||||
"isCaseInsensitive": false,
|
||||
"isRecommendation": false,
|
||||
"description": "Entries in this dictionary will only be redacted in this dossier",
|
||||
"addToDictionaryAction": false,
|
||||
"label": "Dossier Redaction",
|
||||
"hasDictionary": true,
|
||||
"systemManaged": true,
|
||||
"autoHideSkipped": false
|
||||
}
|
||||
]
|
||||
+4
@@ -9,3 +9,7 @@ Naka-27 Aomachi, Nomi, Ishikawa 923-1101, Japan, JP
|
||||
Özgür U. Reyhan
|
||||
Sude Halide Nurullah
|
||||
Xinyi Y. Tao
|
||||
Dorn
|
||||
Prasher
|
||||
David
|
||||
annotation
|
||||
+1
@@ -86,3 +86,4 @@ Toxicol Sci
|
||||
Toxicol Sci.
|
||||
Toxicol Sci. 1
|
||||
Test Ignored Hint Published Information
|
||||
Workshop
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"importedRedactions":{"1":[{"id":"755273b5ddc6933eff7f6b6e055c803f","positions":[{"topLeft":{"x":275.0,"y":520.504},"width":55.675,"height":15.582,"page":1}]},{"id":"5e517d2c890272fc140578a26971b459","positions":[{"topLeft":{"x":164.0,"y":316.492},"width":50.7,"height":15.658,"page":1},{"topLeft":{"x":127.0,"y":302.613},"width":119.675,"height":14.902,"page":1},{"topLeft":{"x":81.0,"y":289.622},"width":210.025,"height":14.846,"page":1},{"topLeft":{"x":136.0,"y":275.912},"width":94.2,"height":13.033,"page":1},{"topLeft":{"x":158.0,"y":261.402},"width":52.793,"height":16.218,"page":1},{"topLeft":{"x":177.0,"y":247.413},"width":15.507,"height":16.148,"page":1}]}]}}
|
||||
BIN
Binary file not shown.
+236
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"importedRedactions": {
|
||||
"1": [
|
||||
{
|
||||
"id": "e9b15eafe60232957c4805439d41b4bb",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 173.4,
|
||||
"y": 396.0
|
||||
},
|
||||
"width": -29.5,
|
||||
"height": -8.1,
|
||||
"page": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "5dc06b8f402a5ea6f4a71e7f0ae5f42e",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 189.4,
|
||||
"y": 694.0
|
||||
},
|
||||
"width": 28.0,
|
||||
"height": -12.1,
|
||||
"page": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "42ced0b82072cd154f05f917b5063c23",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 181.8,
|
||||
"y": 593.3
|
||||
},
|
||||
"width": -10.1,
|
||||
"height": -30.0,
|
||||
"page": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "8569561e9cca5d96f2fe966d1f470fbe",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 168.0,
|
||||
"y": 180.1
|
||||
},
|
||||
"width": 10.1,
|
||||
"height": 30.8,
|
||||
"page": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"2": [
|
||||
{
|
||||
"id": "22f46754f11d153e4693c64dd95a0f5f",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 398.8,
|
||||
"y": 302.1
|
||||
},
|
||||
"width": -27.6,
|
||||
"height": -12.1,
|
||||
"page": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "27386f133b866d27a25ceafcb4bd4774",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 153.1,
|
||||
"y": 258.2
|
||||
},
|
||||
"width": 25.5,
|
||||
"height": -10.1,
|
||||
"page": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "294b8d00efd202e4aa51f6fc5e225e80",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 46.3,
|
||||
"y": 304.8
|
||||
},
|
||||
"width": 10.1,
|
||||
"height": -25.5,
|
||||
"page": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2f7142be7b26f9efd1cd608ceaae8f25",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 523.6,
|
||||
"y": 238.9
|
||||
},
|
||||
"width": -10.1,
|
||||
"height": 25.5,
|
||||
"page": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"3": [
|
||||
{
|
||||
"id": "2566b5f6f11eb7609d0d9722513d6bec",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 444.1,
|
||||
"y": 415.4
|
||||
},
|
||||
"width": 28.0,
|
||||
"height": -12.1,
|
||||
"page": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "84350bb01e0e93be7146a09f975c3065",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 448.6,
|
||||
"y": 615.9
|
||||
},
|
||||
"width": -10.1,
|
||||
"height": -27.5,
|
||||
"page": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e1dc39e4f5006fa11e6dde38003d4b08",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 434.8,
|
||||
"y": 208.2
|
||||
},
|
||||
"width": 10.1,
|
||||
"height": 27.8,
|
||||
"page": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "75e67d849627e16becb66ec1ba51c7c8",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 428.2,
|
||||
"y": 117.5
|
||||
},
|
||||
"width": -29.4,
|
||||
"height": -8.1,
|
||||
"page": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"4": [
|
||||
{
|
||||
"id": "1c1a66ed056257e6e5ebc58b882acaee",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 526.6,
|
||||
"y": 487.8
|
||||
},
|
||||
"width": 10.1,
|
||||
"height": 27.9,
|
||||
"page": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7f79cfc276f448f5ee46c3fa2c84ed8b",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 212.6,
|
||||
"y": 504.9
|
||||
},
|
||||
"width": -27.5,
|
||||
"height": -8.1,
|
||||
"page": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "64b11b7d6143f1deb4f1fd3bd72c0464",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 69.5,
|
||||
"y": 558.5
|
||||
},
|
||||
"width": -10.1,
|
||||
"height": -27.9,
|
||||
"page": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d77ea54e7d2b2276efa7cd9f1d08b411",
|
||||
"positions": [
|
||||
{
|
||||
"topLeft": {
|
||||
"x": 404.4,
|
||||
"y": 545.8
|
||||
},
|
||||
"width": 27.1,
|
||||
"height": -9.1,
|
||||
"page": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user