215 lines
10 KiB
Plaintext
215 lines
10 KiB
Plaintext
package drools
|
|
|
|
import static java.lang.String.format;
|
|
import static com.iqser.red.service.redaction.v1.server.document.utils.RedactionSearchUtility.anyMatch;
|
|
import static com.iqser.red.service.redaction.v1.server.document.utils.RedactionSearchUtility.exactMatch;
|
|
|
|
import java.util.List;
|
|
import java.util.LinkedList;
|
|
import java.util.Set;
|
|
import java.util.stream.Collectors;
|
|
import java.util.Collection;
|
|
import java.util.stream.Stream;
|
|
import java.util.Optional;
|
|
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.*;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.*;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.Section;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.Table;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.SemanticNode;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.Document;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.Paragraph;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.Image;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.entity.*;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.textblock.*;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.entity.EntityType;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.ImageType;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Engine;
|
|
import com.iqser.red.service.redaction.v1.server.document.services.EntityCreationService;
|
|
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.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualImageRecategorization;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;
|
|
import com.iqser.red.service.redaction.v1.server.document.services.ManualChangesApplicationService;
|
|
import com.iqser.red.service.redaction.v1.server.client.model.EntityRecognitionEntity;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.TextRange;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.entity.TextEntity;
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.TextRange;
|
|
import com.iqser.red.service.redaction.v1.server.redaction.adapter.NerEntities;
|
|
import com.iqser.red.service.redaction.v1.server.document.utils.RedactionSearchUtility
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.nodes.ImageType
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.entity.MatchedRule
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.entity.EntityType
|
|
import com.iqser.red.service.redaction.v1.server.document.utils.RedactionSearchUtility
|
|
import com.iqser.red.service.redaction.v1.server.document.graph.entity.TextEntity;
|
|
|
|
global Document document
|
|
global EntityCreationService entityCreationService
|
|
global ManualChangesApplicationService manualChangesApplicationService
|
|
global Dictionary dictionary
|
|
// --------------------------------------- queries -------------------------------------------------------------------
|
|
|
|
query "getFileAttributes"
|
|
$fileAttribute: FileAttribute()
|
|
end
|
|
|
|
// --------------------------------------- NER Entities rules -------------------------------------------------------------------
|
|
|
|
rule "add NER Entities of type CBI_author or CBI_address"
|
|
salience 999
|
|
when
|
|
$nerEntity: EntityRecognitionEntity($type: type, (type == "CBI_author" || type == "CBI_address"))
|
|
then
|
|
entityCreationService.byBoundary(new TextRange($nerEntity.getStartOffset(), $nerEntity.getEndOffset()), $type, EntityType.RECOMMENDATION, document)
|
|
.ifPresent(redactionEntity -> insert(redactionEntity));
|
|
end
|
|
|
|
// --------------------------------------- CBI rules -------------------------------------------------------------------
|
|
|
|
rule "Always redact CBI_author"
|
|
|
|
when
|
|
$cbiAuthor: TextEntity(type == "CBI_author", entityType == EntityType.ENTITY)
|
|
then
|
|
$cbiAuthor.apply("CBI.0.0", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
// --------------------------------------- PII rules -------------------------------------------------------------------
|
|
|
|
rule "Always redact PII"
|
|
|
|
when
|
|
$cbiAuthor: TextEntity(type == "PII", entityType == EntityType.ENTITY)
|
|
then
|
|
$cbiAuthor.apply("PII.0.0", "PII found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
|
|
|
|
//------------------------------------ Entity merging rules ------------------------------------
|
|
|
|
// Rule unit: X.0
|
|
rule "X.0.0: remove Entity contained by Entity of same type"
|
|
salience 65
|
|
when
|
|
$larger: TextEntity($type: type, $entityType: entityType, active())
|
|
$contained: TextEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !resized(), active())
|
|
then
|
|
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
|
retract($contained);
|
|
end
|
|
|
|
|
|
// Rule unit: X.1
|
|
rule "X.1.0: merge intersecting Entities of same type"
|
|
salience 64
|
|
when
|
|
$first: TextEntity($type: type, $entityType: entityType, !resized(), active())
|
|
$second: TextEntity(intersects($first), type == $type, entityType == $entityType, this != $first, !resized(), active())
|
|
then
|
|
TextEntity mergedEntity = entityCreationService.mergeEntitiesOfSameType(List.of($first, $second), $type, $entityType, document);
|
|
$first.remove("X.1.0", "merge intersecting Entities of same type");
|
|
$second.remove("X.1.0", "merge intersecting Entities of same type");
|
|
retract($first);
|
|
retract($second);
|
|
mergedEntity.getIntersectingNodes().forEach(node -> update(node));
|
|
end
|
|
|
|
|
|
// Rule unit: X.2
|
|
rule "X.2.0: remove Entity of type ENTITY when contained by FALSE_POSITIVE"
|
|
salience 64
|
|
when
|
|
$falsePositive: TextEntity($type: type, entityType == EntityType.FALSE_POSITIVE, active())
|
|
$entity: TextEntity(containedBy($falsePositive), type == $type, entityType == EntityType.ENTITY, !resized(), active())
|
|
then
|
|
$entity.getIntersectingNodes().forEach(node -> update(node));
|
|
$entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
|
|
retract($entity)
|
|
end
|
|
|
|
|
|
// Rule unit: X.3
|
|
rule "X.3.0: remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
|
|
salience 64
|
|
when
|
|
$falseRecommendation: TextEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION, active())
|
|
$recommendation: TextEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !resized(), active())
|
|
then
|
|
$recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
|
|
retract($recommendation);
|
|
end
|
|
|
|
|
|
// Rule unit: X.4
|
|
rule "X.4.0: remove Entity of type RECOMMENDATION when intersected by ENTITY with same type"
|
|
salience 256
|
|
when
|
|
$entity: TextEntity($type: type, entityType == EntityType.ENTITY, active())
|
|
$recommendation: TextEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !resized(), active())
|
|
then
|
|
$entity.addEngines($recommendation.getEngines());
|
|
$recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY with same type");
|
|
retract($recommendation);
|
|
end
|
|
|
|
|
|
// Rule unit: X.5
|
|
rule "X.5.0: remove Entity of type RECOMMENDATION when contained by ENTITY"
|
|
salience 256
|
|
when
|
|
$entity: TextEntity(entityType == EntityType.ENTITY, active())
|
|
$recommendation: TextEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !resized(), active())
|
|
then
|
|
$recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when contained by ENTITY");
|
|
retract($recommendation);
|
|
end
|
|
|
|
|
|
// Rule unit: X.6
|
|
rule "X.6.0: remove Entity of lower rank, when intersected by entity of type ENTITY"
|
|
salience 32
|
|
when
|
|
$higherRank: TextEntity($type: type, entityType == EntityType.ENTITY, active())
|
|
$lowerRank: TextEntity(intersects($higherRank), type != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !resized(), active())
|
|
then
|
|
$lowerRank.getIntersectingNodes().forEach(node -> update(node));
|
|
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when intersected by entity of type ENTITY");
|
|
retract($lowerRank);
|
|
end
|
|
|
|
|
|
//------------------------------------ File attributes rules ------------------------------------
|
|
|
|
// Rule unit: FA.1
|
|
rule "FA.1.0: remove duplicate FileAttributes"
|
|
salience 64
|
|
when
|
|
$fileAttribute: FileAttribute($label: label, $value: value)
|
|
$duplicate: FileAttribute(this != $fileAttribute, label == $label, value == $value)
|
|
then
|
|
retract($duplicate);
|
|
end
|
|
|
|
|
|
//------------------------------------ Local dictionary search rules ------------------------------------
|
|
|
|
// Rule unit: LDS.0
|
|
rule "LDS.0.0: run local dictionary search"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
salience -999
|
|
when
|
|
$dictionaryModel: DictionaryModel(!localEntriesWithMatchedRules.isEmpty()) from dictionary.getDictionaryModels()
|
|
then
|
|
entityCreationService.bySearchImplementation($dictionaryModel.getLocalSearch(), $dictionaryModel.getType(), EntityType.RECOMMENDATION, document)
|
|
.forEach(entity -> {
|
|
Collection<MatchedRule> matchedRules = $dictionaryModel.getLocalEntriesWithMatchedRules().get(entity.getValue());
|
|
entity.addMatchedRules(matchedRules);
|
|
});
|
|
end
|
|
|