Merge branch 'RED-6686' into 'master'

RED-6686 Extract Tenant and user-management code into a separate service.

See merge request redactmanager/redaction-service!2
This commit is contained in:
Timo Bejan 2023-06-26 22:02:55 +02:00
commit 0f4effa68b
54 changed files with 106 additions and 390 deletions

View File

@ -18,10 +18,15 @@
<javaassist.version>3.29.2-GA</javaassist.version>
<ahocorasick.version>0.6.3</ahocorasick.version>
<jackson.version>2.14.2</jackson.version>
<tennat-commons.version>0.6.0</tennat-commons.version>
</properties>
<dependencies>
<dependency>
<groupId>com.knecon.fforesight</groupId>
<artifactId>tenant-commons</artifactId>
<version>${tennat-commons.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>

View File

@ -2,6 +2,7 @@ package com.iqser.red.service.redaction.v1.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -11,16 +12,15 @@ import org.springframework.context.annotation.Import;
import com.iqser.red.service.dictionarymerge.commons.DictionaryMergeService;
import com.iqser.red.service.redaction.v1.server.client.RulesClient;
import com.iqser.red.service.redaction.v1.server.multitenancy.AsyncConfig;
import com.iqser.red.service.redaction.v1.server.multitenancy.MultiTenancyMessagingConfiguration;
import com.iqser.red.service.redaction.v1.server.multitenancy.MultiTenancyWebConfiguration;
import com.iqser.red.service.redaction.v1.server.settings.RedactionServiceSettings;
import com.iqser.red.storage.commons.StorageAutoConfiguration;
import com.knecon.fforesight.tenantcommons.MultiTenancyAutoConfiguration;
import io.micrometer.core.aop.TimedAspect;
import io.micrometer.core.instrument.MeterRegistry;
@Import({MultiTenancyWebConfiguration.class, AsyncConfig.class, MultiTenancyMessagingConfiguration.class, MetricsConfiguration.class, StorageAutoConfiguration.class})
@ImportAutoConfiguration({MultiTenancyAutoConfiguration.class})
@Import({MetricsConfiguration.class, StorageAutoConfiguration.class})
@EnableFeignClients(basePackageClasses = RulesClient.class)
@EnableConfigurationProperties(RedactionServiceSettings.class)
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class})

View File

@ -1,10 +0,0 @@
package com.iqser.red.service.redaction.v1.server.client;
import org.springframework.cloud.openfeign.FeignClient;
import com.iqser.red.service.persistence.service.v1.api.internal.resources.TenantsResource;
@FeignClient(name = "TenantsResource", url = "${persistence-service.url}")
public interface TenantsClient extends TenantsResource {
}

View File

@ -25,4 +25,22 @@ public class ImageServiceResponse {
@JsonAlias("data")
public void setData(List<ImageMetadata> data) {this.data = data;}
public List<ImageMetadata> getData() {
if (this.data == null) {
this.data = new ArrayList<>();
}
return data;
}
public List<ImageMetadata> getDataCV() {
if (this.dataCV == null) {
this.dataCV = new ArrayList<>();
}
return dataCV;
}
}

View File

@ -1,27 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import java.util.concurrent.Executor;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("TenantAwareTaskExecutor-");
executor.setTaskDecorator(new TenantAwareTaskDecorator());
executor.initialize();
return executor;
}
}

View File

