Merge branch 'RED-9947-fix2' into 'master'

RED-9947 && RED-10104

See merge request redactmanager/persistence-service!759
This commit is contained in:
Maverick Studer 2024-09-27 09:25:00 +02:00
commit dc42fcad40
22 changed files with 1245 additions and 350 deletions

View File

@ -19,15 +19,13 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.model.ManualChangesQueryOptions;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.CommentService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierService;
import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogMergeService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionProviderService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionUndoService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.PendingEntryFactory;
@ -40,11 +38,12 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.CommentResp
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.EntityLogEntryResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationComments;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Comment;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.CommentRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAddResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAnnotationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactionResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
@ -190,7 +189,7 @@ public class ManualRedactionController implements ManualRedactionResource {
addRedactionRequests.stream()
.anyMatch(AddRedactionRequestModel::isAddToAllDossiers));
List<ManualAddResponse> responseList = manualRedactionService.addAddRedaction(dossierId, fileId, addRedactionRequests, dossier);
List<ManualAnnotationResponse> responseList = manualRedactionService.addAddRedaction(dossierId, fileId, addRedactionRequests, dossier);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
@ -199,7 +198,7 @@ public class ManualRedactionController implements ManualRedactionResource {
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return ManualRedactionResponse.builder().manualAddResponses(responseList).build();
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@ -212,15 +211,15 @@ public class ManualRedactionController implements ManualRedactionResource {
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
dictionaryPersistenceService.getType(TypeIdUtils.toTypeId(addRedactionRequest.getType(), dossier.getDossierTemplateId()));
fileStatusService.setStatusBulkLocalRedactionsProcessing(dossierId, fileId, addRedactionRequest);
fileStatusService.setStatusBulkLocalRedactionsProcessing(fileId, addRedactionRequest);
EntityLogEntry entityLogEntry = pendingEntryFactory.buildAddRedactionBulkLocalEntry(addRedactionRequest);
return ManualRedactionResponse.builder()
.manualAddResponses(List.of(ManualAddResponse.builder()
.annotationId(manualRedactionService.getPendingBulkLocalAnnotationId(fileId, addRedactionRequest))
.entityLogEntry(entityLogEntry)
.build()))
.manualAnnotationResponses(List.of(ManualAnnotationResponse.builder()
.annotationId(manualRedactionService.getNewAnnotationId())
.entityLogEntry(entityLogEntry)
.build()))
.build();
}
@ -238,11 +237,11 @@ public class ManualRedactionController implements ManualRedactionResource {
removeRedactionRequests.stream()
.anyMatch(RemoveRedactionRequestModel::isRemoveFromAllDossiers));
List<ManualAddResponse> responseList = manualRedactionService.addRemoveRedaction(dossierId,
fileId,
removeRedactionRequests,
dossier.getDossierTemplateId(),
includeUnprocessed);
List<ManualAnnotationResponse> responseList = manualRedactionService.addRemoveRedaction(dossierId,
fileId,
removeRedactionRequests,
dossier.getDossierTemplateId(),
includeUnprocessed);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -252,7 +251,7 @@ public class ManualRedactionController implements ManualRedactionResource {
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return ManualRedactionResponse.builder().manualAddResponses(responseList).build();
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@ -263,20 +262,23 @@ public class ManualRedactionController implements ManualRedactionResource {
boolean includeUnprocessed) {
verifyAccess(dossierId, fileId);
verifyRequest(removeRedactionRequest.isRectangle(), removeRedactionRequest.getPosition(), removeRedactionRequest.getValue());
Set<RemoveRedactionRequestModel> removeRedactionRequestModels;
FileModel status = fileStatusService.getStatus(fileId);
if (!status.isExcludedFromAutomaticAnalysis()) {
Set<String> entryIds = getFilteredEntityLogEntryIds(removeRedactionRequest.isRectangle(),
removeRedactionRequest.getValue(),
removeRedactionRequest.isCaseSensitive(),
removeRedactionRequest.getOriginTypes(),
removeRedactionRequest.getOriginLegalBases(),
removeRedactionRequest.getPageNumbers(),
removeRedactionRequest.getPosition());
removeRedactionRequestModels = entryIds.stream()
.map(entryId -> RemoveRedactionRequestModel.builder().annotationId(entryId).build())
Set<EntityLogEntry> entries = getFilteredEntityLogEntries(dossierId,
fileId,
removeRedactionRequest.isRectangle(),
removeRedactionRequest.getValue(),
removeRedactionRequest.isCaseSensitive(),
removeRedactionRequest.getOriginTypes(),
removeRedactionRequest.getOriginLegalBases(),
removeRedactionRequest.getPageNumbers(),
removeRedactionRequest.getPosition());
removeRedactionRequestModels = entries.stream()
.map(entry -> RemoveRedactionRequestModel.builder().annotationId(entry.getId()).build())
.collect(Collectors.toSet());
} else {
@ -293,7 +295,7 @@ public class ManualRedactionController implements ManualRedactionResource {
removeRedactionRequest.getPosition());
removeRedactionRequestModels = filteredEntityLogResponses.stream()
.map(entityLogEntry -> RemoveRedactionRequestModel.builder().annotationId(entityLogEntry.getId()).build())
.map(entityLogEntry -> RemoveRedactionRequestModel.builder().annotationId(entityLogEntry.getId()).comment(removeRedactionRequest.getComment()).build())
.collect(Collectors.toSet());
}
return removeRedactionBulk(dossierId, fileId, removeRedactionRequestModels, includeUnprocessed);
@ -307,7 +309,7 @@ public class ManualRedactionController implements ManualRedactionResource {
verifyAccessAndDossierExistence(dossierId, fileId);
List<ManualAddResponse> responseList = manualRedactionService.addForceRedaction(dossierId, fileId, forceRedactionRequests);
List<ManualAnnotationResponse> responseList = manualRedactionService.addForceRedaction(dossierId, fileId, forceRedactionRequests);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -317,7 +319,7 @@ public class ManualRedactionController implements ManualRedactionResource {
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return ManualRedactionResponse.builder().manualAddResponses(responseList).build();
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@ -328,7 +330,7 @@ public class ManualRedactionController implements ManualRedactionResource {
verifyAccessAndDossierExistence(dossierId, fileId);
List<ManualAddResponse> responseList = manualRedactionService.addLegalBasisChange(dossierId, fileId, legalBasisChangeRequests);
List<ManualAnnotationResponse> responseList = manualRedactionService.addLegalBasisChange(dossierId, fileId, legalBasisChangeRequests);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -338,7 +340,7 @@ public class ManualRedactionController implements ManualRedactionResource {
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return ManualRedactionResponse.builder().manualAddResponses(responseList).build();
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@ -351,7 +353,7 @@ public class ManualRedactionController implements ManualRedactionResource {
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
verifyAccess(dossierId, fileId);
List<ManualAddResponse> responseList = manualRedactionService.addRecategorization(dossierId, fileId, dossier, recategorizationRequests, includeUnprocessed);
List<ManualAnnotationResponse> responseList = manualRedactionService.addRecategorization(dossierId, fileId, dossier, recategorizationRequests, includeUnprocessed);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -361,7 +363,7 @@ public class ManualRedactionController implements ManualRedactionResource {
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return ManualRedactionResponse.builder().manualAddResponses(responseList).build();
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@ -372,25 +374,28 @@ public class ManualRedactionController implements ManualRedactionResource {
boolean includeUnprocessed) {
verifyAccess(dossierId, fileId);
verifyRequest(recategorizationRequest.isRectangle(), recategorizationRequest.getPosition(), recategorizationRequest.getValue());
Set<RecategorizationRequestModel> recategorizationRequestModels;
FileModel status = fileStatusService.getStatus(fileId);
if (!status.isExcludedFromAutomaticAnalysis()) {
Set<String> entryIds = getFilteredEntityLogEntryIds(recategorizationRequest.isRectangle(),
recategorizationRequest.getValue(),
recategorizationRequest.isCaseSensitive(),
recategorizationRequest.getOriginTypes(),
recategorizationRequest.getOriginLegalBases(),
recategorizationRequest.getPageNumbers(),
recategorizationRequest.getPosition());
Set<EntityLogEntry> entries = getFilteredEntityLogEntries(dossierId,
fileId,
recategorizationRequest.isRectangle(),
recategorizationRequest.getValue(),
recategorizationRequest.isCaseSensitive(),
recategorizationRequest.getOriginTypes(),
recategorizationRequest.getOriginLegalBases(),
recategorizationRequest.getPageNumbers(),
recategorizationRequest.getPosition());
recategorizationRequestModels = entryIds.stream()
.map(entryId -> RecategorizationRequestModel.builder()
.annotationId(entryId)
.type(recategorizationRequest.getType())
recategorizationRequestModels = entries.stream()
.map(entry -> RecategorizationRequestModel.builder()
.annotationId(entry.getId())
.type(recategorizationRequest.isRectangle() ? entry.getType() : recategorizationRequest.getType())
.legalBasis(recategorizationRequest.getLegalBasis())
.section(recategorizationRequest.getSection())
.value(recategorizationRequest.getValue())
.value(entry.getValue())
.build())
.collect(Collectors.toSet());
@ -410,10 +415,11 @@ public class ManualRedactionController implements ManualRedactionResource {
recategorizationRequestModels = filteredEntityLogResponses.stream()
.map(entityLogEntry -> RecategorizationRequestModel.builder()
.annotationId(entityLogEntry.getId())
.type(recategorizationRequest.getType())
.type(recategorizationRequest.isRectangle() ? entityLogEntry.getType() : recategorizationRequest.getType())
.legalBasis(recategorizationRequest.getLegalBasis())
.section(recategorizationRequest.getSection())
.value(recategorizationRequest.getValue())
.value(entityLogEntry.getValue())
.comment(recategorizationRequest.getComment())
.build())
.collect(Collectors.toSet());
}
@ -430,7 +436,7 @@ public class ManualRedactionController implements ManualRedactionResource {
verifyAccessAndDossierExistence(dossierId, fileId);
List<ManualAddResponse> responseList = manualRedactionService.addResizeRedaction(dossierId, fileId, resizeRedactionRequests, includeUnprocessed);
List<ManualAnnotationResponse> responseList = manualRedactionService.addResizeRedaction(dossierId, fileId, resizeRedactionRequests, includeUnprocessed);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -440,25 +446,27 @@ public class ManualRedactionController implements ManualRedactionResource {
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return ManualRedactionResponse.builder().manualAddResponses(responseList).build();
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
private Set<String> getFilteredEntityLogEntryIds(boolean rectangle,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers,
Position position) {
private Set<EntityLogEntry> getFilteredEntityLogEntries(String dossierId,
String fileId,
boolean rectangle,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers,
Position position) {
Set<String> entryIds;
Set<EntityLogEntry> entries;
if (!rectangle) {
entryIds = entityLogMongoService.findEntryIdsByValueWithFilters(value, caseSensitive, originTypes, originLegalBases, pageNumbers);
entries = entityLogMongoService.findEntriesByValueWithFilters(dossierId, fileId, value, caseSensitive, originTypes, originLegalBases, pageNumbers);
} else {
entryIds = entityLogMongoService.findEntryIdsByMatchingFullPositionWithFilters(position.getRectangle(), originTypes, originLegalBases, pageNumbers);
entries = entityLogMongoService.findEntriesByMatchingFullPositionWithFilters(dossierId, fileId, position.getRectangle(), originTypes, originLegalBases, pageNumbers);
}
return entryIds;
return entries;
}
@ -496,6 +504,7 @@ public class ManualRedactionController implements ManualRedactionResource {
Set<Integer> pageNumbers) {
return entries.stream()
.filter(entry -> !entry.getEntryType().equals(EntryType.AREA))
.filter(entry -> filterByValue(entry, value, caseSensitive))
.filter(entry -> filterByOriginType(entry, originTypes))
.filter(entry -> filterByLegalBases(entry, originLegalBases))
@ -511,6 +520,7 @@ public class ManualRedactionController implements ManualRedactionResource {
Set<Integer> pageNumbers) {
return entries.stream()
.filter(entry -> entry.getEntryType().equals(EntryType.AREA))
.filter(entry -> filterByRectangle(entry, rectangle))
.filter(entry -> filterByOriginType(entry, originTypes))
.filter(entry -> filterByLegalBases(entry, originLegalBases))
@ -563,6 +573,19 @@ public class ManualRedactionController implements ManualRedactionResource {
}
private void verifyRequest(boolean isRectangle, Position position, String value) {
if (isRectangle && position == null) {
throw new BadRequestException("Position must be set for rectangle annotations.");
}
if (!isRectangle && value == null) {
throw new BadRequestException("Value must be set.");
}
}
private void verifyAccessForDossier(String dossierId, String fileId, boolean allDossiersAffected) {
accessControlService.checkAccessPermissionsToDossier(dossierId);

View File

@ -12,6 +12,7 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.Dossi
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
@ -142,8 +143,13 @@ public class AccessControlService {
public void verifyFileIsNotApproved(String dossierId, String fileId) {
try {
var status = fileStatusManagementService.getFileStatus(fileId, false).getWorkflowStatus();
FileModel fileStatus = fileStatusManagementService.getFileStatus(fileId, false);
if(!fileStatus.getDossierId().equals(dossierId)) {
throw new NotFoundException("File " + fileId + " dossier " + dossierId + " not found.");
}
var status = fileStatus.getWorkflowStatus();
if (WorkflowStatus.APPROVED.equals(status)) {
throw new NotAllowedException("File is already in status APPROVED.");
}

View File

@ -1020,7 +1020,7 @@ public class FileStatusService {
@Transactional
public void setStatusBulkLocalRedactionsProcessing(String dossierId, String fileId, AddRedactionBulkLocalRequestModel addRedactionBulkLocalRequestModel) {
public void setStatusBulkLocalRedactionsProcessing(String fileId, AddRedactionBulkLocalRequestModel addRedactionBulkLocalRequestModel) {
FileEntity fileStatus = fileStatusPersistenceService.getStatus(fileId);
@ -1038,6 +1038,7 @@ public class FileStatusService {
.legalBasis(addRedactionBulkLocalRequestModel.getLegalBasis())
.section(addRedactionBulkLocalRequestModel.getSection())
.pageNumbers(addRedactionBulkLocalRequestModel.getPageNumbers())
.comment(addRedactionBulkLocalRequestModel.getComment())
.build();
addSearchTermOccurrencesAnalysisRequestToAnalysisQueue(fileStatus, bulkLocalRequest);
}

View File

@ -3,13 +3,13 @@ package com.iqser.red.service.persistence.management.v1.processor.service.manual
import static com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionMapper.getDictionaryEntryType;
import static com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter.convert;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@ -57,7 +57,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.AddRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ForceRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.LegalBasisChangeRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAddResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAnnotationResponse;
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.RecategorizationRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
@ -71,7 +71,6 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.LegalBasisChangeRequestModel;
@ -117,17 +116,18 @@ public class ManualRedactionService {
@Transactional
@Observed(name = "ManualRedactionService", contextualName = "add-manual-redaction")
public List<ManualAddResponse> addAddRedaction(String dossierId, String fileId, Set<AddRedactionRequestModel> addRedactionRequests, Dossier dossier) {
public List<ManualAnnotationResponse> addAddRedaction(String dossierId, String fileId, Set<AddRedactionRequestModel> addRedactionRequests, Dossier dossier) {
if (addRedactionRequests.isEmpty()) {
throw new BadRequestException("add redaction requests is empty for request: dossierId:" + dossierId + " fileId:" + fileId);
}
var response = new ArrayList<ManualAddResponse>();
var response = new ArrayList<ManualAnnotationResponse>();
List<ManualRedactionEntryEntity> manualRedactionEntryEntities = new ArrayList<>();
List<AddRedactionRequest> requests = manualRedactionMapper.toAddRedactionRequestList(dossierId, addRedactionRequests, dossier);
var dossierEntity = dossierPersistenceService.getAndValidateDossier(dossierId);
FileModel fileStatus = fileStatusService.getStatus(fileId);
for (AddRedactionRequest addRedactionRequest : requests) {
@ -136,7 +136,7 @@ public class ManualRedactionService {
manualRedactionDictionaryUpdateHandler.validateDictionariesForAdd(addRedactionRequest, addRedactionRequest.getValue());
validatePositions(fileId, addRedactionRequest);
String annotationId = hashString(fileId + addRedactionRequest);
String annotationId = getNewAnnotationId();
ManualRedactionEntryEntity manualRedactionEntryEntity = addRedactionPersistenceService.insert(fileId, annotationId, addRedactionRequest);
manualRedactionEntryEntities.add(manualRedactionEntryEntity);
@ -160,14 +160,13 @@ public class ManualRedactionService {
new ManualRedactionEntryMapper()));
}
Long commentId = commentService.addCommentAndGetId(fileId, annotationId, addRedactionRequest.getComment(), addRedactionRequest.getUser());
response.add(ManualAddResponse.builder().annotationId(annotationId).commentId(commentId).entityLogEntry(entityLogEntry).build());
response.add(ManualAnnotationResponse.builder().annotationId(annotationId).commentId(commentId).entityLogEntry(entityLogEntry).build());
}
var localManualRedactionEntryEntities = manualRedactionEntryEntities.stream()
.filter(manualRedactionEntry -> !manualRedactionEntry.isAddToDictionary() && !manualRedactionEntry.isAddToAllDossiers())
.collect(Collectors.toList());
FileModel fileStatus = fileStatusService.getStatus(fileId);
if (!localManualRedactionEntryEntities.isEmpty() && fileStatus.isExcludedFromAutomaticAnalysis()) {
ManualRedactions manualRedactions = ManualRedactions.builder().entriesToAdd(convertEntriesToAdd(localManualRedactionEntryEntities)).build();
sendToSurroundingTextAnalysisQueue(fileId, dossierEntity, fileStatusService.getStatus(fileId), manualRedactions);
@ -197,11 +196,11 @@ public class ManualRedactionService {
@Transactional
public List<ManualAddResponse> addRemoveRedaction(String dossierId,
String fileId,
Set<RemoveRedactionRequestModel> removeRedactionRequests,
String dossierTemplateId,
boolean includeUnprocessed) {
public List<ManualAnnotationResponse> addRemoveRedaction(String dossierId,
String fileId,
Set<RemoveRedactionRequestModel> removeRedactionRequests,
String dossierTemplateId,
boolean includeUnprocessed) {
if (removeRedactionRequests.isEmpty()) {
throw new BadRequestException("remove redaction requests is empty for request: dossierId:" + dossierId + " fileId:" + fileId);
@ -212,7 +211,7 @@ public class ManualRedactionService {
if (numberOfDictRemoves > 100) {
throw new BadRequestException("Maximum number of remove from dictionary requests is 100.");
}
var response = new ArrayList<ManualAddResponse>();
var response = new ArrayList<ManualAnnotationResponse>();
List<RequestEntryPair<RemoveRedactionRequest>> requests = manualRedactionMapper.toRemoveRedactionRequestList(dossierId,
fileId,
dossierTemplateId,
@ -257,7 +256,7 @@ public class ManualRedactionService {
} else {
entityLogEntry = pendingEntryFactory.buildRemoveFromDictionary(MagicConverter.convert(idRemoval, IdRemoval.class), entityLogEntry);
}
response.add(ManualAddResponse.builder().annotationId(removeRedactionRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
response.add(ManualAnnotationResponse.builder().annotationId(removeRedactionRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
}
reprocess(dossierId, fileId);
@ -287,12 +286,12 @@ public class ManualRedactionService {
@Transactional
public List<ManualAddResponse> addForceRedaction(String dossierId, String fileId, Set<ForceRedactionRequestModel> forceRedactionRequests) {
public List<ManualAnnotationResponse> addForceRedaction(String dossierId, String fileId, Set<ForceRedactionRequestModel> forceRedactionRequests) {
if (forceRedactionRequests.isEmpty()) {
throw new BadRequestException("force redaction requests is empty for request: dossierId:" + dossierId + " fileId:" + fileId);
}
var response = new ArrayList<ManualAddResponse>();
var response = new ArrayList<ManualAnnotationResponse>();
dossierPersistenceService.getAndValidateDossier(dossierId);
List<RequestEntryPair<ForceRedactionRequest>> requests = manualRedactionMapper.toForceRedactionRequestList(dossierId,
fileId,
@ -312,7 +311,7 @@ public class ManualRedactionService {
entityLogMergeService.mergeForceRedaction(MagicConverter.convert(manualForceRedaction, ManualForceRedaction.class),
entityLogEntry,
getAnalysisNumber(dossierId, fileId));
response.add(ManualAddResponse.builder().annotationId(forceRedactionRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
response.add(ManualAnnotationResponse.builder().annotationId(forceRedactionRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
}
reprocess(dossierId, fileId);
@ -324,13 +323,13 @@ public class ManualRedactionService {
@Transactional
public List<ManualAddResponse> addLegalBasisChange(String dossierId, String fileId, Set<LegalBasisChangeRequestModel> legalBasisChangeRequests) {
public List<ManualAnnotationResponse> addLegalBasisChange(String dossierId, String fileId, Set<LegalBasisChangeRequestModel> legalBasisChangeRequests) {
if (legalBasisChangeRequests.isEmpty()) {
throw new BadRequestException("legal absis change requests is empty for request: dossierId:" + dossierId + " fileId:" + fileId);
}
var response = new ArrayList<ManualAddResponse>();
var response = new ArrayList<ManualAnnotationResponse>();
dossierPersistenceService.getAndValidateDossier(dossierId);
List<RequestEntryPair<LegalBasisChangeRequest>> requests = manualRedactionMapper.toLegalBasisChangeRequestList(dossierId,
fileId,
@ -350,7 +349,7 @@ public class ManualRedactionService {
entityLogMergeService.mergeLegalBasisChange(MagicConverter.convert(manualLegalBasisChange, ManualLegalBasisChange.class),
entityLogEntry,
getAnalysisNumber(dossierId, fileId));
response.add(ManualAddResponse.builder().annotationId(legalBasisChangeRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
response.add(ManualAnnotationResponse.builder().annotationId(legalBasisChangeRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
}
reprocess(dossierId, fileId);
fileStatusPersistenceService.setLastManualChangeDate(fileId, OffsetDateTime.now());
@ -360,17 +359,17 @@ public class ManualRedactionService {
@Transactional
public List<ManualAddResponse> addRecategorization(String dossierId,
String fileId,
Dossier dossier,
Set<RecategorizationRequestModel> recategorizationRequests,
boolean includeUnprocessed) {
public List<ManualAnnotationResponse> addRecategorization(String dossierId,
String fileId,
Dossier dossier,
Set<RecategorizationRequestModel> recategorizationRequests,
boolean includeUnprocessed) {
if (recategorizationRequests.isEmpty()) {
throw new BadRequestException("recat redaction requests is empty for request: dossierId:" + dossierId + " fileId:" + fileId);
}
var response = new ArrayList<ManualAddResponse>();
var response = new ArrayList<ManualAnnotationResponse>();
dossierPersistenceService.getAndValidateDossier(dossierId);
var dossierEntity = dossierPersistenceService.getAndValidateDossier(dossierId);
List<RequestEntryPair<RecategorizationRequest>> requests = manualRedactionMapper.toRecategorizationRequestList(dossierId,
@ -418,7 +417,7 @@ public class ManualRedactionService {
ManualRecategorization.class,
new ManualRecategorizationMapper()), entityLogEntry);
}
response.add(ManualAddResponse.builder().annotationId(recategorizationRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
response.add(ManualAnnotationResponse.builder().annotationId(recategorizationRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
recategorizationPersistenceService.saveAndFlush(recategorizationEntity);
}
@ -433,13 +432,16 @@ public class ManualRedactionService {
@Transactional
@Observed(name = "ManualRedactionService", contextualName = "add-manual-redaction")
public List<ManualAddResponse> addResizeRedaction(String dossierId, String fileId, Set<ResizeRedactionRequestModel> resizeRedactionRequests, boolean includeUnprocessed) {
public List<ManualAnnotationResponse> addResizeRedaction(String dossierId,
String fileId,
Set<ResizeRedactionRequestModel> resizeRedactionRequests,
boolean includeUnprocessed) {
if (resizeRedactionRequests.isEmpty()) {
throw new BadRequestException("resize redaction requests is empty for request: dossierId:" + dossierId + " fileId:" + fileId);
}
List<ManualAddResponse> response = new ArrayList<>();
List<ManualAnnotationResponse> response = new ArrayList<>();
List<ManualResizeRedactionEntity> manualResizeRedactionEntities = new ArrayList<>();
List<RequestEntryPair<ResizeRedactionRequest>> requests = manualRedactionMapper.toResizeRedactionRequestList(dossierId,
fileId,
@ -479,7 +481,7 @@ public class ManualRedactionService {
ManualResizeRedaction.class,
new ManualResizeRedactionMapper()), entityLogEntry);
}
response.add(ManualAddResponse.builder().annotationId(resizeRedactionRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
response.add(ManualAnnotationResponse.builder().annotationId(resizeRedactionRequest.getAnnotationId()).commentId(commentId).entityLogEntry(entityLogEntry).build());
}
manualResizeRedactionEntities = manualResizeRedactionEntities.stream()
@ -701,15 +703,9 @@ public class ManualRedactionService {
}
public String getPendingBulkLocalAnnotationId(String fileId, AddRedactionBulkLocalRequestModel addRedactionBulkLocalRequestModel) {
public String getNewAnnotationId() {
return hashString(fileId + addRedactionBulkLocalRequestModel);
}
private String hashString(String input) {
return hashFunction.hashString(input, StandardCharsets.UTF_8).toString();
return UUID.randomUUID().toString();
}
}

View File

@ -4,6 +4,7 @@ import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.springframework.beans.BeanUtils;
@ -12,6 +13,7 @@ import org.springframework.transaction.annotation.Transactional;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.AnnotationEntityId;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.IdRemovalEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRedactionEntryEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.model.ManualChangesQueryOptions;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.RemoveRedactionRepository;
@ -47,29 +49,38 @@ public class RemoveRedactionPersistenceService {
.orElseThrow(() -> new NotFoundException("Unknown file/annotation combination: " + fileId + "/" + annotationId));
}
public List<IdRemovalEntity> findEntriesByFileIdAndOptions(String fileId, ManualChangesQueryOptions options) {
if (options.getAnnotationIds().isEmpty()) {
return removeRedactionRepository.findByFileIdAndOptions(fileId, options.isIncludeDeletions(), options.isIncludeOnlyUnprocessed(), options.isIncludeDictChanges());
}
return removeRedactionRepository.findByFileIdAndOptionsAndAnnotationIds(fileId, options.isIncludeDeletions(), options.isIncludeOnlyUnprocessed(), options.isIncludeDictChanges(), options.getAnnotationIds());
return removeRedactionRepository.findByFileIdAndOptionsAndAnnotationIds(fileId,
options.isIncludeDeletions(),
options.isIncludeOnlyUnprocessed(),
options.isIncludeDictChanges(),
options.getAnnotationIds());
}
public int softDeleteByFileIds(List<String> fileIds, OffsetDateTime softDeletionTime) {
return removeRedactionRepository.softDeleteByFileIds(fileIds, softDeletionTime);
}
public int deleteByFileIds(List<String> fileIds) {
return removeRedactionRepository.deleteByFileIds(fileIds);
}
public void undeleteByFileId(String fileId, OffsetDateTime deletionTime) {
removeRedactionRepository.undeleteByFileId(fileId, deletionTime);
}
@Transactional
public void hardDelete(String fileId, String annotationId) {
@ -110,4 +121,10 @@ public class RemoveRedactionPersistenceService {
removeRedactionRepository.markAsProcessed(new AnnotationEntityId(e.getAnnotationId(), e.getFileId()), OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
}
public Optional<IdRemovalEntity> findById(String annotationId, String fileId) {
return removeRedactionRepository.findById(new AnnotationEntityId(annotationId, fileId));
}
}

View File

@ -14,7 +14,6 @@ import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.model.websocket.AnalyseStatus;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionService;
@ -24,10 +23,11 @@ import com.iqser.red.service.persistence.management.v1.processor.service.websock
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.BulkLocalResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAddResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAnnotationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddCommentRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequestModel;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
@ -75,39 +75,41 @@ public class SearchTermOccurrencesResponseReceiver {
var dossier = dossierManagementService.getDossierById(response.getDossierId(), false, false);
Set<AddRedactionRequestModel> addRedactionRequests = response.getEntityPositions()
Set<AddRedactionRequestModel> addRedactionRequests = response.getFoundTerms()
.stream()
.map(entityPosition -> AddRedactionRequestModel.builder()
.map(term -> AddRedactionRequestModel.builder()
.type(response.getType())
.value(response.getSearchTerm())
.value(term.value())
.reason(response.getReason())
.legalBasis(response.getLegalBasis())
.positions(convertPositions(entityPosition.positions()))
.positions(convertPositions(term.positions()))
.section(response.getSection())
.comment(response.getComment() == null ? null : new AddCommentRequestModel(response.getComment()))
.build())
.collect(Collectors.toSet());
log.info("Received manual redaction requests for file {} in dossier {} after term search", response.getFileId(), dossier.getId());
List<ManualAddResponse> manualAddResponses = manualRedactionService.addAddRedaction(response.getDossierId(), response.getFileId(), addRedactionRequests, dossier);
manualAddResponses.forEach(manualAddResponse -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(response.getFileId())
.category(AuditCategory.DOCUMENT.name())
.message("Manual annotation was added.")
.details(Map.of("dossierId",
response.getDossierId(),
"fileId",
response.getFileId(),
"annotationId",
manualAddResponse.getAnnotationId()))
.build()));
List<ManualAnnotationResponse> manualAnnotationResponses = manualRedactionService.addAddRedaction(response.getDossierId(),
response.getFileId(),
addRedactionRequests,
dossier);
manualAnnotationResponses.forEach(manualAddResponse -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(response.getFileId())
.category(AuditCategory.DOCUMENT.name())
.message("Manual annotation was added.")
.details(Map.of("dossierId",
response.getDossierId(),
"fileId",
response.getFileId(),
"annotationId",
manualAddResponse.getAnnotationId()))
.build()));
fileStatusPersistenceService.updateProcessingStatus(response.getFileId(), ProcessingStatus.PROCESSED);
int analysisVersion = fileStatusPersistenceService.getStatus(response.getFileId()).getAnalysisVersion();
webSocketService.sendAnalysisEvent(response.getDossierId(),
response.getFileId(),
AnalyseStatus.BULK_LOCAL_REDACTIONS_PROCESSED, analysisVersion);
webSocketService.sendAnalysisEvent(response.getDossierId(), response.getFileId(), AnalyseStatus.BULK_LOCAL_REDACTIONS_PROCESSED, analysisVersion);
log.info("Finished adding all manual redactions, the file is processed again");
}

View File

@ -343,33 +343,36 @@ public class EntityLogMongoServiceTest extends AbstractPersistenceServerServiceT
entityLogMongoService.saveEntityLog(TEST_DOSSIER_ID, TEST_FILE1_ID, entityLog);
String akessonValue = "Akesson";
Set<String> akessonSet = entityLogMongoService.findEntryIdsOfManualsWithValue(akessonValue);
assertEquals(akessonSet.size(), 3);
float[] akessonRectangle = {322.63608f, 308.1f, 41.375977f, 12.642f};
Set<String> akessonSetByPositions = entityLogMongoService.findEntryIdsOfManualsWithPositionRectangle(akessonRectangle);
assertEquals(akessonSetByPositions.size(), 3);
Set<String> akessonSetFilterQuery = entityLogMongoService.findEntryIdsByMatchingFullPositionWithFilters(akessonRectangle,
Set<String> akessonSetFilterQuery = entityLogMongoService.findEntryIdsByMatchingFullPositionWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
akessonRectangle,
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet());
assertEquals(akessonSetFilterQuery.size(), 3);
Set<String> akessonSetFilterQueryByPositions = entityLogMongoService.findEntryIdsByValueWithFilters(akessonValue,
Set<String> akessonSetFilterQueryByPositions = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
akessonValue,
false,
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet());
assertEquals(akessonSetFilterQueryByPositions.size(), 3);
akessonSetFilterQuery = entityLogMongoService.findEntryIdsByMatchingFullPositionWithFilters(akessonRectangle,
akessonSetFilterQuery = entityLogMongoService.findEntryIdsByMatchingFullPositionWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
akessonRectangle,
Collections.emptySet(),
Collections.emptySet(),
Set.of(1, 3));
assertEquals(akessonSetFilterQuery.size(), 2);
akessonSetFilterQueryByPositions = entityLogMongoService.findEntryIdsByValueWithFilters(akessonValue,
akessonSetFilterQueryByPositions = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
akessonValue,
false,
Collections.emptySet(),
Collections.emptySet(),
@ -377,52 +380,65 @@ public class EntityLogMongoServiceTest extends AbstractPersistenceServerServiceT
assertEquals(akessonSetFilterQueryByPositions.size(), 2);
String baldridgeValue = "Baldridge";
Set<String> baldridgeSet = entityLogMongoService.findEntryIdsByValueWithFilters(baldridgeValue,
Set<String> baldridgeSet = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
baldridgeValue,
false,
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet());
assertEquals(baldridgeSet.size(), 4);
Set<String> baldridgeSetWithTypeFilter = entityLogMongoService.findEntryIdsByValueWithFilters(baldridgeValue,
Set<String> baldridgeSetWithTypeFilter = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
baldridgeValue,
false,
Set.of("CBI_author", "Test"),
Collections.emptySet(),
Collections.emptySet());
assertEquals(baldridgeSetWithTypeFilter.size(), 2);
Set<String> baldridgeSetWithLegalBasesFilter = entityLogMongoService.findEntryIdsByValueWithFilters(baldridgeValue,
Set<String> baldridgeSetWithLegalBasesFilter = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
baldridgeValue,
false,
Set.of("CBI_author", "Test"),
Set.of("Test"),
Collections.emptySet());
assertEquals(baldridgeSetWithLegalBasesFilter.size(), 1);
String albaValueCapitalA = "Alba";
Set<String> albaSet = entityLogMongoService.findEntryIdsByValueWithFilters(albaValueCapitalA,
Set<String> albaSet = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
albaValueCapitalA,
true,
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet());
assertEquals(albaSet.size(), 4);
albaSet = entityLogMongoService.findEntryIdsByValueWithFilters(albaValueCapitalA,
false,
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet());
assertEquals(albaSet.size(), 4);
String albaValue = "alba";
albaSet = entityLogMongoService.findEntryIdsByValueWithFilters(albaValue,
albaSet = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
albaValueCapitalA,
false,
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet());
assertEquals(albaSet.size(), 4);
albaSet = entityLogMongoService.findEntryIdsByValueWithFilters(albaValue,
String albaValue = "alba";
albaSet = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
albaValue,
false,
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet());
assertEquals(albaSet.size(), 4);
albaSet = entityLogMongoService.findEntryIdsByValueWithFilters(TEST_DOSSIER_ID,
TEST_FILE1_ID,
albaValue,
true,
Collections.emptySet(),
Collections.emptySet(),

View File

@ -811,7 +811,7 @@ public class FileTest extends AbstractPersistenceServerServiceTest {
assertThat(annotationCommentsForInitialAnnotation.getComments()).hasSize(0);
AnnotationComments annotationCommentsForManualRecat = manualRedactionClient.getComments(dossierId,
fileId,
recatResponse.getManualAddResponses()
recatResponse.getManualAnnotationResponses()
.get(0).getAnnotationId());
assertThat(annotationCommentsForManualRecat.getComments()).hasSize(1);
@ -830,7 +830,7 @@ public class FileTest extends AbstractPersistenceServerServiceTest {
assertThat(annotationCommentsForInitialAnnotation.getComments()).hasSize(0);
annotationCommentsForManualRecat = manualRedactionClient.getComments(dossierId,
fileId,
recatResponse.getManualAddResponses()
recatResponse.getManualAnnotationResponses()
.get(0).getAnnotationId());
assertThat(annotationCommentsForManualRecat.getComments()).hasSize(0);

View File

@ -1,70 +1,32 @@
package com.iqser.red.service.peristence.v1.server.integration.tests;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.pdftron.redaction.v1.api.model.RedactionResultMessage;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DownloadClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileClient;
import com.iqser.red.service.peristence.v1.server.integration.client.ReportTemplateClient;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.FileTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.TypeProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRedactionEntryEntity;
import com.iqser.red.service.persistence.management.v1.processor.model.DownloadJob;
import com.iqser.red.service.persistence.management.v1.processor.service.download.DownloadCompressionMessageReceiver;
import com.iqser.red.service.persistence.management.v1.processor.service.download.DownloadReportMessageReceiver;
import com.iqser.red.service.persistence.management.v1.processor.service.download.RedactionResultMessageReceiver;
import com.iqser.red.service.persistence.management.v1.processor.service.job.DownloadReadyJob;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.PendingEntryFactory;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.ManualRedactionRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.queue.SearchTermOccurrencesResponseReceiver;
import com.iqser.red.service.persistence.service.v1.api.shared.model.BulkLocalResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.EntityPosition;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PrepareDownloadWithOptionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FoundTerm;
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.Position;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplate;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatusValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionBulkLocalRequestModel;
import com.iqser.red.service.redaction.report.v1.api.model.ReportResultMessage;
import com.iqser.red.service.redaction.report.v1.api.model.StoredFileInformation;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
@FieldDefaults(level = AccessLevel.PRIVATE)
@ -101,12 +63,12 @@ public class SearchTermOccurrencesTest extends AbstractPersistenceServerServiceT
searchTermOccurrencesResponseReceiver.receive(BulkLocalResponse.builder()
.fileId(file.getId())
.dossierId(dossier.getId())
.searchTerm("searchTerm")
.type(type.getType())
.legalBasis("legalBasis")
.reason("reason")
.section("section")
.entityPositions(List.of(new EntityPosition(List.of(new Position(new float[]{1f, 2f, 3f, 4f}, 1)))))
.foundTerms(List.of(new FoundTerm(List.of(new Position(new float[]{1f, 2f, 3f, 4f}, 1)), "searchTerm")))
.comment(null)
.build());
List<ManualRedactionEntryEntity> newEntries = manualRedactionRepository.findByFileIdAndOptions(file.getId(), false, false, false);

View File

@ -2024,6 +2024,243 @@
"importedRedactionIntersections": [],
"numberOfComments": 0
},
{
"id": "e5a6654664d689949e3823e1681d6272123",
"type": "CBI_author",
"entryType": "AREA",
"state": "APPLIED",
"value": "Akesson",
"reason": "Author found, removed by manual override, forced by manual override",
"matchedRule": "CBI.0.0",
"legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false,
"containingNodeId": [
0,
0
],
"closestHeadline": "",
"section": "[0, 0]: Paragraph: $ D Jones $.Friedrich",
"color": null,
"positions": [
{
"rectangle": [
322.63608,
308.1,
41.375977,
12.642
],
"pageNumber": 1
}
],
"textBefore": "Akashi Akashiba Akerman ",
"textAfter": " Akhavan Akhavan M",
"startOffset": 2806,
"endOffset": 2813,
"imageHasTransparency": false,
"dictionaryEntry": true,
"dossierDictionaryEntry": false,
"excluded": false,
"changes": [
{
"analysisNumber": 1,
"type": "ADDED",
"dateTime": "2024-03-13T08:44:54.811245839Z"
},
{
"analysisNumber": 2,
"type": "REMOVED",
"dateTime": "2024-03-13T08:45:53.93986414Z"
},
{
"analysisNumber": 7,
"type": "ADDED",
"dateTime": "2024-03-13T08:59:31.444615299Z"
}
],
"manualChanges": [
{
"manualRedactionType": "REMOVE",
"processedDate": "2024-03-13T09:05:21.954045754Z",
"requestedDate": "2024-03-13T08:45:49.298559Z",
"userId": "abbc3e3a-c499-48da-9346-063e1863a7f5",
"propertyChanges": {}
},
{
"manualRedactionType": "FORCE",
"processedDate": "2024-03-13T09:05:21.954046415Z",
"requestedDate": "2024-03-13T08:59:29.8778Z",
"userId": "abbc3e3a-c499-48da-9346-063e1863a7f5",
"propertyChanges": {
"legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002"
}
}
],
"engines": [
"MANUAL",
"DICTIONARY"
],
"reference": [],
"importedRedactionIntersections": [],
"numberOfComments": 0
},
{
"id": "e5a6654664d689949e3823e1681d6273123",
"type": "CBI_author",
"entryType": "AREA",
"state": "APPLIED",
"value": "Akesson",
"reason": "Author found, removed by manual override, forced by manual override",
"matchedRule": "CBI.0.0",
"legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false,
"containingNodeId": [
0,
0
],
"closestHeadline": "",
"section": "[0, 0]: Paragraph: $ D Jones $.Friedrich",
"color": null,
"positions": [
{
"rectangle": [
322.63608,
308.1,
41.375977,
12.642
],
"pageNumber": 2
}
],
"textBefore": "Akashi Akashiba Akerman ",
"textAfter": " Akhavan Akhavan M",
"startOffset": 2806,
"endOffset": 2813,
"imageHasTransparency": false,
"dictionaryEntry": true,
"dossierDictionaryEntry": false,
"excluded": false,
"changes": [
{
"analysisNumber": 1,
"type": "ADDED",
"dateTime": "2024-03-13T08:44:54.811245839Z"
},
{
"analysisNumber": 2,
"type": "REMOVED",
"dateTime": "2024-03-13T08:45:53.93986414Z"
},
{
"analysisNumber": 7,
"type": "ADDED",
"dateTime": "2024-03-13T08:59:31.444615299Z"
}
],
"manualChanges": [
{
"manualRedactionType": "REMOVE",
"processedDate": "2024-03-13T09:05:21.954045754Z",
"requestedDate": "2024-03-13T08:45:49.298559Z",
"userId": "abbc3e3a-c499-48da-9346-063e1863a7f5",
"propertyChanges": {}
},
{
"manualRedactionType": "FORCE",
"processedDate": "2024-03-13T09:05:21.954046415Z",
"requestedDate": "2024-03-13T08:59:29.8778Z",
"userId": "abbc3e3a-c499-48da-9346-063e1863a7f5",
"propertyChanges": {
"legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002"
}
}
],
"engines": [
"MANUAL",
"DICTIONARY"
],
"reference": [],
"importedRedactionIntersections": [],
"numberOfComments": 0
},
{
"id": "e5a6654664d689949e3823e1681d6274123",
"type": "CBI_author",
"entryType": "AREA",
"state": "APPLIED",
"value": "Akesson",
"reason": "Author found, removed by manual override, forced by manual override",
"matchedRule": "CBI.0.0",
"legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false,
"containingNodeId": [
0,
0
],
"closestHeadline": "",
"section": "[0, 0]: Paragraph: $ D Jones $.Friedrich",
"color": null,
"positions": [
{
"rectangle": [
322.63608,
308.1,
41.375977,
12.642
],
"pageNumber": 3
}
],
"textBefore": "Akashi Akashiba Akerman ",
"textAfter": " Akhavan Akhavan M",
"startOffset": 2806,
"endOffset": 2813,
"imageHasTransparency": false,
"dictionaryEntry": true,
"dossierDictionaryEntry": false,
"excluded": false,
"changes": [
{
"analysisNumber": 1,
"type": "ADDED",
"dateTime": "2024-03-13T08:44:54.811245839Z"
},
{
"analysisNumber": 2,
"type": "REMOVED",
"dateTime": "2024-03-13T08:45:53.93986414Z"
},
{
"analysisNumber": 7,
"type": "ADDED",
"dateTime": "2024-03-13T08:59:31.444615299Z"
}
],
"manualChanges": [
{
"manualRedactionType": "REMOVE",
"processedDate": "2024-03-13T09:05:21.954045754Z",
"requestedDate": "2024-03-13T08:45:49.298559Z",
"userId": "abbc3e3a-c499-48da-9346-063e1863a7f5",
"propertyChanges": {}
},
{
"manualRedactionType": "FORCE",
"processedDate": "2024-03-13T09:05:21.954046415Z",
"requestedDate": "2024-03-13T08:59:29.8778Z",
"userId": "abbc3e3a-c499-48da-9346-063e1863a7f5",
"propertyChanges": {
"legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002"
}
}
],
"engines": [
"MANUAL",
"DICTIONARY"
],
"reference": [],
"importedRedactionIntersections": [],
"numberOfComments": 0
},
{
"id": "ad3096cde99eac5a9c41ec85058efcfe",
"type": "CBI_author",

View File

@ -34,4 +34,6 @@ public class BulkLocalRequest {
@Builder.Default
private Set<Integer> pageNumbers = Collections.emptySet();
private String comment;
}

View File

@ -23,9 +23,6 @@ public class BulkLocalResponse {
@NonNull
private String fileId;
@NonNull
private String searchTerm;
@NonNull
private String type;
@ -38,7 +35,8 @@ public class BulkLocalResponse {
@NonNull
@Builder.Default
private List<EntityPosition> entityPositions = new ArrayList<>();
private List<FoundTerm> foundTerms = new ArrayList<>();
private String comment;
}

View File

@ -4,6 +4,6 @@ import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
public record EntityPosition(List<Position> positions) {
public record FoundTerm(List<Position> positions, String value) {
}

View File

@ -11,7 +11,7 @@ import lombok.NoArgsConstructor;
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ManualAddResponse {
public class ManualAnnotationResponse {
private String annotationId;
private Long commentId;

View File

@ -13,5 +13,5 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class ManualRedactionResponse {
private List<ManualAddResponse> manualAddResponses;
private List<ManualAnnotationResponse> manualAnnotationResponses;
}

View File

@ -42,4 +42,6 @@ public class AddRedactionBulkLocalRequestModel {
@Builder.Default
private Set<Integer> pageNumbers = Collections.emptySet();
private String comment;
}

View File

@ -39,4 +39,6 @@ public class RecategorizationBulkLocalRequestModel {
@Builder.Default
private Set<Integer> pageNumbers = Collections.emptySet();
private String comment;
}

View File

@ -36,4 +36,6 @@ public class RemoveRedactionBulkLocalRequestModel {
@Builder.Default
private Set<Integer> pageNumbers = Collections.emptySet();
private String comment;
}

View File

@ -3,11 +3,18 @@ package com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository
import java.util.List;
import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.EntityLogEntryDocument;
public interface EntityLogEntryDocumentCustomRepository {
List<String> findEntryIdsByValueWithFilters(String value, boolean caseSensitive, Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers);
List<EntityLogEntryDocument> findEntryIdsByValueWithFilters(String entityLogId,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers);
List<String> findEntryIdsByMatchingPositionWithFilters(float[] rectangle, Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers);
List<EntityLogEntryDocument> findEntryIdsByMatchingPositionWithFilters(String entityLogId, float[] rectangle, Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers);
}

View File

@ -4,13 +4,13 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.EntityLogEntryDocument;
import jakarta.validation.constraints.NotNull;
@ -27,10 +27,16 @@ public class EntityLogEntryDocumentCustomRepositoryImpl implements EntityLogEntr
@Override
public List<String> findEntryIdsByValueWithFilters(String value, boolean caseSensitive, Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers) {
public List<EntityLogEntryDocument> findEntryIdsByValueWithFilters(String entityLogId,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers) {
Query query = new Query();
List<Criteria> criteriaList = new ArrayList<>();
criteriaList.add(Criteria.where("entryType").ne(EntryType.AREA));
if (caseSensitive) {
criteriaList.add(Criteria.where("value").is(value));
@ -38,34 +44,41 @@ public class EntityLogEntryDocumentCustomRepositoryImpl implements EntityLogEntr
criteriaList.add(Criteria.where("value").regex(Pattern.compile("^" + Pattern.quote(value) + "$", Pattern.CASE_INSENSITIVE)));
}
addCommonCriteria(originTypes, originLegalBases, pageNumbers, criteriaList);
addCommonCriteria(entityLogId, originTypes, originLegalBases, pageNumbers, criteriaList);
Criteria mergedCriteria = new Criteria().andOperator(criteriaList.toArray(new Criteria[0]));
query.addCriteria(mergedCriteria);
return executeQuery(query);
return mongoTemplate.find(query, EntityLogEntryDocument.class);
}
@Override
public List<String> findEntryIdsByMatchingPositionWithFilters(float[] rectangle, Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers) {
public List<EntityLogEntryDocument> findEntryIdsByMatchingPositionWithFilters(String entityLogId,
float[] rectangle,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers) {
Query query = new Query();
List<Criteria> criteriaList = new ArrayList<>();
criteriaList.add(Criteria.where("entryType").is(EntryType.AREA));
criteriaList.add(Criteria.where("positions").elemMatch(Criteria.where("rectangle").is(rectangle)));
addCommonCriteria(originTypes, originLegalBases, pageNumbers, criteriaList);
addCommonCriteria(entityLogId, originTypes, originLegalBases, pageNumbers, criteriaList);
Criteria mergedCriteria = new Criteria().andOperator(criteriaList.toArray(new Criteria[0]));
query.addCriteria(mergedCriteria);
return executeQuery(query);
return mongoTemplate.find(query, EntityLogEntryDocument.class);
}
@NotNull
private void addCommonCriteria(Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers, List<Criteria> criteriaList) {
private void addCommonCriteria(String entityLogId, Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers, List<Criteria> criteriaList) {
criteriaList.add(Criteria.where("entityLogId").is(entityLogId));
if (originTypes != null && !originTypes.isEmpty()) {
criteriaList.add(Criteria.where("type").in(originTypes));
@ -82,14 +95,5 @@ public class EntityLogEntryDocumentCustomRepositoryImpl implements EntityLogEntr
}
private List<String> executeQuery(Query query) {
query.fields().include("entryId").exclude("id");
return mongoTemplate.find(query, EntityLogEntryDocument.class)
.stream()
.map(EntityLogEntryDocument::getEntryId)
.collect(Collectors.toList());
}
}

View File

@ -338,27 +338,68 @@ public class EntityLogMongoService {
}
public Set<String> findEntryIdsOfManualsWithValue(String value) {
public Set<EntityLogEntry> findEntriesByValueWithFilters(String dossierId,
String fileId,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers) {
return new HashSet<>(entityLogEntryDocumentRepository.findEntryIdsByValueAndEngineManual(value));
return new HashSet<>(entityLogEntryDocumentCustomRepository.findEntryIdsByValueWithFilters(mapper.getLogId(dossierId, fileId),
value,
caseSensitive,
originTypes,
originLegalBases,
pageNumbers)
.stream()
.map(mapper::fromLogEntryDocument)
.toList());
}
public Set<String> findEntryIdsOfManualsWithPositionRectangle(float[] rectangle) {
public Set<EntityLogEntry> findEntriesByMatchingFullPositionWithFilters(String dossierId,
String fileId,
float[] rectangle,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers) {
return new HashSet<>(entityLogEntryDocumentRepository.findEntryIdsByMatchingPositionAndEngineManual(rectangle));
return new HashSet<>(entityLogEntryDocumentCustomRepository.findEntryIdsByMatchingPositionWithFilters(mapper.getLogId(dossierId, fileId),
rectangle,
originTypes,
originLegalBases,
pageNumbers)
.stream()
.map(mapper::fromLogEntryDocument)
.toList());
}
public Set<String> findEntryIdsByValueWithFilters(String value, boolean caseSensitive, Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers) {
public Set<String> findEntryIdsByMatchingFullPositionWithFilters(String dossierId,
String fileId,
float[] rectangle,
Set<String> originTypes,
Set<String> originalLegalBases,
Set<Integer> pageNumbers) {
return new HashSet<>(entityLogEntryDocumentCustomRepository.findEntryIdsByValueWithFilters(value, caseSensitive, originTypes, originLegalBases, pageNumbers));
return findEntriesByMatchingFullPositionWithFilters(dossierId, fileId, rectangle, originTypes, originalLegalBases, pageNumbers).stream()
.map(EntityLogEntry::getId)
.collect(Collectors.toSet());
}
public Set<String> findEntryIdsByMatchingFullPositionWithFilters(float[] rectangle, Set<String> originTypes, Set<String> originLegalBases, Set<Integer> pageNumbers) {
public Set<String> findEntryIdsByValueWithFilters(String dossierId,
String fileId,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers) {
return new HashSet<>(entityLogEntryDocumentCustomRepository.findEntryIdsByMatchingPositionWithFilters(rectangle, originTypes, originLegalBases, pageNumbers));
return findEntriesByValueWithFilters(dossierId, fileId, value, caseSensitive, originTypes, originLegalBases, pageNumbers).stream()
.map(EntityLogEntry::getId)
.collect(Collectors.toSet());
}
}