Compare commits

..

21 Commits

Author SHA1 Message Date
Kilian Schuettler
f8a97f306e RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-14 13:53:33 +01:00
Kilian Schuettler
6ae777c802 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-13 13:46:14 +01:00
Kilian Schuettler
3671d96c26 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-13 12:26:10 +01:00
Kilian Schuettler
6de802b5a4 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-13 11:06:45 +01:00
Kilian Schuettler
924a8607d2 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-13 11:04:47 +01:00
Kilian Schuettler
d8d9ced7b6 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-13 11:01:21 +01:00
Kilian Schuettler
dc84e12695 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-13 10:53:45 +01:00
Kilian Schuettler
3e2d3169af RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-12 16:51:56 +01:00
Kilian Schuettler
2b567af8ea RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-12 16:48:16 +01:00
Kilian Schuettler
f1ba80fff6 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* update LayoutEngine package
2024-11-12 16:19:17 +01:00
Kilian Schuettler
192910be9c RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* ignore checkstyle and pmd for generated proto files
2024-11-12 13:53:05 +01:00
Kilian Schuettler
6884a05db9 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* ignore checkstyle and pmd for generated proto files
2024-11-12 13:48:34 +01:00
Kilian Schuettler
95b07e5dd3 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* ignore checkstyle and pmd for generated proto files
2024-11-12 13:00:33 +01:00
Kilian Schuettler
da13a5a419 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* ignore checkstyle and pmd for generated proto files
2024-11-12 11:29:04 +01:00
Kilian Schuettler
c9e91972f8 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* ignore checkstyle and pmd for generated proto files
2024-11-12 11:16:43 +01:00
Kilian Schuettler
b73f9a12cc RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem
* ignore checkstyle and pmd for generated proto files
2024-11-12 11:16:23 +01:00
Kilian Schuettler
464f68e573 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem 2024-11-12 11:00:06 +01:00
Kilian Schuettler
d6c4619340 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem 2024-11-11 16:49:16 +01:00
Kilian Schuettler
b52d3928ef RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem 2024-11-11 16:23:43 +01:00
Kilian Schuettler
5432635037 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem 2024-11-11 16:15:02 +01:00
Kilian Schuettler
c5290fc908 RED-9139: move document to its own module, add TableOfContents and TableOfContentsItem 2024-11-11 15:57:26 +01:00
130 changed files with 3059 additions and 128270 deletions

View File

@ -2,7 +2,6 @@ package com.iqser.red.service.redaction.v1.server.mapper;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -61,7 +60,6 @@ public class DocumentDataMapper {
List<DocumentPage> documentPageData = document.getPages() List<DocumentPage> documentPageData = document.getPages()
.stream() .stream()
.sorted(Comparator.comparingInt(Page::getNumber))
.map(DocumentDataMapper::toPageData) .map(DocumentDataMapper::toPageData)
.toList(); .toList();

View File

@ -6,11 +6,11 @@ import static com.iqser.red.service.redaction.v1.server.data.DocumentTextDataPro
import static com.iqser.red.service.redaction.v1.server.data.EntryDataProto.EntryData; import static com.iqser.red.service.redaction.v1.server.data.EntryDataProto.EntryData;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException;
import com.iqser.red.service.redaction.v1.server.data.DocumentData; import com.iqser.red.service.redaction.v1.server.data.DocumentData;
import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree; import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
@ -45,10 +45,15 @@ public class DocumentGraphMapper {
DocumentTree documentTree = new DocumentTree(document); DocumentTree documentTree = new DocumentTree(document);
Context context = new Context(documentData, documentTree); Context context = new Context(documentData, documentTree);
context.pages.addAll(documentData.getDocumentPages().getDocumentPagesList()
.stream()
.map(DocumentGraphMapper::buildPage)
.toList());
context.documentTree.getRoot().getChildren().addAll(buildEntries(documentData.getDocumentStructure().getRoot().getChildrenList(), context)); context.documentTree.getRoot().getChildren().addAll(buildEntries(documentData.getDocumentStructure().getRoot().getChildrenList(), context));
document.setDocumentTree(context.documentTree); document.setDocumentTree(context.documentTree);
document.setPages(new HashSet<>(context.pageData)); document.setPages(new HashSet<>(context.pages));
document.setNumberOfPages(documentData.getDocumentPages().getDocumentPagesCount()); document.setNumberOfPages(documentData.getDocumentPages().getDocumentPagesCount());
document.setTextBlock(document.getTextBlock()); document.setTextBlock(document.getTextBlock());
@ -199,10 +204,13 @@ public class DocumentGraphMapper {
private AtomicTextBlock getAtomicTextBlock(Context context, SemanticNode parent, Long atomicTextBlockId) { private AtomicTextBlock getAtomicTextBlock(Context context, SemanticNode parent, Long atomicTextBlockId) {
return AtomicTextBlock.fromAtomicTextBlockData(context.documentTextData.getDocumentTextData(Math.toIntExact(atomicTextBlockId)), return AtomicTextBlock.fromAtomicTextBlockData(context.documentTextDataBlockData.getDocumentTextDataList()
context.documentPositionData.getDocumentPositionData(Math.toIntExact(atomicTextBlockId)), .get(Math.toIntExact(atomicTextBlockId)),
context.atomicPositionBlockData.getDocumentPositionDataList()
.get(Math.toIntExact(atomicTextBlockId)),
parent, parent,
context.getPage(context.documentTextData.getDocumentTextData(Math.toIntExact(atomicTextBlockId)).getPage())); context.getPage(context.documentTextDataBlockData.getDocumentTextDataList()
.get(Math.toIntExact(atomicTextBlockId)).getPage()));
} }
@ -215,30 +223,27 @@ public class DocumentGraphMapper {
static final class Context { static final class Context {
private final DocumentTree documentTree; private final DocumentTree documentTree;
private final List<Page> pageData; private final List<Page> pages;
private final AllDocumentTextData documentTextData; private final AllDocumentTextData documentTextDataBlockData;
private final AllDocumentPositionData documentPositionData; private final AllDocumentPositionData atomicPositionBlockData;
Context(DocumentData documentData, DocumentTree documentTree) { Context(DocumentData documentData, DocumentTree documentTree) {
this.documentTree = documentTree; this.documentTree = documentTree;
this.pageData = documentData.getDocumentPages().getDocumentPagesList() this.pages = new LinkedList<>();
.stream() this.documentTextDataBlockData = documentData.getDocumentTextData();
.map(DocumentGraphMapper::buildPage) this.atomicPositionBlockData = documentData.getDocumentPositionData();
.sorted(Comparator.comparingInt(Page::getNumber))
.toList();
this.documentTextData = documentData.getDocumentTextData();
this.documentPositionData = documentData.getDocumentPositionData();
} }
public Page getPage(Long pageIndex) { public Page getPage(Long pageIndex) {
Page page = pageData.get(Math.toIntExact(pageIndex) - 1); return pages.stream()
assert page.getNumber() == Math.toIntExact(pageIndex); .filter(page -> page.getNumber() == Math.toIntExact(pageIndex))
return page; .findFirst()
.orElseThrow(() -> new NoSuchElementException(String.format("ClassificationPage with number %d not found", pageIndex)));
} }
} }

View File

@ -68,7 +68,6 @@ public class PropertiesMapper {
builder.imageType(parseImageType(properties.get(DocumentStructureWrapper.ImageProperties.IMAGE_TYPE))); builder.imageType(parseImageType(properties.get(DocumentStructureWrapper.ImageProperties.IMAGE_TYPE)));
builder.transparent(Boolean.parseBoolean(properties.get(DocumentStructureWrapper.ImageProperties.TRANSPARENT))); builder.transparent(Boolean.parseBoolean(properties.get(DocumentStructureWrapper.ImageProperties.TRANSPARENT)));
builder.position(DocumentStructureWrapper.parseRectangle2D(properties.get(DocumentStructureWrapper.ImageProperties.POSITION))); builder.position(DocumentStructureWrapper.parseRectangle2D(properties.get(DocumentStructureWrapper.ImageProperties.POSITION)));
builder.id(properties.get(DocumentStructureWrapper.ImageProperties.ID));
} }

View File

@ -9,8 +9,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.GenericSemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.GenericSemanticNode;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType; import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType;
@ -19,8 +17,6 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell; import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock; import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector; import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;
import com.iqser.red.service.redaction.v1.server.utils.EntityCreationUtility;
import com.iqser.red.service.redaction.v1.server.utils.EntityEnrichmentService;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -39,7 +35,7 @@ public class DocumentTree {
public DocumentTree(Document document) { public DocumentTree(Document document) {
this.root = Entry.builder().treeId(Collections.emptyList()).children(new LinkedList<>()).node(document).build(); root = Entry.builder().treeId(Collections.emptyList()).children(new LinkedList<>()).node(document).build();
} }
@ -362,25 +358,6 @@ public class DocumentTree {
} }
public void addEntityToGraph(TextEntity entity) {
getRoot().getNode().addThisToEntityIfIntersects(entity);
TextBlock textBlock = entity.getDeepestFullyContainingNode().getTextBlock();
EntityEnrichmentService.enrichEntity(entity, textBlock);
EntityCreationUtility.addToPages(entity);
EntityCreationUtility.addEntityToNodeEntitySets(entity);
if (entity.getEntityType().equals(EntityType.TEMPORARY)) {
return;
}
entity.computeRelations();
entity.notifyEntityInserted();
}
@Builder @Builder
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor

View File

@ -1,32 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.document;
import java.util.HashSet;
import java.util.Set;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
import lombok.Getter;
public class IntersectingNodeVisitor extends AbstractNodeVisitor {
@Getter
private Set<SemanticNode> intersectingNodes;
private final TextRange textRange;
public IntersectingNodeVisitor(TextRange textRange) {
this.textRange = textRange;
this.intersectingNodes = new HashSet<>();
}
@Override
public void visitNodeDefault(SemanticNode node) {
if (textRange.intersects(node.getTextRange())) {
intersectingNodes.add(node);
}
}
}

View File

@ -1,20 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.document.entity;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public abstract class AbstractRelation implements Relation {
protected final TextEntity a;
protected final TextEntity b;
@Override
public String toString() {
return this.getClass().getSimpleName() + "{" + "a=" + a + ", b=" + b + '}';
}
}

View File

@ -1,18 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.document.entity;
public class Containment extends Intersection {
public Containment(TextEntity container, TextEntity contained) {
super(container, contained);
}
public TextEntity getContainer() {
return a;
}
public TextEntity getContained() {
return b;
}
}

View File

@ -1,25 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.document.entity;
public interface EntityEventListener {
/**
* Invoked when an entity is inserted.
*
* @param entity The entity that was inserted.
*/
void onEntityInserted(IEntity entity);
/**
* Invoked when an entity is updated.
*
* @param entity The entity that was updated.
*/
void onEntityUpdated(IEntity entity);
/**
* Invoked when an entity is removed.
*
* @param entity The entity that was removed.
*/
void onEntityRemoved(IEntity entity);
}

View File

@ -6,6 +6,5 @@ public enum EntityType {
RECOMMENDATION, RECOMMENDATION,
FALSE_POSITIVE, FALSE_POSITIVE,
FALSE_RECOMMENDATION, FALSE_RECOMMENDATION,
DICTIONARY_REMOVAL, DICTIONARY_REMOVAL
TEMPORARY
} }

View File

@ -1,10 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.document.entity;
public class Equality extends Containment {
public Equality(TextEntity a, TextEntity b) {
super(a, b);
}
}

View File

@ -339,9 +339,7 @@ public interface IEntity {
*/ */
default void addMatchedRule(MatchedRule matchedRule) { default void addMatchedRule(MatchedRule matchedRule) {
boolean wasValid = valid();
getMatchedRuleList().add(matchedRule); getMatchedRuleList().add(matchedRule);
handleStateChange(wasValid);
} }
@ -355,53 +353,7 @@ public interface IEntity {
if (getMatchedRuleList().equals(matchedRules)) { if (getMatchedRuleList().equals(matchedRules)) {
return; return;
} }
boolean wasValid = valid();
getMatchedRuleList().addAll(matchedRules); getMatchedRuleList().addAll(matchedRules);
handleStateChange(wasValid);
}
void addEntityEventListener(EntityEventListener listener);
void removeEntityEventListener(EntityEventListener listener);
default void notifyEntityInserted() {
for (EntityEventListener listener : getEntityEventListeners()) {
listener.onEntityInserted(this);
}
}
default void notifyEntityUpdated() {
for (EntityEventListener listener : getEntityEventListeners()) {
listener.onEntityUpdated(this);
}
}
default void notifyEntityRemoved() {
for (EntityEventListener listener : getEntityEventListeners()) {
listener.onEntityRemoved(this);
}
}
Collection<EntityEventListener> getEntityEventListeners();
default void handleStateChange(boolean wasValid) {
if (valid() == wasValid) {
return;
}
if (!removed()) {
notifyEntityUpdated();
} else {
notifyEntityRemoved();
}
} }

View File

@ -1,10 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.document.entity;
public class Intersection extends AbstractRelation {
public Intersection(TextEntity a, TextEntity b) {
super(a, b);
}
}

View File

@ -27,7 +27,7 @@ public final class MatchedRule implements Comparable<MatchedRule> {
public static final RuleType IMPORTED_TYPE = RuleType.fromString("IMP"); public static final RuleType IMPORTED_TYPE = RuleType.fromString("IMP");
public static final RuleType MANUAL_TYPE = RuleType.fromString("MAN"); public static final RuleType MANUAL_TYPE = RuleType.fromString("MAN");
public static final RuleType DICTIONARY_TYPE = RuleType.fromString("DICT"); public static final RuleType DICTIONARY_TYPE = RuleType.fromString("DICT");
private static final List<RuleType> RULE_TYPE_PRIORITIES = List.of(FINAL_TYPE, ELIMINATION_RULE_TYPE, MANUAL_TYPE, IMPORTED_TYPE, DICTIONARY_TYPE); private static final List<RuleType> RULE_TYPE_PRIORITIES = List.of(FINAL_TYPE, ELIMINATION_RULE_TYPE, IMPORTED_TYPE, MANUAL_TYPE, DICTIONARY_TYPE);
RuleIdentifier ruleIdentifier; RuleIdentifier ruleIdentifier;
@Builder.Default @Builder.Default

View File

@ -1,10 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.document.entity;
public interface Relation {
TextEntity getA();
TextEntity getB();
}

View File

@ -4,7 +4,6 @@ import java.awt.geom.Rectangle2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -12,10 +11,7 @@ import java.util.Map;
import java.util.PriorityQueue; import java.util.PriorityQueue;
import java.util.Set; import java.util.Set;
import org.apache.commons.collections4.map.HashedMap;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.BaseAnnotation;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange; import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
@ -47,14 +43,13 @@ public class TextEntity implements IEntity {
TextRange textRange; TextRange textRange;
@Builder.Default @Builder.Default
Set<TextRange> duplicateTextRanges = new HashSet<>(); List<TextRange> duplicateTextRanges = new ArrayList<>();
String type; // TODO: make final once ManualChangesApplicationService::recategorize is deleted String type; // TODO: make final once ManualChangesApplicationService::recategorize is deleted
final EntityType entityType; final EntityType entityType;
@Builder.Default @Builder.Default
final PriorityQueue<MatchedRule> matchedRuleList = new PriorityQueue<>(); final PriorityQueue<MatchedRule> matchedRuleList = new PriorityQueue<>();
@Builder.Default final ManualChangeOverwrite manualOverwrite;
final ManualChangeOverwrite manualOverwrite = new ManualChangeOverwrite();
boolean dictionaryEntry; boolean dictionaryEntry;
boolean dossierDictionaryEntry; boolean dossierDictionaryEntry;
@ -73,12 +68,6 @@ public class TextEntity implements IEntity {
List<SemanticNode> intersectingNodes = new LinkedList<>(); List<SemanticNode> intersectingNodes = new LinkedList<>();
SemanticNode deepestFullyContainingNode; SemanticNode deepestFullyContainingNode;
@Builder.Default
Map<TextEntity, Set<Relation>> relations = new HashMap<>();
@Builder.Default
Collection<EntityEventListener> entityEventListeners = new ArrayList<>();
public static TextEntity initialEntityNode(TextRange textRange, String type, EntityType entityType, SemanticNode node) { public static TextEntity initialEntityNode(TextRange textRange, String type, EntityType entityType, SemanticNode node) {
@ -169,15 +158,12 @@ public class TextEntity implements IEntity {
public void removeFromGraph() { public void removeFromGraph() {
remove("FINAL.0.0", "removed completely");
intersectingNodes.forEach(node -> node.getEntities().remove(this)); intersectingNodes.forEach(node -> node.getEntities().remove(this));
pages.forEach(page -> page.getEntities().remove(this)); pages.forEach(page -> page.getEntities().remove(this));
intersectingNodes = new LinkedList<>(); intersectingNodes = new LinkedList<>();
relations.keySet()
.forEach(entity -> entity.getRelations().remove(this));
relations = new HashedMap<>();
deepestFullyContainingNode = null; deepestFullyContainingNode = null;
pages = new HashSet<>(); pages = new HashSet<>();
remove("FINAL.0.0", "removed completely");
} }
@ -215,20 +201,22 @@ public class TextEntity implements IEntity {
return textEntity.contains(this); return textEntity.contains(this);
} }
public boolean contains(TextEntity textEntity) { public boolean contains(TextEntity textEntity) {
if (this.textRange.contains(textEntity.getTextRange())) { if (this.textRange.contains(textEntity.getTextRange())) {
return true; return true;
} }
Set<TextRange> textEntityDuplicateRanges = textEntity.getDuplicateTextRanges(); List<TextRange> textEntityDuplicateRanges = textEntity.getDuplicateTextRanges();
// use optimized indexed loops for extra performance boost
for (TextRange duplicateTextRange : this.duplicateTextRanges) { for (int i = 0, duplicateTextRangesSize = duplicateTextRanges.size(); i < duplicateTextRangesSize; i++) {
TextRange duplicateTextRange = duplicateTextRanges.get(i);
if (duplicateTextRange.contains(textEntity.getTextRange())) { if (duplicateTextRange.contains(textEntity.getTextRange())) {
return true; return true;
} }
for (int j = 0, textEntityDuplicateRangesSize = textEntityDuplicateRanges.size(); j < textEntityDuplicateRangesSize; j++) {
for (TextRange otherRange : textEntityDuplicateRanges) { TextRange otherRange = textEntityDuplicateRanges.get(j);
if (duplicateTextRange.contains(otherRange)) { if (duplicateTextRange.contains(otherRange)) {
return true; return true;
} }
@ -239,7 +227,6 @@ public class TextEntity implements IEntity {
} }
public boolean intersects(TextEntity textEntity) { public boolean intersects(TextEntity textEntity) {
return this.textRange.intersects(textEntity.getTextRange()) // return this.textRange.intersects(textEntity.getTextRange()) //
@ -264,20 +251,6 @@ public class TextEntity implements IEntity {
} }
public void addManualChange(BaseAnnotation manualChange) {
manualOverwrite.addChange(manualChange);
notifyEntityUpdated();
}
public void addManualChanges(List<BaseAnnotation> manualChanges) {
manualOverwrite.addChanges(manualChanges);
notifyEntityUpdated();
}
public boolean matchesAnnotationId(String manualRedactionId) { public boolean matchesAnnotationId(String manualRedactionId) {
return getPositionsOnPagePerPage().stream() return getPositionsOnPagePerPage().stream()
@ -338,42 +311,4 @@ public class TextEntity implements IEntity {
.orElse(getMatchedRule().isWriteValueWithLineBreaks() ? getValueWithLineBreaks() : value); .orElse(getMatchedRule().isWriteValueWithLineBreaks() ? getValueWithLineBreaks() : value);
} }
@Override
public void addEntityEventListener(EntityEventListener listener) {
entityEventListeners.add(listener);
}
@Override
public void removeEntityEventListener(EntityEventListener listener) {
entityEventListeners.remove(listener);
}
public void computeRelations() {
for (TextEntity textEntity : this.getDeepestFullyContainingNode().getEntities()) {
if (this.intersects(textEntity) && !this.equals(textEntity) && !textEntity.getEntityType().equals(EntityType.TEMPORARY)) {
if (textEntity.getTextRange().equals(this.getTextRange())) {
textEntity.getRelations().computeIfAbsent(this, k -> new HashSet<>()).add(new Equality(this, textEntity));
this.getRelations().computeIfAbsent(textEntity, k -> new HashSet<>()).add(new Equality(textEntity, this));
} else if (textEntity.containedBy(this)) {
textEntity.getRelations().computeIfAbsent(this, k -> new HashSet<>()).add(new Intersection(textEntity, this));
this.getRelations().computeIfAbsent(textEntity, k -> new HashSet<>()).add(new Containment(this, textEntity));
} else if (this.containedBy(textEntity)) {
textEntity.getRelations().computeIfAbsent(this, k -> new HashSet<>()).add(new Containment(textEntity, this));
this.getRelations().computeIfAbsent(textEntity, k -> new HashSet<>()).add(new Intersection(this, textEntity));
} else {
textEntity.getRelations().computeIfAbsent(this, k -> new HashSet<>()).add(new Intersection(textEntity, this));
this.getRelations().computeIfAbsent(textEntity, k -> new HashSet<>()).add(new Intersection(this, textEntity));
}
}
}
}
} }

View File

@ -39,6 +39,7 @@ public class Document extends AbstractSemanticNode {
@Builder.Default @Builder.Default
static final SectionIdentifier sectionIdentifier = SectionIdentifier.document(); static final SectionIdentifier sectionIdentifier = SectionIdentifier.document();
@Override @Override
public NodeType getType() { public NodeType getType() {

View File

@ -1,8 +1,6 @@
package com.iqser.red.service.redaction.v1.server.model.document.nodes; package com.iqser.red.service.redaction.v1.server.model.document.nodes;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
@ -12,7 +10,6 @@ import java.util.Set;
import com.iqser.red.service.redaction.v1.server.model.document.NodeVisitor; import com.iqser.red.service.redaction.v1.server.model.document.NodeVisitor;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange; import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityEventListener;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.ManualChangeOverwrite; import com.iqser.red.service.redaction.v1.server.model.document.entity.ManualChangeOverwrite;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
@ -55,9 +52,6 @@ public class Image extends AbstractSemanticNode implements IEntity {
Page page; Page page;
@Builder.Default
Collection<EntityEventListener> entityEventListeners = new ArrayList<>();
@Override @Override
public NodeType getType() { public NodeType getType() {
@ -106,21 +100,6 @@ public class Image extends AbstractSemanticNode implements IEntity {
} }
@Override
public void addEntityEventListener(EntityEventListener listener) {
entityEventListeners.add(listener);
}
@Override
public void removeEntityEventListener(EntityEventListener listener) {
entityEventListeners.remove(listener);
}
@Override @Override
public String type() { public String type() {

View File

@ -4,7 +4,7 @@ plugins {
} }
description = "redaction-service-api-v1" description = "redaction-service-api-v1"
val persistenceServiceVersion = "2.631.0" val persistenceServiceVersion = "2.612.0-RED10072.1"
dependencies { dependencies {
implementation("org.springframework:spring-web:6.0.12") implementation("org.springframework:spring-web:6.0.12")

View File

@ -12,11 +12,11 @@ plugins {
description = "redaction-service-server-v1" description = "redaction-service-server-v1"
val layoutParserVersion = "0.193.0" val layoutParserVersion = "0.181.0"
val jacksonVersion = "2.15.2" val jacksonVersion = "2.15.2"
val droolsVersion = "9.44.0.Final" val droolsVersion = "9.44.0.Final"
val pdfBoxVersion = "3.0.0" val pdfBoxVersion = "3.0.0"
val persistenceServiceVersion = "2.641.0" val persistenceServiceVersion = "2.612.0-RED10072.1"
val llmServiceVersion = "1.20.0-RED10072.2" val llmServiceVersion = "1.20.0-RED10072.2"
val springBootStarterVersion = "3.1.5" val springBootStarterVersion = "3.1.5"
val springCloudVersion = "4.0.4" val springCloudVersion = "4.0.4"
@ -56,9 +56,7 @@ dependencies {
implementation("com.fasterxml.jackson.module:jackson-module-afterburner:${jacksonVersion}") implementation("com.fasterxml.jackson.module:jackson-module-afterburner:${jacksonVersion}")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}")
implementation("org.ahocorasick:ahocorasick:0.9.0") implementation("org.ahocorasick:ahocorasick:0.6.3")
implementation("com.hankcs:aho-corasick-double-array-trie:1.2.2")
implementation("com.github.roklenarcic:aho-corasick:1.2")
implementation("org.javassist:javassist:3.29.2-GA") implementation("org.javassist:javassist:3.29.2-GA")
implementation("org.drools:drools-engine:${droolsVersion}") implementation("org.drools:drools-engine:${droolsVersion}")
@ -77,9 +75,6 @@ dependencies {
implementation("org.apache.tomcat:tomcat-websocket:${tomcatVersion}") implementation("org.apache.tomcat:tomcat-websocket:${tomcatVersion}")
implementation("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") implementation("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
implementation("org.liquibase:liquibase-core:4.29.2") // Needed to be set explicit, otherwise spring dependency management sets it to 4.20.0
implementation("org.liquibase.ext:liquibase-mongodb:4.29.2")
implementation("net.logstash.logback:logstash-logback-encoder:7.4") implementation("net.logstash.logback:logstash-logback-encoder:7.4")
api("ch.qos.logback:logback-classic") api("ch.qos.logback:logback-classic")
@ -102,10 +97,6 @@ dependencies {
group = "com.iqser.red.service", group = "com.iqser.red.service",
module = "persistence-service-shared-api-v1" module = "persistence-service-shared-api-v1"
) )
exclude(
group = "com.knecon.fforesight",
module = "document"
)
} }
testImplementation("com.pdftron:PDFNet:10.11.0") testImplementation("com.pdftron:PDFNet:10.11.0")
} }

View File

@ -28,8 +28,6 @@ public class RedactionServiceSettings {
private boolean priorityMode; private boolean priorityMode;
private long firstLevelDictionaryCacheMaximumSize = 1000;
private long dictionaryCacheMaximumSize = 100; private long dictionaryCacheMaximumSize = 100;
private int dictionaryCacheExpireAfterAccessDays = 3; private int dictionaryCacheExpireAfterAccessDays = 3;

View File

@ -2,7 +2,6 @@ package com.iqser.red.service.redaction.v1.server.model;
import static com.iqser.red.service.redaction.v1.server.service.NotFoundImportedEntitiesService.IMPORTED_REDACTION_TYPE; import static com.iqser.red.service.redaction.v1.server.service.NotFoundImportedEntitiesService.IMPORTED_REDACTION_TYPE;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.PriorityQueue; import java.util.PriorityQueue;
@ -16,7 +15,6 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
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.ManualResizeRedaction;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange; import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityEventListener;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.ManualChangeOverwrite; import com.iqser.red.service.redaction.v1.server.model.document.entity.ManualChangeOverwrite;
@ -54,8 +52,7 @@ public class PrecursorEntity implements IEntity {
@Builder.Default @Builder.Default
PriorityQueue<MatchedRule> matchedRuleList = new PriorityQueue<>(); PriorityQueue<MatchedRule> matchedRuleList = new PriorityQueue<>();
@Builder.Default ManualChangeOverwrite manualOverwrite;
ManualChangeOverwrite manualOverwrite = new ManualChangeOverwrite();
public static PrecursorEntity fromManualRedactionEntry(ManualRedactionEntry manualRedactionEntry, boolean hint) { public static PrecursorEntity fromManualRedactionEntry(ManualRedactionEntry manualRedactionEntry, boolean hint) {
@ -129,7 +126,6 @@ public class PrecursorEntity implements IEntity {
.id(importedRedaction.getId()) .id(importedRedaction.getId())
.value(value) .value(value)
.entityPosition(rectangleWithPages) .entityPosition(rectangleWithPages)
.ruleIdentifier("IMP.0.0")
.reason(Optional.ofNullable(importedRedaction.getReason()) .reason(Optional.ofNullable(importedRedaction.getReason())
.orElse("")) .orElse(""))
.legalBasis(Optional.ofNullable(importedRedaction.getLegalBasis()) .legalBasis(Optional.ofNullable(importedRedaction.getLegalBasis())
@ -201,28 +197,6 @@ public class PrecursorEntity implements IEntity {
} }
@Override
public void addEntityEventListener(EntityEventListener listener) {
throw new UnsupportedOperationException("PrecursorEntity does not support entityEventListeners");
}
@Override
public void removeEntityEventListener(EntityEventListener listener) {
throw new UnsupportedOperationException("PrecursorEntity does not support entityEventListeners");
}
@Override
public Collection<EntityEventListener> getEntityEventListeners() {
throw new UnsupportedOperationException("PrecursorEntity does not support entityEventListeners");
}
private static EntityType getEntityType(EntryType entryType) { private static EntityType getEntityType(EntryType entryType) {
switch (entryType) { switch (entryType) {

View File

@ -1,130 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import java.util.*;
import java.util.stream.Stream;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
public abstract class AbstractDictionarySearch implements DictionarySearch {
protected final Map<String, List<DictionaryIdentifierWithKeyword>> keyWordToIdentifiersMap;
public AbstractDictionarySearch(Map<String, List<DictionaryIdentifierWithKeyword>> keyWordToIdentifiersMap) {
this.keyWordToIdentifiersMap = keyWordToIdentifiersMap;
}
@Override
public Stream<MatchTextRange> getBoundaries(CharSequence text) {
TextContext textContext = new TextContext(text);
return getMatchTextRangeStream(textContext);
}
@Override
public Stream<MatchTextRange> getBoundaries(CharSequence text, TextRange region) {
CharSequence subText = text.subSequence(region.start(), region.end());
TextContext textContext = new TextContext(subText, region.start());
return getMatchTextRangeStream(textContext);
}
@Override
public Stream<MatchTextRange> getBoundaries(TextBlock textBlock) {
return getBoundaries(textBlock, textBlock.getTextRange());
}
@Override
public Stream<MatchPosition> getMatches(String text) {
TextContext textContext = new TextContext(text);
List<MatchPosition> matches = new ArrayList<>();
parseText(textContext.getLowerText(), (begin, end, value) -> addMatchPositionsForHit(textContext, matches, new Hit(begin, end, value)));
return matches.stream();
}
private Stream<MatchTextRange> getMatchTextRangeStream(TextContext textContext) {
List<MatchTextRange> matches = new ArrayList<>();
parseText(textContext.getLowerText(), (begin, end, value) -> addMatchesForHit(textContext, matches, new Hit(begin, end, value)));
return matches.stream();
}
protected abstract void parseText(CharSequence text, HitHandler handler);
protected void addMatchesForHit(TextContext textContext, List<MatchTextRange> matches, Hit hit) {
int start = textContext.getStart(hit.begin);
int end = textContext.getEnd(hit.end);
String matchedText = textContext.getMatchedText(hit.begin, hit.end);
List<DictionaryIdentifierWithKeyword> idWithKeywords = hit.value;
for (DictionaryIdentifierWithKeyword idkw : idWithKeywords) {
if (idkw.identifier().caseSensitive()) {
if (matchedText.equals(idkw.keyword())) {
matches.add(new MatchTextRange(idkw.identifier(), new TextRange(start, end)));
}
} else {
matches.add(new MatchTextRange(idkw.identifier(), new TextRange(start, end)));
}
}
}
protected void addMatchPositionsForHit(TextContext textContext, List<MatchPosition> matches, Hit hit) {
int start = textContext.getStart(hit.begin);
int end = textContext.getEnd(hit.end);
String matchedText = textContext.getMatchedText(hit.begin, hit.end);
List<DictionaryIdentifierWithKeyword> idWithKeywords = hit.value;
for (DictionaryIdentifierWithKeyword idkw : idWithKeywords) {
MatchPosition matchPosition = new MatchPosition(idkw.identifier(), start, end);
if (idkw.identifier().caseSensitive()) {
if (matchedText.equals(idkw.keyword())) {
matches.add(matchPosition);
}
} else {
matches.add(matchPosition);
}
}
}
protected interface HitHandler {
void handle(int begin, int end, List<DictionaryIdentifierWithKeyword> value);
}
protected static class Hit {
final int begin;
final int end;
final List<DictionaryIdentifierWithKeyword> value;
Hit(int begin, int end, List<DictionaryIdentifierWithKeyword> value) {
this.begin = begin;
this.end = end;
this.value = value;
}
}
}

View File

@ -1,32 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import java.util.List;
import java.util.Map;
import com.roklenarcic.util.strings.AhoCorasickMap;
import com.roklenarcic.util.strings.MapMatchListener;
import com.roklenarcic.util.strings.StringMap;
public class AhoCorasickMapDictionarySearch extends AbstractDictionarySearch {
private final StringMap<List<DictionaryIdentifierWithKeyword>> map;
public AhoCorasickMapDictionarySearch(Map<String, List<DictionaryIdentifierWithKeyword>> keyWordToIdentifiersMap) {
super(keyWordToIdentifiersMap);
map = new AhoCorasickMap<>(keyWordToIdentifiersMap.keySet(), keyWordToIdentifiersMap.values(), false);
}
@Override
protected void parseText(CharSequence text, HitHandler handler) {
MapMatchListener<List<DictionaryIdentifierWithKeyword>> listener = (haystack, startPosition, endPosition, value) -> {
handler.handle(startPosition, endPosition, value);
return true;
};
map.match(text.toString(), listener);
}
}

View File

@ -2,16 +2,20 @@ package com.iqser.red.service.redaction.v1.server.model.dictionary;
import static java.lang.String.format; import static java.lang.String.format;
import java.util.*; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntry;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Relation;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.utils.Patterns; import com.iqser.red.service.redaction.v1.server.utils.Patterns;
import com.iqser.red.service.redaction.v1.server.utils.exception.NotFoundException; import com.iqser.red.service.redaction.v1.server.utils.exception.NotFoundException;
@ -25,77 +29,31 @@ import lombok.Getter;
@Data @Data
public class Dictionary { public class Dictionary {
private final Map<String, Map<Level, DictionaryModel>> localAccessMap = new HashMap<>(); @Getter
private List<DictionaryModel> dictionaryModels;
// todo: dossier and dossier template level DictionaryModels override each other
// at the moment there are no problems because they always have the same rank / hint information
// but it should be changed so that the localAccessMap contains all models
private Map<String, DictionaryModel> localAccessMap = new HashMap<>();
private final DictionaryVersion version; @Getter
private DictionaryVersion version;
private final DictionarySearch dictionarySearch;
public enum Level {
DOSSIER_TEMPLATE,
DOSSIER
}
Dictionary(List<DictionaryModel> dictionaryModels, DictionaryVersion version, DictionarySearch dictionarySearch) { public Dictionary(List<DictionaryModel> dictionaryModels, DictionaryVersion version) {
dictionaryModels.forEach(dm -> localAccessMap.put(dm.getType(), Map.of(getLevel(dm.isDossierDictionary()), dm))); this.dictionaryModels = dictionaryModels;
this.dictionaryModels.forEach(dm -> localAccessMap.put(dm.getType(), dm));
this.version = version; this.version = version;
this.dictionarySearch = dictionarySearch;
}
public boolean containsType(String type) {
Map<Level, DictionaryModel> levelMap = localAccessMap.get(type);
return !(levelMap == null || levelMap.isEmpty());
}
private Level getLevel(boolean isDossierDictionary) {
return isDossierDictionary ? Level.DOSSIER : Level.DOSSIER_TEMPLATE;
}
/**
* Determines the default level for a given type based on the levels present.
* If both levels are present, it defaults to {@code Level.DOSSIER}.
*
* @param type The type to determine the default level for.
* @return The default {@link Level} for the specified type.
* @throws NotFoundException If the type is not found in the dictionary.
*/
private Level getDefaultLevel(String type) {
Map<Level, DictionaryModel> levelMap = localAccessMap.get(type);
if (levelMap == null || levelMap.isEmpty()) {
throw new NotFoundException("Type: " + type + " is not found");
}
if (levelMap.containsKey(Level.DOSSIER)) {
return Level.DOSSIER;
} else {
// Use whatever level is present
return levelMap.keySet()
.iterator().next();
}
}
public int getDictionaryRank(String type, Level level) {
if (!localAccessMap.containsKey(type)) {
return 0;
}
DictionaryModel model = localAccessMap.get(type)
.get(level);
return model != null ? model.getRank() : 0;
} }
public int getDictionaryRank(String type) { public int getDictionaryRank(String type) {
return getDictionaryRank(type, getDefaultLevel(type)); if (!localAccessMap.containsKey(type)) {
return 0;
}
return localAccessMap.get(type).getRank();
} }
@ -106,21 +64,11 @@ public class Dictionary {
*/ */
public boolean hasLocalEntries() { public boolean hasLocalEntries() {
return getDictionaryModels().stream() return dictionaryModels.stream()
.anyMatch(dm -> !dm.getLocalEntriesWithMatchedRules().isEmpty()); .anyMatch(dm -> !dm.getLocalEntriesWithMatchedRules().isEmpty());
} }
public List<DictionaryModel> getDictionaryModels() {
return localAccessMap.values()
.stream()
.flatMap(levelDictionaryModelMap -> levelDictionaryModelMap.values()
.stream())
.toList();
}
public Set<String> getTypes() { public Set<String> getTypes() {
return localAccessMap.keySet(); return localAccessMap.keySet();
@ -128,144 +76,56 @@ public class Dictionary {
/** /**
* Retrieves the {@link DictionaryModel} of a specified type and level. * Retrieves the {@link DictionaryModel} of a specified type.
*
* @param type The type of dictionary model to retrieve.
* @param level The level of the dictionary model to retrieve.
* @return The {@link DictionaryModel} of the specified type and level.
* @throws NotFoundException If the specified type or level is not found in the dictionary.
*/
public DictionaryModel getType(String type, Level level) {
Map<Level, DictionaryModel> levelMap = localAccessMap.get(type);
if (levelMap == null || !levelMap.containsKey(level)) {
throw new NotFoundException("Type: " + type + " with level: " + level + " is not found");
}
return levelMap.get(level);
}
/**
* Retrieves the {@link DictionaryModel} of a specified type at the default level.
* *
* @param type The type of dictionary model to retrieve. * @param type The type of dictionary model to retrieve.
* @return The {@link DictionaryModel} of the specified type at the default level. * @return The {@link DictionaryModel} of the specified type.
* @throws NotFoundException If the specified type is not found in the dictionary. * @throws NotFoundException If the specified type is not found in the dictionary.
*/ */
public DictionaryModel getType(String type) { public DictionaryModel getType(String type) {
return getType(type, getDefaultLevel(type)); DictionaryModel model = localAccessMap.get(type);
if (model == null) {
throw new NotFoundException("Type: " + type + " is not found");
}
return model;
} }
/** /**
* Checks if the dictionary of a specific type and level is considered a hint. * Checks if the dictionary of a specific type is considered a hint.
*
* @param type The type of dictionary to check.
* @param level The level of the dictionary to check.
* @return true if the dictionary model is marked as a hint, false otherwise.
*/
public boolean isHint(String type, Level level) {
DictionaryModel model = localAccessMap.get(type)
.get(level);
return model != null && model.isHint();
}
/**
* Checks if the dictionary of a specific type is considered a hint at the default level.
* *
* @param type The type of dictionary to check. * @param type The type of dictionary to check.
* @return true if the dictionary model is marked as a hint, false otherwise. * @return true if the dictionary model is marked as a hint, false otherwise.
*/ */
public boolean isHint(String type) { public boolean isHint(String type) {
return isHint(type, getDefaultLevel(type)); DictionaryModel model = localAccessMap.get(type);
if (model != null) {
return model.isHint();
}
return false;
} }
/** /**
* Checks if the dictionary of a specific type and level is case-insensitive. * Checks if the dictionary of a specific type is case-insensitive.
*
* @param type The type of dictionary to check.
* @param level The level of the dictionary to check.
* @return true if the dictionary is case-insensitive, false otherwise.
*/
public boolean isCaseInsensitiveDictionary(String type, Level level) {
DictionaryModel dictionaryModel = localAccessMap.get(type)
.get(level);
return dictionaryModel != null && dictionaryModel.isCaseInsensitive();
}
/**
* Checks if the dictionary of a specific type is case-insensitive at the default level.
* *
* @param type The type of dictionary to check. * @param type The type of dictionary to check.
* @return true if the dictionary is case-insensitive, false otherwise. * @return true if the dictionary is case-insensitive, false otherwise.
*/ */
public boolean isCaseInsensitiveDictionary(String type) { public boolean isCaseInsensitiveDictionary(String type) {
return isCaseInsensitiveDictionary(type, getDefaultLevel(type)); DictionaryModel dictionaryModel = localAccessMap.get(type);
if (dictionaryModel != null) {
return dictionaryModel.isCaseInsensitive();
}
return false;
} }
/** /**
* Adds a local dictionary entry of a specific type and level. * Adds a local dictionary entry of a specific type.
*
* @param type The type of dictionary to add the entry to.
* @param value The value of the entry.
* @param matchedRules A collection of {@link MatchedRule} associated with the entry.
* @param alsoAddLastname Indicates whether to also add the lastname separately as an entry.
* @param level The level of the dictionary where the entry should be added.
* @throws IllegalArgumentException If the specified type does not exist within the dictionary, if the type
* does not have any local entries defined, or if the provided value is
* blank. This ensures that only valid, non-empty entries
* are added to the dictionary.
*/
private void addLocalDictionaryEntry(String type, String value, Collection<MatchedRule> matchedRules, boolean alsoAddLastname, Level level) {
if (value.isBlank()) {
return;
}
Map<Level, DictionaryModel> levelMap = localAccessMap.get(type);
if (levelMap == null || !levelMap.containsKey(level)) {
throw new IllegalArgumentException(format("DictionaryModel of type %s with level %s does not exist", type, level));
}
DictionaryModel dictionaryModel = levelMap.get(level);
if (dictionaryModel.getLocalEntriesWithMatchedRules() == null) {
throw new IllegalArgumentException(format("DictionaryModel of type %s has no local Entries", type));
}
if (StringUtils.isEmpty(value)) {
throw new IllegalArgumentException(format("%s is not a valid dictionary entry", value));
}
boolean isCaseInsensitive = dictionaryModel.isCaseInsensitive();
Set<MatchedRule> matchedRulesSet = new HashSet<>(matchedRules);
String cleanedValue = value;
if (isCaseInsensitive) {
cleanedValue = cleanedValue.toLowerCase(Locale.US);
}
dictionaryModel.getLocalEntriesWithMatchedRules()
.merge(cleanedValue.trim(),
matchedRulesSet,
(set1, set2) -> Stream.concat(set1.stream(), set2.stream())
.collect(Collectors.toSet()));
if (alsoAddLastname) {
String lastname = cleanedValue.split(" ")[0];
dictionaryModel.getLocalEntriesWithMatchedRules()
.merge(lastname,
matchedRulesSet,
(set1, set2) -> Stream.concat(set1.stream(), set2.stream())
.collect(Collectors.toSet()));
}
}
/**
* Adds a local dictionary entry of a specific type at the default level.
* *
* @param type The type of dictionary to add the entry to. * @param type The type of dictionary to add the entry to.
* @param value The value of the entry. * @param value The value of the entry.
@ -278,7 +138,40 @@ public class Dictionary {
*/ */
private void addLocalDictionaryEntry(String type, String value, Collection<MatchedRule> matchedRules, boolean alsoAddLastname) { private void addLocalDictionaryEntry(String type, String value, Collection<MatchedRule> matchedRules, boolean alsoAddLastname) {
addLocalDictionaryEntry(type, value, matchedRules, alsoAddLastname, getDefaultLevel(type)); if (value.isBlank()) {
return;
}
if (localAccessMap.get(type) == null) {
throw new IllegalArgumentException(format("DictionaryModel of type %s does not exist", type));
}
if (localAccessMap.get(type).getLocalEntriesWithMatchedRules() == null) {
throw new IllegalArgumentException(format("DictionaryModel of type %s has no local Entries", type));
}
if (StringUtils.isEmpty(value)) {
throw new IllegalArgumentException(format("%s is not a valid dictionary entry", value));
}
boolean isCaseInsensitive = localAccessMap.get(type).isCaseInsensitive();
Set<MatchedRule> matchedRulesSet = new HashSet<>(matchedRules);
String cleanedValue = value;
if (isCaseInsensitive) {
cleanedValue = cleanedValue.toLowerCase(Locale.US);
}
localAccessMap.get(type)
.getLocalEntriesWithMatchedRules()
.merge(cleanedValue.trim(),
matchedRulesSet,
(set1, set2) -> Stream.concat(set1.stream(), set2.stream())
.collect(Collectors.toSet()));
if (alsoAddLastname) {
String lastname = cleanedValue.split(" ")[0];
localAccessMap.get(type)
.getLocalEntriesWithMatchedRules()
.merge(lastname,
matchedRulesSet,
(set1, set2) -> Stream.concat(set1.stream(), set2.stream())
.collect(Collectors.toSet()));
}
} }
@ -286,22 +179,10 @@ public class Dictionary {
* Recommends a text entity for inclusion in every dictionary model without separating the last name. * Recommends a text entity for inclusion in every dictionary model without separating the last name.
* *
* @param textEntity The {@link TextEntity} to be recommended. * @param textEntity The {@link TextEntity} to be recommended.
* @param level The level of the dictionary where the recommendation should be added.
*/
public void recommendEverywhere(TextEntity textEntity, Level level) {
addLocalDictionaryEntry(textEntity.type(), textEntity.getValue(), textEntity.getMatchedRuleList(), false, level);
}
/**
* Recommends a text entity for inclusion in every dictionary model without separating the last name at the default level.
*
* @param textEntity The {@link TextEntity} to be recommended.
*/ */
public void recommendEverywhere(TextEntity textEntity) { public void recommendEverywhere(TextEntity textEntity) {
recommendEverywhere(textEntity, getDefaultLevel(textEntity.type())); addLocalDictionaryEntry(textEntity.type(), textEntity.getValue(), textEntity.getMatchedRuleList(), false);
} }
@ -309,22 +190,10 @@ public class Dictionary {
* Recommends a text entity for inclusion in every dictionary model with the last name added separately. * Recommends a text entity for inclusion in every dictionary model with the last name added separately.
* *
* @param textEntity The {@link TextEntity} to be recommended. * @param textEntity The {@link TextEntity} to be recommended.
* @param level The level of the dictionary where the recommendation should be added.
*/
public void recommendEverywhereWithLastNameSeparately(TextEntity textEntity, Level level) {
addLocalDictionaryEntry(textEntity.type(), textEntity.getValue(), textEntity.getMatchedRuleList(), true, level);
}
/**
* Recommends a text entity for inclusion in every dictionary model with the last name added separately at the default level.
*
* @param textEntity The {@link TextEntity} to be recommended.
*/ */
public void recommendEverywhereWithLastNameSeparately(TextEntity textEntity) { public void recommendEverywhereWithLastNameSeparately(TextEntity textEntity) {
recommendEverywhereWithLastNameSeparately(textEntity, getDefaultLevel(textEntity.type())); addLocalDictionaryEntry(textEntity.type(), textEntity.getValue(), textEntity.getMatchedRuleList(), true);
} }
@ -332,22 +201,11 @@ public class Dictionary {
* Adds multiple author names contained within a text entity as recommendations in the dictionary. * Adds multiple author names contained within a text entity as recommendations in the dictionary.
* *
* @param textEntity The {@link TextEntity} containing author names to be added. * @param textEntity The {@link TextEntity} containing author names to be added.
* @param level The level of the dictionary where the recommendations should be added.
*/
public void addMultipleAuthorsAsRecommendation(TextEntity textEntity, Level level) {
splitIntoAuthorNames(textEntity).forEach(authorName -> addLocalDictionaryEntry(textEntity.type(), authorName, textEntity.getMatchedRuleList(), true, level));
}
/**
* Adds multiple author names contained within a text entity as recommendations in the dictionary at the default level.
*
* @param textEntity The {@link TextEntity} containing author names to be added.
*/ */
public void addMultipleAuthorsAsRecommendation(TextEntity textEntity) { public void addMultipleAuthorsAsRecommendation(TextEntity textEntity) {
addMultipleAuthorsAsRecommendation(textEntity, getDefaultLevel(textEntity.type())); splitIntoAuthorNames(textEntity).forEach(authorName -> addLocalDictionaryEntry(textEntity.type(), authorName, textEntity.getMatchedRuleList(), true));
} }

View File

@ -1,90 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.springframework.stereotype.Service;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntry;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntryModel;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@Service
@RequiredArgsConstructor
public class DictionaryFactory {
@SneakyThrows
public Dictionary create(List<DictionaryModel> dictionaryModels, DictionaryVersion dictionaryVersion) {
Map<String, List<DictionaryIdentifierWithKeyword>> keyWordToIdentifiersMap = computeStringIdentifiersMap(dictionaryModels);
DictionarySearch dictionarySearch = getDictionarySearch(keyWordToIdentifiersMap);
return new Dictionary(dictionaryModels, dictionaryVersion, dictionarySearch);
}
private static DictionarySearch getDictionarySearch(Map<String, List<DictionaryIdentifierWithKeyword>> keyWordToIdentifiersMap) {
// a more sophisticated selection of the dictionarySearch could be done here
// but as we do not have the need to fine-tune at the moment we use the all-rounder solution, which is the AhoCoraSickMapDictionarySearch
// based on this repository https://github.com/RokLenarcic/AhoCorasick
// This is an outline how a more complex dictionarySearch decision could be made:
// if (!redactionServiceSettings.isPriorityMode() && keyWordToIdentifiersMap.keySet().size() < 50_000) {
// dictionarySearch = new DoubleArrayTrieDictionarySearch(keyWordToIdentifiersMap);
// } else {
// dictionarySearch = new AhoCorasickMapDictionarySearch(keyWordToIdentifiersMap);
// }
return new AhoCorasickMapDictionarySearch(keyWordToIdentifiersMap);
}
protected static Map<String, List<DictionaryIdentifierWithKeyword>> computeStringIdentifiersMap(List<DictionaryModel> dictionaryModels) {
Map<String, List<DictionaryIdentifierWithKeyword>> stringToIdentifiersMap = new HashMap<>();
for (DictionaryModel model : dictionaryModels) {
// Add entries for different entity types
addEntriesToMap(stringToIdentifiersMap, model, model.isHint() ? EntityType.HINT : EntityType.ENTITY, model.getEntries(), false);
addEntriesToMap(stringToIdentifiersMap, model, EntityType.FALSE_POSITIVE, model.getFalsePositives(), false);
addEntriesToMap(stringToIdentifiersMap, model, EntityType.FALSE_RECOMMENDATION, model.getFalseRecommendations(), false);
if (model.isDossierDictionary()) {
addEntriesToMap(stringToIdentifiersMap, model, EntityType.DICTIONARY_REMOVAL, model.getEntries(), true);
}
}
return stringToIdentifiersMap;
}
private static void addEntriesToMap(Map<String, List<DictionaryIdentifierWithKeyword>> stringToIdentifiersMap,
DictionaryModel model,
EntityType entityType,
Set<DictionaryEntryModel> entries,
boolean isDeleted) {
DictionaryIdentifier identifier = new DictionaryIdentifier(model.getType(), entityType, model.isDossierDictionary(), !model.isCaseInsensitive());
List<String> values = entries.stream()
.filter(entry -> entry.isDeleted() == isDeleted)
.map(DictionaryEntry::getValue)
.toList();
for (String value : values) {
DictionaryIdentifierWithKeyword idWithKeyword = new DictionaryIdentifierWithKeyword(identifier, value);
String key = value.toLowerCase(Locale.ROOT);
stringToIdentifiersMap.computeIfAbsent(key, k -> new ArrayList<>()).add(idWithKeyword);
}
}
}

View File

@ -1,8 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
public record DictionaryIdentifier(String type, EntityType entityType, boolean dossierDictionaryEntry, boolean caseSensitive) {
}

View File

@ -1,51 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import org.ahocorasick.trie.PayloadEmit;
import org.ahocorasick.trie.PayloadTrie;
import java.util.Collection;
public final class DictionaryIdentifierTrie {
private final PayloadTrie<DictionaryIdentifier> trie;
private DictionaryIdentifierTrie(PayloadTrie<DictionaryIdentifier> trie) {
this.trie = trie;
}
public static class DictionaryIdentifierTrieBuilder {
private final PayloadTrie.PayloadTrieBuilder<DictionaryIdentifier> builder;
public DictionaryIdentifierTrieBuilder() {
this.builder = PayloadTrie.builder();
}
public DictionaryIdentifierTrieBuilder ignoreCase() {
builder.ignoreCase();
return this;
}
public DictionaryIdentifierTrieBuilder addKeyword(String keyword, DictionaryIdentifier payload) {
builder.addKeyword(keyword, payload);
return this;
}
public DictionaryIdentifierTrieBuilder addKeywords(Collection<String> keywords, DictionaryIdentifier payload) {
for (String keyword : keywords) {
builder.addKeyword(keyword, payload);
}
return this;
}
public DictionaryIdentifierTrie build() {
return new DictionaryIdentifierTrie(builder.build());
}
}
public Collection<PayloadEmit<DictionaryIdentifier>> parseText(CharSequence text) {
return trie.parseText(text);
}
public boolean containsMatch(CharSequence text) {
return trie.containsMatch(text);
}
}

View File

@ -1,5 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
public record DictionaryIdentifierWithKeyword(DictionaryIdentifier identifier, String keyword) {
}

View File

@ -1,12 +1,13 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary; package com.iqser.red.service.redaction.v1.server.model.dictionary;
import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntry;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntryModel; import com.iqser.red.service.dictionarymerge.commons.DictionaryEntryModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import lombok.Data; import lombok.Data;
@ -20,7 +21,7 @@ import lombok.extern.slf4j.Slf4j;
*/ */
@Data @Data
@Slf4j @Slf4j
public class DictionaryModel implements Cloneable { public class DictionaryModel implements Serializable {
private final String type; private final String type;
private final int rank; private final int rank;
@ -32,8 +33,13 @@ public class DictionaryModel implements Cloneable {
private final Set<DictionaryEntryModel> falsePositives; private final Set<DictionaryEntryModel> falsePositives;
private final Set<DictionaryEntryModel> falseRecommendations; private final Set<DictionaryEntryModel> falseRecommendations;
private transient SearchImplementation entriesSearch;
private transient SearchImplementation deletionEntriesSearch;
private transient SearchImplementation falsePositiveSearch;
private transient SearchImplementation falseRecommendationsSearch;
private final HashMap<String, Set<MatchedRule>> localEntriesWithMatchedRules = new HashMap<>(); private final HashMap<String, Set<MatchedRule>> localEntriesWithMatchedRules = new HashMap<>();
private SearchImplementation localSearch; private transient SearchImplementation localSearch;
/** /**
@ -85,6 +91,74 @@ public class DictionaryModel implements Cloneable {
} }
/**
* Returns the search implementation for non-deleted dictionary entries.
*
* @return The {@link SearchImplementation} for non-deleted dictionary entries.
*/
public SearchImplementation getEntriesSearch() {
if (entriesSearch == null) {
this.entriesSearch = new SearchImplementation(this.entries.stream()
.filter(e -> !e.isDeleted())
.map(DictionaryEntry::getValue)
.collect(Collectors.toList()), caseInsensitive);
}
return entriesSearch;
}
/**
* Returns the search implementation for deleted dictionary entries.
*
* @return The {@link SearchImplementation} for deleted dictionary entries.
*/
public SearchImplementation getDeletionEntriesSearch() {
if (deletionEntriesSearch == null) {
this.deletionEntriesSearch = new SearchImplementation(this.entries.stream()
.filter(DictionaryEntry::isDeleted)
.map(DictionaryEntry::getValue)
.collect(Collectors.toList()), caseInsensitive);
}
return deletionEntriesSearch;
}
/**
* Returns the search implementation for non-deleted false positive entries.
*
* @return The {@link SearchImplementation} for non-deleted false positive entries.
*/
public SearchImplementation getFalsePositiveSearch() {
if (falsePositiveSearch == null) {
this.falsePositiveSearch = new SearchImplementation(this.falsePositives.stream()
.filter(e -> !e.isDeleted())
.map(DictionaryEntry::getValue)
.collect(Collectors.toList()), caseInsensitive);
}
return falsePositiveSearch;
}
/**
* Returns the search implementation for non-deleted false recommendation entries.
*
* @return The {@link SearchImplementation} for non-deleted false recommendation entries.
*/
public SearchImplementation getFalseRecommendationsSearch() {
if (falseRecommendationsSearch == null) {
this.falseRecommendationsSearch = new SearchImplementation(this.falseRecommendations.stream()
.filter(e -> !e.isDeleted())
.map(DictionaryEntry::getValue)
.collect(Collectors.toList()), caseInsensitive);
}
return falseRecommendationsSearch;
}
/** /**
* Retrieves the matched rules for a given value from the local dictionary entries. * Retrieves the matched rules for a given value from the local dictionary entries.
* The value is processed based on the case sensitivity of the dictionary. * The value is processed based on the case sensitivity of the dictionary.
@ -98,149 +172,4 @@ public class DictionaryModel implements Cloneable {
return localEntriesWithMatchedRules.get(cleanedValue); return localEntriesWithMatchedRules.get(cleanedValue);
} }
@Override
public DictionaryModel clone() {
try {
DictionaryModel cloned = (DictionaryModel) super.clone();
cloned.localSearch = null;
return cloned;
} catch (CloneNotSupportedException e) {
throw new AssertionError("Cloning not supported", e);
}
}
public void addNewEntries(long versionThreshold, Set<DictionaryIncrementValue> newValues) {
getEntries().forEach(entry -> {
if (entry.getVersion() > versionThreshold) {
newValues.add(new DictionaryIncrementValue(entry.getValue(), isCaseInsensitive()));
}
});
getFalsePositives().forEach(entry -> {
if (entry.getVersion() > versionThreshold) {
newValues.add(new DictionaryIncrementValue(entry.getValue(), isCaseInsensitive()));
}
});
getFalseRecommendations().forEach(entry -> {
if (entry.getVersion() > versionThreshold) {
newValues.add(new DictionaryIncrementValue(entry.getValue(), isCaseInsensitive()));
}
});
}
public void handleOldEntries(Type newType,
DictionaryEntries newEntries,
Set<DictionaryEntryModel> combinedEntries,
Set<DictionaryEntryModel> combinedFalsePositives,
Set<DictionaryEntryModel> combinedFalseRecommendations) {
if (isCaseInsensitive() && !newType.isCaseInsensitive()) {
// Compute new entries' values in lowercase once
Set<String> newEntryValuesLower = newEntries.getEntries()
.stream()
.map(s -> s.getValue().toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
combinedEntries.addAll(getEntries()
.stream()
.filter(f -> !newEntryValuesLower.contains(f.getValue()))
.collect(Collectors.toSet()));
// Similarly for false positives
Set<String> newFalsePositivesValuesLower = newEntries.getFalsePositives()
.stream()
.map(s -> s.getValue().toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
combinedFalsePositives.addAll(getFalsePositives()
.stream()
.filter(f -> !newFalsePositivesValuesLower.contains(f.getValue()))
.collect(Collectors.toSet()));
// Similarly for false recommendations
Set<String> newFalseRecommendationsValuesLower = newEntries.getFalseRecommendations()
.stream()
.map(s -> s.getValue().toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
combinedFalseRecommendations.addAll(getFalseRecommendations()
.stream()
.filter(f -> !newFalseRecommendationsValuesLower.contains(f.getValue()))
.collect(Collectors.toSet()));
} else if (!isCaseInsensitive() && newType.isCaseInsensitive()) {
// Compute new entries' values in lowercase once
Set<String> newEntryValuesLower = newEntries.getEntries()
.stream()
.map(s -> s.getValue().toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
combinedEntries.addAll(getEntries()
.stream()
.filter(f -> !newEntryValuesLower.contains(f.getValue().toLowerCase(Locale.ROOT)))
.collect(Collectors.toSet()));
// Similarly for false positives
Set<String> newFalsePositivesValuesLower = newEntries.getFalsePositives()
.stream()
.map(s -> s.getValue().toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
combinedFalsePositives.addAll(getFalsePositives()
.stream()
.filter(f -> !newFalsePositivesValuesLower.contains(f.getValue().toLowerCase(Locale.ROOT)))
.collect(Collectors.toSet()));
// Similarly for false recommendations
Set<String> newFalseRecommendationsValuesLower = newEntries.getFalseRecommendations()
.stream()
.map(s -> s.getValue().toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
combinedFalseRecommendations.addAll(getFalseRecommendations()
.stream()
.filter(f -> !newFalseRecommendationsValuesLower.contains(f.getValue().toLowerCase(Locale.ROOT)))
.collect(Collectors.toSet()));
} else {
// Both have the same case sensitivity
Set<String> newEntryValues = newEntries.getEntries()
.stream()
.map(DictionaryEntryModel::getValue)
.collect(Collectors.toSet());
combinedEntries.addAll(getEntries()
.stream()
.filter(f -> !newEntryValues.contains(f.getValue()))
.collect(Collectors.toSet()));
// Similarly for false positives
Set<String> newFalsePositivesValues = newEntries.getFalsePositives()
.stream()
.map(DictionaryEntryModel::getValue)
.collect(Collectors.toSet());
combinedFalsePositives.addAll(getFalsePositives()
.stream()
.filter(f -> !newFalsePositivesValues.contains(f.getValue()))
.collect(Collectors.toSet()));
// Similarly for false recommendations
Set<String> newFalseRecommendationsValues = newEntries.getFalseRecommendations()
.stream()
.map(DictionaryEntryModel::getValue)
.collect(Collectors.toSet());
combinedFalseRecommendations.addAll(getFalseRecommendations()
.stream()
.filter(f -> !newFalseRecommendationsValues.contains(f.getValue()))
.collect(Collectors.toSet()));
}
}
} }

View File

@ -1,86 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import java.util.List;
import java.util.stream.Stream;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
/**
* Common interface for dictionary search implementations.
*/
public interface DictionarySearch {
/**
* Retrieves a list of match boundaries within the given text.
*
* @param text The text to search within.
* @return A list of MatchTextRange representing the boundaries of matches.
*/
default List<MatchTextRange> getBoundariesAsList(CharSequence text) {
return getBoundaries(text).toList();
}
/**
* Retrieves a stream of match boundaries within the given text.
*
* @param text The text to search within.
* @return A stream of MatchTextRange representing the boundaries of matches.
*/
Stream<MatchTextRange> getBoundaries(CharSequence text);
/**
* Retrieves a list of match boundaries within a specified region of the text.
*
* @param text The text to search within.
* @param region The specific region of the text to search.
* @return A list of MatchTextRange representing the boundaries of matches.
*/
default List<MatchTextRange> getBoundariesAsList(CharSequence text, TextRange region) {
return getBoundaries(text, region).toList();
}
/**
* Retrieves a stream of match boundaries within a specified region of the text.
*
* @param text The text to search within.
* @param region The specific region of the text to search.
* @return A stream of MatchTextRange representing the boundaries of matches.
*/
Stream<MatchTextRange> getBoundaries(CharSequence text, TextRange region);
/**
* Retrieves a stream of match boundaries within the given TextBlock.
*
* @param textBlock The TextBlock to search within.
* @return A stream of MatchTextRange representing the boundaries of matches.
*/
Stream<MatchTextRange> getBoundaries(TextBlock textBlock);
/**
* Retrieves a list of match positions within the given text.
*
* @param text The text to search within.
* @return A list of MatchPosition representing the positions of matches.
*/
default List<MatchPosition> getMatchesAsList(String text) {
return getMatches(text).toList();
}
/**
* Retrieves a stream of match positions within the given text.
*
* @param text The text to search within.
* @return A stream of MatchPosition representing the positions of matches.
*/
Stream<MatchPosition> getMatches(String text);
/**
* Record representing the range of matched text along with its identifier.
*/
record MatchTextRange(DictionaryIdentifier identifier, TextRange textRange) {}
/**
* Record representing the start and end positions of a match along with its identifier.
*/
record MatchPosition(DictionaryIdentifier identifier, int startIndex, int endIndex) {}
}

View File

@ -1,31 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import java.util.List;
import java.util.Map;
import com.hankcs.algorithm.AhoCorasickDoubleArrayTrie;
public class DoubleArrayTrieDictionarySearch extends AbstractDictionarySearch {
private final AhoCorasickDoubleArrayTrie<List<DictionaryIdentifierWithKeyword>> trie;
public DoubleArrayTrieDictionarySearch(Map<String, List<DictionaryIdentifierWithKeyword>> keyWordToIdentifiersMap) {
super(keyWordToIdentifiersMap);
trie = new AhoCorasickDoubleArrayTrie<>();
trie.build(keyWordToIdentifiersMap);
}
@Override
protected void parseText(CharSequence text, HitHandler handler) {
List<AhoCorasickDoubleArrayTrie.Hit<List<DictionaryIdentifierWithKeyword>>> hits = trie.parseText(text);
for (AhoCorasickDoubleArrayTrie.Hit<List<DictionaryIdentifierWithKeyword>> hit : hits) {
handler.handle(hit.begin, hit.end, hit.value);
}
}
}

View File

@ -1,138 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
public class DoubleTrieDictionarySearch implements DictionarySearch {
private final Map<DictionaryIdentifier, List<String>> caseSensitiveEntries = new HashMap<>();
private final Map<DictionaryIdentifier, List<String>> caseInsensitiveEntries = new HashMap<>();
private final DictionaryIdentifierTrie caseSensitiveTrie;
private final DictionaryIdentifierTrie caseInsensitiveTrie;
public DoubleTrieDictionarySearch(Map<DictionaryIdentifier, List<String>> dictionaryValues) {
for (Map.Entry<DictionaryIdentifier, List<String>> entry : dictionaryValues.entrySet()) {
DictionaryIdentifier identifier = entry.getKey();
List<String> values = entry.getValue();
if (identifier.caseSensitive()) {
caseSensitiveEntries.put(identifier, values);
} else {
caseInsensitiveEntries.put(identifier, values);
}
}
this.caseSensitiveTrie = createTrie(caseSensitiveEntries, false);
this.caseInsensitiveTrie = createTrie(caseInsensitiveEntries, true);
}
private DictionaryIdentifierTrie createTrie(Map<DictionaryIdentifier, List<String>> entries, boolean ignoreCase) {
if (entries.isEmpty()) {
return null;
}
DictionaryIdentifierTrie.DictionaryIdentifierTrieBuilder builder = new DictionaryIdentifierTrie.DictionaryIdentifierTrieBuilder();
if (ignoreCase) {
builder.ignoreCase();
}
entries.forEach((identifier, values) -> {
for (String value : values) {
builder.addKeyword(value, identifier);
}
});
return builder.build();
}
public boolean atLeastOneMatches(String text) {
if (!caseSensitiveEntries.isEmpty() && caseSensitiveTrie != null && caseSensitiveTrie.containsMatch(text)) {
return true;
}
return !caseInsensitiveEntries.isEmpty() && caseInsensitiveTrie != null && caseInsensitiveTrie.containsMatch(text);
}
@Override
public Stream<MatchTextRange> getBoundaries(CharSequence text) {
List<MatchTextRange> matches = new ArrayList<>();
addMatchTextRangesForTrie(caseSensitiveEntries, caseSensitiveTrie, matches, text);
addMatchTextRangesForTrie(caseInsensitiveEntries, caseInsensitiveTrie, matches, text);
return matches.stream();
}
@Override
public Stream<MatchTextRange> getBoundaries(TextBlock textBlock) {
return getBoundaries(textBlock, textBlock.getTextRange());
}
@Override
public Stream<MatchTextRange> getBoundaries(CharSequence text, TextRange region) {
List<MatchTextRange> matches = new ArrayList<>();
addMatchTextRangesForTrie(text, region, matches, caseSensitiveEntries, caseSensitiveTrie);
addMatchTextRangesForTrie(text, region, matches, caseInsensitiveEntries, caseInsensitiveTrie);
return matches.stream();
}
@Override
public Stream<MatchPosition> getMatches(String text) {
List<MatchPosition> matches = new ArrayList<>();
addMatchPositionsForTrie(caseSensitiveEntries, caseSensitiveTrie, matches, text);
addMatchPositionsForTrie(caseInsensitiveEntries, caseInsensitiveTrie, matches, text);
return matches.stream();
}
private void addMatchTextRangesForTrie(Map<DictionaryIdentifier, List<String>> entries, DictionaryIdentifierTrie trie, List<MatchTextRange> matches, CharSequence text) {
if (!entries.isEmpty() && trie != null) {
matches.addAll(trie.parseText(text)
.stream()
.map(r -> new MatchTextRange(r.getPayload(), new TextRange(r.getStart(), r.getEnd() + 1)))
.toList());
}
}
private void addMatchTextRangesForTrie(CharSequence text,
TextRange region,
List<MatchTextRange> matches,
Map<DictionaryIdentifier, List<String>> entries,
DictionaryIdentifierTrie trie) {
if (!entries.isEmpty() && trie != null) {
CharSequence subSequence = text.subSequence(region.start(), region.end());
matches.addAll(trie.parseText(subSequence)
.stream()
.map(r -> new MatchTextRange(r.getPayload(), new TextRange(r.getStart() + region.start(), r.getEnd() + region.start() + 1)))
.toList());
}
}
private void addMatchPositionsForTrie(Map<DictionaryIdentifier, List<String>> entries, DictionaryIdentifierTrie trie, List<MatchPosition> matches, String text) {
if (!entries.isEmpty() && trie != null) {
matches.addAll(trie.parseText(text)
.stream()
.map(r -> new MatchPosition(r.getPayload(), r.getStart(), r.getEnd() + 1))
.toList());
}
}
}

View File

@ -1,46 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model.dictionary;
import java.util.Locale;
import lombok.Getter;
public class TextContext {
private final CharSequence text;
@Getter
private final String lowerText;
private final int offset;
TextContext(CharSequence text, int offset) {
this.text = text;
this.lowerText = text.toString().toLowerCase(Locale.ROOT);
this.offset = offset;
}
TextContext(CharSequence text) {
this(text, 0);
}
public int getStart(int hitBegin) {
return hitBegin + offset;
}
public int getEnd(int hitEnd) {
return hitEnd + offset;
}
public String getMatchedText(int hitBegin, int hitEnd) {
return text.subSequence(hitBegin, hitEnd).toString();
}
}

View File

@ -8,7 +8,6 @@ import static java.lang.String.format;
import java.io.IOException; import java.io.IOException;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.concurrent.CompletionException;
import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.amqp.core.Message; import org.springframework.amqp.core.Message;
@ -172,10 +171,12 @@ public class RedactionMessageReceiver {
ErrorCode errorCode = null; ErrorCode errorCode = null;
if(e instanceof CompletionException ce){ if(e instanceof DroolsTimeoutException dre){
errorCode = computeErrorCodeFromDroolsTimeoutException(ce.getCause()); if (!dre.isReported()){
} else { errorCode = ErrorCode.RULES_EXECUTION_TIMEOUT;
errorCode = computeErrorCodeFromDroolsTimeoutException(e); } else {
errorCode = ErrorCode.LOCKED_RULES;
}
} }
fileStatusProcessingUpdateClient.analysisFailed(analyzeRequest.getDossierId(), fileStatusProcessingUpdateClient.analysisFailed(analyzeRequest.getDossierId(),
@ -186,18 +187,6 @@ public class RedactionMessageReceiver {
errorCode)); errorCode));
} }
private ErrorCode computeErrorCodeFromDroolsTimeoutException(Throwable e){
ErrorCode errorCode = null;
if(e instanceof DroolsTimeoutException dre){
if (!dre.isReported()){
errorCode = ErrorCode.RULES_EXECUTION_TIMEOUT;
} else {
errorCode = ErrorCode.LOCKED_RULES;
}
}
return errorCode;
}
@RabbitHandler @RabbitHandler
@RabbitListener(queues = REDACTION_DLQ) @RabbitListener(queues = REDACTION_DLQ)

View File

@ -102,7 +102,7 @@ public class AnalysisPreparationService {
CompletableFuture.allOf(kieWrapperEntityRulesFuture, kieWrapperComponentRulesFuture, documentFuture, importedRedactionsFuture, nerEntitiesFuture).join(); CompletableFuture.allOf(kieWrapperEntityRulesFuture, kieWrapperComponentRulesFuture, documentFuture, importedRedactionsFuture, nerEntitiesFuture).join();
Dictionary dictionary = dictionaryService.getDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId()); Dictionary dictionary = getDictionary(analyzeRequest);
Document document = documentFuture.get(); Document document = documentFuture.get();
ImportedRedactions importedRedactions = importedRedactionsFuture.get(); ImportedRedactions importedRedactions = importedRedactionsFuture.get();
@ -193,7 +193,7 @@ public class AnalysisPreparationService {
taskExecutor); taskExecutor);
CompletableFuture<DictionaryAndNotFoundEntries> dictionaryAndNotFoundEntriesCompletableFuture = CompletableFuture.supplyAsync(() -> { CompletableFuture<DictionaryAndNotFoundEntries> dictionaryAndNotFoundEntriesCompletableFuture = CompletableFuture.supplyAsync(() -> {
Dictionary dictionary = dictionaryService.getDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId()); Dictionary dictionary = getDictionary(analyzeRequest);
NotFoundEntries notFoundEntries = getNotFoundEntries(analyzeRequest, reanalysisSetupData.document(), reanalysisInitialProcessingData.importedRedactions()); NotFoundEntries notFoundEntries = getNotFoundEntries(analyzeRequest, reanalysisSetupData.document(), reanalysisInitialProcessingData.importedRedactions());
return new DictionaryAndNotFoundEntries(dictionary, notFoundEntries.notFoundManualRedactionEntries(), notFoundEntries.notFoundImportedEntries()); return new DictionaryAndNotFoundEntries(dictionary, notFoundEntries.notFoundManualRedactionEntries(), notFoundEntries.notFoundImportedEntries());
}, taskExecutor); }, taskExecutor);
@ -251,6 +251,15 @@ public class AnalysisPreparationService {
} }
private Dictionary getDictionary(AnalyzeRequest analyzeRequest) {
dictionaryService.updateDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
Dictionary dictionary = dictionaryService.getDeepCopyDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
log.info("Updated Dictionaries for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
return dictionary;
}
private NotFoundEntries getNotFoundEntries(AnalyzeRequest analyzeRequest, Document document, ImportedRedactions importedRedactions) { private NotFoundEntries getNotFoundEntries(AnalyzeRequest analyzeRequest, Document document, ImportedRedactions importedRedactions) {
var notFoundManualRedactionEntries = manualRedactionEntryService.addManualRedactionEntriesAndReturnNotFoundEntries(analyzeRequest, var notFoundManualRedactionEntries = manualRedactionEntryService.addManualRedactionEntriesAndReturnNotFoundEntries(analyzeRequest,

View File

@ -1,107 +0,0 @@
package com.iqser.red.service.redaction.v1.server.service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Service;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryRepresentation;
import com.iqser.red.service.redaction.v1.server.model.dictionary.TenantDictionary;
import com.knecon.fforesight.tenantcommons.TenantContext;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class DictionaryCacheService {
private final LoadingCache<String, TenantDictionary> tenantDictionaryCache;
private final Cache<DictionaryCacheKey, Dictionary> dictionaryCache;
public DictionaryCacheService(RedactionServiceSettings settings) {
tenantDictionaryCache = CacheBuilder.newBuilder()
.maximumSize(settings.getDictionaryCacheMaximumSize())
.expireAfterAccess(settings.getDictionaryCacheExpireAfterAccessDays(), TimeUnit.DAYS)
.build(new CacheLoader<>() {
public TenantDictionary load(String key) {
return new TenantDictionary();
}
});
dictionaryCache = CacheBuilder.newBuilder()
.maximumSize(settings.getFirstLevelDictionaryCacheMaximumSize())
.expireAfterAccess(settings.getDictionaryCacheExpireAfterAccessDays(), TimeUnit.DAYS)
.build();
}
public void clearAllCaches() {
tenantDictionaryCache.invalidateAll();
dictionaryCache.invalidateAll();
}
@SneakyThrows
public DictionaryRepresentation getDossierTemplateDictionary(String dossierTemplateId) {
return tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossierTemplate()
.get(dossierTemplateId);
}
@SneakyThrows
public DictionaryRepresentation getDossierDictionary(String dossierId) {
return tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossier()
.get(dossierId);
}
@SneakyThrows
public void addDictionaryRepresentationForDossierTemplate(String dossierTemplateId, DictionaryRepresentation dictionaryRepresentation) {
tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossierTemplate().put(dossierTemplateId, dictionaryRepresentation);
}
@SneakyThrows
public void addDictionaryRepresentationForDossier(String dossierId, DictionaryRepresentation dictionaryRepresentation) {
tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossier().put(dossierId, dictionaryRepresentation);
}
@SneakyThrows
public Optional<Dictionary> getDictionary(String tenantId, String dossierId) {
return Optional.ofNullable(dictionaryCache.getIfPresent(new DictionaryCacheKey(tenantId, dossierId)));
}
@SneakyThrows
public void putDictionary(String tenantId, String dossierId, Dictionary newDictionary) {
dictionaryCache.put(new DictionaryCacheKey(tenantId, dossierId), newDictionary);
}
public record DictionaryCacheKey(String tenantId, String dossierId) {
}
}

View File

@ -7,9 +7,11 @@ import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary; import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.dictionary.SearchImplementation;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import io.micrometer.observation.annotation.Observed; import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel; import lombok.AccessLevel;
@ -23,6 +25,8 @@ import lombok.extern.slf4j.Slf4j;
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class DictionarySearchService { public class DictionarySearchService {
EntityEnrichmentService entityEnrichmentService;
@Observed(name = "DictionarySearchService", contextualName = "add-dictionary-entries") @Observed(name = "DictionarySearchService", contextualName = "add-dictionary-entries")
public void addDictionaryEntities(Dictionary dictionary, List<SemanticNode> semanticNodes) { public void addDictionaryEntities(Dictionary dictionary, List<SemanticNode> semanticNodes) {
@ -34,21 +38,40 @@ public class DictionarySearchService {
@Observed(name = "DictionarySearchService", contextualName = "add-dictionary-entries") @Observed(name = "DictionarySearchService", contextualName = "add-dictionary-entries")
public void addDictionaryEntities(Dictionary dictionary, SemanticNode node) { public void addDictionaryEntities(Dictionary dictionary, SemanticNode node) {
EntityCreationService entityCreationService = new EntityCreationService(); dictionary.getDictionaryModels()
dictionary.getDictionarySearch().getBoundaries(node.getTextBlock()) .forEach(model -> {
.filter(boundary -> entityCreationService.isValidEntityTextRange(node.getTextBlock(), boundary.textRange())) bySearchImplementationAsDictionary(model.getEntriesSearch(),
.forEach(match -> { model.getType(),
model.isHint() ? EntityType.HINT : EntityType.ENTITY,
Set<Engine> engines = match.identifier().dossierDictionaryEntry() ? Set.of(Engine.DOSSIER_DICTIONARY) : Set.of(Engine.DICTIONARY); node,
entityCreationService.byTextRangeWithEngine(match.textRange(), match.identifier().type(), match.identifier().entityType(), node, engines) model.isDossierDictionary());
.ifPresent(entity -> { bySearchImplementationAsDictionary(model.getFalsePositiveSearch(), model.getType(), EntityType.FALSE_POSITIVE, node, model.isDossierDictionary());
entity.setDictionaryEntry(true); bySearchImplementationAsDictionary(model.getFalseRecommendationsSearch(), model.getType(), EntityType.FALSE_RECOMMENDATION, node, model.isDossierDictionary());
entity.setDossierDictionaryEntry(match.identifier().dossierDictionaryEntry()); if (model.isDossierDictionary()) {
if (match.identifier().entityType().equals(EntityType.DICTIONARY_REMOVAL)) { bySearchImplementationAsDictionary(model.getDeletionEntriesSearch(), model.getType(), EntityType.DICTIONARY_REMOVAL, node, model.isDossierDictionary());
entity.ignore("DICT.0.0", "Ignore Dossier Dictionary Entity with DICTIONARY_REMOVAL entity type"); }
}
});
}); });
} }
public void bySearchImplementationAsDictionary(SearchImplementation searchImplementation,
String type,
EntityType entityType,
SemanticNode node,
boolean isDossierDictionaryEntry) {
Set<Engine> engines = isDossierDictionaryEntry ? Set.of(Engine.DOSSIER_DICTIONARY) : Set.of(Engine.DICTIONARY);
EntityCreationService entityCreationService = new EntityCreationService(entityEnrichmentService);
searchImplementation.getBoundaries(node.getTextBlock())
.filter(boundary -> entityCreationService.isValidEntityTextRange(node.getTextBlock(), boundary))
.forEach(bounds -> entityCreationService.byTextRangeWithEngine(bounds, type, entityType, node, engines)
.ifPresent(entity -> {
entity.setDictionaryEntry(true);
entity.setDossierDictionaryEntry(isDossierDictionaryEntry);
if (entityType.equals(EntityType.DICTIONARY_REMOVAL)) {
entity.ignore("DICT.0.0", "Ignore Dossier Dictionary Entity with DICTIONARY_REMOVAL entity type");
}
}));
}
} }

View File

@ -1,6 +1,7 @@
package com.iqser.red.service.redaction.v1.server.service; package com.iqser.red.service.redaction.v1.server.service;
import java.util.Collections; import java.awt.Color;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -8,27 +9,38 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.iqser.red.service.dictionarymerge.commons.CommonsDictionaryModel;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntry;
import com.iqser.red.service.dictionarymerge.commons.DictionaryEntryModel; 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.persistence.service.v1.api.shared.model.dossiertemplate.type.Type; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.client.DictionaryClient; import com.iqser.red.service.redaction.v1.server.client.DictionaryClient;
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary; import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryEntries; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryEntries;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryFactory;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrement; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrement;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrementValue; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrementValue;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryModel; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryModel;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryRepresentation; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryRepresentation;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryVersion; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryVersion;
import com.iqser.red.service.redaction.v1.server.model.dictionary.TenantDictionary;
import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantcommons.TenantContext;
import feign.FeignException; import feign.FeignException;
import io.micrometer.core.annotation.Timed; import io.micrometer.core.annotation.Timed;
import io.micrometer.observation.annotation.Observed; import io.micrometer.observation.annotation.Observed;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -38,40 +50,34 @@ import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor @RequiredArgsConstructor
public class DictionaryService { public class DictionaryService {
public static final String DEFAULT_COLOR = "#cccccc";
private final DictionaryClient dictionaryClient; private final DictionaryClient dictionaryClient;
private final DictionaryCacheService dictionaryCacheService;
private final DictionaryFactory dictionaryFactory; private final RedactionServiceSettings settings;
private final DictionaryMergeService dictionaryMergeService;
private LoadingCache<String, TenantDictionary> tenantDictionaryCache;
@SneakyThrows @PostConstruct
@Observed(name = "DictionaryService", contextualName = "get-dictionary") protected void createCache() {
@Timed("redactmanager_getDictionary")
public Dictionary getDictionary(String dossierTemplateId, String dossierId) {
String tenantId = TenantContext.getTenantId(); tenantDictionaryCache = CacheBuilder.newBuilder()
.maximumSize(settings.getDictionaryCacheMaximumSize())
.expireAfterAccess(settings.getDictionaryCacheExpireAfterAccessDays(), TimeUnit.DAYS)
.build(new CacheLoader<>() {
public TenantDictionary load(String key) {
Optional<Dictionary> cachedDictionary = dictionaryCacheService.getDictionary(tenantId, dossierId); return new TenantDictionary();
}
});
}
if (cachedDictionary.isPresent()) {
log.debug("Dictionary found in cache");
boolean isUpToDate = checkIfDictionaryIsUpToDate(dossierTemplateId, dossierId, cachedDictionary.get());
if (isUpToDate) {
log.info("Returning cached Dictionary for tenantId: {}, dossierId: {}", tenantId, dossierId);
return cachedDictionary.get();
} else {
log.debug("Cached Dictionary is outdated for tenantId: {}, dossierId: {}", tenantId, dossierId);
}
} else {
log.info("No cached Dictionary found for tenantId: {}, dossierId: {}", tenantId, dossierId);
}
DictionaryVersion latestVersion = updateDictionary(dossierTemplateId, dossierId); public void clearTenantDictionaryCache() {
Dictionary newDictionary = buildDictionary(dossierTemplateId, dossierId, latestVersion);
dictionaryCacheService.putDictionary(tenantId, dossierId, newDictionary); tenantDictionaryCache.invalidateAll();
log.debug("Cached new Dictionary for tenantId: {}, dossierId: {}", tenantId, dossierId);
return newDictionary;
} }
@ -81,24 +87,19 @@ public class DictionaryService {
public DictionaryVersion updateDictionary(String dossierTemplateId, String dossierId) { public DictionaryVersion updateDictionary(String dossierTemplateId, String dossierId) {
log.debug("Updating dictionary data for dossierTemplate {} and dossier {}", dossierTemplateId, dossierId); log.debug("Updating dictionary data for dossierTemplate {} and dossier {}", dossierTemplateId, dossierId);
long dossierTemplateDictionaryVersion = dictionaryClient.getVersion(dossierTemplateId);
// Update template dictionary var dossierTemplateDictionary = getDossierTemplateDictionary(dossierTemplateId);
long latestTemplateVersion = dictionaryClient.getVersion(dossierTemplateId); if (dossierTemplateDictionary == null || dossierTemplateDictionaryVersion > dossierTemplateDictionary.getDictionaryVersion()) {
DictionaryRepresentation templateDictRep = dictionaryCacheService.getDossierTemplateDictionary(dossierTemplateId); updateDictionaryEntry(dossierTemplateId, dossierTemplateDictionaryVersion, getVersion(dossierTemplateDictionary), null);
if (templateDictRep == null || latestTemplateVersion > templateDictRep.getDictionaryVersion()) {
updateDictionaryEntry(dossierTemplateId, latestTemplateVersion, templateDictRep != null ? templateDictRep.getDictionaryVersion() : null, null);
} }
// Update dossier dictionary long dossierDictionaryVersion = dictionaryClient.getVersionForDossier(dossierId);
long latestDossierVersion = dictionaryClient.getVersionForDossier(dossierId); var dossierDictionary = getDossierDictionary(dossierId);
DictionaryRepresentation dossierDictRep = dictionaryCacheService.getDossierDictionary(dossierId); if (dossierDictionary == null || dossierDictionaryVersion > dossierDictionary.getDictionaryVersion()) {
updateDictionaryEntry(dossierTemplateId, dossierDictionaryVersion, getVersion(dossierDictionary), dossierId);
if (dossierDictRep == null || latestDossierVersion > dossierDictRep.getDictionaryVersion()) {
updateDictionaryEntry(dossierTemplateId, latestDossierVersion, dossierDictRep != null ? dossierDictRep.getDictionaryVersion() : null, dossierId);
} }
return DictionaryVersion.builder().dossierTemplateVersion(latestTemplateVersion).dossierVersion(latestDossierVersion).build(); return DictionaryVersion.builder().dossierTemplateVersion(dossierTemplateDictionaryVersion).dossierVersion(dossierDictionaryVersion).build();
} }
@ -107,21 +108,57 @@ public class DictionaryService {
@Timed("redactmanager_getDictionaryIncrements") @Timed("redactmanager_getDictionaryIncrements")
public DictionaryIncrement getDictionaryIncrements(String dossierTemplateId, DictionaryVersion fromVersion, String dossierId) { public DictionaryIncrement getDictionaryIncrements(String dossierTemplateId, DictionaryVersion fromVersion, String dossierId) {
DictionaryVersion latestVersion = updateDictionary(dossierTemplateId, dossierId); DictionaryVersion version = updateDictionary(dossierTemplateId, dossierId);
Set<DictionaryIncrementValue> newValues = Collections.synchronizedSet(new HashSet<>()); Set<DictionaryIncrementValue> newValues = new HashSet<>();
List<DictionaryModel> templateDictionaries = dictionaryCacheService.getDossierTemplateDictionary(dossierTemplateId).getDictionary(); List<DictionaryModel> dictionaryModels = getDossierTemplateDictionary(dossierTemplateId).getDictionary();
templateDictionaries.parallelStream() dictionaryModels.forEach(dictionaryModel -> {
.forEach(dictionaryModel -> dictionaryModel.addNewEntries(fromVersion.getDossierTemplateVersion(), newValues)); dictionaryModel.getEntries()
.forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierTemplateVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
dictionaryModel.getFalsePositives()
.forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierTemplateVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
dictionaryModel.getFalseRecommendations()
.forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierTemplateVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
});
if (dossierDictionaryExists(dossierId)) { if (dossierDictionaryExists(dossierId)) {
List<DictionaryModel> dossierDictionaries = dictionaryCacheService.getDossierDictionary(dossierId).getDictionary(); dictionaryModels = getDossierDictionary(dossierId).getDictionary();
dossierDictionaries.parallelStream() dictionaryModels.forEach(dictionaryModel -> {
.forEach(dictionaryModel -> dictionaryModel.addNewEntries(fromVersion.getDossierVersion(), newValues)); dictionaryModel.getEntries()
.forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
dictionaryModel.getFalsePositives()
.forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
dictionaryModel.getFalseRecommendations()
.forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
});
} }
return new DictionaryIncrement(newValues, latestVersion); return new DictionaryIncrement(newValues, version);
} }
@ -131,164 +168,342 @@ public class DictionaryService {
try { try {
DictionaryRepresentation dictionaryRepresentation = new DictionaryRepresentation(); DictionaryRepresentation dictionaryRepresentation = new DictionaryRepresentation();
List<Type> typeResponse; var typeResponse = dossierId == null ? dictionaryClient.getAllTypesForDossierTemplate(dossierTemplateId, currentVersion, true) : dictionaryClient.getAllTypesForDossier(
if (dossierId == null) { dossierId,
typeResponse = dictionaryClient.getAllTypesForDossierTemplate(dossierTemplateId, currentVersion, true); currentVersion,
} else { true);
typeResponse = dictionaryClient.getAllTypesForDossier(dossierId, currentVersion, true);
}
if (CollectionUtils.isNotEmpty(typeResponse)) { if (CollectionUtils.isNotEmpty(typeResponse)) {
String tenantId = TenantContext.getTenantId(); String tenantId = TenantContext.getTenantId();
List<DictionaryModel> dictionary = typeResponse.stream() List<DictionaryModel> dictionary = typeResponse.stream()
.parallel() .parallel()
.map(t -> mapTypeToDictionaryModel(tenantId, t, dossierTemplateId, dossierId)) .map(t -> {
TenantContext.setTenantId(tenantId);
Optional<DictionaryModel> optionalOldModel;
if (dossierId == null) {
var representation = getDossierTemplateDictionary(dossierTemplateId);
optionalOldModel = representation != null ? representation.getDictionary()
.stream()
.filter(f -> f.getType().equals(t.getType()))
.findAny() : Optional.empty();
} else {
var representation = getDossierDictionary(dossierId);
optionalOldModel = representation != null ? representation.getDictionary()
.stream()
.filter(f -> f.getType().equals(t.getType()))
.findAny() : Optional.empty();
}
Set<DictionaryEntryModel> entries = new HashSet<>();
Set<DictionaryEntryModel> falsePositives = new HashSet<>();
Set<DictionaryEntryModel> falseRecommendations = new HashSet<>();
DictionaryEntries newEntries = mapEntries(t);
var newValues = newEntries.getEntries()
.stream()
.map(DictionaryEntry::getValue)
.collect(Collectors.toSet());
var newFalsePositivesValues = newEntries.getFalsePositives()
.stream()
.map(DictionaryEntry::getValue)
.collect(Collectors.toSet());
var newFalseRecommendationsValues = newEntries.getFalseRecommendations()
.stream()
.map(DictionaryEntry::getValue)
.collect(Collectors.toSet());
optionalOldModel.ifPresent(oldDictionaryModel -> {
});
if (optionalOldModel.isPresent()) {
var oldModel = optionalOldModel.get();
if (oldModel.isCaseInsensitive() && !t.isCaseInsensitive()) {
// add old entries from existing DictionaryModel but exclude lower case representation
entries.addAll(oldModel.getEntries()
.stream()
.filter(f -> !newValues.stream()
.map(s -> s.toLowerCase(Locale.ROOT))
.toList().contains(f.getValue()))
.toList());
falsePositives.addAll(oldModel.getFalsePositives()
.stream()
.filter(f -> !newFalsePositivesValues.stream()
.map(s -> s.toLowerCase(Locale.ROOT))
.toList().contains(f.getValue()))
.toList());
falseRecommendations.addAll(oldModel.getFalseRecommendations()
.stream()
.filter(f -> !newFalseRecommendationsValues.stream()
.map(s -> s.toLowerCase(Locale.ROOT))
.toList().contains(f.getValue()))
.toList());
} else if (!oldModel.isCaseInsensitive() && t.isCaseInsensitive()) {
// add old entries from existing DictionaryModel but exclude upper case representation
entries.addAll(oldModel.getEntries()
.stream()
.filter(f -> !newValues.contains(f.getValue().toLowerCase(Locale.ROOT)))
.toList());
falsePositives.addAll(oldModel.getFalsePositives()
.stream()
.filter(f -> !newFalsePositivesValues.contains(f.getValue().toLowerCase(Locale.ROOT)))
.toList());
falseRecommendations.addAll(oldModel.getFalseRecommendations()
.stream()
.filter(f -> !newFalseRecommendationsValues.contains(f.getValue().toLowerCase(Locale.ROOT)))
.toList());
} else {
// add old entries from existing DictionaryModel
entries.addAll(oldModel.getEntries()
.stream()
.filter(f -> !newValues.contains(f.getValue()))
.toList());
falsePositives.addAll(oldModel.getFalsePositives()
.stream()
.filter(f -> !newFalsePositivesValues.contains(f.getValue()))
.toList());
falseRecommendations.addAll(oldModel.getFalseRecommendations()
.stream()
.filter(f -> !newFalseRecommendationsValues.contains(f.getValue()))
.toList());
}
}
// Add Increments
entries.addAll(newEntries.getEntries());
falsePositives.addAll(newEntries.getFalsePositives());
falseRecommendations.addAll(newEntries.getFalseRecommendations());
return new DictionaryModel(t.getType(),
t.getRank(),
convertColor(t.getHexColor()),
t.isCaseInsensitive(),
t.isHint(),
entries,
falsePositives,
falseRecommendations,
dossierId != null);
})
.sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed()) .sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed())
.collect(Collectors.toList()); .collect(Collectors.toList());
dictionary.forEach(dm -> dictionaryRepresentation.getLocalAccessMap().put(dm.getType(), dm)); dictionary.forEach(dm -> dictionaryRepresentation.getLocalAccessMap().put(dm.getType(), dm));
Colors colors = dictionaryClient.getColors(dossierTemplateId);
dictionaryRepresentation.setDefaultColor(convertColor(DEFAULT_COLOR));
dictionaryRepresentation.setRequestAddColor(convertColor(colors.getRequestAddColor()));
dictionaryRepresentation.setRequestRemoveColor(convertColor(colors.getRequestRemoveColor()));
dictionaryRepresentation.setNotRedactedColor(convertColor(colors.getSkippedColor()));
dictionaryRepresentation.setDossierTemplateId(dossierTemplateId); dictionaryRepresentation.setDossierTemplateId(dossierTemplateId);
dictionaryRepresentation.setDictionaryVersion(version); dictionaryRepresentation.setDictionaryVersion(version);
dictionaryRepresentation.setDictionary(dictionary); dictionaryRepresentation.setDictionary(dictionary);
if (dossierId == null) { if (dossierId == null) {
dictionaryCacheService.addDictionaryRepresentationForDossierTemplate(dossierTemplateId, dictionaryRepresentation); addDictionaryRepresentationForDossierTemplate(dossierTemplateId, dictionaryRepresentation);
} else { } else {
dictionaryCacheService.addDictionaryRepresentationForDossier(dossierId, dictionaryRepresentation); addDictionaryRepresentationForDossier(dossierId, dictionaryRepresentation);
} }
} }
} catch (FeignException e) { } catch (FeignException e) {
log.warn("Got some unknown FeignException", e); log.warn("Got some unknown feignException", e);
throw e; throw e;
} }
} }
private DictionaryModel mapTypeToDictionaryModel(String tenantId, Type type, String dossierTemplateId, String dossierId) {
TenantContext.setTenantId(tenantId);
Optional<DictionaryModel> optionalOldModel = getExistingDictionaryModel(type.getType(), dossierTemplateId, dossierId);
DictionaryEntries newEntries = mapEntries(type);
Set<DictionaryEntryModel> combinedEntries = new HashSet<>(newEntries.getEntries());
Set<DictionaryEntryModel> combinedFalsePositives = new HashSet<>(newEntries.getFalsePositives());
Set<DictionaryEntryModel> combinedFalseRecommendations = new HashSet<>(newEntries.getFalseRecommendations());
optionalOldModel.ifPresent(oldModel -> oldModel.handleOldEntries(type, newEntries, combinedEntries, combinedFalsePositives, combinedFalseRecommendations));
combinedEntries.addAll(newEntries.getEntries());
combinedFalsePositives.addAll(newEntries.getFalsePositives());
combinedFalseRecommendations.addAll(newEntries.getFalseRecommendations());
return new DictionaryModel(type.getType(),
type.getRank(),
null,
type.isCaseInsensitive(),
type.isHint(),
combinedEntries,
combinedFalsePositives,
combinedFalseRecommendations,
dossierId != null);
}
private Optional<DictionaryModel> getExistingDictionaryModel(String type, String dossierTemplateId, String dossierId) {
DictionaryRepresentation representation = dossierId == null ? //
dictionaryCacheService.getDossierTemplateDictionary(dossierTemplateId) : dictionaryCacheService.getDossierDictionary(dossierId);
return representation != null ? representation.getDictionary()
.stream()
.filter(f -> f.getType().equals(type))
.findAny() : Optional.empty();
}
private DictionaryEntries mapEntries(Type type) { private DictionaryEntries mapEntries(Type type) {
Set<DictionaryEntryModel> entries = type.getEntries() != null ? type.getEntries() Set<DictionaryEntryModel> entries = type.getEntries() != null ? new HashSet<>(type.getEntries()
.stream() .stream()
.map(DictionaryEntryModel::new) .map(DictionaryEntryModel::new)
.collect(Collectors.toSet()) : new HashSet<>(); .collect(Collectors.toSet())) : new HashSet<>();
Set<DictionaryEntryModel> falsePositives = type.getFalsePositiveEntries() != null ? new HashSet<>(type.getFalsePositiveEntries()
Set<DictionaryEntryModel> falsePositives = type.getFalsePositiveEntries() != null ? type.getFalsePositiveEntries() .stream()
.stream() .map(DictionaryEntryModel::new)
.map(DictionaryEntryModel::new) .collect(Collectors.toSet())) : new HashSet<>();
.collect(Collectors.toSet()) : new HashSet<>(); Set<DictionaryEntryModel> falseRecommendations = type.getFalseRecommendationEntries() != null ? new HashSet<>(type.getFalseRecommendationEntries()
.stream()
Set<DictionaryEntryModel> falseRecommendations = type.getFalseRecommendationEntries() != null ? type.getFalseRecommendationEntries() .map(DictionaryEntryModel::new)
.stream() .collect(Collectors.toSet())) : new HashSet<>();
.map(DictionaryEntryModel::new)
.collect(Collectors.toSet()) : new HashSet<>();
if (type.isCaseInsensitive()) { if (type.isCaseInsensitive()) {
entries.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT))); entries.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT)));
falsePositives.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT))); falsePositives.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT)));
falseRecommendations.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT))); falseRecommendations.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT)));
} }
log.debug("Dictionary update returned {} entries {} falsePositives and {} falseRecommendations for type {}",
log.debug("Dictionary update returned {} entries, {} falsePositives, and {} falseRecommendations for type {}",
entries.size(), entries.size(),
falsePositives.size(), falsePositives.size(),
falseRecommendations.size(), falseRecommendations.size(),
type.getType()); entries);
return new DictionaryEntries(entries, falsePositives, falseRecommendations); return new DictionaryEntries(entries, falsePositives, falseRecommendations);
} }
private float[] convertColor(String hex) {
Color color = Color.decode(hex);
return new float[]{color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f};
}
@SneakyThrows
public float[] getColor(String type, String dossierTemplateId) {
DictionaryModel model = getDossierTemplateDictionary(dossierTemplateId).getLocalAccessMap()
.get(type);
if (model != null) {
return model.getColor();
}
return getDossierTemplateDictionary(dossierTemplateId).getDefaultColor();
}
@SneakyThrows @SneakyThrows
public boolean isHint(String type, String dossierTemplateId) { public boolean isHint(String type, String dossierTemplateId) {
DictionaryModel model = dictionaryCacheService.getDossierTemplateDictionary(dossierTemplateId).getLocalAccessMap() DictionaryModel model = getDossierTemplateDictionary(dossierTemplateId).getLocalAccessMap()
.get(type); .get(type);
return model != null && model.isHint(); if (model != null) {
return model.isHint();
}
return false;
} }
private Dictionary buildDictionary(String dossierTemplateId, String dossierId, DictionaryVersion dictionaryVersion) { @SneakyThrows
@Timed("redactmanager_getDeepCopyDictionary")
@Observed(name = "DictionaryService", contextualName = "deep-copy-dictionary")
public Dictionary getDeepCopyDictionary(String dossierTemplateId, String dossierId) {
List<DictionaryModel> mergedDictionaries = new LinkedList<>(); List<DictionaryModel> mergedDictionaries = new LinkedList<>();
DictionaryRepresentation templateDictRep = dictionaryCacheService.getDossierTemplateDictionary(dossierTemplateId); DictionaryRepresentation dossierTemplateRepresentation = getDossierTemplateDictionary(dossierTemplateId);
if (templateDictRep != null) { List<DictionaryModel> dossierTemplateDictionaries = dossierTemplateRepresentation.getDictionary();
templateDictRep.getDictionary() dossierTemplateDictionaries.forEach(dm -> mergedDictionaries.add(SerializationUtils.clone(dm)));
.forEach(dm -> mergedDictionaries.add(dm.clone()));
}
// add dossier
long dossierDictionaryVersion = -1;
if (dossierDictionaryExists(dossierId)) { if (dossierDictionaryExists(dossierId)) {
DictionaryRepresentation dossierDictRep = dictionaryCacheService.getDossierDictionary(dossierId); DictionaryRepresentation dossierRepresentation = getDossierDictionary(dossierId);
if (dossierDictRep != null) { List<DictionaryModel> dossierDictionaries = dossierRepresentation.getDictionary();
dossierDictRep.getDictionary() dossierDictionaries.forEach(dm -> mergedDictionaries.add(SerializationUtils.clone(dm)));
.forEach(dm -> mergedDictionaries.add(dm.clone())); return getDictionary(mergedDictionaries, dossierTemplateRepresentation, dossierRepresentation.getDictionaryVersion());
} } else {
return getDictionary(mergedDictionaries, dossierTemplateRepresentation, dossierDictionaryVersion);
} }
return dictionaryFactory.create(mergedDictionaries.stream()
.sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed())
.collect(Collectors.toList()), dictionaryVersion);
} }
private boolean checkIfDictionaryIsUpToDate(String dossierTemplateId, String dossierId, Dictionary cachedDictionary) { private Dictionary getDictionary(List<DictionaryModel> mergedDictionaries, DictionaryRepresentation dossierTemplateRepresentation, long dossierDictionaryVersion) {
long latestTemplateVersion = dictionaryClient.getVersion(dossierTemplateId); return new Dictionary(mergedDictionaries.stream()
long latestDossierVersion = dictionaryClient.getVersionForDossier(dossierId); .sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed())
.collect(Collectors.toList()),
DictionaryVersion.builder()
.dossierTemplateVersion(dossierTemplateRepresentation.getDictionaryVersion())
.dossierVersion(dossierDictionaryVersion)
.build());
}
DictionaryVersion cachedVersion = cachedDictionary.getVersion();
return (cachedVersion.getDossierTemplateVersion() >= latestTemplateVersion) && (cachedVersion.getDossierVersion() >= latestDossierVersion); @SneakyThrows
public float[] getNotRedactedColor(String dossierTemplateId) {
return getDossierTemplateDictionary(dossierTemplateId).getNotRedactedColor();
}
@SneakyThrows
private void addDictionaryRepresentationForDossierTemplate(String dossierTemplateId, DictionaryRepresentation dictionaryRepresentation) {
tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossierTemplate().put(dossierTemplateId, dictionaryRepresentation);
}
@SneakyThrows
private void addDictionaryRepresentationForDossier(String dossierId, DictionaryRepresentation dictionaryRepresentation) {
tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossier().put(dossierId, dictionaryRepresentation);
}
@SneakyThrows
private DictionaryRepresentation getDossierTemplateDictionary(String dossierTemplateId) {
return tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossierTemplate()
.get(dossierTemplateId);
}
@SneakyThrows
private DictionaryRepresentation getDossierDictionary(String dossierId) {
return tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossier()
.get(dossierId);
} }
@SneakyThrows @SneakyThrows
private boolean dossierDictionaryExists(String dossierId) { private boolean dossierDictionaryExists(String dossierId) {
DictionaryRepresentation dossierDictRep = dictionaryCacheService.getDossierDictionary(dossierId); return tenantDictionaryCache.get(TenantContext.getTenantId()).getDictionariesByDossier().containsKey(dossierId);
return dossierDictRep != null; }
private Long getVersion(DictionaryRepresentation dictionaryRepresentation) {
if (dictionaryRepresentation == null) {
return null;
} else {
return dictionaryRepresentation.getDictionaryVersion();
}
}
private List<CommonsDictionaryModel> convertDictionaryModel(List<DictionaryModel> dictionaries) {
return dictionaries.stream()
.map(d -> CommonsDictionaryModel.builder()
.type(d.getType())
.rank(d.getRank())
.color(d.getColor())
.caseInsensitive(d.isCaseInsensitive())
.hint(d.isHint())
.isDossierDictionary(d.isDossierDictionary())
.entries(d.getEntries())
.falsePositives(d.getFalsePositives())
.falseRecommendations(d.getFalseRecommendations())
.build())
.collect(Collectors.toList());
}
private List<DictionaryModel> convertCommonsDictionaryModel(List<CommonsDictionaryModel> commonsDictionaries) {
return commonsDictionaries.stream()
.map(cd -> new DictionaryModel(cd.getType(),
cd.getRank(),
cd.getColor(),
cd.isCaseInsensitive(),
cd.isHint(),
cd.getEntries(),
cd.getFalsePositives(),
cd.getFalseRecommendations(),
cd.isDossierDictionary()))
.collect(Collectors.toList());
}
public List<Type> getAllTypes(String dossierTemplateId, String dossierId) {
List<Type> allTypes = dictionaryClient.getAllTypesForDossierTemplate(dossierTemplateId, null, false);
allTypes.addAll(dictionaryClient.getAllTypesForDossier(dossierId, null, false));
return allTypes;
} }
} }

View File

@ -14,6 +14,7 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -29,6 +30,7 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock; import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.utils.RectangleTransformations; import com.iqser.red.service.redaction.v1.server.utils.RectangleTransformations;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -37,7 +39,15 @@ import lombok.extern.slf4j.Slf4j;
@Service @Service
public class EntityFindingUtility { public class EntityFindingUtility {
EntityCreationService entityCreationService = new EntityCreationService(); EntityCreationService entityCreationService;
@Autowired
public EntityFindingUtility(EntityEnrichmentService entityEnrichmentService) {
entityCreationService = new EntityCreationService(entityEnrichmentService);
}
public Optional<TextEntity> findClosestEntityAndReturnEmptyIfNotFound(PrecursorEntity precursorEntity, public Optional<TextEntity> findClosestEntityAndReturnEmptyIfNotFound(PrecursorEntity precursorEntity,
Map<String, List<TextEntity>> entitiesWithSameValue, Map<String, List<TextEntity>> entitiesWithSameValue,
@ -181,7 +191,7 @@ public class EntityFindingUtility {
return textBlocks.stream() return textBlocks.stream()
.flatMap(searchImplementation::getBoundaries) .flatMap(searchImplementation::getBoundaries)
.map(boundary -> entityCreationService.byTextRangeWithEngine(boundary, "temp", EntityType.TEMPORARY, node, Collections.emptySet())) .map(boundary -> entityCreationService.byTextRangeWithEngine(boundary, "temp", EntityType.ENTITY, node, Collections.emptySet()))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.distinct() .distinct()
@ -208,7 +218,7 @@ public class EntityFindingUtility {
return textBlocks.stream() return textBlocks.stream()
.flatMap(tb -> searchImplementation.getBoundaries(tb) .flatMap(tb -> searchImplementation.getBoundaries(tb)
.filter(textRange -> entityCreationService.isValidEntityTextRange(tb, textRange))) .filter(textRange -> entityCreationService.isValidEntityTextRange(tb, textRange)))
.map(boundary -> entityCreationService.byTextRangeWithEngine(boundary, "temp", EntityType.TEMPORARY, document, Collections.emptySet())) .map(boundary -> entityCreationService.byTextRangeWithEngine(boundary, "temp", EntityType.ENTITY, document, Collections.emptySet()))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.distinct() .distinct()
@ -222,7 +232,7 @@ public class EntityFindingUtility {
return searchImplementation.getBoundaries(document.getTextBlock()) return searchImplementation.getBoundaries(document.getTextBlock())
.filter(textRange -> entityCreationService.isValidEntityTextRange(document.getTextBlock(), textRange)) .filter(textRange -> entityCreationService.isValidEntityTextRange(document.getTextBlock(), textRange))
.map(boundary -> entityCreationService.byTextRangeWithEngine(boundary, "temp", EntityType.TEMPORARY, document, Collections.emptySet())) .map(boundary -> entityCreationService.byTextRangeWithEngine(boundary, "temp", EntityType.ENTITY, document, Collections.emptySet()))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.distinct() .distinct()

View File

@ -431,11 +431,12 @@ public class EntityLogCreatorService {
private static EntryType getEntryType(EntityType entityType) { private static EntryType getEntryType(EntityType entityType) {
return switch (entityType) { return switch (entityType) {
case ENTITY, TEMPORARY -> EntryType.ENTITY; case ENTITY -> EntryType.ENTITY;
case HINT -> EntryType.HINT; case HINT -> EntryType.HINT;
case FALSE_POSITIVE, DICTIONARY_REMOVAL -> EntryType.FALSE_POSITIVE; case FALSE_POSITIVE -> EntryType.FALSE_POSITIVE;
case RECOMMENDATION -> EntryType.RECOMMENDATION; case RECOMMENDATION -> EntryType.RECOMMENDATION;
case FALSE_RECOMMENDATION -> EntryType.FALSE_RECOMMENDATION; case FALSE_RECOMMENDATION -> EntryType.FALSE_RECOMMENDATION;
case DICTIONARY_REMOVAL -> EntryType.FALSE_POSITIVE;
}; };
} }

View File

@ -3,7 +3,6 @@ package com.iqser.red.service.redaction.v1.server.service;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -18,8 +17,6 @@ import com.google.common.collect.Sets;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRecategorization; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRecategorization;
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.ManualResizeRedaction;
import com.iqser.red.service.redaction.v1.server.model.PrecursorEntity; import com.iqser.red.service.redaction.v1.server.model.PrecursorEntity;
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryModel;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.PositionOnPage; import com.iqser.red.service.redaction.v1.server.model.document.entity.PositionOnPage;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
@ -47,19 +44,18 @@ public class ManualChangesApplicationService {
public void recategorize(IEntity entityToBeReCategorized, ManualRecategorization manualRecategorization) { public void recategorize(IEntity entityToBeReCategorized, ManualRecategorization manualRecategorization) {
entityToBeReCategorized.getMatchedRuleList().clear(); entityToBeReCategorized.getMatchedRuleList().clear();
entityToBeReCategorized.getManualOverwrite().addChange(manualRecategorization);
if (manualRecategorization.getType() == null) { if (manualRecategorization.getType() == null) {
return; return;
} }
if (entityToBeReCategorized instanceof Image image) { if (entityToBeReCategorized instanceof Image image) {
entityToBeReCategorized.getManualOverwrite().addChange(manualRecategorization);
image.setImageType(ImageType.fromString(manualRecategorization.getType())); image.setImageType(ImageType.fromString(manualRecategorization.getType()));
return; return;
} }
if (entityToBeReCategorized instanceof TextEntity textEntity) { if (entityToBeReCategorized instanceof TextEntity textEntity) {
textEntity.addManualChange(manualRecategorization);
textEntity.setType(manualRecategorization.getType()); textEntity.setType(manualRecategorization.getType());
} }
} }
@ -80,8 +76,6 @@ public class ManualChangesApplicationService {
@Deprecated @Deprecated
public void resizeEntityAndReinsert(TextEntity entityToBeResized, ManualResizeRedaction manualResizeRedaction) { public void resizeEntityAndReinsert(TextEntity entityToBeResized, ManualResizeRedaction manualResizeRedaction) {
entityToBeResized.notifyEntityRemoved();
PositionOnPage positionOnPageToBeResized = entityToBeResized.getPositionsOnPagePerPage() PositionOnPage positionOnPageToBeResized = entityToBeResized.getPositionsOnPagePerPage()
.stream() .stream()
.filter(redactionPosition -> redactionPosition.getId().equals(manualResizeRedaction.getAnnotationId())) .filter(redactionPosition -> redactionPosition.getId().equals(manualResizeRedaction.getAnnotationId()))
@ -109,7 +103,7 @@ public class ManualChangesApplicationService {
.stream() .stream()
.flatMap(Collection::stream) .flatMap(Collection::stream)
.forEach(TextEntity::removeFromGraph); .forEach(TextEntity::removeFromGraph);
break; return;
} }
possibleEntities.values() possibleEntities.values()
@ -120,15 +114,9 @@ public class ManualChangesApplicationService {
if (node.hasParent()) { if (node.hasParent()) {
node = node.getParent(); node = node.getParent();
} else { } else {
node = null; break;
} }
} }
entityToBeResized.getRelations().keySet()
.forEach(textEntity -> textEntity.getRelations().remove(entityToBeResized));
entityToBeResized.setRelations(new HashMap<>());
entityToBeResized.computeRelations();
entityToBeResized.notifyEntityInserted();
} }
@ -155,7 +143,7 @@ public class ManualChangesApplicationService {
entityToBeResized.setTextRange(closestEntity.getTextRange()); entityToBeResized.setTextRange(closestEntity.getTextRange());
entityToBeResized.setTextAfter(closestEntity.getTextAfter()); entityToBeResized.setTextAfter(closestEntity.getTextAfter());
entityToBeResized.setTextBefore(closestEntity.getTextBefore()); entityToBeResized.setTextBefore(closestEntity.getTextBefore());
entityToBeResized.setDuplicateTextRanges(new HashSet<>(closestEntity.getDuplicateTextRanges())); entityToBeResized.setDuplicateTextRanges(new ArrayList<>(closestEntity.getDuplicateTextRanges()));
entityToBeResized.setValue(closestEntity.getValue()); entityToBeResized.setValue(closestEntity.getValue());
entityToBeResized.setPages(newIntersectingPages); entityToBeResized.setPages(newIntersectingPages);
} }

View File

@ -1,9 +1,17 @@
package com.iqser.red.service.redaction.v1.server.service.document; package com.iqser.red.service.redaction.v1.server.service.document;
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.addEntityToNodeEntitySets;
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.addToPages;
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.allEntitiesIntersectAndHaveSameTypes;
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.checkIfBothStartAndEndAreEmpty;
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.findIntersectingSubNodes;
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.toLineAfterTextRange;
import static com.iqser.red.service.redaction.v1.server.service.document.EntityCreationUtility.truncateEndIfLineBreakIsBetween;
import static com.iqser.red.service.redaction.v1.server.utils.SeparatorUtils.boundaryIsSurroundedBySeparators; import static com.iqser.red.service.redaction.v1.server.utils.SeparatorUtils.boundaryIsSurroundedBySeparators;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -13,6 +21,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.kie.api.runtime.KieSession;
import com.google.common.base.Functions; import com.google.common.base.Functions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;
@ -29,26 +38,35 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNo
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell; import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock; import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
import com.iqser.red.service.redaction.v1.server.service.drools.KieSessionUpdater;
import com.iqser.red.service.redaction.v1.server.utils.EntityCreationUtility;
import com.iqser.red.service.redaction.v1.server.utils.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.utils.RectangleTransformations; import com.iqser.red.service.redaction.v1.server.utils.RectangleTransformations;
import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility; import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility;
import com.iqser.red.service.redaction.v1.server.utils.exception.NotFoundException; import com.iqser.red.service.redaction.v1.server.utils.exception.NotFoundException;
import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@NoArgsConstructor @RequiredArgsConstructor
public class EntityCreationService { public class EntityCreationService {
KieSessionUpdater kieSessionUpdater; private final EntityEnrichmentService entityEnrichmentService;
private final KieSession kieSession;
private final Set<SemanticNode> nodesInKieSession; // empty set means all nodes are in kieSession
public EntityCreationService(KieSessionUpdater kieSessionUpdater) { public EntityCreationService(EntityEnrichmentService entityEnrichmentService) {
this.kieSessionUpdater = kieSessionUpdater; this.entityEnrichmentService = entityEnrichmentService;
this.kieSession = null;
this.nodesInKieSession = Collections.emptySet();
}
public EntityCreationService(EntityEnrichmentService entityEnrichmentService, KieSession kieSession) {
this.entityEnrichmentService = entityEnrichmentService;
this.kieSession = kieSession;
this.nodesInKieSession = Collections.emptySet();
} }
@ -65,7 +83,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> betweenStrings(String start, String stop, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> betweenStrings(String start, String stop, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(start, stop); checkIfBothStartAndEndAreEmpty(start, stop);
List<TextRange> startTextRanges = RedactionSearchUtility.findTextRangesByString(start, node.getTextBlock()); List<TextRange> startTextRanges = RedactionSearchUtility.findTextRangesByString(start, node.getTextBlock());
List<TextRange> stopTextRanges = RedactionSearchUtility.findTextRangesByString(stop, node.getTextBlock()); List<TextRange> stopTextRanges = RedactionSearchUtility.findTextRangesByString(stop, node.getTextBlock());
@ -87,7 +105,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> betweenStringsIgnoreCase(String start, String stop, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> betweenStringsIgnoreCase(String start, String stop, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(start, stop); checkIfBothStartAndEndAreEmpty(start, stop);
List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(start, node.getTextBlock()); List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(start, node.getTextBlock());
List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(stop, node.getTextBlock()); List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(stop, node.getTextBlock());
@ -109,7 +127,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> betweenStringsIncludeStart(String start, String stop, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> betweenStringsIncludeStart(String start, String stop, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(start, stop); checkIfBothStartAndEndAreEmpty(start, stop);
List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByString(start, node.getTextBlock()); List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByString(start, node.getTextBlock());
List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByString(stop, node.getTextBlock()); List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByString(stop, node.getTextBlock());
@ -136,7 +154,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> betweenStringsIncludeStartIgnoreCase(String start, String stop, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> betweenStringsIncludeStartIgnoreCase(String start, String stop, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(start, stop); checkIfBothStartAndEndAreEmpty(start, stop);
List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(start, node.getTextBlock()); List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(start, node.getTextBlock());
List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(stop, node.getTextBlock()); List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(stop, node.getTextBlock());
@ -163,7 +181,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> betweenStringsIncludeEnd(String start, String stop, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> betweenStringsIncludeEnd(String start, String stop, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(start, stop); checkIfBothStartAndEndAreEmpty(start, stop);
List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByString(start, node.getTextBlock()); List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByString(start, node.getTextBlock());
List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByString(stop, node.getTextBlock()); List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByString(stop, node.getTextBlock());
@ -190,7 +208,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> betweenStringsIncludeEndIgnoreCase(String start, String stop, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> betweenStringsIncludeEndIgnoreCase(String start, String stop, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(start, stop); checkIfBothStartAndEndAreEmpty(start, stop);
List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(start, node.getTextBlock()); List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(start, node.getTextBlock());
List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(stop, node.getTextBlock()); List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(stop, node.getTextBlock());
@ -217,7 +235,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> betweenStringsIncludeStartAndEnd(String start, String stop, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> betweenStringsIncludeStartAndEnd(String start, String stop, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(start, stop); checkIfBothStartAndEndAreEmpty(start, stop);
List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByString(start, node.getTextBlock()); List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByString(start, node.getTextBlock());
List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByString(stop, node.getTextBlock()); List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByString(stop, node.getTextBlock());
@ -248,7 +266,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> betweenStringsIncludeStartAndEndIgnoreCase(String start, String stop, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> betweenStringsIncludeStartAndEndIgnoreCase(String start, String stop, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(start, stop); checkIfBothStartAndEndAreEmpty(start, stop);
List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(start, node.getTextBlock()); List<TextRange> startBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(start, node.getTextBlock());
List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(stop, node.getTextBlock()); List<TextRange> stopBoundaries = RedactionSearchUtility.findTextRangesByStringIgnoreCase(stop, node.getTextBlock());
@ -279,7 +297,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> shortestBetweenAnyString(List<String> starts, List<String> stops, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> shortestBetweenAnyString(List<String> starts, List<String> stops, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(starts, stops); checkIfBothStartAndEndAreEmpty(starts, stops);
List<TextRange> startTextRanges = RedactionSearchUtility.findTextRangesByList(starts, node.getTextBlock()); List<TextRange> startTextRanges = RedactionSearchUtility.findTextRangesByList(starts, node.getTextBlock());
List<TextRange> stopTextRanges = RedactionSearchUtility.findTextRangesByList(stops, node.getTextBlock()); List<TextRange> stopTextRanges = RedactionSearchUtility.findTextRangesByList(stops, node.getTextBlock());
@ -301,7 +319,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> shortestBetweenAnyStringIgnoreCase(List<String> starts, List<String> stops, String type, EntityType entityType, SemanticNode node) { public Stream<TextEntity> shortestBetweenAnyStringIgnoreCase(List<String> starts, List<String> stops, String type, EntityType entityType, SemanticNode node) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(starts, stops); checkIfBothStartAndEndAreEmpty(starts, stops);
List<TextRange> startTextRanges = RedactionSearchUtility.findTextRangesByListIgnoreCase(starts, node.getTextBlock()); List<TextRange> startTextRanges = RedactionSearchUtility.findTextRangesByListIgnoreCase(starts, node.getTextBlock());
List<TextRange> stopTextRanges = RedactionSearchUtility.findTextRangesByListIgnoreCase(stops, node.getTextBlock()); List<TextRange> stopTextRanges = RedactionSearchUtility.findTextRangesByListIgnoreCase(stops, node.getTextBlock());
@ -324,7 +342,7 @@ public class EntityCreationService {
*/ */
public Stream<TextEntity> shortestBetweenAnyStringIgnoreCase(List<String> starts, List<String> stops, String type, EntityType entityType, SemanticNode node, int limit) { public Stream<TextEntity> shortestBetweenAnyStringIgnoreCase(List<String> starts, List<String> stops, String type, EntityType entityType, SemanticNode node, int limit) {
EntityCreationUtility.checkIfBothStartAndEndAreEmpty(starts, stops); checkIfBothStartAndEndAreEmpty(starts, stops);
List<TextRange> startTextRanges = RedactionSearchUtility.findTextRangesByListIgnoreCase(starts, node.getTextBlock()); List<TextRange> startTextRanges = RedactionSearchUtility.findTextRangesByListIgnoreCase(starts, node.getTextBlock());
List<TextRange> stopTextRanges = RedactionSearchUtility.findTextRangesByListIgnoreCase(stops, node.getTextBlock()); List<TextRange> stopTextRanges = RedactionSearchUtility.findTextRangesByListIgnoreCase(stops, node.getTextBlock());
@ -480,7 +498,7 @@ public class EntityCreationService {
TextBlock textBlock = node.getTextBlock(); TextBlock textBlock = node.getTextBlock();
SearchImplementation searchImplementation = new SearchImplementation(strings, false); SearchImplementation searchImplementation = new SearchImplementation(strings, false);
return searchImplementation.getBoundaries(textBlock) return searchImplementation.getBoundaries(textBlock)
.map(boundary -> EntityCreationUtility.toLineAfterTextRange(textBlock, boundary)) .map(boundary -> toLineAfterTextRange(textBlock, boundary))
.filter(boundary -> isValidEntityTextRange(textBlock, boundary)) .filter(boundary -> isValidEntityTextRange(textBlock, boundary))
.map(boundary -> byTextRange(boundary, type, entityType, node)) .map(boundary -> byTextRange(boundary, type, entityType, node))
.filter(Optional::isPresent) .filter(Optional::isPresent)
@ -502,7 +520,7 @@ public class EntityCreationService {
TextBlock textBlock = node.getTextBlock(); TextBlock textBlock = node.getTextBlock();
SearchImplementation searchImplementation = new SearchImplementation(strings, true); SearchImplementation searchImplementation = new SearchImplementation(strings, true);
return searchImplementation.getBoundaries(textBlock) return searchImplementation.getBoundaries(textBlock)
.map(boundary -> EntityCreationUtility.toLineAfterTextRange(textBlock, boundary)) .map(boundary -> toLineAfterTextRange(textBlock, boundary))
.filter(boundary -> isValidEntityTextRange(textBlock, boundary)) .filter(boundary -> isValidEntityTextRange(textBlock, boundary))
.map(boundary -> byTextRange(boundary, type, entityType, node)) .map(boundary -> byTextRange(boundary, type, entityType, node))
.filter(Optional::isPresent) .filter(Optional::isPresent)
@ -524,7 +542,7 @@ public class EntityCreationService {
TextBlock textBlock = node.getTextBlock(); TextBlock textBlock = node.getTextBlock();
return RedactionSearchUtility.findTextRangesByString(string, textBlock) return RedactionSearchUtility.findTextRangesByString(string, textBlock)
.stream() .stream()
.map(boundary -> EntityCreationUtility.toLineAfterTextRange(textBlock, boundary)) .map(boundary -> toLineAfterTextRange(textBlock, boundary))
.filter(boundary -> isValidEntityTextRange(textBlock, boundary)) .filter(boundary -> isValidEntityTextRange(textBlock, boundary))
.map(boundary -> byTextRange(boundary, type, entityType, node)) .map(boundary -> byTextRange(boundary, type, entityType, node))
.filter(Optional::isPresent) .filter(Optional::isPresent)
@ -546,7 +564,7 @@ public class EntityCreationService {
TextBlock textBlock = node.getTextBlock(); TextBlock textBlock = node.getTextBlock();
return RedactionSearchUtility.findTextRangesByStringIgnoreCase(string, textBlock) return RedactionSearchUtility.findTextRangesByStringIgnoreCase(string, textBlock)
.stream() .stream()
.map(boundary -> EntityCreationUtility.toLineAfterTextRange(textBlock, boundary)) .map(boundary -> toLineAfterTextRange(textBlock, boundary))
.filter(boundary -> isValidEntityTextRange(textBlock, boundary)) .filter(boundary -> isValidEntityTextRange(textBlock, boundary))
.map(boundary -> byTextRange(boundary, type, entityType, node)) .map(boundary -> byTextRange(boundary, type, entityType, node))
.filter(Optional::isPresent) .filter(Optional::isPresent)
@ -931,7 +949,7 @@ public class EntityCreationService {
public Optional<TextEntity> bySuffixExpansionRegex(TextEntity entity, String regexPattern) { public Optional<TextEntity> bySuffixExpansionRegex(TextEntity entity, String regexPattern) {
int expandedEnd = RedactionSearchUtility.getExpandedEndByRegex(entity, regexPattern); int expandedEnd = RedactionSearchUtility.getExpandedEndByRegex(entity, regexPattern);
expandedEnd = EntityCreationUtility.truncateEndIfLineBreakIsBetween(entity.getTextRange().end(), expandedEnd, entity.getDeepestFullyContainingNode().getTextBlock()); expandedEnd = truncateEndIfLineBreakIsBetween(entity.getTextRange().end(), expandedEnd, entity.getDeepestFullyContainingNode().getTextBlock());
return byTextRange(new TextRange(entity.getTextRange().start(), expandedEnd), entity.type(), entity.getEntityType(), entity.getDeepestFullyContainingNode()); return byTextRange(new TextRange(entity.getTextRange().start(), expandedEnd), entity.type(), entity.getEntityType(), entity.getDeepestFullyContainingNode());
} }
@ -985,21 +1003,21 @@ public class EntityCreationService {
return Optional.empty(); // Entity has been recategorized and should not be created at all. return Optional.empty(); // Entity has been recategorized and should not be created at all.
} }
TextEntity existingEntity = optionalTextEntity.get(); TextEntity existingEntity = optionalTextEntity.get();
if (existingEntity.getTextRange().equals(trimmedTextRange)) { if (existingEntity.getTextRange().equals(textRange)) {
return optionalTextEntity; // exactly the same entity, return directly return optionalTextEntity; // exactly the same entity, return directly
} }
if (!existingEntity.resized()) { if (!existingEntity.resized()) {
addDuplicateEntityToGraph(existingEntity, trimmedTextRange, node); addDuplicateEntityToGraph(existingEntity, textRange, node);
return optionalTextEntity; // If Entity has not been resized, insert as duplicate at appropriate position return optionalTextEntity; // If Entity has not been resized, insert as duplicate at appropriate position
} }
return Optional.empty(); // Entity has been resized, if there are duplicates they should be treated there return Optional.empty(); // Entity has been resized, if there are duplicates they should be treated there
} }
boolean added = addEntityToGraph(entity, node.getDocumentTree());
addListenerToEntity(entity); if (!added) {
node.getDocumentTree().addEntityToGraph(entity); return Optional.empty();
}
entity.addEngines(engines); entity.addEngines(engines);
insertToKieSession(entity);
return Optional.of(entity); return Optional.of(entity);
} }
@ -1039,7 +1057,7 @@ public class EntityCreationService {
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
public TextEntity mergeEntitiesOfSameType(List<TextEntity> entitiesToMerge, String type, EntityType entityType, SemanticNode node) { public TextEntity mergeEntitiesOfSameType(List<TextEntity> entitiesToMerge, String type, EntityType entityType, SemanticNode node) {
if (!EntityCreationUtility.allEntitiesIntersectAndHaveSameTypes(entitiesToMerge)) { if (!allEntitiesIntersectAndHaveSameTypes(entitiesToMerge)) {
throw new IllegalArgumentException("Provided entities can not be merged, since they do not intersect or are not the same type!" + entitiesToMerge); throw new IllegalArgumentException("Provided entities can not be merged, since they do not intersect or are not the same type!" + entitiesToMerge);
} }
if (entitiesToMerge.isEmpty()) { if (entitiesToMerge.isEmpty()) {
@ -1064,16 +1082,17 @@ public class EntityCreationService {
.map(TextEntity::getManualOverwrite) .map(TextEntity::getManualOverwrite)
.map(ManualChangeOverwrite::getManualChangeLog) .map(ManualChangeOverwrite::getManualChangeLog)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.forEach(mergedEntity::addManualChange); .forEach(manualChange -> mergedEntity.getManualOverwrite().addChange(manualChange));
mergedEntity.setDictionaryEntry(entitiesToMerge.stream() mergedEntity.setDictionaryEntry(entitiesToMerge.stream()
.anyMatch(TextEntity::isDictionaryEntry)); .anyMatch(TextEntity::isDictionaryEntry));
mergedEntity.setDossierDictionaryEntry(entitiesToMerge.stream() mergedEntity.setDossierDictionaryEntry(entitiesToMerge.stream()
.anyMatch(TextEntity::isDossierDictionaryEntry)); .anyMatch(TextEntity::isDossierDictionaryEntry));
EntityEnrichmentService.enrichEntity(mergedEntity, node.getTextBlock()); entityEnrichmentService.enrichEntity(mergedEntity, node.getTextBlock());
addEntityToGraph(mergedEntity, node); addEntityToGraph(mergedEntity, node);
insertToKieSession(mergedEntity);
entitiesToMerge.stream() entitiesToMerge.stream()
.filter(e -> !e.equals(mergedEntity)) .filter(e -> !e.equals(mergedEntity))
@ -1128,13 +1147,26 @@ public class EntityCreationService {
TextEntity newEntity = byTextRangeWithEngine(entity.getTextRange(), type, entityType, node, entity.getEngines()).orElseThrow(() -> new NotFoundException( TextEntity newEntity = byTextRangeWithEngine(entity.getTextRange(), type, entityType, node, entity.getEngines()).orElseThrow(() -> new NotFoundException(
"No entity present!")); "No entity present!"));
newEntity.addManualChanges(entity.getManualOverwrite().getManualChangeLog()); newEntity.getManualOverwrite().addChanges(entity.getManualOverwrite().getManualChangeLog());
newEntity.setDictionaryEntry(entity.isDictionaryEntry()); newEntity.setDictionaryEntry(entity.isDictionaryEntry());
newEntity.setDossierDictionaryEntry(entity.isDossierDictionaryEntry()); newEntity.setDossierDictionaryEntry(entity.isDossierDictionaryEntry());
return newEntity; return newEntity;
} }
/**
* Inserts a text entity into the kieSession for further processing.
*
* @param textEntity The merged text entity to insert.
*/
public void insertToKieSession(TextEntity textEntity) {
if (kieSession != null) {
kieSession.insert(textEntity);
}
}
/** /**
* Creates a text entity based on a Named Entity Recognition (NER) entity. * Creates a text entity based on a Named Entity Recognition (NER) entity.
* *
@ -1422,8 +1454,7 @@ public class EntityCreationService {
.ifPresent(e -> addDuplicateEntityToGraph(e, entity.getTextRange(), node)); .ifPresent(e -> addDuplicateEntityToGraph(e, entity.getTextRange(), node));
} else { } else {
addListenerToEntity(entity); addEntityToGraph(entity, documentTree);
documentTree.addEntityToGraph(entity);
} }
} }
@ -1461,7 +1492,7 @@ public class EntityCreationService {
entityToDuplicate.setDeepestFullyContainingNode(deepestSharedNode); entityToDuplicate.setDeepestFullyContainingNode(deepestSharedNode);
Set<SemanticNode> additionalIntersectingNodes = EntityCreationUtility.findIntersectingSubNodes(deepestSharedNode, newTextRange); Set<SemanticNode> additionalIntersectingNodes = findIntersectingSubNodes(deepestSharedNode, newTextRange);
additionalIntersectingNodes.forEach(additionalIntersectingNode -> { additionalIntersectingNodes.forEach(additionalIntersectingNode -> {
if (entityToDuplicate.getIntersectingNodes().contains(additionalIntersectingNode)) { if (entityToDuplicate.getIntersectingNodes().contains(additionalIntersectingNode)) {
@ -1475,13 +1506,23 @@ public class EntityCreationService {
} }
private void addListenerToEntity(TextEntity textEntity) { private boolean addEntityToGraph(TextEntity entity, DocumentTree documentTree) {
if(kieSessionUpdater != null) { documentTree.getRoot().getNode().addThisToEntityIfIntersects(entity);
textEntity.addEntityEventListener(kieSessionUpdater);
if (!nodesInKieSession.isEmpty() && entity.getIntersectingNodes()
.stream()
.anyMatch(node -> !nodesInKieSession.contains(node))) {
entity.removeFromGraph();
return false;
} }
TextBlock textBlock = entity.getDeepestFullyContainingNode().getTextBlock();
entityEnrichmentService.enrichEntity(entity, textBlock);
addToPages(entity);
addEntityToNodeEntitySets(entity);
return true;
} }
} }

View File

@ -1,27 +1,23 @@
package com.iqser.red.service.redaction.v1.server.utils; package com.iqser.red.service.redaction.v1.server.service.document;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import com.iqser.red.service.redaction.v1.server.model.document.IntersectingNodeVisitor;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange; import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock; import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
import lombok.experimental.UtilityClass;
@UtilityClass
public class EntityCreationUtility { public class EntityCreationUtility {
public void checkIfBothStartAndEndAreEmpty(String start, String end) { public static void checkIfBothStartAndEndAreEmpty(String start, String end) {
checkIfBothStartAndEndAreEmpty(List.of(start), List.of(end)); checkIfBothStartAndEndAreEmpty(List.of(start), List.of(end));
} }
public <T> void checkIfBothStartAndEndAreEmpty(List<T> start, List<T> end) { public static <T> void checkIfBothStartAndEndAreEmpty(List<T> start, List<T> end) {
if ((start == null || start.isEmpty()) && (end == null || end.isEmpty())) { if ((start == null || start.isEmpty()) && (end == null || end.isEmpty())) {
throw new IllegalArgumentException("Start and end values are empty!"); throw new IllegalArgumentException("Start and end values are empty!");
@ -29,7 +25,7 @@ public class EntityCreationUtility {
} }
public int truncateEndIfLineBreakIsBetween(int end, int expandedEnd, TextBlock textBlock) { public static int truncateEndIfLineBreakIsBetween(int end, int expandedEnd, TextBlock textBlock) {
if (textBlock.getNextLinebreak(end) < expandedEnd) { if (textBlock.getNextLinebreak(end) < expandedEnd) {
return end; return end;
@ -38,7 +34,7 @@ public class EntityCreationUtility {
} }
public Set<SemanticNode> findIntersectingSubNodes(SemanticNode initialIntersectingNode, TextRange textRange) { public static Set<SemanticNode> findIntersectingSubNodes(SemanticNode initialIntersectingNode, TextRange textRange) {
IntersectingNodeVisitor visitor = new IntersectingNodeVisitor(textRange); IntersectingNodeVisitor visitor = new IntersectingNodeVisitor(textRange);
@ -50,7 +46,7 @@ public class EntityCreationUtility {
} }
public void addToPages(TextEntity entity) { public static void addToPages(TextEntity entity) {
Set<Page> pages = entity.getDeepestFullyContainingNode().getPages(entity.getTextRange()); Set<Page> pages = entity.getDeepestFullyContainingNode().getPages(entity.getTextRange());
entity.getPages().addAll(pages); entity.getPages().addAll(pages);
@ -58,14 +54,14 @@ public class EntityCreationUtility {
} }
public void addEntityToNodeEntitySets(TextEntity entity) { public static void addEntityToNodeEntitySets(TextEntity entity) {
entity.getIntersectingNodes() entity.getIntersectingNodes()
.forEach(node -> node.getEntities().add(entity)); .forEach(node -> node.getEntities().add(entity));
} }
public boolean allEntitiesIntersectAndHaveSameTypes(List<TextEntity> entitiesToMerge) { public static boolean allEntitiesIntersectAndHaveSameTypes(List<TextEntity> entitiesToMerge) {
if (entitiesToMerge.isEmpty()) { if (entitiesToMerge.isEmpty()) {
return true; return true;
@ -83,7 +79,7 @@ public class EntityCreationUtility {
} }
public TextRange toLineAfterTextRange(TextBlock textBlock, TextRange textRange) { public static TextRange toLineAfterTextRange(TextBlock textBlock, TextRange textRange) {
if (textBlock.getTextRange().end() == textRange.end()) { if (textBlock.getTextRange().end() == textRange.end()) {
return new TextRange(textRange.end(), textRange.end()); return new TextRange(textRange.end(), textRange.end());

View File

@ -1,36 +1,39 @@
package com.iqser.red.service.redaction.v1.server.utils; package com.iqser.red.service.redaction.v1.server.service.document;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock; import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
import lombok.AccessLevel; import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.experimental.UtilityClass;
@UtilityClass @Service
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @RequiredArgsConstructor
public class EntityEnrichmentService { public class EntityEnrichmentService {
int SURROUNDING_WORDS_OFFSET_WINDOW = 100; private final RedactionServiceSettings redactionServiceSettings;
int NUMBER_OF_SURROUNDING_WORDS = 3;
public void enrichEntity(TextEntity entity, TextBlock textBlock) { public void enrichEntity(TextEntity entity, TextBlock textBlock) {
entity.setValue(textBlock.subSequence(entity.getTextRange()).toString()); entity.setValue(textBlock.subSequence(entity.getTextRange()).toString());
entity.setTextAfter(findTextAfter(entity.getTextRange().end(), textBlock)); entity.setTextAfter(findTextAfter(entity.getTextRange().end(), textBlock));
entity.setTextBefore(findTextBefore(entity.getTextRange().start(), textBlock)); entity.setTextBefore(findTextBefore(entity.getTextRange().start(), textBlock));
} }
private String findTextAfter(int index, TextBlock textBlock) { private String findTextAfter(int index, TextBlock textBlock) {
int endOffset = Math.min(index + SURROUNDING_WORDS_OFFSET_WINDOW, textBlock.getTextRange().end());
int endOffset = Math.min(index + redactionServiceSettings.getSurroundingWordsOffsetWindow(), textBlock.getTextRange().end());
String textAfter = textBlock.subSequence(index, endOffset).toString(); String textAfter = textBlock.subSequence(index, endOffset).toString();
if (!textAfter.isBlank()) { if (!textAfter.isBlank()) {
List<String> wordsAfter = splitToWordsAndRemoveEmptyWords(textAfter); List<String> wordsAfter = splitToWordsAndRemoveEmptyWords(textAfter);
int numberOfWordsAfter = Math.min(wordsAfter.size(), NUMBER_OF_SURROUNDING_WORDS); int numberOfWordsAfter = Math.min(wordsAfter.size(), redactionServiceSettings.getNumberOfSurroundingWords());
if (!wordsAfter.isEmpty()) { if (!wordsAfter.isEmpty()) {
return concatWordsAfter(wordsAfter.subList(0, numberOfWordsAfter), textAfter.startsWith(" ")); return concatWordsAfter(wordsAfter.subList(0, numberOfWordsAfter), textAfter.startsWith(" "));
} }
@ -38,12 +41,14 @@ public class EntityEnrichmentService {
return ""; return "";
} }
private String findTextBefore(int index, TextBlock textBlock) { private String findTextBefore(int index, TextBlock textBlock) {
int offsetBefore = Math.max(index - SURROUNDING_WORDS_OFFSET_WINDOW, textBlock.getTextRange().start());
int offsetBefore = Math.max(index - redactionServiceSettings.getSurroundingWordsOffsetWindow(), textBlock.getTextRange().start());
String textBefore = textBlock.subSequence(offsetBefore, index).toString(); String textBefore = textBlock.subSequence(offsetBefore, index).toString();
if (!textBefore.isBlank()) { if (!textBefore.isBlank()) {
List<String> wordsBefore = splitToWordsAndRemoveEmptyWords(textBefore); List<String> wordsBefore = splitToWordsAndRemoveEmptyWords(textBefore);
int numberOfWordsBefore = Math.min(wordsBefore.size(), NUMBER_OF_SURROUNDING_WORDS); int numberOfWordsBefore = Math.min(wordsBefore.size(), redactionServiceSettings.getNumberOfSurroundingWords());
if (!wordsBefore.isEmpty()) { if (!wordsBefore.isEmpty()) {
return concatWordsBefore(wordsBefore.subList(wordsBefore.size() - numberOfWordsBefore, wordsBefore.size()), textBefore.endsWith(" ")); return concatWordsBefore(wordsBefore.subList(wordsBefore.size() - numberOfWordsBefore, wordsBefore.size()), textBefore.endsWith(" "));
} }
@ -51,26 +56,36 @@ public class EntityEnrichmentService {
return ""; return "";
} }
private List<String> splitToWordsAndRemoveEmptyWords(String text) {
return Arrays.stream(text.split(" ")) private static List<String> splitToWordsAndRemoveEmptyWords(String textAfter) {
return Arrays.stream(textAfter.split(" "))
.filter(word -> !Objects.equals("", word)) .filter(word -> !Objects.equals("", word))
.toList(); .toList();
} }
private String concatWordsBefore(List<String> words, boolean endWithSpace) {
private static String concatWordsBefore(List<String> words, boolean endWithSpace) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (String word : words) { for (String word : words) {
sb.append(word).append(" "); sb.append(word).append(" ");
} }
String result = sb.toString().trim(); String result = sb.toString().trim();
return endWithSpace ? result + " " : result; return endWithSpace ? result + " " : result;
} }
private String concatWordsAfter(List<String> words, boolean startWithSpace) {
private static String concatWordsAfter(List<String> words, boolean startWithSpace) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (String word : words) { for (String word : words) {
sb.append(word).append(" "); sb.append(word).append(" ");
} }
String result = sb.toString().trim(); String result = sb.toString().trim();
return startWithSpace ? " " + result : result; return startWithSpace ? " " + result : result;
} }

View File

@ -14,15 +14,12 @@ import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedactions; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.imported.ImportedRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.BaseAnnotation; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.BaseAnnotation;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings; import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.model.PrecursorEntity; import com.iqser.red.service.redaction.v1.server.model.PrecursorEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
import com.iqser.red.service.redaction.v1.server.service.DictionaryService; import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
import com.iqser.red.service.redaction.v1.server.service.EntityFindingUtility; import com.iqser.red.service.redaction.v1.server.service.EntityFindingUtility;
@ -44,14 +41,9 @@ public class EntityFromPrecursorCreationService {
RedactionServiceSettings settings; RedactionServiceSettings settings;
public List<PrecursorEntity> createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions manualRedactions, Document document, String dossierTemplateId) { public List<PrecursorEntity> createEntitiesIfFoundAndReturnNotFoundEntries(ManualRedactions manualRedactions, SemanticNode node, String dossierTemplateId) {
Set<Integer> pageNumbers = document.getPages()
.stream()
.map(Page::getNumber)
.collect(Collectors.toSet());
Set<IdRemoval> idRemovals = manualRedactions.getIdsToRemove(); Set<IdRemoval> idRemovals = manualRedactions.getIdsToRemove();
List<PrecursorEntity> manualEntities = manualRedactions.getEntriesToAdd() List<PrecursorEntity> manualEntities = manualRedactions.getEntriesToAdd()
.stream() .stream()
.filter(BaseAnnotation::isLocal) .filter(BaseAnnotation::isLocal)
@ -60,10 +52,6 @@ public class EntityFromPrecursorCreationService {
.filter(idRemoval -> idRemoval.getRequestDate().isAfter(manualRedactionEntry.getRequestDate())) .filter(idRemoval -> idRemoval.getRequestDate().isAfter(manualRedactionEntry.getRequestDate()))
.findAny()// .findAny()//
.isEmpty()) .isEmpty())
.filter(manualRedactionEntry -> manualRedactionEntry.getPositions()
.stream()
.map(Rectangle::getPage)
.allMatch(pageNumbers::contains))
.map(manualRedactionEntry -> // .map(manualRedactionEntry -> //
PrecursorEntity.fromManualRedactionEntry(manualRedactionEntry, dictionaryService.isHint(manualRedactionEntry.getType(), dossierTemplateId))) PrecursorEntity.fromManualRedactionEntry(manualRedactionEntry, dictionaryService.isHint(manualRedactionEntry.getType(), dossierTemplateId)))
.peek(manualEntity -> { .peek(manualEntity -> {
@ -74,7 +62,7 @@ public class EntityFromPrecursorCreationService {
} }
}) })
.toList(); .toList();
return toTextEntity(manualEntities, document); return toTextEntity(manualEntities, node);
} }
@ -152,7 +140,7 @@ public class EntityFromPrecursorCreationService {
} }
correctEntity.setDeepestFullyContainingNode(closestEntity.getDeepestFullyContainingNode()); correctEntity.setDeepestFullyContainingNode(closestEntity.getDeepestFullyContainingNode());
correctEntity.setIntersectingNodes(new ArrayList<>(closestEntity.getIntersectingNodes())); correctEntity.setIntersectingNodes(new ArrayList<>(closestEntity.getIntersectingNodes()));
correctEntity.setDuplicateTextRanges(new HashSet<>(closestEntity.getDuplicateTextRanges())); correctEntity.setDuplicateTextRanges(new ArrayList<>(closestEntity.getDuplicateTextRanges()));
correctEntity.setPages(new HashSet<>(closestEntity.getPages())); correctEntity.setPages(new HashSet<>(closestEntity.getPages()));
correctEntity.setValue(closestEntity.getValue()); correctEntity.setValue(closestEntity.getValue());

View File

@ -2,6 +2,7 @@ package com.iqser.red.service.redaction.v1.server.service.drools;
import static com.iqser.red.service.redaction.v1.server.service.drools.ComponentDroolsExecutionService.RULES_LOGGER_GLOBAL; import static com.iqser.red.service.redaction.v1.server.service.drools.ComponentDroolsExecutionService.RULES_LOGGER_GLOBAL;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -28,12 +29,11 @@ import com.iqser.red.service.redaction.v1.server.logger.RulesLogger;
import com.iqser.red.service.redaction.v1.server.logger.TrackingAgendaEventListener; import com.iqser.red.service.redaction.v1.server.logger.TrackingAgendaEventListener;
import com.iqser.red.service.redaction.v1.server.model.NerEntities; import com.iqser.red.service.redaction.v1.server.model.NerEntities;
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary; import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService; import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.service.websocket.WebSocketService; import com.iqser.red.service.redaction.v1.server.service.websocket.WebSocketService;
import com.iqser.red.service.redaction.v1.server.utils.exception.DroolsTimeoutException; import com.iqser.red.service.redaction.v1.server.utils.exception.DroolsTimeoutException;
@ -51,6 +51,7 @@ import lombok.extern.slf4j.Slf4j;
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class EntityDroolsExecutionService { public class EntityDroolsExecutionService {
EntityEnrichmentService entityEnrichmentService;
ObservationRegistry observationRegistry; ObservationRegistry observationRegistry;
ManualChangesApplicationService manualChangesApplicationService; ManualChangesApplicationService manualChangesApplicationService;
RedactionServiceSettings settings; RedactionServiceSettings settings;
@ -94,86 +95,81 @@ public class EntityDroolsExecutionService {
KieSession kieSession = kieContainer.newKieSession(); KieSession kieSession = kieContainer.newKieSession();
try { Set<SemanticNode> nodesInKieSession = sectionsToAnalyze.size() == document.streamAllSubNodes()
KieSessionUpdater kieSessionUpdater = new KieSessionUpdater(kieSession); .count() ? Collections.emptySet() : buildSet(sectionsToAnalyze, document);
EntityCreationService entityCreationService = new EntityCreationService(kieSessionUpdater);
RulesLogger logger = new RulesLogger(webSocketService, context);
if (settings.isDroolsDebug()) {
logger.enableAgendaTracking();
logger.enableObjectTracking();
}
kieSession.addEventListener(new TrackingAgendaEventListener(logger));
kieSession.addEventListener(new ObjectTrackingEventListener(logger));
kieSession.setGlobal("document", document); EntityCreationService entityCreationService = new EntityCreationService(entityEnrichmentService, kieSession, nodesInKieSession);
kieSession.setGlobal("entityCreationService", entityCreationService); RulesLogger logger = new RulesLogger(webSocketService, context);
kieSession.setGlobal("manualChangesApplicationService", manualChangesApplicationService); if (settings.isDroolsDebug()) {
kieSession.setGlobal("dictionary", dictionary); logger.enableAgendaTracking();
logger.enableObjectTracking();
if (hasGlobalWithName(kieSession, RULES_LOGGER_GLOBAL)) {
kieSession.setGlobal(RULES_LOGGER_GLOBAL, logger);
}
kieSession.insert(document);
sectionsToAnalyze.forEach(kieSession::insert);
sectionsToAnalyze.stream()
.flatMap(SemanticNode::streamAllSubNodes)
.forEach(semanticNode -> {
if (semanticNode instanceof Image image) {
image.addEntityEventListener(kieSessionUpdater);
image.notifyEntityInserted();
} else {
kieSession.insert(semanticNode);
}
});
for (TextEntity textEntity : document.getEntities()) {
textEntity.addEntityEventListener(kieSessionUpdater);
textEntity.notifyEntityInserted();
}
document.getPages()
.forEach(kieSession::insert);
fileAttributes.stream()
.filter(f -> f.getValue() != null)
.forEach(kieSession::insert);
if (manualRedactions != null) {
manualRedactions.buildAll()
.stream()
.filter(BaseAnnotation::isLocal)
.forEach(kieSession::insert);
}
kieSession.insert(nerEntities);
kieSession.getAgenda().getAgendaGroup("LOCAL_DICTIONARY_ADDS").setFocus();
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(kieSession::fireAllRules);
try {
completableFuture.get(settings.getDroolsExecutionTimeoutSecs(document.getNumberOfPages()), TimeUnit.SECONDS);
} catch (ExecutionException e) {
logger.error(e, "Exception during rule execution");
if (e.getCause() instanceof TimeoutException) {
throw new DroolsTimeoutException(String.format("The file %s caused a timeout", context.getFileId()), e, false, RuleFileType.ENTITY);
}
throw new RuntimeException(e);
} catch (InterruptedException e) {
logger.error(e, "Exception during rule execution");
throw new RuntimeException(e);
} catch (TimeoutException e) {
throw new DroolsTimeoutException(String.format("The file %s caused a timeout", context.getFileId()), e, false, RuleFileType.ENTITY);
}
List<FileAttribute> resultingFileAttributes = getFileAttributes(kieSession);
return resultingFileAttributes;
} finally {
kieSession.dispose();
} }
kieSession.addEventListener(new TrackingAgendaEventListener(logger));
kieSession.addEventListener(new ObjectTrackingEventListener(logger));
kieSession.setGlobal("document", document);
kieSession.setGlobal("entityCreationService", entityCreationService);
kieSession.setGlobal("manualChangesApplicationService", manualChangesApplicationService);
kieSession.setGlobal("dictionary", dictionary);
if (hasGlobalWithName(kieSession, RULES_LOGGER_GLOBAL)) {
kieSession.setGlobal(RULES_LOGGER_GLOBAL, logger);
}
kieSession.insert(document);
document.getEntities()
.forEach(kieSession::insert);
sectionsToAnalyze.forEach(kieSession::insert);
sectionsToAnalyze.stream()
.flatMap(SemanticNode::streamAllSubNodes)
.forEach(kieSession::insert);
document.getPages()
.forEach(kieSession::insert);
fileAttributes.stream()
.filter(f -> f.getValue() != null)
.forEach(kieSession::insert);
if (manualRedactions != null) {
manualRedactions.buildAll()
.stream()
.filter(BaseAnnotation::isLocal)
.forEach(kieSession::insert);
}
kieSession.insert(nerEntities);
kieSession.getAgenda().getAgendaGroup("LOCAL_DICTIONARY_ADDS").setFocus();
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
kieSession.fireAllRules();
return null;
});
try {
completableFuture.get(settings.getDroolsExecutionTimeoutSecs(document.getNumberOfPages()), TimeUnit.SECONDS);
} catch (ExecutionException e) {
logger.error(e, "Exception during rule execution");
kieSession.dispose();
if (e.getCause() instanceof TimeoutException) {
throw new DroolsTimeoutException(String.format("The file %s caused a timeout",context.getFileId()), e, false, RuleFileType.ENTITY);
}
throw new RuntimeException(e);
} catch (InterruptedException e) {
logger.error(e, "Exception during rule execution");
kieSession.dispose();
throw new RuntimeException(e);
} catch (TimeoutException e) {
throw new DroolsTimeoutException(String.format("The file %s caused a timeout",context.getFileId()), e, false, RuleFileType.ENTITY);
}
List<FileAttribute> resultingFileAttributes = getFileAttributes(kieSession);
kieSession.dispose();
return resultingFileAttributes;
} }

View File

@ -1,98 +0,0 @@
package com.iqser.red.service.redaction.v1.server.service.drools;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Consumer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityEventListener;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class KieSessionUpdater implements EntityEventListener {
KieSession kieSession;
@Override
public void onEntityInserted(IEntity entity) {
handleOnEntityEvent(entity, kieSession::insert);
kieSession.insert(entity);
}
@Override
public void onEntityUpdated(IEntity entity) {
handleOnEntityEvent(entity, this::updateFactIfPresent);
kieSession.update(kieSession.getFactHandle(entity), entity);
}
@Override
public void onEntityRemoved(IEntity entity) {
handleOnEntityEvent(entity, this::deleteFactIfPresent);
kieSession.delete(kieSession.getFactHandle(entity));
}
private void handleOnEntityEvent(IEntity entity, Consumer<Object> consumer) {
if (entity instanceof TextEntity textEntity) {
updateIntersectingNodes(textEntity);
textEntity.getRelations().values()
.stream()
.flatMap(Collection::stream)
.forEach(consumer);
textEntity.getRelations().keySet()
.forEach(k -> k.getRelations().getOrDefault(textEntity, Collections.emptySet())
.forEach(consumer));
}
if (entity instanceof Image image) {
SemanticNode parent = image;
while (parent.hasParent()) {
parent = parent.getParent();
kieSession.update(kieSession.getFactHandle(parent), parent);
}
}
}
private void updateIntersectingNodes(TextEntity textEntity) {
textEntity.getIntersectingNodes()
.forEach(this::updateFactIfPresent);
}
private void updateFactIfPresent(Object o) {
FactHandle factHandle = kieSession.getFactHandle(o);
if (factHandle != null) {
kieSession.update(factHandle, o);
}
}
private void deleteFactIfPresent(Object o) {
FactHandle factHandle = kieSession.getFactHandle(o);
if (factHandle != null) {
kieSession.delete(factHandle);
}
}
}

View File

@ -265,7 +265,7 @@ public class RedactionStorageService {
// And the cache eviction logic when a file changes after e.g. ocr is not implemented yet. // And the cache eviction logic when a file changes after e.g. ocr is not implemented yet.
// See https://knecon.atlassian.net/jira/software/c/projects/RED/boards/37?selectedIssue=RED-8106. // See https://knecon.atlassian.net/jira/software/c/projects/RED/boards/37?selectedIssue=RED-8106.
@Timed("redactmanager_getDocumentGraph") @Timed("redactmanager_getDocumentGraph")
//@Cacheable(value = "documentDataCache") @Cacheable(value = "documentDataCache")
public DocumentData getDocumentData(String dossierId, String fileId) { public DocumentData getDocumentData(String dossierId, String fileId) {
try { try {

View File

@ -55,7 +55,6 @@ public final class SeparatorUtils {
return textRange.end() == textBlock.getTextRange().end() ||// return textRange.end() == textBlock.getTextRange().end() ||//
SeparatorUtils.isSeparator(textBlock.charAt(textRange.end())) ||// SeparatorUtils.isSeparator(textBlock.charAt(textRange.end())) ||//
SeparatorUtils.isSeparator(textBlock.charAt(textRange.end() - 1)) ||//
SeparatorUtils.isJapaneseSeparator(textBlock.charAt(textRange.end() - 1)); SeparatorUtils.isJapaneseSeparator(textBlock.charAt(textRange.end() - 1));
} }

View File

@ -25,8 +25,7 @@ fforesight:
ignored-endpoints: [ '/redaction-gateway-v1', '/actuator/health/**',"/api/rules-logging/rulesocket","/api/rules-logging/rulesocket/**", '/redaction-gateway-v1/async/download/with-ott/**', ignored-endpoints: [ '/redaction-gateway-v1', '/actuator/health/**',"/api/rules-logging/rulesocket","/api/rules-logging/rulesocket/**", '/redaction-gateway-v1/async/download/with-ott/**',
'/internal-api/**', '/redaction-gateway-v1/docs/swagger-ui', '/rules/test', '/internal-api/**', '/redaction-gateway-v1/docs/swagger-ui', '/rules/test',
'/redaction-gateway-v1/docs/**','/redaction-gateway-v1/docs', '/redaction-gateway-v1/docs/**','/redaction-gateway-v1/docs',
'/api', '/api/','/api/docs/**','/api/docs','/api/docs/swagger-ui', '/api', '/api/','/api/docs/**','/api/docs','/api/docs/swagger-ui' ]
'/actuator/prometheus' ]
spring: spring:
application: application:

View File

@ -13,14 +13,12 @@ import java.util.stream.Stream;
import java.util.Optional; import java.util.Optional;
import com.iqser.red.service.redaction.v1.server.logger.RulesLogger; import com.iqser.red.service.redaction.v1.server.logger.RulesLogger;
import com.iqser.red.service.redaction.v1.server.model.document.*;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange; import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.IEntity;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell; import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
@ -49,19 +47,22 @@ 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.ManualRecategorization; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRecategorization;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange;
import com.iqser.red.service.redaction.v1.server.data.LayoutEngineProto.LayoutEngine;
global Document document global Document document
global EntityCreationService entityCreationService global EntityCreationService entityCreationService
global ManualChangesApplicationService manualChangesApplicationService global ManualChangesApplicationService manualChangesApplicationService
global Dictionary dictionary global Dictionary dictionary
global RulesLogger logger global RulesLogger logger
//------------------------------------ queries ------------------------------------ //------------------------------------ queries ------------------------------------
query "getFileAttributes" query "getFileAttributes"
$fileAttribute: FileAttribute() $fileAttribute: FileAttribute()
end end
//------------------------------------ Headlines rules ------------------------------------ //------------------------------------ H rules ------------------------------------
// Rule unit: H.0 // Rule unit: H.0
rule "H.0.0: retract table of contents page" rule "H.0.0: retract table of contents page"
@ -128,7 +129,6 @@ rule "H.3.1: Study Type File Attribute in Headlines"
.ifPresent(fileAttribute -> insert(fileAttribute)); .ifPresent(fileAttribute -> insert(fileAttribute));
end end
//------------------------------------ General documine rules ------------------------------------ //------------------------------------ General documine rules ------------------------------------
// Rule unit: DOC.1 // Rule unit: DOC.1
@ -296,7 +296,6 @@ rule "DOC.1.4: Guideline in Headlines"
); );
end end
// Rule unit: DOC.2 // Rule unit: DOC.2
rule "DOC.2.0: Report number" rule "DOC.2.0: Report number"
when when
@ -1148,7 +1147,6 @@ rule "DOC.35.0: Doses (mg/kg bodyweight)"
.forEach(entity -> entity.apply("DOC.35.0", "Doses per bodyweight information found", "n-a")); .forEach(entity -> entity.apply("DOC.35.0", "Doses per bodyweight information found", "n-a"));
end end
//------------------------------------ Table extraction rules ------------------------------------ //------------------------------------ Table extraction rules ------------------------------------
// Rule unit: TAB.0 // Rule unit: TAB.0
@ -1298,8 +1296,7 @@ rule "TAB.7.0: Indicator (Species)"
.ifPresent(redactionEntity -> redactionEntity.apply("TAB.7.0", "Vertebrate study found")); .ifPresent(redactionEntity -> redactionEntity.apply("TAB.7.0", "Vertebrate study found"));
end end
//------------------------------------ Manual redaction rules ------------------------------------
//------------------------------------ Manual changes rules ------------------------------------
// Rule unit: MAN.0 // Rule unit: MAN.0
rule "MAN.0.0: Apply manual resize redaction" rule "MAN.0.0: Apply manual resize redaction"
@ -1311,6 +1308,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -1322,6 +1321,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -1332,8 +1333,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -1343,7 +1346,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -1354,7 +1359,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -1365,6 +1372,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -1377,7 +1386,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -1388,7 +1399,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -1400,6 +1411,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -1411,7 +1424,6 @@ rule "MAN.3.3: Apply recategorization entities by default"
$entity.apply("MAN.3.3", "Recategorized entities are applied by default.", $entity.legalBasis()); $entity.apply("MAN.3.3", "Recategorized entities are applied by default.", $entity.legalBasis());
end end
// Rule unit: MAN.4 // Rule unit: MAN.4
rule "MAN.4.0: Apply legal basis change" rule "MAN.4.0: Apply legal basis change"
salience 128 salience 128
@ -1420,6 +1432,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1429,7 +1442,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1440,114 +1454,72 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" // Rule unit: X.3
rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -1555,49 +1527,23 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
// Rule unit: X.5
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
@ -1616,36 +1562,24 @@ rule "X.7.0: Remove all images"
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
// Rule unit: X.9 // Rule unit: X.9
rule "X.9.0: Merge mostly contained signatures" rule "X.9.0: Merge mostly contained signatures"
when when
@ -1656,7 +1590,6 @@ rule "X.9.0: Merge mostly contained signatures"
$signature.addEngine(LayoutEngine.AI); $signature.addEngine(LayoutEngine.AI);
end end
// Rule unit: X.10 // Rule unit: X.10
rule "X.10.0: remove false positives of ai" rule "X.10.0: remove false positives of ai"
when when
@ -1671,16 +1604,11 @@ rule "X.10.0: remove false positives of ai"
rule "X.11.0: Remove dictionary entity which intersects with a manual entity" rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.dictionaryEntry,
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.0", "remove dictionary entity which intersects with a manual entity"); $dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
retract($dictionaryEntity);
end end
@ -1693,6 +1621,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -34,6 +34,11 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribu
global ComponentCreationService componentCreationService global ComponentCreationService componentCreationService
/**
The imports, globals, queries and rules from this file are required for any component rule file.
Since customers may edit their rules we need to ensure they can't change the imports to prevent malicious code execution!
*/
//------------------------------------ queries ------------------------------------ //------------------------------------ queries ------------------------------------
query "getFileAttributes" query "getFileAttributes"

View File

@ -18,10 +18,8 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.Containment; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.entity.Equality;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Intersection;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
@ -63,6 +61,12 @@ global EntityCreationService entityCreationService
global ManualChangesApplicationService manualChangesApplicationService global ManualChangesApplicationService manualChangesApplicationService
global Dictionary dictionary global Dictionary dictionary
/**
The imports, globals, queries and rules from this file are required for any entity rule file.
Since customers may edit their rules we need to ensure they can't change the imports to prevent malicious code execution!
*/
//------------------------------------ queries ------------------------------------ //------------------------------------ queries ------------------------------------
query "getFileAttributes" query "getFileAttributes"
@ -71,7 +75,7 @@ query "getFileAttributes"
//------------------------------------ Local dictionary search rules ------------------------------------ //------------------------------------ Local dictionary search rules ------------------------------------
// Rule unit: LDS.0 // Rule unit: LocalDictionarySearch.0
rule "LDS.0.0: Run local dictionary search" rule "LDS.0.0: Run local dictionary search"
agenda-group "LOCAL_DICTIONARY_ADDS" agenda-group "LOCAL_DICTIONARY_ADDS"
salience -999 salience -999

View File

@ -14,5 +14,4 @@
<appender-ref ref="${logType}"/> <appender-ref ref="${logType}"/>
</root> </root>
<logger name="org.drools.mvel" level="ERROR"/> <logger name="org.drools.mvel" level="ERROR"/>
<logger name="org.springframework.web.socket.config" level="WARN"/>
</configuration> </configuration>

View File

@ -4,5 +4,4 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"> xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd">
<include file="/mongo/changelog/tenant/1-initial-database.changelog.xml"/> <include file="/mongo/changelog/tenant/1-initial-database.changelog.xml"/>
<include file="/mongo/changelog/tenant/2-create-indices-for-entries.xml"/> <include file="/mongo/changelog/tenant/2-create-indices-for-entries.xml"/>
<include file="/mongo/changelog/tenant/7-add-entity-log-value-index.xml"/>
</databaseChangeLog> </databaseChangeLog>

View File

@ -1,23 +0,0 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<changeSet id="createIndexForEntityLogIdAndValue" author="corina">
<ext:createIndex collectionName="entity-log-entries">
<ext:keys>
{
"entityLogId": 1,
"value": 1,
}
</ext:keys>
<ext:options>
{name: "entityLogId_value_index"}
</ext:options>
</ext:createIndex>
</changeSet>
</databaseChangeLog>

View File

@ -67,7 +67,6 @@ 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.client.RulesClient;
import com.iqser.red.service.redaction.v1.server.controller.RedactionController; import com.iqser.red.service.redaction.v1.server.controller.RedactionController;
import com.iqser.red.service.redaction.v1.server.service.AnalyzeService; import com.iqser.red.service.redaction.v1.server.service.AnalyzeService;
import com.iqser.red.service.redaction.v1.server.service.DictionaryCacheService;
import com.iqser.red.service.redaction.v1.server.service.DocumentSearchService; import com.iqser.red.service.redaction.v1.server.service.DocumentSearchService;
import com.iqser.red.service.redaction.v1.server.service.UnprocessedChangesService; import com.iqser.red.service.redaction.v1.server.service.UnprocessedChangesService;
import com.iqser.red.service.redaction.v1.server.service.websocket.RedisSyncedWebSocketService; import com.iqser.red.service.redaction.v1.server.service.websocket.RedisSyncedWebSocketService;
@ -208,10 +207,6 @@ public abstract class AbstractRedactionIntegrationTest {
@Autowired @Autowired
protected TenantMongoLiquibaseExecutor tenantMongoLiquibaseExecutor; protected TenantMongoLiquibaseExecutor tenantMongoLiquibaseExecutor;
@Autowired
DictionaryCacheService dictionaryCacheService;
protected final Map<String, List<String>> dictionary = new HashMap<>(); protected final Map<String, List<String>> dictionary = new HashMap<>();
protected final Map<String, List<String>> dossierDictionary = new HashMap<>(); protected final Map<String, List<String>> dossierDictionary = new HashMap<>();
protected final Map<String, List<String>> falsePositive = new HashMap<>(); protected final Map<String, List<String>> falsePositive = new HashMap<>();
@ -243,7 +238,7 @@ public abstract class AbstractRedactionIntegrationTest {
public static void init() { public static void init() {
synchronized (PDFNet.class) { synchronized (PDFNet.class) {
PDFNet.initialize("demo:admin@knecon.com:7ed45ca80200000000c1e1765b94afccf5bbc0cb8582fd932c602e286a"); PDFNet.initialize("demo:1650351709282:7bd235e003000000004ec28a6743e1163a085e2115de2536ab6e2cfe5a");
} }
} }
@ -278,7 +273,6 @@ public abstract class AbstractRedactionIntegrationTest {
} }
entityLogDocumentRepository.deleteAll(); entityLogDocumentRepository.deleteAll();
entityLogEntryDocumentRepository.deleteAll(); entityLogEntryDocumentRepository.deleteAll();
dictionaryCacheService.clearAllCaches();
} }

View File

@ -32,7 +32,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -62,30 +61,22 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp
import com.iqser.red.service.redaction.v1.server.client.DictionaryClient; 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.LegalBasisClient;
import com.iqser.red.service.redaction.v1.server.client.RulesClient; import com.iqser.red.service.redaction.v1.server.client.RulesClient;
import com.iqser.red.service.redaction.v1.server.model.NerEntities;
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary; import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryFactory;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrement; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryIncrement;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryModel; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryModel;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryVersion; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryVersion;
import com.iqser.red.service.redaction.v1.server.queue.RedactionMessageReceiver;
import com.iqser.red.service.redaction.v1.server.service.AnalyzeService; import com.iqser.red.service.redaction.v1.server.service.AnalyzeService;
import com.iqser.red.service.redaction.v1.server.service.DictionaryService; import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
import com.iqser.red.service.redaction.v1.server.service.websocket.RedisSyncedWebSocketService; import com.iqser.red.service.redaction.v1.server.service.websocket.RedisSyncedWebSocketService;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService; import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import com.iqser.red.service.redaction.v1.server.testcontainers.MongoDBTestContainer; import com.iqser.red.service.redaction.v1.server.testcontainers.MongoDBTestContainer;
import com.iqser.red.service.redaction.v1.server.utils.LayoutParsingRequestProvider;
import com.iqser.red.storage.commons.service.StorageService; import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.keycloakcommons.security.TenantAuthenticationManagerResolver; import com.knecon.fforesight.keycloakcommons.security.TenantAuthenticationManagerResolver;
import com.knecon.fforesight.mongo.database.commons.liquibase.TenantMongoLiquibaseExecutor; import com.knecon.fforesight.mongo.database.commons.liquibase.TenantMongoLiquibaseExecutor;
import com.knecon.fforesight.mongo.database.commons.service.MongoConnectionProvider; import com.knecon.fforesight.mongo.database.commons.service.MongoConnectionProvider;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingRequest;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingPipeline;
import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.TenantProvider; import com.knecon.fforesight.tenantcommons.TenantProvider;
import com.knecon.fforesight.tenantcommons.model.MongoDBConnection; import com.knecon.fforesight.tenantcommons.model.MongoDBConnection;
import com.pdftron.pdf.PDFNet;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -122,7 +113,7 @@ import lombok.extern.slf4j.Slf4j;
FileType.DOCUMENT_POSITION, FileType.DOCUMENT_POSITION,
FileType.DOCUMENT_STRUCTURE, FileType.DOCUMENT_STRUCTURE,
FileType.DOCUMENT_TEXT); FileType.DOCUMENT_TEXT);
Path dossierTemplateToUse = Path.of("/home/kschuettler/iqser/business-logic/redactmanager/prod-cp-eu-reg/EFSA_sanitisation_pre_GFL_v1"); // Add your dossier-template here Path dossierTemplateToUse = Path.of("/home/kschuettler/Downloads/New Folder/DOSSIER_TEMPLATE"); // Add your dossier-template here
ObjectMapper mapper = ObjectMapperFactory.create(); ObjectMapper mapper = ObjectMapperFactory.create();
final String TENANT_ID = "tenant"; final String TENANT_ID = "tenant";
TestDossierTemplate testDossierTemplate; TestDossierTemplate testDossierTemplate;
@ -157,28 +148,13 @@ import lombok.extern.slf4j.Slf4j;
private MongoConnectionProvider mongoConnectionProvider; private MongoConnectionProvider mongoConnectionProvider;
@MockBean @MockBean
private TenantProvider tenantProvider; private TenantProvider tenantProvider;
@Autowired
private DictionaryFactory dictionaryFactory;
@Autowired
private LayoutParsingPipeline layoutParsingPipeline;
@MockBean
private RedactionMessageReceiver redactionMessageReceiver;
@BeforeAll
public static void init() {
synchronized (PDFNet.class) {
PDFNet.initialize("demo:admin@knecon.com:7ed45ca80200000000c1e1765b94afccf5bbc0cb8582fd932c602e286a");
}
}
@Test @Test
@SneakyThrows @SneakyThrows
public void runAnalysisEnd2End() { public void runAnalysisEnd2End() {
String folder = "/home/kschuettler/Dokumente/analysisend2end/file0"; // Should contain all files from minio directly, still zipped. Can contain multiple files. String folder = "/home/kschuettler/Downloads/New Folder/436e4a2a-0ba3-4d3c-9944-c355f5c1cca2"; // Should contain all files from minio directly, still zipped. Can contain multiple files.
Path absoluteFolderPath; Path absoluteFolderPath;
if (folder.startsWith("files")) { // if it starts with "files" it is most likely in the resources folder, else it should be an absolute path if (folder.startsWith("files")) { // if it starts with "files" it is most likely in the resources folder, else it should be an absolute path
@ -192,18 +168,8 @@ import lombok.extern.slf4j.Slf4j;
List<AnalyzeRequest> analyzeRequests = prepareStorageForFolder(absoluteFolderPath); List<AnalyzeRequest> analyzeRequests = prepareStorageForFolder(absoluteFolderPath);
log.info("Found {} distinct fileIds with all required files", analyzeRequests.size()); log.info("Found {} distinct fileIds with all required files", analyzeRequests.size());
for (int i = 0; i < analyzeRequests.size(); i++) { for (int i = 0; i < analyzeRequests.size(); i++) {
AnalyzeRequest analyzeRequest = analyzeRequests.get(i);
Path nerEntitiesFile = absoluteFolderPath.resolve(analyzeRequest.getFileId() + ".NER_ENTITIES.json");
if (!Files.exists(nerEntitiesFile)) {
storageService.storeJSONObject(TenantContext.getTenantId(),
RedactionStorageService.StorageIdUtils.getStorageId(analyzeRequest.getDossierId(),
analyzeRequest.getFileId(),
FileType.NER_ENTITIES),
new NerEntities());
}
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
AnalyzeRequest analyzeRequest = analyzeRequests.get(i);
log.info("----------------------------------------------------------------------------------"); log.info("----------------------------------------------------------------------------------");
log.info("{}/{}: Starting analysis for file {}", i + 1, analyzeRequests.size(), analyzeRequest.getFileId()); log.info("{}/{}: Starting analysis for file {}", i + 1, analyzeRequests.size(), analyzeRequest.getFileId());
analyzeService.analyze(analyzeRequest); analyzeService.analyze(analyzeRequest);
@ -223,7 +189,7 @@ import lombok.extern.slf4j.Slf4j;
manualRedactionEntry.setValue("7232"); manualRedactionEntry.setValue("7232");
manualRedactionEntry.setReason( manualRedactionEntry.setReason(
"(Regulations (EU) 2016/679 and (EU) 2018/1725 shall apply to the processing of personal data carried out pursuant to this Regulation. Any personal data made public pursuant to Article 38 of this Regulation and this Article shall only be used to ensure the transparency of the risk assessment under this Regulation and shall not be further processed in a manner that is incompatible with these purposes, in accordance with point (b) of Article 5(1) of Regulation (EU) 2016/679 and point (b) of Article 4(1) of Regulation (EU) 2018/1725, as the case may be)"); "(Regulations (EU) 2016/679 and (EU) 2018/1725 shall apply to the processing of personal data carried out pursuant to this Regulation. Any personal data made public pursuant to Article 38 of this Regulation and this Article shall only be used to ensure the transparency of the risk assessment under this Regulation and shall not be further processed in a manner that is incompatible with these purposes, in accordance with point (b) of Article 5(1) of Regulation (EU) 2016/679 and point (b) of Article 4(1) of Regulation (EU) 2018/1725, as the case may be)");
manualRedactionEntry.setLegalBasis("personal_data_geolocation_article_39e3"); manualRedactionEntry.setLegalBasis("Article 39(e)(3) of Regulation (EC) No 178/2002");
manualRedactionEntry.setProcessedDate(OffsetDateTime.now()); manualRedactionEntry.setProcessedDate(OffsetDateTime.now());
manualRedactionEntry.setRequestDate(OffsetDateTime.now()); manualRedactionEntry.setRequestDate(OffsetDateTime.now());
manualRedactionEntry.setPositions(List.of(Rectangle.builder().topLeftX(332.134f).topLeftY(689.72f).width(26.688f).height(13.872f).page(1).build())); manualRedactionEntry.setPositions(List.of(Rectangle.builder().topLeftX(332.134f).topLeftY(689.72f).width(26.688f).height(13.872f).page(1).build()));
@ -284,12 +250,17 @@ import lombok.extern.slf4j.Slf4j;
testDossierTemplate = new TestDossierTemplate(dossierTemplateToUse); testDossierTemplate = new TestDossierTemplate(dossierTemplateToUse);
when(dictionaryService.updateDictionary(any(), any())).thenReturn(new DictionaryVersion(0, 0)); when(dictionaryService.updateDictionary(any(), any())).thenReturn(new DictionaryVersion(0, 0));
when(dictionaryService.getDictionary(any(), any())).thenReturn(testDossierTemplate.testDictionary); when(dictionaryService.getDeepCopyDictionary(any(), any())).thenReturn(testDossierTemplate.testDictionary);
when(dictionaryService.getDictionaryIncrements(any(), any(), any())).thenReturn(new DictionaryIncrement(Collections.emptySet(), new DictionaryVersion(0, 0))); when(dictionaryService.getDictionaryIncrements(any(), any(), any())).thenReturn(new DictionaryIncrement(Collections.emptySet(), new DictionaryVersion(0, 0)));
when(dictionaryService.isHint(any(String.class), any())).thenAnswer(invocation -> { when(dictionaryService.isHint(any(String.class), any())).thenAnswer(invocation -> {
String type = invocation.getArgument(0); String type = invocation.getArgument(0);
return testDossierTemplate.testDictionary.isHint(type); return testDossierTemplate.testDictionary.isHint(type);
}); });
when(dictionaryService.getColor(any(String.class), any())).thenAnswer(invocation -> {
String type = invocation.getArgument(0);
return testDossierTemplate.testDictionary.getType(type).getColor();
});
when(dictionaryService.getNotRedactedColor(any())).thenReturn(new float[]{0.2f, 0.2f, 0.2f});
when(rulesClient.getVersion(testDossierTemplate.id, RuleFileType.ENTITY)).thenReturn(System.currentTimeMillis()); when(rulesClient.getVersion(testDossierTemplate.id, RuleFileType.ENTITY)).thenReturn(System.currentTimeMillis());
when(rulesClient.getRules(testDossierTemplate.id, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(testDossierTemplate.rules)); when(rulesClient.getRules(testDossierTemplate.id, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(testDossierTemplate.rules));
@ -334,7 +305,7 @@ import lombok.extern.slf4j.Slf4j;
AnalyzeRequest request = new AnalyzeRequest(); AnalyzeRequest request = new AnalyzeRequest();
request.setDossierId(UUID.randomUUID().toString()); request.setDossierId(UUID.randomUUID().toString());
request.setFileId(fileName); request.setFileId(UUID.randomUUID().toString());
request.setDossierTemplateId(testDossierTemplate.id); request.setDossierTemplateId(testDossierTemplate.id);
request.setAnalysisNumber(-1); request.setAnalysisNumber(-1);
@ -351,72 +322,18 @@ import lombok.extern.slf4j.Slf4j;
Set<FileType> missingFileTypes = Sets.difference(REQUIRED_FILES, uploadedFileTypes); Set<FileType> missingFileTypes = Sets.difference(REQUIRED_FILES, uploadedFileTypes);
if (!missingFileTypes.isEmpty() && !missingFileTypes.contains(FileType.ORIGIN)) { if (!missingFileTypes.isEmpty()) {
runLayoutParsingAndSaveFilesToFolder(folder, uploadedFileTypes, request); log.error("Folder {} is missing files of type {}",
folder.toFile(),
missingFileTypes.stream()
.map(Enum::toString)
.collect(Collectors.joining(", ")));
return Optional.empty();
} }
// if (!missingFileTypes.isEmpty()) {
// log.error("Folder {} is missing files of type {}",
// folder.toFile(),
// missingFileTypes.stream()
// .map(Enum::toString)
// .collect(Collectors.joining(", ")));
// return Optional.empty();
// }
return Optional.of(request); return Optional.of(request);
} }
private void runLayoutParsingAndSaveFilesToFolder(Path folder, Set<FileType> uploadedFileTypes, AnalyzeRequest request) throws IOException {
uploadImageAndTableFilesIfMissing(uploadedFileTypes, request);
LayoutParsingRequest layoutParsingRequest = LayoutParsingRequestProvider.build(LayoutParsingType.DOCUMINE_OLD, request);
layoutParsingPipeline.parseLayoutAndSaveFilesToStorage(layoutParsingRequest);
try {
storeFileFromStorage(TENANT_ID, layoutParsingRequest.structureFileStorageId(), folder);
storeFileFromStorage(TENANT_ID, layoutParsingRequest.textBlockFileStorageId(), folder);
storeFileFromStorage(TENANT_ID, layoutParsingRequest.positionBlockFileStorageId(), folder);
storeFileFromStorage(TENANT_ID, layoutParsingRequest.pageFileStorageId(), folder);
} catch (IOException e) {
log.error("Failed to store files from storage to folder {}", folder, e);
}
}
private void uploadImageAndTableFilesIfMissing(Set<FileType> uploadedFileTypes, AnalyzeRequest request) throws IOException {
if (!uploadedFileTypes.contains(FileType.TABLES)) {
var cvServiceResponse = "files/cv_service_empty_response.json";
ClassPathResource cvServiceResponseFileResource = new ClassPathResource(cvServiceResponse);
storageService.storeObject(TenantContext.getTenantId(),
RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.TABLES),
cvServiceResponseFileResource.getInputStream());
}
if (!uploadedFileTypes.contains(FileType.IMAGE_INFO)) {
var imageServiceResponse = "files/empty_image_response.json";
ClassPathResource imageServiceResponseFileResource = new ClassPathResource(imageServiceResponse);
storageService.storeObject(TenantContext.getTenantId(),
RedactionStorageService.StorageIdUtils.getStorageId(request.getDossierId(), request.getFileId(), FileType.IMAGE_INFO),
imageServiceResponseFileResource.getInputStream());
}
}
private void storeFileFromStorage(String tenantId, String storageId, Path folder) throws IOException {
var inputStream = storageService.getObject(tenantId, storageId);
try (FileOutputStream fileOut = new FileOutputStream(folder.toString() + "/" + storageId.split("/")[1])) {
fileOut.write(inputStream.getContentAsByteArray());
} catch (IOException e) {
e.printStackTrace();
}
log.info("Stored file {} to {}", storageId, folder);
}
private static Stream<FileToUpload> findFilesToUpload(String fileName, Path folder, Set<FileType> endingsToUpload) throws IOException { private static Stream<FileToUpload> findFilesToUpload(String fileName, Path folder, Set<FileType> endingsToUpload) throws IOException {
return Files.walk(folder) return Files.walk(folder)
@ -505,7 +422,7 @@ import lombok.extern.slf4j.Slf4j;
componentRules = new String(Files.readAllBytes(componentRuleFile.toPath())); componentRules = new String(Files.readAllBytes(componentRuleFile.toPath()));
} }
testDictionary = dictionaryFactory.create(dictionaries, new DictionaryVersion(0, 0)); testDictionary = new Dictionary(dictionaries, new DictionaryVersion(0, 0));
} }

View File

@ -225,14 +225,14 @@ public class DictionaryServiceTest {
when(dictionaryClient.getDictionaryForType("dossierType", 0L)).thenReturn(dossierType); when(dictionaryClient.getDictionaryForType("dossierType", 0L)).thenReturn(dossierType);
dictionaryService.updateDictionary("dtId", "dossierId"); dictionaryService.updateDictionary("dtId", "dossierId");
var dict = dictionaryService.getDictionary("dtId", "dossierId"); var dict = dictionaryService.getDeepCopyDictionary("dtId", "dossierId");
assertThat(dict.getDictionaryModels().size()).isEqualTo(1); assertThat(dict.getDictionaryModels().size()).isEqualTo(2);
var dictModel = dict.getDictionaryModels() var dictModel = dict.getDictionaryModels()
.get(0); .get(0);
assertThat(dictModel.getType()).isEqualTo(type); assertThat(dictModel.getType()).isEqualTo(type);
assertThat(dictModel.getEntries().size()).isEqualTo(3); assertThat(dictModel.getEntries().size()).isEqualTo(3);
dictModel.getEntries() dictModel.getEntries()
.forEach(entry -> assertThat(entry.getTypeId()).isEqualTo("dossierType")); .forEach(entry -> assertThat(entry.getTypeId()).isEqualTo(dtType.getTypeId()));
} }
} }

View File

@ -261,6 +261,7 @@ public class DocumineFloraTest extends AbstractRedactionIntegrationTest {
} }
@Disabled
@Test @Test
public void testDoseMortalityExtraction() { public void testDoseMortalityExtraction() {

View File

@ -14,7 +14,6 @@ import java.util.UUID;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -39,7 +38,6 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp
import com.iqser.red.service.redaction.v1.server.annotate.AnnotateRequest; import com.iqser.red.service.redaction.v1.server.annotate.AnnotateRequest;
import com.iqser.red.service.redaction.v1.server.annotate.AnnotateResponse; import com.iqser.red.service.redaction.v1.server.annotate.AnnotateResponse;
import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils; import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils;
import com.iqser.red.service.redaction.v1.server.service.DictionaryCacheService;
import com.iqser.red.service.redaction.v1.server.service.DictionaryService; import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService; import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
@ -56,9 +54,6 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
@Autowired @Autowired
DictionaryService dictionaryService; DictionaryService dictionaryService;
@Autowired
DictionaryCacheService dictionaryCacheService;
@BeforeEach @BeforeEach
public void stubClients() { public void stubClients() {
@ -110,7 +105,7 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl"); String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES)); when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
dossierDictionary.put(PUBLISHED_INFORMATION_INDICATOR, new ArrayList<>()); dossierDictionary.put(PUBLISHED_INFORMATION_INDICATOR, new ArrayList<>());
dictionaryCacheService.clearAllCaches(); dictionaryService.clearTenantDictionaryCache();
AnalyzeRequest request = uploadFileToStorage("files/syngenta/CustomerFiles/SYNGENTA_EFSA_sanitisation_GFL_v1_moreSections.pdf"); AnalyzeRequest request = uploadFileToStorage("files/syngenta/CustomerFiles/SYNGENTA_EFSA_sanitisation_GFL_v1_moreSections.pdf");
System.out.println("Start Full integration test"); System.out.println("Start Full integration test");
analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request); analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request);
@ -331,7 +326,6 @@ public class RedactionAcceptanceTest extends AbstractRedactionIntegrationTest {
@Test @Test
@SneakyThrows @SneakyThrows
@Disabled
void testNerEntitiesAfterReanalysis() { void testNerEntitiesAfterReanalysis() {
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl"); String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");

View File

@ -23,7 +23,6 @@ import java.time.ZoneOffset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -1262,7 +1261,7 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
.value("0049 331 441 551 14") .value("0049 331 441 551 14")
.requestDate(OffsetDateTime.now()) .requestDate(OffsetDateTime.now())
.fileId(TEST_FILE_ID) .fileId(TEST_FILE_ID)
.legalBasis("vertebrate_study_personal_data_geolocation_article_39e2") .legalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002")
.user("user") .user("user")
.build())) .build()))
.build()); .build());
@ -1315,7 +1314,7 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
.value("0049 331 441 551 14") .value("0049 331 441 551 14")
.requestDate(OffsetDateTime.now()) .requestDate(OffsetDateTime.now())
.fileId(TEST_FILE_ID) .fileId(TEST_FILE_ID)
.legalBasis("vertebrate_study_personal_data_geolocation_article_39e2") .legalBasis("Article 39(e)(2) of Regulation (EC) No 178/2002")
.user("user") .user("user")
.build())) .build()))
.recategorizations(Set.of(ManualRecategorization.builder() .recategorizations(Set.of(ManualRecategorization.builder()
@ -1654,46 +1653,35 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
.get(); .get();
request.setManualRedactions(ManualRedactions.builder() request.setManualRedactions(ManualRedactions.builder()
.entriesToAdd(Set.of(ManualRedactionEntry.builder()
.annotationId("newId")
.fileId(TEST_FILE_ID)
.user("user")
.requestDate(OffsetDateTime.now())
.value("David Ksenia")
.type("CBI_author")
.positions(List.of(Rectangle.builder().topLeftX(56.8f).topLeftY(295.2f).width(65.59f).height(12.64f).page(1).build()))
.build()))
.resizeRedactions(Set.of(ManualResizeRedaction.builder() .resizeRedactions(Set.of(ManualResizeRedaction.builder()
.updateDictionary(false) .updateDictionary(false)
.annotationId("newId") .annotationId(davidKsenia.getId())
.fileId(TEST_FILE_ID) .fileId(TEST_FILE_ID)
.user("user") .user("user")
.requestDate(OffsetDateTime.now()) .requestDate(OffsetDateTime.now())
.value("David") .value("David")
.positions(List.of(Rectangle.builder().topLeftX(56.8f).topLeftY(293.564f).width(29.2922f).height(15.408f).page(1).build())) .positions(List.of(Rectangle.builder()
.topLeftX(56.8f)
.topLeftY(293.564f)
.width(29.2922f)
.height(15.408f)
.page(1)
.build()))
.addToAllDossiers(false) .addToAllDossiers(false)
.build())) .build()))
.build()); .build());
analyzeService.reanalyze(request); analyzeService.reanalyze(request);
entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID); entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
var resizedEntity = entityLog.getEntityLogEntry() var resizedEntity = entityLog.getEntityLogEntry()
.stream()
.filter(e -> e.getId().equals("newId"))
.findFirst()
.get();
var removedEntity = entityLog.getEntityLogEntry()
.stream() .stream()
.filter(e -> e.getId().equals(davidKsenia.getId())) .filter(e -> e.getId().equals(davidKsenia.getId()))
.findFirst() .findFirst()
.get(); .get();
assertEquals(resizedEntity.getState(), EntryState.APPLIED);
assertEquals(EntryState.APPLIED, resizedEntity.getState()); assertEquals(resizedEntity.getValue(), "David");
assertEquals("David", resizedEntity.getValue()); assertEquals(1, resizedEntity.getManualChanges().size());
assertEquals(2, resizedEntity.getManualChanges().size()); assertEquals(resizedEntity.getManualChanges()
assertEquals(1, resizedEntity.getEngines().size()); .get(0).getManualRedactionType(), ManualRedactionType.RESIZE);
assertEquals(EntryState.REMOVED, removedEntity.getState());
} }
@ -1893,7 +1881,7 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
.get("startOffset"), "3793 -> 3790"); .get("startOffset"), "3793 -> 3790");
assertEquals(inLibraryOutlook.getChanges() assertEquals(inLibraryOutlook.getChanges()
.get(1).getPropertyChanges() .get(1).getPropertyChanges()
.get("legalBasis"), "personal_data_geolocation -> links_producer_applicant"); .get("legalBasis"), "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data) -> Reg (EC) No 1107/2009 Art. 63 (2e)");
assertEquals(inLibraryOutlook.getChanges() assertEquals(inLibraryOutlook.getChanges()
.get(1).getPropertyChanges() .get(1).getPropertyChanges()
.get("positions"), "[[171.748, 305.0, 103.296036, 12.642, 4]] -> [[159.364, 303.364, 115.68, 15.408, 4]]"); .get("positions"), "[[171.748, 305.0, 103.296036, 12.642, 4]] -> [[159.364, 303.364, 115.68, 15.408, 4]]");
@ -1926,7 +1914,7 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
.reason("(Regulations (EU) 2016/679 and (EU) 2018/1725 shall apply to the processing of personal data carried out pursuant to this Regulation. Any personal data made public pursuant to Article 38 of this Regulation and this Article shall only be used to ensure the transparency of the risk assessment under this Regulation and shall not be further processed in a manner that is incompatible with these purposes, in accordance with point (b) of Article 5(1) of Regulation (EU) 2016/679 and point (b) of Article 4(1) of Regulation (EU) 2018/1725, as the case may be)") .reason("(Regulations (EU) 2016/679 and (EU) 2018/1725 shall apply to the processing of personal data carried out pursuant to this Regulation. Any personal data made public pursuant to Article 38 of this Regulation and this Article shall only be used to ensure the transparency of the risk assessment under this Regulation and shall not be further processed in a manner that is incompatible with these purposes, in accordance with point (b) of Article 5(1) of Regulation (EU) 2016/679 and point (b) of Article 4(1) of Regulation (EU) 2018/1725, as the case may be)")
.addToDossierDictionary(false) .addToDossierDictionary(false)
.addToDictionary(false) .addToDictionary(false)
.legalBasis("personal_data_geolocation_article_39e3") .legalBasis("Article 39(e)(3) of Regulation (EC) No 178/2002")
.rectangle(false) .rectangle(false)
.positions(List.of(Rectangle.builder() .positions(List.of(Rectangle.builder()
.topLeftX(270.844f) .topLeftX(270.844f)
@ -2273,14 +2261,14 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
manualRedactionEntry.setValue(valueToAdd); manualRedactionEntry.setValue(valueToAdd);
manualRedactionEntry.setReason("Author found, removed by manual override"); manualRedactionEntry.setReason("Author found, removed by manual override");
manualRedactionEntry.setSection("Header: This is my test"); manualRedactionEntry.setSection("Header: This is my test");
manualRedactionEntry.setLegalBasis("personal_data_geolocation_article_39e3"); manualRedactionEntry.setLegalBasis("Article 39(e)(3) of Regulation (EC) No 178/2002");
manualRedactionEntry.setTextBefore("Lorem My Ipsum "); manualRedactionEntry.setTextBefore("Lorem My Ipsum ");
manualRedactionEntry.setTextAfter("Crandu Seku Laku"); manualRedactionEntry.setTextAfter("Crandu Seku Laku");
manualRedactionEntry.setPositions(localfullPositions); manualRedactionEntry.setPositions(localfullPositions);
ManualForceRedaction forceRequest = new ManualForceRedaction(); ManualForceRedaction forceRequest = new ManualForceRedaction();
forceRequest.setAnnotationId(manualAddId); forceRequest.setAnnotationId(manualAddId);
forceRequest.setLegalBasis("personal_data_geolocation_article_39e3"); forceRequest.setLegalBasis("Article 39(e)(3) of Regulation (EC) No 178/2002");
forceRequest.setUser("test"); forceRequest.setUser("test");
forceRequest.setRequestDate(OffsetDateTime.now()); forceRequest.setRequestDate(OffsetDateTime.now());
@ -2365,25 +2353,6 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
} }
@Test
public void testPurityRule() {
String EFSA_SANITISATION_RULES = loadFromClassPath("drools/efsa_sanitisation.drl");
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(EFSA_SANITISATION_RULES));
AnalyzeRequest request = uploadFileToStorage("files/new/crafted document.pdf");
analyzeDocumentStructure(LayoutParsingType.REDACT_MANAGER, request);
analyzeService.analyze(request);
var entityLog = redactionStorageService.getEntityLog(TEST_DOSSIER_ID, TEST_FILE_ID);
var entriesCount = entityLog.getEntityLogEntry()
.stream()
.filter(e -> e.getValue().toLowerCase(Locale.ENGLISH).startsWith("purity"))
.collect(Collectors.toList()).size();
assertEquals(7, entriesCount);
}
private IdRemoval getIdRemoval(String id) { private IdRemoval getIdRemoval(String id) {
return IdRemoval.builder() return IdRemoval.builder()
@ -2407,7 +2376,7 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
manualRedactionEntry.setRectangle(false); manualRedactionEntry.setRectangle(false);
manualRedactionEntry.setRequestDate(OffsetDateTime.now()); manualRedactionEntry.setRequestDate(OffsetDateTime.now());
manualRedactionEntry.setValue("assessment"); manualRedactionEntry.setValue("assessment");
manualRedactionEntry.setLegalBasis("manufacturing_production_process"); manualRedactionEntry.setLegalBasis("Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)");
manualRedactionEntry.setReason(reason); manualRedactionEntry.setReason(reason);
manualRedactionEntry.setPositions(positions); manualRedactionEntry.setPositions(positions);
return manualRedactionEntry; return manualRedactionEntry;
@ -2424,7 +2393,7 @@ public class RedactionIntegrationTest extends RulesIntegrationTest {
manualRedactionEntry.setRectangle(false); manualRedactionEntry.setRectangle(false);
manualRedactionEntry.setRequestDate(OffsetDateTime.now()); manualRedactionEntry.setRequestDate(OffsetDateTime.now());
manualRedactionEntry.setValue("Comments on the assessment report"); manualRedactionEntry.setValue("Comments on the assessment report");
manualRedactionEntry.setLegalBasis("manufacturing_production_process"); manualRedactionEntry.setLegalBasis("Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)");
manualRedactionEntry.setReason( manualRedactionEntry.setReason(
"the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety"); "the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety");
manualRedactionEntry.setPositions(positions); manualRedactionEntry.setPositions(positions);

View File

@ -1,253 +0,0 @@
package com.iqser.red.service.redaction.v1.server.document.graph;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import com.hankcs.algorithm.AhoCorasickDoubleArrayTrie;
import com.iqser.red.service.redaction.v1.server.model.dictionary.*;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
public class DictionarySearchImplementationsTest {
private static final int LARGE_TEXT_REPETITIONS = 50_000;
private static final int MAX_PII_ENTRY_COUNT = 500_000;
private static final String LARGE_TEXT_SAMPLE = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ "Entity_1 match text. Recommendation_1 also here. Random text continues. ";
// Dictionary identifiers
protected static final String VERTEBRATE_INDICATOR = "vertebrate";
protected static final String DICTIONARY_ADDRESS = "CBI_address";
protected static final String DICTIONARY_AUTHOR = "CBI_author";
protected static final String DICTIONARY_SPONSOR = "CBI_sponsor";
protected static final String DICTIONARY_PII = "PII";
protected static final String NO_REDACTION_INDICATOR = "no_redaction_indicator";
protected static final String REDACTION_INDICATOR = "redaction_indicator";
protected static final String HINT_ONLY_INDICATOR = "hint_only";
protected static final String MUST_REDACT_INDICATOR = "must_redact";
protected static final String PUBLISHED_INFORMATION_INDICATOR = "published_information";
protected static final String TEST_METHOD_INDICATOR = "test_method";
protected static final String PURITY_INDICATOR = "purity";
@Test
public void performanceTestWithRealDictionaries() {
// Load dictionaries from files
Map<String, List<String>> loadedDictionaries = loadDictionaries();
Map<DictionaryIdentifier, List<String>> dictionaryValues = new HashMap<>();
Random random = new Random();
// Randomly assign EntityType.ENTITY or EntityType.RECOMMENDATION to dictionaries
for (Map.Entry<String, List<String>> entry : loadedDictionaries.entrySet()) {
String dictionaryName = entry.getKey();
List<String> dictionaryTerms = entry.getValue();
EntityType entityType = random.nextBoolean() ? EntityType.ENTITY : EntityType.RECOMMENDATION;
boolean caseSensitive = random.nextBoolean();
DictionaryIdentifier identifier = new DictionaryIdentifier(dictionaryName, entityType, true, caseSensitive);
dictionaryValues.put(identifier, dictionaryTerms);
}
// **Added dummy dictionaries as per request**
// Case-sensitive dictionary containing "Entity_1"
DictionaryIdentifier entity1Identifier = new DictionaryIdentifier("dummy_case_sensitive", EntityType.ENTITY, true, true // Case-sensitive
);
dictionaryValues.put(entity1Identifier, List.of("Entity_1"));
// Case-insensitive dictionary containing "recommendation_1"
DictionaryIdentifier recommendation1Identifier = new DictionaryIdentifier("dummy_case_insensitive", EntityType.RECOMMENDATION, true, false // Case-insensitive
);
dictionaryValues.put(recommendation1Identifier, List.of("recommendation_1"));
// Measure construction time for TrieDictionarySearch
long trieDictionaryConstructionStart = System.currentTimeMillis();
DoubleTrieDictionarySearch doubleTrieDictionarySearchImpl = new DoubleTrieDictionarySearch(dictionaryValues);
long trieDictionaryConstructionDuration = System.currentTimeMillis() - trieDictionaryConstructionStart;
// Measure construction time for AnotherTrieDictionarySearch
long anotherTrieConstructionStart = System.currentTimeMillis();
Map<String, List<DictionaryIdentifierWithKeyword>> keyWordToIdentifiersMap = computeStringIdentifiersMap(dictionaryValues);
AhoCorasickMapDictionarySearch ahoCorasickMapDictionarySearchImpl = new AhoCorasickMapDictionarySearch(keyWordToIdentifiersMap);
long anotherTrieConstructionDuration = System.currentTimeMillis() - anotherTrieConstructionStart;
// Measure construction time for SearchImplementations
long searchTrieConstructionStart = System.currentTimeMillis();
List<SearchImplementation> searchImplementations = dictionaryValues.entrySet()
.stream()
.map(entry -> new SearchImplementation(entry.getValue(), !entry.getKey().caseSensitive()))
.toList();
long searchTrieConstructionDuration = System.currentTimeMillis() - searchTrieConstructionStart;
// Measure construction time for DoubleArrayTrieDictionarySearch
long doubleArrayTrieConstructionStart = System.currentTimeMillis();
DoubleArrayTrieDictionarySearch doubleArrayTrieSearchImpl = new DoubleArrayTrieDictionarySearch(keyWordToIdentifiersMap);
long doubleArrayTrieConstructionDuration = System.currentTimeMillis() - doubleArrayTrieConstructionStart;
String largeText = LARGE_TEXT_SAMPLE.repeat(LARGE_TEXT_REPETITIONS);
// Measure search time for TrieDictionarySearch
long trieDictionarySearchStart = System.currentTimeMillis();
List<DoubleTrieDictionarySearch.MatchTextRange> trieDictionaryMatches = doubleTrieDictionarySearchImpl.getBoundariesAsList(largeText);
long trieDictionarySearchDuration = System.currentTimeMillis() - trieDictionarySearchStart;
// Measure search time for AnotherTrieDictionarySearch
long anotherTrieSearchStart = System.currentTimeMillis();
List<DictionarySearch.MatchTextRange> anotherTrieMatches = ahoCorasickMapDictionarySearchImpl.getBoundaries(largeText)
.toList();
long anotherTrieSearchDuration = System.currentTimeMillis() - anotherTrieSearchStart;
// Measure search time for SearchImplementations
long searchImplStart = System.currentTimeMillis();
List<TextRange> searchMatches = new ArrayList<>();
for (SearchImplementation searchImpl : searchImplementations) {
searchMatches.addAll(searchImpl.getBoundaries(largeText));
}
long searchImplDuration = System.currentTimeMillis() - searchImplStart;
// Measure search time for DoubleArrayTrieDictionarySearch
long doubleArrayTrieSearchStart = System.currentTimeMillis();
List<DictionarySearch.MatchTextRange> doubleArrayTrieMatches = doubleArrayTrieSearchImpl.getBoundariesAsList(largeText);
long doubleArrayTrieSearchDuration = System.currentTimeMillis() - doubleArrayTrieSearchStart;
// Output the performance results
System.out.println("\nTotal number of keywords is: " + keyWordToIdentifiersMap.size());
System.out.printf("DoubleTrieDictionarySearch construction took %d ms%n", trieDictionaryConstructionDuration);
System.out.printf("DoubleTrieDictionarySearch search took %d ms and found %d matches%n", trieDictionarySearchDuration, trieDictionaryMatches.size());
System.out.println();
System.out.printf("AhoCorasickMapDictionarySearch construction took %d ms%n", anotherTrieConstructionDuration);
System.out.printf("AhoCorasickMapDictionarySearch search took %d ms and found %d matches%n", anotherTrieSearchDuration, anotherTrieMatches.size());
System.out.println();
System.out.printf("Multiple Tries construction took %d ms%n", searchTrieConstructionDuration);
System.out.printf("Combined SearchImplementations search took %d ms and found %d matches%n", searchImplDuration, searchMatches.size());
System.out.println();
System.out.printf("DoubleArrayTrieDictionarySearch construction took %d ms%n", doubleArrayTrieConstructionDuration);
System.out.printf("DoubleArrayTrieDictionarySearch search took %d ms and found %d matches%n", doubleArrayTrieSearchDuration, doubleArrayTrieMatches.size());
System.out.println();
// Assert that all implementations found matches
assert !trieDictionaryMatches.isEmpty()
&& !anotherTrieMatches.isEmpty()
&& !searchMatches.isEmpty()
&& !doubleArrayTrieMatches.isEmpty() : "All implementations should find entities.";
// Ensure all implementations found the same number of matches
int expectedMatches = trieDictionaryMatches.size();
assertEquals(expectedMatches, anotherTrieMatches.size(), "Mismatch between DoubleTrieDictionarySearch and AhoCorasickMapDictionarySearch");
assertEquals(expectedMatches, searchMatches.size(), "Mismatch between DoubleTrieDictionarySearch and Combined SearchImplementations");
assertEquals(expectedMatches, doubleArrayTrieMatches.size(), "Mismatch between DoubleTrieDictionarySearch and DoubleArrayTrieDictionarySearch");
}
private Map<String, List<String>> loadDictionaries() {
Map<String, List<String>> dictionaries = new HashMap<>();
dictionaries.put(DICTIONARY_AUTHOR, loadDictionaryFromFile("dictionaries/CBI_author.txt"));
dictionaries.put(DICTIONARY_SPONSOR, loadDictionaryFromFile("dictionaries/CBI_sponsor.txt"));
dictionaries.put(VERTEBRATE_INDICATOR, loadDictionaryFromFile("dictionaries/vertebrate.txt"));
dictionaries.put(DICTIONARY_ADDRESS, loadDictionaryFromFile("dictionaries/CBI_address.txt"));
dictionaries.put(NO_REDACTION_INDICATOR, loadDictionaryFromFile("dictionaries/no_redaction_indicator.txt"));
dictionaries.put(REDACTION_INDICATOR, loadDictionaryFromFile("dictionaries/redaction_indicator.txt"));
dictionaries.put(HINT_ONLY_INDICATOR, loadDictionaryFromFile("dictionaries/hint_only.txt"));
dictionaries.put(MUST_REDACT_INDICATOR, loadDictionaryFromFile("dictionaries/must_redact.txt"));
dictionaries.put(PUBLISHED_INFORMATION_INDICATOR, loadDictionaryFromFile("dictionaries/published_information.txt"));
dictionaries.put(TEST_METHOD_INDICATOR, loadDictionaryFromFile("dictionaries/test_method.txt"));
List<String> piis = loadDictionaryFromFile("dictionaries/PII_large.txt");
dictionaries.put(DICTIONARY_PII, MAX_PII_ENTRY_COUNT < piis.size() ? piis.subList(0, MAX_PII_ENTRY_COUNT) : piis);
dictionaries.put(PURITY_INDICATOR, loadDictionaryFromFile("dictionaries/purity.txt"));
return dictionaries;
}
private List<String> loadDictionaryFromFile(String filePath) {
List<String> terms = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(filePath))))) {
terms = reader.lines()
.map(this::cleanDictionaryEntry)
.collect(Collectors.toList());
} catch (Exception e) {
System.err.println("Failed to load dictionary from " + filePath + ": " + e.getMessage());
}
return terms;
}
private String cleanDictionaryEntry(String entry) {
return entry.trim();
}
private static Map<String, List<DictionaryIdentifierWithKeyword>> computeStringIdentifiersMap(Map<DictionaryIdentifier, List<String>> dictionaryValues) {
Map<String, List<DictionaryIdentifierWithKeyword>> stringToIdentifiersMap = new HashMap<>();
for (Map.Entry<DictionaryIdentifier, List<String>> entry : dictionaryValues.entrySet()) {
DictionaryIdentifier identifier = entry.getKey();
List<String> values = entry.getValue();
for (String value : values) {
DictionaryIdentifierWithKeyword idWithKeyword = new DictionaryIdentifierWithKeyword(identifier, value);
stringToIdentifiersMap.computeIfAbsent(value.toLowerCase(Locale.ROOT), k -> new ArrayList<>()).add(idWithKeyword);
}
}
return stringToIdentifiersMap;
}
@Test
public void testMultiplePayloads() {
DoubleTrieDictionarySearch dictionarySearchImpl = new DoubleTrieDictionarySearch(Map.of(new DictionaryIdentifier("type1", EntityType.ENTITY, false, false),
List.of("apple", "banana"),
new DictionaryIdentifier("type2", EntityType.RECOMMENDATION, false, false),
List.of("apple", "orange"),
new DictionaryIdentifier("type3", EntityType.FALSE_POSITIVE, false, false),
List.of("apple", "kiwi")));
List<DoubleTrieDictionarySearch.MatchTextRange> dictionaryMatches = dictionarySearchImpl.getBoundariesAsList(
"an apple is delicious, a banana and a kiwi as well. orange is a color.");
assertEquals(dictionaryMatches.size(), 6);
}
@Test
public void testDoubleArrayTrie() {
Map<String, List<String>> map = new HashMap<>();
String[] keyArray = new String[]{"hers", "his", "she", "he"};
for (String key : keyArray) {
map.put(key, List.of(key, key, key));
}
AhoCorasickDoubleArrayTrie<List<String>> acdat = new AhoCorasickDoubleArrayTrie<>();
acdat.build(map);
final String text = "uhers";
List<AhoCorasickDoubleArrayTrie.Hit<List<String>>> wordList = acdat.parseText(text);
assertEquals(wordList.size(), 2);
}
}

View File

@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test;
import org.kie.api.runtime.KieSession; import org.kie.api.runtime.KieSession;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree; import com.iqser.red.service.redaction.v1.server.model.document.DocumentTree;
import com.iqser.red.service.redaction.v1.server.model.document.TextRange; import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
@ -29,10 +30,12 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell; import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock; import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.drools.KieSessionUpdater; import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
public class DocumentIEntityInsertionIntegrationTest extends BuildDocumentIntegrationTest { public class DocumentIEntityInsertionIntegrationTest extends BuildDocumentIntegrationTest {
@Autowired
private EntityEnrichmentService entityEnrichmentService;
private EntityCreationService entityCreationService; private EntityCreationService entityCreationService;
@Mock @Mock
@ -43,7 +46,7 @@ public class DocumentIEntityInsertionIntegrationTest extends BuildDocumentIntegr
public void createEntityCreationService() { public void createEntityCreationService() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
entityCreationService = new EntityCreationService(new KieSessionUpdater(kieSession)); entityCreationService = new EntityCreationService(entityEnrichmentService, kieSession);
} }

View File

@ -15,7 +15,6 @@ import java.util.List;
import org.apache.pdfbox.Loader; import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieContainer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -29,7 +28,9 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSON
import com.iqser.red.service.redaction.v1.server.logger.Context; import com.iqser.red.service.redaction.v1.server.logger.Context;
import com.iqser.red.service.redaction.v1.server.model.NerEntities; import com.iqser.red.service.redaction.v1.server.model.NerEntities;
import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary; import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionarySearch; import com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryModel;
import com.iqser.red.service.redaction.v1.server.model.dictionary.SearchImplementation;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;
@ -39,6 +40,7 @@ import com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBl
import com.iqser.red.service.redaction.v1.server.rules.RulesIntegrationTest; import com.iqser.red.service.redaction.v1.server.rules.RulesIntegrationTest;
import com.iqser.red.service.redaction.v1.server.service.DictionaryService; import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.service.drools.EntityDroolsExecutionService; import com.iqser.red.service.redaction.v1.server.service.drools.EntityDroolsExecutionService;
import com.iqser.red.service.redaction.v1.server.utils.PdfVisualisationUtility; import com.iqser.red.service.redaction.v1.server.utils.PdfVisualisationUtility;
import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantcommons.TenantContext;
@ -52,6 +54,8 @@ public class DocumentPerformanceIntegrationTest extends RulesIntegrationTest {
@Autowired @Autowired
private DictionaryService dictionaryService; private DictionaryService dictionaryService;
@Autowired
private EntityEnrichmentService entityEnrichmentService;
private EntityCreationService entityCreationService; private EntityCreationService entityCreationService;
@Autowired @Autowired
@ -65,7 +69,7 @@ public class DocumentPerformanceIntegrationTest extends RulesIntegrationTest {
@BeforeEach @BeforeEach
public void stubClients() { public void stubClients() {
entityCreationService = new EntityCreationService(); entityCreationService = new EntityCreationService(entityEnrichmentService);
TenantContext.setTenantId("redaction"); TenantContext.setTenantId("redaction");
when(rulesClient.getVersion(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(System.currentTimeMillis()); when(rulesClient.getVersion(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(System.currentTimeMillis());
@ -95,11 +99,15 @@ public class DocumentPerformanceIntegrationTest extends RulesIntegrationTest {
Document document = buildGraph(filename); Document document = buildGraph(filename);
dictionaryService.updateDictionary(TEST_DOSSIER_TEMPLATE_ID, TEST_DOSSIER_ID); dictionaryService.updateDictionary(TEST_DOSSIER_TEMPLATE_ID, TEST_DOSSIER_ID);
Dictionary dictionary = dictionaryService.getDictionary(TEST_DOSSIER_TEMPLATE_ID, TEST_DOSSIER_ID); Dictionary dictionary = dictionaryService.getDeepCopyDictionary(TEST_DOSSIER_TEMPLATE_ID, TEST_DOSSIER_ID);
long dictionarySearchStart = System.currentTimeMillis(); long dictionarySearchStart = System.currentTimeMillis();
List<TextEntity> foundEntities = new LinkedList<>(); List<TextEntity> foundEntities = new LinkedList<>();
findEntitiesWithSearchImplementation(document, dictionary.getDictionarySearch(), foundEntities); for (DictionaryModel model : dictionary.getDictionaryModels()) {
findEntitiesWithSearchImplementation(document, model.getEntriesSearch(), EntityType.ENTITY, foundEntities, model.getType());
findEntitiesWithSearchImplementation(document, model.getFalsePositiveSearch(), EntityType.FALSE_POSITIVE, foundEntities, model.getType());
findEntitiesWithSearchImplementation(document, model.getFalseRecommendationsSearch(), EntityType.FALSE_RECOMMENDATION, foundEntities, model.getType());
}
System.out.printf("Dictionary search took %d ms and found %d entities\n", System.currentTimeMillis() - dictionarySearchStart, foundEntities.size()); System.out.printf("Dictionary search took %d ms and found %d entities\n", System.currentTimeMillis() - dictionarySearchStart, foundEntities.size());
long graphInsertionStart = System.currentTimeMillis(); long graphInsertionStart = System.currentTimeMillis();
@ -126,7 +134,6 @@ public class DocumentPerformanceIntegrationTest extends RulesIntegrationTest {
@Test @Test
@SneakyThrows @SneakyThrows
@Disabled
public void testBuildTextBlockPerformance() { public void testBuildTextBlockPerformance() {
int n = 10000; int n = 10000;
@ -167,7 +174,7 @@ public class DocumentPerformanceIntegrationTest extends RulesIntegrationTest {
Document document = buildGraph(filename); Document document = buildGraph(filename);
dictionaryService.updateDictionary(TEST_DOSSIER_TEMPLATE_ID, TEST_DOSSIER_ID); dictionaryService.updateDictionary(TEST_DOSSIER_TEMPLATE_ID, TEST_DOSSIER_ID);
Dictionary dictionary = dictionaryService.getDictionary(TEST_DOSSIER_TEMPLATE_ID, TEST_DOSSIER_ID); Dictionary dictionary = dictionaryService.getDeepCopyDictionary(TEST_DOSSIER_TEMPLATE_ID, TEST_DOSSIER_ID);
int numberOfRuns = 1; int numberOfRuns = 1;
float totalSearchTime = 0; float totalSearchTime = 0;
@ -183,7 +190,11 @@ public class DocumentPerformanceIntegrationTest extends RulesIntegrationTest {
totalGraphTime += graphTime; totalGraphTime += graphTime;
var searchStart = System.currentTimeMillis(); var searchStart = System.currentTimeMillis();
findEntitiesWithSearchImplementation(document, dictionary.getDictionarySearch(), foundEntities); for (var model : dictionary.getDictionaryModels()) {
findEntitiesWithSearchImplementation(document, model.getEntriesSearch(), EntityType.ENTITY, foundEntities, model.getType());
findEntitiesWithSearchImplementation(document, model.getFalsePositiveSearch(), EntityType.FALSE_POSITIVE, foundEntities, model.getType());
findEntitiesWithSearchImplementation(document, model.getFalseRecommendationsSearch(), EntityType.FALSE_RECOMMENDATION, foundEntities, model.getType());
}
var searchTime = System.currentTimeMillis() - searchStart; var searchTime = System.currentTimeMillis() - searchStart;
totalSearchTime += searchTime; totalSearchTime += searchTime;
@ -261,12 +272,16 @@ public class DocumentPerformanceIntegrationTest extends RulesIntegrationTest {
} }
private void findEntitiesWithSearchImplementation(Document document, DictionarySearch dictionarySearch, List<TextEntity> foundEntities) { private void findEntitiesWithSearchImplementation(Document document,
SearchImplementation searchImplementation,
EntityType entityType,
List<TextEntity> foundEntities,
String type) {
TextBlock textBlock = document.getTextBlock(); TextBlock textBlock = document.getTextBlock();
dictionarySearch.getBoundaries(textBlock) searchImplementation.getBoundaries(textBlock)
.filter(match -> boundaryIsSurroundedBySeparators(textBlock, match.textRange())) .filter(boundary -> boundaryIsSurroundedBySeparators(textBlock, boundary))
.map(match -> TextEntity.initialEntityNode(match.textRange(), match.identifier().type(), match.identifier().entityType(), document)) .map(bounds -> TextEntity.initialEntityNode(bounds, type, entityType, document))
.forEach(foundEntities::add); .forEach(foundEntities::add);
} }

View File

@ -5,26 +5,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.iqser.red.service.redaction.v1.server.model.dictionary.SearchImplementation; import com.iqser.red.service.redaction.v1.server.model.dictionary.SearchImplementation;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
public class SearchImplementationTest extends BuildDocumentIntegrationTest { public class SearchImplementationTest extends BuildDocumentIntegrationTest {
@Test @Autowired
public void testSearchImplementationWithPunctuation() { private EntityEnrichmentService entityEnrichmentService;
Document document = buildGraph("files/Minimal Examples/TestPunctuation");
SearchImplementation searchImplementation = new SearchImplementation(List.of("Kuhn, J. O."), true);
EntityCreationService entityCreationService = new EntityCreationService();
List<TextEntity> entities = entityCreationService.bySearchImplementation(searchImplementation, "CBI_author", EntityType.ENTITY, document)
.toList();
assertEquals(2, entities.size());
}
@Test @Test
public void testSearchImplementationWithSingleEntry() { public void testSearchImplementationWithSingleEntry() {
@ -32,7 +26,7 @@ public class SearchImplementationTest extends BuildDocumentIntegrationTest {
Document document = buildGraph("files/syngenta/CustomerFiles/SYNGENTA_EFSA_sanitisation_GFL_v1_moreSections"); Document document = buildGraph("files/syngenta/CustomerFiles/SYNGENTA_EFSA_sanitisation_GFL_v1_moreSections");
SearchImplementation searchImplementation = new SearchImplementation(List.of("mydossierredaction"), true); SearchImplementation searchImplementation = new SearchImplementation(List.of("mydossierredaction"), true);
EntityCreationService entityCreationService = new EntityCreationService(); EntityCreationService entityCreationService = new EntityCreationService(entityEnrichmentService);
List<TextEntity> entities = entityCreationService.bySearchImplementation(searchImplementation, "dossier_redaction", EntityType.ENTITY, document) List<TextEntity> entities = entityCreationService.bySearchImplementation(searchImplementation, "dossier_redaction", EntityType.ENTITY, document)
.toList(); .toList();
assertEquals(2, entities.size()); assertEquals(2, entities.size());

View File

@ -12,6 +12,7 @@ import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
@ -20,6 +21,7 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType; import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService; import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import com.iqser.red.service.redaction.v1.server.utils.EntityVisualizationUtility; import com.iqser.red.service.redaction.v1.server.utils.EntityVisualizationUtility;
import com.knecon.fforesight.service.viewerdoc.service.PDFTronViewerDocumentService; import com.knecon.fforesight.service.viewerdoc.service.PDFTronViewerDocumentService;
@ -30,6 +32,10 @@ import lombok.SneakyThrows;
public class TableTest extends BuildDocumentIntegrationTest { public class TableTest extends BuildDocumentIntegrationTest {
private static final boolean DRAW_FILE = false; private static final boolean DRAW_FILE = false;
@Autowired
private EntityEnrichmentService entityEnrichmentService;
private EntityCreationService entityCreationService; private EntityCreationService entityCreationService;
private static final String TYPE_1 = "type1"; private static final String TYPE_1 = "type1";
@ -46,7 +52,7 @@ public class TableTest extends BuildDocumentIntegrationTest {
@BeforeEach @BeforeEach
public void createTable() { public void createTable() {
entityCreationService = new EntityCreationService(); entityCreationService = new EntityCreationService(entityEnrichmentService);
String fileName = "files/Minimal Examples/BasicTable.pdf"; String fileName = "files/Minimal Examples/BasicTable.pdf";

View File

@ -23,6 +23,7 @@ import com.iqser.red.service.redaction.v1.server.DeprecatedElementsFinder;
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings; import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.client.RulesClient; import com.iqser.red.service.redaction.v1.server.client.RulesClient;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleFileBluePrint; import com.iqser.red.service.redaction.v1.server.model.drools.RuleFileBluePrint;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.service.drools.DroolsValidationService; import com.iqser.red.service.redaction.v1.server.service.drools.DroolsValidationService;
import com.iqser.red.service.redaction.v1.server.service.drools.KieContainerCreationService; import com.iqser.red.service.redaction.v1.server.service.drools.KieContainerCreationService;
import com.iqser.red.service.redaction.v1.server.service.drools.RuleFileParser; import com.iqser.red.service.redaction.v1.server.service.drools.RuleFileParser;
@ -35,6 +36,8 @@ class DroolsValidationServiceTest {
@MockBean @MockBean
RulesClient rulesClient; RulesClient rulesClient;
@MockBean @MockBean
EntityEnrichmentService entityEnrichmentService;
@MockBean
RedactionServiceSettings redactionServiceSettings; RedactionServiceSettings redactionServiceSettings;
@ -452,7 +455,7 @@ class DroolsValidationServiceTest {
$entity.redact( $entity.redact(
"CBI.1.0", "CBI.1.0",
"Author found", "Author found",
"personal_data_geolocation_article_39e3" "Article 39(e)(3) of Regulation (EC) No 178/2002"
); );
end end
"""; """;

View File

@ -23,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -53,6 +54,7 @@ import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityTyp
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils; import com.iqser.red.service.redaction.v1.server.redaction.utils.OsUtils;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType; import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
import com.knecon.fforesight.tenantcommons.TenantContext; import com.knecon.fforesight.tenantcommons.TenantContext;
@ -65,7 +67,8 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
private static final String RULES = loadFromClassPath("drools/acceptance_rules.drl"); private static final String RULES = loadFromClassPath("drools/acceptance_rules.drl");
private static final String DM_RULES = loadFromClassPath("drools/documine_flora.drl"); private static final String DM_RULES = loadFromClassPath("drools/documine_flora.drl");
@Autowired
private EntityEnrichmentService entityEnrichmentService;
private EntityCreationService entityCreationService; private EntityCreationService entityCreationService;
@ -73,7 +76,7 @@ public class ManualChangesEnd2EndTest extends AbstractRedactionIntegrationTest {
@BeforeEach @BeforeEach
public void createServices() { public void createServices() {
entityCreationService = new EntityCreationService(); entityCreationService = new EntityCreationService(entityEnrichmentService);
} }

View File

@ -20,9 +20,12 @@ import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityTyp
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
public class ManualChangesUnitTest extends BuildDocumentIntegrationTest { public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
@Autowired
private EntityEnrichmentService entityEnrichmentService;
private EntityCreationService entityCreationService; private EntityCreationService entityCreationService;
@ -30,7 +33,7 @@ public class ManualChangesUnitTest extends BuildDocumentIntegrationTest {
@BeforeEach @BeforeEach
public void createServices() { public void createServices() {
entityCreationService = new EntityCreationService(); entityCreationService = new EntityCreationService(entityEnrichmentService);
} }

View File

@ -35,12 +35,15 @@ import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.service.DictionaryService; import com.iqser.red.service.redaction.v1.server.service.DictionaryService;
import com.iqser.red.service.redaction.v1.server.service.EntityLogCreatorService; import com.iqser.red.service.redaction.v1.server.service.EntityLogCreatorService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityFromPrecursorCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityFromPrecursorCreationService;
import lombok.SneakyThrows; import lombok.SneakyThrows;
public class PrecursorEntityTest extends BuildDocumentIntegrationTest { public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
@Autowired
private EntityEnrichmentService entityEnrichmentService;
@Autowired @Autowired
private EntityFromPrecursorCreationService entityFromPrecursorCreationService; private EntityFromPrecursorCreationService entityFromPrecursorCreationService;
@ -56,6 +59,7 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
public void stubMethods() { public void stubMethods() {
MockitoAnnotations.openMocks(this); MockitoAnnotations.openMocks(this);
when(dictionaryService.getColor(DICTIONARY_AUTHOR, TEST_DOSSIER_TEMPLATE_ID)).thenReturn(new float[]{0f, 0f, 0f});
when(dictionaryService.isHint(any(), any())).thenReturn(false); when(dictionaryService.isHint(any(), any())).thenReturn(false);
} }
@ -97,17 +101,6 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
assertTrue(context.entity().removed()); assertTrue(context.entity().removed());
} }
@Test
public void createFoundManualRedaction2() {
Document document = buildGraph("files/Minimal Examples/TestPunctuation");
EntityCreationService entityCreationService = new EntityCreationService();
List<TextEntity> tempEntities = entityCreationService.byString("Kuhn, J. O.", "CBI_author", EntityType.ENTITY, document)
.toList();
assertFalse(tempEntities.isEmpty());
assertEquals(2, tempEntities.size());
}
private DocumentAndEntity createNotFoundManualRedaction() { private DocumentAndEntity createNotFoundManualRedaction() {
@ -156,9 +149,9 @@ public class PrecursorEntityTest extends BuildDocumentIntegrationTest {
private DocumentAndEntity createFoundManualRedaction() { private DocumentAndEntity createFoundManualRedaction() {
Document document = buildGraph("files/syngenta/CustomerFiles/VV-919901.pdf"); Document document = buildGraph("files/syngenta/CustomerFiles/VV-919901.pdf");
EntityCreationService entityCreationService = new EntityCreationService(); EntityCreationService entityCreationService = new EntityCreationService(entityEnrichmentService);
List<TextEntity> tempEntities = entityCreationService.byString("To: Syngenta Ltd.", "temp", EntityType.TEMPORARY, document) List<TextEntity> tempEntities = entityCreationService.byString("To: Syngenta Ltd.", "temp", EntityType.ENTITY, document)
.toList(); .toList();
assertFalse(tempEntities.isEmpty()); assertFalse(tempEntities.isEmpty());
var tempEntity = tempEntities.get(0); var tempEntity = tempEntities.get(0);

View File

@ -218,7 +218,7 @@ public class LiveDataIntegrationTest {
dictionaryService.updateDictionary("dossierTemplateId", "dossierId"); dictionaryService.updateDictionary("dossierTemplateId", "dossierId");
var dict = dictionaryService.getDictionary("dossierTemplateId", "dossierId"); var dict = dictionaryService.getDeepCopyDictionary("dossierTemplateId", "dossierId");
assertThat(dict.getLocalAccessMap().size()).isEqualTo(12); assertThat(dict.getLocalAccessMap().size()).isEqualTo(12);
} }

View File

@ -19,6 +19,7 @@ import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import com.iqser.red.commons.jackson.ObjectMapperFactory; import com.iqser.red.commons.jackson.ObjectMapperFactory;
@ -31,6 +32,7 @@ import com.iqser.red.service.redaction.v1.server.model.document.entity.PositionO
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.service.document.NerEntitiesAdapter; import com.iqser.red.service.redaction.v1.server.service.document.NerEntitiesAdapter;
import com.iqser.red.service.redaction.v1.server.utils.PdfVisualisationUtility; import com.iqser.red.service.redaction.v1.server.utils.PdfVisualisationUtility;
@ -40,6 +42,8 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
class NerEntitiesAdapterTest extends BuildDocumentIntegrationTest { class NerEntitiesAdapterTest extends BuildDocumentIntegrationTest {
@Autowired
private EntityEnrichmentService entityEnrichmentService;
private EntityCreationService entityCreationService; private EntityCreationService entityCreationService;
@ -54,7 +58,7 @@ class NerEntitiesAdapterTest extends BuildDocumentIntegrationTest {
@BeforeEach @BeforeEach
public void createEntityCreationService() { public void createEntityCreationService() {
entityCreationService = new EntityCreationService(); entityCreationService = new EntityCreationService(entityEnrichmentService);
} }

View File

@ -109,41 +109,4 @@ public class RegExPatternTest {
} }
} }
@Test
public void testPurity() {
String text = "purity: 100% -> ok\n"
+ "purity: <100% -> ok\n"
+ "purity: 9% -> ok\n"
+ "purity: <200% -> not ok\n"
+ "purity 45%aa -> not ok\n"
+ "purity: <45% -> ok\n"
+ "purity: >45% -> ok\n"
+ "purity: 101% -> not ok\n"
+ "purity: 99.9% -> ok\n"
+ "purity: 99,9% -> ok\n"
+ "purity: 99,90% -> ok\n"
+ "purity: aa 45% -> not ok\n"
+ "purity: 99% -> ok\n"
+ "purity: 99.99% -> ok\n"
+ "purity: 100.00% -> ok?\n"
+ "purity: <=45% -> not ok\n"
+ "purity: >=45% -> not ok\n"
+ "purity: <>45% -> not ok\n"
+ "purity: =<45% -> not ok\n"
+ "purity: =>45% -> not ok\n"
+ "purity: aa45% -> not ok\n"
+ "purity: 045% -> not ok\n"
+ "purity: .45% -> not ok \n"
+ "purity: 1000% -> not ok";
String text2 = "Rule 39: Purity Hint Add Purity as Hint when Percent-Numbers is there Test Item: Soda Purity: 45% ← should be Hint Purity: <45% ← should be Hint Purity: >45% ← should be Hint Purity: 101% ← should ne be Hint because >100 % is not possible Purity: =>45% ← should be not Hint because additional symbols Purity: =<45% ← should be not Hint because additional symbols Purity: aa 45% ← should be not Hint because additional symbols Purity: 45% aa ← should be not Hint because additional symbols Purity: aa45% ← should be not Hint because additional symbols Purity: 45%aa ← should be not Hint because additional symbols Product-Code: EAK-L443 purity: 99% ← not Hint because case sensitive purity: >99% ← not Hint because case sensitive purity: <99% ← not Hint because case sensitive Supplier: GreenForce ";
Pattern p = Pattern.compile("(purity ?( of|\\(.{1,20}\\))?( ?:)?) [<>]{0,1}(100|([1-9]{1}[0-9]{0,1}([.,]{1}[0-9]{1,2})?)) ?% ", Pattern.CASE_INSENSITIVE);
Matcher matcher = p.matcher(text);
while (matcher.find()) {
String match = matcher.group(0);
String match1 = matcher.group(1);
System.out.println("Group 0: " + match);
System.out.println("Group 1: " + match1);
}
}
} }

View File

@ -25,7 +25,7 @@ import com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;
import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService; import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService;
import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;
import com.iqser.red.service.redaction.v1.server.service.drools.KieSessionUpdater; import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@Import(RulesIntegrationTest.TestConfiguration.class) @Import(RulesIntegrationTest.TestConfiguration.class)
@ -33,6 +33,8 @@ public class RulesIntegrationTest extends BuildDocumentIntegrationTest {
protected static final String RULES = "drools/rules.drl"; protected static final String RULES = "drools/rules.drl";
@Autowired
protected EntityEnrichmentService entityEnrichmentService;
@Autowired @Autowired
protected ManualChangesApplicationService manualChangesApplicationService; protected ManualChangesApplicationService manualChangesApplicationService;
protected EntityCreationService entityCreationService; protected EntityCreationService entityCreationService;
@ -80,7 +82,7 @@ public class RulesIntegrationTest extends BuildDocumentIntegrationTest {
Dictionary dict = Mockito.mock(Dictionary.class); Dictionary dict = Mockito.mock(Dictionary.class);
kieSession = kieContainer.newKieSession(); kieSession = kieContainer.newKieSession();
entityCreationService = new EntityCreationService(new KieSessionUpdater(kieSession)); entityCreationService = new EntityCreationService(entityEnrichmentService, kieSession);
kieSession.setGlobal("manualChangesApplicationService", manualChangesApplicationService); kieSession.setGlobal("manualChangesApplicationService", manualChangesApplicationService);
kieSession.setGlobal("entityCreationService", entityCreationService); kieSession.setGlobal("entityCreationService", entityCreationService);
kieSession.setGlobal("dictionary", dict); kieSession.setGlobal("dictionary", dict);

View File

@ -1,6 +1,5 @@
package com.iqser.red.service.redaction.v1.server.service.document; package com.iqser.red.service.redaction.v1.server.service.document;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@ -127,7 +126,7 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
UnprocessedManualEntity unprocessedManualEntity = optionalUnprocessedManualEntity.get(); UnprocessedManualEntity unprocessedManualEntity = optionalUnprocessedManualEntity.get();
assertEquals(unprocessedManualEntity.getTextBefore(), "was above the "); assertEquals(unprocessedManualEntity.getTextBefore(), "was above the ");
assertEquals(unprocessedManualEntity.getTextAfter(), " without PPE (34%"); assertEquals(unprocessedManualEntity.getTextAfter(), " without PPE (34%");
assertThat(unprocessedManualEntity.getSection()).contains("Paragraph: A9396G containing 960 g/L"); assertEquals(unprocessedManualEntity.getSection(), "[1, 1, 0]: Paragraph: A9396G containing 960 g/L");
assertEquals(unprocessedManualEntity.getPositions() assertEquals(unprocessedManualEntity.getPositions()
.get(0).x(), 355.53775f); .get(0).x(), 355.53775f);
assertEquals(unprocessedManualEntity.getPositions() assertEquals(unprocessedManualEntity.getPositions()
@ -174,7 +173,7 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
assertEquals(unprocessedManualEntities.get(0).getAnnotationId(), aoelId); assertEquals(unprocessedManualEntities.get(0).getAnnotationId(), aoelId);
assertEquals(unprocessedManualEntities.get(0).getTextAfter(), " without PPE (34%"); assertEquals(unprocessedManualEntities.get(0).getTextAfter(), " without PPE (34%");
assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "to EFSA guidance "); assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "to EFSA guidance ");
assertThat(unprocessedManualEntities.get(0).getSection()).contains("Paragraph: A9396G containing 960 g/L"); assertEquals(unprocessedManualEntities.get(0).getSection(), "[1, 1, 0]: Paragraph: A9396G containing 960 g/L");
assertEquals(unprocessedManualEntities.get(0).getPositions() assertEquals(unprocessedManualEntities.get(0).getPositions()
.get(0).x(), positions.get(0).getTopLeftX()); .get(0).x(), positions.get(0).getTopLeftX());
assertEquals(unprocessedManualEntities.get(0).getPositions() assertEquals(unprocessedManualEntities.get(0).getPositions()
@ -257,7 +256,7 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
assertTrue(resizedAoel.isPresent()); assertTrue(resizedAoel.isPresent());
assertEquals(resizedAoel.get().getTextAfter(), " (max. 43% of"); assertEquals(resizedAoel.get().getTextAfter(), " (max. 43% of");
assertEquals(resizedAoel.get().getTextBefore(), "is below the "); assertEquals(resizedAoel.get().getTextBefore(), "is below the ");
assertThat(resizedAoel.get().getSection()).contains("Paragraph: A9396G containing 960 g/L"); assertEquals(resizedAoel.get().getSection(), "[1, 1, 0]: Paragraph: A9396G containing 960 g/L");
assertEquals(resizedAoel.get().getPositions() assertEquals(resizedAoel.get().getPositions()
.get(0).x(), positions.get(0).getTopLeftX()); .get(0).x(), positions.get(0).getTopLeftX());
assertEquals(resizedAoel.get().getPositions() assertEquals(resizedAoel.get().getPositions()
@ -273,7 +272,7 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
assertTrue(cormsResized.isPresent()); assertTrue(cormsResized.isPresent());
assertEquals(cormsResized.get().getTextAfter(), " a NOAEL of"); assertEquals(cormsResized.get().getTextAfter(), " a NOAEL of");
assertEquals(cormsResized.get().getTextBefore(), "mg/kg bw/d. Furthermore "); assertEquals(cormsResized.get().getTextBefore(), "mg/kg bw/d. Furthermore ");
assertThat(cormsResized.get().getSection()).contains("Paragraph: The Co-RMS indicated the"); assertEquals(cormsResized.get().getSection(), "[0, 3]: Paragraph: The Co-RMS indicated the");
assertEquals(cormsResized.get().getPositions() assertEquals(cormsResized.get().getPositions()
.get(0).x(), positions2.get(0).getTopLeftX()); .get(0).x(), positions2.get(0).getTopLeftX());
assertEquals(cormsResized.get().getPositions() assertEquals(cormsResized.get().getPositions()
@ -289,7 +288,7 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
assertTrue(a9Resized.isPresent()); assertTrue(a9Resized.isPresent());
assertEquals(a9Resized.get().getTextAfter(), " were obtained from"); assertEquals(a9Resized.get().getTextAfter(), " were obtained from");
assertEquals(a9Resized.get().getTextBefore(), "data for S"); assertEquals(a9Resized.get().getTextBefore(), "data for S");
assertThat(a9Resized.get().getSection()).contains("Paragraph: A9396G containing 960 g/L"); assertEquals(a9Resized.get().getSection(), "[1, 1, 0]: Paragraph: A9396G containing 960 g/L");
assertEquals(a9Resized.get().getPositions() assertEquals(a9Resized.get().getPositions()
.get(0).x(), positions3.get(0).getTopLeftX()); .get(0).x(), positions3.get(0).getTopLeftX());
assertEquals(a9Resized.get().getPositions() assertEquals(a9Resized.get().getPositions()
@ -339,7 +338,7 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
assertEquals(unprocessedManualEntities.get(0).getAnnotationId(), aoelId); assertEquals(unprocessedManualEntities.get(0).getAnnotationId(), aoelId);
assertEquals(unprocessedManualEntities.get(0).getTextAfter(), " (max. 43% of"); assertEquals(unprocessedManualEntities.get(0).getTextAfter(), " (max. 43% of");
assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "is below the "); assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "is below the ");
assertThat(unprocessedManualEntities.get(0).getSection()).contains("Paragraph: A9396G containing 960 g/L"); assertEquals(unprocessedManualEntities.get(0).getSection(), "[1, 1, 0]: Paragraph: A9396G containing 960 g/L");
assertEquals(unprocessedManualEntities.get(0).getPositions() assertEquals(unprocessedManualEntities.get(0).getPositions()
.get(0).x(), positions.get(0).getTopLeftX()); .get(0).x(), positions.get(0).getTopLeftX());
assertEquals(unprocessedManualEntities.get(0).getPositions() assertEquals(unprocessedManualEntities.get(0).getPositions()
@ -389,7 +388,7 @@ public class UnprocessedChangesServiceTest extends AbstractRedactionIntegrationT
assertEquals(unprocessedManualEntities.get(0).getAnnotationId(), aoelId); assertEquals(unprocessedManualEntities.get(0).getAnnotationId(), aoelId);
assertEquals(unprocessedManualEntities.get(0).getTextAfter(), ", the same"); assertEquals(unprocessedManualEntities.get(0).getTextAfter(), ", the same");
assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "to set an "); assertEquals(unprocessedManualEntities.get(0).getTextBefore(), "to set an ");
assertThat(unprocessedManualEntities.get(0).getSection()).contains("Paragraph: With respect to the"); assertEquals(unprocessedManualEntities.get(0).getSection(), "[0, 4]: Paragraph: With respect to the");
assertEquals(unprocessedManualEntities.get(0).getPositions() assertEquals(unprocessedManualEntities.get(0).getPositions()
.get(0).x(), positions.get(0).getTopLeftX()); .get(0).x(), positions.get(0).getTopLeftX());
assertEquals(unprocessedManualEntities.get(0).getPositions() assertEquals(unprocessedManualEntities.get(0).getPositions()

View File

@ -11,6 +11,7 @@ Sude Halide Nurullah
Xinyi Y. Tao Xinyi Y. Tao
Dorn Dorn
Prasher Prasher
David
annotation annotation
J.B. RASCLE J.B. RASCLE
(果梗を除去したもの) (果梗を除去したもの)

View File

@ -1,100 +0,0 @@
the
be
to
of
and
a
in
that
have
I
it
for
not
on
with
he
as
you
do
at
this
but
his
by
from
they
we
say
her
she
or
an
will
my
one
all
would
there
their
what
so
up
out
if
about
who
get
which
go
me
when
make
can
like
time
no
just
him
know
take
people
into
year
your
good
some
could
them
see
other
than
then
now
look
only
come
its
over
think
also
back
after
use
two
how
our
work
first
well
way
even
new
want
because
any
these
give
day
most
us

View File

@ -18,10 +18,10 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SuperSection;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell; import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
@ -104,7 +104,7 @@ rule "CBI.0.1: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.1", "Author found by \"et al\" regex", "personal_data_geolocation_article_39e3"); entity.redact("CBI.0.1", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -117,7 +117,7 @@ rule "CBI.0.2: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.2", "Author found by \"et al\" regex", "vertebrate_study_personal_data_geolocation_article_39e2"); entity.redact("CBI.0.2", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -127,7 +127,7 @@ rule "CBI.0.3: Redact CBI Authors (non vertebrate Study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_author", dictionaryEntry) $entity: TextEntity(type() == "CBI_author", dictionaryEntry)
then then
$entity.redact("CBI.0.3", "Author found", "personal_data_geolocation_article_39e3"); $entity.redact("CBI.0.3", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "CBI.0.4: Redact CBI Authors (vertebrate Study)" rule "CBI.0.4: Redact CBI Authors (vertebrate Study)"
@ -135,7 +135,7 @@ rule "CBI.0.4: Redact CBI Authors (vertebrate Study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_author", dictionaryEntry) $entity: TextEntity(type() == "CBI_author", dictionaryEntry)
then then
$entity.redact("CBI.0.4", "Author found", "vertebrate_study_personal_data_geolocation_article_39e2"); $entity.redact("CBI.0.4", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -153,7 +153,7 @@ rule "CBI.1.1: Redact CBI Address (vertebrate Study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_address", dictionaryEntry) $entity: TextEntity(type() == "CBI_address", dictionaryEntry)
then then
$entity.redact("CBI.1.1", "Address found", "vertebrate_study_personal_data_geolocation_article_39e2"); $entity.redact("CBI.1.1", "Address found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -178,7 +178,7 @@ rule "CBI.9.0: Redact all cells with Header Author(s) as CBI_author (non vertebr
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "personal_data_geolocation_article_39e3")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)" rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)"
@ -191,7 +191,7 @@ rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "personal_data_geolocation_article_39e3")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.9.2: Redact all cells with Header Author(s) as CBI_author (non vertebrate study)" rule "CBI.9.2: Redact all cells with Header Author(s) as CBI_author (non vertebrate study)"
@ -204,7 +204,7 @@ rule "CBI.9.2: Redact all cells with Header Author(s) as CBI_author (non vertebr
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.2", "Author(s) found", "personal_data_geolocation")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.2", "Author(s) found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -219,7 +219,7 @@ rule "CBI.10.0: Redact all cells with Header Author(s) as CBI_author (vertebrate
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.0", "Author(s) found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(redactionEntity -> redactionEntity.redact("CBI.10.0", "Author(s) found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)" rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)"
@ -232,7 +232,7 @@ rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate st
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.1", "Author found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(redactionEntity -> redactionEntity.redact("CBI.10.1", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "CBI.10.2: Redact all cells with Header Author(s) as CBI_author (vertebrate study)" rule "CBI.10.2: Redact all cells with Header Author(s) as CBI_author (vertebrate study)"
@ -245,7 +245,7 @@ rule "CBI.10.2: Redact all cells with Header Author(s) as CBI_author (vertebrate
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.2", "Author(s) found", "personal_data_geolocation")); .forEach(redactionEntity -> redactionEntity.redact("CBI.10.2", "Author(s) found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
rule "CBI.10.3: Redact all cells with Header Author as CBI_author (vertebrate study)" rule "CBI.10.3: Redact all cells with Header Author as CBI_author (vertebrate study)"
@ -258,7 +258,7 @@ rule "CBI.10.3: Redact all cells with Header Author as CBI_author (vertebrate st
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.3", "Author found", "personal_data_geolocation")); .forEach(redactionEntity -> redactionEntity.redact("CBI.10.3", "Author found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -353,7 +353,7 @@ rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
then then
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "PII", EntityType.ENTITY, $section)
.forEach(laboratoryEntity -> { .forEach(laboratoryEntity -> {
laboratoryEntity.redact("CBI.20.0", "PERFORMING LABORATORY was found", "links_producer_applicant"); laboratoryEntity.redact("CBI.20.0", "PERFORMING LABORATORY was found", "Reg (EC) No 1107/2009 Art. 63 (2e)");
dictionary.recommendEverywhere(laboratoryEntity); dictionary.recommendEverywhere(laboratoryEntity);
}); });
end end
@ -379,7 +379,7 @@ rule "CBI.20.2: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
then then
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section) entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
.forEach(laboratoryEntity -> { .forEach(laboratoryEntity -> {
laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "vertebrate_study_personal_data_geolocation_article_39e2"); laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(laboratoryEntity); dictionary.recommendEverywhere(laboratoryEntity);
}); });
end end
@ -391,7 +391,7 @@ rule "CBI.20.3: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
then then
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section) entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
.forEach(laboratoryEntity -> { .forEach(laboratoryEntity -> {
laboratoryEntity.redact("CBI.20.3", "PERFORMING LABORATORY was found", "personal_data_geolocation"); laboratoryEntity.redact("CBI.20.3", "PERFORMING LABORATORY was found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
dictionary.recommendEverywhere(laboratoryEntity); dictionary.recommendEverywhere(laboratoryEntity);
}); });
end end
@ -404,7 +404,7 @@ rule "CBI.23.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (no
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "personal_data_geolocation_article_39e3")); .forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)" rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
@ -413,7 +413,7 @@ rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (ve
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -424,7 +424,7 @@ rule "PII.0.0: Redact all PII"
when when
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.0", "Personal Information found", "links_producer_applicant"); $pii.redact("PII.0.0", "Personal Information found", "Reg (EC) No 1107/2009 Art. 63 (2e)");
end end
rule "PII.0.1: Redact all PII (non vertebrate study)" rule "PII.0.1: Redact all PII (non vertebrate study)"
@ -432,7 +432,7 @@ rule "PII.0.1: Redact all PII (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.1", "Personal Information found", "personal_data_geolocation_article_39e3"); $pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "PII.0.2: Redact all PII (vertebrate study)" rule "PII.0.2: Redact all PII (vertebrate study)"
@ -440,14 +440,14 @@ rule "PII.0.2: Redact all PII (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.2", "Personal Information found", "vertebrate_study_personal_data_geolocation_article_39e2"); $pii.redact("PII.0.2", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
rule "PII.0.3: Redact all PII" rule "PII.0.3: Redact all PII"
when when
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.3", "Personal Information found", "personal_data_geolocation"); $pii.redact("PII.0.3", "Personal Information found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
end end
@ -457,7 +457,7 @@ rule "PII.1.0: Redact Emails by RegEx"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.0", "Found by Email Regex", "links_producer_applicant")); .forEach(emailEntity -> emailEntity.redact("PII.1.0", "Found by Email Regex", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)" rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
@ -466,7 +466,7 @@ rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "personal_data_geolocation_article_39e3")); .forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.1.2: Redact Emails by RegEx (vertebrate study)" rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
@ -475,7 +475,7 @@ rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.1.5: Redact Emails by RegEx" rule "PII.1.5: Redact Emails by RegEx"
@ -483,7 +483,7 @@ rule "PII.1.5: Redact Emails by RegEx"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.5", "Found by Email Regex", "personal_data_geolocation")); .forEach(emailEntity -> emailEntity.redact("PII.1.5", "Found by Email Regex", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
rule "PII.1.6: Redact typoed Emails with indicator" rule "PII.1.6: Redact typoed Emails with indicator"
@ -491,7 +491,7 @@ rule "PII.1.6: Redact typoed Emails with indicator"
$section: Section(containsString("@") || containsStringIgnoreCase("mail")) $section: Section(containsString("@") || containsStringIgnoreCase("mail"))
then then
entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.6", "Personal information found", "personal_data_geolocation")); .forEach(emailEntity -> emailEntity.redact("PII.1.6", "Personal information found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -510,7 +510,7 @@ rule "PII.2.0: Redact Phone and Fax by RegEx"
containsString("Fer")) containsString("Fer"))
then then
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section) entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
.forEach(contactEntity -> contactEntity.redact("PII.2.0", "Found by Phone and Fax Regex", "links_producer_applicant")); .forEach(contactEntity -> contactEntity.redact("PII.2.0", "Found by Phone and Fax Regex", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.2.1: Redact Phone and Fax by RegEx (non vertebrate study)" rule "PII.2.1: Redact Phone and Fax by RegEx (non vertebrate study)"
@ -527,7 +527,7 @@ rule "PII.2.1: Redact Phone and Fax by RegEx (non vertebrate study)"
containsString("Fer")) containsString("Fer"))
then then
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section) entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
.forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)" rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)"
@ -544,7 +544,7 @@ rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)"
containsString("Fer")) containsString("Fer"))
then then
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section) entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
.forEach(contactEntity -> contactEntity.redact("PII.2.2", "Found by Phone and Fax Regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.2.2", "Found by Phone and Fax Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -554,7 +554,7 @@ rule "PII.3.0: Redact telephone numbers by RegEx"
$section: Section(!hasTables(), matchesRegex("[+]\\d{1,}")) $section: Section(!hasTables(), matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.3.0", "Telephone number found by regex", "links_producer_applicant")); .forEach(entity -> entity.redact("PII.3.0", "Telephone number found by regex", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.3.1: Redact telephone numbers by RegEx (Non vertebrate study)" rule "PII.3.1: Redact telephone numbers by RegEx (Non vertebrate study)"
@ -563,7 +563,7 @@ rule "PII.3.1: Redact telephone numbers by RegEx (Non vertebrate study)"
$section: Section(!hasTables(), matchesRegex("[+]\\d{1,}")) $section: Section(!hasTables(), matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)" rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)"
@ -572,7 +572,7 @@ rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)"
$section: Section(!hasTables(), matchesRegex("[+]\\d{1,}")) $section: Section(!hasTables(), matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.3.2", "Telephone number found by regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.3.2", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.4: Redact telephone numbers by RegEx (Non vertebrate study)" rule "PII.3.4: Redact telephone numbers by RegEx (Non vertebrate study)"
@ -581,7 +581,7 @@ rule "PII.3.4: Redact telephone numbers by RegEx (Non vertebrate study)"
$rowCell: TableCell(matchesRegex("[+]\\d{1,}")) $rowCell: TableCell(matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell)
.forEach(entity -> entity.redact("PII.3.4", "Telephone number found by regex", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.3.4", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)" rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)"
@ -590,7 +590,7 @@ rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)"
$rowCell: TableCell(matchesRegex("[+]\\d{1,}")) $rowCell: TableCell(matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell)
.forEach(entity -> entity.redact("PII.3.5", "Telephone number found by regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.3.5", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -617,7 +617,7 @@ rule "PII.4.0: Redact line after contact information keywords"
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.4.0", "Found after \"" + $contactKeyword + "\" contact keyword", "links_producer_applicant")); .forEach(contactEntity -> contactEntity.redact("PII.4.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.4.1: Redact line after contact information keywords" rule "PII.4.1: Redact line after contact information keywords"
@ -644,7 +644,7 @@ rule "PII.4.1: Redact line after contact information keywords"
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "personal_data_geolocation")); .forEach(contactEntity -> contactEntity.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -658,7 +658,7 @@ rule "PII.5.0: Redact line after contact information keywords reduced"
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.5.0", "Found after \"" + $contactKeyword + "\" contact keyword", "links_producer_applicant")); .forEach(contactEntity -> contactEntity.redact("PII.5.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.5.1: Redact line after contact information keywords reduced (non vertebrate study)" rule "PII.5.1: Redact line after contact information keywords reduced (non vertebrate study)"
@ -671,7 +671,7 @@ rule "PII.5.1: Redact line after contact information keywords reduced (non verte
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrate study)" rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrate study)"
@ -684,7 +684,7 @@ rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrat
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.5.2", "Found after \"" + $contactKeyword + "\" contact keyword", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.5.2", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -697,7 +697,7 @@ rule "PII.6.0: Redact line between contact keywords"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.0", "Found between contact keywords", "links_producer_applicant")); .forEach(contactEntity -> contactEntity.redact("PII.6.0", "Found between contact keywords", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.6.1: Redact line between contact keywords (non vertebrate study)" rule "PII.6.1: Redact line between contact keywords (non vertebrate study)"
@ -709,7 +709,7 @@ rule "PII.6.1: Redact line between contact keywords (non vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.6.2: Redact line between contact keywords (vertebrate study)" rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
@ -721,7 +721,7 @@ rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.6.3: Redact line between contact keywords (non vertebrate study)" rule "PII.6.3: Redact line between contact keywords (non vertebrate study)"
@ -733,7 +733,7 @@ rule "PII.6.3: Redact line between contact keywords (non vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.3", "Found between contact keywords", "personal_data_geolocation")); .forEach(contactEntity -> contactEntity.redact("PII.6.3", "Found between contact keywords", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -747,7 +747,7 @@ rule "PII.7.0: Redact contact information if applicant is found"
containsString("Telephone number:")) containsString("Telephone number:"))
then then
entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "Tel.:", "Tel:", "Telephone number:", "Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "Tel.:", "Tel:", "Telephone number:", "Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section)
.forEach(entity -> entity.redact("PII.7.0", "Applicant information was found", "links_producer_applicant")); .forEach(entity -> entity.redact("PII.7.0", "Applicant information was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.7.1: Redact contact information if applicant is found (non vertebrate study)" rule "PII.7.1: Redact contact information if applicant is found (non vertebrate study)"
@ -765,7 +765,7 @@ rule "PII.7.1: Redact contact information if applicant is found (non vertebrate
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)" rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)"
@ -783,7 +783,7 @@ rule "PII.7.2: Redact contact information if applicant is found (vertebrate stud
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -802,7 +802,7 @@ rule "PII.8.0: Redact contact information if producer is found"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.0", "Producer was found", "links_producer_applicant")); .forEach(entity -> entity.redact("PII.8.0", "Producer was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.8.1: Redact contact information if producer is found (non vertebrate study)" rule "PII.8.1: Redact contact information if producer is found (non vertebrate study)"
@ -820,7 +820,7 @@ rule "PII.8.1: Redact contact information if producer is found (non vertebrate s
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.8.2: Redact contact information if producer is found (vertebrate study)" rule "PII.8.2: Redact contact information if producer is found (vertebrate study)"
@ -838,7 +838,7 @@ rule "PII.8.2: Redact contact information if producer is found (vertebrate study
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.2", "Producer was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.8.2", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -848,15 +848,15 @@ rule "PII.9.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\""
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("PII.9.0", "AUTHOR(S) was found", "links_producer_applicant")); .forEach(authorEntity -> authorEntity.redact("PII.9.0", "AUTHOR(S) was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.9.3: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\"" rule "PII.9.3: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\""
when when
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document)
.forEach(authorEntity -> authorEntity.redact("PII.9.3", "AUTHOR(S) was found", "personal_data_geolocation")); .forEach(authorEntity -> authorEntity.redact("PII.9.3", "AUTHOR(S) was found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -867,17 +867,17 @@ rule "PII.10.0: Redact study director abbreviation (non vertebrate study)"
$section: Section(containsString("KATH") || containsString("BECH") || containsString("KML")) $section: Section(containsString("KATH") || containsString("BECH") || containsString("KML"))
then then
entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.10.0", "Personal information found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.10.0", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
// Rule unit: PII.11 // Rule unit: PII.11
rule "PII.11.0: Redact On behalf of Sequani Ltd.:" rule "PII.11.0: Redact On behalf of Sequani Ltd.:"
when when
$section: SuperSection(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title")) $section: Section(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title"))
then then
entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section)
.forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "personal_data_geolocation_article_39e3")); .forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -888,7 +888,7 @@ rule "PII.12.0: Expand PII entities with salutation prefix"
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")) $entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
then then
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*") entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "personal_data_geolocation_article_39e3")); .ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.12.1: Expand PII entities with salutation prefix" rule "PII.12.1: Expand PII entities with salutation prefix"
@ -897,7 +897,7 @@ rule "PII.12.1: Expand PII entities with salutation prefix"
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")) $entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
then then
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*") entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "vertebrate_study_personal_data_geolocation_article_39e2")); .ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -908,7 +908,7 @@ rule "ETC.0.0: Purity Hint"
when when
$section: Section(containsStringIgnoreCase("purity")) $section: Section(containsStringIgnoreCase("purity"))
then then
entityCreationService.byRegexIgnoreCase("(purity ?( of|\\(.{1,20}\\))?( ?:)?) [<>]{0,1}(100|([1-9]{1}[0-9]{0,1}([.,]{1}[0-9]{1,2})?)) ?% ", "hint_only", EntityType.HINT, 1, $section) entityCreationService.byRegexIgnoreCase("(purity ?( of|\\(.{1,20}\\))?( ?:)?) .{0,5}[\\d\\.]+( .{0,4}\\.)? ?%", "hint_only", EntityType.HINT, 1, $section)
.forEach(hint -> hint.skip("ETC.0.0", "hint only")); .forEach(hint -> hint.skip("ETC.0.0", "hint only"));
end end
@ -918,7 +918,7 @@ rule "ETC.2.0: Redact signatures"
when when
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.0", "Signature Found", "names_addresses_persons"); $signature.redact("ETC.2.0", "Signature Found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
end end
rule "ETC.2.1: Redact signatures (non vertebrate study)" rule "ETC.2.1: Redact signatures (non vertebrate study)"
@ -926,7 +926,7 @@ rule "ETC.2.1: Redact signatures (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.1", "Signature Found", "personal_data_geolocation_article_39e3"); $signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "ETC.2.2: Redact signatures (vertebrate study)" rule "ETC.2.2: Redact signatures (vertebrate study)"
@ -934,14 +934,14 @@ rule "ETC.2.2: Redact signatures (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.2", "Signature Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $signature.redact("ETC.2.2", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
rule "ETC.2.3: Redact signatures" rule "ETC.2.3: Redact signatures"
when when
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.3", "Signature Found", "personal_data_geolocation"); $signature.redact("ETC.2.3", "Signature Found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
end end
@ -950,7 +950,7 @@ rule "ETC.3.0: Redact logos"
when when
$logo: Image(imageType == ImageType.LOGO) $logo: Image(imageType == ImageType.LOGO)
then then
$logo.redact("ETC.3.0", "Logo Found", "names_addresses_persons"); $logo.redact("ETC.3.0", "Logo Found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
end end
rule "ETC.3.1: Skip logos (non vertebrate study)" rule "ETC.3.1: Skip logos (non vertebrate study)"
@ -966,14 +966,14 @@ rule "ETC.3.2: Redact logos (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$logo: Image(imageType == ImageType.LOGO) $logo: Image(imageType == ImageType.LOGO)
then then
$logo.redact("ETC.3.2", "Logo Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $logo.redact("ETC.3.2", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
rule "ETC.3.3: Redact logos" rule "ETC.3.3: Redact logos"
when when
$logo: Image(imageType == ImageType.LOGO) $logo: Image(imageType == ImageType.LOGO)
then then
$logo.redact("ETC.3.3", "Logo Found", "personal_data_geolocation"); $logo.redact("ETC.3.3", "Logo Found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
end end
@ -984,6 +984,7 @@ rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidentia
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential"); $dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end end
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'" rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
@ -993,6 +994,7 @@ rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confi
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential"); $dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
retract($dossierRedaction);
end end
@ -1111,6 +1113,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -1122,6 +1126,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -1132,8 +1138,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -1143,7 +1151,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -1154,7 +1164,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -1165,6 +1177,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -1177,7 +1191,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -1188,7 +1204,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -1200,6 +1216,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -1220,6 +1238,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1229,7 +1248,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1240,115 +1260,71 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
// Rule unit: X.3 // Rule unit: X.3
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -1356,117 +1332,68 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
// Rule unit: X.6 // Rule unit: X.6
rule "X.6.0: Remove Lower Rank Entity Contained by ENTITY or HINT" rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
salience 32 salience 32
when when
$containment: Containment( $higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval(dictionary.getDictionaryRank($contained.type()) < dictionary.getDictionaryRank($container.type())),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.0", "remove Entity of lower rank when contained by entity of type ENTITY or HINT"); $lowerRank.getIntersectingNodes().forEach(node -> update(node));
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
retract($lowerRank);
end end
rule "X.6.1: Remove Entity, when contained in another entity of type ENTITY or HINT with larger text range" rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
salience 32 salience 32
when when
$containment: Containment( $outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval($container.getTextRange().length() > $contained.getTextRange().length()),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.1", "remove Entity when contained in another entity of type ENTITY or HINT with larger text range"); $inner.getIntersectingNodes().forEach(node -> update(node));
end $inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
retract($inner);
end
// Rule unit: X.8 // Rule unit: X.8
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -1492,36 +1419,25 @@ rule "X.10.0: remove false positives of ai"
// Rule unit: X.11 // Rule unit: X.11
rule "X.11.1: Remove non-manual entity which intersects with a manual entity" rule "X.11.1: Remove non manual entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $nonManualEntity: TextEntity(intersects($manualEntity), engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.1", "remove entity which intersects with a manual entity"); $nonManualEntity.remove("X.11.1", "remove entity which intersects with a manual entity");
retract($nonManualEntity);
end end
rule "X.11.2: Remove non-manual entity which is equal to manual entity" rule "X.11.2: Remove non manual entity which are equal to manual entity"
salience 70 salience 70
when when
$equality: Equality( $manualEntity: TextEntity(engines contains Engine.MANUAL, active(), $type: type())
$a: a, $nonManualEntity: TextEntity(getTextRange().equals($manualEntity.getTextRange()), type() == $type, entityType == EntityType.ENTITY, !hasManualChanges(), engines not contains Engine.MANUAL)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.MANUAL,
$a.active(),
$b.entityType == EntityType.ENTITY,
!$b.hasManualChanges(),
$b.engines not contains Engine.MANUAL
)
then then
$a.addEngines($b.getEngines()); $manualEntity.addEngines($nonManualEntity.getEngines());
$b.remove("X.11.2", "remove non-manual entity which is equal to manual entity"); $nonManualEntity.remove("X.11.2", "remove non manual entity which are equal to manual entity");
retract($nonManualEntity);
end end
@ -1534,6 +1450,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -18,7 +18,8 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
@ -1247,6 +1248,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -1258,6 +1261,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -1268,8 +1273,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -1279,7 +1286,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -1290,7 +1299,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -1301,6 +1312,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -1313,7 +1326,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -1324,7 +1339,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -1336,6 +1351,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -1356,6 +1373,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1365,7 +1383,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1376,115 +1395,71 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
// Rule unit: X.3 // Rule unit: X.3
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -1492,49 +1467,21 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
@ -1553,33 +1500,22 @@ rule "X.7.0: Remove all images"
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -1608,16 +1544,11 @@ rule "X.10.0: remove false positives of ai"
rule "X.11.0: Remove dictionary entity which intersects with a manual entity" rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.dictionaryEntry,
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.0", "remove dictionary entity which intersects with a manual entity"); $dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
retract($dictionaryEntity);
end end
@ -1630,6 +1561,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -18,10 +18,10 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SuperSection;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell; import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
@ -104,7 +104,7 @@ rule "CBI.0.1: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.1", "Author found by \"et al\" regex", "personal_data_geolocation_article_39e3"); entity.redact("CBI.0.1", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -117,7 +117,7 @@ rule "CBI.0.2: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.2", "Author found by \"et al\" regex", "vertebrate_study_personal_data_geolocation_article_39e2"); entity.redact("CBI.0.2", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -127,7 +127,7 @@ rule "CBI.0.3: Redact CBI Authors (non vertebrate Study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_author", dictionaryEntry) $entity: TextEntity(type() == "CBI_author", dictionaryEntry)
then then
$entity.redact("CBI.0.3", "Author found", "personal_data_geolocation_article_39e3"); $entity.redact("CBI.0.3", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "CBI.0.4: Redact CBI Authors (vertebrate Study)" rule "CBI.0.4: Redact CBI Authors (vertebrate Study)"
@ -135,7 +135,7 @@ rule "CBI.0.4: Redact CBI Authors (vertebrate Study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_author", dictionaryEntry) $entity: TextEntity(type() == "CBI_author", dictionaryEntry)
then then
$entity.redact("CBI.0.4", "Author found", "vertebrate_study_personal_data_geolocation_article_39e2"); $entity.redact("CBI.0.4", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -153,7 +153,7 @@ rule "CBI.1.1: Redact CBI Address (vertebrate Study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_address", dictionaryEntry) $entity: TextEntity(type() == "CBI_address", dictionaryEntry)
then then
$entity.redact("CBI.1.1", "Address found", "vertebrate_study_personal_data_geolocation_article_39e2"); $entity.redact("CBI.1.1", "Address found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -178,7 +178,7 @@ rule "CBI.9.0: Redact all cells with Header Author(s) as CBI_author (non vertebr
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "personal_data_geolocation_article_39e3")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)" rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)"
@ -191,7 +191,7 @@ rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "personal_data_geolocation_article_39e3")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -206,7 +206,7 @@ rule "CBI.10.0: Redact all cells with Header Author(s) as CBI_author (vertebrate
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.0", "Author(s) found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(redactionEntity -> redactionEntity.redact("CBI.10.0", "Author(s) found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)" rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)"
@ -219,7 +219,7 @@ rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate st
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.1", "Author found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(redactionEntity -> redactionEntity.redact("CBI.10.1", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -302,7 +302,7 @@ rule "CBI.20.2: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
then then
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section) entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
.forEach(laboratoryEntity -> { .forEach(laboratoryEntity -> {
laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "vertebrate_study_personal_data_geolocation_article_39e2"); laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(laboratoryEntity); dictionary.recommendEverywhere(laboratoryEntity);
}); });
end end
@ -315,7 +315,7 @@ rule "CBI.23.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (no
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "personal_data_geolocation_article_39e3")); .forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)" rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
@ -324,7 +324,7 @@ rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (ve
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -336,7 +336,7 @@ rule "PII.0.1: Redact all PII (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.1", "Personal Information found", "personal_data_geolocation_article_39e3"); $pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "PII.0.2: Redact all PII (vertebrate study)" rule "PII.0.2: Redact all PII (vertebrate study)"
@ -344,7 +344,7 @@ rule "PII.0.2: Redact all PII (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.2", "Personal Information found", "vertebrate_study_personal_data_geolocation_article_39e2"); $pii.redact("PII.0.2", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -355,7 +355,7 @@ rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "personal_data_geolocation_article_39e3")); .forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.1.2: Redact Emails by RegEx (vertebrate study)" rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
@ -364,7 +364,7 @@ rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.1.3: Redact typoed Emails with indicator" rule "PII.1.3: Redact typoed Emails with indicator"
@ -372,7 +372,7 @@ rule "PII.1.3: Redact typoed Emails with indicator"
$section: Section(containsString("@") || containsStringIgnoreCase("mail")) $section: Section(containsString("@") || containsStringIgnoreCase("mail"))
then then
entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.3", "Personal information found", "personal_data_geolocation_article_39e3")); .forEach(emailEntity -> emailEntity.redact("PII.1.3", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -391,7 +391,7 @@ rule "PII.2.1: Redact Phone and Fax by RegEx (non vertebrate study)"
containsString("Fer")) containsString("Fer"))
then then
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section) entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
.forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)" rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)"
@ -408,7 +408,7 @@ rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)"
containsString("Fer")) containsString("Fer"))
then then
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section) entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
.forEach(contactEntity -> contactEntity.redact("PII.2.2", "Found by Phone and Fax Regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.2.2", "Found by Phone and Fax Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.2.3: Redact phone numbers without indicators" rule "PII.2.3: Redact phone numbers without indicators"
@ -416,7 +416,7 @@ rule "PII.2.3: Redact phone numbers without indicators"
$section: Section(containsString("+")) $section: Section(containsString("+"))
then then
entityCreationService.byRegex("(\\+[\\dO]{1,2} )(\\([\\dO]{1,3}\\))?[\\d\\-O ]{8,15}", "PII", EntityType.ENTITY, $section) entityCreationService.byRegex("(\\+[\\dO]{1,2} )(\\([\\dO]{1,3}\\))?[\\d\\-O ]{8,15}", "PII", EntityType.ENTITY, $section)
.forEach(entity -> entity.redact("PII.2.3", "Personal information found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.2.3", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -427,7 +427,7 @@ rule "PII.3.1: Redact telephone numbers by RegEx (Non vertebrate study)"
$section: Section(!hasTables(), matchesRegex("[+]\\d{1,}")) $section: Section(!hasTables(), matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)" rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)"
@ -436,7 +436,7 @@ rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)"
$section: Section(!hasTables(), matchesRegex("[+]\\d{1,}")) $section: Section(!hasTables(), matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.3.2", "Telephone number found by regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.3.2", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.4: Redact telephone numbers by RegEx (Non vertebrate study)" rule "PII.3.4: Redact telephone numbers by RegEx (Non vertebrate study)"
@ -445,7 +445,7 @@ rule "PII.3.4: Redact telephone numbers by RegEx (Non vertebrate study)"
$rowCell: TableCell(matchesRegex("[+]\\d{1,}")) $rowCell: TableCell(matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell)
.forEach(entity -> entity.redact("PII.3.4", "Telephone number found by regex", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.3.4", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)" rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)"
@ -454,7 +454,7 @@ rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)"
$rowCell: TableCell(matchesRegex("[+]\\d{1,}")) $rowCell: TableCell(matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell)
.forEach(entity -> entity.redact("PII.3.5", "Telephone number found by regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.3.5", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -469,7 +469,7 @@ rule "PII.5.1: Redact line after contact information keywords reduced (non verte
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrate study)" rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrate study)"
@ -482,7 +482,7 @@ rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrat
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.5.2", "Found after \"" + $contactKeyword + "\" contact keyword", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.5.2", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -496,7 +496,7 @@ rule "PII.6.1: Redact line between contact keywords (non vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.6.2: Redact line between contact keywords (vertebrate study)" rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
@ -508,7 +508,7 @@ rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -528,7 +528,7 @@ rule "PII.7.1: Redact contact information if applicant is found (non vertebrate
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)" rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)"
@ -546,7 +546,7 @@ rule "PII.7.2: Redact contact information if applicant is found (vertebrate stud
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -566,7 +566,7 @@ rule "PII.8.1: Redact contact information if producer is found (non vertebrate s
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.8.2: Redact contact information if producer is found (vertebrate study)" rule "PII.8.2: Redact contact information if producer is found (vertebrate study)"
@ -584,7 +584,7 @@ rule "PII.8.2: Redact contact information if producer is found (vertebrate study
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.2", "Producer was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.8.2", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -595,7 +595,7 @@ rule "PII.10.0: Redact study director abbreviation (non vertebrate study)"
$section: Section(containsString("KATH") || containsString("BECH") || containsString("KML")) $section: Section(containsString("KATH") || containsString("BECH") || containsString("KML"))
then then
entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.10.0", "Personal information found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.10.0", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.10.1: Redact study director abbreviation (vertebrate study)" rule "PII.10.1: Redact study director abbreviation (vertebrate study)"
@ -604,17 +604,17 @@ rule "PII.10.1: Redact study director abbreviation (vertebrate study)"
$section: Section(containsString("KATH") || containsString("BECH") || containsString("KML")) $section: Section(containsString("KATH") || containsString("BECH") || containsString("KML"))
then then
entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.10.1", "Personal information found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.10.1", "Personal information found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
// Rule unit: PII.11 // Rule unit: PII.11
rule "PII.11.0: Redact On behalf of Sequani Ltd.:" rule "PII.11.0: Redact On behalf of Sequani Ltd.:"
when when
$section: SuperSection(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title")) $section: Section(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title"))
then then
entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section)
.forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "personal_data_geolocation_article_39e3")); .forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -625,7 +625,7 @@ rule "PII.12.0: Expand PII entities with salutation prefix"
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")) $entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
then then
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*") entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "personal_data_geolocation_article_39e3")); .ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.12.1: Expand PII entities with salutation prefix" rule "PII.12.1: Expand PII entities with salutation prefix"
@ -634,7 +634,7 @@ rule "PII.12.1: Expand PII entities with salutation prefix"
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")) $entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
then then
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*") entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "vertebrate_study_personal_data_geolocation_article_39e2")); .ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -645,7 +645,7 @@ rule "ETC.0.0: Purity Hint"
when when
$section: Section(containsStringIgnoreCase("purity")) $section: Section(containsStringIgnoreCase("purity"))
then then
entityCreationService.byRegexIgnoreCase("(purity ?( of|\\(.{1,20}\\))?( ?:)?) [<>]{0,1}(100|([1-9]{1}[0-9]{0,1}([.,]{1}[0-9]{1,2})?)) ?% ", "hint_only", EntityType.HINT, 1, $section) entityCreationService.byRegexIgnoreCase("(purity ?( of|\\(.{1,20}\\))?( ?:)?) .{0,5}[\\d\\.]+( .{0,4}\\.)? ?%", "hint_only", EntityType.HINT, 1, $section)
.forEach(hint -> hint.skip("ETC.0.0", "hint only")); .forEach(hint -> hint.skip("ETC.0.0", "hint only"));
end end
@ -656,7 +656,7 @@ rule "ETC.2.1: Redact signatures (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.1", "Signature Found", "personal_data_geolocation_article_39e3"); $signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "ETC.2.2: Redact signatures (vertebrate study)" rule "ETC.2.2: Redact signatures (vertebrate study)"
@ -664,7 +664,7 @@ rule "ETC.2.2: Redact signatures (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.2", "Signature Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $signature.redact("ETC.2.2", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -682,7 +682,7 @@ rule "ETC.3.2: Redact logos (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$logo: Image(imageType == ImageType.LOGO) $logo: Image(imageType == ImageType.LOGO)
then then
$logo.redact("ETC.3.2", "Logo Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $logo.redact("ETC.3.2", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -693,6 +693,7 @@ rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidentia
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential"); $dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end end
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'" rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
@ -702,6 +703,7 @@ rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confi
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential"); $dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
retract($dossierRedaction);
end end
@ -838,6 +840,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -849,6 +853,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -859,8 +865,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -870,7 +878,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -881,7 +891,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -892,6 +904,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -904,7 +918,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -915,7 +931,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -927,6 +943,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -947,6 +965,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -956,7 +975,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -967,115 +987,71 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
// Rule unit: X.3 // Rule unit: X.3
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -1083,117 +1059,68 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
// Rule unit: X.6 // Rule unit: X.6
rule "X.6.0: Remove Lower Rank Entity Contained by ENTITY or HINT" rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
salience 32 salience 32
when when
$containment: Containment( $higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval(dictionary.getDictionaryRank($contained.type()) < dictionary.getDictionaryRank($container.type())),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.0", "remove Entity of lower rank when contained by entity of type ENTITY or HINT"); $lowerRank.getIntersectingNodes().forEach(node -> update(node));
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
retract($lowerRank);
end end
rule "X.6.1: Remove Entity, when contained in another entity of type ENTITY or HINT with larger text range" rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
salience 32 salience 32
when when
$containment: Containment( $outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval($container.getTextRange().length() > $contained.getTextRange().length()),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.1", "remove Entity when contained in another entity of type ENTITY or HINT with larger text range"); $inner.getIntersectingNodes().forEach(node -> update(node));
end $inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
retract($inner);
end
// Rule unit: X.8 // Rule unit: X.8
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -1219,36 +1146,25 @@ rule "X.10.0: remove false positives of ai"
// Rule unit: X.11 // Rule unit: X.11
rule "X.11.1: Remove non-manual entity which intersects with a manual entity" rule "X.11.1: Remove non manual entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $nonManualEntity: TextEntity(intersects($manualEntity), engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.1", "remove entity which intersects with a manual entity"); $nonManualEntity.remove("X.11.1", "remove entity which intersects with a manual entity");
retract($nonManualEntity);
end end
rule "X.11.2: Remove non-manual entity which is equal to manual entity" rule "X.11.2: Remove non manual entity which are equal to manual entity"
salience 70 salience 70
when when
$equality: Equality( $manualEntity: TextEntity(engines contains Engine.MANUAL, active(), $type: type())
$a: a, $nonManualEntity: TextEntity(getTextRange().equals($manualEntity.getTextRange()), type() == $type, entityType == EntityType.ENTITY, !hasManualChanges(), engines not contains Engine.MANUAL)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.MANUAL,
$a.active(),
$b.entityType == EntityType.ENTITY,
!$b.hasManualChanges(),
$b.engines not contains Engine.MANUAL
)
then then
$a.addEngines($b.getEngines()); $manualEntity.addEngines($nonManualEntity.getEngines());
$b.remove("X.11.2", "remove non-manual entity which is equal to manual entity"); $nonManualEntity.remove("X.11.2", "remove non manual entity which are equal to manual entity");
retract($nonManualEntity);
end end
@ -1261,6 +1177,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -18,7 +18,8 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
@ -159,6 +160,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -170,6 +173,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -180,8 +185,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -191,7 +198,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -202,7 +211,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -213,6 +224,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -225,7 +238,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -236,7 +251,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -248,6 +263,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -268,6 +285,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -277,7 +295,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -288,115 +307,71 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
// Rule unit: X.3 // Rule unit: X.3
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -404,117 +379,68 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
// Rule unit: X.6 // Rule unit: X.6
rule "X.6.0: Remove Lower Rank Entity Contained by ENTITY or HINT" rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
salience 32 salience 32
when when
$containment: Containment( $higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval(dictionary.getDictionaryRank($contained.type()) < dictionary.getDictionaryRank($container.type())),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.0", "remove Entity of lower rank when contained by entity of type ENTITY or HINT"); $lowerRank.getIntersectingNodes().forEach(node -> update(node));
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
retract($lowerRank);
end end
rule "X.6.1: Remove Entity, when contained in another entity of type ENTITY or HINT with larger text range" rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
salience 32 salience 32
when when
$containment: Containment( $outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval($container.getTextRange().length() > $contained.getTextRange().length()),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.1", "remove Entity when contained in another entity of type ENTITY or HINT with larger text range"); $inner.getIntersectingNodes().forEach(node -> update(node));
end $inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
retract($inner);
end
// Rule unit: X.8 // Rule unit: X.8
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -540,36 +466,25 @@ rule "X.10.0: remove false positives of ai"
// Rule unit: X.11 // Rule unit: X.11
rule "X.11.1: Remove non-manual entity which intersects with a manual entity" rule "X.11.1: Remove non manual entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $nonManualEntity: TextEntity(intersects($manualEntity), engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.1", "remove entity which intersects with a manual entity"); $nonManualEntity.remove("X.11.1", "remove entity which intersects with a manual entity");
retract($nonManualEntity);
end end
rule "X.11.2: Remove non-manual entity which is equal to manual entity" rule "X.11.2: Remove non manual entity which are equal to manual entity"
salience 70 salience 70
when when
$equality: Equality( $manualEntity: TextEntity(engines contains Engine.MANUAL, active(), $type: type())
$a: a, $nonManualEntity: TextEntity(getTextRange().equals($manualEntity.getTextRange()), type() == $type, entityType == EntityType.ENTITY, !hasManualChanges(), engines not contains Engine.MANUAL)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.MANUAL,
$a.active(),
$b.entityType == EntityType.ENTITY,
!$b.hasManualChanges(),
$b.engines not contains Engine.MANUAL
)
then then
$a.addEngines($b.getEngines()); $manualEntity.addEngines($nonManualEntity.getEngines());
$b.remove("X.11.2", "remove non-manual entity which is equal to manual entity"); $nonManualEntity.remove("X.11.2", "remove non manual entity which are equal to manual entity");
retract($nonManualEntity);
end end
@ -582,6 +497,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -18,13 +18,10 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.Containment; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.entity.Equality;
import com.iqser.red.service.redaction.v1.server.model.document.entity.Intersection;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SuperSection;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell; import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
@ -109,7 +106,7 @@ rule "CBI.0.1: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.1", "Author found by \"et al\" regex", "personal_data_geolocation_article_39e3"); entity.redact("CBI.0.1", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -122,7 +119,7 @@ rule "CBI.0.2: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.2", "Author found by \"et al\" regex", "vertebrate_study_personal_data_geolocation_article_39e2"); entity.redact("CBI.0.2", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -139,7 +136,7 @@ rule "CBI.9.0: Redact all cells with Header Author(s) as CBI_author (non vertebr
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "personal_data_geolocation_article_39e3")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)" rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)"
@ -152,7 +149,7 @@ rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "personal_data_geolocation_article_39e3")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.9.2: Redact all cells with Header Author(s) as CBI_author (non vertebrate study)" rule "CBI.9.2: Redact all cells with Header Author(s) as CBI_author (non vertebrate study)"
@ -165,7 +162,7 @@ rule "CBI.9.2: Redact all cells with Header Author(s) as CBI_author (non vertebr
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.2", "Author(s) found", "personal_data_geolocation")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.2", "Author(s) found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -208,7 +205,7 @@ rule "CBI.12.1: Redact and recommend TableCell with header 'Author' or 'Author(s
then then
entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY) entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY)
.ifPresent(authorEntity -> { .ifPresent(authorEntity -> {
authorEntity.redact("CBI.12.1", "Redacted because it's row belongs to a vertebrate study", "personal_data_geolocation_article_39e3"); authorEntity.redact("CBI.12.1", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(3) of Regulation (EC) No 178/2002");
dictionary.addMultipleAuthorsAsRecommendation(authorEntity); dictionary.addMultipleAuthorsAsRecommendation(authorEntity);
}); });
end end
@ -226,7 +223,7 @@ rule "CBI.12.2: Redact and recommend TableCell with header 'Author' or 'Author(s
entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY) entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY)
.ifPresent(authorEntity -> { .ifPresent(authorEntity -> {
authorEntity.redact("CBI.12.2", "Redacted because it's row belongs to a vertebrate study", "vertebrate_study_personal_data_geolocation_article_39e2"); authorEntity.redact("CBI.12.2", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.addMultipleAuthorsAsRecommendation(authorEntity); dictionary.addMultipleAuthorsAsRecommendation(authorEntity);
}); });
@ -254,7 +251,7 @@ rule "CBI.12.4: Redacted because table row contains a redaction_indicator"
entity.applyWithReferences( entity.applyWithReferences(
"CBI.12.4", "CBI.12.4",
"Redaction_indicator found", "Redaction_indicator found",
"names_addresses_persons", "Reg (EC) No 1107/2009 Art. 63 (2g)",
$table.getEntitiesOfTypeInSameRow("redaction_indicator", entity) $table.getEntitiesOfTypeInSameRow("redaction_indicator", entity)
); );
}); });
@ -270,7 +267,7 @@ rule "CBI.12.5: Redacted because table row contains a vertebrate"
entity.applyWithReferences( entity.applyWithReferences(
"CBI.12.5", "CBI.12.5",
"Vertebrate found", "Vertebrate found",
"names_addresses_persons", "Reg (EC) No 1107/2009 Art. 63 (2g)",
$table.getEntitiesOfTypeInSameRow("vertebrate", entity) $table.getEntitiesOfTypeInSameRow("vertebrate", entity)
); );
}); });
@ -287,7 +284,7 @@ rule "CBI.13.1: Redacted because Section contains a vertebrate"
entity.applyWithReferences( entity.applyWithReferences(
"CBI.13.1", "CBI.13.1",
"Vertebrate found", "Vertebrate found",
"names_addresses_persons", "Reg (EC) No 1107/2009 Art. 63 (2g)",
$section.getEntitiesOfType("vertebrate") $section.getEntitiesOfType("vertebrate")
); );
}); });
@ -331,7 +328,7 @@ rule "CBI.13.4: Redact Names and Addresses if vertebrate and no_redaction_indica
entity.applyWithReferences( entity.applyWithReferences(
"CBI.13.4", "CBI.13.4",
"no_redaction_indicator but also redaction_indicator found", "no_redaction_indicator but also redaction_indicator found",
"names_addresses_persons", "Reg (EC) No 1107/2009 Art. 63 (2g)",
Stream.concat( Stream.concat(
$section.getEntitiesOfType("redaction_indicator").stream(), $section.getEntitiesOfType("redaction_indicator").stream(),
$section.getEntitiesOfType("no_redaction_indicator").stream()).toList() $section.getEntitiesOfType("no_redaction_indicator").stream()).toList()
@ -348,7 +345,7 @@ rule "CBI.13.5: Redacted because Section contains must_redact entity"
entity.applyWithReferences( entity.applyWithReferences(
"CBI.13.5", "CBI.13.5",
"must_redact entity found", "must_redact entity found",
"specification_impurity_active_substance", "Reg (EC) No 1107/2009 Art. 63 (2b)",
$section.getEntitiesOfType("must_redact") $section.getEntitiesOfType("must_redact")
); );
}); });
@ -361,7 +358,7 @@ rule "CBI.14.0: Redact CBI_sponsor entities if preceded by \"batches produced at
$section: Section(containsStringIgnoreCase("batches produced at")) $section: Section(containsStringIgnoreCase("batches produced at"))
$sponsorEntity: TextEntity(type() == "CBI_sponsor", textBefore.contains("batches produced at")) $sponsorEntity: TextEntity(type() == "CBI_sponsor", textBefore.contains("batches produced at"))
then then
$sponsorEntity.redact("CBI.14.0", "Redacted because it represents a sponsor company", "names_addresses_persons"); $sponsorEntity.redact("CBI.14.0", "Redacted because it represents a sponsor company", "Reg (EC) No 1107/2009 Art. 63 (2g)");
entityCreationService.byString("batches produced at", "must_redact", EntityType.HINT, $section) entityCreationService.byString("batches produced at", "must_redact", EntityType.HINT, $section)
.forEach(entity -> entity.skip("CBI.14.0", "must_redact")); .forEach(entity -> entity.skip("CBI.14.0", "must_redact"));
end end
@ -389,7 +386,7 @@ rule "CBI.15.0: Redact row if row contains \"determination of residues\" and liv
entityCreationService.byString($residueKeyword, "must_redact", EntityType.HINT, $section) entityCreationService.byString($residueKeyword, "must_redact", EntityType.HINT, $section)
.forEach(entity -> entity.skip("CBI.15.0", "must_redact")); .forEach(entity -> entity.skip("CBI.15.0", "must_redact"));
$section.getEntitiesOfType(List.of("CBI_author", "CBI_address")) $section.getEntitiesOfType(List.of("CBI_author", "CBI_address"))
.forEach(redactionEntity -> redactionEntity.redact("CBI.15.0", "Determination of residues and keyword \"" + $keyword + "\" was found.", "names_addresses_persons")); .forEach(redactionEntity -> redactionEntity.redact("CBI.15.0", "Determination of residues and keyword \"" + $keyword + "\" was found.", "Reg (EC) No 1107/2009 Art. 63 (2g)"));
end end
rule "CBI.15.1: Redact CBI_author and CBI_address if row contains \"determination of residues\" and livestock keyword" rule "CBI.15.1: Redact CBI_author and CBI_address if row contains \"determination of residues\" and livestock keyword"
@ -412,7 +409,7 @@ rule "CBI.15.1: Redact CBI_author and CBI_address if row contains \"determinatio
.forEach(entity -> entity.skip("CBI.15.1", "must_redact")); .forEach(entity -> entity.skip("CBI.15.1", "must_redact"));
$table.streamEntitiesWhereRowContainsStringsIgnoreCase(List.of($keyword, $residueKeyword)) $table.streamEntitiesWhereRowContainsStringsIgnoreCase(List.of($keyword, $residueKeyword))
.filter(redactionEntity -> redactionEntity.isAnyType(List.of("CBI_author", "CBI_address"))) .filter(redactionEntity -> redactionEntity.isAnyType(List.of("CBI_author", "CBI_address")))
.forEach(redactionEntity -> redactionEntity.redact("CBI.15.1", "Determination of residues and keyword \"" + $keyword + "\" was found.", "names_addresses_persons")); .forEach(redactionEntity -> redactionEntity.redact("CBI.15.1", "Determination of residues and keyword \"" + $keyword + "\" was found.", "Reg (EC) No 1107/2009 Art. 63 (2g)"));
end end
@ -449,6 +446,7 @@ rule "CBI.18.0: Expand CBI_author entities with firstname initials"
.ifPresent(expandedEntity -> { .ifPresent(expandedEntity -> {
expandedEntity.addMatchedRules($entityToExpand.getMatchedRuleList()); expandedEntity.addMatchedRules($entityToExpand.getMatchedRuleList());
$entityToExpand.remove("CBI.18.0", "Expand CBI_author entities with firstname initials"); $entityToExpand.remove("CBI.18.0", "Expand CBI_author entities with firstname initials");
retract($entityToExpand);
}); });
end end
@ -462,6 +460,7 @@ rule "CBI.19.0: Expand CBI_author entities with salutation prefix"
.ifPresent(expandedEntity -> { .ifPresent(expandedEntity -> {
expandedEntity.addMatchedRules($entityToExpand.getMatchedRuleList()); expandedEntity.addMatchedRules($entityToExpand.getMatchedRuleList());
$entityToExpand.remove("CBI.19.0", "Expand CBI_author entities with salutation prefix"); $entityToExpand.remove("CBI.19.0", "Expand CBI_author entities with salutation prefix");
retract($entityToExpand);
}); });
end end
@ -474,7 +473,7 @@ rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
then then
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "PII", EntityType.ENTITY, $section)
.forEach(laboratoryEntity -> { .forEach(laboratoryEntity -> {
laboratoryEntity.redact("CBI.20.0", "PERFORMING LABORATORY was found", "links_producer_applicant"); laboratoryEntity.redact("CBI.20.0", "PERFORMING LABORATORY was found", "Reg (EC) No 1107/2009 Art. 63 (2e)");
dictionary.recommendEverywhere(laboratoryEntity); dictionary.recommendEverywhere(laboratoryEntity);
}); });
end end
@ -500,7 +499,7 @@ rule "CBI.20.2: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
then then
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section) entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
.forEach(laboratoryEntity -> { .forEach(laboratoryEntity -> {
laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "vertebrate_study_personal_data_geolocation_article_39e2"); laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(laboratoryEntity); dictionary.recommendEverywhere(laboratoryEntity);
}); });
end end
@ -512,7 +511,7 @@ rule "CBI.20.3: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
then then
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section) entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
.forEach(laboratoryEntity -> { .forEach(laboratoryEntity -> {
laboratoryEntity.redact("CBI.20.3", "PERFORMING LABORATORY was found", "personal_data_geolocation"); laboratoryEntity.redact("CBI.20.3", "PERFORMING LABORATORY was found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
dictionary.recommendEverywhere(laboratoryEntity); dictionary.recommendEverywhere(laboratoryEntity);
}); });
end end
@ -525,7 +524,7 @@ rule "CBI.23.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (no
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "personal_data_geolocation_article_39e3")); .forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)" rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
@ -534,7 +533,7 @@ rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (ve
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -545,7 +544,7 @@ rule "PII.0.0: Redact all PII"
when when
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.0", "Personal Information found", "links_producer_applicant"); $pii.redact("PII.0.0", "Personal Information found", "Reg (EC) No 1107/2009 Art. 63 (2e)");
end end
rule "PII.0.1: Redact all PII (non vertebrate study)" rule "PII.0.1: Redact all PII (non vertebrate study)"
@ -553,7 +552,7 @@ rule "PII.0.1: Redact all PII (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.1", "Personal Information found", "personal_data_geolocation_article_39e3"); $pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "PII.0.2: Redact all PII (vertebrate study)" rule "PII.0.2: Redact all PII (vertebrate study)"
@ -561,14 +560,14 @@ rule "PII.0.2: Redact all PII (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.2", "Personal Information found", "vertebrate_study_personal_data_geolocation_article_39e2"); $pii.redact("PII.0.2", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
rule "PII.0.3: Redact all PII" rule "PII.0.3: Redact all PII"
when when
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.3", "Personal Information found", "personal_data_geolocation"); $pii.redact("PII.0.3", "Personal Information found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
end end
@ -578,7 +577,7 @@ rule "PII.1.0: Redact Emails by RegEx"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.0", "Found by Email Regex", "links_producer_applicant")); .forEach(emailEntity -> emailEntity.redact("PII.1.0", "Found by Email Regex", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)" rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
@ -587,7 +586,7 @@ rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "personal_data_geolocation_article_39e3")); .forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.1.2: Redact Emails by RegEx (vertebrate study)" rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
@ -596,7 +595,7 @@ rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.1.5: Redact Emails by RegEx" rule "PII.1.5: Redact Emails by RegEx"
@ -604,7 +603,7 @@ rule "PII.1.5: Redact Emails by RegEx"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.5", "Found by Email Regex", "personal_data_geolocation")); .forEach(emailEntity -> emailEntity.redact("PII.1.5", "Found by Email Regex", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
rule "PII.1.6: Redact typoed Emails with indicator" rule "PII.1.6: Redact typoed Emails with indicator"
@ -612,7 +611,7 @@ rule "PII.1.6: Redact typoed Emails with indicator"
$section: Section(containsString("@") || containsStringIgnoreCase("mail")) $section: Section(containsString("@") || containsStringIgnoreCase("mail"))
then then
entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.6", "Personal information found", "personal_data_geolocation")); .forEach(emailEntity -> emailEntity.redact("PII.1.6", "Personal information found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -639,7 +638,7 @@ rule "PII.4.0: Redact line after contact information keywords"
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.4.0", "Found after \"" + $contactKeyword + "\" contact keyword", "links_producer_applicant")); .forEach(contactEntity -> contactEntity.redact("PII.4.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.4.1: Redact line after contact information keywords" rule "PII.4.1: Redact line after contact information keywords"
@ -666,7 +665,7 @@ rule "PII.4.1: Redact line after contact information keywords"
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "personal_data_geolocation")); .forEach(contactEntity -> contactEntity.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -679,7 +678,7 @@ rule "PII.6.0: Redact line between contact keywords"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.0", "Found between contact keywords", "links_producer_applicant")); .forEach(contactEntity -> contactEntity.redact("PII.6.0", "Found between contact keywords", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.6.1: Redact line between contact keywords (non vertebrate study)" rule "PII.6.1: Redact line between contact keywords (non vertebrate study)"
@ -691,7 +690,7 @@ rule "PII.6.1: Redact line between contact keywords (non vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.6.2: Redact line between contact keywords (vertebrate study)" rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
@ -703,7 +702,7 @@ rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.6.3: Redact line between contact keywords (non vertebrate study)" rule "PII.6.3: Redact line between contact keywords (non vertebrate study)"
@ -715,7 +714,7 @@ rule "PII.6.3: Redact line between contact keywords (non vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.3", "Found between contact keywords", "personal_data_geolocation")); .forEach(contactEntity -> contactEntity.redact("PII.6.3", "Found between contact keywords", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
@ -729,7 +728,7 @@ rule "PII.7.0: Redact contact information if applicant is found"
containsString("Telephone number:")) containsString("Telephone number:"))
then then
entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "Tel.:", "Tel:", "Telephone number:", "Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "Tel.:", "Tel:", "Telephone number:", "Telephone No:", "Telephone:", "Phone No.", "Phone:", "Fax number:", "Fax:", "E-mail:", "Email:", "e-mail:", "E-mail address:"), "PII", EntityType.ENTITY, $section)
.forEach(entity -> entity.redact("PII.7.0", "Applicant information was found", "links_producer_applicant")); .forEach(entity -> entity.redact("PII.7.0", "Applicant information was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.7.1: Redact contact information if applicant is found (non vertebrate study)" rule "PII.7.1: Redact contact information if applicant is found (non vertebrate study)"
@ -747,7 +746,7 @@ rule "PII.7.1: Redact contact information if applicant is found (non vertebrate
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)" rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)"
@ -765,7 +764,7 @@ rule "PII.7.2: Redact contact information if applicant is found (vertebrate stud
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -784,7 +783,7 @@ rule "PII.8.0: Redact contact information if producer is found"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.0", "Producer was found", "links_producer_applicant")); .forEach(entity -> entity.redact("PII.8.0", "Producer was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.8.1: Redact contact information if producer is found (non vertebrate study)" rule "PII.8.1: Redact contact information if producer is found (non vertebrate study)"
@ -802,7 +801,7 @@ rule "PII.8.1: Redact contact information if producer is found (non vertebrate s
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.8.2: Redact contact information if producer is found (vertebrate study)" rule "PII.8.2: Redact contact information if producer is found (vertebrate study)"
@ -820,7 +819,7 @@ rule "PII.8.2: Redact contact information if producer is found (vertebrate study
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.2", "Producer was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.8.2", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -830,25 +829,25 @@ rule "PII.9.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\""
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("PII.9.0", "AUTHOR(S) was found", "links_producer_applicant")); .forEach(authorEntity -> authorEntity.redact("PII.9.0", "AUTHOR(S) was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
end end
rule "PII.9.3: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\"" rule "PII.9.3: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\""
when when
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "PII", EntityType.ENTITY, $document)
.forEach(authorEntity -> authorEntity.redact("PII.9.3", "AUTHOR(S) was found", "personal_data_geolocation")); .forEach(authorEntity -> authorEntity.redact("PII.9.3", "AUTHOR(S) was found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)"));
end end
// Rule unit: PII.11 // Rule unit: PII.11
rule "PII.11.0: Redact On behalf of Sequani Ltd.:" rule "PII.11.0: Redact On behalf of Sequani Ltd.:"
when when
$section: SuperSection(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title")) $section: Section(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title"))
then then
entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section)
.forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "personal_data_geolocation_article_39e3")); .forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -859,7 +858,7 @@ rule "PII.12.0: Expand PII entities with salutation prefix"
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")) $entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
then then
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*") entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "personal_data_geolocation_article_39e3")); .ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.12.1: Expand PII entities with salutation prefix" rule "PII.12.1: Expand PII entities with salutation prefix"
@ -868,7 +867,7 @@ rule "PII.12.1: Expand PII entities with salutation prefix"
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")) $entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
then then
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*") entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "vertebrate_study_personal_data_geolocation_article_39e2")); .ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -880,7 +879,7 @@ rule "ETC.1.0: Redact Purity"
$section: Section(containsStringIgnoreCase("purity")) $section: Section(containsStringIgnoreCase("purity"))
then then
entityCreationService.byRegex("\\bPurity:\\s*(<?>?\\s*\\d{1,2}(?:\\.\\d{1,2})?\\s*%)", "purity", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\bPurity:\\s*(<?>?\\s*\\d{1,2}(?:\\.\\d{1,2})?\\s*%)", "purity", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("ETC.1.0", "Purity found", "method_manufacture")); .forEach(entity -> entity.redact("ETC.1.0", "Purity found", "Reg (EC) No 1107/2009 Art. 63 (2a)"));
end end
@ -889,7 +888,7 @@ rule "ETC.2.0: Redact signatures"
when when
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.0", "Signature Found", "names_addresses_persons"); $signature.redact("ETC.2.0", "Signature Found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
end end
rule "ETC.2.1: Redact signatures (non vertebrate study)" rule "ETC.2.1: Redact signatures (non vertebrate study)"
@ -897,7 +896,7 @@ rule "ETC.2.1: Redact signatures (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.1", "Signature Found", "personal_data_geolocation_article_39e3"); $signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "ETC.2.2: Redact signatures (vertebrate study)" rule "ETC.2.2: Redact signatures (vertebrate study)"
@ -905,14 +904,14 @@ rule "ETC.2.2: Redact signatures (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.2", "Signature Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $signature.redact("ETC.2.2", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
rule "ETC.2.3: Redact signatures" rule "ETC.2.3: Redact signatures"
when when
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.3", "Signature Found", "personal_data_geolocation"); $signature.redact("ETC.2.3", "Signature Found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
end end
@ -921,7 +920,7 @@ rule "ETC.3.0: Redact logos"
when when
$logo: Image(imageType == ImageType.LOGO) $logo: Image(imageType == ImageType.LOGO)
then then
$logo.redact("ETC.3.0", "Logo Found", "names_addresses_persons"); $logo.redact("ETC.3.0", "Logo Found", "Reg (EC) No 1107/2009 Art. 63 (2g)");
end end
rule "ETC.3.1: Skip logos (non vertebrate study)" rule "ETC.3.1: Skip logos (non vertebrate study)"
@ -937,14 +936,14 @@ rule "ETC.3.2: Redact logos (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$logo: Image(imageType == ImageType.LOGO) $logo: Image(imageType == ImageType.LOGO)
then then
$logo.redact("ETC.3.2", "Logo Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $logo.redact("ETC.3.2", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
rule "ETC.3.3: Redact logos" rule "ETC.3.3: Redact logos"
when when
$logo: Image(imageType == ImageType.LOGO) $logo: Image(imageType == ImageType.LOGO)
then then
$logo.redact("ETC.3.3", "Logo Found", "personal_data_geolocation"); $logo.redact("ETC.3.3", "Logo Found", "Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)");
end end
@ -953,7 +952,7 @@ rule "ETC.4.0: Redact dossier dictionary entries"
when when
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.redact("ETC.4.0", "Specification of impurity found", "personal_data_geolocation_article_39e3"); $dossierRedaction.redact("ETC.4.0", "Specification of impurity found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
@ -964,6 +963,7 @@ rule "ETC.5.0: Skip dossier_redaction entries if confidentiality is 'confidentia
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential"); $dossierRedaction.skip("ETC.5.0", "Ignore dossier_redaction when confidential");
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
end end
rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'" rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confidential'"
@ -973,6 +973,7 @@ rule "ETC.5.1: Remove dossier_redaction entries if confidentiality is not 'confi
$dossierRedaction: TextEntity(type() == "dossier_redaction") $dossierRedaction: TextEntity(type() == "dossier_redaction")
then then
$dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential"); $dossierRedaction.remove("ETC.5.1", "Remove dossier_redaction when not confidential");
retract($dossierRedaction);
end end
@ -985,7 +986,7 @@ rule "ETC.6.0: Redact CAS Number"
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "PII", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "PII", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("ETC.6.0", "Sample # found in Header", "names_addresses_persons")); .forEach(redactionEntity -> redactionEntity.redact("ETC.6.0", "Sample # found in Header", "Reg (EC) No 1107/2009 Art. 63 (2g)"));
end end
@ -1007,7 +1008,7 @@ rule "ETC.8.0: Redact formulas (vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$logo: Image(imageType == ImageType.FORMULA) $logo: Image(imageType == ImageType.FORMULA)
then then
$logo.redact("ETC.8.0", "Logo Found", "personal_data_geolocation_article_39e3"); $logo.redact("ETC.8.0", "Logo Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "ETC.8.1: Redact formulas (non vertebrate study)" rule "ETC.8.1: Redact formulas (non vertebrate study)"
@ -1015,7 +1016,7 @@ rule "ETC.8.1: Redact formulas (non vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$logo: Image(imageType == ImageType.FORMULA) $logo: Image(imageType == ImageType.FORMULA)
then then
$logo.redact("ETC.8.1", "Logo Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $logo.redact("ETC.8.1", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -1134,6 +1135,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -1145,6 +1148,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -1155,8 +1160,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -1166,7 +1173,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -1177,7 +1186,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -1188,6 +1199,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -1200,7 +1213,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -1211,7 +1226,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -1223,6 +1238,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -1243,6 +1260,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1252,7 +1270,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1263,115 +1282,71 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
// Rule unit: X.3 // Rule unit: X.3
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -1379,117 +1354,68 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
// Rule unit: X.6 // Rule unit: X.6
rule "X.6.0: Remove Lower Rank Entity Contained by ENTITY or HINT" rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
salience 32 salience 32
when when
$containment: Containment( $higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval(dictionary.getDictionaryRank($contained.type()) < dictionary.getDictionaryRank($container.type())),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.0", "remove Entity of lower rank when contained by entity of type ENTITY or HINT"); $lowerRank.getIntersectingNodes().forEach(node -> update(node));
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
retract($lowerRank);
end end
rule "X.6.1: Remove Entity, when contained in another entity of type ENTITY or HINT with larger text range" rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
salience 32 salience 32
when when
$containment: Containment( $outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval($container.getTextRange().length() > $contained.getTextRange().length()),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.1", "remove Entity when contained in another entity of type ENTITY or HINT with larger text range"); $inner.getIntersectingNodes().forEach(node -> update(node));
end $inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
retract($inner);
end
// Rule unit: X.8 // Rule unit: X.8
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -1515,36 +1441,25 @@ rule "X.10.0: remove false positives of ai"
// Rule unit: X.11 // Rule unit: X.11
rule "X.11.1: Remove non-manual entity which intersects with a manual entity" rule "X.11.1: Remove non manual entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $nonManualEntity: TextEntity(intersects($manualEntity), engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.1", "remove entity which intersects with a manual entity"); $nonManualEntity.remove("X.11.1", "remove entity which intersects with a manual entity");
retract($nonManualEntity);
end end
rule "X.11.2: Remove non-manual entity which is equal to manual entity" rule "X.11.2: Remove non manual entity which are equal to manual entity"
salience 70 salience 70
when when
$equality: Equality( $manualEntity: TextEntity(engines contains Engine.MANUAL, active(), $type: type())
$a: a, $nonManualEntity: TextEntity(getTextRange().equals($manualEntity.getTextRange()), type() == $type, entityType == EntityType.ENTITY, !hasManualChanges(), engines not contains Engine.MANUAL)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.MANUAL,
$a.active(),
$b.entityType == EntityType.ENTITY,
!$b.hasManualChanges(),
$b.engines not contains Engine.MANUAL
)
then then
$a.addEngines($b.getEngines()); $manualEntity.addEngines($nonManualEntity.getEngines());
$b.remove("X.11.2", "remove non-manual entity which is equal to manual entity"); $nonManualEntity.remove("X.11.2", "remove non manual entity which are equal to manual entity");
retract($nonManualEntity);
end end
@ -1557,6 +1472,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -18,7 +18,8 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
@ -78,7 +79,7 @@ rule "CBI.0.1: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.1", "Author found by \"et al\" regex", "personal_data_geolocation_article_39e3"); entity.redact("CBI.0.1", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -91,7 +92,7 @@ rule "CBI.0.2: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.2", "Author found by \"et al\" regex", "vertebrate_study_personal_data_geolocation_article_39e2"); entity.redact("CBI.0.2", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -101,7 +102,7 @@ rule "CBI.0.3: Redact CBI Authors (non vertebrate Study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_author", dictionaryEntry) $entity: TextEntity(type() == "CBI_author", dictionaryEntry)
then then
$entity.redact("CBI.0.3", "Author found", "personal_data_geolocation_article_39e3"); $entity.redact("CBI.0.3", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "CBI.0.4: Redact CBI Authors (vertebrate Study)" rule "CBI.0.4: Redact CBI Authors (vertebrate Study)"
@ -109,7 +110,7 @@ rule "CBI.0.4: Redact CBI Authors (vertebrate Study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_author", dictionaryEntry) $entity: TextEntity(type() == "CBI_author", dictionaryEntry)
then then
$entity.redact("CBI.0.4", "Author found", "vertebrate_study_personal_data_geolocation_article_39e2"); $entity.redact("CBI.0.4", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -121,7 +122,7 @@ rule "PII.0.1: Redact all PII (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.1", "Personal Information found", "personal_data_geolocation_article_39e3"); $pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
@ -230,6 +231,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -241,6 +244,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -251,8 +256,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -262,7 +269,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -273,7 +282,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -284,6 +295,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -296,7 +309,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -307,7 +322,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -328,6 +343,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -337,7 +353,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -348,115 +365,71 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
// Rule unit: X.3 // Rule unit: X.3
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -464,117 +437,68 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
// Rule unit: X.6 // Rule unit: X.6
rule "X.6.0: Remove Lower Rank Entity Contained by ENTITY or HINT" rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
salience 32 salience 32
when when
$containment: Containment( $higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval(dictionary.getDictionaryRank($contained.type()) < dictionary.getDictionaryRank($container.type())),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.0", "remove Entity of lower rank when contained by entity of type ENTITY or HINT"); $lowerRank.getIntersectingNodes().forEach(node -> update(node));
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
retract($lowerRank);
end end
rule "X.6.1: Remove Entity, when contained in another entity of type ENTITY or HINT with larger text range" rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
salience 32 salience 32
when when
$containment: Containment( $outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval($container.getTextRange().length() > $contained.getTextRange().length()),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.1", "remove Entity when contained in another entity of type ENTITY or HINT with larger text range"); $inner.getIntersectingNodes().forEach(node -> update(node));
end $inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
retract($inner);
end
// Rule unit: X.8 // Rule unit: X.8
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -600,36 +524,25 @@ rule "X.10.0: remove false positives of ai"
// Rule unit: X.11 // Rule unit: X.11
rule "X.11.1: Remove non-manual entity which intersects with a manual entity" rule "X.11.1: Remove non manual entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $nonManualEntity: TextEntity(intersects($manualEntity), engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.1", "remove entity which intersects with a manual entity"); $nonManualEntity.remove("X.11.1", "remove entity which intersects with a manual entity");
retract($nonManualEntity);
end end
rule "X.11.2: Remove non-manual entity which is equal to manual entity" rule "X.11.2: Remove non manual entity which are equal to manual entity"
salience 70 salience 70
when when
$equality: Equality( $manualEntity: TextEntity(engines contains Engine.MANUAL, active(), $type: type())
$a: a, $nonManualEntity: TextEntity(getTextRange().equals($manualEntity.getTextRange()), type() == $type, entityType == EntityType.ENTITY, !hasManualChanges(), engines not contains Engine.MANUAL)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.MANUAL,
$a.active(),
$b.entityType == EntityType.ENTITY,
!$b.hasManualChanges(),
$b.engines not contains Engine.MANUAL
)
then then
$a.addEngines($b.getEngines()); $manualEntity.addEngines($nonManualEntity.getEngines());
$b.remove("X.11.2", "remove non-manual entity which is equal to manual entity"); $nonManualEntity.remove("X.11.2", "remove non manual entity which are equal to manual entity");
retract($nonManualEntity);
end end
@ -642,6 +555,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -18,7 +18,8 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
@ -309,6 +310,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -320,6 +323,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -330,8 +335,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -341,7 +348,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -352,7 +361,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -363,6 +374,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -375,7 +388,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -386,7 +401,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -398,6 +413,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -418,6 +435,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -427,7 +445,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -438,115 +457,71 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
// Rule unit: X.3 // Rule unit: X.3
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -554,84 +529,46 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
// Rule unit: X.6 // Rule unit: X.6
rule "X.6.0: Remove Lower Rank Entity Contained by ENTITY or HINT" rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
salience 32 salience 32
when when
$containment: Containment( $higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval(dictionary.getDictionaryRank($contained.type()) < dictionary.getDictionaryRank($container.type())),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.0", "remove Entity of lower rank when contained by entity of type ENTITY or HINT"); $lowerRank.getIntersectingNodes().forEach(node -> update(node));
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
retract($lowerRank);
end end
rule "X.6.1: Remove Entity, when contained in another entity of type ENTITY or HINT with larger text range" rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
salience 32 salience 32
when when
$containment: Containment( $outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval($container.getTextRange().length() > $contained.getTextRange().length()),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.1", "remove Entity when contained in another entity of type ENTITY or HINT with larger text range"); $inner.getIntersectingNodes().forEach(node -> update(node));
end $inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
retract($inner);
end
// Rule unit: X.7 // Rule unit: X.7
@ -649,33 +586,22 @@ rule "X.7.0: Remove all images"
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -701,36 +627,25 @@ rule "X.10.0: remove false positives of ai"
// Rule unit: X.11 // Rule unit: X.11
rule "X.11.1: Remove non-manual entity which intersects with a manual entity" rule "X.11.1: Remove non manual entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $nonManualEntity: TextEntity(intersects($manualEntity), engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.1", "remove entity which intersects with a manual entity"); $nonManualEntity.remove("X.11.1", "remove entity which intersects with a manual entity");
retract($nonManualEntity);
end end
rule "X.11.2: Remove non-manual entity which is equal to manual entity" rule "X.11.2: Remove non manual entity which are equal to manual entity"
salience 70 salience 70
when when
$equality: Equality( $manualEntity: TextEntity(engines contains Engine.MANUAL, active(), $type: type())
$a: a, $nonManualEntity: TextEntity(getTextRange().equals($manualEntity.getTextRange()), type() == $type, entityType == EntityType.ENTITY, !hasManualChanges(), engines not contains Engine.MANUAL)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.MANUAL,
$a.active(),
$b.entityType == EntityType.ENTITY,
!$b.hasManualChanges(),
$b.engines not contains Engine.MANUAL
)
then then
$a.addEngines($b.getEngines()); $manualEntity.addEngines($nonManualEntity.getEngines());
$b.remove("X.11.2", "remove non-manual entity which is equal to manual entity"); $nonManualEntity.remove("X.11.2", "remove non manual entity which are equal to manual entity");
retract($nonManualEntity);
end end
@ -743,6 +658,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -18,7 +18,8 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
@ -209,6 +210,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -220,6 +223,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -230,8 +235,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -241,7 +248,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -252,7 +261,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -263,6 +274,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -275,7 +288,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -286,7 +301,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -298,6 +313,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -318,6 +335,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -327,7 +345,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -338,115 +357,71 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
// Rule unit: X.3 // Rule unit: X.3
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -454,84 +429,46 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
// Rule unit: X.6 // Rule unit: X.6
rule "X.6.0: Remove Lower Rank Entity Contained by ENTITY or HINT" rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
salience 32 salience 32
when when
$containment: Containment( $higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval(dictionary.getDictionaryRank($contained.type()) < dictionary.getDictionaryRank($container.type())),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.0", "remove Entity of lower rank when contained by entity of type ENTITY or HINT"); $lowerRank.getIntersectingNodes().forEach(node -> update(node));
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
retract($lowerRank);
end end
rule "X.6.1: Remove Entity, when contained in another entity of type ENTITY or HINT with larger text range" rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
salience 32 salience 32
when when
$containment: Containment( $outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval($container.getTextRange().length() > $contained.getTextRange().length()),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.1", "remove Entity when contained in another entity of type ENTITY or HINT with larger text range"); $inner.getIntersectingNodes().forEach(node -> update(node));
end $inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
retract($inner);
end
// Rule unit: X.7 // Rule unit: X.7
@ -549,33 +486,22 @@ rule "X.7.0: Remove all images"
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -601,36 +527,25 @@ rule "X.10.0: remove false positives of ai"
// Rule unit: X.11 // Rule unit: X.11
rule "X.11.1: Remove non-manual entity which intersects with a manual entity" rule "X.11.1: Remove non manual entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $nonManualEntity: TextEntity(intersects($manualEntity), engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.1", "remove entity which intersects with a manual entity"); $nonManualEntity.remove("X.11.1", "remove entity which intersects with a manual entity");
retract($nonManualEntity);
end end
rule "X.11.2: Remove non-manual entity which is equal to manual entity" rule "X.11.2: Remove non manual entity which are equal to manual entity"
salience 70 salience 70
when when
$equality: Equality( $manualEntity: TextEntity(engines contains Engine.MANUAL, active(), $type: type())
$a: a, $nonManualEntity: TextEntity(getTextRange().equals($manualEntity.getTextRange()), type() == $type, entityType == EntityType.ENTITY, !hasManualChanges(), engines not contains Engine.MANUAL)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.MANUAL,
$a.active(),
$b.entityType == EntityType.ENTITY,
!$b.hasManualChanges(),
$b.engines not contains Engine.MANUAL
)
then then
$a.addEngines($b.getEngines()); $manualEntity.addEngines($nonManualEntity.getEngines());
$b.remove("X.11.2", "remove non-manual entity which is equal to manual entity"); $nonManualEntity.remove("X.11.2", "remove non manual entity which are equal to manual entity");
retract($nonManualEntity);
end end
@ -643,6 +558,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -65,7 +65,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -121,7 +121,7 @@
"reason": "PII (Personal Identification Information) found", "reason": "PII (Personal Identification Information) found",
"matchedRule": 12, "matchedRule": 12,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -325,7 +325,7 @@
"reason": "PII (Personal Identification Information) found", "reason": "PII (Personal Identification Information) found",
"matchedRule": 12, "matchedRule": 12,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -381,7 +381,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -437,7 +437,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -549,7 +549,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -605,7 +605,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -661,7 +661,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -997,7 +997,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1053,7 +1053,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -1165,7 +1165,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1221,7 +1221,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1277,7 +1277,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1333,7 +1333,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1445,7 +1445,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1557,7 +1557,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1613,7 +1613,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1669,7 +1669,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -1893,7 +1893,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -2117,7 +2117,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -2285,7 +2285,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -2677,7 +2677,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -2789,7 +2789,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -2845,7 +2845,7 @@
"reason": "PII (Personal Identification Information) found", "reason": "PII (Personal Identification Information) found",
"matchedRule": 12, "matchedRule": 12,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -2957,7 +2957,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -3125,7 +3125,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -3237,7 +3237,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -3349,7 +3349,7 @@
"reason": "PII (Personal Identification Information) found", "reason": "PII (Personal Identification Information) found",
"matchedRule": 12, "matchedRule": 12,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -3405,7 +3405,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -3461,7 +3461,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -3629,7 +3629,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -3685,7 +3685,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -3797,7 +3797,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -3909,7 +3909,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -4021,7 +4021,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -4077,7 +4077,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -4189,7 +4189,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -4357,7 +4357,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -4413,7 +4413,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -4469,7 +4469,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -4525,7 +4525,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -4581,7 +4581,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -4637,7 +4637,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -4749,7 +4749,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -4917,7 +4917,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5141,7 +5141,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5253,7 +5253,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5309,7 +5309,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5365,7 +5365,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5421,7 +5421,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -5477,7 +5477,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5533,7 +5533,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -5589,7 +5589,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5645,7 +5645,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -5757,7 +5757,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -5813,7 +5813,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5878,7 +5878,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -5934,7 +5934,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -5990,7 +5990,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6046,7 +6046,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6102,7 +6102,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -6158,7 +6158,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -6214,7 +6214,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6279,7 +6279,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6335,7 +6335,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -6391,7 +6391,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6456,7 +6456,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6512,7 +6512,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6624,7 +6624,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6680,7 +6680,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -6801,7 +6801,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6857,7 +6857,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -6913,7 +6913,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -6969,7 +6969,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7025,7 +7025,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7081,7 +7081,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7137,7 +7137,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7193,7 +7193,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7249,7 +7249,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7305,7 +7305,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -7361,7 +7361,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7473,7 +7473,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -7529,7 +7529,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7585,7 +7585,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7641,7 +7641,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -7697,7 +7697,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -7753,7 +7753,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -7809,7 +7809,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7865,7 +7865,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -7977,7 +7977,7 @@
"reason": "Published Information found", "reason": "Published Information found",
"matchedRule": 11, "matchedRule": 11,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": false, "redacted": false,
"section": "", "section": "",
@ -8145,7 +8145,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "Footer", "section": "Footer",
@ -8313,7 +8313,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -8481,7 +8481,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -8705,7 +8705,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -8761,7 +8761,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -8826,7 +8826,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -8882,7 +8882,7 @@
"reason": "Author found", "reason": "Author found",
"matchedRule": 1, "matchedRule": 1,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -8938,7 +8938,7 @@
"reason": "Address found", "reason": "Address found",
"matchedRule": 2, "matchedRule": 2,
"rectangle": false, "rectangle": false,
"legalBasis": "personal_data_geolocation_article_39e3", "legalBasis": "Article 39(e)(3) of Regulation (EC) No 178/2002",
"imported": false, "imported": false,
"redacted": true, "redacted": true,
"section": "", "section": "",
@ -9046,58 +9046,49 @@
], ],
"legalBasis": [ "legalBasis": [
{ {
"name": "1.1 personal data (incl. geolocation); Article 39(e)(3)", "name": "1.1 personal data (incl. geolocation); Article 39(e)(1)",
"description": "(Regulations (EU) 2016/679 and (EU) 2018/1725 shall apply to the processing of personal data carried out pursuant to this Regulation. Any personal data made public pursuant to Article 38 of this Regulation and this Article shall only be used to ensure the transparency of the risk assessment under this Regulation and shall not be further processed in a manner that is incompatible with these purposes, in accordance with point (b) of Article 5(1) of Regulation (EU) 2016/679 and point (b) of Article 4(1) of Regulation (EU) 2018/1725, as the case may be)", "description": "any other personal data except for\n a. the name and address of the applicant;\n b. the names of authors of published or publicly available studies supporting such requests; and the names of all participants and observers in meetings of the Scientific Committee and the Scientific Panels, their working groups and any other ad hoc group meeting on the subject matter.",
"reason": "personal_data_geolocation_article_39e3", "reason": "Article 39(e)(3) of Regulation (EC) No 178/2002"
"technicalName": "personal_data_geolocation_article_39e3"
}, },
{ {
"name": "1.2 vertebrate study related personal data (incl. geolocation); Article 39(e)(2)", "name": "1.2 vertebrate study related personal data (incl. geolocation); Article 39(e)(2)",
"description": "personal data (names and addresses) of individuals involved in testing on vertebrate studies or in obtaining toxicological information", "description": "personal data (names and addresses) of individuals involved in testing on vertebrate studies or in obtaining toxicological information",
"reason": "Article 39(e)(2) of Regulation (EC) No 178/2002", "reason": "Article 39(e)(2) of Regulation (EC) No 178/2002"
"technicalName": "vertebrate_study_personal_data_geolocation_article_39e2"
}, },
{ {
"name": "2. manufacturing or production process", "name": "2. manufacturing or production process",
"description": "the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety", "description": "the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety",
"reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)", "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
"technicalName": "manufacturing_production_process"
}, },
{ {
"name": "3. links between a producer and applicant", "name": "3. links between a producer and applicant",
"description": "commercial links between a producer or importer and the applicant or the authorisation holder, where applicable", "description": "commercial links between a producer or importer and the applicant or the authorisation holder, where applicable;",
"reason": "links_producer_applicant", "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
"technicalName": "links_producer_applicant"
}, },
{ {
"name": "4. commercial information", "name": "4. commercial information",
"description": "commercial information revealing sourcing, market shares or business strategy of the applicant", "description": "commercial information revealing sourcing, market shares or business strategy of the applicant",
"reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)", "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
"technicalName": "commercial_information"
}, },
{ {
"name": "5. quantitative composition", "name": "5. quantitative composition",
"description": "quantitative composition of the subject matter of the request, except for information which is relevant to the assessment of safety", "description": "quantitative composition of the subject matter of the request, except for information which is relevant to the assessment of safety",
"reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)", "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
"technicalName": "quantitative_composition"
}, },
{ {
"name": "6. specification of impurity", "name": "6. specification of impurity",
"description": "the specification of impurity of the active substance and the related methods of analysis for impurities in the active substance as manufactured, except for the impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant and the related methods of analysis for such impurities", "description": "the specification of impurity of the active substance and the related methods of analysis for impurities in the active substance as manufactured, except for the impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant and the related methods of analysis for such impurities",
"reason": "Article 63(2)(b) of Regulation (EC) No 1107/2009", "reason": "Article 63(2)(b) of Regulation (EC) No 1107/2009"
"technicalName": "specification_impurity"
}, },
{ {
"name": "7. results of production batches", "name": "7. results of production batches",
"description": "results of production batches of the active substance including impurities", "description": "results of production batches of the active substance including impurities",
"reason": "Article 63(2)(c) of Regulation (EC) No 1107/2009", "reason": "Article 63(2)(c) of Regulation (EC) No 1107/2009"
"technicalName": "results_production_batches"
}, },
{ {
"name": "8. composition of a plant protection product", "name": "8. composition of a plant protection product",
"description": "information on the complete composition of a plant protection product", "description": "information on the complete composition of a plant protection product",
"reason": "Article 63(2)(d) of Regulation (EC) No 1107/2009", "reason": "Article 63(2)(d) of Regulation (EC) No 1107/2009"
"technicalName": "composition_plant_protection_product"
} }
], ],
"dictionaryVersion": 492, "dictionaryVersion": 492,

View File

@ -15761,7 +15761,7 @@
{ {
"name": "2. manufacturing or production process", "name": "2. manufacturing or production process",
"description": "the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety", "description": "the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety",
"reason": "manufacturing_production_process" "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
}, },
{ {
"name": "3. links between a producer and applicant", "name": "3. links between a producer and applicant",
@ -15781,17 +15781,17 @@
{ {
"name": "6. specification of impurity", "name": "6. specification of impurity",
"description": "the specification of impurity of the active substance and the related methods of analysis for impurities in the active substance as manufactured, except for the impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant and the related methods of analysis for such impurities", "description": "the specification of impurity of the active substance and the related methods of analysis for impurities in the active substance as manufactured, except for the impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant and the related methods of analysis for such impurities",
"reason": "specification_impurity" "reason": "Article 63(2)(b) of Regulation (EC) No 1107/2009"
}, },
{ {
"name": "7. results of production batches", "name": "7. results of production batches",
"description": "results of production batches of the active substance including impurities", "description": "results of production batches of the active substance including impurities",
"reason": "results_production_batches" "reason": "Article 63(2)(c) of Regulation (EC) No 1107/2009"
}, },
{ {
"name": "8. composition of a plant protection product", "name": "8. composition of a plant protection product",
"description": "information on the complete composition of a plant protection product", "description": "information on the complete composition of a plant protection product",
"reason": "composition_plant_protection_product" "reason": "Article 63(2)(d) of Regulation (EC) No 1107/2009"
} }
], ],
"dictionaryVersion": 21, "dictionaryVersion": 21,

@ -1 +1 @@
Subproject commit 57e6e0dd3c08a3a65ec59b5dfb70f0f77ebcc7c7 Subproject commit 5705cc0782605fdca5dfff134b436f7143c9e421

View File

@ -16,7 +16,5 @@
<logger name="org.apache.fontbox.ttf" level="ERROR"/> <logger name="org.apache.fontbox.ttf" level="ERROR"/>
<logger name="org.drools.mvel" level="ERROR"/> <logger name="org.drools.mvel" level="ERROR"/>
<logger name="org.springframework.web.socket.config" level="WARN"/>
<logger name="org.mongodb.driver.client" level="ERROR"/>
</configuration> </configuration>

View File

@ -4,13 +4,7 @@ import com.iqser.red.service.redaction.v1.server.logger.RulesLogger;
global RulesLogger logger global RulesLogger logger
//------------------------------------ queries ------------------------------------
//------------------------------------ LOG rules ------------------------------------
// Rule unit: LOG.0
rule "LOG.0.0: Test log info" rule "LOG.0.0: Test log info"
salience 1 salience 1
when when

View File

@ -2,55 +2,46 @@
{ {
"name": "1.1 personal data (incl. geolocation); Article 39(e)(3)", "name": "1.1 personal data (incl. geolocation); Article 39(e)(3)",
"description": "(Regulations (EU) 2016/679 and (EU) 2018/1725 shall apply to the processing of personal data carried out pursuant to this Regulation. Any personal data made public pursuant to Article 38 of this Regulation and this Article shall only be used to ensure the transparency of the risk assessment under this Regulation and shall not be further processed in a manner that is incompatible with these purposes, in accordance with point (b) of Article 5(1) of Regulation (EU) 2016/679 and point (b) of Article 4(1) of Regulation (EU) 2018/1725, as the case may be)", "description": "(Regulations (EU) 2016/679 and (EU) 2018/1725 shall apply to the processing of personal data carried out pursuant to this Regulation. Any personal data made public pursuant to Article 38 of this Regulation and this Article shall only be used to ensure the transparency of the risk assessment under this Regulation and shall not be further processed in a manner that is incompatible with these purposes, in accordance with point (b) of Article 5(1) of Regulation (EU) 2016/679 and point (b) of Article 4(1) of Regulation (EU) 2018/1725, as the case may be)",
"reason": "Article 39(e)(3) of Regulation (EC) No 178/2002", "reason": "Article 39(e)(3) of Regulation (EC) No 178/2002"
"technicalName": "personal_data_geolocation_article_39e3"
}, },
{ {
"name": "1.2 vertebrate study related personal data (incl. geolocation); Article 39(e)(2)", "name": "1.2 vertebrate study related personal data (incl. geolocation); Article 39(e)(2)",
"description": "personal data (names and addresses) of individuals involved in testing on vertebrate studies or in obtaining toxicological information", "description": "personal data (names and addresses) of individuals involved in testing on vertebrate studies or in obtaining toxicological information",
"reason": "Article 39(e)(2) of Regulation (EC) No 178/2002", "reason": "Article 39(e)(2) of Regulation (EC) No 178/2002"
"technicalName": "vertebrate_study_personal_data_geolocation_article_39e2"
}, },
{ {
"name": "2. manufacturing or production process", "name": "2. manufacturing or production process",
"description": "the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety", "description": "the manufacturing or production process, including the method and innovative aspects thereof, as well as other technical and industrial specifications inherent to that process or method, except for information which is relevant to the assessment of safety",
"reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)", "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
"technicalName": "manufacturing_production_process"
}, },
{ {
"name": "3. links between a producer and applicant", "name": "3. links between a producer and applicant",
"description": "commercial links between a producer or importer and the applicant or the authorisation holder, where applicable", "description": "commercial links between a producer or importer and the applicant or the authorisation holder, where applicable",
"reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)", "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
"technicalName": "links_producer_applicant"
}, },
{ {
"name": "4. commercial information", "name": "4. commercial information",
"description": "commercial information revealing sourcing, market shares or business strategy of the applicant", "description": "commercial information revealing sourcing, market shares or business strategy of the applicant",
"reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)", "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
"technicalName": "commercial_information"
}, },
{ {
"name": "5. quantitative composition", "name": "5. quantitative composition",
"description": "quantitative composition of the subject matter of the request, except for information which is relevant to the assessment of safety", "description": "quantitative composition of the subject matter of the request, except for information which is relevant to the assessment of safety",
"reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)", "reason": "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)"
"technicalName": "quantitative_composition"
}, },
{ {
"name": "6. specification of impurity", "name": "6. specification of impurity",
"description": "the specification of impurity of the active substance and the related methods of analysis for impurities in the active substance as manufactured, except for the impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant and the related methods of analysis for such impurities", "description": "the specification of impurity of the active substance and the related methods of analysis for impurities in the active substance as manufactured, except for the impurities that are considered to be toxicologically, ecotoxicologically or environmentally relevant and the related methods of analysis for such impurities",
"reason": "Article 63(2)(b) of Regulation (EC) No 1107/2009", "reason": "Article 63(2)(b) of Regulation (EC) No 1107/2009"
"technicalName": "specification_impurity"
}, },
{ {
"name": "7. results of production batches", "name": "7. results of production batches",
"description": "results of production batches of the active substance including impurities", "description": "results of production batches of the active substance including impurities",
"reason": "Article 63(2)(c) of Regulation (EC) No 1107/2009", "reason": "Article 63(2)(c) of Regulation (EC) No 1107/2009"
"technicalName": "results_production_batches"
}, },
{ {
"name": "8. composition of a plant protection product", "name": "8. composition of a plant protection product",
"description": "information on the complete composition of a plant protection product", "description": "information on the complete composition of a plant protection product",
"reason": "Article 63(2)(d) of Regulation (EC) No 1107/2009", "reason": "Article 63(2)(d) of Regulation (EC) No 1107/2009"
"technicalName": "composition_plant_protection_product"
} }
] ]

View File

@ -18,10 +18,10 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SuperSection;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell; import com.iqser.red.service.redaction.v1.server.model.document.nodes.TableCell;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;
@ -93,7 +93,7 @@ rule "CBI.0.1: Add CBI_author with \"et al.\" RegEx (non vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.1", "Author found by \"et al\" regex", "personal_data_geolocation_article_39e3"); entity.redact("CBI.0.1", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -106,7 +106,7 @@ rule "CBI.0.2: Add CBI_author with \"et al.\" RegEx (vertebrate study)"
then then
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
.forEach(entity -> { .forEach(entity -> {
entity.redact("CBI.0.2", "Author found by \"et al\" regex", "vertebrate_study_personal_data_geolocation_article_39e2"); entity.redact("CBI.0.2", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(entity); dictionary.recommendEverywhere(entity);
}); });
end end
@ -116,7 +116,7 @@ rule "CBI.0.3: Redact CBI Authors (non vertebrate Study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_author", dictionaryEntry) $entity: TextEntity(type() == "CBI_author", dictionaryEntry)
then then
$entity.apply("CBI.0.0", "Author found", "personal_data_geolocation_article_39e3"); $entity.apply("CBI.0.0", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "CBI.0.4: Redact CBI Authors (vertebrate Study)" rule "CBI.0.4: Redact CBI Authors (vertebrate Study)"
@ -124,7 +124,7 @@ rule "CBI.0.4: Redact CBI Authors (vertebrate Study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$entity: TextEntity(type() == "CBI_author", dictionaryEntry) $entity: TextEntity(type() == "CBI_author", dictionaryEntry)
then then
$entity.redact("CBI.0.4", "Author found", "vertebrate_study_personal_data_geolocation_article_39e2"); $entity.redact("CBI.0.4", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -142,7 +142,7 @@ rule "CBI.1.1: Redact CBI Address (Vertebrate Study)"
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
$entity: TextEntity(type() == "CBI_address", dictionaryEntry) $entity: TextEntity(type() == "CBI_address", dictionaryEntry)
then then
$entity.redact("CBI.1.1", "Address found", "vertebrate_study_personal_data_geolocation_article_39e2"); $entity.redact("CBI.1.1", "Address found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -167,7 +167,7 @@ rule "CBI.9.0: Redact all cells with Header Author(s) as CBI_author (non vertebr
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "personal_data_geolocation_article_39e3")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.0", "Author(s) found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)" rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate study)"
@ -180,7 +180,7 @@ rule "CBI.9.1: Redact all cells with Header Author as CBI_author (non vertebrate
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "personal_data_geolocation_article_39e3")); .forEach(redactionEntity -> redactionEntity.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -195,7 +195,7 @@ rule "CBI.10.0: Redact all cells with Header Author(s) as CBI_author (vertebrate
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.0", "Author(s) found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(redactionEntity -> redactionEntity.redact("CBI.10.0", "Author(s) found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)" rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)"
@ -208,7 +208,7 @@ rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate st
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY)) .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.forEach(redactionEntity -> redactionEntity.redact("CBI.10.1", "Author found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(redactionEntity -> redactionEntity.redact("CBI.10.1", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -291,7 +291,7 @@ rule "CBI.20.2: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJEC
then then
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section) entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
.forEach(laboratoryEntity -> { .forEach(laboratoryEntity -> {
laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "vertebrate_study_personal_data_geolocation_article_39e2"); laboratoryEntity.redact("CBI.20.2", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
dictionary.recommendEverywhere(laboratoryEntity); dictionary.recommendEverywhere(laboratoryEntity);
}); });
end end
@ -304,7 +304,7 @@ rule "CBI.23.0: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (no
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "personal_data_geolocation_article_39e3")); .forEach(authorEntity -> authorEntity.redact("CBI.23.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)" rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (vertebrate study)"
@ -313,7 +313,7 @@ rule "CBI.23.1: Redact between \"AUTHOR(S)\" and \"(STUDY) COMPLETION DATE\" (ve
$document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE"))
then then
entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200) entityCreationService.shortestBetweenAnyStringIgnoreCase(List.of("AUTHOR(S)", "AUTHOR(S):"), List.of("COMPLETION DATE", "COMPLETION DATE:", "STUDY COMPLETION DATE", "STUDY COMPLETION DATE:"), "CBI_author", EntityType.ENTITY, $document, 200)
.forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(authorEntity -> authorEntity.redact("CBI.23.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -325,7 +325,7 @@ rule "PII.0.1: Redact all PII (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.1", "Personal Information found", "personal_data_geolocation_article_39e3"); $pii.redact("PII.0.1", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "PII.0.2: Redact all PII (vertebrate study)" rule "PII.0.2: Redact all PII (vertebrate study)"
@ -333,7 +333,7 @@ rule "PII.0.2: Redact all PII (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$pii: TextEntity(type() == "PII", dictionaryEntry) $pii: TextEntity(type() == "PII", dictionaryEntry)
then then
$pii.redact("PII.0.2", "Personal Information found", "vertebrate_study_personal_data_geolocation_article_39e2"); $pii.redact("PII.0.2", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -344,7 +344,7 @@ rule "PII.1.1: Redact Emails by RegEx (Non vertebrate study)"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "personal_data_geolocation_article_39e3")); .forEach(emailEntity -> emailEntity.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.1.2: Redact Emails by RegEx (vertebrate study)" rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
@ -353,7 +353,7 @@ rule "PII.1.2: Redact Emails by RegEx (vertebrate study)"
$section: Section(containsString("@")) $section: Section(containsString("@"))
then then
entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("\\b([A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z\\-]{1,23}[A-Za-z])\\b", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(emailEntity -> emailEntity.redact("PII.1.2", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.1.3: Redact typoed Emails with indicator" rule "PII.1.3: Redact typoed Emails with indicator"
@ -361,7 +361,7 @@ rule "PII.1.3: Redact typoed Emails with indicator"
$section: Section(containsString("@") || containsStringIgnoreCase("mail")) $section: Section(containsString("@") || containsStringIgnoreCase("mail"))
then then
entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("mail[:\\.\\s]{1,2}([\\w\\/\\-\\{\\(\\. ]{3,20}(@|a|f)\\s?[\\w\\/\\-\\{\\(\\. ]{3,20}(\\. \\w{2,4}\\b|\\.\\B|\\.\\w{1,4}\\b))", "PII", EntityType.ENTITY, 1, $section)
.forEach(emailEntity -> emailEntity.redact("PII.1.3", "Personal information found", "personal_data_geolocation_article_39e3")); .forEach(emailEntity -> emailEntity.redact("PII.1.3", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -380,7 +380,7 @@ rule "PII.2.1: Redact Phone and Fax by RegEx (non vertebrate study)"
containsString("Fer")) containsString("Fer"))
then then
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section) entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
.forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)" rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)"
@ -397,7 +397,7 @@ rule "PII.2.2: Redact Phone and Fax by RegEx (vertebrate study)"
containsString("Fer")) containsString("Fer"))
then then
entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section) entityCreationService.byRegexIgnoreCase("\\b(contact|telephone|phone|ph\\.|fax|tel|ter[^\\w]|mobile|fel[^\\w]|fer[^\\w])[a-zA-Z\\s]{0,10}[:.\\s]{0,3}([\\+\\d\\(][\\s\\d\\(\\)\\-\\/\\.]{4,100}\\d)\\b", "PII", EntityType.ENTITY, 2, $section)
.forEach(contactEntity -> contactEntity.redact("PII.2.2", "Found by Phone and Fax Regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.2.2", "Found by Phone and Fax Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -408,7 +408,7 @@ rule "PII.3.1: Redact telephone numbers by RegEx (Non vertebrate study)"
$section: Section(!hasTables(), matchesRegex("[+]\\d{1,}")) $section: Section(!hasTables(), matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)" rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)"
@ -417,16 +417,17 @@ rule "PII.3.2: Redact telephone numbers by RegEx (vertebrate study)"
$section: Section(!hasTables(), matchesRegex("[+]\\d{1,}")) $section: Section(!hasTables(), matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.3.2", "Telephone number found by regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.3.2", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.4: Redact telephone numbers by RegEx (Non vertebrate study)" rule "PII.3.4: Redact telephone numbers by RegEx (Non vertebrate study)"
when when
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$rowCell: TableCell(matchesRegex("[+]\\d{1,}")) $rowCell: TableCell(matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell)
.forEach(entity -> entity.redact("PII.3.4", "Telephone number found by regex", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.3.4", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)" rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)"
@ -435,7 +436,7 @@ rule "PII.3.5: Redact telephone numbers by RegEx (vertebrate study)"
$rowCell: TableCell(matchesRegex("[+]\\d{1,}")) $rowCell: TableCell(matchesRegex("[+]\\d{1,}"))
then then
entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell) entityCreationService.byRegex("((([+]\\d{1,3} (\\d{7,12})\\b)|([+]\\d{1,3}(\\d{3,12})\\b|[+]\\d{1,3}([ -]\\(?\\d{1,6}\\)?){2,4})|[+]\\d{1,3} ?((\\d{2,6}\\)?)([ -]\\d{2,6}){1,4}))(-\\d{1,3})?\\b)", "PII", EntityType.ENTITY, 1, $rowCell)
.forEach(entity -> entity.redact("PII.3.5", "Telephone number found by regex", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.3.5", "Telephone number found by regex", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -450,7 +451,7 @@ rule "PII.5.1: Redact line after contact information keywords reduced (non verte
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrate study)" rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrate study)"
@ -463,7 +464,7 @@ rule "PII.5.2: Redact line after contact information keywords reduced (Vertebrat
$section: Section(containsString($contactKeyword)) $section: Section(containsString($contactKeyword))
then then
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
.forEach(contactEntity -> contactEntity.redact("PII.5.2", "Found after \"" + $contactKeyword + "\" contact keyword", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.5.2", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -477,7 +478,7 @@ rule "PII.6.1: Redact line between contact keywords (non vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "personal_data_geolocation_article_39e3")); .forEach(contactEntity -> contactEntity.redact("PII.6.1", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.6.2: Redact line between contact keywords (vertebrate study)" rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
@ -489,7 +490,7 @@ rule "PII.6.2: Redact line between contact keywords (vertebrate study)"
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
) )
.forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(contactEntity -> contactEntity.redact("PII.6.2", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -509,7 +510,7 @@ rule "PII.7.1: Redact contact information if applicant is found (non vertebrate
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.7.1", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)" rule "PII.7.2: Redact contact information if applicant is found (vertebrate study)"
@ -527,7 +528,7 @@ rule "PII.7.2: Redact contact information if applicant is found (vertebrate stud
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.7.2", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -547,7 +548,7 @@ rule "PII.8.1: Redact contact information if producer is found (non vertebrate s
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.1", "Producer was found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.8.1", "Producer was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.8.2: Redact contact information if producer is found (vertebrate study)" rule "PII.8.2: Redact contact information if producer is found (vertebrate study)"
@ -565,7 +566,7 @@ rule "PII.8.2: Redact contact information if producer is found (vertebrate study
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section), entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
)) ))
.forEach(entity -> entity.redact("PII.8.2", "Producer was found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.8.2", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -576,7 +577,7 @@ rule "PII.10.0: Redact study director abbreviation (non vertebrate study)"
$section: Section(containsString("KATH") || containsString("BECH") || containsString("KML")) $section: Section(containsString("KATH") || containsString("BECH") || containsString("KML"))
then then
entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.10.0", "Personal information found", "personal_data_geolocation_article_39e3")); .forEach(entity -> entity.redact("PII.10.0", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.10.1: Redact study director abbreviation (vertebrate study)" rule "PII.10.1: Redact study director abbreviation (vertebrate study)"
@ -585,17 +586,17 @@ rule "PII.10.1: Redact study director abbreviation (vertebrate study)"
$section: Section(containsString("KATH") || containsString("BECH") || containsString("KML")) $section: Section(containsString("KATH") || containsString("BECH") || containsString("KML"))
then then
entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section) entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section)
.forEach(entity -> entity.redact("PII.10.1", "Personal information found", "vertebrate_study_personal_data_geolocation_article_39e2")); .forEach(entity -> entity.redact("PII.10.1", "Personal information found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
// Rule unit: PII.11 // Rule unit: PII.11
rule "PII.11.0: Redact On behalf of Sequani Ltd.:" rule "PII.11.0: Redact On behalf of Sequani Ltd.:"
when when
$section: SuperSection(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title")) $section: Section(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title"))
then then
entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section) entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section)
.forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "personal_data_geolocation_article_39e3")); .forEach(authorEntity -> authorEntity.redact("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
@ -606,7 +607,7 @@ rule "PII.12.0: Expand PII entities with salutation prefix"
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")) $entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
then then
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*") entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "personal_data_geolocation_article_39e3")); .ifPresent(expandedEntity -> expandedEntity.apply("PII.12.0", "Expanded PII with salutation prefix", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
end end
rule "PII.12.1: Expand PII entities with salutation prefix" rule "PII.12.1: Expand PII entities with salutation prefix"
@ -615,7 +616,7 @@ rule "PII.12.1: Expand PII entities with salutation prefix"
$entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")) $entityToExpand: TextEntity(type() == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
then then
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*") entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
.ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "vertebrate_study_personal_data_geolocation_article_39e2")); .ifPresent(expandedEntity -> expandedEntity.apply("PII.12.1", "Expanded PII with salutation prefix", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
end end
@ -626,7 +627,7 @@ rule "ETC.0.0: Purity Hint"
when when
$section: Section(containsStringIgnoreCase("purity")) $section: Section(containsStringIgnoreCase("purity"))
then then
entityCreationService.byRegexIgnoreCase("(purity ?( of|\\(.{1,20}\\))?( ?:)?) [<>]{0,1}(100|([1-9]{1}[0-9]{0,1}([.,]{1}[0-9]{1,2})?)) ?% ", "hint_only", EntityType.HINT, 1, $section) entityCreationService.byRegexIgnoreCase("(purity ?( of|\\(.{1,20}\\))?( ?:)?) .{0,5}[\\d\\.]+( .{0,4}\\.)? ?%", "hint_only", EntityType.HINT, 1, $section)
.forEach(hint -> hint.skip("ETC.0.0", "hint only")); .forEach(hint -> hint.skip("ETC.0.0", "hint only"));
end end
@ -637,7 +638,7 @@ rule "ETC.2.1: Redact signatures (non vertebrate study)"
not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") not FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.1", "Signature Found", "personal_data_geolocation_article_39e3"); $signature.redact("ETC.2.1", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
end end
rule "ETC.2.2: Redact signatures (vertebrate study)" rule "ETC.2.2: Redact signatures (vertebrate study)"
@ -645,7 +646,7 @@ rule "ETC.2.2: Redact signatures (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$signature: Image(imageType == ImageType.SIGNATURE) $signature: Image(imageType == ImageType.SIGNATURE)
then then
$signature.redact("ETC.2.2", "Signature Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $signature.redact("ETC.2.2", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -663,7 +664,7 @@ rule "ETC.3.2: Redact logos (vertebrate study)"
FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y") FileAttribute(label == "Vertebrate Study", value soundslike "Yes" || value.toLowerCase() == "y")
$logo: Image(imageType == ImageType.LOGO) $logo: Image(imageType == ImageType.LOGO)
then then
$logo.redact("ETC.3.2", "Logo Found", "vertebrate_study_personal_data_geolocation_article_39e2"); $logo.redact("ETC.3.2", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
end end
@ -722,6 +723,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -733,6 +736,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -743,8 +748,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -754,7 +761,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -765,7 +774,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -776,6 +787,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -788,7 +801,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -799,7 +814,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -811,6 +826,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -831,6 +848,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -840,7 +858,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -851,114 +870,72 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" // Rule unit: X.3
rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -966,117 +943,68 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
// Rule unit: X.6 // Rule unit: X.6
rule "X.6.0: Remove Lower Rank Entity Contained by ENTITY or HINT" rule "X.6.0: Remove Entity of lower rank, when contained by entity of type ENTITY or HINT"
salience 32 salience 32
when when
$containment: Containment( $higherRank: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $lowerRank: TextEntity(containedBy($higherRank), type() != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval(dictionary.getDictionaryRank($contained.type()) < dictionary.getDictionaryRank($container.type())),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.0", "remove Entity of lower rank when contained by entity of type ENTITY or HINT"); $lowerRank.getIntersectingNodes().forEach(node -> update(node));
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY or HINT");
retract($lowerRank);
end end
rule "X.6.1: Remove Entity, when contained in another entity of type ENTITY or HINT with larger text range" rule "X.6.1: remove Entity, when contained in another entity of type ENTITY or HINT with larger text range"
salience 32 salience 32
when when
$containment: Containment( $outer: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$container: container, $inner: TextEntity(containedBy($outer), type() != $type, $outer.getTextRange().length > getTextRange().length(), !hasManualChanges())
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
$container.active(),
$contained.type() != $container.type(),
eval($container.getTextRange().length() > $contained.getTextRange().length()),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.6.1", "remove Entity when contained in another entity of type ENTITY or HINT with larger text range"); $inner.getIntersectingNodes().forEach(node -> update(node));
end $inner.remove("X.6.1", "remove Entity, when contained in another entity of type ENTITY or HINT with larger text range");
retract($inner);
end
// Rule unit: X.8 // Rule unit: X.8
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
@ -1102,36 +1030,26 @@ rule "X.10.0: remove false positives of ai"
// Rule unit: X.11 // Rule unit: X.11
rule "X.11.1: Remove non-manual entity which intersects with a manual entity" rule "X.11.1: Remove non manual entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $nonManualEntity: TextEntity(intersects($manualEntity), engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.1", "remove entity which intersects with a manual entity"); $nonManualEntity.remove("X.11.1", "remove entity which intersects with a manual entity");
retract($nonManualEntity);
end end
rule "X.11.2: Remove non-manual entity which is equal to manual entity"
rule "X.11.2: Remove non manual entity which are equal to manual entity"
salience 70 salience 70
when when
$equality: Equality( $manualEntity: TextEntity(engines contains Engine.MANUAL, active(), $type: type())
$a: a, $nonManualEntity: TextEntity(getTextRange().equals($manualEntity.getTextRange()), type() == $type, entityType == EntityType.ENTITY, !hasManualChanges(), engines not contains Engine.MANUAL)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.MANUAL,
$a.active(),
$b.entityType == EntityType.ENTITY,
!$b.hasManualChanges(),
$b.engines not contains Engine.MANUAL
)
then then
$a.addEngines($b.getEngines()); $manualEntity.addEngines($nonManualEntity.getEngines());
$b.remove("X.11.2", "remove non-manual entity which is equal to manual entity"); $nonManualEntity.remove("X.11.2", "remove non manual entity which are equal to manual entity");
retract($nonManualEntity);
end end
@ -1144,6 +1062,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -18,7 +18,8 @@ import com.iqser.red.service.redaction.v1.server.model.document.TextRange;
import com.iqser.red.service.redaction.v1.server.model.document.entity.*; import com.iqser.red.service.redaction.v1.server.model.document.entity.*;
import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule; import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;
import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity
import com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule
import com.iqser.red.service.redaction.v1.server.model.document.nodes.*; import com.iqser.red.service.redaction.v1.server.model.document.nodes.*;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;
import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;
@ -1314,6 +1315,8 @@ rule "MAN.0.0: Apply manual resize redaction"
then then
manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction); manualChangesApplicationService.resize($entityToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($entityToBeResized);
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.0.1: Apply manual resize redaction" rule "MAN.0.1: Apply manual resize redaction"
@ -1325,6 +1328,8 @@ rule "MAN.0.1: Apply manual resize redaction"
then then
manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction); manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
retract($resizeRedaction); retract($resizeRedaction);
update($imageToBeResized);
update($imageToBeResized.getParent());
end end
@ -1335,8 +1340,10 @@ rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to
$idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers) $idRemoval: IdRemoval($id: annotationId, !removeFromDictionary, !removeFromAllDossiers)
$entityToBeRemoved: TextEntity(matchesAnnotationId($id)) $entityToBeRemoved: TextEntity(matchesAnnotationId($id))
then then
$entityToBeRemoved.addManualChange($idRemoval); $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($entityToBeRemoved);
retract($idRemoval); retract($idRemoval);
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
end end
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image" rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
@ -1346,7 +1353,9 @@ rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to
$imageEntityToBeRemoved: Image($id == id) $imageEntityToBeRemoved: Image($id == id)
then then
$imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);
update($imageEntityToBeRemoved);
retract($idRemoval); retract($idRemoval);
update($imageEntityToBeRemoved.getParent());
end end
@ -1357,7 +1366,9 @@ rule "MAN.2.0: Apply force redaction"
$force: ManualForceRedaction($id: annotationId) $force: ManualForceRedaction($id: annotationId)
$entityToForce: TextEntity(matchesAnnotationId($id)) $entityToForce: TextEntity(matchesAnnotationId($id))
then then
$entityToForce.addManualChange($force); $entityToForce.getManualOverwrite().addChange($force);
update($entityToForce);
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
retract($force); retract($force);
end end
@ -1368,6 +1379,8 @@ rule "MAN.2.1: Apply force redaction to images"
$imageToForce: Image(id == $id) $imageToForce: Image(id == $id)
then then
$imageToForce.getManualOverwrite().addChange($force); $imageToForce.getManualOverwrite().addChange($force);
update($imageToForce);
update($imageToForce.getParent());
retract($force); retract($force);
end end
@ -1380,7 +1393,9 @@ rule "MAN.3.0: Apply entity recategorization"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() != $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));
$entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
update($entityToBeRecategorized);
retract($recategorization); retract($recategorization);
end end
@ -1391,7 +1406,7 @@ rule "MAN.3.1: Apply entity recategorization of same type"
not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))
$entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type) $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type() == $type)
then then
$entityToBeRecategorized.addManualChange($recategorization); $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);
retract($recategorization); retract($recategorization);
end end
@ -1403,6 +1418,8 @@ rule "MAN.3.2: Apply image recategorization"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);
update($imageToBeRecategorized);
update($imageToBeRecategorized.getParent());
retract($recategorization); retract($recategorization);
end end
@ -1422,6 +1439,7 @@ rule "MAN.4.0: Apply legal basis change"
$imageToBeRecategorized: Image($id == id) $imageToBeRecategorized: Image($id == id)
then then
$imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange); $imageToBeRecategorized.getManualOverwrite().addChange($legalBasisChange);
update($imageToBeRecategorized)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1431,7 +1449,8 @@ rule "MAN.4.1: Apply legal basis change"
$legalBasisChange: ManualLegalBasisChange($id: annotationId) $legalBasisChange: ManualLegalBasisChange($id: annotationId)
$entityToBeChanged: TextEntity(matchesAnnotationId($id)) $entityToBeChanged: TextEntity(matchesAnnotationId($id))
then then
$entityToBeChanged.addManualChange($legalBasisChange); $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);
update($entityToBeChanged)
retract($legalBasisChange) retract($legalBasisChange)
end end
@ -1442,115 +1461,72 @@ rule "MAN.4.1: Apply legal basis change"
rule "X.0.0: Remove Entity contained by Entity of same type" rule "X.0.0: Remove Entity contained by Entity of same type"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), !hasManualChanges())
$container: container, not TextEntity(getTextRange().equals($larger.getTextRange()), type() == $type, entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY, !hasManualChanges())
$contained: contained, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
!$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
not TextEntity(
getTextRange().equals($container.getTextRange()),
type() == $container.type(),
entityType == EntityType.DICTIONARY_REMOVAL,
engines contains Engine.DOSSIER_DICTIONARY,
!hasManualChanges()
)
then then
$contained.remove("X.0.0", "remove Entity contained by Entity of same type"); $contained.remove("X.0.0", "remove Entity contained by Entity of same type");
end retract($contained);
end
rule "X.0.1: Remove Entity contained by Entity of same type with manual changes" rule "X.0.1: Remove Entity contained by Entity of same type with manual changes"
salience 65 salience 65
when when
$containment: Containment( $larger: TextEntity($type: type(), $entityType: entityType, !removed(), hasManualChanges())
$container: container, $contained: TextEntity(containedBy($larger), type() == $type, entityType == $entityType, this != $larger, !hasManualChanges())
$contained: contained,
$container.type() == $contained.type(),
$container.entityType == $contained.entityType,
$container != $contained,
!$container.removed(),
$container.hasManualChanges(),
!$contained.hasManualChanges(),
!$contained.removed()
)
then then
$contained.getIntersectingNodes().forEach(node -> update(node));
$contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes"); $contained.remove("X.0.1", "remove Entity contained by Entity of same type with manual changes");
end retract($contained);
end
// Rule unit: X.2 // Rule unit: X.2
rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.ENTITY), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.ENTITY,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
end retract($entity)
end
rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE" rule "X.2.1: Remove Entity of type HINT when contained by FALSE_POSITIVE"
salience 64 salience 64
when when
$containment: Containment( $falsePositive: TextEntity($type: type(), entityType == EntityType.FALSE_POSITIVE, active())
$container: container, $entity: TextEntity(containedBy($falsePositive), type() == $type, (entityType == EntityType.HINT), !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_POSITIVE,
$container.active(),
$contained.entityType == EntityType.HINT,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.2.1", "remove Entity of type HINT when contained by FALSE_POSITIVE"); $entity.getIntersectingNodes().forEach(node -> update(node));
end $entity.remove("X.2.1", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
retract($entity)
end
rule "X.3.0: Remove RECOMMENDATION Contained by FALSE_RECOMMENDATION" // Rule unit: X.3
rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
salience 64 salience 64
when when
$containment: Containment( $falseRecommendation: TextEntity($type: type(), entityType == EntityType.FALSE_RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($falseRecommendation), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.FALSE_RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$contained.type() == $container.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
end retract($recommendation);
end
// Rule unit: X.4 // Rule unit: X.4
rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type" rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"
salience 256 salience 256
when when
$equality: Equality( $entity: TextEntity($type: type(), (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(getTextRange().equals($entity.getTextRange()), type() == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
$a.type() == $b.type(),
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$a.addEngines($b.getEngines()); $entity.addEngines($recommendation.getEngines());
$b.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type"); $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when text range equals ENTITY with same type");
retract($recommendation);
end end
@ -1558,16 +1534,11 @@ rule "X.4.0: Remove Entity of type RECOMMENDATION when text range equals ENTITY
rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY" rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active())
$a: a, $recommendation: TextEntity(intersects($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$b: b,
($a.entityType == EntityType.ENTITY || $a.entityType == EntityType.HINT),
$a.active(),
$b.entityType == EntityType.RECOMMENDATION,
!$b.hasManualChanges()
)
then then
$b.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY"); $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY");
retract($recommendation);
end end
@ -1575,34 +1546,11 @@ rule "X.5.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY"
rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION" rule "X.5.1: Remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"
salience 256 salience 256
when when
$containment: Containment( $entity: TextEntity($type: type(), entityType == EntityType.RECOMMENDATION, active())
$container: container, $recommendation: TextEntity(containedBy($entity), type() != $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges())
$contained: contained,
$container.entityType == EntityType.RECOMMENDATION,
$container.active(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() != $contained.type(),
!$contained.hasManualChanges()
)
then then
$contained.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION"); $recommendation.remove("X.5.1", "remove Entity of type RECOMMENDATION when contained by RECOMMENDATION");
end retract($recommendation);
rule "X.5.2: Remove Entity of type RECOMMENDATION when contained by ENTITY of same type"
salience 256
when
$intersection: Containment(
$container: container,
$contained: contained,
($container.entityType == EntityType.ENTITY || $container.entityType == EntityType.HINT),
!$container.removed(),
$contained.entityType == EntityType.RECOMMENDATION,
$container.type() == $contained.type(),
!$contained.hasManualChanges()
)
then
$contained.remove("X.5.2", "remove Entity of type RECOMMENDATION when contained by ENTITY of same type");
end end
@ -1621,33 +1569,22 @@ rule "X.7.0: Remove all images"
rule "X.8.0: Remove Entity when text range and type equals to imported Entity" rule "X.8.0: Remove Entity when text range and type equals to imported Entity"
salience 257 salience 257
when when
$equality: Equality( $entity: TextEntity($type: type(), engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(getTextRange().equals($entity.getTextRange()), this != $entity, type() == $type, engines not contains Engine.IMPORTED)
$b: b,
$a.type() == $b.type(),
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.0", "remove Entity when text range and type equals to imported Entity"); $other.remove("X.8.0", "remove Entity when text range and type equals to imported Entity");
$a.addEngines($b.getEngines()); $entity.addEngines($other.getEngines());
retract($other);
end end
rule "X.8.1: Remove Entity when intersected by imported Entity" rule "X.8.1: Remove Entity when intersected by imported Entity"
salience 256 salience 256
when when
$intersection: Intersection( $entity: TextEntity(engines contains Engine.IMPORTED, active())
$a: a, $other: TextEntity(intersects($entity), this != $entity, engines not contains Engine.IMPORTED)
$b: b,
$a.engines contains Engine.IMPORTED,
$a.active(),
$b.engines not contains Engine.IMPORTED,
$a != $b
)
then then
$b.remove("X.8.1", "remove Entity when intersected by imported Entity"); $other.remove("X.8.1", "remove Entity when intersected by imported Entity");
retract($other);
end end
// Rule unit: X.9 // Rule unit: X.9
@ -1674,16 +1611,11 @@ rule "X.10.0: remove false positives of ai"
rule "X.11.0: Remove dictionary entity which intersects with a manual entity" rule "X.11.0: Remove dictionary entity which intersects with a manual entity"
salience 64 salience 64
when when
$intersection: Intersection( $manualEntity: TextEntity(engines contains Engine.MANUAL, active())
$a: a, $dictionaryEntity: TextEntity(intersects($manualEntity), dictionaryEntry, engines not contains Engine.MANUAL)
$b: b,
$a.engines contains Engine.MANUAL,
$a.active(),
$b.dictionaryEntry,
$b.engines not contains Engine.MANUAL
)
then then
$b.remove("X.11.0", "remove dictionary entity which intersects with a manual entity"); $dictionaryEntity.remove("X.11.0", "remove dictionary entity which intersects with a manual entity");
retract($dictionaryEntity);
end end
@ -1696,6 +1628,7 @@ rule "DICT.0.0: Remove Template Dictionary Entity when contained by Dossier Dict
$dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY) $dictionaryRemoval: TextEntity($type: type(), entityType == EntityType.DICTIONARY_REMOVAL, engines contains Engine.DOSSIER_DICTIONARY)
$entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges()) $entity: TextEntity(getTextRange().equals($dictionaryRemoval.getTextRange()), engines contains Engine.DICTIONARY, type() == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges())
then then
$entity.getIntersectingNodes().forEach(node -> update(node));
$entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL"); $entity.remove("DICT.0.0", "Remove Template Dictionary Entity when contained by Dossier Dictionary DICTIONARY_REMOVAL");
$entity.addEngine(Engine.DOSSIER_DICTIONARY); $entity.addEngine(Engine.DOSSIER_DICTIONARY);
end end

View File

@ -0,0 +1,240 @@
id,old rule names,old rule code,translates to
0,"""0: Add CBI_author from ai 5""","""\n when\n Section(aiMatchesType(\""CARDINAL\""))\n then\n section.addAiEntities(\""CARDINAL\"", \""cardinal\"");\n end""",[AI.2.0]
1,"""0: Add CBI_author from ai 3""","""\n when\n Section(aiMatchesType(\""POSTAL\""))\n then\n section.addAiEntities(\""POSTAL\"", \""postal\"");\n end""",[AI.2.0]
2,"""0: Add CBI_author from ai 6""","""\n when\n Section(aiMatchesType(\""CITY\""))\n then\n section.addAiEntities(\""CITY\"", \""city\"");\n end""",[AI.2.0]
3,"""0: Add CBI_author from ai 7""","""\n when\n Section(aiMatchesType(\""STATE\""))\n then\n section.addAiEntities(\""STATE\"", \""state\"");\n end""",[AI.2.0]
4,"""0: Add CBI_author from ai 2""","""\n when\n Section(aiMatchesType(\""STREET\""))\n then\n section.addAiEntities(\""STREET\"", \""street\"");\n end""",[AI.2.0]
5,"""0: Recommend CTL/BL laboratory that start with BL or CTL""","""\n when\n Section(searchText.contains(\""CT\"") || searchText.contains(\""BL\""))\n then\n /* Regular expression: ((\\b((([Cc]T(([1ILli\\/])| L|~P))|(BL))[\\. ]?([\\dA-Ziltphz~\\/.:!]| ?[\\(',][Ppi](\\(e)?|([\\(-?']\\/))+( ?[\\(\\/\\dA-Znasieg]+)?)\\b( ?\\/? ?\\d+)?)|(\\bCT[L1i]\\b)) */\n section.addRecommendationByRegEx(\""((\\\\b((([Cc]T(([1ILli\\\\/])| L|~P))|(BL))[\\\\. ]?([\\\\dA-Ziltphz~\\\\/.:!]| ?[\\\\(',][Ppi](\\\\(e)?|([\\\\(-?']\\\\/))+( ?[\\\\(\\\\/\\\\dA-Znasieg]+)?)\\\\b( ?\\\\/? ?\\\\d+)?)|(\\\\bCT[L1i]\\\\b))\"", true, 0, \""CBI_address\"");\n end""",[SYN.1.0]
6,"""0: Add CBI_author from ai 4""","""\n when\n Section(aiMatchesType(\""COUNTRY\""))\n then\n section.addAiEntities(\""COUNTRY\"", \""country\"");\n end""",[AI.2.0]
7,"""0: Combine address parts from ai to CBI_address (org is mandatory)""","""\n when\n Section(aiMatchesType(\""ORG\""))\n then\n section.combineAiTypes(\""ORG\"", \""STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\"", 20, \""CBI_address\"", 3, false);\n end""",[AI.1.0]
8,"""0: Combine address parts from ai to CBI_address (city is mandatory)""","""\n when\n Section(aiMatchesType(\""CITY\""))\n then\n section.combineAiTypes(\""CITY\"", \""ORG,STREET,POSTAL,COUNTRY,CARDINAL,STATE\"", 20, \""CBI_address\"", 3, false);\n end""",[AI.1.0]
9,"""0: Combine address parts from ai to CBI_address (street is mandatory)""","""\n when\n Section(aiMatchesType(\""STREET\""))\n then\n section.combineAiTypes(\""STREET\"", \""ORG,POSTAL,COUNTRY,CARDINAL,CITY,STATE\"", 20, \""CBI_address\"", 3, false);\n end""",[AI.1.0]
10,"""0: Add CBI_author from ai, 1: Add CBI_author from ai""","""\n when\n Section(aiMatchesType(\""CBI_author\""))\n then\n section.addAiEntities(\""CBI_author\"", \""CBI_author\"");\n end""",[AI.0.0]
11,"""0: Add CBI_author from ai 8""","""\n when\n Section(aiMatchesType(\""ORG\""))\n then\n section.addAiEntities(\""ORG\"", \""org\"");\n end""",[AI.2.0]
12,"""1: Redacted because Section contains Vertebrate""","""\n when\n Section(matchesType(\""vertebrate\""))\n then\n section.redact(\""CBI_author\"", 1, \""Vertebrate found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 1, \""Vertebrate found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[CBI.3.0]
13,"""5: Do not redact genitive CBI_author, 7: Do not redact genitive CBI_author, 1: Do not redact genitive CBI_author""","""\n when\n Section(matchesType(\""CBI_author\""))\n then\n section.expandToFalsePositiveByRegEx(\""CBI_author\"", \""[''ʼˈ´`ʻ']s\"", false, 0);\n end""",[CBI.2.0]
14,"""1: Redact CBI Authors (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_author\""))\n then\n section.redact(\""CBI_author\"", 1, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[CBI.0.0]
15,"""2: Combine ai types CBI_author from ai""","""\n when\n Section(aiMatchesType(\""ORG\""))\n then\n section.combineAiTypes(\""ORG\"", \""STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\"", 20, \""CBI_address\"", 2, false);\n end""",[AI.1.0]
16,"""2: Not Redacted because Section contains no Vertebrate""","""\n when\n Section(!matchesType(\""vertebrate\""))\n then\n section.redactNot(\""CBI_author\"", 2, \""No Vertebrate found\"");\n section.redactNot(\""CBI_address\"", 2, \""No Vertebrate found\"");\n end""",[CBI.3.0]
17,"""2: Redact CBI Authors (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_author\""))\n then\n section.redact(\""CBI_author\"", 2, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.0.0]
18,"""2: Redacted because Section contains Vertebrate""","""\n when\n Section(matchesType(\""vertebrate\""))\n then\n section.redact(\""CBI_author\"", 2, \""Vertebrate found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 2, \""Vertebrate found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[CBI.3.0]
19,"""3: Redact CBI Authors (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_author\""))\n then\n section.redact(\""CBI_author\"", 3, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[CBI.0.0]
20,"""3: Not Redacted because Section contains no Vertebrate""","""\n when\n Section(!matchesType(\""vertebrate\""))\n then\n section.redactNot(\""CBI_author\"", 3, \""No Vertebrate found\"");\n section.redactNot(\""CBI_address\"", 3, \""No Vertebrate found\"");\n end""",[CBI.3.2]
21,"""3: Do not redact Names and Addresses if no redaction Indicator is contained""","""\n when\n Section(matchesType(\""vertebrate\""), matchesType(\""no_redaction_indicator\""))\n then\n section.redactNot(\""CBI_author\"", 3, \""Vertebrate and No Redaction Indicator found\"");\n section.redactNot(\""CBI_address\"", 3, \""Vertebrate and No Redaction Indicator found\"");\n end""",[CBI.4.0]
22,"""3: Redact not CBI Address (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_address\""))\n then\n section.redactNot(\""CBI_address\"", 3, \""Address found for non vertebrate study\"");\n end""",[CBI.1.0]
23,"""3: Redact not CBI Address (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_address\""))\n then\n section.redactNot(\""CBI_address\"", 3, \""Address found for non vertebrate study\"");\n section.ignoreRecommendations(\""CBI_address\"");\n end""",[CBI.1.0]
24,"""4: Redact Names and Addresses if no_redaction_indicator and redaction_indicator is contained""","""\n when\n Section(matchesType(\""vertebrate\""), matchesType(\""no_redaction_indicator\""), matchesType(\""redaction_indicator\""))\n then\n section.redact(\""CBI_author\"", 4, \""Vertebrate and Redaction Indicator found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 4, \""Vertebrate and Redaction Indicator found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[CBI.5.0]
25,"""4: Redact CBI Address (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_address\""))\n then\n section.redact(\""CBI_address\"", 4, \""Address found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.1.1]
26,"""4: Redact CBI Authors (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_author\""))\n then\n section.redact(\""CBI_author\"", 4, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.0.0]
27,"""4: Do not redact Names and Addresses if no redaction Indicator is contained""","""\n when\n Section(matchesType(\""vertebrate\""), matchesType(\""no_redaction_indicator\""))\n then\n section.redactNot(\""CBI_author\"", 4, \""Vertebrate and No Redaction Indicator found\"");\n section.redactNot(\""CBI_address\"", 4, \""Vertebrate and No Redaction Indicator found\"");\n end""",[CBI.4.0]
28,"""5: Do not redact Names and Addresses if no redaction Indicator is contained""","""\n when\n Section(matchesType(\""vertebrate\""), matchesType(\""published_information\""))\n then\n section.redactNotAndReference(\""CBI_author\"",\""published_information\"", 5, \""Published Information found\"");\n section.redactNotAndReference(\""CBI_address\"",\""published_information\"", 5, \""Published Information found\"");\n end""",[CBI.4.0]
29,"""5: Redact Names and Addresses if no_redaction_indicator and redaction_indicator is contained""","""\n when\n Section(matchesType(\""vertebrate\""), matchesType(\""no_redaction_indicator\""), matchesType(\""redaction_indicator\""))\n then\n section.redact(\""CBI_author\"", 5, \""Vertebrate and Redaction Indicator found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 5, \""Vertebrate and Redaction Indicator found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[CBI.5.0]
30,"""5: Redact not CBI Address (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_address\""))\n then\n section.redactNot(\""CBI_address\"", 5, \""Address found for non vertebrate study\"");\n section.ignoreRecommendations(\""CBI_address\"");\n end""",[CBI.1.0]
31,"""6: Do not redact Names and Addresses if no redaction Indicator is contained""","""\n when\n Section(matchesType(\""vertebrate\""), matchesType(\""published_information\""))\n then\n section.redactNotAndReference(\""CBI_author\"",\""published_information\"", 6, \""Published Information found\"");\n section.redactNotAndReference(\""CBI_address\"",\""published_information\"", 6, \""Published Information found\"");\n end""",[CBI.4.0]
32,"""6: Redact CBI Address (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""CBI_address\""))\n then\n section.redact(\""CBI_address\"", 6, \""Address found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.1.1]
33,"""6: Redact Author(s) cells in Tables with Author(s) header (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && hasTableHeader(\""Author(s)\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author(s)\"", 6, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.11.0]"
34,"""6: Not redacted because Vertebrate Study = N""","""\n when\n Section(rowEquals(\""Vertebrate study Y/N\"", \""N\"") || rowEquals(\""Vertebrate study Y/N\"", \""No\""))\n then\n section.redactNotCell(\""Author(s)\"", 6, \""CBI_author\"", true, \""Not redacted because row is not a vertebrate study\"");\n section.redactNot(\""CBI_author\"", 6, \""Not redacted because row is not a vertebrate study\"");\n section.redactNot(\""CBI_address\"", 6, \""Not redacted because row is not a vertebrate study\"");\n section.highlightCell(\""Vertebrate study Y/N\"", 6, \""hint_only\"");\n end""",[CBI.12.0]
35,"""7: Redact Author(s) cells in Tables with Author(s) header (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && hasTableHeader(\""Author(s)\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author(s)\"", 7, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.10.0, CBI.11.0]"
36,"""7: Not redacted because Vertebrate Study = N""","""\n when\n Section(rowEquals(\""Vertebrate study Y/N\"", \""N\"") || rowEquals(\""Vertebrate study Y/N\"", \""No\""))\n then\n section.redactNotCell(\""Author(s)\"", 7, \""CBI_author\"", true, \""Not redacted because row is not a vertebrate study\"");\n section.redactNot(\""CBI_author\"", 7, \""Not redacted because row is not a vertebrate study\"");\n section.redactNot(\""CBI_address\"", 7, \""Not redacted because row is not a vertebrate study\"");\n section.highlightCell(\""Vertebrate study Y/N\"", 7, \""hint_only\"");\n end""",[CBI.12.0]
37,"""7: Redact if must redact entry is found""","""\n when\n Section(matchesType(\""must_redact\""))\n then\n section.redact(\""CBI_author\"", 7, \""must_redact entry was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 7, \""must_redact entry was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[CBI.8.0]
38,"""8: Redact Authors and Addresses in Reference Table if it is a Vertebrate study""","""\n when\n Section(rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\""))\n then\n section.redactCell(\""Author(s)\"", 8, \""CBI_author\"", true, \""Redacted because row is a vertebrate study\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 8, \""Redacted because row is a vertebrate study\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.highlightCell(\""Vertebrate study Y/N\"", 8, \""must_redact\"");\n end""",[CBI.12.0]
39,"""8: Redact if must redact entry is found""","""\n when\n Section(matchesType(\""must_redact\""))\n then\n section.redact(\""CBI_author\"", 8, \""Specification of impurity of the active substance was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2b)\"");\n section.redact(\""CBI_address\"", 8, \""Specification of impurity of the active substance was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2b)\"");\n end""",[CBI.8.0]
40,"""8: Redact Author cells in Tables with Author header (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && hasTableHeader(\""Author\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author\"", 8, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.11.0]"
41,"""8: Redact Author(s) cells in Tables with Author(s) header (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && hasTableHeader(\""Author(s)\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author(s)\"", 8, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.11.0]"
42,"""9: Redact Author(s) cells in Tables with Author(s) header (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && hasTableHeader(\""Author(s)\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author(s)\"", 9, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.10.0, CBI.11.0]"
43,"""9: Redact Author cells in Tables with Author header (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && hasTableHeader(\""Author\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author\"", 9, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.10.0, CBI.11.0]"
44,"""9: Redact sponsor company""","""\n when\n Section(searchText.toLowerCase().contains(\""batches produced at\""))\n then\n section.redactIfPrecededBy(\""batches produced at\"", \""CBI_sponsor\"", 9, \""Redacted because it represents a sponsor company\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.addHintAnnotation(\""batches produced at\"", \""must_redact\"");\n end""",[CBI.14.0]
45,"""9: Redact Authors and Addresses in Reference Table if it is a Vertebrate study""","""\n when\n Section(rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\""))\n then\n section.redactCell(\""Author(s)\"", 9, \""CBI_author\"", true, \""Redacted because row is a vertebrate study\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 9, \""Redacted because row is a vertebrate study\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.highlightCell(\""Vertebrate study Y/N\"", 9, \""must_redact\"");\n end""",[CBI.12.0]
46,"""10: Redact and recommand Authors in Tables with Vertebrate study Y/N header (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\"") || rowEquals(\""Vertebrate study Y/N\"", \""N\"") || rowEquals(\""Vertebrate study Y/N\"", \""No\"")))\n then\n section.redactCell(\""Author(s)\"", 10, \""CBI_author\"", true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.11.0]"
47,"""10: Redact determination of residues""","""\n when\n Section((\n searchText.toLowerCase.contains(\""determination of residues\"") ||\n searchText.toLowerCase.contains(\""determination of total residues\"")\n ) && (\n searchText.toLowerCase.contains(\""livestock\"") ||\n searchText.toLowerCase.contains(\""live stock\"") ||\n searchText.toLowerCase.contains(\""tissue\"") ||\n searchText.toLowerCase.contains(\""tissues\"") ||\n searchText.toLowerCase.contains(\""liver\"") ||\n searchText.toLowerCase.contains(\""muscle\"") ||\n searchText.toLowerCase.contains(\""bovine\"") ||\n searchText.toLowerCase.contains(\""ruminant\"") ||\n searchText.toLowerCase.contains(\""ruminants\"")\n ))\n then\n section.redact(\""CBI_author\"", 10, \""Determination of residues was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 10, \""Determination of residues was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.addHintAnnotation(\""determination of residues\"", \""must_redact\"");\n section.addHintAnnotation(\""determination of total residues\"", \""must_redact\"");\n section.addHintAnnotation(\""livestock\"", \""must_redact\"");\n section.addHintAnnotation(\""live stock\"", \""must_redact\"");\n section.addHintAnnotation(\""tissue\"", \""must_redact\"");\n section.addHintAnnotation(\""tissues\"", \""must_redact\"");\n section.addHintAnnotation(\""liver\"", \""must_redact\"");\n section.addHintAnnotation(\""muscle\"", \""must_redact\"");\n section.addHintAnnotation(\""bovine\"", \""must_redact\"");\n section.addHintAnnotation(\""ruminant\"", \""must_redact\"");\n section.addHintAnnotation(\""ruminants\"", \""must_redact\"");\n end""",[CBI.15.0]
48,"""10: Redact Author cells in Tables with Author header (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && hasTableHeader(\""Author\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author\"", 10, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.11.0]"
49,"""10: Redact sponsor company""","""\n when\n Section(searchText.toLowerCase().contains(\""batches produced at\""))\n then\n section.redactIfPrecededBy(\""batches produced at\"", \""CBI_sponsor\"", 10, \""Redacted because it represents a sponsor company\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.addHintAnnotation(\""batches produced at\"", \""must_redact\"");\n end""",[CBI.14.0]
50,"""11: Redact and recommand Authors in Tables with Vertebrate study Y/N header (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\"") || rowEquals(\""Vertebrate study Y/N\"", \""N\"") || rowEquals(\""Vertebrate study Y/N\"", \""No\"")))\n then\n section.redactCell(\""Author(s)\"", 11, \""CBI_author\"", true, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.10.0, CBI.11.0]"
51,"""11: Redact if CTL/* or BL/* was found""","""\n when\n Section(searchText.contains(\""CTL/\"") || searchText.contains(\""BL/\""))\n then\n section.redact(\""CBI_author\"", 11, \""Laboraty for vertebrate studies found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 11, \""Laboraty for vertebrate studies found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.addHintAnnotation(\""CTL\"", \""must_redact\"");\n section.addHintAnnotation(\""BL\"", \""must_redact\"");\n end""",[SYN.0.0]
52,"""11: Redact determination of residues""","""\n when\n Section((\n searchText.toLowerCase.contains(\""determination of residues\"") ||\n searchText.toLowerCase.contains(\""determination of total residues\"")\n ) && (\n searchText.toLowerCase.contains(\""livestock\"") ||\n searchText.toLowerCase.contains(\""live stock\"") ||\n searchText.toLowerCase.contains(\""tissue\"") ||\n searchText.toLowerCase.contains(\""tissues\"") ||\n searchText.toLowerCase.contains(\""liver\"") ||\n searchText.toLowerCase.contains(\""muscle\"") ||\n searchText.toLowerCase.contains(\""bovine\"") ||\n searchText.toLowerCase.contains(\""ruminant\"") ||\n searchText.toLowerCase.contains(\""ruminants\"")\n ))\n then\n section.redact(\""CBI_author\"", 11, \""Determination of residues was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 11, \""Determination of residues was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.addHintAnnotation(\""determination of residues\"", \""must_redact\"");\n section.addHintAnnotation(\""determination of total residues\"", \""must_redact\"");\n section.addHintAnnotation(\""livestock\"", \""must_redact\"");\n section.addHintAnnotation(\""live stock\"", \""must_redact\"");\n section.addHintAnnotation(\""tissue\"", \""must_redact\"");\n section.addHintAnnotation(\""tissues\"", \""must_redact\"");\n section.addHintAnnotation(\""liver\"", \""must_redact\"");\n section.addHintAnnotation(\""muscle\"", \""must_redact\"");\n section.addHintAnnotation(\""bovine\"", \""must_redact\"");\n section.addHintAnnotation(\""ruminant\"", \""must_redact\"");\n section.addHintAnnotation(\""ruminants\"", \""must_redact\"");\n end""",[CBI.15.0]
53,"""11: Redact Author cells in Tables with Author header (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && hasTableHeader(\""Author\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author\"", 11, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.10.0, CBI.11.0]"
54,"""12: Redact and add recommendation for et al. author""","""\n when\n Section(searchText.contains(\""et al\""))\n then\n\t\tsection.redactAndRecommendByRegEx(\""\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\"", false, 1, \""CBI_author\"", 12, \""Author found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[CBI.16.0]
55,"""12: Redact and recommand Authors in Tables with Vertebrate study Y/N header (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\"") || rowEquals(\""Vertebrate study Y/N\"", \""N\"") || rowEquals(\""Vertebrate study Y/N\"", \""No\"")))\n then\n section.redactCell(\""Author(s)\"", 12, \""CBI_author\"", true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.11.0]"
56,"""12: Recommend CTL/BL laboratory""","""\n when\n Section(searchText.contains(\""CT\"") || searchText.contains(\""BL\""))\n then\n section.addRecommendationByRegEx(\""((\\\\b((([Cc]T(([1ILli\\\\/])| L|~P))|(BL))[\\\\. ]?([\\\\dA-Ziltphz~\\\\/.:!]| ?[\\\\(',][Ppi](\\\\(e)?|([\\\\(-?']\\\\/))+( ?[\\\\(\\\\/\\\\dA-Znasieg]+)?)\\\\b( ?\\\\/? ?\\\\d+)?)|(\\\\bCT[L1i]\\\\b))\"", true, 0, \""CBI_address\"");\n end""",[SYN.1.0]
57,"""12: Redact if CTL/* or BL/* was found (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (searchText.contains(\""CTL/\"") || searchText.contains(\""BL/\"")))\n then\n section.addHintAnnotation(\""CTL\"", \""hint_only\"");\n section.addHintAnnotation(\""BL\"", \""hint_only\"");\n end""",[SYN.0.0]
58,"""13: Redact and recommand Authors in Tables with Vertebrate study Y/N header (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\"") || rowEquals(\""Vertebrate study Y/N\"", \""N\"") || rowEquals(\""Vertebrate study Y/N\"", \""No\"")))\n then\n section.redactCell(\""Author(s)\"", 13, \""CBI_author\"", true, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.10.0, CBI.11.0]"
59,"""14: Add recommendation for Addresses in Test Organism sections, 13: Add recommendation for Addresses in Test Organism sections""","""\n when\n Section(searchText.contains(\""Species:\"") && searchText.contains(\""Source:\""))\n then\n\t\tsection.recommendLineAfter(\""Source:\"", \""CBI_address\"");\n end""",[CBI.17.0]
60,"""13: Redact and add recommendation for et al. author""","""\n when\n Section(searchText.contains(\""et al\""))\n then\n\t\tsection.redactAndRecommendByRegEx(\""\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\"", false, 1, \""CBI_author\"", 13, \""Author found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[CBI.16.0]
61,"""13: Redact if CTL/* or BL/* was found (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (searchText.contains(\""CTL/\"") || searchText.contains(\""BL/\"")))\n then\n section.addRedaction(\""CTL\"", \""must_redact\"", 13, \""Laboratory for vertebrate studies found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"" );\n section.addRedaction(\""BL\"", \""must_redact\"", 13, \""Laboratory for vertebrate studies found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"" );\n end""",[SYN.0.0]
62,"""15: Add recommendation for Addresses in Test Animals sections, 14: Add recommendation for Addresses in Test Animals sections""","""\n when\n Section(searchText.contains(\""Species\"") && searchText.contains(\""Source\""))\n then\n\t\tsection.recommendLineAfter(\""Source\"", \""CBI_address\"");\n end""",[CBI.17.0]
63,"""14: Redact addresses that start with BL or CTL""","""\n when\n Section(searchText.contains(\""BL\"") || searchText.contains(\""CT\""))\n then\n section.addRecommendationByRegEx(\""((\\\\b((([Cc]T(([1ILli\\\\/])| L|~P))|(BL))[\\\\. ]?([\\\\dA-Ziltphz~\\\\/.:!]| ?[\\\\(',][Ppi](\\\\(e)?|([\\\\(-?']\\\\/))+( ?[\\\\(\\\\/\\\\dA-Znasieg]+)?)\\\\b( ?\\\\/? ?\\\\d+)?)|(\\\\bCT[L1i]\\\\b))\"", true, 0, \""CBI_address\"");\n end""",[SYN.1.0]
64,"""14: Redacted PII Personal Identification Information""","""\n when\n Section(matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 14, \""PII (Personal Identification Information) found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[PII.0.0, PII.0.1]"
65,"""14: Redact and add recommendation for et al. author (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""et al\""))\n then\n section.redactAndRecommendByRegEx(\""\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\"", false, 1, \""CBI_author\"", 14, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[CBI.16.0]
66,"""15: Redact Emails by RegEx""","""\n when\n Section(searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}\\\\b\"", true, 0, \""PII\"", 15, \""PII (Personal Identification Information) found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[PII.1.0, PII.1.1]"
67,"""15: Redact and add recommendation for et al. author (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""et al\""))\n then\n section.redactAndRecommendByRegEx(\""\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\"", false, 1, \""CBI_author\"", 15, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[CBI.16.0]
68,"""15: Redact and add recommendation for et al. author (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""et al\""))\n then\n section.redactAndRecommendByRegEx(\""\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\"", false, 1, \""CBI_author\"", 15, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.16.0]
69,"""16: Redacted PII Personal Identification Information""","""\n when\n Section(matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 16, \""PII (Personal Identification Information) found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[PII.0.0, PII.0.1]"
70,"""16: Redact and add recommendation for et al. author (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""et al\""))\n then\n section.redactAndRecommendByRegEx(\""\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\"", false, 1, \""CBI_author\"", 16, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.16.0]
71,"""16: Redact contact information""","""\n when\n Section(text.contains(\""Contact point:\"")\n || text.contains(\""Phone:\"")\n || text.contains(\""Fax:\"")\n || text.contains(\""Tel.:\"")\n || text.contains(\""Tel:\"")\n || text.contains(\""E-mail:\"")\n || text.contains(\""Email:\"")\n || text.contains(\""e-mail:\"")\n || text.contains(\""E-mail address:\"")\n || text.contains(\""Alternative contact:\"")\n || text.contains(\""Telephone number:\"")\n || text.contains(\""Telephone No:\"")\n || text.contains(\""Fax number:\"")\n || text.contains(\""Telephone:\"")\n || text.contains(\""European contact:\""))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 16, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[PII.4.0, PII.4.1, PII.6.0, PII.6.1]"
72,"""16: Add recommendation for Addresses in Test Organism sections, 17: Add recommendation for Addresses in Test Organism sections""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""Species:\"") && searchText.contains(\""Source:\""))\n then\n section.recommendLineAfter(\""Source:\"", \""CBI_address\"");\n end""",[CBI.17.0]
73,"""17: Redact Emails by RegEx""","""\n when\n Section(searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 17, \""PII (Personal Identification Information) found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[PII.1.0, PII.1.1]"
74,"""17: Add recommendation for Addresses in Test Animals sections, 18: Add recommendation for Addresses in Test Animals sections""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""Species\"") && searchText.contains(\""Source\""))\n then\n section.recommendLineAfter(\""Source\"", \""CBI_address\"");\n end""",[CBI.17.0]
75,"""17: Redact contact information if applicant is found""","""\n when\n Section(headlineContainsWord(\""applicant\"") || text.contains(\""Applicant\"") || headlineContainsWord(\""Primary contact\"") || headlineContainsWord(\""Alternative contact\"") || text.contains(\""Telephone number:\""))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 17, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""",[PII.7.0]
76,"""18: Do not redact Names and Addresses if Published Information found""","""\n when\n Section(matchesType(\""published_information\""))\n then\n section.redactNotAndReference(\""CBI_author\"",\""published_information\"", 18, \""Published Information found\"");\n section.redactNotAndReference(\""CBI_address\"",\""published_information\"", 18, \""Published Information found\"");\n end""",[CBI.7.0]
77,"""18: Redact contact information if Producer is found""","""\n when\n Section(text.toLowerCase().contains(\""producer of the plant protection\"") || text.toLowerCase().contains(\""producer of the active substance\"") || text.contains(\""Manufacturer of the active substance\"") || text.contains(\""Manufacturer:\"") || text.contains(\""Producer or producers of the active substance\""))\n then\n section.redactLineAfter(\""Contact:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 18, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""",[PII.8.0]
78,"""18: Redact contact information""","""\n when\n Section(text.contains(\""Contact point:\"")\n || text.contains(\""Phone:\"")\n || text.contains(\""Fax:\"")\n || text.contains(\""Tel.:\"")\n || text.contains(\""Tel:\"")\n || text.contains(\""E-mail:\"")\n || text.contains(\""Email:\"")\n || text.contains(\""e-mail:\"")\n || text.contains(\""E-mail address:\"")\n || text.contains(\""Alternative contact:\"")\n || text.contains(\""Telephone number:\"")\n || text.contains(\""Telephone No:\"")\n || text.contains(\""Fax number:\"")\n || text.contains(\""Telephone:\"")\n || text.contains(\""Phone No.\"")\n || text.contains(\""European contact:\""))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 18, true, \""Contact information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[PII.4.0, PII.4.1, PII.6.0, PII.6.1]"
79,"""19: Redact contact information if applicant is found""","""\n when\n Section(headlineContainsWord(\""applicant\"") || text.contains(\""Applicant\"") || headlineContainsWord(\""Primary contact\"") || headlineContainsWord(\""Alternative contact\"") || text.contains(\""Telephone number:\""))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 19, true, \""Applicant information was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""",[PII.7.0]
80,"""19: Redact AUTHOR(S)""","""\n when\n Section(searchText.contains(\""AUTHOR(S):\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 19, true, \""AUTHOR(S) was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[PII.9.0, PII.9.1]"
81,"""19: Redacted PII Personal Identification Information (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 19, \""PII (Personal Identification Information) found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.0.0]
82,"""19: Redacted PII Personal Identification Information (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 19, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.0.0]
83,"""19: Do not redact Names and Addresses if Published Information found""","""\n when\n Section(matchesType(\""published_information\""))\n then\n section.redactNotAndReference(\""CBI_author\"",\""published_information\"", 19, \""Published Information found\"");\n section.redactNotAndReference(\""CBI_address\"",\""published_information\"", 19, \""Published Information found\"");\n end""",[CBI.7.0]
84,"""20: Redact contact information if Producer is found""","""\n when\n Section(text.toLowerCase().contains(\""producer of the plant protection\"") || text.toLowerCase().contains(\""producer of the active substance\"") || text.contains(\""Manufacturer of the active substance\"") || text.contains(\""Manufacturer:\"") || text.contains(\""Producer or producers of the active substance\""))\n then\n section.redactLineAfter(\""Contact:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 20, true, \""Producer was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""",[PII.8.0]
85,"""20: Redact PERFORMING LABORATORY""","""\n when\n Section(searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""PII\"", 20, true, \""PERFORMING LABORATORY was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[CBI.20.0, CBI.20.1]"
86,"""20: Redacted PII Personal Identification Information (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 20, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.0.1]
87,"""20: Redacted PII Personal Identification Information (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 20, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.0.1]
88,"""21: Redact Emails by RegEx (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}\\\\b\"", true, 0, \""PII\"", 21, \""PII (Personal Identification Information) found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.0]
89,"""21: Redact Emails by RegEx (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 21, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.0]
90,"""21: Redact On behalf of Sequani Ltd.:""","""\n when\n Section(searchText.contains(\""On behalf of Sequani Ltd.: Name Title\""))\n then\n section.redactBetween(\""On behalf of Sequani Ltd.: Name Title\"", \""On behalf of\"", \""PII\"", 21, false , \""PII (Personal Identification Information) found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""",[PII.11.0]
91,"""21: Redact AUTHOR(S)""","""\n when\n Section(searchText.contains(\""AUTHOR(S):\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 21, true, \""AUTHOR(S) was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[PII.9.0, PII.9.1]"
92,"""22: Redact Emails by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}\\\\b\"", true, 0, \""PII\"", 22, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.1]
93,"""22: Redact Emails by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 22, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.1]
94,"""22: Redact PERFORMING LABORATORY""","""\n when\n Section(searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""PII\"", 22, true, \""PERFORMING LABORATORY was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""","[CBI.20.0, CBI.20.1]"
95,"""22: Redact On behalf of Syngenta Ltd.:""","""\n when\n Section(searchText.contains(\""On behalf of Syngenta Ltd.: Name Title\""))\n then\n section.redactBetween(\""On behalf of Syngenta Ltd.: Name Title\"", \""Study dates\"", \""PII\"", 22, false , \""PII (Personal Identification Information) found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""",[PII.11.0]
96,"""23: Redact contact information (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (text.contains(\""Contact point:\"")\n || text.contains(\""Phone:\"")\n || text.contains(\""Fax:\"")\n || text.contains(\""Tel.:\"")\n || text.contains(\""Tel:\"")\n || text.contains(\""E-mail:\"")\n || text.contains(\""Email:\"")\n || text.contains(\""e-mail:\"")\n || text.contains(\""E-mail address:\"")\n || text.contains(\""Alternative contact:\"")\n || text.contains(\""Telephone number:\"")\n || text.contains(\""Telephone No:\"")\n || text.contains(\""Fax number:\"")\n || text.contains(\""Telephone:\"")\n || text.contains(\""Phone No.\"")\n || text.contains(\""European contact:\"")))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 23, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[PII.5.0, PII.6.0]"
97,"""23: Redact contact information (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (text.contains(\""Contact point:\"")\n || text.contains(\""Contact:\"")\n || text.contains(\""Alternative contact:\"")\n || (text.contains(\""No:\"") && text.contains(\""Fax\""))\n || (text.contains(\""Contact:\"") && text.contains(\""Tel.:\""))\n || text.contains(\""European contact:\"")\n ))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 23, true, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 23, true, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 23, true, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 23, true, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 23, true, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 23, true, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[PII.4.0, PII.6.0]"
98,"""24: Redact contact information (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (text.contains(\""Contact point:\"")\n || text.contains(\""Contact:\"")\n || text.contains(\""Alternative contact:\"")\n || (text.contains(\""No:\"") && text.contains(\""Fax\""))\n || (text.contains(\""Contact:\"") && text.contains(\""Tel.:\""))\n || text.contains(\""European contact:\"")\n ))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 24, true, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 24, true, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 24, true, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 24, true, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 24, true, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 24, true, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.5.1, PII.6.1]"
99,"""24: Redact contact information (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (text.contains(\""Contact point:\"")\n || text.contains(\""Phone:\"")\n || text.contains(\""Fax:\"")\n || text.contains(\""Tel.:\"")\n || text.contains(\""Tel:\"")\n || text.contains(\""E-mail:\"")\n || text.contains(\""Email:\"")\n || text.contains(\""e-mail:\"")\n || text.contains(\""E-mail address:\"")\n || text.contains(\""Alternative contact:\"")\n || text.contains(\""Telephone number:\"")\n || text.contains(\""Telephone No:\"")\n || text.contains(\""Fax number:\"")\n || text.contains(\""Telephone:\"")\n || text.contains(\""Phone No.\"")\n || text.contains(\""European contact:\"")))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 24, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.4.1, PII.6.1]"
100,"""25: Redact Phone and Fax by RegEx""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (\n text.contains(\""Contact\"")\n || text.contains(\""Telephone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Ph.\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Cell\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n ))\n then\n section.redactByRegEx(\""\\\\b(contact|telephone|phone|fax|tel|ter|cell|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\"", true, 2, \""PII\"", 25, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.0]
101,"""25: Redact Phone and Fax by RegEx (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (\n text.contains(\""Contact\"")\n || text.contains(\""Telephone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n ))\n then\n section.redactByRegEx(\""\\\\b(contact|telephone|phone|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\"", true, 2, \""PII\"", 25, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.0]
102,"""25: Redact contact information if applicant is found (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (headlineContainsWord(\""applicant\"") || text.contains(\""Applicant\"") || headlineContainsWord(\""Primary contact\"") || headlineContainsWord(\""Alternative contact\"") || text.contains(\""Telephone number:\"")))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 25, true, \""Applicant information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.7.0]
103,"""33: Purity Hint, 34: Purity Hint, 50: Purity Hint, 25: Purity Hint, 39: Purity Hint""","""\n when\n Section(searchText.toLowerCase().contains(\""purity\""))\n then\n\t section.addHintAnnotationByRegEx(\""(purity ?( of|\\\\(.{1,20}\\\\))?( ?:)?) .{0,5}[\\\\d\\\\.]+( .{0,4}\\\\.)? ?%\"", true, 1, \""hint_only\"");\n end""",[ETC.0.0]
104,"""25: Redact Purity""","""\n when\n Section(searchText.contains(\""purity\""))\n then\n\t section.redactByRegEx(\""purity ?:? (([\\\\d\\\\.]+)( .{0,4}\\\\.)? ?%)\"", true, 1, \""purity\"", 17, \""Purity found\"", \""Reg (EC) No 1107/2009 Art. 63 (2a)\"");\n end""",[ETC.1.0]
105,"""26: Redact Phone and Fax by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (\n text.contains(\""Contact\"")\n || text.contains(\""Telephone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Ph.\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Cell\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n ))\n then\n section.redactByRegEx(\""\\\\b(contact|telephone|phone|fax|tel|ter|cell|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\"", true, 2, \""PII\"", 26, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.1]
106,"""26: Redact contact information if applicant is found (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (headlineContainsWord(\""applicant\"") || text.contains(\""Applicant\"") || headlineContainsWord(\""Primary contact\"") || headlineContainsWord(\""Alternative contact\"") || text.contains(\""Telephone number:\"")))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 26, true, \""Applicant information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.7.0]
107,"""26: Redact signatures""","""\n when\n Section(matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 26, \""Signature found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[ETC.2.0]
108,"""26: Redact Phone and Fax by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (\n text.contains(\""Contact\"")\n || text.contains(\""Telephone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n ))\n then\n section.redactByRegEx(\""\\\\b(contact|telephone|phone|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\"", true, 2, \""PII\"", 26, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.1]
109,"""27: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""COMPLETION DATE:\"")\n && !searchText.contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
110,"""27: Redact Logos""","""\n when\n Section(matchesImageType(\""logo\""))\n then\n section.redactImage(\""logo\"", 27, \""Logo found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[ETC.3.0]
111,"""27: Redact contact information if Producer is found (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (text.toLowerCase().contains(\""producer of the plant protection\"") || text.toLowerCase().contains(\""producer of the active substance\"") || text.contains(\""Manufacturer of the active substance\"") || text.contains(\""Manufacturer:\"") || text.contains(\""Producer or producers of the active substance\"")))\n then\n section.redactLineAfter(\""Contact:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 27, true, \""Producer was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.8.0]
112,"""28: Redact contact information if Producer is found (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (text.toLowerCase().contains(\""producer of the plant protection\"") || text.toLowerCase().contains(\""producer of the active substance\"") || text.contains(\""Manufacturer of the active substance\"") || text.contains(\""Manufacturer:\"") || text.contains(\""Producer or producers of the active substance\"")))\n then\n section.redactLineAfter(\""Contact:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 28, true, \""Producer was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.8.0]
113,"""28: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""COMPLETION DATE:\"")\n && !searchText.contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 28, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
114,"""28: Redact dossier dictionary match""","""\n when\n Section(matchesType(\""dossier_redaction\""))\n then\n section.redact(\""dossier_redaction\"", 28, \""Specification of impurity found\"", \""Reg (EC) No 1107/2009 Art. 63 (2b)\"");\n end""","[ETC.4.0, ETC.4.1, ETC.4.2]"
115,"""29: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""COMPLETION DATE:\"") && !searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
116,"""34: Ignore dossier_redaction entries if confidentiality is not 'confidential', 51: Ignore dossier_redaction entries if confidential, 29: Ignore dossier_redaction unless confidential, 40: Ignore dossier_redaction entries if confidential""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Confidentiality\"",\""confidential\"") && matchesType(\""dossier_redaction\""));\n then\n section.ignore(\""dossier_redaction\"");\n end""",[ETC.5.0]
117,"""29: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""PII\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
118,"""30: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""PII\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
119,"""30: Redacted PII Personal Identification Information (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 30, \""PII (Personal Identification Information) found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.0.0]
120,"""30: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""COMPLETION DATE:\"") && !searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
121,"""31: Redacted PII Personal Identification Information (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 31, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.0.1]
122,"""31: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""PII\"", 31, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
123,"""31: Redact PERFORMING LABORATORY (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""PERFORMING LABORATORY:\"")\n )\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""CBI_address\"", 31, true, \""PERFORMING LABORATORY was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactNot(\""CBI_address\"", 31, \""Performing laboratory found for non vertebrate study\"");\n end""",[CBI.20.0]
124,"""32: Redact PERFORMING LABORATORY (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""CBI_address\"", 32, true, \""PERFORMING LABORATORY was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.20.1]
125,"""32: Redact Emails by RegEx (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 32, \""PII (Personal Identification Information) found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.0]
126,"""32: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""PII\"", 32, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
127,"""33: Redact Emails by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 33, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.1]
128,"""33: Redact study director abbreviation""","""\n when\n Section((searchText.contains(\""KATH\"") || searchText.contains(\""BECH\"") || searchText.contains(\""KML\"")))\n then\n section.redactWordPartByRegEx(\""((KATH)|(BECH)|(KML)) ?(\\\\d{4})\"", true, 0, 1, \""PII\"", 34, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.10.0]
129,"""33: Redact PERFORMING LABORATORY (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""CBI_address\"", 33, true, \""PERFORMING LABORATORY was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactNot(\""CBI_address\"", 33, \""Performing laboratory found for non vertebrate study\"");\n end""",[CBI.20.0]
130,"""34: Redact PERFORMING LABORATORY (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""CBI_address\"", 34, true, \""PERFORMING LABORATORY was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.20.1]
131,"""34: Redact telephone numbers by RegEx (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && containsRegEx(\""[+]\\\\d{1,}\"", true))\n then\n section.redactByRegEx(\""((([+]\\\\d{1,3} (\\\\d{7,12})\\\\b)|([+]\\\\d{1,3}(\\\\d{3,12})\\\\b|[+]\\\\d{1,3}([ -]\\\\(?\\\\d{1,6}\\\\)?){2,4})|[+]\\\\d{1,3} ?((\\\\d{2,6}\\\\)?)([ -]\\\\d{2,6}){1,4}))(-\\\\d{1,3})?\\\\b)\"", true, 1, \""PII\"", 34, \""PII (Personal Identification Information) found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.3.0]
132,"""35: Redact telephone numbers by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && containsRegEx(\""[+]\\\\d{1,}\"", true))\n then\n section.redactByRegEx(\""((([+]\\\\d{1,3} (\\\\d{7,12})\\\\b)|([+]\\\\d{1,3}(\\\\d{3,12})\\\\b|[+]\\\\d{1,3}([ -]\\\\(?\\\\d{1,6}\\\\)?){2,4})|[+]\\\\d{1,3} ?((\\\\d{2,6}\\\\)?)([ -]\\\\d{2,6}){1,4}))(-\\\\d{1,3})?\\\\b)\"", true, 1, \""PII\"", 35, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.3.1]
133,"""35: Redact signatures (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 35, \""Signature found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[ETC.2.0]
134,"""36: Redact signatures (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 36, \""Signature found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[ETC.2.0]
135,"""37: Redact contact information (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (text.contains(\""Contact point:\"")\n || text.contains(\""Phone:\"")\n || text.contains(\""Fax:\"")\n || text.contains(\""Tel.:\"")\n || text.contains(\""Tel:\"")\n || text.contains(\""E-mail:\"")\n || text.contains(\""Email:\"")\n || text.contains(\""e-mail:\"")\n || text.contains(\""E-mail address:\"")\n || text.contains(\""Alternative contact:\"")\n || text.contains(\""Telephone number:\"")\n || text.contains(\""Telephone No:\"")\n || text.contains(\""Fax number:\"")\n || text.contains(\""Telephone:\"")\n || text.contains(\""Phone No.\"")\n || text.contains(\""European contact:\"")))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 37, true, \""Contact information was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[PII.4.0, PII.6.0]"
136,"""38: Redact contact information (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (text.contains(\""Contact point:\"")\n || text.contains(\""Phone:\"")\n || text.contains(\""Fax:\"")\n || text.contains(\""Tel.:\"")\n || text.contains(\""Tel:\"")\n || text.contains(\""E-mail:\"")\n || text.contains(\""Email:\"")\n || text.contains(\""e-mail:\"")\n || text.contains(\""E-mail address:\"")\n || text.contains(\""Alternative contact:\"")\n || text.contains(\""Telephone number:\"")\n || text.contains(\""Telephone No:\"")\n || text.contains(\""Fax number:\"")\n || text.contains(\""Telephone:\"")\n || text.contains(\""Phone No.\"")\n || text.contains(\""European contact:\"")))\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 38, true, \""Contact information was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.4.1, PII.6.1]"
137,"""39: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""COMPLETION DATE:\"") && !searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 39, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
138,"""40: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""COMPLETION DATE:\"") && !searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 40, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
139,"""41: Redact signatures (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 41, \""Signature found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[ETC.2.0]
140,"""41: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""PII\"", 41, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
141,"""42: Redact signatures (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 42, \""Signature found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[ETC.2.0]
142,"""42: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""PII\"", 42, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
143,"""43: Redact PERFORMING LABORATORY (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""CBI_address\"", 43, true, \""PERFORMING LABORATORY was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactNot(\""CBI_address\"", 43, \""Performing laboratory found for non vertebrate study\"");\n end""",[CBI.20.0]
144,"""43: Redact Logos (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesImageType(\""logo\""))\n then\n section.redactImage(\""logo\"", 43, \""Logo found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[ETC.3.0]
145,"""44: Redact PERFORMING LABORATORY (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""CBI_address\"", 44, true, \""PERFORMING LABORATORY was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.20.1]
146,"""52: Redact signatures (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 52, \""Signature found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[ETC.2.0]
147,"""53: Redact signatures (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 53, \""Signature found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[ETC.2.0]
148,"""54: Redact Logos (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesImageType(\""logo\""))\n then\n section.redactImage(\""logo\"", 54, \""Logo found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[ETC.3.0]
149,"""0: Expand CBI Authors with firstname initials""","""\n when\n Section(matchesType(\""CBI_author\""))\n then\n section.expandByRegEx(\""CBI_author\"", \""(,? [A-Z]\\\\.?( ?[A-Z]\\\\.?)?( ?[A-Z]\\\\.?)?\\\\b\\\\.?)\"", false, 1);\n end""",[CBI.18.0]
150,"""0: Expand CBI Authors with firstname initials""","""\n when\n Section(matchesType(\""CBI_author\""))\n then\n section.expandByRegEx(\""CBI_author\"", \""(,? [A-Z]\\\\.?( ?[A-Z]\\\\.?)?( ?[A-Z]\\\\.?)?\\\\b\\\\.?)\"", false, 1, \""[^\\\\s]+\"");\n end""",[CBI.18.0]
151,"""0: Expand CBI_author and PII matches with salutation prefix""","""\n when\n Section((matchesType(\""CBI_author\"") || matchesType(\""PII\"")) && (\n searchText.contains(\""Mr\"")\n || searchText.contains(\""Mrs\"")\n || searchText.contains(\""Ms\"")\n || searchText.contains(\""Miss\"")\n || searchText.contains(\""Sir\"")\n || searchText.contains(\""Madam\"")\n || searchText.contains(\""Madame\"")\n || searchText.contains(\""Mme\"")\n ))\n then\n section.expandByPrefixRegEx(\""CBI_author\"", \""\\\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\\\s?\\\\.?\\\\s*\"", false, 0);\n section.expandByPrefixRegEx(\""PII\"", \""\\\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\\\s?\\\\.?\\\\s*\"", false, 0);\n end""","[CBI.19.0, PII.12.0]"
152,"""102: Guidelines FileAttributes""","""\n when\n Section((text.contains(\""DATA REQUIREMENT(S):\"") || text.contains(\""TEST GUIDELINE(S):\"")) && (text.contains(\""OECD\"") || text.contains(\""EPA\"") || text.contains(\""OPPTS\"")))\n then\n section.addFileAttribute(\""OECD Number\"", \""OECD (No\\\\.? )?\\\\d{3}( \\\\(\\\\d{4}\\\\))?\"", false, 0);\n end""",[ETC.7.0]
153,"""28: Redact Logos""","""\n when\n Section(matchesImageType(\""logo\""))\n then\n section.redactImage(\""logo\"", 28, \""Logo found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[ETC.3.0]
154,"""8: Redact Author cells in Tables with Author header (Non vertebrate study)""","""\n when\n Section(hasTableHeader(\""h5.1\""))\n then\n section.redactCell(\""h5.1\"", 8, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.11.0]"
155,"""30: Ignore dossier_redactions if confidential""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Confidentiality\"",\""confidential\"") && matchesType(\""dossier_redactions\""));\n then\n section.ignore(\""dossier_redactions\"");\n end""",[ETC.5.0]
156,"""27: Redact formula""","""\n when\n Section(matchesImageType(\""formula\""))\n then\n section.redactImage(\""formula\"", 27, \""Formula found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[ETC.8.0]
157,"""5: Do not redact Names and Addresses if no redaction Indicator is contained""","""\n when\n Section(matchesType(\""vertebrate\""), matchesType(\""published_information\""))\n then\n section.redactNotAndReference(\""CBI_author\"",\""published_information\"", 5, \""Vertebrate and Published Information found\"");\n section.redactNotAndReference(\""CBI_address\"",\""published_information\"", 5, \""Vertebrate and Published Information found\"");\n end""",[CBI.4.0]
158,"""29: Redact Dossier Redactions""","""\n when\n Section(matchesType(\""dossier_redactions\""))\n then\n section.redact(\""dossier_redactions\"", 29, \""Dossier Redaction found\"", \""Article 39(1)(2) of Regulation (EC) No 178/2002\"");\n end""","[ETC.4.0, ETC.4.1, ETC.4.2]"
159,"""10: Redact determination of residues""","""\n when\n Section((\n searchText.toLowerCase.contains(\""determination of residues\"") ||\n searchText.toLowerCase.contains(\""determination of total residues\"")\n ) && (\n searchText.toLowerCase.contains(\""livestock\"") ||\n searchText.toLowerCase.contains(\""live stock\"") ||\n searchText.toLowerCase.contains(\""tissue\"") ||\n searchText.toLowerCase.contains(\""tissues\"") ||\n searchText.toLowerCase.contains(\""liver\"") ||\n searchText.toLowerCase.contains(\""muscle\"") ||\n searchText.toLowerCase.contains(\""bovine\"") ||\n searchText.toLowerCase.contains(\""ruminant\"") ||\n searchText.toLowerCase.contains(\""ruminants\"")\n ))\n then\n section.redact(\""CBI_author\"", 10, \""Determination of residues was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redact(\""CBI_address\"", 10, \""Determination of residues was found.\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.addHintAnnotation(\""determination of residues\"", \""must_redact\"");\n section.addHintAnnotation(\""livestock\"", \""must_redact\"");\n section.addHintAnnotation(\""live stock\"", \""must_redact\"");\n section.addHintAnnotation(\""tissue\"", \""must_redact\"");\n section.addHintAnnotation(\""tissues\"", \""must_redact\"");\n section.addHintAnnotation(\""liver\"", \""must_redact\"");\n section.addHintAnnotation(\""muscle\"", \""must_redact\"");\n section.addHintAnnotation(\""bovine\"", \""must_redact\"");\n section.addHintAnnotation(\""ruminant\"", \""must_redact\"");\n section.addHintAnnotation(\""ruminants\"", \""must_redact\"");\n end""",[CBI.15.0]
160,"""19: Redact AUTHOR(S)""","""\n when\n Section(searchText.contains(\""AUTHOR(S):\"") && fileAttributeByPlaceholderEquals(\""{fileattributes.vertebrateStudy}\"", \""true\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 19, true, \""AUTHOR(S) was found\"", \""Reg (EC) No 1107/2009 Art. 63 (2e)\"");\n end""",[PII.9.1]
161,"""101: Redact CAS numbers""","""\n when\n Section(hasTableHeader(\""Sample #\""))\n then\n section.redactCell(\""Sample #\"", 8, \""PII\"", true, \""Redacted because row is a vertebrate study\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[ETC.6.0]
162,"""21: Redact Emails by RegEx""","""\n when\n Section(searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 21, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.1.0, PII.1.1]"
163,"""32: Redact signatures""","""\n when\n Section(matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 32, \""Signature found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[ETC.2.0, ETC.2.1]"
164,"""11: Do not redact Names and Addresses if Published Information found""","""\n when\n Section(matchesType(\""published_information\""))\n then\n section.redactNotAndReference(\""CBI_author\"",\""published_information\"", 11, \""Published Information found\"");\n section.redactNotAndReference(\""CBI_address\"",\""published_information\"", 11, \""Published Information found\"");\n end""","[CBI.7.0, CBI.7.1]"
165,"""9: Add recommendation for Addresses in Test Organism sections""","""\n when\n Section(searchText.contains(\""Species:\"") && searchText.contains(\""Source:\""))\n then\n section.recommendLineAfter(\""Source:\"", \""CBI_address\"");\n end""",[CBI.17.1]
166,"""5: Redact Author cells in Tables with Author header""","""\n when\n Section(hasTableHeader(\""Author\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author\"", 5, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.1, CBI.10.1]"
167,"""21: Redact Phone and Fax by RegEx""","""\n when\n Section(\n text.contains(\""Telephone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Ph.\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Cell\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n )\n then\n section.redactByRegEx(\""\\\\b(telephone|phone|fax|tel|ter|cell|mobile|fel|fer)[:.\\\\s]{0,3}((\\\\(?\\\\+?[0-9])(\\\\(?[0-9\\\\/.\\\\-\\\\s]+\\\\)?)*([0-9]+\\\\)?))\\\\b\"", true, 2, \""PII\"", 23, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.2.0, PII.2.1]"
168,"""2: Redact CBI Address""","""\n when\n Section(matchesType(\""CBI_address\""))\n then\n section.redact(\""CBI_address\"", 4, \""Address found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.1.1]
169,"""25: Redact AUTHOR(S)""","""\n when\n Section(searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""COMPLETION DATE:\"")\n && !searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 25, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.9.0, PII.9.1]"
170,"""6: Redact and recommand Authors in Tables with Vertebrate study Y/N header""","""\n when\n Section(rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\"") || rowEquals(\""Vertebrate study Y/N\"", \""N\"") || rowEquals(\""Vertebrate study Y/N\"", \""No\""))\n then\n section.redactCell(\""Author(s)\"", 6, \""CBI_author\"", true, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.9.1, CBI.10.0, CBI.10.1, CBI.11.0, CBI.12.0, CBI.12.1]"
171,"""22: Redact contact information""","""\n when\n Section(text.contains(\""Contact point:\"")\n || text.contains(\""Phone:\"")\n || text.contains(\""Fax:\"")\n || text.contains(\""Tel.:\"")\n || text.contains(\""Tel:\"")\n || text.contains(\""E-mail:\"")\n || text.contains(\""Email:\"")\n || text.contains(\""e-mail:\"")\n || text.contains(\""E-mail address:\"")\n || text.contains(\""Contact:\"")\n || text.contains(\""Alternative contact:\"")\n || text.contains(\""Telephone number:\"")\n || text.contains(\""Telephone No:\"")\n || text.contains(\""Fax number:\"")\n || text.contains(\""Telephone:\"")\n || text.contains(\""Phone No.\"")\n || (text.contains(\""No:\"") && text.contains(\""Fax\""))\n || (text.contains(\""Contact:\"") && text.contains(\""Tel.:\""))\n || text.contains(\""European contact:\"")\n )\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 22, true, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.4.0, PII.4.1, PII.5.0, PII.5.1, PII.6.0, PII.6.1, PII.7.0, PII.7.1, PII.8.0, PII.8.1]"
172,"""20: Redacted PII Personal Identification Information""","""\n when\n Section(matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 20, \""PII (Personal Identification Information) found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.0.0, PII.0.1]"
173,"""1: Redact CBI Authors""","""\n when\n Section(matchesType(\""CBI_author\""))\n then\n section.redact(\""CBI_author\"", 1, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.0.0, CBI.0.1]"
174,"""4: Redact Author(s) cells in Tables with Author(s) header""","""\n when\n Section(hasTableHeader(\""Author(s)\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author(s)\"", 4, \""CBI_author\"", false, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.9.0, CBI.10.0]"
175,"""8: Redact and add recommendation for et al. author""","""\n when\n Section(searchText.contains(\""et al\""))\n then\n section.redactAndRecommendByRegEx(\""\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\"", false, 1, \""CBI_author\"", 15, \""Author found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.16.0, CBI.16.1]"
176,"""26: Redact AUTHOR(S)""","""\n when\n Section(searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""PII\"", 26, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[PII.9.0, PII.9.1]"
177,"""27: Redact PERFORMING LABORATORY""","""\n when\n Section(searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""CBI_address\"", 27, true, \""PERFORMING LABORATORY was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[CBI.20.0, CBI.20.1]"
178,"""10: Add recommendation for Addresses in Test Animals sections""","""\n when\n Section(searchText.contains(\""Species\"") && searchText.contains(\""Source\""))\n then\n section.recommendLineAfter(\""Source\"", \""CBI_address\"");\n end""","[CBI.17.0, CBI.17.1]"
179,"""33: Redact Logos""","""\n when\n Section(matchesImageType(\""logo\""))\n then\n section.redactImage(\""logo\"", 33, \""Logo found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""","[ETC.3.0, ETC.3.1]"
180,"""26: Redact Phone and Fax by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (\n text.contains(\""Contact\"")\n || text.contains(\""Telephone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Ph.\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n ))\n then\n section.redactByRegEx(\""\\\\b(contact|telephone|phone|ph\\\\.|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\"", true, 2, \""PII\"", 26, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.1]
181,"""25: Redact Phone and Fax by RegEx (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (\n text.contains(\""Contact\"")\n || text.contains(\""Telephone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Ph.\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n ))\n then\n section.redactByRegEx(\""\\\\b(contact|telephone|phone|ph\\\\.|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\"", true, 2, \""PII\"", 25, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.0]
182,"""10: Add recommendation for Addresses in Test Animals sections""","""\n when\n Section(searchText.contains(\""Species\"") && searchText.contains(\""Source\""))\n then\n\t\tsection.recommendLineAfter(\""Source\"", \""PII\"");\n end""","[CBI.17.0, CBI.17.1]"
183,"""0: Combine address parts from AI to addresses as PII (org is mandatory)""","""\n when\n Section(aiMatchesType(\""ORG\""))\n then\n section.combineAiTypes(\""ORG\"", \""STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\"", 20, \""PII\"", 3, false);\n end""",[AI.1.0]
184,"""0: Combine address parts from AI to addresses as PII (street is mandatory)""","""\n when\n Section(aiMatchesType(\""STREET\""))\n then\n section.combineAiTypes(\""STREET\"", \""ORG,POSTAL,COUNTRY,CARDINAL,CITY,STATE\"", 20, \""PII\"", 3, false);\n end""",[AI.1.0]
185,"""18: Redact AUTHOR(S)""","""\n when\n Section(searchText.contains(\""AUTHOR(S):\"") && searchText.contains(\""STUDY COMPLETION DATE:\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""PII\"", 18, true, \""AUTHOR(S) was found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[PII.9.0, PII.9.1]"
186,"""0: Combine address parts from AI to addresses as PII (city is mandatory)""","""\n when\n Section(aiMatchesType(\""CITY\""))\n then\n section.combineAiTypes(\""CITY\"", \""ORG,STREET,POSTAL,COUNTRY,CARDINAL,STATE\"", 20, \""PII\"", 3, false);\n end""",[AI.1.0]
187,"""24: Redact signatures""","""\n when\n Section(matchesImageType(\""signature\""))\n then\n section.redactImage(\""signature\"", 24, \""Signature found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[ETC.2.0, ETC.2.1]"
188,"""13: Redact Emails by RegEx""","""\n when\n Section(searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 13, \""PII (Personal Identification Information) found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[PII.1.0, PII.1.1]"
189,"""4: Redact Author(s) cells in Tables with Author(s) header""","""\n when\n Section(hasTableHeader(\""Author(s)\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author(s)\"", 4, \""PII\"", false, \""Author found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[CBI.9.0, CBI.10.0, CBI.11.0]"
190,"""25: Redact Logos""","""\n when\n Section(matchesImageType(\""logo\""))\n then\n section.redactImage(\""logo\"", 25, \""Logo found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[ETC.3.0, ETC.3.1]"
191,"""11: Do not redact Names and Addresses if Published Information found""","""\n when\n Section(matchesType(\""published_information\""))\n then\n section.redactNot(\""PII\"", 11, \""Published Information found\"");\n section.redactNot(\""PII\"", 11, \""Published Information found\"");\n end""","[CBI.7.0, CBI.7.1, CBI.6.0, CBI.6.1]"
192,"""2: Do not redact genitive PIIs""","""\n when\n Section(matchesType(\""PII\""))\n then\n section.expandToFalsePositiveByRegEx(\""PII\"", \""[''ʼˈ´`ʻ']s\"", false, 0);\n end""",[CBI.2.0]
193,"""5: Redact Author cells in Tables with Author header""","""\n when\n Section(hasTableHeader(\""Author\"") && !hasTableHeader(\""Vertebrate study Y/N\""))\n then\n section.redactCell(\""Author\"", 5, \""PII\"", false, \""Author found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[CBI.9.1, CBI.10.1, CBI.12.0]"
194,"""19: Redact PERFORMING LABORATORY""","""\n when\n Section(searchText.contains(\""PERFORMING LABORATORY:\""))\n then\n section.redactBetween(\""PERFORMING LABORATORY:\"", \""LABORATORY PROJECT ID:\"", \""PII\"", 19, true, \""PERFORMING LABORATORY was found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[CBI.20.0, CBI.20.1]"
195,"""0: Recommend authors from AI as PII""","""\n when\n Section(aiMatchesType(\""CBI_author\""))\n then\n section.addAiEntities(\""CBI_author\"", \""PII\"");\n end""",[AI.3.0]
196,"""9: Add recommendation for Addresses in Test Organism sections""","""\n when\n Section(searchText.contains(\""Species:\"") && searchText.contains(\""Source:\""))\n then\n\t\tsection.recommendLineAfter(\""Source:\"", \""PII\"");\n end""",[CBI.17.1]
197,"""1: Redacted PII Personal Identification Information""","""\n when\n Section(matchesType(\""PII\""))\n then\n section.redact(\""PII\"", 1, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[PII.0.0, PII.0.1]"
198,"""8: Redact and add recommendation for et al. author""","""\n when\n Section(searchText.contains(\""et al\""))\n then\n\t\tsection.redactAndRecommendByRegEx(\""\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\"", false, 1, \""PII\"", 8, \""Author found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[CBI.16.0, CBI.16.1]"
199,"""14: Redact contact information""","""\n when\n Section(text.contains(\""Contact point:\"")\n || text.contains(\""Phone:\"")\n || text.contains(\""Fax:\"")\n || text.contains(\""Tel.:\"")\n || text.contains(\""Tel:\"")\n || text.contains(\""E-mail:\"")\n || text.contains(\""Email:\"")\n || text.contains(\""e-mail:\"")\n || text.contains(\""E-mail address:\"")\n || text.contains(\""Contact:\"")\n || text.contains(\""Alternative contact:\"")\n || text.contains(\""Telephone number:\"")\n || text.contains(\""Telephone No:\"")\n || text.contains(\""Fax number:\"")\n || text.contains(\""Telephone:\"")\n || text.contains(\""Phone No.\"")\n || (text.contains(\""No:\"") && text.contains(\""Fax\""))\n || (text.contains(\""Contact:\"") && text.contains(\""Tel.:\""))\n || text.contains(\""European contact:\"")\n )\n then\n section.redactLineAfter(\""Contact point:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Phone:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Fax:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Tel.:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Tel:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""E-mail:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Email:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""e-mail:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""E-mail address:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Contact:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Alternative contact:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Telephone number:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Telephone No:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Fax number:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Telephone:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""Phone No.\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactBetween(\""No:\"", \""Fax\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactBetween(\""Contact:\"", \""Tel.:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n section.redactLineAfter(\""European contact:\"", \""PII\"", 14, true, \""Personal information found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[PII.4.0, PII.4.1, PII.6.0, PII.6.1]"
200,"""17: Redact AUTHOR(S)""","""\n when\n Section(searchText.contains(\""AUTHOR(S):\""))\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""PII\"", 17, true, \""AUTHOR(S) was found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[PII.9.0, PII.9.1]"
201,"""6: Redact and recommand Authors in Tables with Vertebrate study Y/N header""","""\n when\n Section(rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\"") || rowEquals(\""Vertebrate study Y/N\"", \""N\"") || rowEquals(\""Vertebrate study Y/N\"", \""No\""))\n then\n section.redactCell(\""Author(s)\"", 6, \""PII\"", false, \""Author found\"", \""Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\"");\n end""","[CBI.11.0, CBI.12.2, CBI.12.1]"
202,"""27: Redact Logos""","""\n °when\n Section(matchesImageType(\""logo\""))\n then\n //section.redactImage(\""logo\"", 27, \""Logo found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redactNotImage(\""logo\"", 27, \""No Logos in preGFL documents\"");\n end""","[ETC.3.0, ETC.3.1]"
203,"""27: Redact Logos""","""\n when\n Section(matchesImageType(\""logo\""))\n then\n //section.redactImage(\""logo\"", 27, \""Logo found\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n section.redactNotImage(\""logo\"", 27, \""No Logos in preGFL documents\"");\n end""","[ETC.3.0, ETC.3.1]"
204,"""10a: Redact Addresses in Reference Tables for vertebrate studies in non-vertebrate documents""","""\n when\n Section(hasTableHeader(\""Vertebrate study Y/N\"") && (rowEquals(\""Vertebrate study Y/N\"", \""Y\"") || rowEquals(\""Vertebrate study Y/N\"", \""Yes\"")))\n then\n section.redact(\""CBI_address\"", 10, \""Redacted because row is a vertebrate study\"", \""Reg (EC) No 1107/2009 Art. 63 (2g)\"");\n end""",[CBI.22.0]
205,"""27a: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toUpperCase().contains(\""AUTHOR(S)\"")\n && !searchText.toUpperCase().contains(\""AUTHOR(S):\"")\n && searchText.toUpperCase().contains(\""COMPLETION DATE\"")\n && !searchText.toUpperCase().contains(\""STUDY COMPLETION DATE\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S)\"", \""COMPLETION DATE\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\"", \""Completion Date\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""AUTHOR(S)\\n\"", \""COMPLETION DATE\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\\n\"", \""Completion Date\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
206,"""29b: Redact short Authors section""","""\n when\n Section(\n !fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toLowerCase().contains(\""author(s)\"")\n && searchText.length() < 50\n && sectionNumber <= 20\n && !aiMatchesType(\""CBI_Author\"")\n )\n then\n section.redactByRegEx(\""(?<=author\\\\(?s\\\\)?\\\\s\\\\n?)([\\\\p{Lu}\\\\p{L} ]{5,15}(,|\\\\n)?){1,3}\"",true,0,\""CBI_author\"",29,\""AUTHOR(S) was found\"",\""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[CBI.21.0]
207,"""28a: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toUpperCase().contains(\""AUTHOR(S)\"")\n && !searchText.toUpperCase().contains(\""AUTHOR(S):\"")\n && searchText.toUpperCase().contains(\""COMPLETION DATE\"")\n && !searchText.toUpperCase().contains(\""STUDY COMPLETION DATE\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S)\"", \""COMPLETION DATE\"", \""CBI_author\"", 28, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\"", \""Completion Date\"", \""CBI_author\"", 28, true, \""Author(s) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""AUTHOR(S)\\n\"", \""COMPLETION DATE\"", \""CBI_author\"", 28, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\\n\"", \""Completion Date\"", \""CBI_author\"", 28, true, \""Author(s) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
208,"""65: Redact Skipped Impurities""","""\n when\n Section((fileAttributeByLabelEqualsIgnoreCase(\""Redact Skipped Impurities\"",\""Yes\"") || fileAttributeByLabelEqualsIgnoreCase(\""Redact Skipped Impurities\"",\""Y\"")) && matchesType(\""skipped_impurities\""))\n then\n section.redact(\""skipped_impurities\"", 65, \""Occasional Impurity found\"", \""Article 63(2)(b) of Regulation (EC) No 1107/2009\"");\n end""",[ETC.9.0]
209,"""19c: Recommend first line in table cell with name and address of owner""","""\n when\n Section(searchText.toLowerCase().contains(\""trial site\"") && hasTableHeader(\""Name and Address of Owner / Tenant\""))\n then\n section.redactCell(\""Name and Address of Owner / Tenant\"",19,\""PII\"",true,\""Trial Site owner and address found\"",\""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[ETC.11.0]
210,"""67: Redact Product Composition Information""","""\n when\n Section(matchesType(\""product_composition\""))\n then\n section.redact(\""product_composition\"",67, \""Product Composition Information found\"",\""Article 63(2)(d) of Regulation (EC) No 1107/2009\"");\n end""",[ETC.10.0]
211,"""25: Redact Phone and Fax by RegEx (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (\n text.contains(\""Contact\"")\n || text.contains(\""Telephone\"")\n || text.contains(\""Téléphone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n ))\n then\n section.redactByRegEx(\""\\\\b(contact|t\\\\p{Ll}l\\\\p{Ll}phone|phone|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s\\\\.]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(O][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.O]{4,100}\\\\d)\\\\b\"", true, 2, \""PII\"", 25, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.0]
212,"""19a: recommend title prefixed words as PII""","""\n when\n Section(\n searchText.contains(\""Dr \"")\n || searchText.contains(\""PD Dr \"")\n || searchText.contains(\""Prof. Dr \"")\n || searchText.contains(\""Dr. med. vet \"")\n || searchText.contains(\""Dr. rer. nat \"")\n || searchText.contains(\""PhD \"")\n || searchText.contains(\""BSc \"")\n || searchText.contains(\""(FH) \"")\n || searchText.contains(\""Mr \"")\n || searchText.contains(\""Mrs \"")\n || searchText.contains(\""Ms \"")\n || searchText.contains(\""Miss \"")\n || searchText.contains(\""Dr.\"")\n || searchText.contains(\""PD Dr.\"")\n || searchText.contains(\""Prof. Dr.\"")\n || searchText.contains(\""Dr. med. vet.\"")\n || searchText.contains(\""Dr. rer. nat.\"")\n || searchText.contains(\""PhD.\"")\n || searchText.contains(\""BSc.\"")\n || searchText.contains(\""(FH).\"")\n || searchText.contains(\""Mr.\"")\n || searchText.contains(\""Mrs.\"")\n || searchText.contains(\""Ms.\"")\n || searchText.contains(\""Miss.\"")\n )\n then\n section.addRecommendationByRegEx(\""((Dr|PD Dr|Prof. Dr|Dr. med. vet|Dr. rer. nat|PhD|BSc|\\\\(FH\\\\)|Mr|Mrs|Ms|Miss)[.\\\\s]{1,2})([\\\\p{Lu}][\\\\p{L}\\\\-.]{1,20}\\\\s[\\\\p{Lu}][\\\\p{L}\\\\-.]{1,20})\"", false, 3, \""PII\"");\n end""",[PII.14.0]
213,"""0: Combine address parts from ai to CBI_address (city is mandatory)""","""\n when\n Section(aiMatchesType(\""CITY\""))\n then\n section.combineAiTypes(\""CITY\"", \""ORG,DEPARTMENT,STREET,POSTAL,COUNTRY,CARDINAL,STATE\"", 20, \""CBI_address\"", 3, false);\n end""",[AI.1.0]
214,"""29: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toUpperCase().contains(\""AUTHOR(S):\"")\n && searchText.toUpperCase().contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\"", \""Study Completion Date:\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\"", \""Study completion Date:\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""AUTHOR(S):\\n\"", \""STUDY COMPLETION DATE:\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\\n\"", \""Study Completion Date:\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\\n\"", \""Study completion Date:\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
215,"""22: Redact Emails by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@\\\\s?[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 22, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.1]
216,"""27: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toUpperCase().contains(\""AUTHOR(S):\"")\n && searchText.toUpperCase().contains(\""COMPLETION DATE:\"")\n && !searchText.toUpperCase().contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\"", \""Completion Date:\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""AUTHOR(S):\\n\"", \""COMPLETION DATE:\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\\n\"", \""Completion Date:\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
217,"""21a: Redact typoed Emails with indicator""","""\n when\n Section(searchText.contains(\""@\"") || searchText.toLowerCase().contains(\""mail\""))\n then\n section.redactByRegEx(\""mail[:\\\\.\\\\s]{1,2}([\\\\w\\\\/\\\\-\\\\{\\\\(\\\\. ]{3,20 }(@|a|f)\\\\s?[\\\\w\\\\/\\\\-\\\\{\\\\(\\\\. ]{3,20}(\\\\. \\\\w{2,4}\\\\b|\\\\.\\\\B|\\\\.\\\\w{1,4}\\\\b))\"",true,1,\""PII\"",21,\""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.2]
218,"""0: Combine address parts from ai to CBI_address (department is mandatory)""","""\n when\n Section(aiMatchesType(\""DEPARTMENT\""))\n then\n section.combineAiTypes(\""DEPARTMENT\"", \""ORG,STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\"", 20, \""CBI_address\"", 3, false);\n end""",[AI.1.0]
219,"""0: Combine address parts from ai to CBI_address (street is mandatory)""","""\n when\n Section(aiMatchesType(\""STREET\""))\n then\n section.combineAiTypes(\""STREET\"", \""ORG,DEPARTMENT,POSTAL,COUNTRY,CARDINAL,CITY,STATE\"", 20, \""CBI_address\"", 3, false);\n end""",[AI.1.0]
220,"""19b: Add recommendation for PII after Contact Person""","""\n when\n Section(searchText.toLowerCase().contains(\""contact person:\""))\n then\n section.recommendLineAfter(\""Contact Person\"", \""PII\"");\n section.recommendLineAfter(\""Contact person\"", \""PII\"");\n section.recommendLineAfter(\""contact person\"", \""PII\"");\n section.recommendLineAfter(\""Contact Person:\"", \""PII\"");\n section.recommendLineAfter(\""Contact person:\"", \""PII\"");\n section.recommendLineAfter(\""contact person:\"", \""PII\"");\n end""",[PII.13.0]
221,"""0: Combine address parts from ai to CBI_address (org is mandatory)""","""\n when\n Section(aiMatchesType(\""ORG\""))\n then\n section.combineAiTypes(\""ORG\"", \""DEPARTMENT,STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\"", 20, \""CBI_address\"", 3, false);\n end""",[AI.1.0]
222,"""25a: Redact phone numbers without indicators""","""\n when\n Section(text.contains(\""+\""))\n then\n section.redactByRegEx(\""(\\\\+[\\\\dO]{1,2} )(\\\\([\\\\dO]{1,3}\\\\))?[\\\\d\\\\-O ]{8,15}\"",false,0,\""PII\"",25,\""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.2]
223,"""34: Dossier""","""\n when\n Section(matchesType(\""dossier_redaction\""))\n then\n section.redact(\""dossier_redaction\"", 34, \""Dossier redaction found\"", \""Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)\"");\n end""","[ETC.4.0, ETC.4.1, ETC.4.2]"
224,"""30: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toUpperCase().contains(\""AUTHOR(S):\"")\n && searchText.toUpperCase().contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\"", \""Study Completion Date:\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\"", \""Study completion Date:\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""AUTHOR(S):\\n\"", \""STUDY COMPLETION DATE:\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\\n\"", \""Study Completion Date:\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\\n\"", \""Study completion Date:\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
225,"""30a: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toUpperCase().contains(\""AUTHOR(S)\"")\n && !searchText.toUpperCase().contains(\""AUTHOR(S):\"")\n && searchText.toUpperCase().contains(\""STUDY COMPLETION DATE\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S)\"", \""STUDY COMPLETION DATE\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\"", \""Study Completion Date\"", \""CBI_author\"", 30, true, \""Author(s) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\"", \""Study completion Date\"", \""CBI_author\"", 30, true, \""Author(s) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""AUTHOR(S)\\n\"", \""STUDY COMPLETION DATE\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\\n\"", \""Study Completion Date\"", \""CBI_author\"", 30, true, \""Author(s) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\\n\"", \""Study completion Date\"", \""CBI_author\"", 30, true, \""Author(s) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
226,"""29a: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toUpperCase().contains(\""AUTHOR(S)\"")\n && !searchText.toUpperCase().contains(\""AUTHOR(S):\"")\n && searchText.toUpperCase().contains(\""STUDY COMPLETION DATE\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S)\"", \""STUDY COMPLETION DATE\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\"", \""Study Completion Date\"", \""CBI_author\"", 29, true, \""Author(s) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\"", \""Study completion Date\"", \""CBI_author\"", 29, true, \""Author(s) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""AUTHOR(S)\\n\"", \""STUDY COMPLETION DATE\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\\n\"", \""Study Completion Date\"", \""CBI_author\"", 29, true, \""Author(s) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s)\\n\"", \""Study completion Date\"", \""CBI_author\"", 29, true, \""Author(s) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
227,"""28: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toUpperCase().contains(\""AUTHOR(S):\"")\n && searchText.toUpperCase().contains(\""COMPLETION DATE:\"")\n && !searchText.toUpperCase().contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""CBI_author\"", 28, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\"", \""Completion Date:\"", \""CBI_author\"", 28, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""AUTHOR(S):\\n\"", \""COMPLETION DATE:\"", \""CBI_author\"", 28, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n section.redactLinesBetween(\""Author(s):\\n\"", \""Completion Date:\"", \""CBI_author\"", 28, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
228,"""30b: Redact short Authors section""","""\n when\n Section(\n fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.toLowerCase().contains(\""author(s)\"")\n && searchText.length() < 50\n && sectionNumber <= 20\n && !aiMatchesType(\""CBI_Author\"")\n )\n then\n section.redactByRegEx(\""(?<=author\\\\(?s\\\\)?\\\\s\\\\n?)([\\\\p{Lu}\\\\p{L} ]{5,15}(,|\\\\n)?){1,3}\"",true,0,\""CBI_author\"",30,\""AUTHOR(S) was found\"",\""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[CBI.21.1]
229,"""26: Redact Phone and Fax by RegEx (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && (\n text.contains(\""Contact\"")\n || text.contains(\""Telephone\"")\n || text.contains(\""Téléphone\"")\n || text.contains(\""Phone\"")\n || text.contains(\""Fax\"")\n || text.contains(\""Tel\"")\n || text.contains(\""Ter\"")\n || text.contains(\""Mobile\"")\n || text.contains(\""Fel\"")\n || text.contains(\""Fer\"")\n ))\n then\n section.redactByRegEx(\""\\\\b(contact|t\\\\p{Ll}l\\\\p{Ll}phone|phone|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(O][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.O]{4,100}\\\\d)\\\\b\"", true, 2, \""PII\"", 26, \""Personal information found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.1]
230,"""60: Redact Must Impurities""","""\n when\n Section((fileAttributeByLabelEqualsIgnoreCase(\""Redact Impurities\"",\""Yes\"") || fileAttributeByLabelEqualsIgnoreCase(\""Redact Impurities\"",\""Y\"")) && matchesType(\""impurities\""))\n then\n section.redact(\""impurities\"", 60, \""Impurity found\"", \""Article 63(2)(b) of Regulation (EC) No 1107/2009\"");\n end""",[ETC.9.1]
231,"""21: Redact Emails by RegEx (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && searchText.contains(\""@\""))\n then\n section.redactByRegEx(\""\\\\b([A-Za-z0-9._%+\\\\-]+@\\\\s?[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\"", true, 1, \""PII\"", 21, \""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.2.0]
232,"""21a: Redact typoed Emails with indicator""","""\n when\n Section(searchText.contains(\""@\"") || searchText.toLowerCase().contains(\""mail\""))\n then\n section.redactByRegEx(\""mail[:\\\\.\\\\s]{1,2}([\\\\w\\\\/\\\\-\\\\{\\\\(\\\\. ]{3,20}(@|a|f)\\\\s?[\\\\w\\\\/\\\\-\\\\{\\\\(\\\\. ]{3,20}(\\\\. \\\\w{2,4}\\\\b|\\\\.\\\\B|\\\\.\\\\w{1,4}\\\\b))\"",true,1,\""PII\"",21,\""Personal information found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.1.2]
233,"""29: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""CBI_author\"", 29, true, \""AUTHOR(S) was found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
234,"""28: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""COMPLETION DATE:\"")\n && !searchText.contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""CBI_author\"", 28, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
235,"""30: Redact AUTHOR(S) (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""STUDY COMPLETION DATE:\"", \""CBI_author\"", 30, true, \""AUTHOR(S) was found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.1]
236,"""27: Redact AUTHOR(S) (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"")\n && searchText.contains(\""AUTHOR(S):\"")\n && searchText.contains(\""COMPLETION DATE:\"")\n && !searchText.contains(\""STUDY COMPLETION DATE:\"")\n )\n then\n section.redactLinesBetween(\""AUTHOR(S):\"", \""COMPLETION DATE:\"", \""CBI_author\"", 27, true, \""Author found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[PII.9.0]
237,"""34a: Redact dossier_redaction (Non vertebrate study)""","""\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""dossier_redaction\""))\n then\n section.redact(\""dossier_redaction\"", 34, \""Dossier dictionary entry found\"", \""Article 39(e)(3) of Regulation (EC) No 178/2002\"");\n end""",[ETC.12.0]
238,"""34b: Redact dossier_redaction (Vertebrate study)""","""\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\""Vertebrate Study\"",\""Yes\"") && matchesType(\""dossier_redaction\""))\n then\n section.redact(\""dossier_redaction\"", 34, \""Dossier dictionary entry found\"", \""Article 39(e)(2) of Regulation (EC) No 178/2002\"");\n end""",[ETC.12.1]
1 id old rule names old rule code translates to
1 id old rule names old rule code translates to
2 0 "0: Add CBI_author from ai 5" "\n when\n Section(aiMatchesType(\"CARDINAL\"))\n then\n section.addAiEntities(\"CARDINAL\", \"cardinal\");\n end" [AI.2.0]
3 1 "0: Add CBI_author from ai 3" "\n when\n Section(aiMatchesType(\"POSTAL\"))\n then\n section.addAiEntities(\"POSTAL\", \"postal\");\n end" [AI.2.0]
4 2 "0: Add CBI_author from ai 6" "\n when\n Section(aiMatchesType(\"CITY\"))\n then\n section.addAiEntities(\"CITY\", \"city\");\n end" [AI.2.0]
5 3 "0: Add CBI_author from ai 7" "\n when\n Section(aiMatchesType(\"STATE\"))\n then\n section.addAiEntities(\"STATE\", \"state\");\n end" [AI.2.0]
6 4 "0: Add CBI_author from ai 2" "\n when\n Section(aiMatchesType(\"STREET\"))\n then\n section.addAiEntities(\"STREET\", \"street\");\n end" [AI.2.0]
7 5 "0: Recommend CTL/BL laboratory that start with BL or CTL" "\n when\n Section(searchText.contains(\"CT\") || searchText.contains(\"BL\"))\n then\n /* Regular expression: ((\\b((([Cc]T(([1ILli\\/])| L|~P))|(BL))[\\. ]?([\\dA-Ziltphz~\\/.:!]| ?[\\(',][Ppi](\\(e)?|([\\(-?']\\/))+( ?[\\(\\/\\dA-Znasieg]+)?)\\b( ?\\/? ?\\d+)?)|(\\bCT[L1i]\\b)) */\n section.addRecommendationByRegEx(\"((\\\\b((([Cc]T(([1ILli\\\\/])| L|~P))|(BL))[\\\\. ]?([\\\\dA-Ziltphz~\\\\/.:!]| ?[\\\\(',][Ppi](\\\\(e)?|([\\\\(-?']\\\\/))+( ?[\\\\(\\\\/\\\\dA-Znasieg]+)?)\\\\b( ?\\\\/? ?\\\\d+)?)|(\\\\bCT[L1i]\\\\b))\", true, 0, \"CBI_address\");\n end" [SYN.1.0]
8 6 "0: Add CBI_author from ai 4" "\n when\n Section(aiMatchesType(\"COUNTRY\"))\n then\n section.addAiEntities(\"COUNTRY\", \"country\");\n end" [AI.2.0]
9 7 "0: Combine address parts from ai to CBI_address (org is mandatory)" "\n when\n Section(aiMatchesType(\"ORG\"))\n then\n section.combineAiTypes(\"ORG\", \"STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\", 20, \"CBI_address\", 3, false);\n end" [AI.1.0]
10 8 "0: Combine address parts from ai to CBI_address (city is mandatory)" "\n when\n Section(aiMatchesType(\"CITY\"))\n then\n section.combineAiTypes(\"CITY\", \"ORG,STREET,POSTAL,COUNTRY,CARDINAL,STATE\", 20, \"CBI_address\", 3, false);\n end" [AI.1.0]
11 9 "0: Combine address parts from ai to CBI_address (street is mandatory)" "\n when\n Section(aiMatchesType(\"STREET\"))\n then\n section.combineAiTypes(\"STREET\", \"ORG,POSTAL,COUNTRY,CARDINAL,CITY,STATE\", 20, \"CBI_address\", 3, false);\n end" [AI.1.0]
12 10 "0: Add CBI_author from ai, 1: Add CBI_author from ai" "\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end" [AI.0.0]
13 11 "0: Add CBI_author from ai 8" "\n when\n Section(aiMatchesType(\"ORG\"))\n then\n section.addAiEntities(\"ORG\", \"org\");\n end" [AI.2.0]
14 12 "1: Redacted because Section contains Vertebrate" "\n when\n Section(matchesType(\"vertebrate\"))\n then\n section.redact(\"CBI_author\", 1, \"Vertebrate found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 1, \"Vertebrate found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [CBI.3.0]
15 13 "5: Do not redact genitive CBI_author, 7: Do not redact genitive CBI_author, 1: Do not redact genitive CBI_author" "\n when\n Section(matchesType(\"CBI_author\"))\n then\n section.expandToFalsePositiveByRegEx(\"CBI_author\", \"['’’'ʼˈ´`‘′ʻ’']s\", false, 0);\n end" [CBI.2.0]
16 14 "1: Redact CBI Authors (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_author\"))\n then\n section.redact(\"CBI_author\", 1, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.0.0]
17 15 "2: Combine ai types CBI_author from ai" "\n when\n Section(aiMatchesType(\"ORG\"))\n then\n section.combineAiTypes(\"ORG\", \"STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\", 20, \"CBI_address\", 2, false);\n end" [AI.1.0]
18 16 "2: Not Redacted because Section contains no Vertebrate" "\n when\n Section(!matchesType(\"vertebrate\"))\n then\n section.redactNot(\"CBI_author\", 2, \"No Vertebrate found\");\n section.redactNot(\"CBI_address\", 2, \"No Vertebrate found\");\n end" [CBI.3.0]
19 17 "2: Redact CBI Authors (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_author\"))\n then\n section.redact(\"CBI_author\", 2, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.0.0]
20 18 "2: Redacted because Section contains Vertebrate" "\n when\n Section(matchesType(\"vertebrate\"))\n then\n section.redact(\"CBI_author\", 2, \"Vertebrate found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 2, \"Vertebrate found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [CBI.3.0]
21 19 "3: Redact CBI Authors (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_author\"))\n then\n section.redact(\"CBI_author\", 3, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.0.0]
22 20 "3: Not Redacted because Section contains no Vertebrate" "\n when\n Section(!matchesType(\"vertebrate\"))\n then\n section.redactNot(\"CBI_author\", 3, \"No Vertebrate found\");\n section.redactNot(\"CBI_address\", 3, \"No Vertebrate found\");\n end" [CBI.3.2]
23 21 "3: Do not redact Names and Addresses if no redaction Indicator is contained" "\n when\n Section(matchesType(\"vertebrate\"), matchesType(\"no_redaction_indicator\"))\n then\n section.redactNot(\"CBI_author\", 3, \"Vertebrate and No Redaction Indicator found\");\n section.redactNot(\"CBI_address\", 3, \"Vertebrate and No Redaction Indicator found\");\n end" [CBI.4.0]
24 22 "3: Redact not CBI Address (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_address\"))\n then\n section.redactNot(\"CBI_address\", 3, \"Address found for non vertebrate study\");\n end" [CBI.1.0]
25 23 "3: Redact not CBI Address (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_address\"))\n then\n section.redactNot(\"CBI_address\", 3, \"Address found for non vertebrate study\");\n section.ignoreRecommendations(\"CBI_address\");\n end" [CBI.1.0]
26 24 "4: Redact Names and Addresses if no_redaction_indicator and redaction_indicator is contained" "\n when\n Section(matchesType(\"vertebrate\"), matchesType(\"no_redaction_indicator\"), matchesType(\"redaction_indicator\"))\n then\n section.redact(\"CBI_author\", 4, \"Vertebrate and Redaction Indicator found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 4, \"Vertebrate and Redaction Indicator found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [CBI.5.0]
27 25 "4: Redact CBI Address (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_address\"))\n then\n section.redact(\"CBI_address\", 4, \"Address found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.1.1]
28 26 "4: Redact CBI Authors (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_author\"))\n then\n section.redact(\"CBI_author\", 4, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.0.0]
29 27 "4: Do not redact Names and Addresses if no redaction Indicator is contained" "\n when\n Section(matchesType(\"vertebrate\"), matchesType(\"no_redaction_indicator\"))\n then\n section.redactNot(\"CBI_author\", 4, \"Vertebrate and No Redaction Indicator found\");\n section.redactNot(\"CBI_address\", 4, \"Vertebrate and No Redaction Indicator found\");\n end" [CBI.4.0]
30 28 "5: Do not redact Names and Addresses if no redaction Indicator is contained" "\n when\n Section(matchesType(\"vertebrate\"), matchesType(\"published_information\"))\n then\n section.redactNotAndReference(\"CBI_author\",\"published_information\", 5, \"Published Information found\");\n section.redactNotAndReference(\"CBI_address\",\"published_information\", 5, \"Published Information found\");\n end" [CBI.4.0]
31 29 "5: Redact Names and Addresses if no_redaction_indicator and redaction_indicator is contained" "\n when\n Section(matchesType(\"vertebrate\"), matchesType(\"no_redaction_indicator\"), matchesType(\"redaction_indicator\"))\n then\n section.redact(\"CBI_author\", 5, \"Vertebrate and Redaction Indicator found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 5, \"Vertebrate and Redaction Indicator found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [CBI.5.0]
32 30 "5: Redact not CBI Address (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_address\"))\n then\n section.redactNot(\"CBI_address\", 5, \"Address found for non vertebrate study\");\n section.ignoreRecommendations(\"CBI_address\");\n end" [CBI.1.0]
33 31 "6: Do not redact Names and Addresses if no redaction Indicator is contained" "\n when\n Section(matchesType(\"vertebrate\"), matchesType(\"published_information\"))\n then\n section.redactNotAndReference(\"CBI_author\",\"published_information\", 6, \"Published Information found\");\n section.redactNotAndReference(\"CBI_address\",\"published_information\", 6, \"Published Information found\");\n end" [CBI.4.0]
34 32 "6: Redact CBI Address (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"CBI_address\"))\n then\n section.redact(\"CBI_address\", 6, \"Address found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.1.1]
35 33 "6: Redact Author(s) cells in Tables with Author(s) header (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && hasTableHeader(\"Author(s)\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author(s)\", 6, \"CBI_author\", false, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.11.0]
36 34 "6: Not redacted because Vertebrate Study = N" "\n when\n Section(rowEquals(\"Vertebrate study Y/N\", \"N\") || rowEquals(\"Vertebrate study Y/N\", \"No\"))\n then\n section.redactNotCell(\"Author(s)\", 6, \"CBI_author\", true, \"Not redacted because row is not a vertebrate study\");\n section.redactNot(\"CBI_author\", 6, \"Not redacted because row is not a vertebrate study\");\n section.redactNot(\"CBI_address\", 6, \"Not redacted because row is not a vertebrate study\");\n section.highlightCell(\"Vertebrate study Y/N\", 6, \"hint_only\");\n end" [CBI.12.0]
37 35 "7: Redact Author(s) cells in Tables with Author(s) header (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && hasTableHeader(\"Author(s)\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author(s)\", 7, \"CBI_author\", false, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.10.0, CBI.11.0]
38 36 "7: Not redacted because Vertebrate Study = N" "\n when\n Section(rowEquals(\"Vertebrate study Y/N\", \"N\") || rowEquals(\"Vertebrate study Y/N\", \"No\"))\n then\n section.redactNotCell(\"Author(s)\", 7, \"CBI_author\", true, \"Not redacted because row is not a vertebrate study\");\n section.redactNot(\"CBI_author\", 7, \"Not redacted because row is not a vertebrate study\");\n section.redactNot(\"CBI_address\", 7, \"Not redacted because row is not a vertebrate study\");\n section.highlightCell(\"Vertebrate study Y/N\", 7, \"hint_only\");\n end" [CBI.12.0]
39 37 "7: Redact if must redact entry is found" "\n when\n Section(matchesType(\"must_redact\"))\n then\n section.redact(\"CBI_author\", 7, \"must_redact entry was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 7, \"must_redact entry was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [CBI.8.0]
40 38 "8: Redact Authors and Addresses in Reference Table if it is a Vertebrate study" "\n when\n Section(rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\"))\n then\n section.redactCell(\"Author(s)\", 8, \"CBI_author\", true, \"Redacted because row is a vertebrate study\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 8, \"Redacted because row is a vertebrate study\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.highlightCell(\"Vertebrate study Y/N\", 8, \"must_redact\");\n end" [CBI.12.0]
41 39 "8: Redact if must redact entry is found" "\n when\n Section(matchesType(\"must_redact\"))\n then\n section.redact(\"CBI_author\", 8, \"Specification of impurity of the active substance was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2b)\");\n section.redact(\"CBI_address\", 8, \"Specification of impurity of the active substance was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2b)\");\n end" [CBI.8.0]
42 40 "8: Redact Author cells in Tables with Author header (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && hasTableHeader(\"Author\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author\", 8, \"CBI_author\", false, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.11.0]
43 41 "8: Redact Author(s) cells in Tables with Author(s) header (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && hasTableHeader(\"Author(s)\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author(s)\", 8, \"CBI_author\", false, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.11.0]
44 42 "9: Redact Author(s) cells in Tables with Author(s) header (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && hasTableHeader(\"Author(s)\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author(s)\", 9, \"CBI_author\", false, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.10.0, CBI.11.0]
45 43 "9: Redact Author cells in Tables with Author header (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && hasTableHeader(\"Author\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author\", 9, \"CBI_author\", false, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.10.0, CBI.11.0]
46 44 "9: Redact sponsor company" "\n when\n Section(searchText.toLowerCase().contains(\"batches produced at\"))\n then\n section.redactIfPrecededBy(\"batches produced at\", \"CBI_sponsor\", 9, \"Redacted because it represents a sponsor company\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.addHintAnnotation(\"batches produced at\", \"must_redact\");\n end" [CBI.14.0]
47 45 "9: Redact Authors and Addresses in Reference Table if it is a Vertebrate study" "\n when\n Section(rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\"))\n then\n section.redactCell(\"Author(s)\", 9, \"CBI_author\", true, \"Redacted because row is a vertebrate study\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 9, \"Redacted because row is a vertebrate study\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.highlightCell(\"Vertebrate study Y/N\", 9, \"must_redact\");\n end" [CBI.12.0]
48 46 "10: Redact and recommand Authors in Tables with Vertebrate study Y/N header (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\") || rowEquals(\"Vertebrate study Y/N\", \"N\") || rowEquals(\"Vertebrate study Y/N\", \"No\")))\n then\n section.redactCell(\"Author(s)\", 10, \"CBI_author\", true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.11.0]
49 47 "10: Redact determination of residues" "\n when\n Section((\n searchText.toLowerCase.contains(\"determination of residues\") ||\n searchText.toLowerCase.contains(\"determination of total residues\")\n ) && (\n searchText.toLowerCase.contains(\"livestock\") ||\n searchText.toLowerCase.contains(\"live stock\") ||\n searchText.toLowerCase.contains(\"tissue\") ||\n searchText.toLowerCase.contains(\"tissues\") ||\n searchText.toLowerCase.contains(\"liver\") ||\n searchText.toLowerCase.contains(\"muscle\") ||\n searchText.toLowerCase.contains(\"bovine\") ||\n searchText.toLowerCase.contains(\"ruminant\") ||\n searchText.toLowerCase.contains(\"ruminants\")\n ))\n then\n section.redact(\"CBI_author\", 10, \"Determination of residues was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 10, \"Determination of residues was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.addHintAnnotation(\"determination of residues\", \"must_redact\");\n section.addHintAnnotation(\"determination of total residues\", \"must_redact\");\n section.addHintAnnotation(\"livestock\", \"must_redact\");\n section.addHintAnnotation(\"live stock\", \"must_redact\");\n section.addHintAnnotation(\"tissue\", \"must_redact\");\n section.addHintAnnotation(\"tissues\", \"must_redact\");\n section.addHintAnnotation(\"liver\", \"must_redact\");\n section.addHintAnnotation(\"muscle\", \"must_redact\");\n section.addHintAnnotation(\"bovine\", \"must_redact\");\n section.addHintAnnotation(\"ruminant\", \"must_redact\");\n section.addHintAnnotation(\"ruminants\", \"must_redact\");\n end" [CBI.15.0]
50 48 "10: Redact Author cells in Tables with Author header (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && hasTableHeader(\"Author\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author\", 10, \"CBI_author\", false, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.11.0]
51 49 "10: Redact sponsor company" "\n when\n Section(searchText.toLowerCase().contains(\"batches produced at\"))\n then\n section.redactIfPrecededBy(\"batches produced at\", \"CBI_sponsor\", 10, \"Redacted because it represents a sponsor company\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.addHintAnnotation(\"batches produced at\", \"must_redact\");\n end" [CBI.14.0]
52 50 "11: Redact and recommand Authors in Tables with Vertebrate study Y/N header (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\") || rowEquals(\"Vertebrate study Y/N\", \"N\") || rowEquals(\"Vertebrate study Y/N\", \"No\")))\n then\n section.redactCell(\"Author(s)\", 11, \"CBI_author\", true, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.10.0, CBI.11.0]
53 51 "11: Redact if CTL/* or BL/* was found" "\n when\n Section(searchText.contains(\"CTL/\") || searchText.contains(\"BL/\"))\n then\n section.redact(\"CBI_author\", 11, \"Laboraty for vertebrate studies found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 11, \"Laboraty for vertebrate studies found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.addHintAnnotation(\"CTL\", \"must_redact\");\n section.addHintAnnotation(\"BL\", \"must_redact\");\n end" [SYN.0.0]
54 52 "11: Redact determination of residues" "\n when\n Section((\n searchText.toLowerCase.contains(\"determination of residues\") ||\n searchText.toLowerCase.contains(\"determination of total residues\")\n ) && (\n searchText.toLowerCase.contains(\"livestock\") ||\n searchText.toLowerCase.contains(\"live stock\") ||\n searchText.toLowerCase.contains(\"tissue\") ||\n searchText.toLowerCase.contains(\"tissues\") ||\n searchText.toLowerCase.contains(\"liver\") ||\n searchText.toLowerCase.contains(\"muscle\") ||\n searchText.toLowerCase.contains(\"bovine\") ||\n searchText.toLowerCase.contains(\"ruminant\") ||\n searchText.toLowerCase.contains(\"ruminants\")\n ))\n then\n section.redact(\"CBI_author\", 11, \"Determination of residues was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 11, \"Determination of residues was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.addHintAnnotation(\"determination of residues\", \"must_redact\");\n section.addHintAnnotation(\"determination of total residues\", \"must_redact\");\n section.addHintAnnotation(\"livestock\", \"must_redact\");\n section.addHintAnnotation(\"live stock\", \"must_redact\");\n section.addHintAnnotation(\"tissue\", \"must_redact\");\n section.addHintAnnotation(\"tissues\", \"must_redact\");\n section.addHintAnnotation(\"liver\", \"must_redact\");\n section.addHintAnnotation(\"muscle\", \"must_redact\");\n section.addHintAnnotation(\"bovine\", \"must_redact\");\n section.addHintAnnotation(\"ruminant\", \"must_redact\");\n section.addHintAnnotation(\"ruminants\", \"must_redact\");\n end" [CBI.15.0]
55 53 "11: Redact Author cells in Tables with Author header (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && hasTableHeader(\"Author\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author\", 11, \"CBI_author\", false, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.10.0, CBI.11.0]
56 54 "12: Redact and add recommendation for et al. author" "\n when\n Section(searchText.contains(\"et al\"))\n then\n\t\tsection.redactAndRecommendByRegEx(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", false, 1, \"CBI_author\", 12, \"Author found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [CBI.16.0]
57 55 "12: Redact and recommand Authors in Tables with Vertebrate study Y/N header (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\") || rowEquals(\"Vertebrate study Y/N\", \"N\") || rowEquals(\"Vertebrate study Y/N\", \"No\")))\n then\n section.redactCell(\"Author(s)\", 12, \"CBI_author\", true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.11.0]
58 56 "12: Recommend CTL/BL laboratory" "\n when\n Section(searchText.contains(\"CT\") || searchText.contains(\"BL\"))\n then\n section.addRecommendationByRegEx(\"((\\\\b((([Cc]T(([1ILli\\\\/])| L|~P))|(BL))[\\\\. ]?([\\\\dA-Ziltphz~\\\\/.:!]| ?[\\\\(',][Ppi](\\\\(e)?|([\\\\(-?']\\\\/))+( ?[\\\\(\\\\/\\\\dA-Znasieg]+)?)\\\\b( ?\\\\/? ?\\\\d+)?)|(\\\\bCT[L1i]\\\\b))\", true, 0, \"CBI_address\");\n end" [SYN.1.0]
59 57 "12: Redact if CTL/* or BL/* was found (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (searchText.contains(\"CTL/\") || searchText.contains(\"BL/\")))\n then\n section.addHintAnnotation(\"CTL\", \"hint_only\");\n section.addHintAnnotation(\"BL\", \"hint_only\");\n end" [SYN.0.0]
60 58 "13: Redact and recommand Authors in Tables with Vertebrate study Y/N header (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\") || rowEquals(\"Vertebrate study Y/N\", \"N\") || rowEquals(\"Vertebrate study Y/N\", \"No\")))\n then\n section.redactCell(\"Author(s)\", 13, \"CBI_author\", true, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.10.0, CBI.11.0]
61 59 "14: Add recommendation for Addresses in Test Organism sections, 13: Add recommendation for Addresses in Test Organism sections" "\n when\n Section(searchText.contains(\"Species:\") && searchText.contains(\"Source:\"))\n then\n\t\tsection.recommendLineAfter(\"Source:\", \"CBI_address\");\n end" [CBI.17.0]
62 60 "13: Redact and add recommendation for et al. author" "\n when\n Section(searchText.contains(\"et al\"))\n then\n\t\tsection.redactAndRecommendByRegEx(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", false, 1, \"CBI_author\", 13, \"Author found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [CBI.16.0]
63 61 "13: Redact if CTL/* or BL/* was found (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (searchText.contains(\"CTL/\") || searchText.contains(\"BL/\")))\n then\n section.addRedaction(\"CTL\", \"must_redact\", 13, \"Laboratory for vertebrate studies found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\" );\n section.addRedaction(\"BL\", \"must_redact\", 13, \"Laboratory for vertebrate studies found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\" );\n end" [SYN.0.0]
64 62 "15: Add recommendation for Addresses in Test Animals sections, 14: Add recommendation for Addresses in Test Animals sections" "\n when\n Section(searchText.contains(\"Species\") && searchText.contains(\"Source\"))\n then\n\t\tsection.recommendLineAfter(\"Source\", \"CBI_address\");\n end" [CBI.17.0]
65 63 "14: Redact addresses that start with BL or CTL" "\n when\n Section(searchText.contains(\"BL\") || searchText.contains(\"CT\"))\n then\n section.addRecommendationByRegEx(\"((\\\\b((([Cc]T(([1ILli\\\\/])| L|~P))|(BL))[\\\\. ]?([\\\\dA-Ziltphz~\\\\/.:!]| ?[\\\\(',][Ppi](\\\\(e)?|([\\\\(-?']\\\\/))+( ?[\\\\(\\\\/\\\\dA-Znasieg]+)?)\\\\b( ?\\\\/? ?\\\\d+)?)|(\\\\bCT[L1i]\\\\b))\", true, 0, \"CBI_address\");\n end" [SYN.1.0]
66 64 "14: Redacted PII Personal Identification Information" "\n when\n Section(matchesType(\"PII\"))\n then\n section.redact(\"PII\", 14, \"PII (Personal Identification Information) found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.0.0, PII.0.1]
67 65 "14: Redact and add recommendation for et al. author (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"et al\"))\n then\n section.redactAndRecommendByRegEx(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", false, 1, \"CBI_author\", 14, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.16.0]
68 66 "15: Redact Emails by RegEx" "\n when\n Section(searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}\\\\b\", true, 0, \"PII\", 15, \"PII (Personal Identification Information) found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.1.0, PII.1.1]
69 67 "15: Redact and add recommendation for et al. author (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"et al\"))\n then\n section.redactAndRecommendByRegEx(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", false, 1, \"CBI_author\", 15, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.16.0]
70 68 "15: Redact and add recommendation for et al. author (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"et al\"))\n then\n section.redactAndRecommendByRegEx(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", false, 1, \"CBI_author\", 15, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.16.0]
71 69 "16: Redacted PII Personal Identification Information" "\n when\n Section(matchesType(\"PII\"))\n then\n section.redact(\"PII\", 16, \"PII (Personal Identification Information) found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.0.0, PII.0.1]
72 70 "16: Redact and add recommendation for et al. author (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"et al\"))\n then\n section.redactAndRecommendByRegEx(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", false, 1, \"CBI_author\", 16, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.16.0]
73 71 "16: Redact contact information" "\n when\n Section(text.contains(\"Contact point:\")\n || text.contains(\"Phone:\")\n || text.contains(\"Fax:\")\n || text.contains(\"Tel.:\")\n || text.contains(\"Tel:\")\n || text.contains(\"E-mail:\")\n || text.contains(\"Email:\")\n || text.contains(\"e-mail:\")\n || text.contains(\"E-mail address:\")\n || text.contains(\"Alternative contact:\")\n || text.contains(\"Telephone number:\")\n || text.contains(\"Telephone No:\")\n || text.contains(\"Fax number:\")\n || text.contains(\"Telephone:\")\n || text.contains(\"European contact:\"))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Email:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Contact:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"European contact:\", \"PII\", 16, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.4.0, PII.4.1, PII.6.0, PII.6.1]
74 72 "16: Add recommendation for Addresses in Test Organism sections, 17: Add recommendation for Addresses in Test Organism sections" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"Species:\") && searchText.contains(\"Source:\"))\n then\n section.recommendLineAfter(\"Source:\", \"CBI_address\");\n end" [CBI.17.0]
75 73 "17: Redact Emails by RegEx" "\n when\n Section(searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 17, \"PII (Personal Identification Information) found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.1.0, PII.1.1]
76 74 "17: Add recommendation for Addresses in Test Animals sections, 18: Add recommendation for Addresses in Test Animals sections" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"Species\") && searchText.contains(\"Source\"))\n then\n section.recommendLineAfter(\"Source\", \"CBI_address\");\n end" [CBI.17.0]
77 75 "17: Redact contact information if applicant is found" "\n when\n Section(headlineContainsWord(\"applicant\") || text.contains(\"Applicant\") || headlineContainsWord(\"Primary contact\") || headlineContainsWord(\"Alternative contact\") || text.contains(\"Telephone number:\"))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Email:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Contact:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"European contact:\", \"PII\", 17, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.7.0]
78 76 "18: Do not redact Names and Addresses if Published Information found" "\n when\n Section(matchesType(\"published_information\"))\n then\n section.redactNotAndReference(\"CBI_author\",\"published_information\", 18, \"Published Information found\");\n section.redactNotAndReference(\"CBI_address\",\"published_information\", 18, \"Published Information found\");\n end" [CBI.7.0]
79 77 "18: Redact contact information if Producer is found" "\n when\n Section(text.toLowerCase().contains(\"producer of the plant protection\") || text.toLowerCase().contains(\"producer of the active substance\") || text.contains(\"Manufacturer of the active substance\") || text.contains(\"Manufacturer:\") || text.contains(\"Producer or producers of the active substance\"))\n then\n section.redactLineAfter(\"Contact:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Contact:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel:\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 18, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.8.0]
80 78 "18: Redact contact information" "\n when\n Section(text.contains(\"Contact point:\")\n || text.contains(\"Phone:\")\n || text.contains(\"Fax:\")\n || text.contains(\"Tel.:\")\n || text.contains(\"Tel:\")\n || text.contains(\"E-mail:\")\n || text.contains(\"Email:\")\n || text.contains(\"e-mail:\")\n || text.contains(\"E-mail address:\")\n || text.contains(\"Alternative contact:\")\n || text.contains(\"Telephone number:\")\n || text.contains(\"Telephone No:\")\n || text.contains(\"Fax number:\")\n || text.contains(\"Telephone:\")\n || text.contains(\"Phone No.\")\n || text.contains(\"European contact:\"))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Email:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Contact:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"European contact:\", \"PII\", 18, true, \"Contact information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.4.0, PII.4.1, PII.6.0, PII.6.1]
81 79 "19: Redact contact information if applicant is found" "\n when\n Section(headlineContainsWord(\"applicant\") || text.contains(\"Applicant\") || headlineContainsWord(\"Primary contact\") || headlineContainsWord(\"Alternative contact\") || text.contains(\"Telephone number:\"))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Email:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Contact:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"European contact:\", \"PII\", 19, true, \"Applicant information was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.7.0]
82 80 "19: Redact AUTHOR(S)" "\n when\n Section(searchText.contains(\"AUTHOR(S):\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 19, true, \"AUTHOR(S) was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.9.0, PII.9.1]
83 81 "19: Redacted PII Personal Identification Information (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"PII\"))\n then\n section.redact(\"PII\", 19, \"PII (Personal Identification Information) found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.0.0]
84 82 "19: Redacted PII Personal Identification Information (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"PII\"))\n then\n section.redact(\"PII\", 19, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.0.0]
85 83 "19: Do not redact Names and Addresses if Published Information found" "\n when\n Section(matchesType(\"published_information\"))\n then\n section.redactNotAndReference(\"CBI_author\",\"published_information\", 19, \"Published Information found\");\n section.redactNotAndReference(\"CBI_address\",\"published_information\", 19, \"Published Information found\");\n end" [CBI.7.0]
86 84 "20: Redact contact information if Producer is found" "\n when\n Section(text.toLowerCase().contains(\"producer of the plant protection\") || text.toLowerCase().contains(\"producer of the active substance\") || text.contains(\"Manufacturer of the active substance\") || text.contains(\"Manufacturer:\") || text.contains(\"Producer or producers of the active substance\"))\n then\n section.redactLineAfter(\"Contact:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Contact:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Tel:\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 20, true, \"Producer was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.8.0]
87 85 "20: Redact PERFORMING LABORATORY" "\n when\n Section(searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"PII\", 20, true, \"PERFORMING LABORATORY was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [CBI.20.0, CBI.20.1]
88 86 "20: Redacted PII Personal Identification Information (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"PII\"))\n then\n section.redact(\"PII\", 20, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.0.1]
89 87 "20: Redacted PII Personal Identification Information (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"PII\"))\n then\n section.redact(\"PII\", 20, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.0.1]
90 88 "21: Redact Emails by RegEx (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}\\\\b\", true, 0, \"PII\", 21, \"PII (Personal Identification Information) found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.1.0]
91 89 "21: Redact Emails by RegEx (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 21, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.1.0]
92 90 "21: Redact On behalf of Sequani Ltd.:" "\n when\n Section(searchText.contains(\"On behalf of Sequani Ltd.: Name Title\"))\n then\n section.redactBetween(\"On behalf of Sequani Ltd.: Name Title\", \"On behalf of\", \"PII\", 21, false , \"PII (Personal Identification Information) found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.11.0]
93 91 "21: Redact AUTHOR(S)" "\n when\n Section(searchText.contains(\"AUTHOR(S):\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 21, true, \"AUTHOR(S) was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.9.0, PII.9.1]
94 92 "22: Redact Emails by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\\\.[A-Z]{2,4}\\\\b\", true, 0, \"PII\", 22, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.1.1]
95 93 "22: Redact Emails by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 22, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.1.1]
96 94 "22: Redact PERFORMING LABORATORY" "\n when\n Section(searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"PII\", 22, true, \"PERFORMING LABORATORY was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [CBI.20.0, CBI.20.1]
97 95 "22: Redact On behalf of Syngenta Ltd.:" "\n when\n Section(searchText.contains(\"On behalf of Syngenta Ltd.: Name Title\"))\n then\n section.redactBetween(\"On behalf of Syngenta Ltd.: Name Title\", \"Study dates\", \"PII\", 22, false , \"PII (Personal Identification Information) found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.11.0]
98 96 "23: Redact contact information (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (text.contains(\"Contact point:\")\n || text.contains(\"Phone:\")\n || text.contains(\"Fax:\")\n || text.contains(\"Tel.:\")\n || text.contains(\"Tel:\")\n || text.contains(\"E-mail:\")\n || text.contains(\"Email:\")\n || text.contains(\"e-mail:\")\n || text.contains(\"E-mail address:\")\n || text.contains(\"Alternative contact:\")\n || text.contains(\"Telephone number:\")\n || text.contains(\"Telephone No:\")\n || text.contains(\"Fax number:\")\n || text.contains(\"Telephone:\")\n || text.contains(\"Phone No.\")\n || text.contains(\"European contact:\")))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Email:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 23, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.5.0, PII.6.0]
99 97 "23: Redact contact information (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (text.contains(\"Contact point:\")\n || text.contains(\"Contact:\")\n || text.contains(\"Alternative contact:\")\n || (text.contains(\"No:\") && text.contains(\"Fax\"))\n || (text.contains(\"Contact:\") && text.contains(\"Tel.:\"))\n || text.contains(\"European contact:\")\n ))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 23, true, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 23, true, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 23, true, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 23, true, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 23, true, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 23, true, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.4.0, PII.6.0]
100 98 "24: Redact contact information (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (text.contains(\"Contact point:\")\n || text.contains(\"Contact:\")\n || text.contains(\"Alternative contact:\")\n || (text.contains(\"No:\") && text.contains(\"Fax\"))\n || (text.contains(\"Contact:\") && text.contains(\"Tel.:\"))\n || text.contains(\"European contact:\")\n ))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 24, true, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 24, true, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 24, true, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 24, true, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 24, true, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 24, true, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.5.1, PII.6.1]
101 99 "24: Redact contact information (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (text.contains(\"Contact point:\")\n || text.contains(\"Phone:\")\n || text.contains(\"Fax:\")\n || text.contains(\"Tel.:\")\n || text.contains(\"Tel:\")\n || text.contains(\"E-mail:\")\n || text.contains(\"Email:\")\n || text.contains(\"e-mail:\")\n || text.contains(\"E-mail address:\")\n || text.contains(\"Alternative contact:\")\n || text.contains(\"Telephone number:\")\n || text.contains(\"Telephone No:\")\n || text.contains(\"Fax number:\")\n || text.contains(\"Telephone:\")\n || text.contains(\"Phone No.\")\n || text.contains(\"European contact:\")))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Email:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 24, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.4.1, PII.6.1]
102 100 "25: Redact Phone and Fax by RegEx" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (\n text.contains(\"Contact\")\n || text.contains(\"Telephone\")\n || text.contains(\"Phone\")\n || text.contains(\"Ph.\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Cell\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n ))\n then\n section.redactByRegEx(\"\\\\b(contact|telephone|phone|fax|tel|ter|cell|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\", true, 2, \"PII\", 25, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.2.0]
103 101 "25: Redact Phone and Fax by RegEx (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (\n text.contains(\"Contact\")\n || text.contains(\"Telephone\")\n || text.contains(\"Phone\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n ))\n then\n section.redactByRegEx(\"\\\\b(contact|telephone|phone|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\", true, 2, \"PII\", 25, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.2.0]
104 102 "25: Redact contact information if applicant is found (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (headlineContainsWord(\"applicant\") || text.contains(\"Applicant\") || headlineContainsWord(\"Primary contact\") || headlineContainsWord(\"Alternative contact\") || text.contains(\"Telephone number:\")))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Email:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 25, true, \"Applicant information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.7.0]
105 103 "33: Purity Hint, 34: Purity Hint, 50: Purity Hint, 25: Purity Hint, 39: Purity Hint" "\n when\n Section(searchText.toLowerCase().contains(\"purity\"))\n then\n\t section.addHintAnnotationByRegEx(\"(purity ?( of|\\\\(.{1,20}\\\\))?( ?:)?) .{0,5}[\\\\d\\\\.]+( .{0,4}\\\\.)? ?%\", true, 1, \"hint_only\");\n end" [ETC.0.0]
106 104 "25: Redact Purity" "\n when\n Section(searchText.contains(\"purity\"))\n then\n\t section.redactByRegEx(\"purity ?:? (([\\\\d\\\\.]+)( .{0,4}\\\\.)? ?%)\", true, 1, \"purity\", 17, \"Purity found\", \"Reg (EC) No 1107/2009 Art. 63 (2a)\");\n end" [ETC.1.0]
107 105 "26: Redact Phone and Fax by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (\n text.contains(\"Contact\")\n || text.contains(\"Telephone\")\n || text.contains(\"Phone\")\n || text.contains(\"Ph.\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Cell\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n ))\n then\n section.redactByRegEx(\"\\\\b(contact|telephone|phone|fax|tel|ter|cell|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\", true, 2, \"PII\", 26, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.2.1]
108 106 "26: Redact contact information if applicant is found (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (headlineContainsWord(\"applicant\") || text.contains(\"Applicant\") || headlineContainsWord(\"Primary contact\") || headlineContainsWord(\"Alternative contact\") || text.contains(\"Telephone number:\")))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Email:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 26, true, \"Applicant information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.7.0]
109 107 "26: Redact signatures" "\n when\n Section(matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 26, \"Signature found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [ETC.2.0]
110 108 "26: Redact Phone and Fax by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (\n text.contains(\"Contact\")\n || text.contains(\"Telephone\")\n || text.contains(\"Phone\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n ))\n then\n section.redactByRegEx(\"\\\\b(contact|telephone|phone|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\", true, 2, \"PII\", 26, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.2.1]
111 109 "27: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"COMPLETION DATE:\")\n && !searchText.contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
112 110 "27: Redact Logos" "\n when\n Section(matchesImageType(\"logo\"))\n then\n section.redactImage(\"logo\", 27, \"Logo found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [ETC.3.0]
113 111 "27: Redact contact information if Producer is found (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (text.toLowerCase().contains(\"producer of the plant protection\") || text.toLowerCase().contains(\"producer of the active substance\") || text.contains(\"Manufacturer of the active substance\") || text.contains(\"Manufacturer:\") || text.contains(\"Producer or producers of the active substance\")))\n then\n section.redactLineAfter(\"Contact:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 27, true, \"Producer was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.8.0]
114 112 "28: Redact contact information if Producer is found (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (text.toLowerCase().contains(\"producer of the plant protection\") || text.toLowerCase().contains(\"producer of the active substance\") || text.contains(\"Manufacturer of the active substance\") || text.contains(\"Manufacturer:\") || text.contains(\"Producer or producers of the active substance\")))\n then\n section.redactLineAfter(\"Contact:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 28, true, \"Producer was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.8.0]
115 113 "28: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"COMPLETION DATE:\")\n && !searchText.contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 28, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
116 114 "28: Redact dossier dictionary match" "\n when\n Section(matchesType(\"dossier_redaction\"))\n then\n section.redact(\"dossier_redaction\", 28, \"Specification of impurity found\", \"Reg (EC) No 1107/2009 Art. 63 (2b)\");\n end" [ETC.4.0, ETC.4.1, ETC.4.2]
117 115 "29: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"COMPLETION DATE:\") && !searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
118 116 "34: Ignore dossier_redaction entries if confidentiality is not 'confidential', 51: Ignore dossier_redaction entries if confidential, 29: Ignore dossier_redaction unless confidential, 40: Ignore dossier_redaction entries if confidential" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end" [ETC.5.0]
119 117 "29: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
120 118 "30: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
121 119 "30: Redacted PII Personal Identification Information (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"PII\"))\n then\n section.redact(\"PII\", 30, \"PII (Personal Identification Information) found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.0.0]
122 120 "30: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"COMPLETION DATE:\") && !searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
123 121 "31: Redacted PII Personal Identification Information (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"PII\"))\n then\n section.redact(\"PII\", 31, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.0.1]
124 122 "31: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", 31, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
125 123 "31: Redact PERFORMING LABORATORY (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"PERFORMING LABORATORY:\")\n )\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", 31, true, \"PERFORMING LABORATORY was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactNot(\"CBI_address\", 31, \"Performing laboratory found for non vertebrate study\");\n end" [CBI.20.0]
126 124 "32: Redact PERFORMING LABORATORY (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", 32, true, \"PERFORMING LABORATORY was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.20.1]
127 125 "32: Redact Emails by RegEx (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 32, \"PII (Personal Identification Information) found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.1.0]
128 126 "32: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", 32, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
129 127 "33: Redact Emails by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 33, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.1.1]
130 128 "33: Redact study director abbreviation" "\n when\n Section((searchText.contains(\"KATH\") || searchText.contains(\"BECH\") || searchText.contains(\"KML\")))\n then\n section.redactWordPartByRegEx(\"((KATH)|(BECH)|(KML)) ?(\\\\d{4})\", true, 0, 1, \"PII\", 34, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.10.0]
131 129 "33: Redact PERFORMING LABORATORY (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", 33, true, \"PERFORMING LABORATORY was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactNot(\"CBI_address\", 33, \"Performing laboratory found for non vertebrate study\");\n end" [CBI.20.0]
132 130 "34: Redact PERFORMING LABORATORY (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", 34, true, \"PERFORMING LABORATORY was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.20.1]
133 131 "34: Redact telephone numbers by RegEx (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && containsRegEx(\"[+]\\\\d{1,}\", true))\n then\n section.redactByRegEx(\"((([+]\\\\d{1,3} (\\\\d{7,12})\\\\b)|([+]\\\\d{1,3}(\\\\d{3,12})\\\\b|[+]\\\\d{1,3}([ -]\\\\(?\\\\d{1,6}\\\\)?){2,4})|[+]\\\\d{1,3} ?((\\\\d{2,6}\\\\)?)([ -]\\\\d{2,6}){1,4}))(-\\\\d{1,3})?\\\\b)\", true, 1, \"PII\", 34, \"PII (Personal Identification Information) found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.3.0]
134 132 "35: Redact telephone numbers by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && containsRegEx(\"[+]\\\\d{1,}\", true))\n then\n section.redactByRegEx(\"((([+]\\\\d{1,3} (\\\\d{7,12})\\\\b)|([+]\\\\d{1,3}(\\\\d{3,12})\\\\b|[+]\\\\d{1,3}([ -]\\\\(?\\\\d{1,6}\\\\)?){2,4})|[+]\\\\d{1,3} ?((\\\\d{2,6}\\\\)?)([ -]\\\\d{2,6}){1,4}))(-\\\\d{1,3})?\\\\b)\", true, 1, \"PII\", 35, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.3.1]
135 133 "35: Redact signatures (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 35, \"Signature found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [ETC.2.0]
136 134 "36: Redact signatures (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 36, \"Signature found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.2.0]
137 135 "37: Redact contact information (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (text.contains(\"Contact point:\")\n || text.contains(\"Phone:\")\n || text.contains(\"Fax:\")\n || text.contains(\"Tel.:\")\n || text.contains(\"Tel:\")\n || text.contains(\"E-mail:\")\n || text.contains(\"Email:\")\n || text.contains(\"e-mail:\")\n || text.contains(\"E-mail address:\")\n || text.contains(\"Alternative contact:\")\n || text.contains(\"Telephone number:\")\n || text.contains(\"Telephone No:\")\n || text.contains(\"Fax number:\")\n || text.contains(\"Telephone:\")\n || text.contains(\"Phone No.\")\n || text.contains(\"European contact:\")))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Email:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 37, true, \"Contact information was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.4.0, PII.6.0]
138 136 "38: Redact contact information (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (text.contains(\"Contact point:\")\n || text.contains(\"Phone:\")\n || text.contains(\"Fax:\")\n || text.contains(\"Tel.:\")\n || text.contains(\"Tel:\")\n || text.contains(\"E-mail:\")\n || text.contains(\"Email:\")\n || text.contains(\"e-mail:\")\n || text.contains(\"E-mail address:\")\n || text.contains(\"Alternative contact:\")\n || text.contains(\"Telephone number:\")\n || text.contains(\"Telephone No:\")\n || text.contains(\"Fax number:\")\n || text.contains(\"Telephone:\")\n || text.contains(\"Phone No.\")\n || text.contains(\"European contact:\")))\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Email:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 38, true, \"Contact information was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.4.1, PII.6.1]
139 137 "39: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"COMPLETION DATE:\") && !searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 39, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
140 138 "40: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"COMPLETION DATE:\") && !searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 40, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
141 139 "41: Redact signatures (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 41, \"Signature found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [ETC.2.0]
142 140 "41: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", 41, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
143 141 "42: Redact signatures (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 42, \"Signature found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.2.0]
144 142 "42: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", 42, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
145 143 "43: Redact PERFORMING LABORATORY (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", 43, true, \"PERFORMING LABORATORY was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactNot(\"CBI_address\", 43, \"Performing laboratory found for non vertebrate study\");\n end" [CBI.20.0]
146 144 "43: Redact Logos (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesImageType(\"logo\"))\n then\n section.redactImage(\"logo\", 43, \"Logo found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.3.0]
147 145 "44: Redact PERFORMING LABORATORY (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", 44, true, \"PERFORMING LABORATORY was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.20.1]
148 146 "52: Redact signatures (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 52, \"Signature found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [ETC.2.0]
149 147 "53: Redact signatures (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 53, \"Signature found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.2.0]
150 148 "54: Redact Logos (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesImageType(\"logo\"))\n then\n section.redactImage(\"logo\", 54, \"Logo found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.3.0]
151 149 "0: Expand CBI Authors with firstname initials" "\n when\n Section(matchesType(\"CBI_author\"))\n then\n section.expandByRegEx(\"CBI_author\", \"(,? [A-Z]\\\\.?( ?[A-Z]\\\\.?)?( ?[A-Z]\\\\.?)?\\\\b\\\\.?)\", false, 1);\n end" [CBI.18.0]
152 150 "0: Expand CBI Authors with firstname initials" "\n when\n Section(matchesType(\"CBI_author\"))\n then\n section.expandByRegEx(\"CBI_author\", \"(,? [A-Z]\\\\.?( ?[A-Z]\\\\.?)?( ?[A-Z]\\\\.?)?\\\\b\\\\.?)\", false, 1, \"[^\\\\s]+\");\n end" [CBI.18.0]
153 151 "0: Expand CBI_author and PII matches with salutation prefix" "\n when\n Section((matchesType(\"CBI_author\") || matchesType(\"PII\")) && (\n searchText.contains(\"Mr\")\n || searchText.contains(\"Mrs\")\n || searchText.contains(\"Ms\")\n || searchText.contains(\"Miss\")\n || searchText.contains(\"Sir\")\n || searchText.contains(\"Madam\")\n || searchText.contains(\"Madame\")\n || searchText.contains(\"Mme\")\n ))\n then\n section.expandByPrefixRegEx(\"CBI_author\", \"\\\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\\\s?\\\\.?\\\\s*\", false, 0);\n section.expandByPrefixRegEx(\"PII\", \"\\\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\\\s?\\\\.?\\\\s*\", false, 0);\n end" [CBI.19.0, PII.12.0]
154 152 "102: Guidelines FileAttributes" "\n when\n Section((text.contains(\"DATA REQUIREMENT(S):\") || text.contains(\"TEST GUIDELINE(S):\")) && (text.contains(\"OECD\") || text.contains(\"EPA\") || text.contains(\"OPPTS\")))\n then\n section.addFileAttribute(\"OECD Number\", \"OECD (No\\\\.? )?\\\\d{3}( \\\\(\\\\d{4}\\\\))?\", false, 0);\n end" [ETC.7.0]
155 153 "28: Redact Logos" "\n when\n Section(matchesImageType(\"logo\"))\n then\n section.redactImage(\"logo\", 28, \"Logo found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [ETC.3.0]
156 154 "8: Redact Author cells in Tables with Author header (Non vertebrate study)" "\n when\n Section(hasTableHeader(\"h5.1\"))\n then\n section.redactCell(\"h5.1\", 8, \"CBI_author\", false, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.11.0]
157 155 "30: Ignore dossier_redactions if confidential" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redactions\"));\n then\n section.ignore(\"dossier_redactions\");\n end" [ETC.5.0]
158 156 "27: Redact formula" "\n when\n Section(matchesImageType(\"formula\"))\n then\n section.redactImage(\"formula\", 27, \"Formula found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [ETC.8.0]
159 157 "5: Do not redact Names and Addresses if no redaction Indicator is contained" "\n when\n Section(matchesType(\"vertebrate\"), matchesType(\"published_information\"))\n then\n section.redactNotAndReference(\"CBI_author\",\"published_information\", 5, \"Vertebrate and Published Information found\");\n section.redactNotAndReference(\"CBI_address\",\"published_information\", 5, \"Vertebrate and Published Information found\");\n end" [CBI.4.0]
160 158 "29: Redact Dossier Redactions" "\n when\n Section(matchesType(\"dossier_redactions\"))\n then\n section.redact(\"dossier_redactions\", 29, \"Dossier Redaction found\", \"Article 39(1)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.4.0, ETC.4.1, ETC.4.2]
161 159 "10: Redact determination of residues" "\n when\n Section((\n searchText.toLowerCase.contains(\"determination of residues\") ||\n searchText.toLowerCase.contains(\"determination of total residues\")\n ) && (\n searchText.toLowerCase.contains(\"livestock\") ||\n searchText.toLowerCase.contains(\"live stock\") ||\n searchText.toLowerCase.contains(\"tissue\") ||\n searchText.toLowerCase.contains(\"tissues\") ||\n searchText.toLowerCase.contains(\"liver\") ||\n searchText.toLowerCase.contains(\"muscle\") ||\n searchText.toLowerCase.contains(\"bovine\") ||\n searchText.toLowerCase.contains(\"ruminant\") ||\n searchText.toLowerCase.contains(\"ruminants\")\n ))\n then\n section.redact(\"CBI_author\", 10, \"Determination of residues was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redact(\"CBI_address\", 10, \"Determination of residues was found.\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.addHintAnnotation(\"determination of residues\", \"must_redact\");\n section.addHintAnnotation(\"livestock\", \"must_redact\");\n section.addHintAnnotation(\"live stock\", \"must_redact\");\n section.addHintAnnotation(\"tissue\", \"must_redact\");\n section.addHintAnnotation(\"tissues\", \"must_redact\");\n section.addHintAnnotation(\"liver\", \"must_redact\");\n section.addHintAnnotation(\"muscle\", \"must_redact\");\n section.addHintAnnotation(\"bovine\", \"must_redact\");\n section.addHintAnnotation(\"ruminant\", \"must_redact\");\n section.addHintAnnotation(\"ruminants\", \"must_redact\");\n end" [CBI.15.0]
162 160 "19: Redact AUTHOR(S)" "\n when\n Section(searchText.contains(\"AUTHOR(S):\") && fileAttributeByPlaceholderEquals(\"{fileattributes.vertebrateStudy}\", \"true\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 19, true, \"AUTHOR(S) was found\", \"Reg (EC) No 1107/2009 Art. 63 (2e)\");\n end" [PII.9.1]
163 161 "101: Redact CAS numbers" "\n when\n Section(hasTableHeader(\"Sample #\"))\n then\n section.redactCell(\"Sample #\", 8, \"PII\", true, \"Redacted because row is a vertebrate study\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [ETC.6.0]
164 162 "21: Redact Emails by RegEx" "\n when\n Section(searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 21, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.1.0, PII.1.1]
165 163 "32: Redact signatures" "\n when\n Section(matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 32, \"Signature found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.2.0, ETC.2.1]
166 164 "11: Do not redact Names and Addresses if Published Information found" "\n when\n Section(matchesType(\"published_information\"))\n then\n section.redactNotAndReference(\"CBI_author\",\"published_information\", 11, \"Published Information found\");\n section.redactNotAndReference(\"CBI_address\",\"published_information\", 11, \"Published Information found\");\n end" [CBI.7.0, CBI.7.1]
167 165 "9: Add recommendation for Addresses in Test Organism sections" "\n when\n Section(searchText.contains(\"Species:\") && searchText.contains(\"Source:\"))\n then\n section.recommendLineAfter(\"Source:\", \"CBI_address\");\n end" [CBI.17.1]
168 166 "5: Redact Author cells in Tables with Author header" "\n when\n Section(hasTableHeader(\"Author\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author\", 5, \"CBI_author\", false, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.9.1, CBI.10.1]
169 167 "21: Redact Phone and Fax by RegEx" "\n when\n Section(\n text.contains(\"Telephone\")\n || text.contains(\"Phone\")\n || text.contains(\"Ph.\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Cell\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n )\n then\n section.redactByRegEx(\"\\\\b(telephone|phone|fax|tel|ter|cell|mobile|fel|fer)[:.\\\\s]{0,3}((\\\\(?\\\\+?[0-9])(\\\\(?[0-9\\\\/.\\\\-\\\\s]+\\\\)?)*([0-9]+\\\\)?))\\\\b\", true, 2, \"PII\", 23, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.2.0, PII.2.1]
170 168 "2: Redact CBI Address" "\n when\n Section(matchesType(\"CBI_address\"))\n then\n section.redact(\"CBI_address\", 4, \"Address found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.1.1]
171 169 "25: Redact AUTHOR(S)" "\n when\n Section(searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"COMPLETION DATE:\")\n && !searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 25, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.0, PII.9.1]
172 170 "6: Redact and recommand Authors in Tables with Vertebrate study Y/N header" "\n when\n Section(rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\") || rowEquals(\"Vertebrate study Y/N\", \"N\") || rowEquals(\"Vertebrate study Y/N\", \"No\"))\n then\n section.redactCell(\"Author(s)\", 6, \"CBI_author\", true, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.9.1, CBI.10.0, CBI.10.1, CBI.11.0, CBI.12.0, CBI.12.1]
173 171 "22: Redact contact information" "\n when\n Section(text.contains(\"Contact point:\")\n || text.contains(\"Phone:\")\n || text.contains(\"Fax:\")\n || text.contains(\"Tel.:\")\n || text.contains(\"Tel:\")\n || text.contains(\"E-mail:\")\n || text.contains(\"Email:\")\n || text.contains(\"e-mail:\")\n || text.contains(\"E-mail address:\")\n || text.contains(\"Contact:\")\n || text.contains(\"Alternative contact:\")\n || text.contains(\"Telephone number:\")\n || text.contains(\"Telephone No:\")\n || text.contains(\"Fax number:\")\n || text.contains(\"Telephone:\")\n || text.contains(\"Phone No.\")\n || (text.contains(\"No:\") && text.contains(\"Fax\"))\n || (text.contains(\"Contact:\") && text.contains(\"Tel.:\"))\n || text.contains(\"European contact:\")\n )\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Tel:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Email:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Contact:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLineAfter(\"European contact:\", \"PII\", 22, true, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.4.0, PII.4.1, PII.5.0, PII.5.1, PII.6.0, PII.6.1, PII.7.0, PII.7.1, PII.8.0, PII.8.1]
174 172 "20: Redacted PII Personal Identification Information" "\n when\n Section(matchesType(\"PII\"))\n then\n section.redact(\"PII\", 20, \"PII (Personal Identification Information) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.0.0, PII.0.1]
175 173 "1: Redact CBI Authors" "\n when\n Section(matchesType(\"CBI_author\"))\n then\n section.redact(\"CBI_author\", 1, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.0.0, CBI.0.1]
176 174 "4: Redact Author(s) cells in Tables with Author(s) header" "\n when\n Section(hasTableHeader(\"Author(s)\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author(s)\", 4, \"CBI_author\", false, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.9.0, CBI.10.0]
177 175 "8: Redact and add recommendation for et al. author" "\n when\n Section(searchText.contains(\"et al\"))\n then\n section.redactAndRecommendByRegEx(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", false, 1, \"CBI_author\", 15, \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.16.0, CBI.16.1]
178 176 "26: Redact AUTHOR(S)" "\n when\n Section(searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", 26, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.0, PII.9.1]
179 177 "27: Redact PERFORMING LABORATORY" "\n when\n Section(searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", 27, true, \"PERFORMING LABORATORY was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.20.0, CBI.20.1]
180 178 "10: Add recommendation for Addresses in Test Animals sections" "\n when\n Section(searchText.contains(\"Species\") && searchText.contains(\"Source\"))\n then\n section.recommendLineAfter(\"Source\", \"CBI_address\");\n end" [CBI.17.0, CBI.17.1]
181 179 "33: Redact Logos" "\n when\n Section(matchesImageType(\"logo\"))\n then\n section.redactImage(\"logo\", 33, \"Logo found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.3.0, ETC.3.1]
182 180 "26: Redact Phone and Fax by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (\n text.contains(\"Contact\")\n || text.contains(\"Telephone\")\n || text.contains(\"Phone\")\n || text.contains(\"Ph.\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n ))\n then\n section.redactByRegEx(\"\\\\b(contact|telephone|phone|ph\\\\.|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\", true, 2, \"PII\", 26, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.2.1]
183 181 "25: Redact Phone and Fax by RegEx (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (\n text.contains(\"Contact\")\n || text.contains(\"Telephone\")\n || text.contains(\"Phone\")\n || text.contains(\"Ph.\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n ))\n then\n section.redactByRegEx(\"\\\\b(contact|telephone|phone|ph\\\\.|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.]{4,100}\\\\d)\\\\b\", true, 2, \"PII\", 25, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.2.0]
184 182 "10: Add recommendation for Addresses in Test Animals sections" "\n when\n Section(searchText.contains(\"Species\") && searchText.contains(\"Source\"))\n then\n\t\tsection.recommendLineAfter(\"Source\", \"PII\");\n end" [CBI.17.0, CBI.17.1]
185 183 "0: Combine address parts from AI to addresses as PII (org is mandatory)" "\n when\n Section(aiMatchesType(\"ORG\"))\n then\n section.combineAiTypes(\"ORG\", \"STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\", 20, \"PII\", 3, false);\n end" [AI.1.0]
186 184 "0: Combine address parts from AI to addresses as PII (street is mandatory)" "\n when\n Section(aiMatchesType(\"STREET\"))\n then\n section.combineAiTypes(\"STREET\", \"ORG,POSTAL,COUNTRY,CARDINAL,CITY,STATE\", 20, \"PII\", 3, false);\n end" [AI.1.0]
187 185 "18: Redact AUTHOR(S)" "\n when\n Section(searchText.contains(\"AUTHOR(S):\") && searchText.contains(\"STUDY COMPLETION DATE:\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", 18, true, \"AUTHOR(S) was found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [PII.9.0, PII.9.1]
188 186 "0: Combine address parts from AI to addresses as PII (city is mandatory)" "\n when\n Section(aiMatchesType(\"CITY\"))\n then\n section.combineAiTypes(\"CITY\", \"ORG,STREET,POSTAL,COUNTRY,CARDINAL,STATE\", 20, \"PII\", 3, false);\n end" [AI.1.0]
189 187 "24: Redact signatures" "\n when\n Section(matchesImageType(\"signature\"))\n then\n section.redactImage(\"signature\", 24, \"Signature found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [ETC.2.0, ETC.2.1]
190 188 "13: Redact Emails by RegEx" "\n when\n Section(searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 13, \"PII (Personal Identification Information) found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [PII.1.0, PII.1.1]
191 189 "4: Redact Author(s) cells in Tables with Author(s) header" "\n when\n Section(hasTableHeader(\"Author(s)\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author(s)\", 4, \"PII\", false, \"Author found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [CBI.9.0, CBI.10.0, CBI.11.0]
192 190 "25: Redact Logos" "\n when\n Section(matchesImageType(\"logo\"))\n then\n section.redactImage(\"logo\", 25, \"Logo found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [ETC.3.0, ETC.3.1]
193 191 "11: Do not redact Names and Addresses if Published Information found" "\n when\n Section(matchesType(\"published_information\"))\n then\n section.redactNot(\"PII\", 11, \"Published Information found\");\n section.redactNot(\"PII\", 11, \"Published Information found\");\n end" [CBI.7.0, CBI.7.1, CBI.6.0, CBI.6.1]
194 192 "2: Do not redact genitive PIIs" "\n when\n Section(matchesType(\"PII\"))\n then\n section.expandToFalsePositiveByRegEx(\"PII\", \"['’’'ʼˈ´`‘′ʻ’']s\", false, 0);\n end" [CBI.2.0]
195 193 "5: Redact Author cells in Tables with Author header" "\n when\n Section(hasTableHeader(\"Author\") && !hasTableHeader(\"Vertebrate study Y/N\"))\n then\n section.redactCell(\"Author\", 5, \"PII\", false, \"Author found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [CBI.9.1, CBI.10.1, CBI.12.0]
196 194 "19: Redact PERFORMING LABORATORY" "\n when\n Section(searchText.contains(\"PERFORMING LABORATORY:\"))\n then\n section.redactBetween(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"PII\", 19, true, \"PERFORMING LABORATORY was found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [CBI.20.0, CBI.20.1]
197 195 "0: Recommend authors from AI as PII" "\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"PII\");\n end" [AI.3.0]
198 196 "9: Add recommendation for Addresses in Test Organism sections" "\n when\n Section(searchText.contains(\"Species:\") && searchText.contains(\"Source:\"))\n then\n\t\tsection.recommendLineAfter(\"Source:\", \"PII\");\n end" [CBI.17.1]
199 197 "1: Redacted PII Personal Identification Information" "\n when\n Section(matchesType(\"PII\"))\n then\n section.redact(\"PII\", 1, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [PII.0.0, PII.0.1]
200 198 "8: Redact and add recommendation for et al. author" "\n when\n Section(searchText.contains(\"et al\"))\n then\n\t\tsection.redactAndRecommendByRegEx(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", false, 1, \"PII\", 8, \"Author found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [CBI.16.0, CBI.16.1]
201 199 "14: Redact contact information" "\n when\n Section(text.contains(\"Contact point:\")\n || text.contains(\"Phone:\")\n || text.contains(\"Fax:\")\n || text.contains(\"Tel.:\")\n || text.contains(\"Tel:\")\n || text.contains(\"E-mail:\")\n || text.contains(\"Email:\")\n || text.contains(\"e-mail:\")\n || text.contains(\"E-mail address:\")\n || text.contains(\"Contact:\")\n || text.contains(\"Alternative contact:\")\n || text.contains(\"Telephone number:\")\n || text.contains(\"Telephone No:\")\n || text.contains(\"Fax number:\")\n || text.contains(\"Telephone:\")\n || text.contains(\"Phone No.\")\n || (text.contains(\"No:\") && text.contains(\"Fax\"))\n || (text.contains(\"Contact:\") && text.contains(\"Tel.:\"))\n || text.contains(\"European contact:\")\n )\n then\n section.redactLineAfter(\"Contact point:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Phone:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Fax:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Tel.:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Tel:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"E-mail:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Email:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"e-mail:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"E-mail address:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Contact:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Alternative contact:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Telephone number:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Telephone No:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Fax number:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Telephone:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"Phone No.\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactBetween(\"No:\", \"Fax\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactBetween(\"Contact:\", \"Tel.:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n section.redactLineAfter(\"European contact:\", \"PII\", 14, true, \"Personal information found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [PII.4.0, PII.4.1, PII.6.0, PII.6.1]
202 200 "17: Redact AUTHOR(S)" "\n when\n Section(searchText.contains(\"AUTHOR(S):\"))\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", 17, true, \"AUTHOR(S) was found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [PII.9.0, PII.9.1]
203 201 "6: Redact and recommand Authors in Tables with Vertebrate study Y/N header" "\n when\n Section(rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\") || rowEquals(\"Vertebrate study Y/N\", \"N\") || rowEquals(\"Vertebrate study Y/N\", \"No\"))\n then\n section.redactCell(\"Author(s)\", 6, \"PII\", false, \"Author found\", \"Article 4(1)(b), Regulation (EC) No 1049/2001 (Personal data)\");\n end" [CBI.11.0, CBI.12.2, CBI.12.1]
204 202 "27: Redact Logos" "\n °when\n Section(matchesImageType(\"logo\"))\n then\n //section.redactImage(\"logo\", 27, \"Logo found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redactNotImage(\"logo\", 27, \"No Logos in preGFL documents\");\n end" [ETC.3.0, ETC.3.1]
205 203 "27: Redact Logos" "\n when\n Section(matchesImageType(\"logo\"))\n then\n //section.redactImage(\"logo\", 27, \"Logo found\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n section.redactNotImage(\"logo\", 27, \"No Logos in preGFL documents\");\n end" [ETC.3.0, ETC.3.1]
206 204 "10a: Redact Addresses in Reference Tables for vertebrate studies in non-vertebrate documents" "\n when\n Section(hasTableHeader(\"Vertebrate study Y/N\") && (rowEquals(\"Vertebrate study Y/N\", \"Y\") || rowEquals(\"Vertebrate study Y/N\", \"Yes\")))\n then\n section.redact(\"CBI_address\", 10, \"Redacted because row is a vertebrate study\", \"Reg (EC) No 1107/2009 Art. 63 (2g)\");\n end" [CBI.22.0]
207 205 "27a: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toUpperCase().contains(\"AUTHOR(S)\")\n && !searchText.toUpperCase().contains(\"AUTHOR(S):\")\n && searchText.toUpperCase().contains(\"COMPLETION DATE\")\n && !searchText.toUpperCase().contains(\"STUDY COMPLETION DATE\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S)\", \"COMPLETION DATE\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\", \"Completion Date\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"AUTHOR(S)\\n\", \"COMPLETION DATE\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\\n\", \"Completion Date\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
208 206 "29b: Redact short Authors section" "\n when\n Section(\n !fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toLowerCase().contains(\"author(s)\")\n && searchText.length() < 50\n && sectionNumber <= 20\n && !aiMatchesType(\"CBI_Author\")\n )\n then\n section.redactByRegEx(\"(?<=author\\\\(?s\\\\)?\\\\s\\\\n?)([\\\\p{Lu}\\\\p{L} ]{5,15}(,|\\\\n)?){1,3}\",true,0,\"CBI_author\",29,\"AUTHOR(S) was found\",\"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [CBI.21.0]
209 207 "28a: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toUpperCase().contains(\"AUTHOR(S)\")\n && !searchText.toUpperCase().contains(\"AUTHOR(S):\")\n && searchText.toUpperCase().contains(\"COMPLETION DATE\")\n && !searchText.toUpperCase().contains(\"STUDY COMPLETION DATE\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S)\", \"COMPLETION DATE\", \"CBI_author\", 28, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\", \"Completion Date\", \"CBI_author\", 28, true, \"Author(s) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"AUTHOR(S)\\n\", \"COMPLETION DATE\", \"CBI_author\", 28, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\\n\", \"Completion Date\", \"CBI_author\", 28, true, \"Author(s) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
210 208 "65: Redact Skipped Impurities" "\n when\n Section((fileAttributeByLabelEqualsIgnoreCase(\"Redact Skipped Impurities\",\"Yes\") || fileAttributeByLabelEqualsIgnoreCase(\"Redact Skipped Impurities\",\"Y\")) && matchesType(\"skipped_impurities\"))\n then\n section.redact(\"skipped_impurities\", 65, \"Occasional Impurity found\", \"Article 63(2)(b) of Regulation (EC) No 1107/2009\");\n end" [ETC.9.0]
211 209 "19c: Recommend first line in table cell with name and address of owner" "\n when\n Section(searchText.toLowerCase().contains(\"trial site\") && hasTableHeader(\"Name and Address of Owner / Tenant\"))\n then\n section.redactCell(\"Name and Address of Owner / Tenant\",19,\"PII\",true,\"Trial Site owner and address found\",\"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [ETC.11.0]
212 210 "67: Redact Product Composition Information" "\n when\n Section(matchesType(\"product_composition\"))\n then\n section.redact(\"product_composition\",67, \"Product Composition Information found\",\"Article 63(2)(d) of Regulation (EC) No 1107/2009\");\n end" [ETC.10.0]
213 211 "25: Redact Phone and Fax by RegEx (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (\n text.contains(\"Contact\")\n || text.contains(\"Telephone\")\n || text.contains(\"Téléphone\")\n || text.contains(\"Phone\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n ))\n then\n section.redactByRegEx(\"\\\\b(contact|t\\\\p{Ll}l\\\\p{Ll}phone|phone|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s\\\\.]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(O][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.O]{4,100}\\\\d)\\\\b\", true, 2, \"PII\", 25, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.2.0]
214 212 "19a: recommend title prefixed words as PII" "\n when\n Section(\n searchText.contains(\"Dr \")\n || searchText.contains(\"PD Dr \")\n || searchText.contains(\"Prof. Dr \")\n || searchText.contains(\"Dr. med. vet \")\n || searchText.contains(\"Dr. rer. nat \")\n || searchText.contains(\"PhD \")\n || searchText.contains(\"BSc \")\n || searchText.contains(\"(FH) \")\n || searchText.contains(\"Mr \")\n || searchText.contains(\"Mrs \")\n || searchText.contains(\"Ms \")\n || searchText.contains(\"Miss \")\n || searchText.contains(\"Dr.\")\n || searchText.contains(\"PD Dr.\")\n || searchText.contains(\"Prof. Dr.\")\n || searchText.contains(\"Dr. med. vet.\")\n || searchText.contains(\"Dr. rer. nat.\")\n || searchText.contains(\"PhD.\")\n || searchText.contains(\"BSc.\")\n || searchText.contains(\"(FH).\")\n || searchText.contains(\"Mr.\")\n || searchText.contains(\"Mrs.\")\n || searchText.contains(\"Ms.\")\n || searchText.contains(\"Miss.\")\n )\n then\n section.addRecommendationByRegEx(\"((Dr|PD Dr|Prof. Dr|Dr. med. vet|Dr. rer. nat|PhD|BSc|\\\\(FH\\\\)|Mr|Mrs|Ms|Miss)[.\\\\s]{1,2})([\\\\p{Lu}][\\\\p{L}\\\\-.]{1,20}\\\\s[\\\\p{Lu}][\\\\p{L}\\\\-.]{1,20})\", false, 3, \"PII\");\n end" [PII.14.0]
215 213 "0: Combine address parts from ai to CBI_address (city is mandatory)" "\n when\n Section(aiMatchesType(\"CITY\"))\n then\n section.combineAiTypes(\"CITY\", \"ORG,DEPARTMENT,STREET,POSTAL,COUNTRY,CARDINAL,STATE\", 20, \"CBI_address\", 3, false);\n end" [AI.1.0]
216 214 "29: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toUpperCase().contains(\"AUTHOR(S):\")\n && searchText.toUpperCase().contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\", \"Study Completion Date:\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\", \"Study completion Date:\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"AUTHOR(S):\\n\", \"STUDY COMPLETION DATE:\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\\n\", \"Study Completion Date:\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\\n\", \"Study completion Date:\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
217 215 "22: Redact Emails by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@\\\\s?[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 22, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.1.1]
218 216 "27: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toUpperCase().contains(\"AUTHOR(S):\")\n && searchText.toUpperCase().contains(\"COMPLETION DATE:\")\n && !searchText.toUpperCase().contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\", \"Completion Date:\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"AUTHOR(S):\\n\", \"COMPLETION DATE:\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\\n\", \"Completion Date:\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
219 217 "21a: Redact typoed Emails with indicator" "\n when\n Section(searchText.contains(\"@\") || searchText.toLowerCase().contains(\"mail\"))\n then\n section.redactByRegEx(\"mail[:\\\\.\\\\s]{1,2}([\\\\w\\\\/\\\\-\\\\{\\\\(\\\\. ]{3,20 }(@|a|f)\\\\s?[\\\\w\\\\/\\\\-\\\\{\\\\(\\\\. ]{3,20}(\\\\. \\\\w{2,4}\\\\b|\\\\.\\\\B|\\\\.\\\\w{1,4}\\\\b))\",true,1,\"PII\",21,\"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.1.2]
220 218 "0: Combine address parts from ai to CBI_address (department is mandatory)" "\n when\n Section(aiMatchesType(\"DEPARTMENT\"))\n then\n section.combineAiTypes(\"DEPARTMENT\", \"ORG,STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\", 20, \"CBI_address\", 3, false);\n end" [AI.1.0]
221 219 "0: Combine address parts from ai to CBI_address (street is mandatory)" "\n when\n Section(aiMatchesType(\"STREET\"))\n then\n section.combineAiTypes(\"STREET\", \"ORG,DEPARTMENT,POSTAL,COUNTRY,CARDINAL,CITY,STATE\", 20, \"CBI_address\", 3, false);\n end" [AI.1.0]
222 220 "19b: Add recommendation for PII after Contact Person" "\n when\n Section(searchText.toLowerCase().contains(\"contact person:\"))\n then\n section.recommendLineAfter(\"Contact Person\", \"PII\");\n section.recommendLineAfter(\"Contact person\", \"PII\");\n section.recommendLineAfter(\"contact person\", \"PII\");\n section.recommendLineAfter(\"Contact Person:\", \"PII\");\n section.recommendLineAfter(\"Contact person:\", \"PII\");\n section.recommendLineAfter(\"contact person:\", \"PII\");\n end" [PII.13.0]
223 221 "0: Combine address parts from ai to CBI_address (org is mandatory)" "\n when\n Section(aiMatchesType(\"ORG\"))\n then\n section.combineAiTypes(\"ORG\", \"DEPARTMENT,STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE\", 20, \"CBI_address\", 3, false);\n end" [AI.1.0]
224 222 "25a: Redact phone numbers without indicators" "\n when\n Section(text.contains(\"+\"))\n then\n section.redactByRegEx(\"(\\\\+[\\\\dO]{1,2} )(\\\\([\\\\dO]{1,3}\\\\))?[\\\\d\\\\-O ]{8,15}\",false,0,\"PII\",25,\"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.2.2]
225 223 "34: Dossier" "\n when\n Section(matchesType(\"dossier_redaction\"))\n then\n section.redact(\"dossier_redaction\", 34, \"Dossier redaction found\", \"Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)\");\n end" [ETC.4.0, ETC.4.1, ETC.4.2]
226 224 "30: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toUpperCase().contains(\"AUTHOR(S):\")\n && searchText.toUpperCase().contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\", \"Study Completion Date:\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\", \"Study completion Date:\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"AUTHOR(S):\\n\", \"STUDY COMPLETION DATE:\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\\n\", \"Study Completion Date:\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\\n\", \"Study completion Date:\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
227 225 "30a: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toUpperCase().contains(\"AUTHOR(S)\")\n && !searchText.toUpperCase().contains(\"AUTHOR(S):\")\n && searchText.toUpperCase().contains(\"STUDY COMPLETION DATE\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S)\", \"STUDY COMPLETION DATE\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\", \"Study Completion Date\", \"CBI_author\", 30, true, \"Author(s) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\", \"Study completion Date\", \"CBI_author\", 30, true, \"Author(s) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"AUTHOR(S)\\n\", \"STUDY COMPLETION DATE\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\\n\", \"Study Completion Date\", \"CBI_author\", 30, true, \"Author(s) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\\n\", \"Study completion Date\", \"CBI_author\", 30, true, \"Author(s) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
228 226 "29a: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toUpperCase().contains(\"AUTHOR(S)\")\n && !searchText.toUpperCase().contains(\"AUTHOR(S):\")\n && searchText.toUpperCase().contains(\"STUDY COMPLETION DATE\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S)\", \"STUDY COMPLETION DATE\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\", \"Study Completion Date\", \"CBI_author\", 29, true, \"Author(s) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\", \"Study completion Date\", \"CBI_author\", 29, true, \"Author(s) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"AUTHOR(S)\\n\", \"STUDY COMPLETION DATE\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\\n\", \"Study Completion Date\", \"CBI_author\", 29, true, \"Author(s) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s)\\n\", \"Study completion Date\", \"CBI_author\", 29, true, \"Author(s) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
229 227 "28: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toUpperCase().contains(\"AUTHOR(S):\")\n && searchText.toUpperCase().contains(\"COMPLETION DATE:\")\n && !searchText.toUpperCase().contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"CBI_author\", 28, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\", \"Completion Date:\", \"CBI_author\", 28, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"AUTHOR(S):\\n\", \"COMPLETION DATE:\", \"CBI_author\", 28, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n section.redactLinesBetween(\"Author(s):\\n\", \"Completion Date:\", \"CBI_author\", 28, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
230 228 "30b: Redact short Authors section" "\n when\n Section(\n fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.toLowerCase().contains(\"author(s)\")\n && searchText.length() < 50\n && sectionNumber <= 20\n && !aiMatchesType(\"CBI_Author\")\n )\n then\n section.redactByRegEx(\"(?<=author\\\\(?s\\\\)?\\\\s\\\\n?)([\\\\p{Lu}\\\\p{L} ]{5,15}(,|\\\\n)?){1,3}\",true,0,\"CBI_author\",30,\"AUTHOR(S) was found\",\"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [CBI.21.1]
231 229 "26: Redact Phone and Fax by RegEx (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && (\n text.contains(\"Contact\")\n || text.contains(\"Telephone\")\n || text.contains(\"Téléphone\")\n || text.contains(\"Phone\")\n || text.contains(\"Fax\")\n || text.contains(\"Tel\")\n || text.contains(\"Ter\")\n || text.contains(\"Mobile\")\n || text.contains(\"Fel\")\n || text.contains(\"Fer\")\n ))\n then\n section.redactByRegEx(\"\\\\b(contact|t\\\\p{Ll}l\\\\p{Ll}phone|phone|fax|tel|ter|mobile|fel|fer)[a-zA-Z\\\\s]{0,10}[:.\\\\s]{0,3}([\\\\+\\\\d\\\\(O][\\\\s\\\\d\\\\(\\\\)\\\\-\\\\/\\\\.O]{4,100}\\\\d)\\\\b\", true, 2, \"PII\", 26, \"Personal information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.2.1]
232 230 "60: Redact Must Impurities" "\n when\n Section((fileAttributeByLabelEqualsIgnoreCase(\"Redact Impurities\",\"Yes\") || fileAttributeByLabelEqualsIgnoreCase(\"Redact Impurities\",\"Y\")) && matchesType(\"impurities\"))\n then\n section.redact(\"impurities\", 60, \"Impurity found\", \"Article 63(2)(b) of Regulation (EC) No 1107/2009\");\n end" [ETC.9.1]
233 231 "21: Redact Emails by RegEx (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && searchText.contains(\"@\"))\n then\n section.redactByRegEx(\"\\\\b([A-Za-z0-9._%+\\\\-]+@\\\\s?[A-Za-z0-9.\\\\-]+\\\\.[A-Za-z\\\\-]{1,23}[A-Za-z])\\\\b\", true, 1, \"PII\", 21, \"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.2.0]
234 232 "21a: Redact typoed Emails with indicator" "\n when\n Section(searchText.contains(\"@\") || searchText.toLowerCase().contains(\"mail\"))\n then\n section.redactByRegEx(\"mail[:\\\\.\\\\s]{1,2}([\\\\w\\\\/\\\\-\\\\{\\\\(\\\\. ]{3,20}(@|a|f)\\\\s?[\\\\w\\\\/\\\\-\\\\{\\\\(\\\\. ]{3,20}(\\\\. \\\\w{2,4}\\\\b|\\\\.\\\\B|\\\\.\\\\w{1,4}\\\\b))\",true,1,\"PII\",21,\"Personal information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.1.2]
235 233 "29: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"CBI_author\", 29, true, \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
236 234 "28: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"COMPLETION DATE:\")\n && !searchText.contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"CBI_author\", 28, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
237 235 "30: Redact AUTHOR(S) (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"CBI_author\", 30, true, \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [PII.9.1]
238 236 "27: Redact AUTHOR(S) (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\")\n && searchText.contains(\"AUTHOR(S):\")\n && searchText.contains(\"COMPLETION DATE:\")\n && !searchText.contains(\"STUDY COMPLETION DATE:\")\n )\n then\n section.redactLinesBetween(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"CBI_author\", 27, true, \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [PII.9.0]
239 237 "34a: Redact dossier_redaction (Non vertebrate study)" "\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"dossier_redaction\"))\n then\n section.redact(\"dossier_redaction\", 34, \"Dossier dictionary entry found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end" [ETC.12.0]
240 238 "34b: Redact dossier_redaction (Vertebrate study)" "\n when\n Section(fileAttributeByLabelEqualsIgnoreCase(\"Vertebrate Study\",\"Yes\") && matchesType(\"dossier_redaction\"))\n then\n section.redact(\"dossier_redaction\", 34, \"Dossier dictionary entry found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end" [ETC.12.1]

Some files were not shown because too many files have changed in this diff Show More