@ -1,105 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import lombok.SneakyThrows;
@Service
public class EncryptionDecryptionService {
@Value("${redaction-service.crypto.key:redaction}")
private String key;
private SecretKey secretKey;
private byte[] iv;
@SneakyThrows
@PostConstruct
protected void postConstruct() {
SecureRandom secureRandom = new SecureRandom();
iv = new byte[12];
secureRandom.nextBytes(iv);
secretKey = generateSecretKey(key, iv);
}
@SneakyThrows
public String encrypt(String strToEncrypt) {
return Base64.getEncoder().encodeToString(encrypt(strToEncrypt.getBytes()));
}
@SneakyThrows
public String decrypt(String strToDecrypt) {
byte[] bytes = Base64.getDecoder().decode(strToDecrypt);
return new String(decrypt(bytes), StandardCharsets.UTF_8);
}
@SneakyThrows
public byte[] encrypt(byte[] data) {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] encryptedData = cipher.doFinal(data);
ByteBuffer byteBuffer = ByteBuffer.allocate(4 + iv.length + encryptedData.length);
byteBuffer.putInt(iv.length);
byteBuffer.put(iv);
byteBuffer.put(encryptedData);
return byteBuffer.array();
}
@SneakyThrows
public byte[] decrypt(byte[] encryptedData) {
ByteBuffer byteBuffer = ByteBuffer.wrap(encryptedData);
int noonceSize = byteBuffer.getInt();
if (noonceSize < 12 || noonceSize >= 16) {
throw new IllegalArgumentException("Nonce size is incorrect. Make sure that the incoming data is an AES encrypted file.");
}
byte[] iv = new byte[noonceSize];
byteBuffer.get(iv);
SecretKey secretKey = generateSecretKey(key, iv);
byte[] cipherBytes = new byte[byteBuffer.remaining()];
byteBuffer.get(cipherBytes);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
return cipher.doFinal(cipherBytes);
}
@SneakyThrows
public SecretKey generateSecretKey(String password, byte[] iv) {
KeySpec spec = new PBEKeySpec(password.toCharArray(), iv, 65536, 128); // AES-128
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] key = secretKeyFactory.generateSecret(spec).getEncoded();
return new SecretKeySpec(key, "AES");
}
}

View File

@ -1,17 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import org.springframework.stereotype.Component;
import feign.RequestInterceptor;
import feign.RequestTemplate;
@Component
public class ForwardTenantInterceptor implements RequestInterceptor {
public static final String TENANT_HEADER_NAME = "X-TENANT-ID";
@Override
public void apply(RequestTemplate template) {
template.header(TENANT_HEADER_NAME, TenantContext.getTenantId());
}
}

View File

@ -1,49 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import static com.iqser.red.service.redaction.v1.server.multitenancy.ForwardTenantInterceptor.TENANT_HEADER_NAME;
import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MultiTenancyMessagingConfiguration {
@Bean
public static BeanPostProcessor multitenancyBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RabbitTemplate) {
((RabbitTemplate) bean).setBeforePublishPostProcessors(m -> {
m.getMessageProperties().setHeader(TENANT_HEADER_NAME, TenantContext.getTenantId());
return m;
});
} else if (bean instanceof AbstractRabbitListenerContainerFactory) {
((AbstractRabbitListenerContainerFactory<?>) bean).setAfterReceivePostProcessors(m -> {
String tenant = m.getMessageProperties().getHeader(TENANT_HEADER_NAME);
if (tenant != null) {
TenantContext.setTenantId(tenant);
} else {
throw new RuntimeException("No Tenant is set queue message");
}
return m;
});
}
return bean;
}
};
}
}

View File

@ -1,28 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import com.iqser.red.commons.spring.DefaultWebMvcConfiguration;
@Configuration
public class MultiTenancyWebConfiguration extends DefaultWebMvcConfiguration {
private final TenantInterceptor tenantInterceptor;
@Autowired
public MultiTenancyWebConfiguration(TenantInterceptor tenantInterceptor) {
this.tenantInterceptor = tenantInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addWebRequestInterceptor(tenantInterceptor);
}
}

View File

@ -1,45 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.v1.server.client.TenantsClient;
import com.iqser.red.storage.commons.model.AzureStorageConnection;
import com.iqser.red.storage.commons.model.S3StorageConnection;
import com.iqser.red.storage.commons.service.StorageConnectionProvider;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class StorageConnectionProviderImpl implements StorageConnectionProvider {
private final TenantsClient tenantsClient;
private final EncryptionDecryptionService encryptionDecryptionService;
@Override
public AzureStorageConnection getAzureStorageConnection(String tenantId) {
var tenant = tenantsClient.getTenant(tenantId);
return AzureStorageConnection.builder()
.connectionString(encryptionDecryptionService.decrypt(tenant.getAzureStorageConnection().getConnectionString()))
.containerName(tenant.getAzureStorageConnection().getContainerName())
.build();
}
@Override
public S3StorageConnection getS3StorageConnection(String tenantId) {
var tenant = tenantsClient.getTenant(tenantId);
return S3StorageConnection.builder()
.key(tenant.getS3StorageConnection().getKey())
.secret(encryptionDecryptionService.decrypt(tenant.getS3StorageConnection().getSecret()))
.signerType(tenant.getS3StorageConnection().getSignerType())
.bucketName(tenant.getS3StorageConnection().getBucketName())
.region(tenant.getS3StorageConnection().getRegion())
.endpoint(tenant.getS3StorageConnection().getEndpoint())
.build();
}
}

