Compare commits

...

14 Commits

Author SHA1 Message Date
Dominique Eifländer
a27cf72558 RED-9760: Fixed not found headline in anyheadlinecontains 2024-08-02 09:24:36 +02:00
Andrei Isvoran
acb5b4c308 Merge branch 'RED-9770' into 'release/4.348.x'
RED-9770 - Extend date converter

See merge request redactmanager/redaction-service!467
2024-07-30 10:29:48 +02:00
Andrei Isvoran
61ee1c12ca RED-9770 - Extend date converter 2024-07-30 10:56:58 +03:00
Kilian Schüttler
abec7ae6bf Merge branch 'annotationMode-bp' into 'release/4.348.x'
annotationMode: ignore IDs of manual adds in annotationMode

See merge request redactmanager/redaction-service!466
2024-07-26 14:53:41 +02:00
Kilian Schuettler
afeddb4d91 annotationMode: ignore IDs of manual adds in annotationMode 2024-07-26 14:03:28 +02:00
Dominique Eifländer
359c237943 Merge branch 'RED-9658-mongo-4.1' into 'release/4.348.x'
RED-9658: Fixed wrong mongo database name

See merge request redactmanager/redaction-service!461
2024-07-17 11:02:22 +02:00
Dominique Eifländer
9789943f45 RED-9658: Fixed wrong mongo database name 2024-07-17 10:44:30 +02:00
Andrei Isvoran
f096aab156 Merge branch 'RED-9667' into 'release/4.348.x'
RED-9667 - Extend convert dates

See merge request redactmanager/redaction-service!459
2024-07-16 16:02:20 +02:00
Andrei Isvoran
156b102e87 RED-9667 - Extend convert dates 2024-07-16 16:02:20 +02:00
Andrei Isvoran
180728721a Merge branch 'RED-9496-graceful-shutdown-bp' into 'release/4.348.x'
RED-9496 - Implement graceful shutdown

See merge request redactmanager/redaction-service!457
2024-07-04 13:56:45 +02:00
Andrei Isvoran
fb9d1042ac RED-9496 - Implement graceful shutdown 2024-07-04 14:03:43 +03:00
Corina Olariu
046b4b29b9 Merge branch 'RED-9466-bp' into 'release/4.348.x'
RED-9466 - Adding annotation removes all AI based recommendations until forced re-analysis

See merge request redactmanager/redaction-service!452
2024-06-28 15:31:16 +02:00
Corina Olariu
dce797ef8e RED-9466 - Adding annotation removes all AI based recommendations until forced re-analysis 2024-06-28 15:31:16 +02:00
Kilian Schüttler
8b8dab2a18 RED-9375: use storageId for cache names everywhere, such that name may be updated by a user
(cherry picked from commit c1a2e9dee209413ca7a3738dc746ee2397aa1319)
2024-06-27 16:13:24 +02:00
18 changed files with 1273 additions and 25 deletions

View File

