1088 lines
53 KiB
Plaintext
1088 lines
53 KiB
Plaintext
package drools
|
|
|
|
import static java.lang.String.format;
|
|
import static com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.RedactionSearchUtility.anyMatch;
|
|
import static com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.RedactionSearchUtility.exactMatch;
|
|
|
|
import java.util.List;
|
|
import java.util.LinkedList;
|
|
import java.util.Set;
|
|
import java.util.stream.Collectors;
|
|
import java.util.Collection;
|
|
import java.util.stream.Stream;
|
|
import java.util.Optional;
|
|
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.*;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.*;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Section;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Table;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.SemanticNode;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Document;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Paragraph;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Image;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.entity.*;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.textblock.*;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.entity.EntityType;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.ImageType;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Engine;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.services.EntityCreationService;
|
|
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.Dictionary;
|
|
import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.DictionaryModel;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualImageRecategorization;
|
|
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.services.ManualRedactionApplicationService;
|
|
import com.iqser.red.service.redaction.v1.server.client.model.EntityRecognitionEntity;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.Boundary;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.entity.RedactionEntity;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.Boundary;
|
|
import com.iqser.red.service.redaction.v1.server.redaction.adapter.NerEntities;
|
|
import com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.RedactionSearchUtility;
|
|
|
|
global Document document
|
|
global EntityCreationService entityCreationService
|
|
global ManualRedactionApplicationService manualRedactionApplicationService
|
|
global Dictionary dictionary
|
|
|
|
//------------------------------------ queries ------------------------------------
|
|
|
|
query "getFileAttributes"
|
|
$fileAttribute: FileAttribute()
|
|
end
|
|
|
|
//------------------------------------ Syngenta specific rules ------------------------------------
|
|
|
|
// Rule unit: SYN.0
|
|
rule "SYN.0.0: Redact if CTL/* or BL/* was found (Non Vertebrate Study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(containsString("CTL/") || containsString("BL/"))
|
|
then
|
|
Stream.concat(
|
|
entityCreationService.byString("CTL", "must_redact", EntityType.ENTITY, $section),
|
|
entityCreationService.byString("BL", "must_redact", EntityType.ENTITY, $section)
|
|
).forEach(entity -> entity.skip("SYN.0.0", "hint_only"));
|
|
end
|
|
|
|
|
|
//------------------------------------ CBI rules ------------------------------------
|
|
|
|
// Rule unit: CBI.3
|
|
rule "CBI.3.0: Redacted because Section contains Vertebrate"
|
|
when
|
|
$section: Section(!hasTables(), hasEntitiesOfType("vertebrate"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$section.getEntitiesOfType(List.of("CBI_author", "CBI_address"))
|
|
.forEach(entity -> {
|
|
entity.applyWithReferences(
|
|
"CBI.3.0",
|
|
"Vertebrate found",
|
|
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
|
$section.getEntitiesOfType("vertebrate")
|
|
);
|
|
});
|
|
end
|
|
|
|
rule "CBI.3.1: Redacted because Table Row contains Vertebrate"
|
|
when
|
|
$table: Table(hasEntitiesOfType("vertebrate"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate"))
|
|
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
|
.forEach(entity -> {
|
|
entity.applyWithReferences(
|
|
"CBI.3.1",
|
|
"Vertebrate found",
|
|
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
|
$table.getEntitiesOfTypeInSameRow("vertebrate", entity)
|
|
);
|
|
});
|
|
end
|
|
|
|
rule "CBI.3.2: Don't redact because Section doesn't contain Vertebrate"
|
|
when
|
|
$section: Section(!hasTables(), !hasEntitiesOfType("vertebrate"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$section.getEntitiesOfType(List.of("CBI_author", "CBI_address"))
|
|
.forEach(entity -> entity.skip("CBI.3.2", "No vertebrate found"));
|
|
end
|
|
|
|
rule "CBI.3.3: Dont redact because Table Row doesn't contain Vertebrate"
|
|
when
|
|
$table: Table(hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))
|
|
then
|
|
$table.streamEntitiesWhereRowContainsNoEntitiesOfType(List.of("vertebrate"))
|
|
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
|
.forEach(entity -> entity.skip("CBI.3.3", "No vertebrate found"));
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.4
|
|
rule "CBI.4.0: Dont redact Names and Addresses if no_redaction_indicator is found in Section"
|
|
when
|
|
$section: Section(!hasTables(),
|
|
hasEntitiesOfType("vertebrate"),
|
|
hasEntitiesOfType("no_redaction_indicator"),
|
|
(hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$section.getEntitiesOfType(List.of("CBI_author", "CBI_address"))
|
|
.forEach(entity -> {
|
|
entity.skipWithReferences(
|
|
"CBI.4.0",
|
|
"Vertebrate but a no redaction indicator found",
|
|
$section.getEntitiesOfType("no_redaction_indicator")
|
|
);
|
|
});
|
|
end
|
|
|
|
rule "CBI.4.1: Dont redact Names and Addresses if no_redaction_indicator is found in Table Row"
|
|
when
|
|
$table: Table(hasEntitiesOfType("no_redaction_indicator"),
|
|
hasEntitiesOfType("vertebrate"),
|
|
(hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate", "no-redaction_indicator"))
|
|
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
|
.forEach(entity -> {
|
|
entity.skipWithReferences(
|
|
"CBI.4.1",
|
|
"Vertebrate but a no redaction indicator found",
|
|
Stream.concat(
|
|
$table.getEntitiesOfTypeInSameRow("vertebrate", entity).stream(),
|
|
$table.getEntitiesOfTypeInSameRow("no_redaction_indicator", entity).stream()).toList()
|
|
);
|
|
});
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.5
|
|
rule "CBI.5.0: Redact Names and Addresses if no_redaction_indicator but also redaction_indicator is found in section"
|
|
when
|
|
$section: Section(!hasTables(),
|
|
hasEntitiesOfType("redaction_indicator"),
|
|
hasEntitiesOfType("no_redaction_indicator"),
|
|
(hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$section.getEntitiesOfType(List.of("CBI_author", "CBI_address"))
|
|
.forEach(entity -> {
|
|
entity.applyWithReferences(
|
|
"CBI.5.0",
|
|
"no_redaction_indicator but also redaction_indicator found",
|
|
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
|
Stream.concat(
|
|
$section.getEntitiesOfType("vertebrate").stream(),
|
|
$section.getEntitiesOfType("no_redaction_indicator").stream()).toList()
|
|
);
|
|
});
|
|
end
|
|
|
|
rule "CBI.5.1: Redact Names and Addresses if no_redaction_indicator but also redaction_indicator is found in Table Row"
|
|
when
|
|
$table: Table(hasEntitiesOfType("no_redaction_indicator"),
|
|
hasEntitiesOfType("redaction_indicator"),
|
|
(hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("redaction_indicator", "no_redaction_indicator"))
|
|
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
|
.forEach(entity -> {
|
|
entity.applyWithReferences(
|
|
"CBI.5.1",
|
|
"no_redaction_indicator but also redaction_indicator found",
|
|
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
|
Stream.concat(
|
|
$table.getEntitiesOfTypeInSameRow("vertebrate", entity).stream(),
|
|
$table.getEntitiesOfTypeInSameRow("no_redaction_indicator", entity).stream()).toList()
|
|
);
|
|
});
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.8
|
|
rule "CBI.8.0: Redacted because Section contains must_redact entity"
|
|
when
|
|
$section: Section(!hasTables(), hasEntitiesOfType("must_redact"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$section.getEntitiesOfType(List.of("CBI_author", "CBI_address"))
|
|
.forEach(entity -> {
|
|
entity.applyWithReferences(
|
|
"CBI.8.0",
|
|
"must_redact entity found",
|
|
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
|
$section.getEntitiesOfType("must_redact")
|
|
);
|
|
});
|
|
end
|
|
|
|
rule "CBI.8.1: Redacted because Table Row contains must_redact entity"
|
|
when
|
|
$table: Table(hasEntitiesOfType("must_redact"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")))
|
|
then
|
|
$table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("must_redact"))
|
|
.filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address"))
|
|
.forEach(entity -> {
|
|
entity.applyWithReferences(
|
|
"CBI.8.1",
|
|
"must_redact entity found",
|
|
"Reg (EC) No 1107/2009 Art. 63 (2g)",
|
|
$table.getEntitiesOfTypeInSameRow("must_redact", entity)
|
|
);
|
|
});
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.9
|
|
rule "CBI.9.0: Redact all Cell's with Header Author(s) as CBI_author (non vertebrate study)"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$table: Table(hasHeader("Author(s)"))
|
|
then
|
|
$table.streamTableCellsWithHeader("Author(s)")
|
|
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
|
.filter(Optional::isPresent)
|
|
.map(Optional::get)
|
|
.forEach(redactionEntity -> redactionEntity.apply("CBI.9.0", "Author(s) found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
rule "CBI.9.1: Redact all Cell's with Header Author as CBI_author (non vertebrate study)"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$table: Table(hasHeader("Author"))
|
|
then
|
|
$table.streamTableCellsWithHeader("Author")
|
|
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
|
.filter(Optional::isPresent)
|
|
.map(Optional::get)
|
|
.forEach(redactionEntity -> redactionEntity.apply("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.11
|
|
rule "CBI.11.0: Recommend all CBI_author entities in Table with Vertebrate Study Y/N Header"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
salience -1
|
|
when
|
|
$table: Table(hasHeader("Author(s)") && hasHeader("Vertebrate Study Y/N"))
|
|
then
|
|
$table.getEntitiesOfType("CBI_author").forEach(entity -> dictionary.addMultipleAuthorsAsRecommendation(entity));
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.12
|
|
rule "CBI.12.0: Add all Cell's with Header Author(s) as CBI_author"
|
|
salience 1
|
|
when
|
|
$table: Table(hasHeader("Author(s)") || hasHeader("Author"))
|
|
then
|
|
Stream.concat(
|
|
$table.streamTableCellsWithHeader("Author(s)"),
|
|
$table.streamTableCellsWithHeader("Author")
|
|
)
|
|
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "CBI_author", EntityType.ENTITY))
|
|
.filter(Optional::isPresent)
|
|
.map(Optional::get)
|
|
.forEach(redactionEntity -> redactionEntity.skip("CBI.12.0", "Author(s) header found"));
|
|
end
|
|
|
|
rule "CBI.12.1: Dont redact CBI_author, if its row contains a cell with header \"Vertebrate study Y/N\" and value No"
|
|
when
|
|
$table: Table(hasRowWithHeaderAndValue("Vertebrate study Y/N", "N") || hasRowWithHeaderAndValue("Vertebrate study Y/N", "No"))
|
|
then
|
|
$table.streamEntitiesWhereRowHasHeaderAndAnyValue("Vertebrate study Y/N", List.of("N", "No"))
|
|
.filter(redactionEntity -> redactionEntity.isAnyType(List.of("CBI_author", "CBI_address")))
|
|
.forEach(authorEntity -> authorEntity.skip("CBI.12.1", "Not redacted because it's row does not belong to a vertebrate study"));
|
|
end
|
|
|
|
rule "CBI.12.2: Redact CBI_author, if its row contains a cell with header \"Vertebrate study Y/N\" and value Yes"
|
|
when
|
|
$table: Table(hasRowWithHeaderAndValue("Vertebrate study Y/N", "Y") || hasRowWithHeaderAndValue("Vertebrate study Y/N", "Yes"))
|
|
then
|
|
$table.streamEntitiesWhereRowHasHeaderAndAnyValue("Vertebrate study Y/N", List.of("Y", "Yes"))
|
|
.filter(redactionEntity -> redactionEntity.isAnyType(List.of("CBI_author", "CBI_address")))
|
|
.forEach(authorEntity -> authorEntity.apply("CBI.12.2", "Redacted because it's row belongs to a vertebrate study", "Reg (EC) No 1107/2009 Art. 63 (2g)"));
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.14
|
|
rule "CBI.14.0: Redact CBI_sponsor entities if preceded by \"batches produced at\""
|
|
when
|
|
$sponsorEntity: RedactionEntity(type == "CBI_sponsor", textBefore.contains("batches produced at"))
|
|
then
|
|
$sponsorEntity.apply("CBI.14.0", "Redacted because it represents a sponsor company", "Reg (EC) No 1107/2009 Art. 63 (2g)");
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.15
|
|
rule "CBI.15.0: Redact row if row contains \"determination of residues\" and livestock keyword"
|
|
when
|
|
$keyword: String() from List.of("livestock",
|
|
"live stock",
|
|
"tissue",
|
|
"tissues",
|
|
"liver",
|
|
"muscle",
|
|
"bovine",
|
|
"ruminant",
|
|
"ruminants")
|
|
$residueKeyword: String() from List.of("determination of residues", "determination of total residues")
|
|
$section: Section(!hasTables(),
|
|
containsStringIgnoreCase($residueKeyword),
|
|
containsStringIgnoreCase($keyword))
|
|
then
|
|
entityCreationService.byString($keyword, "must_redact", EntityType.ENTITY, $section)
|
|
.toList();
|
|
|
|
$section.getEntitiesOfType(List.of($keyword, $residueKeyword))
|
|
.forEach(redactionEntity -> redactionEntity.apply("CBI.15.0", "Determination of residues and keyword \"" + $keyword + "\" was found.", "Reg (EC) No 1107/2009 Art. 63 (2g)"));
|
|
end
|
|
|
|
rule "CBI.15.1: Redact CBI_author and CBI_address if row contains \"determination of residues\" and livestock keyword"
|
|
when
|
|
$keyword: String() from List.of("livestock",
|
|
"live stock",
|
|
"tissue",
|
|
"tissues",
|
|
"liver",
|
|
"muscle",
|
|
"bovine",
|
|
"ruminant",
|
|
"ruminants")
|
|
$residueKeyword: String() from List.of("determination of residues", "determination of total residues")
|
|
$table: Table(containsStringIgnoreCase($residueKeyword), containsStringIgnoreCase($keyword))
|
|
then
|
|
entityCreationService.byString($keyword, "must_redact", EntityType.ENTITY, $table)
|
|
.toList();
|
|
|
|
$table.streamEntitiesWhereRowContainsStringsIgnoreCase(List.of($keyword, $residueKeyword))
|
|
.filter(redactionEntity -> redactionEntity.isAnyType(List.of("CBI_author", "CBI_address")))
|
|
.forEach(redactionEntity -> redactionEntity.apply("CBI.15.1", "Determination of residues and keyword \"" + $keyword + "\" was found.", "Reg (EC) No 1107/2009 Art. 63 (2g)"));
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.16
|
|
rule "CBI.16.0: Add CBI_author with \"et al.\" Regex (non vertebrate study)"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(containsString("et al."))
|
|
then
|
|
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
|
.forEach(entity -> {
|
|
entity.apply("CBI.16.0", "Author found by \"et al\" regex", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
|
dictionary.recommendEverywhere(entity);
|
|
});
|
|
end
|
|
|
|
rule "CBI.16.1: Add CBI_author with \"et al.\" Regex (vertebrate study)"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(containsString("et al."))
|
|
then
|
|
entityCreationService.byRegex("\\b([A-ZÄÖÜ][^\\s\\.,]+( [A-ZÄÖÜ]{1,2}\\.?)?( ?[A-ZÄÖÜ]\\.?)?) et al\\.?", "CBI_author", EntityType.ENTITY, 1, $section)
|
|
.forEach(entity -> {
|
|
entity.apply("CBI.16.1", "Author found by \"et al\" regex", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
|
dictionary.recommendEverywhere(entity);
|
|
});
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.17
|
|
rule "CBI.17.0: Add recommendation for Addresses in Test Organism sections, without colon"
|
|
when
|
|
$section: Section(!hasTables(), containsString("Species") && containsString("Source") && !containsString("Species:") && !containsString("Source:"))
|
|
then
|
|
entityCreationService.lineAfterString("Source", "CBI_address", EntityType.RECOMMENDATION, $section)
|
|
.forEach(entity -> entity.skip("CBI.17.0", "Line after \"Source\" in Test Organism Section"));
|
|
end
|
|
|
|
rule "CBI.17.1: Add recommendation for Addresses in Test Organism sections, with colon"
|
|
when
|
|
$section: Section(!hasTables(), containsString("Species:"), containsString("Source:"))
|
|
then
|
|
entityCreationService.lineAfterString("Source:", "CBI_address", EntityType.RECOMMENDATION, $section)
|
|
.forEach(entity -> entity.skip("CBI.17.1", "Line after \"Source:\" in Test Animals Section"));
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.18
|
|
rule "CBI.18.0: Expand CBI_author entities with firstname initials"
|
|
no-loop true
|
|
when
|
|
$entityToExpand: RedactionEntity(type == "CBI_author",
|
|
value.matches("[^\\s]+"),
|
|
textAfter.startsWith(" "),
|
|
anyMatch(textAfter, "(,? [A-Z]\\.?( ?[A-Z]\\.?)?( ?[A-Z]\\.?)?\\b\\.?)")
|
|
)
|
|
then
|
|
entityCreationService.bySuffixExpansionRegex($entityToExpand, "(,? [A-Z]\\.?( ?[A-Z]\\.?)?( ?[A-Z]\\.?)?\\b\\.?)")
|
|
.ifPresent(expandedEntity -> {
|
|
expandedEntity.setMatchedRuleList($entityToExpand.getMatchedRuleList());
|
|
$entityToExpand.remove("CBI.18.0", "Expand CBI_author entities with firstname initials");
|
|
retract($entityToExpand);
|
|
});
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.19
|
|
rule "CBI.19.0: Expand CBI_author entities with salutation prefix"
|
|
when
|
|
$entityToExpand: RedactionEntity(type == "CBI_author", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
|
|
then
|
|
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
|
|
.ifPresent(expandedEntity -> {
|
|
expandedEntity.setMatchedRuleList($entityToExpand.getMatchedRuleList());
|
|
$entityToExpand.remove("CBI.19.0", "Expand CBI_author entities with salutation prefix");
|
|
retract($entityToExpand);
|
|
});
|
|
end
|
|
|
|
|
|
// Rule unit: CBI.20
|
|
rule "CBI.20.0: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (non vertebrate study)"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value == "Yes")
|
|
$section: Section(!hasTables(), containsString("PERFORMING LABORATORY:"), containsString("LABORATORY PROJECT ID:"))
|
|
then
|
|
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
|
.forEach(laboratoryEntity -> {
|
|
laboratoryEntity.skip("CBI.20.0", "PERFORMING LABORATORY was found for non vertebrate study");
|
|
dictionary.recommendEverywhere(laboratoryEntity);
|
|
});
|
|
end
|
|
|
|
rule "CBI.20.1: Redact between \"PERFORMING LABORATORY\" and \"LABORATORY PROJECT ID:\" (vertebrate study)"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value == "Yes")
|
|
$section: Section(!hasTables(), containsString("PERFORMING LABORATORY:"), containsString("LABORATORY PROJECT ID:"))
|
|
then
|
|
entityCreationService.betweenStrings("PERFORMING LABORATORY:", "LABORATORY PROJECT ID:", "CBI_address", EntityType.ENTITY, $section)
|
|
.forEach(laboratoryEntity -> {
|
|
laboratoryEntity.apply("CBI.20.1", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
|
dictionary.recommendEverywhere(laboratoryEntity);
|
|
});
|
|
end
|
|
|
|
|
|
//------------------------------------ PII rules ------------------------------------
|
|
|
|
// Rule unit: PII.0
|
|
rule "PII.0.0: Redact all PII (non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$pii: RedactionEntity(type == "PII", dictionaryEntry)
|
|
then
|
|
$pii.apply("PII.0.0", "Personal Information found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
rule "PII.0.1: Redact all PII (vertebrate study)"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$pii: RedactionEntity(type == "PII", dictionaryEntry)
|
|
then
|
|
$pii.apply("PII.0.1", "Personal Information found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
|
|
// Rule unit: PII.1
|
|
rule "PII.1.0: Redact Emails by RegEx (Non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(containsString("@"))
|
|
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)
|
|
.forEach(emailEntity -> emailEntity.apply("PII.1.0", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
rule "PII.1.1: Redact Emails by RegEx (vertebrate study)"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(containsString("@"))
|
|
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)
|
|
.forEach(emailEntity -> emailEntity.apply("PII.1.1", "Found by Email Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
|
|
// Rule unit: PII.4
|
|
rule "PII.4.0: Redact line after contact information keywords (non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$contactKeyword: String() from List.of("Contact point:",
|
|
"Contact:",
|
|
"Alternative contact:",
|
|
"European contact:",
|
|
"No:",
|
|
"Contact:",
|
|
"Tel.:",
|
|
"Tel:",
|
|
"Telephone number:",
|
|
"Telephone No:",
|
|
"Telephone:",
|
|
"Phone No.",
|
|
"Phone:",
|
|
"Fax number:",
|
|
"Fax:",
|
|
"E-mail:",
|
|
"Email:",
|
|
"e-mail:",
|
|
"E-mail address:")
|
|
$section: Section(containsString($contactKeyword))
|
|
then
|
|
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
|
.forEach(contactEntity -> contactEntity.apply("PII.4.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
|
end
|
|
|
|
rule "PII.4.1: Redact line after contact information keywords (non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$contactKeyword: String() from List.of("Contact point:",
|
|
"Contact:",
|
|
"Alternative contact:",
|
|
"European contact:",
|
|
"No:",
|
|
"Contact:",
|
|
"Tel.:",
|
|
"Tel:",
|
|
"Telephone number:",
|
|
"Telephone No:",
|
|
"Telephone:",
|
|
"Phone No.",
|
|
"Phone:",
|
|
"Fax number:",
|
|
"Fax:",
|
|
"E-mail:",
|
|
"Email:",
|
|
"e-mail:",
|
|
"E-mail address:")
|
|
$section: Section(containsString($contactKeyword))
|
|
then
|
|
entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section)
|
|
.forEach(contactEntity -> contactEntity.apply("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
|
end
|
|
|
|
|
|
// Rule unit: PII.6
|
|
rule "PII.6.0: redact line between contact keywords (non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
|
then
|
|
Stream.concat(
|
|
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
|
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
|
)
|
|
.forEach(contactEntity -> contactEntity.apply("PII.6.0", "Found between contact keywords", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
rule "PII.6.1: redact line between contact keywords"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section((containsString("No:") && containsString("Fax")) || (containsString("Contact:") && containsString("Tel")))
|
|
then
|
|
Stream.concat(
|
|
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
|
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
|
)
|
|
.forEach(contactEntity -> contactEntity.apply("PII.6.1", "Found between contact keywords", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
|
|
// Rule unit: PII.7
|
|
rule "PII.7.0: Redact contact information if applicant is found (non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(getHeadline().containsString("applicant") ||
|
|
getHeadline().containsString("Primary contact") ||
|
|
getHeadline().containsString("Alternative contact") ||
|
|
containsString("Applicant") ||
|
|
containsString("Telephone number:"))
|
|
then
|
|
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "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),
|
|
Stream.concat(
|
|
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
|
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
|
))
|
|
.forEach(entity -> entity.apply("PII.7.0", "Applicant information was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
rule "PII.7.1: Redact contact information if applicant is found (non vertebrate study)"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(getHeadline().containsString("applicant") ||
|
|
getHeadline().containsString("Primary contact") ||
|
|
getHeadline().containsString("Alternative contact") ||
|
|
containsString("Applicant") ||
|
|
containsString("Telephone number:"))
|
|
then
|
|
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "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),
|
|
Stream.concat(
|
|
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
|
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
|
))
|
|
.forEach(entity -> entity.apply("PII.7.1", "Applicant information was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
|
|
// Rule unit: PII.8
|
|
rule "PII.8.0: Redact contact information if producer is found"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
|
containsStringIgnoreCase("producer of the active substance") ||
|
|
containsStringIgnoreCase("manufacturer of the active substance") ||
|
|
containsStringIgnoreCase("manufacturer:") ||
|
|
containsStringIgnoreCase("Producer or producers of the active substance"))
|
|
then
|
|
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "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),
|
|
Stream.concat(
|
|
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
|
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
|
))
|
|
.forEach(entity -> entity.apply("PII.8.0", "Producer was found", "Reg (EC) No 1107/2009 Art. 63 (2e)"));
|
|
end
|
|
|
|
rule "PII.8.1: Redact contact information if producer is found"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(containsStringIgnoreCase("producer of the plant protection") ||
|
|
containsStringIgnoreCase("producer of the active substance") ||
|
|
containsStringIgnoreCase("manufacturer of the active substance") ||
|
|
containsStringIgnoreCase("manufacturer:") ||
|
|
containsStringIgnoreCase("Producer or producers of the active substance"))
|
|
then
|
|
Stream.concat(entityCreationService.lineAfterStrings(List.of("Contact point:", "Contact:", "Alternative contact:", "European contact:", "No:", "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),
|
|
Stream.concat(
|
|
entityCreationService.betweenStrings("No:", "Fax", "PII", EntityType.ENTITY, $section),
|
|
entityCreationService.betweenStrings("Contact:", "Tel", "PII", EntityType.ENTITY, $section)
|
|
))
|
|
.forEach(entity -> entity.apply("PII.8.1", "Producer was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
|
|
// Rule unit: PII.9
|
|
rule "PII.9.0: Redact between \"AUTHOR(S)\" and \"COMPLETION DATE\" (non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(!hasTables(), containsString("AUTHOR(S):"), containsString("COMPLETION DATE:"), !containsString("STUDY COMPLETION DATE:"))
|
|
then
|
|
entityCreationService.betweenStrings("AUTHOR(S):", "COMPLETION DATE:", "PII", EntityType.ENTITY, $section)
|
|
.forEach(authorEntity -> authorEntity.apply("PII.9.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
rule "PII.9.1: Redact between \"AUTHOR(S)\" and \"STUDY COMPLETION DATE\" (non vertebrate study)"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(!hasTables(), containsString("AUTHOR(S):"), containsString("COMPLETION DATE:"), !containsString("STUDY COMPLETION DATE:"))
|
|
then
|
|
entityCreationService.betweenStrings("AUTHOR(S):", "COMPLETION DATE:", "PII", EntityType.ENTITY, $section)
|
|
.forEach(authorEntity -> authorEntity.apply("PII.9.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
rule "PII.9.2: Redact between \"AUTHOR(S)\" and \"COMPLETION DATE\" (non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(!hasTables(), containsString("AUTHOR(S):"), containsString("STUDY COMPLETION DATE:"))
|
|
then
|
|
entityCreationService.betweenStrings("AUTHOR(S):", "STUDY COMPLETION DATE:", "PII", EntityType.ENTITY, $section)
|
|
.forEach(authorEntity -> authorEntity.apply("PII.9.2", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
rule "PII.9.3: Redact between \"AUTHOR(S)\" and \"STUDY COMPLETION DATE\" (vertebrate study)"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes")
|
|
$section: Section(!hasTables(), containsString("AUTHOR(S):"), containsString("STUDY COMPLETION DATE:"))
|
|
then
|
|
entityCreationService.betweenStrings("AUTHOR(S):", "STUDY COMPLETION DATE:", "PII", EntityType.ENTITY, $section)
|
|
.forEach(authorEntity -> authorEntity.apply("PII.9.3", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
|
|
// Rule unit: PII.11
|
|
rule "PII.11.0: Redact On behalf of Sequani Ltd.:"
|
|
when
|
|
$section: Section(!hasTables(), containsString("On behalf of Sequani Ltd.: Name Title"))
|
|
then
|
|
entityCreationService.betweenStrings("On behalf of Sequani Ltd.: Name Title", "On behalf of", "PII", EntityType.ENTITY, $section)
|
|
.forEach(authorEntity -> authorEntity.apply("PII.11.0", "On behalf of Sequani Ltd.: Name Title was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"));
|
|
end
|
|
|
|
|
|
// Rule unit: PII.12
|
|
rule "PII.12.0: Expand PII entities with salutation prefix"
|
|
when
|
|
$entityToExpand: RedactionEntity(type == "PII", anyMatch(textBefore, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*"))
|
|
then
|
|
entityCreationService.byPrefixExpansionRegex($entityToExpand, "\\b(Mrs?|Ms|Miss|Sir|Madame?|Mme)\\s?\\.?\\s*")
|
|
.ifPresent(expandedEntity -> expandedEntity.setMatchedRuleList($entityToExpand.getMatchedRuleList()));
|
|
end
|
|
|
|
|
|
//------------------------------------ Other rules ------------------------------------
|
|
|
|
// Rule unit: ETC.1
|
|
rule "ETC.1.0: Redact Purity"
|
|
when
|
|
$section: Section(containsStringIgnoreCase("purity"))
|
|
then
|
|
entityCreationService.byRegex("\\bPurity:\\s*(<?>?\\s*\\d{1,2}(?:\\.\\d{1,2})?\\s*%)", "purity", EntityType.ENTITY, 1, $section)
|
|
.forEach(entity -> entity.apply("ETC.1.0", "Purity found", "Reg (EC) No 1107/2009 Art. 63 (2a)"));
|
|
end
|
|
|
|
|
|
// Rule unit: ETC.2
|
|
rule "ETC.2.0: Redact signatures (non vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value == "Yes")
|
|
$signature: Image(imageType == ImageType.SIGNATURE)
|
|
then
|
|
$signature.apply("ETC.2.0", "Signature Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
rule "ETC.2.1: Redact signatures (vertebrate study)"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value == "Yes")
|
|
$signature: Image(imageType == ImageType.SIGNATURE)
|
|
then
|
|
$signature.apply("ETC.2.0", "Signature Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
|
|
// Rule unit: ETC.3
|
|
rule "ETC.3.0: Redact logos (vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value == "Yes")
|
|
$logo: Image(imageType == ImageType.LOGO)
|
|
then
|
|
$logo.apply("ETC.3.0", "Logo Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
rule "ETC.3.1: Redact logos (non vertebrate study)"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value == "Yes")
|
|
$logo: Image(imageType == ImageType.LOGO)
|
|
then
|
|
$logo.apply("ETC.3.1", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
|
|
// Rule unit: ETC.4
|
|
rule "ETC.4.0: Redact dossier dictionary entries"
|
|
when
|
|
$dossierRedaction: RedactionEntity(type == "dossier_redaction")
|
|
then
|
|
$dossierRedaction.apply("ETC.4.0", "Specification of impurity found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
|
|
// Rule unit: ETC.5
|
|
rule "ETC.5.0: Ignore dossier_redaction entries if confidentiality is not 'confidential'"
|
|
when
|
|
not FileAttribute(label == "Confidentiality", value == "confidential")
|
|
$dossierRedaction: RedactionEntity(type == "dossier_redaction")
|
|
then
|
|
$dossierRedaction.ignore("ETC.5.0", "Ignore dossier redactions, when not confidential");
|
|
update($dossierRedaction);
|
|
$dossierRedaction.getIntersectingNodes().forEach(node -> update(node));
|
|
end
|
|
|
|
|
|
// Rule unit: ETC.6
|
|
rule "ETC.6.0: Redact CAS Number"
|
|
when
|
|
$table: Table(hasHeader("Sample #"))
|
|
then
|
|
$table.streamTableCellsWithHeader("Sample #")
|
|
.map(tableCell -> entityCreationService.bySemanticNode(tableCell, "PII", EntityType.ENTITY))
|
|
.filter(Optional::isPresent)
|
|
.map(Optional::get)
|
|
.forEach(redactionEntity -> redactionEntity.apply("ETC.6.0", "Sample # found in Header", "Reg (EC) No 1107/2009 Art. 63 (2g)"));
|
|
end
|
|
|
|
|
|
// Rule unit: ETC.7
|
|
rule "ETC.7.0: Guidelines FileAttributes"
|
|
when
|
|
$section: Section(!hasTables(), (containsString("DATA REQUIREMENT(S):") || containsString("TEST GUIDELINE(S):")) && (containsString("OECD") || containsString("EPA") || containsString("OPPTS")))
|
|
then
|
|
RedactionSearchUtility.findBoundariesByRegex("OECD (No\\.? )?\\d{3}( \\(\\d{4}\\))?", $section.getTextBlock()).stream()
|
|
.map(boundary -> $section.getTextBlock().subSequence(boundary).toString())
|
|
.map(value -> FileAttribute.builder().label("OECD Number").value(value).build())
|
|
.forEach(fileAttribute -> insert(fileAttribute));
|
|
end
|
|
|
|
|
|
// Rule unit: ETC.8
|
|
rule "ETC.8.0: Redact formulas (vertebrate study)"
|
|
when
|
|
not FileAttribute(label == "Vertebrate Study", value == "Yes")
|
|
$logo: Image(imageType == ImageType.FORMULA)
|
|
then
|
|
$logo.apply("ETC.8.0", "Logo Found", "Article 39(e)(3) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
rule "ETC.8.1: Redact formulas (non vertebrate study)"
|
|
when
|
|
FileAttribute(label == "Vertebrate Study", value == "Yes")
|
|
$logo: Image(imageType == ImageType.FORMULA)
|
|
then
|
|
$logo.apply("ETC.8.1", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002");
|
|
end
|
|
|
|
|
|
//------------------------------------ AI rules ------------------------------------
|
|
|
|
// Rule unit: AI.0
|
|
rule "AI.0.0: add all NER Entities of type CBI_author"
|
|
salience 999
|
|
when
|
|
nerEntities: NerEntities(hasEntitiesOfType("CBI_author"))
|
|
then
|
|
nerEntities.streamEntitiesOfType("CBI_author")
|
|
.forEach(nerEntity -> entityCreationService.byNerEntity(nerEntity, EntityType.RECOMMENDATION, document));
|
|
end
|
|
|
|
|
|
// Rule unit: AI.1
|
|
rule "AI.1.0: combine and add NER Entities as CBI_address"
|
|
salience 999
|
|
when
|
|
nerEntities: NerEntities(hasEntitiesOfType("ORG") || hasEntitiesOfType("STREET") || hasEntitiesOfType("CITY"))
|
|
then
|
|
entityCreationService.combineNerEntitiesToCbiAddressDefaults(nerEntities, "CBI_address", EntityType.RECOMMENDATION, document).toList();
|
|
end
|
|
|
|
|
|
//------------------------------------ Manual redaction rules ------------------------------------
|
|
|
|
// Rule unit: MAN.0
|
|
rule "MAN.0.0: Apply manual resize redaction"
|
|
salience 128
|
|
when
|
|
$resizeRedaction: ManualResizeRedaction($id: annotationId)
|
|
$entityToBeResized: RedactionEntity(matchesAnnotationId($id))
|
|
then
|
|
manualRedactionApplicationService.resizeEntityAndReinsert($entityToBeResized, $resizeRedaction);
|
|
retract($resizeRedaction);
|
|
update($entityToBeResized);
|
|
$entityToBeResized.getIntersectingNodes().forEach(node -> update(node));
|
|
end
|
|
|
|
rule "MAN.0.1: Apply manual resize redaction"
|
|
salience 128
|
|
when
|
|
$resizeRedaction: ManualResizeRedaction($id: annotationId)
|
|
$imageToBeResized: Image(id == $id)
|
|
then
|
|
manualRedactionApplicationService.resizeImage($imageToBeResized, $resizeRedaction);
|
|
retract($resizeRedaction);
|
|
update($imageToBeResized);
|
|
update($imageToBeResized.getParent());
|
|
end
|
|
|
|
|
|
// Rule unit: MAN.1
|
|
rule "MAN.1.0: Apply id removals that are valid and not in forced redactions to Entity"
|
|
salience 128
|
|
when
|
|
$idRemoval: IdRemoval(status == AnnotationStatus.APPROVED, !removeFromDictionary, requestDate != null, $id: annotationId)
|
|
not ManualForceRedaction($id == annotationId, status == AnnotationStatus.APPROVED, requestDate != null)
|
|
$entityToBeRemoved: RedactionEntity(matchesAnnotationId($id))
|
|
then
|
|
$entityToBeRemoved.ignore("MAN.1.0", "Removed by ManualRedaction");
|
|
update($entityToBeRemoved);
|
|
retract($idRemoval);
|
|
$entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));
|
|
end
|
|
|
|
rule "MAN.1.1: Apply id removals that are valid and not in forced redactions to Image"
|
|
salience 128
|
|
when
|
|
$idRemoval: IdRemoval(status == AnnotationStatus.APPROVED, !removeFromDictionary, requestDate != null, $id: annotationId)
|
|
not ManualForceRedaction($id == annotationId, status == AnnotationStatus.APPROVED, requestDate != null)
|
|
$imageEntityToBeRemoved: Image($id == id)
|
|
then
|
|
$imageEntityToBeRemoved.ignore("MAN.1.1", "Removed by ManualRedaction");
|
|
update($imageEntityToBeRemoved);
|
|
retract($idRemoval);
|
|
update($imageEntityToBeRemoved.getParent());
|
|
end
|
|
|
|
|
|
// Rule unit: MAN.2
|
|
rule "MAN.2.0: Apply force redaction"
|
|
no-loop true
|
|
salience 128
|
|
when
|
|
$force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED, requestDate != null, $legalBasis: legalBasis)
|
|
$entityToForce: RedactionEntity(matchesAnnotationId($id))
|
|
then
|
|
$entityToForce.force("MAN.2.0", "Forced redaction", $legalBasis);
|
|
$entityToForce.setRemoved(false);
|
|
$entityToForce.setIgnored(false);
|
|
$entityToForce.setSkipRemoveEntitiesContainedInLarger(true);
|
|
update($entityToForce);
|
|
$entityToForce.getIntersectingNodes().forEach(node -> update(node));
|
|
end
|
|
|
|
rule "MAN.2.1: Apply force redaction to images"
|
|
no-loop true
|
|
salience 128
|
|
when
|
|
$force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED, requestDate != null, $legalBasis: legalBasis)
|
|
$imageToForce: Image(id == $id)
|
|
then
|
|
$imageToForce.force("MAN.2.0", "Forced redaction", $legalBasis);
|
|
$imageToForce.setRemoved(false);
|
|
$imageToForce.setIgnored(false);
|
|
update($imageToForce);
|
|
update($imageToForce.getParent());
|
|
end
|
|
|
|
|
|
// Rule unit: MAN.3
|
|
rule "MAN.3.0: Apply image recategorization"
|
|
salience 128
|
|
when
|
|
$recategorization: ManualImageRecategorization($id: annotationId, status == AnnotationStatus.APPROVED, $imageType: type)
|
|
$imageToBeRecategorized: Image($id == id)
|
|
then
|
|
$imageToBeRecategorized.setImageType(ImageType.fromString($imageType));
|
|
update($imageToBeRecategorized);
|
|
update($imageToBeRecategorized.getParent());
|
|
retract($recategorization);
|
|
end
|
|
|
|
|
|
//------------------------------------ Entity merging rules ------------------------------------
|
|
|
|
// Rule unit: X.0
|
|
rule "X.0.0: remove Entity contained by Entity of same type"
|
|
salience 65
|
|
when
|
|
$larger: RedactionEntity($type: type, $entityType: entityType, isActive())
|
|
$contained: RedactionEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !resized, !skipRemoveEntitiesContainedInLarger, isActive())
|
|
then
|
|
$contained.remove("X.0.0", "remove Entity contained by Entity of same type");
|
|
retract($contained);
|
|
end
|
|
|
|
|
|
// Rule unit: X.1
|
|
rule "X.1.0: merge intersecting Entities of same type"
|
|
salience 64
|
|
when
|
|
$first: RedactionEntity($type: type, $entityType: entityType, !resized, !skipRemoveEntitiesContainedInLarger, isActive())
|
|
$second: RedactionEntity(intersects($first), type == $type, entityType == $entityType, this != $first, !resized, !skipRemoveEntitiesContainedInLarger, isActive())
|
|
then
|
|
RedactionEntity mergedEntity = entityCreationService.byEntities(List.of($first, $second), $type, $entityType, document);
|
|
$first.remove("X.1.0", "merge intersecting Entities of same type");
|
|
$second.remove("X.1.0", "merge intersecting Entities of same type");
|
|
retract($first);
|
|
retract($second);
|
|
mergedEntity.getIntersectingNodes().forEach(node -> update(node));
|
|
end
|
|
|
|
|
|
// Rule unit: X.2
|
|
rule "X.2.0: remove Entity of type ENTITY when contained by FALSE_POSITIVE"
|
|
salience 64
|
|
when
|
|
$falsePositive: RedactionEntity($type: type, entityType == EntityType.FALSE_POSITIVE, isActive())
|
|
$entity: RedactionEntity(containedBy($falsePositive), type == $type, entityType == EntityType.ENTITY, !resized, !skipRemoveEntitiesContainedInLarger, isActive())
|
|
then
|
|
$entity.getIntersectingNodes().forEach(node -> update(node));
|
|
$entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE");
|
|
retract($entity)
|
|
end
|
|
|
|
|
|
// Rule unit: X.3
|
|
rule "X.3.0: remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"
|
|
salience 64
|
|
when
|
|
$falseRecommendation: RedactionEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION, isActive())
|
|
$recommendation: RedactionEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !resized, !skipRemoveEntitiesContainedInLarger, isActive())
|
|
then
|
|
$recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION");
|
|
retract($recommendation);
|
|
end
|
|
|
|
|
|
// Rule unit: X.4
|
|
rule "X.4.0: remove Entity of type RECOMMENDATION when intersected by ENTITY with same type"
|
|
salience 256
|
|
when
|
|
$entity: RedactionEntity($type: type, entityType == EntityType.ENTITY, isActive())
|
|
$recommendation: RedactionEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !resized, !skipRemoveEntitiesContainedInLarger, isActive())
|
|
then
|
|
$entity.addEngines($recommendation.getEngines());
|
|
$recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY with same type");
|
|
retract($recommendation);
|
|
end
|
|
|
|
|
|
// Rule unit: X.5
|
|
rule "X.5.0: remove Entity of type RECOMMENDATION when contained by ENTITY"
|
|
salience 256
|
|
when
|
|
$entity: RedactionEntity(entityType == EntityType.ENTITY, isActive())
|
|
$recommendation: RedactionEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !resized, !skipRemoveEntitiesContainedInLarger, isActive())
|
|
then
|
|
$recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when contained by ENTITY");
|
|
retract($recommendation);
|
|
end
|
|
|
|
|
|
// Rule unit: X.6
|
|
rule "X.6.0: remove Entity of lower rank, when intersected by entity of type ENTITY"
|
|
salience 32
|
|
when
|
|
$higherRank: RedactionEntity($type: type, entityType == EntityType.ENTITY, isActive())
|
|
$lowerRank: RedactionEntity(intersects($higherRank), type != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !resized, !skipRemoveEntitiesContainedInLarger, isActive())
|
|
then
|
|
$lowerRank.getIntersectingNodes().forEach(node -> update(node));
|
|
$lowerRank.remove("X.6.0", "remove Entity of lower rank, when intersected by entity of type ENTITY");
|
|
retract($lowerRank);
|
|
end
|
|
|
|
|
|
//------------------------------------ File attributes rules ------------------------------------
|
|
|
|
// Rule unit: FA.1
|
|
rule "FA.1.0: remove duplicate FileAttributes"
|
|
salience 64
|
|
when
|
|
$fileAttribute: FileAttribute($label: label, $value: value)
|
|
$duplicate: FileAttribute(this != $fileAttribute, label == $label, value == $value)
|
|
then
|
|
retract($duplicate);
|
|
end
|
|
|
|
|
|
//------------------------------------ Local dictionary search rules ------------------------------------
|
|
|
|
// Rule unit: LDS.0
|
|
rule "LDS.0.0: run local dictionary search"
|
|
agenda-group "LOCAL_DICTIONARY_ADDS"
|
|
salience -999
|
|
when
|
|
$dictionaryModel: DictionaryModel(!localEntriesWithMatchedRules.isEmpty()) from dictionary.getDictionaryModels()
|
|
then
|
|
entityCreationService.bySearchImplementation($dictionaryModel.getLocalSearch(), $dictionaryModel.getType(), EntityType.RECOMMENDATION, document)
|
|
.forEach(entity -> {
|
|
Collection<MatchedRule> matchedRules = $dictionaryModel.getLocalEntriesWithMatchedRules().get(entity.getValue());
|
|
entity.addMatchedRules(matchedRules);
|
|
});
|
|
end
|