View File

@ -1,23 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import org.springframework.core.task.TaskDecorator;
import org.springframework.lang.NonNull;
public class TenantAwareTaskDecorator implements TaskDecorator {
@Override
@NonNull
public Runnable decorate(@NonNull Runnable runnable) {
String tenantId = TenantContext.getTenantId();
return () -> {
try {
TenantContext.setTenantId(tenantId);
runnable.run();
} finally {
TenantContext.setTenantId(null);
}
};
}
}

View File

@ -1,29 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public final class TenantContext {
private static InheritableThreadLocal<String> currentTenant = new InheritableThreadLocal<>();
public static void setTenantId(String tenantId) {
log.debug("Setting tenantId to " + tenantId);
currentTenant.set(tenantId);
}
public static String getTenantId() {
return currentTenant.get();
}
public static void clear() {
currentTenant.remove();
}
}

View File

@ -1,35 +0,0 @@
package com.iqser.red.service.redaction.v1.server.multitenancy;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
@Component
public class TenantInterceptor implements WebRequestInterceptor {
public static final String TENANT_HEADER_NAME = "X-TENANT-ID";
@Override
public void preHandle(WebRequest request) {
if (request.getHeader(TENANT_HEADER_NAME) != null) {
TenantContext.setTenantId(request.getHeader(TENANT_HEADER_NAME));
}
}
@Override
public void postHandle(WebRequest request, ModelMap model) {
TenantContext.clear();
}
@Override
public void afterCompletion(WebRequest request, Exception ex) {
}
}

View File