@ -12,11 +12,11 @@ plugins {
description = "redaction-service-server-v1" description = "redaction-service-server-v1"
val layoutParserVersion = "0.141.0" val layoutParserVersion = "0.142.6"
val jacksonVersion = "2.15.2" val jacksonVersion = "2.15.2"
val droolsVersion = "9.44.0.Final" val droolsVersion = "9.44.0.Final"
val pdfBoxVersion = "3.0.0" val pdfBoxVersion = "3.0.0"
val persistenceServiceVersion = "2.444.0" val persistenceServiceVersion = "2.465.25"
val springBootStarterVersion = "3.1.5" val springBootStarterVersion = "3.1.5"
val springCloudVersion = "4.0.4" val springCloudVersion = "4.0.4"
val testContainersVersion = "1.19.7" val testContainersVersion = "1.19.7"
@ -43,6 +43,7 @@ dependencies {
implementation("com.iqser.red.commons:storage-commons:2.45.0") implementation("com.iqser.red.commons:storage-commons:2.45.0")
implementation("com.knecon.fforesight:tenant-commons:0.24.0") implementation("com.knecon.fforesight:tenant-commons:0.24.0")
implementation("com.knecon.fforesight:tracing-commons:0.5.0") implementation("com.knecon.fforesight:tracing-commons:0.5.0")
implementation("com.knecon.fforesight:lifecycle-commons:0.6.0")
implementation("com.fasterxml.jackson.module:jackson-module-afterburner:${jacksonVersion}") implementation("com.fasterxml.jackson.module:jackson-module-afterburner:${jacksonVersion}")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}")

View File

@ -13,6 +13,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@ -20,6 +21,7 @@ import com.iqser.red.service.dictionarymerge.commons.DictionaryMergeService;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.SharedMongoAutoConfiguration; import com.iqser.red.service.persistence.service.v1.api.shared.mongo.SharedMongoAutoConfiguration;
import com.iqser.red.service.redaction.v1.server.client.RulesClient; import com.iqser.red.service.redaction.v1.server.client.RulesClient;
import com.iqser.red.storage.commons.StorageAutoConfiguration; import com.iqser.red.storage.commons.StorageAutoConfiguration;
import com.knecon.fforesight.lifecyclecommons.LifecycleAutoconfiguration;
import com.knecon.fforesight.mongo.database.commons.MongoDatabaseCommonsAutoConfiguration; import com.knecon.fforesight.mongo.database.commons.MongoDatabaseCommonsAutoConfiguration;
import com.knecon.fforesight.mongo.database.commons.liquibase.EnableMongoLiquibase; import com.knecon.fforesight.mongo.database.commons.liquibase.EnableMongoLiquibase;
import com.knecon.fforesight.tenantcommons.MultiTenancyAutoConfiguration; import com.knecon.fforesight.tenantcommons.MultiTenancyAutoConfiguration;
@ -32,13 +34,14 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@EnableCaching @EnableCaching
@ImportAutoConfiguration({MultiTenancyAutoConfiguration.class, SharedMongoAutoConfiguration.class}) @ImportAutoConfiguration({MultiTenancyAutoConfiguration.class, SharedMongoAutoConfiguration.class, LifecycleAutoconfiguration.class})
@Import({MetricsConfiguration.class, StorageAutoConfiguration.class, MongoDatabaseCommonsAutoConfiguration.class}) @Import({MetricsConfiguration.class, StorageAutoConfiguration.class, MongoDatabaseCommonsAutoConfiguration.class})
@EnableFeignClients(basePackageClasses = RulesClient.class) @EnableFeignClients(basePackageClasses = RulesClient.class)
@EnableConfigurationProperties(RedactionServiceSettings.class) @EnableConfigurationProperties(RedactionServiceSettings.class)
@EnableMongoRepositories(basePackages = "com.iqser.red.service.persistence") @EnableMongoRepositories(basePackages = "com.iqser.red.service.persistence")
@EnableMongoLiquibase @EnableMongoLiquibase
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, DataSourceAutoConfiguration.class, LiquibaseAutoConfiguration.class, MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) @SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, DataSourceAutoConfiguration.class, LiquibaseAutoConfiguration.class, MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
@EnableAspectJAutoProxy
public class Application { public class Application {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -85,7 +85,7 @@ public class Section extends AbstractSemanticNode {
*/ */
public boolean anyHeadlineContainsString(String value) { public boolean anyHeadlineContainsString(String value) {
return streamAllSubNodesOfType(NodeType.HEADLINE).anyMatch(h -> h.containsString(value)); return streamAllSubNodesOfType(NodeType.HEADLINE).anyMatch(h -> h.containsString(value)) || getHeadline().containsString(value);
} }
@ -97,7 +97,7 @@ public class Section extends AbstractSemanticNode {
*/ */
public boolean anyHeadlineContainsStringIgnoreCase(String value) { public boolean anyHeadlineContainsStringIgnoreCase(String value) {
return streamAllSubNodesOfType(NodeType.HEADLINE).anyMatch(h -> h.containsStringIgnoreCase(value)); return streamAllSubNodesOfType(NodeType.HEADLINE).anyMatch(h -> h.containsStringIgnoreCase(value)) || getHeadline().containsStringIgnoreCase(value);
} }

View File

@ -367,7 +367,7 @@ public class AnalyzeService {
return new NerEntitiesModel(nerEntitiesModel.getData().entrySet() return new NerEntitiesModel(nerEntitiesModel.getData().entrySet()
.stream() // .stream() //
.filter(entry -> sectionsToReanalyseIds.contains(entry.getKey())) // .filter(entry -> sectionsToReanalyseIds.contains(getSuperSectionID(entry.getKey()))) //
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
} }
@ -383,4 +383,11 @@ public class AnalyzeService {
return nerEntities; return nerEntities;
} }
private static Integer getSuperSectionID(String section) {
return NerEntitiesAdapter.sectionNumberToTreeId(section)
.get(0);
}
} }

View File

@ -223,7 +223,6 @@ public class EntityLogCreatorService {
String type = precursorEntity.getManualOverwrite().getType() String type = precursorEntity.getManualOverwrite().getType()
.orElse(precursorEntity.getType()); .orElse(precursorEntity.getType());
boolean isHint = isHint(precursorEntity.getEntityType());
return EntityLogEntry.builder() return EntityLogEntry.builder()
.id(precursorEntity.getId()) .id(precursorEntity.getId())
.reason(precursorEntity.buildReasonWithManualChangeDescriptions()) .reason(precursorEntity.buildReasonWithManualChangeDescriptions())

View File

@ -40,7 +40,8 @@ public class ComponentMappingFileSystemCache {
public File getComponentMappingFile(ComponentMappingMetadata metadata) { public File getComponentMappingFile(ComponentMappingMetadata metadata) {
Path mappingFile = getMappingFileFromMetadata(metadata); Path mappingFile = getMappingFileFromMetadata(metadata);
Path mappingFileMetaDataFile = mappingFile.resolveSibling(metadata.getName() + METADATA_SUFFIX); Path mappingFileMetaDataFile = getMappingMetadataFileFromMetadata(metadata);
synchronized (ComponentMappingFileSystemCache.class) { synchronized (ComponentMappingFileSystemCache.class) {
if (fileExistsAndUpToDate(metadata, mappingFile, mappingFileMetaDataFile)) { if (fileExistsAndUpToDate(metadata, mappingFile, mappingFileMetaDataFile)) {
@ -87,6 +88,13 @@ public class ComponentMappingFileSystemCache {
} }
private Path getMappingMetadataFileFromMetadata(ComponentMappingMetadata metadata) {
Path tenantStem = mappingFileDir.resolve(TenantContext.getTenantId());
return tenantStem.resolve(metadata.getStorageId() + METADATA_SUFFIX);
}
private boolean fileExistsAndUpToDate(ComponentMappingMetadata metadata, Path mappingFile, Path mappingFileMetaDataFile) { private boolean fileExistsAndUpToDate(ComponentMappingMetadata metadata, Path mappingFile, Path mappingFileMetaDataFile) {
if (mappingFile.toFile().exists() && mappingFile.toFile().isFile() && mappingFileMetaDataFile.toFile().exists() && mappingFileMetaDataFile.toFile().isFile()) { if (mappingFile.toFile().exists() && mappingFile.toFile().isFile() && mappingFileMetaDataFile.toFile().exists() && mappingFileMetaDataFile.toFile().isFile()) {

View File

@ -4,6 +4,7 @@ import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.Reader; import java.io.Reader;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.time.Duration;
import java.util.List; import java.util.List;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -36,7 +37,7 @@ public class ComponentMappingMemoryCache {
public ComponentMappingMemoryCache(ComponentMappingFileSystemCache componentMappingFileSystemCache) { public ComponentMappingMemoryCache(ComponentMappingFileSystemCache componentMappingFileSystemCache) {
this.fileSystemCache = componentMappingFileSystemCache; this.fileSystemCache = componentMappingFileSystemCache;
cache = CacheBuilder.newBuilder().maximumWeight(MAX_NUMBER_OF_LINES).weigher(COMPONENT_MAPPING_WEIGHER).build(); cache = CacheBuilder.newBuilder().maximumWeight(MAX_NUMBER_OF_LINES).weigher(COMPONENT_MAPPING_WEIGHER).expireAfterAccess(Duration.ofDays(1)).build();
} }

View File

@ -15,6 +15,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.BaseAnnotation; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.BaseAnnotation;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.model.PrecursorEntity; import com.iqser.red.service.redaction.v1.server.model.PrecursorEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
@ -35,6 +36,7 @@ public class EntityFromPrecursorCreationService {
static double MATCH_THRESHOLD = 10; // Is compared to the average sum of distances in pdf coordinates for each corner of the bounding box of the entities static double MATCH_THRESHOLD = 10; // Is compared to the average sum of distances in pdf coordinates for each corner of the bounding box of the entities
EntityFindingUtility entityFindingUtility; EntityFindingUtility entityFindingUtility;
DictionaryService dictionaryService; DictionaryService dictionaryService;
RedactionServiceSettings settings;
public List<PrecursorEntity> createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions manualRedactions, SemanticNode node, String dossierTemplateId) { public List<PrecursorEntity> createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions manualRedactions, SemanticNode node, String dossierTemplateId) {
@ -92,7 +94,7 @@ public class EntityFromPrecursorCreationService {
notFoundEntities.add(precursorEntity); notFoundEntities.add(precursorEntity);
continue; continue;
} }
createCorrectEntity(precursorEntity, optionalClosestEntity.get()); createCorrectEntity(precursorEntity, optionalClosestEntity.get(), settings.isAnnotationMode());
} }
tempEntitiesByValue.values() tempEntitiesByValue.values()
@ -129,8 +131,7 @@ public class EntityFromPrecursorCreationService {
precursorEntity.type(), precursorEntity.type(),
precursorEntity.getEntityType(), precursorEntity.getEntityType(),
precursorEntity.getId(), precursorEntity.getId(),
precursorEntity.getManualOverwrite().getSection() precursorEntity.getManualOverwrite().getSection().orElse(null));
.orElse(null));
} }
correctEntity.setDeepestFullyContainingNode(closestEntity.getDeepestFullyContainingNode()); correctEntity.setDeepestFullyContainingNode(closestEntity.getDeepestFullyContainingNode());
correctEntity.setIntersectingNodes(new ArrayList<>(closestEntity.getIntersectingNodes())); correctEntity.setIntersectingNodes(new ArrayList<>(closestEntity.getIntersectingNodes()));

View File

@ -177,7 +177,7 @@ public class NerEntitiesAdapter {
} }
private static List<Integer> sectionNumberToTreeId(String sectionNumber) { public static List<Integer> sectionNumberToTreeId(String sectionNumber) {
return Arrays.stream(sectionNumber.split("\\.")) return Arrays.stream(sectionNumber.split("\\."))
.map(Integer::parseInt) .map(Integer::parseInt)

View File

@ -73,9 +73,10 @@ public class ComponentDroolsExecutionService {
entities.add(Entity.fromEntityLogEntry(entry, document, entry.getStartOffset(), entry.getEndOffset())); entities.add(Entity.fromEntityLogEntry(entry, document, entry.getStartOffset(), entry.getEndOffset()));
if (entry.getDuplicatedTextRanges() != null && !entry.getDuplicatedTextRanges().isEmpty()) { if (entry.getDuplicatedTextRanges() != null && !entry.getDuplicatedTextRanges().isEmpty()) {
entry.getDuplicatedTextRanges() entry.getDuplicatedTextRanges()
.forEach(duplicatedTextRange -> { .forEach(duplicatedTextRange -> entities.add(Entity.fromEntityLogEntry(entry,
entities.add(Entity.fromEntityLogEntry(entry, document, duplicatedTextRange.getStart(), duplicatedTextRange.getEnd())); document,
}); duplicatedTextRange.getStart(),
duplicatedTextRange.getEnd())));
} }
return entities.stream(); return entities.stream();
}) })

View File

@ -16,6 +16,9 @@ project.version: 1.0-SNAPSHOT
server: server:
port: 8080 port: 8080
lifecycle:
base-package: com.iqser.red.service.redaction
spring: spring:
application: application:
name: redaction-service name: redaction-service

View File

@ -14,6 +14,8 @@ dd MMMM yyyy
d MMMM yyyy d MMMM yyyy
MMMM dd, yyyy MMMM dd, yyyy
MMMM d, yyyy MMMM d, yyyy
MMMM, d yyyy
MMMM d,yyyy
dd.MM.yyyy dd.MM.yyyy
d.MM.yyyy d.MM.yyyy
yyyy/MM/dd yyyy/MM/dd
@ -28,6 +30,7 @@ dd['.'] MMM yyyy
d['.'] MMM yyyy d['.'] MMM yyyy
dd['th']['st']['nd']['rd'] 'of' MMMM, yyyy dd['th']['st']['nd']['rd'] 'of' MMMM, yyyy
d['th']['st']['nd']['rd'] 'of' MMMM, yyyy d['th']['st']['nd']['rd'] 'of' MMMM, yyyy
d['st']['nd']['rd']['th'] MMMM yyyy
MMMM dd['th']['st']['nd']['rd'], yyyy MMMM dd['th']['st']['nd']['rd'], yyyy
MMMM d['th']['st']['nd']['rd'], yyyy MMMM d['th']['st']['nd']['rd'], yyyy
yyyy, MMMM dd yyyy, MMMM dd

View File

@ -121,6 +121,7 @@ public abstract class AbstractRedactionIntegrationTest {
public static final String NO_REDACTION_TYPE_ID = NO_REDACTION_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID; public static final String NO_REDACTION_TYPE_ID = NO_REDACTION_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID;
public static final String SPONSOR_TYPE_ID = DICTIONARY_SPONSOR + ":" + TEST_DOSSIER_TEMPLATE_ID; public static final String SPONSOR_TYPE_ID = DICTIONARY_SPONSOR + ":" + TEST_DOSSIER_TEMPLATE_ID;
public static final String AUTHOR_TYPE_ID = DICTIONARY_AUTHOR + ":" + TEST_DOSSIER_TEMPLATE_ID; public static final String AUTHOR_TYPE_ID = DICTIONARY_AUTHOR + ":" + TEST_DOSSIER_TEMPLATE_ID;
public static final String DOSSIER_AUTHOR_TYPE_ID = AUTHOR_TYPE_ID + ":" + TEST_DOSSIER_ID;
public static final String ADDRESS_TYPE_ID = DICTIONARY_ADDRESS + ":" + TEST_DOSSIER_TEMPLATE_ID; public static final String ADDRESS_TYPE_ID = DICTIONARY_ADDRESS + ":" + TEST_DOSSIER_TEMPLATE_ID;
public static final String VERTEBRATE_TYPE_ID = VERTEBRATE_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID; public static final String VERTEBRATE_TYPE_ID = VERTEBRATE_INDICATOR + ":" + TEST_DOSSIER_TEMPLATE_ID;
@ -250,8 +251,10 @@ public abstract class AbstractRedactionIntegrationTest {
true)); true));
when(dictionaryClient.getDictionaryForType(IMPORTED_REDACTION_TYPE_ID, version)).then((Answer<Type>) invocation -> getDictionaryResponse(IMPORTED_REDACTION_INDICATOR, when(dictionaryClient.getDictionaryForType(IMPORTED_REDACTION_TYPE_ID, version)).then((Answer<Type>) invocation -> getDictionaryResponse(IMPORTED_REDACTION_INDICATOR,
true)); true));
when(dictionaryClient.getDictionaryForType(DOSSIER_PUBLISHED_INFORMATION_TYPE_ID, version)).then((Answer<Type>) invocation -> getDictionaryResponse(PUBLISHED_INFORMATION_INDICATOR, when(dictionaryClient.getDictionaryForType(DOSSIER_PUBLISHED_INFORMATION_TYPE_ID, version)).then((Answer<Type>) invocation -> getDictionaryResponse(
PUBLISHED_INFORMATION_INDICATOR,
true)); true));
when(dictionaryClient.getDictionaryForType(DOSSIER_AUTHOR_TYPE_ID, version)).then((Answer<Type>) invocation -> getDictionaryResponse(DICTIONARY_AUTHOR, true));
} }
@ -350,6 +353,7 @@ public abstract class AbstractRedactionIntegrationTest {
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
dossierDictionary.put(IMPORTED_REDACTION_INDICATOR, new ArrayList<>()); dossierDictionary.put(IMPORTED_REDACTION_INDICATOR, new ArrayList<>());
dossierDictionary.put(PUBLISHED_INFORMATION_INDICATOR, new ArrayList<>()); dossierDictionary.put(PUBLISHED_INFORMATION_INDICATOR, new ArrayList<>());
dossierDictionary.put(DICTIONARY_AUTHOR, new ArrayList<>());
falsePositive.computeIfAbsent(DICTIONARY_PII, v -> new ArrayList<>()) falsePositive.computeIfAbsent(DICTIONARY_PII, v -> new ArrayList<>())
.addAll(ResourceLoader.load("dictionaries/PII_false_positive.txt") .addAll(ResourceLoader.load("dictionaries/PII_false_positive.txt")

View File

@ -174,6 +174,16 @@ public class DocumineFloraTest extends AbstractRedactionIntegrationTest {
expectedDates.add("03/08/1992"); expectedDates.add("03/08/1992");
expectedDates.add("13/08/1992"); expectedDates.add("13/08/1992");
expectedDates.add("27/02/1992"); expectedDates.add("27/02/1992");
expectedDates.add("27/10/1989");
expectedDates.add("07/10/1989");
expectedDates.add("21/08/1998");
expectedDates.add("02/08/1998");
expectedDates.add("01/05/1988");
expectedDates.add("02/06/2003");
expectedDates.add("03/09/2005");
expectedDates.add("06/09/2005");
expectedDates.add("17/08/2005");
expectedDates.add("22/08/2035");
String dates = experimentalDates.getComponentValues() String dates = experimentalDates.getComponentValues()
.get(0).getValue(); .get(0).getValue();

View File

@ -10,6 +10,7 @@ import java.time.OffsetDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -25,24 +26,30 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.iqser.red.commons.jackson.ObjectMapperFactory; import com.iqser.red.commons.jackson.ObjectMapperFactory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeRequest; import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeResult; import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeResult;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive; import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
import com.iqser.red.service.redaction.v1.server.annotate.AnnotateRequest; 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.AnnotateResponse;
import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils; import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils;
import com.iqser.red.service.redaction.v1.server.service.DictionaryService; import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import com.iqser.red.storage.commons.StorageAutoConfiguration; import com.iqser.red.storage.commons.StorageAutoConfiguration;
import com.iqser.red.storage.commons.service.StorageService; import com.iqser.red.storage.commons.service.StorageService;
import com.iqser.red.storage.commons.utils.FileSystemBackedStorageService; import com.iqser.red.storage.commons.utils.FileSystemBackedStorageService;
@ -50,6 +57,8 @@ import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsi
import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingServiceProcessorConfiguration; import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingServiceProcessorConfiguration;
import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantcommons.TenantContext;
import lombok.SneakyThrows;
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Import(RedactionIntegrationTest.RedactionIntegrationTestConfiguration.class) @Import(RedactionIntegrationTest.RedactionIntegrationTestConfiguration.class)
@ -103,6 +112,17 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
.isRecommendation(recommendationTypeMap.get(DOSSIER_REDACTIONS_INDICATOR)) .isRecommendation(recommendationTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
.rank(rankTypeMap.get(DOSSIER_REDACTIONS_INDICATOR)) .rank(rankTypeMap.get(DOSSIER_REDACTIONS_INDICATOR))
.build(), .build(),
Type.builder()
.id(DOSSIER_AUTHOR_TYPE_ID)
.type(DICTIONARY_AUTHOR)
.dossierTemplateId(TEST_DOSSIER_TEMPLATE_ID)
.dossierId(TEST_DOSSIER_ID)
.hexColor("#ffe184")
.isHint(hintTypeMap.get(DICTIONARY_AUTHOR))
.isCaseInsensitive(caseInSensitiveMap.get(DICTIONARY_AUTHOR))
.isRecommendation(recommendationTypeMap.get(DICTIONARY_AUTHOR))
.rank(rankTypeMap.get(DICTIONARY_AUTHOR))
.build(),
Type.builder() Type.builder()
.id(DOSSIER_PUBLISHED_INFORMATION_TYPE_ID) .id(DOSSIER_PUBLISHED_INFORMATION_TYPE_ID)
.type(PUBLISHED_INFORMATION_INDICATOR) .type(PUBLISHED_INFORMATION_INDICATOR)
@ -158,10 +178,10 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
var publishedInformationEntry1 = findEntityByTypeAndValue(entityLog, "published_information", "Oxford University Press").findFirst() var publishedInformationEntry1 = findEntityByTypeAndValue(entityLog, "published_information", "Oxford University Press").findFirst()
.orElseThrow(); .orElseThrow();
assertThat(publishedInformationEntry1.getSection().startsWith("Paragraph:")); assertThat(publishedInformationEntry1.getSection()).startsWith("Paragraph:");
var asyaLyon1 = findEntityByTypeAndValueAndSectionNumber(entityLog, "CBI_author", "Asya Lyon", publishedInformationEntry1.getContainingNodeId()).findFirst() var asyaLyon1 = findEntityByTypeAndValueAndSectionNumber(entityLog, "CBI_author", "Asya Lyon", publishedInformationEntry1.getContainingNodeId()).findFirst()
.orElseThrow(); .orElseThrow();
assertThat(asyaLyon1.getSection().startsWith("Paragraph:")); assertThat(asyaLyon1.getSection()).startsWith("Paragraph:");
assertEquals(EntryState.SKIPPED, asyaLyon1.getState()); assertEquals(EntryState.SKIPPED, asyaLyon1.getState());
var idRemoval = buildIdRemoval(publishedInformationEntry1.getId()); var idRemoval = buildIdRemoval(publishedInformationEntry1.getId());
@ -212,10 +232,10 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
var publishedInformationEntry1 = findEntityByTypeAndValue(entityLog, "published_information", "Oxford University Press").findFirst() var publishedInformationEntry1 = findEntityByTypeAndValue(entityLog, "published_information", "Oxford University Press").findFirst()
.orElseThrow(); .orElseThrow();
assertThat(publishedInformationEntry1.getSection().startsWith("Paragraph:")); assertThat(publishedInformationEntry1.getSection()).startsWith("Paragraph:");
var asyaLyon1 = findEntityByTypeAndValueAndSectionNumber(entityLog, "CBI_author", "Asya Lyon", publishedInformationEntry1.getContainingNodeId()).findFirst() var asyaLyon1 = findEntityByTypeAndValueAndSectionNumber(entityLog, "CBI_author", "Asya Lyon", publishedInformationEntry1.getContainingNodeId()).findFirst()
.orElseThrow(); .orElseThrow();
assertThat(publishedInformationEntry1.getSection().startsWith("Paragraph:")); assertThat(publishedInformationEntry1.getSection()).startsWith("Paragraph:");
assertEquals(EntryState.SKIPPED, asyaLyon1.getState()); assertEquals(EntryState.SKIPPED, asyaLyon1.getState());
var idRemoval = buildIdRemoval(publishedInformationEntry1.getId()); var idRemoval = buildIdRemoval(publishedInformationEntry1.getId());
@ -244,6 +264,7 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
assertThat(dictionary.get(PUBLISHED_INFORMATION_INDICATOR).contains("Press")).isFalse(); assertThat(dictionary.get(PUBLISHED_INFORMATION_INDICATOR).contains("Press")).isFalse();
} }
@Test @Test
public void testPublishedInformationRemovalAtDossierLevel() throws IOException { public void testPublishedInformationRemovalAtDossierLevel() throws IOException {
@ -365,6 +386,75 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
} }
@Test
@SneakyThrows
void testNerEntitiesAfterReanalysis() {
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
ClassPathResource responseJson = new ClassPathResource("files/new/SYNGENTA_EFSA_sanitisation_GFL_v1 3.NER_ENTITIES.json");
storageService.storeObject(TenantContext.getTenantId(),
RedactionStorageService.StorageIdUtils.getStorageId(TEST_DOSSIER_ID, TEST_FILE_ID, FileType.NER_ENTITIES),
responseJson.getInputStream());
String pdfFile = "files/new/SYNGENTA_EFSA_sanitisation_GFL_v1 3.pdf";
AnalyzeRequest request = uploadFileToStorage(pdfFile);
analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request);
request.setAnalysisNumber(1);
dossierDictionary.put(DICTIONARY_AUTHOR, new ArrayList<>());
mockDictionaryCalls(0L);
analyzeService.analyze(request);
var entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
String nerValue = "Osip S.";
var nerEntity = findEntityByTypeAndValue(entityLog, DICTIONARY_AUTHOR, nerValue).findFirst()
.orElseThrow();
assertThat(nerEntity.getEngines()).contains(Engine.NER);
String dictionaryAddValue = "cooperation";
ManualRedactionEntry manualRedactionEntry = ManualRedactionEntry.builder()
.value(dictionaryAddValue)
.type(DICTIONARY_AUTHOR)
.user("user")
.addToDossierDictionary(true)
.positions(List.of(Rectangle.builder().topLeftX(180.748f).topLeftY(546.564f).width(56.592f).height(15.408f).page(1).build()))
.type("dossier_redaction")
.fileId(TEST_FILE_ID)
.requestDate(OffsetDateTime.now())
.annotationId(UUID.randomUUID().toString())
.build();
request.setManualRedactions(ManualRedactions.builder().entriesToAdd(Set.of(manualRedactionEntry)).build());
request.setAnalysisNumber(2);
dossierDictionary.get(DICTIONARY_AUTHOR).add(dictionaryAddValue);
reanlysisVersions.put(dictionaryAddValue, 2L);
when(dictionaryClient.getVersionForDossier(TEST_DOSSIER_ID)).thenReturn(2L);
mockDictionaryCalls(1L);
AnalyzeResult reanalyzeResult = analyzeService.reanalyze(request);
entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
EntityLogEntry entityLogEntryAdded = entityLog.getEntityLogEntry()
.stream()
.filter(entityLogEntry -> entityLogEntry.getValue().equals(dictionaryAddValue))
.findFirst()
.get();
assertEquals(EntryState.APPLIED, entityLogEntryAdded.getState());
nerEntity = findEntityByTypeAndValue(entityLog, DICTIONARY_AUTHOR, nerValue).findFirst()
.orElseThrow();
assertThat(nerEntity.getEngines()).contains(Engine.NER);
dossierDictionary.get(DICTIONARY_AUTHOR).remove(dictionaryAddValue);
}
private static IdRemoval buildIdRemoval(String id) { private static IdRemoval buildIdRemoval(String id) {
return IdRemoval.builder().annotationId(id).user("user").requestDate(OffsetDateTime.now()).fileId(TEST_FILE_ID).build(); return IdRemoval.builder().annotationId(id).user("user").requestDate(OffsetDateTime.now()).fileId(TEST_FILE_ID).build();

View File

@ -43,7 +43,17 @@ public class DateConverterTest {
"28 March 2018 (animal 1 - 5000 mg/kg bw)", "28 March 2018 (animal 1 - 5000 mg/kg bw)",
"28 March 2018 (animal1 - 5000 mg/kg bw)", "28 March 2018 (animal1 - 5000 mg/kg bw)",
"28 August 2018 (animal 1)", "28 August 2018 (animal 1)",
"31 August 2018 (animal 1)"); "31 August 2018 (animal 1)",
"October, 27 1989",
"October, 7 1989",
"August 21,1998",
"August 2,1998",
"1st May 1988",
"2nd June 2003",
"3rd September 2005",
"6th September 2005",
"17th August 2005",
"22nd August 2035");
for (String dateStr : goldenStandardDates) { for (String dateStr : goldenStandardDates) {
Optional<Date> parsedDate = DateConverter.parseDate(dateStr); Optional<Date> parsedDate = DateConverter.parseDate(dateStr);