Merge branch 'RED-8586' into 'master'

RED-8586 - Fix confidentiality rules

Closes RED-8586

See merge request redactmanager/redaction-service!294
This commit is contained in:
Andrei Isvoran 2024-02-29 12:58:23 +01:00
commit 89dea4138a
7 changed files with 80 additions and 26 deletions

View File

@ -26,10 +26,9 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@FieldDefaults(makeFinal=true, level= AccessLevel.PRIVATE) @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class EntityChangeLogService { public class EntityChangeLogService {
@Timed("redactmanager_computeChanges") @Timed("redactmanager_computeChanges")
public boolean computeChanges(List<EntityLogEntry> previousEntityLogEntries, List<EntityLogEntry> newEntityLogEntries, ManualRedactions manualRedactions, int analysisNumber) { public boolean computeChanges(List<EntityLogEntry> previousEntityLogEntries, List<EntityLogEntry> newEntityLogEntries, ManualRedactions manualRedactions, int analysisNumber) {
@ -42,7 +41,9 @@ public class EntityChangeLogService {
boolean hasChanges = false; boolean hasChanges = false;
for (EntityLogEntry entityLogEntry : newEntityLogEntries) { for (EntityLogEntry entityLogEntry : newEntityLogEntries) {
Optional<EntityLogEntry> optionalPreviousEntity = previousEntityLogEntries.stream().filter(entry -> entry.getId().equals(entityLogEntry.getId())).findAny(); Optional<EntityLogEntry> optionalPreviousEntity = previousEntityLogEntries.stream()
.filter(entry -> entry.getId().equals(entityLogEntry.getId()))
.findAny();
if (optionalPreviousEntity.isEmpty()) { if (optionalPreviousEntity.isEmpty()) {
hasChanges = true; hasChanges = true;
entityLogEntry.getChanges().add(new Change(analysisNumber, ChangeType.ADDED, now)); entityLogEntry.getChanges().add(new Change(analysisNumber, ChangeType.ADDED, now));
@ -63,15 +64,22 @@ public class EntityChangeLogService {
private void addRemovedEntriesAsRemoved(List<EntityLogEntry> previousEntityLogEntries, private void addRemovedEntriesAsRemoved(List<EntityLogEntry> previousEntityLogEntries,
List<EntityLogEntry> newEntityLogEntries, List<EntityLogEntry> newEntityLogEntries,
ManualRedactions manualRedactions, ManualRedactions manualRedactions,
int analysisNumber, int analysisNumber,
OffsetDateTime now) { OffsetDateTime now) {
Set<String> existingIds = newEntityLogEntries.stream().map(EntityLogEntry::getId).collect(Collectors.toSet()); Set<String> existingIds = newEntityLogEntries.stream()
.map(EntityLogEntry::getId)
.collect(Collectors.toSet());
List<EntityLogEntry> removedEntries = previousEntityLogEntries.stream() List<EntityLogEntry> removedEntries = previousEntityLogEntries.stream()
.filter(entry -> !existingIds.contains(entry.getId())) .filter(entry -> !existingIds.contains(entry.getId()))
.collect(Collectors.toList());
List<EntityLogEntry> removedDossierRedaction = removedEntries.stream()
.filter(e -> e.getState() == EntryState.REMOVED && e.getType().equals("dossier_redaction"))
.toList(); .toList();
previousEntityLogEntries.removeAll(removedDossierRedaction);
removedEntries.removeAll(removedDossierRedaction);
removedEntries.forEach(entry -> entry.getChanges().add(new Change(analysisNumber, ChangeType.REMOVED, now))); removedEntries.forEach(entry -> entry.getChanges().add(new Change(analysisNumber, ChangeType.REMOVED, now)));
removedEntries.forEach(entry -> entry.setState(EntryState.REMOVED)); removedEntries.forEach(entry -> entry.setState(EntryState.REMOVED));
removedEntries.forEach(entry -> addManualChangeForDictionaryRemovals(entry, manualRedactions)); removedEntries.forEach(entry -> addManualChangeForDictionaryRemovals(entry, manualRedactions));
@ -85,13 +93,15 @@ public class EntityChangeLogService {
return; return;
} }
manualRedactions.getIdsToRemove().stream() manualRedactions.getIdsToRemove()
.stream()
.filter(IdRemoval::isRemoveFromDictionary)// .filter(IdRemoval::isRemoveFromDictionary)//
.filter(removed -> removed.getAnnotationId().equals(entry.getId()))// .filter(removed -> removed.getAnnotationId().equals(entry.getId()))//
.findFirst()// .findFirst()//
.ifPresent(idRemove -> entry.getManualChanges().add(ManualChangeFactory.toManualChange(idRemove, false))); .ifPresent(idRemove -> entry.getManualChanges().add(ManualChangeFactory.toManualChange(idRemove, false)));
} }
private ChangeType calculateChangeType(EntryState state, EntryState previousState) { private ChangeType calculateChangeType(EntryState state, EntryState previousState) {
if (state.equals(previousState)) { if (state.equals(previousState)) {

View File

@ -125,7 +125,7 @@ public class EntityLogCreatorService {
.stream() .stream()
.filter(entry -> (newEntityIds.contains(entry.getId()) || entry.getContainingNodeId().isEmpty() || sectionsToReanalyseIds.contains(entry.getContainingNodeId() .filter(entry -> (newEntityIds.contains(entry.getId()) || entry.getContainingNodeId().isEmpty() || sectionsToReanalyseIds.contains(entry.getContainingNodeId()
.get(0)))) .get(0))))
.toList(); .collect(Collectors.toList());
previousEntityLog.getEntityLogEntry().removeAll(previousEntriesFromReAnalyzedSections); previousEntityLog.getEntityLogEntry().removeAll(previousEntriesFromReAnalyzedSections);
boolean hasChanges = entityChangeLogService.computeChanges(previousEntriesFromReAnalyzedSections, boolean hasChanges = entityChangeLogService.computeChanges(previousEntriesFromReAnalyzedSections,

View File

@ -692,13 +692,22 @@ rule "ETC.3.1: Redact logos (vertebrate study)"
// Rule unit: ETC.5 // Rule unit: ETC.5
rule "ETC.5.0: Ignore dossier_redaction entries if confidentiality is not 'confidential'" rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidential'"
when
FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction")
then
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
when when
not FileAttribute(label == "Confidentiality", value == "confidential") not FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.ignore("ETC.5.0", "Ignore dossier redactions, when not confidential"); $dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node)); retract($dossierRedaction);
end end

View File

@ -1150,13 +1150,22 @@ rule "ETC.4.2: Redact dossier dictionary entries"
// Rule unit: ETC.5 // Rule unit: ETC.5
rule "ETC.5.0: Ignore dossier_redaction entries if confidentiality is not 'confidential'" rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidential'"
when
FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction")
then
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
when when
not FileAttribute(label == "Confidentiality", value == "confidential") not FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.ignore("ETC.5.0", "Ignore dossier redactions, when not confidential"); $dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node)); retract($dossierRedaction);
end end

View File

@ -802,13 +802,22 @@ rule "ETC.4.0: Redact dossier dictionary entries"
// Rule unit: ETC.5 // Rule unit: ETC.5
rule "ETC.5.0: Ignore dossier_redaction entries if confidentiality is not 'confidential'" rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidential'"
when
FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction")
then
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
when when
not FileAttribute(label == "Confidentiality", value == "confidential") not FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.ignore("ETC.5.0", "Ignore dossier redactions, when not confidential"); $dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node)); retract($dossierRedaction);
end end

View File

@ -449,14 +449,22 @@ rule "ETC.3.1: Redact logos (non vertebrate study)"
// Rule unit: ETC.5 // Rule unit: ETC.5
rule "ETC.5.0: Ignore dossier_redaction entries if confidentiality is not 'confidential'" rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidential'"
when
FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction")
then
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
when when
not FileAttribute(label == "Confidentiality", value == "confidential") not FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.ignore("ETC.5.0", "Ignore dossier redactions, when not confidential"); $dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
update($dossierRedaction); retract($dossierRedaction);
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end end

View File

@ -1159,13 +1159,22 @@ rule "ETC.4.2: Redact dossier dictionary entries"
// Rule unit: ETC.5 // Rule unit: ETC.5
rule "ETC.5.0: Ignore dossier_redaction entries if confidentiality is not 'confidential'" rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidential'"
when
FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction")
then
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
when when
not FileAttribute(label == "Confidentiality", value == "confidential") not FileAttribute(label == "Confidentiality", value == "confidential")
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.ignore("ETC.5.0", "Ignore dossier redactions, when not confidential"); $dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node)); retract($dossierRedaction);
end end