@ -6,6 +6,7 @@ import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfigura
import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfiguration.X_ERROR_INFO_HEADER;
import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfiguration.X_ERROR_INFO_TIMESTAMP_HEADER;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
@ -110,9 +111,9 @@ public class RedactionMessageReceiver {
@RabbitHandler
@RabbitListener(queues = REDACTION_DQL)
public void receiveAnalyzeRequestDQL(Message in) throws JsonProcessingException {
public void receiveAnalyzeRequestDQL(Message in) throws IOException {
var analyzeRequest = objectMapper.readValue(new String(in.getBody(), StandardCharsets.UTF_8), AnalyzeRequest.class);
var analyzeRequest = objectMapper.readValue(in.getBody(), AnalyzeRequest.class);
log.info("Failed to process analyze request: {}", analyzeRequest);
String errorCause = in.getMessageProperties().getHeader(X_ERROR_INFO_HEADER);
OffsetDateTime timestamp = in.getMessageProperties().getHeader(X_ERROR_INFO_TIMESTAMP_HEADER);

View File

@ -24,7 +24,6 @@ import com.iqser.red.service.dictionarymerge.commons.DictionaryEntryModel;
import com.iqser.red.service.dictionarymerge.commons.DictionaryMergeService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.redaction.v1.server.client.DictionaryClient;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.DictionaryEntries;
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.DictionaryIncrement;
@ -34,6 +33,7 @@ import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.Dict
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.DictionaryVersion;
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.TenantDictionary;
import com.iqser.red.service.redaction.v1.server.settings.RedactionServiceSettings;
import com.knecon.fforesight.tenantcommons.TenantContext;
import feign.FeignException;
import io.micrometer.core.annotation.Timed;

View File

@ -23,6 +23,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribu
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.redaction.v1.server.client.RulesClient;
import com.iqser.red.service.redaction.v1.server.exception.RulesValidationException;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Document;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.SemanticNode;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.services.EntityCreationService;

View File

@ -11,9 +11,9 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlo
import com.iqser.red.service.redaction.v1.server.client.model.NerEntitiesModel;
import com.iqser.red.service.redaction.v1.server.exception.NotFoundException;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.data.DocumentData;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import io.micrometer.core.annotation.Timed;
import lombok.Getter;

View File

@ -2,6 +2,7 @@ server:
port: 8083
persistence-service.url: "http://localhost:8085"
tenant-user-management-service.url: "http://localhost:8091/internal"
storage:
bucket-name: 'redaction'

View File

@ -4,6 +4,8 @@ info:
persistence-service.url: "http://persistence-service-v1:8080"
image-service.url: "http://image-service-v1:8080"
entity-recognition-service.url: "http://entity-recognition-service-v1:8080"
tenant-user-management-service.url: "http://tenant-user-management-service:8080/internal"
fforesight.tenants.remote: true
server:
port: 8080

View File

@ -35,13 +35,14 @@ 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.multitenancy.TenantContext;
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.ResourceLoader;
import com.iqser.red.service.redaction.v1.server.redaction.utils.TextNormalizationUtilities;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantsClient;
import lombok.SneakyThrows;
@ -120,6 +121,9 @@ public abstract class AbstractRedactionIntegrationTest {
@MockBean
protected LegalBasisClient legalBasisClient;
@MockBean
private TenantsClient tenantsClient;
protected final Map<String, List<String>> dictionary = new HashMap<>();
protected final Map<String, List<String>> dossierDictionary = new HashMap<>();
protected final Map<String, List<String>> falsePositive = new HashMap<>();

View File

@ -15,6 +15,7 @@ import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.kie.api.runtime.KieContainer;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
@ -32,16 +33,23 @@ import com.iqser.red.service.dictionarymerge.commons.DictionaryEntryModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
import com.iqser.red.service.redaction.v1.server.client.DictionaryClient;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.DictionaryVersion;
import com.iqser.red.service.redaction.v1.server.redaction.service.DictionaryService;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantsClient;
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Import(RedactionIntegrationTest.RedactionIntegrationTestConfiguration.class)
public class DictionaryServiceTest {
@MockBean
private RabbitTemplate rabbitTemplate;
@MockBean
private TenantsClient tenantsClient;
@MockBean
protected KieContainer kieContainer;

View File

@ -27,10 +27,10 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp
import com.iqser.red.service.redaction.v1.model.StructureAnalyzeRequest;
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.multitenancy.TenantContext;
import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils;
import com.iqser.red.storage.commons.StorageAutoConfiguration;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

View File

@ -16,6 +16,7 @@ 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.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ -33,11 +34,12 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlo
import com.iqser.red.service.redaction.v1.model.StructureAnalyzeRequest;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.data.mapper.DocumentGraphMapper;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.SemanticNode;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.iqser.red.service.redaction.v1.server.redaction.service.AnalyzeService;
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 com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantsClient;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -50,6 +52,9 @@ import lombok.ToString;
@Import(HeadlinesGoldStandardIntegrationTest.RedactionIntegrationTestConfiguration.class)
public class HeadlinesGoldStandardIntegrationTest {
@MockBean
private TenantsClient tenantsClient;
@Autowired
private AnalyzeService analyzeService;

View File

@ -75,11 +75,11 @@ import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.en
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Document;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Section;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.services.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils;
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 com.knecon.fforesight.tenantcommons.TenantContext;
import lombok.SneakyThrows;

View File

@ -25,9 +25,9 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Engine;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLogEntry;
import com.iqser.red.service.redaction.v1.model.StructureAnalyzeRequest;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.iqser.red.storage.commons.StorageAutoConfiguration;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import lombok.SneakyThrows;

View File

@ -77,14 +77,15 @@ 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.multitenancy.TenantContext;
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.service.AnalyzeService;
import com.iqser.red.service.redaction.v1.server.redaction.utils.ResourceLoader;
import com.iqser.red.service.redaction.v1.server.redaction.utils.TextNormalizationUtilities;
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 com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantsClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@ -245,6 +246,8 @@ public class RulesTest {
private RabbitTemplate rabbitTemplate;
@MockBean
private LegalBasisClient legalBasisClient;
@MockBean
private TenantsClient tenantsClient;
@BeforeEach

View File

@ -15,7 +15,7 @@ import com.iqser.red.service.redaction.v1.server.layoutparsing.document.data.map
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Document;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.NodeType;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Table;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantContext;
import lombok.SneakyThrows;

View File

@ -31,8 +31,8 @@ import com.iqser.red.service.redaction.v1.server.layoutparsing.document.data.Doc
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.data.mapper.DocumentGraphMapper;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.entity.RedactionEntity;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Document;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.iqser.red.service.redaction.v1.server.redaction.service.RedactionLogCreatorService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import lombok.SneakyThrows;

View File

@ -41,13 +41,13 @@ import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.no
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.textblock.TextBlock;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.services.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.PdfVisualisationUtility;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
import com.iqser.red.service.redaction.v1.server.redaction.adapter.NerEntities;
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.DictionaryModel;
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.SearchImplementation;
import com.iqser.red.service.redaction.v1.server.redaction.service.DictionaryService;
import com.iqser.red.service.redaction.v1.server.redaction.service.DroolsExecutionService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import lombok.SneakyThrows;

View File

@ -5,7 +5,9 @@ import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.core.io.ClassPathResource;
import com.fasterxml.jackson.databind.ObjectMapper;

View File

@ -17,6 +17,7 @@ import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
@ -47,13 +48,14 @@ import com.iqser.red.service.redaction.v1.server.client.FileStatusProcessingUpda
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.layoutparsing.classification.adapter.TableServiceResponseAdapter;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
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 com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantsClient;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import lombok.SneakyThrows;
@ -68,6 +70,12 @@ public class LiveDataIntegrationTest {
protected static String EFSA_SANITISATION_GFL_V1 = "dictionaries/EFSA_sanitisation_GFL_v1/";
@MockBean
private RabbitTemplate rabbitTemplate;
@MockBean
private TenantsClient tenantsClient;
@MockBean
protected DictionaryClient dictionaryClient;

View File

@ -47,10 +47,11 @@ import com.iqser.red.service.redaction.v1.server.layoutparsing.classification.se
import com.iqser.red.service.redaction.v1.server.layoutparsing.classification.service.RulingCleaningService;
import com.iqser.red.service.redaction.v1.server.layoutparsing.classification.service.TableExtractionService;
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.ImageType;
import com.iqser.red.service.redaction.v1.server.multitenancy.TenantContext;
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 com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantsClient;
import lombok.SneakyThrows;
@ -59,6 +60,9 @@ import lombok.SneakyThrows;
@Import(PdfSegmentationServiceTest.TestConfiguration.class)
public class PdfSegmentationServiceTest {
@MockBean
private TenantsClient tenantsClient;
@Autowired
private PdfSegmentationService pdfSegmentationService;

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[{"id":"c78bdc3af6d0cdee90f6944e433adc93","type":"PII","value":"Page","reason":"Personal information found","matchedRule":19,"rectangle":false,"legalBasis":"Article 39(e)(3) of Regulation (EC) No 178/2002","imported":false,"redacted":true,"section":"","color":[0.4,0.8,1.0],"positions":[{"topLeft":{"x":84.24396,"y":138.47998},"width":-13.883972,"height":-23.327972,"page":1}],"sectionNumber":12,"textBefore":"Report Number: 10/204-001P ","textAfter":" 18 of","comments":[],"startOffset":27,"endOffset":31,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:41.528113+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":false,"image":false}],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[{"id":"8bb0efbea56407b7d8929ab00736df9e","type":"PII","value":"Page","reason":"Personal information found","matchedRule":19,"rectangle":false,"legalBasis":"Article 39(e)(3) of Regulation (EC) No 178/2002","imported":false,"redacted":true,"section":"Footer","color":[0.4,0.8,1.0],"positions":[{"topLeft":{"x":477.10004,"y":50.84796},"width":23.196014,"height":-13.944031,"page":1}],"sectionNumber":10,"textBefore":"Report Number: 51726 ","textAfter":" 17 of","comments":[],"startOffset":21,"endOffset":25,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:39.846863+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":false,"image":false}],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[{"id":"ebd54f87fa1a8cb1da1610222d6bdecc","type":"PII","value":"Page","reason":"Personal information found","matchedRule":19,"rectangle":false,"legalBasis":"Article 39(e)(3) of Regulation (EC) No 178/2002","imported":false,"redacted":true,"section":"Footer","color":[0.4,0.8,1.0],"positions":[{"topLeft":{"x":477.1,"y":48.35797},"width":23.195984,"height":-11.4539795,"page":1}],"sectionNumber":8,"textBefore":"Report Number: 41087 ","textAfter":" 5 of","comments":[],"startOffset":21,"endOffset":25,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.519452+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":false,"image":false},{"id":"243fcf32e12af9128359a89d4e04e9a8","type":"CBI_address","value":"Product Safety Labs","reason":"Address found for non vertebrate study","matchedRule":3,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"","color":[0.8,0.8,0.8],"positions":[{"topLeft":{"x":111.624,"y":676.75397},"width":97.24799,"height":-11.453995,"page":1}],"sectionNumber":7,"textBefore":"ASSURANCE STATEMENT The ","textAfter":" Quality Assurance","comments":[],"startOffset":32,"endOffset":51,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.519456+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":false,"image":false},{"id":"71e4194df126fb383d3f1e29166ff177","type":"CBI_address","value":"Product Safety Labs","reason":"Address found for non vertebrate study","matchedRule":3,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"","color":[0.8,0.8,0.8],"positions":[{"topLeft":{"x":92.0,"y":307.0431},"width":96.024994,"height":-13.043121,"page":1}],"sectionNumber":9,"textBefore":"Quality Assurance Auditor ","textAfter":null,"comments":[],"startOffset":58,"endOffset":77,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.519457+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":false,"image":false}],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[{"id":"9526a0e351925f6dd820ebb1dc249e1a","type":"vertebrate","value":"fish","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":70.9,"y":466.48398},"width":16.488998,"height":-13.282013,"page":1}],"sectionNumber":21,"textBefore":null,"textAfter":null,"comments":[],"startOffset":106,"endOffset":110,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.759347+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false},{"id":"9c0ba267d41e778de9baf1b873dfb9ca","type":"vertebrate","value":"carp","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":334.87698,"y":316.752},"width":21.110016,"height":-13.150024,"page":1}],"sectionNumber":23,"textBefore":null,"textAfter":null,"comments":[],"startOffset":56,"endOffset":60,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.759352+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false},{"id":"a8b9eb90b95dde474e935a50e3bffd88","type":"vertebrate","value":"fish","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"Text in table","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":481.46198,"y":296.289},"width":13.994995,"height":-11.958008,"page":1}],"sectionNumber":24,"textBefore":null,"textAfter":null,"comments":[],"startOffset":132,"endOffset":136,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.759353+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false},{"id":"1e53bed41bf27cbd52fb577867ba5a34","type":"vertebrate","value":"fish","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":231.61401,"y":365.94098},"width":16.488983,"height":-13.282013,"page":1}],"sectionNumber":22,"textBefore":null,"textAfter":null,"comments":[],"startOffset":132,"endOffset":136,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.759354+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false}],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[{"id":"0cb37cfcddadbd5f21be0c6d20c67f10","type":"CBI_address","value":"Syngenta, Jealotts Hill International Research Centre, Bracknell, United Kingdom","reason":"Address found for non vertebrate study","matchedRule":3,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"Text in table","color":[0.8,0.8,0.8],"positions":[{"topLeft":{"x":328.11975,"y":718.18677},"width":186.16266,"height":-10.526825,"page":1},{"topLeft":{"x":140.66,"y":706.7868},"width":143.15887,"height":-10.526825,"page":1}],"sectionNumber":1,"textBefore":"apples in Switzerland. ","textAfter":". Syngenta Report","comments":[],"startOffset":140,"endOffset":221,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:41.098265+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":false,"image":false},{"id":"de5f84fbd56cce42f4656a9f479da2ad","type":"CBI_author","value":"Solé","reason":"Author found","matchedRule":1,"rectangle":false,"legalBasis":"Article 39(e)(3) of Regulation (EC) No 178/2002","imported":false,"redacted":true,"section":"Text in table","color":[1.0,0.88235295,0.5294118],"positions":[{"topLeft":{"x":204.65,"y":729.7068},"width":17.738754,"height":-10.526825,"page":1}],"sectionNumber":1,"textBefore":"Report: KCA 6.5.3/02. ","textAfter":" C. (2004),","comments":[],"startOffset":22,"endOffset":26,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:41.098269+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":false,"image":false}],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[{"id":"a1cc47e3608f5353c4ed748c957f1fa1","type":"vertebrate","value":"quail","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"Text in table","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":76.344,"y":236.69678},"width":22.081337,"height":-10.526794,"page":1}],"sectionNumber":27,"textBefore":null,"textAfter":null,"comments":[],"startOffset":0,"endOffset":5,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.361207+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false},{"id":"780603e0084f2226d5d18a4700ab3154","type":"vertebrate","value":"bird","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":365.04898,"y":513.80774},"width":17.741272,"height":-11.01767,"page":1}],"sectionNumber":18,"textBefore":null,"textAfter":null,"comments":[],"startOffset":59,"endOffset":63,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.361211+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false},{"id":"6eabc8d359ee12e599da3539f15a6c2e","type":"vertebrate","value":"hen","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"Text in table","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":76.344,"y":577.9068},"width":16.593369,"height":-10.526825,"page":1}],"sectionNumber":13,"textBefore":null,"textAfter":null,"comments":[],"startOffset":0,"endOffset":3,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.361212+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false},{"id":"74c55489f8565fad4b2d6a4f410fa839","type":"vertebrate","value":"pigeon","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"Text in table","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":99.36156,"y":613.4268},"width":27.160934,"height":-10.526825,"page":1}],"sectionNumber":11,"textBefore":null,"textAfter":null,"comments":[],"startOffset":6,"endOffset":12,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.361212+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false}],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[{"id":"1c121d1c34095ac0378a3422cdd3ff18","type":"vertebrate","value":"rabbits","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":297.34894,"y":337.41937},"width":36.796326,"height":-10.929382,"page":1}],"sectionNumber":23,"textBefore":null,"textAfter":null,"comments":[],"startOffset":130,"endOffset":137,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.443045+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false},{"id":"9eca9c7227e2dd7ce01c723e7c236af8","type":"vertebrate","value":"rabbits","reason":null,"matchedRule":0,"rectangle":false,"legalBasis":null,"imported":false,"redacted":false,"section":"","color":[1.0,0.52156866,0.96862745],"positions":[{"topLeft":{"x":283.28253,"y":712.9494},"width":36.674896,"height":-10.929367,"page":1}],"sectionNumber":2,"textBefore":null,"textAfter":null,"comments":[],"startOffset":129,"endOffset":136,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:40.44305+03:00"}],"manualChanges":[],"engines":["DICTIONARY"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":true,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":true,"image":false}],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}

View File

@ -0,0 +1 @@
{"analysisVersion":1,"analysisNumber":0,"redactionLogEntry":[{"id":"1ce95cca1f60b72402b89d9fac0fcd38","type":"PII","value":"Dr. Alan Miller","reason":"Author found","matchedRule":27,"rectangle":false,"legalBasis":"Article 39(e)(3) of Regulation (EC) No 178/2002","imported":false,"redacted":true,"section":"Header","color":[0.4,0.8,1.0],"positions":[{"topLeft":{"x":141.62,"y":746.30005},"width":63.601456,"height":-11.519997,"page":1}],"sectionNumber":2,"textBefore":"AUTHOR(S): ","textAfter":" COMPLETION DATE:","comments":[],"startOffset":11,"endOffset":26,"imageHasTransparency":false,"excluded":false,"sourceId":null,"changes":[{"analysisNumber":0,"type":"ADDED","dateTime":"2023-06-01T23:46:41.46219+03:00"}],"manualChanges":[],"engines":["RULE"],"reference":[],"importedRedactionIntersections":[],"recommendation":false,"falsePositive":false,"dictionaryEntry":false,"dossierDictionaryEntry":false,"manuallyRemoved":false,"localManualRedaction":false,"hint":false,"image":false}],"legalBasis":[],"dictionaryVersion":0,"dossierDictionaryVersion":0,"rulesVersion":0,"legalBasisVersion":0}