diff --git a/redaction-service-v1/rules-management/README.md b/redaction-service-v1/rules-management/README.md new file mode 100644 index 00000000..76f35d1a --- /dev/null +++ b/redaction-service-v1/rules-management/README.md @@ -0,0 +1,87 @@ +Rules Management CLI +--- +The rules-management program is a command-line interface (CLI) tool written in Java. It is designed to generate Drools files based on rule classes and units. This README file provides an overview of the program and instructions for usage. + +Table of Contents +-- +* Introduction +* Installation +* Usage +* Options +* Examples + +Introduction +-- +The rules-management CLI program allows you to generate Drools files containing various rule classes and units. Each rule class represents a specific type of rule, such as SYN (Syngenta specific), CBI, PII, ETC (Other), AI, MAN (Manual redactions), X (Entity interactions), FA (FileAttribute interactions), and LDS (Local Dictionary Search). + +A rule unit is identified by a combination of the rule class and an integer. For example, CBI.1 refers to rule unit 1 in the CBI rule class. Each rule unit may contain multiple rules, which are also identified by integers. For instance, CBI.1.2 represents the second rule in rule unit 1 of the CBI rule class. + +The program provides options to specify the input type, input source, output destination, and additional flags for customization. + +Installation +-- +To use the rules-management CLI, you need to have Java 17+ and gradle installed on your system. Follow these steps to get started: + +Clone the latest main branch. +compile with `gradle install` + +Testing +-- +You can validate the rules you are changing if they are correctly written from a syntax point of view using the +`DroolsCompilationTest` class. Make sure you also place the changes in the `test/resources/.drl` and then run the test. + +Usage +-- +To run the rules-management program, open a terminal or command prompt and navigate to the directory where the program files are located. Use the following command: + + +`java -jar rules-management.jar [options]` + +or directly with gradle + +`gradle run --args='[options]'` + +Options +-- +The rules-management program supports the following command-line options: + +-t, --type: Specifies the input type. Default is "list" (if the recursive flag is set: "file"). Available options: +* "list" or "l": Expects a list of rule identifiers. For example: "MAN, X, LDS, CBI.0, PII.0.1", you can use wildcards such as * or leaving out digits with the identifiers to match multiple rule identifiers. This allows for convenient selection of multiple rule units or rules. +* "file" or "f": Expects the absolute path to a rule file. The file must contain the current Rule Identifiers. This option is useful for migrating rule files to the current state. +* "old" or "o": Expects the absolute path to an old rule file. The program attempts to translate the old rules to new rules based on '"main/resources/old_rules_with_translations.csv"'. + + +-i, --input: Specifies the input source based on the chosen type. It can be: + +* A string of rule identifiers like `"MAN, X, LDS, CBI.0, SYN.*.0, PII.0.1"` +* path to a rules file `/path/to/old_rules.txt` +* path to a folder containing rules files named "rules.txt" or "rules.drl" (if the "recursive" flag is set) `/path/to/rules_directory` + + +-o, --output: Specifies the output file path. If a directory is supplied as input, the folder structure is preserved. + +-n, --noop: If this flag is set, the input file will not be attempted to be translated or migrated, use this to only escape files. Currently, this only works with input type "file" + +-e, --escape: If this flag is set, the output files will be escaped and saved as ".txt", similar to dossier templates. + +-r, --recursive: Can only be set in combination with the "file" or "old" type and a directory as input. Searches recursively for files named "rules.txt" or "rules.drl" and attempts to migrate/translate them. +The folder structure is preserved in the output folder. + +-a, --application: Indicate which application you are targeting. For redact manager use RM. For Documine use DM. If it's not specified it will default to RM. + +Examples +-- +Generate Drools files from a list of rule identifiers and save them to the specified output file: +`gradle run --args='-t list -i "MAN, X, LDS, CBI.0, PII.0.1" -o /path/to/output/rules.drl'` + +Migrate an old rule file to the current format and save the translated rules to the specified output file: +`gradle run --args='-t old -i /path/to/old_rules.drl -o /path/to/output/translated_rules.drl'` + +Migrate multiple rule files in a directory (including subdirectories) and save the translated rules: +`gradle run --args='-t old -i /path/to/rules_directory -o /path/to/output/translated_rules -r'` + +Generate Drools files from a rules file and escape the output: +`gradle run --args='-t file -i /path/to/rules.txt -o /path/to/output/escaped_rules -e'` + +Escape Drools file rules.drl as is: +`gradle run --args='-t f -n -e -i /path/to/rules.drl -o /path/to/output/rules.txt'` diff --git a/redaction-service-v1/rules-management/build.gradle.kts b/redaction-service-v1/rules-management/build.gradle.kts new file mode 100644 index 00000000..5235d200 --- /dev/null +++ b/redaction-service-v1/rules-management/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + application +} + +group = "com.knecon.fforesight.utility" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() + maven { + url = uri("https://nexus.knecon.com/repository/red-platform-releases/") + credentials { + username = providers.gradleProperty("mavenUser").getOrNull(); + password = providers.gradleProperty("mavenPassword").getOrNull(); + } + } +} + +dependencies { + implementation(project(":redaction-service-server-v1")) + implementation("org.projectlombok:lombok:1.18.28") + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") + + implementation("com.github.javaparser:javaparser-core:3.25.3") + implementation("org.drools:drools-drl-parser:8.41.0.Final") + implementation("org.apache.commons:commons-csv:1.10.0") + implementation("commons-cli:commons-cli:1.5.0") + + + compileOnly("org.projectlombok:lombok:1.18.28") + annotationProcessor("org.projectlombok:lombok:1.18.28") + + testCompileOnly("org.projectlombok:lombok:1.18.28") + testAnnotationProcessor("org.projectlombok:lombok:1.18.28") + + implementation("org.kie:kie-api:9.44.0.Final") + implementation("org.drools:drools-compiler:9.44.0.Final") + testImplementation("org.kie:kie-ci:9.44.0.Final") +} + +tasks.test { + useJUnitPlatform() +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/Main.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/Main.java new file mode 100644 index 00000000..c61bc9b8 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/Main.java @@ -0,0 +1,214 @@ +package com.knecon.fforesight.utility.rules.management; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +import com.knecon.fforesight.utility.rules.management.factory.RuleFileFactory; +import com.knecon.fforesight.utility.rules.management.factory.RuleFileParser; +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; +import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier; +import com.knecon.fforesight.utility.rules.management.translation.OldRulesParser; +import com.knecon.fforesight.utility.rules.management.utils.RuleFileIO; + +import lombok.SneakyThrows; + +public class Main { + + public static void main(String[] args) { + + Options options = buildOptions(); + + CommandLineParser parser = new DefaultParser(); + HelpFormatter formatter = new HelpFormatter(); + CommandLine cmd = null; //not a good practice, it serves its purpose + + try { + cmd = parser.parse(options, args); + } catch (ParseException e) { + System.out.println(e.getMessage()); + formatter.printHelp("utility-name", options); + + System.exit(1); + } + final CommandLine finalCmd = cmd; + + if (finalCmd.hasOption("r")) { + handleRecursiveFileCreation(finalCmd); + } else { + handleSingleFileCreation(finalCmd); + } + } + + + private static void handleSingleFileCreation(final CommandLine cmd) { + + String ruleFileString; + String type = cmd.hasOption("t") ? cmd.getOptionValue("t") : "l"; + String input = cmd.getOptionValue("input"); + ApplicationType applicationType = getApplicationType(cmd); + + if (cmd.hasOption("n")) { + if (!type.equals("file") && !type.equals("f")) { + System.out.println("No op can only be used with type \"file\""); + System.exit(1); + } + ruleFileString = RuleFileIO.getRulesString(input); + } else { + Set identifiers = switch (type) { + case "list", "l" -> RuleIdentifier.fromListOfIdentifiersString(input); + case "file", "f" -> RuleFileParser.parseRuleIdentifiersFromFile(input); + case "old", "o" -> OldRulesParser.translateOldRulesStringToNewIdentifiers(RuleFileIO.getRulesString(input), applicationType); + default -> throw new IllegalArgumentException(String.format("type \"%s\" is not valid", cmd.getOptionValue("t"))); + }; + ruleFileString = RuleFileFactory.createFileFromIdentifiers(identifiers, applicationType); + } + + ruleFileString = escapeRuleStringIfOptionIsSet(cmd, ruleFileString); + + try (var out = new FileOutputStream(cmd.getOptionValue("output"))) { + out.write(ruleFileString.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + System.out.println(e.getMessage()); + System.exit(1); + } + } + + + private static void handleRecursiveFileCreation(final CommandLine cmd) { + + Path outputDir = Path.of(cmd.getOptionValue("output")); + if (!outputDir.toFile().isDirectory()) { + System.out.printf("%s is not a directory!%n", cmd.getOptionValue("output")); + System.exit(1); + } + outputDir.toFile().mkdirs(); + ApplicationType applicationType = getApplicationType(cmd); + + String type = cmd.hasOption("t") ? cmd.getOptionValue("t") : "f"; + Map> identifiersPerFile = switch (type) { + case "file", "f" -> parseIdentifiersFromFiles(Path.of(cmd.getOptionValue("input"))); + case "old", "o" -> translateIdentifiersFromOldFiles(Path.of(cmd.getOptionValue("input")), applicationType); + default -> throw new IllegalArgumentException(String.format("type \"%s\" is not valid in combination with the \"recursive\" flag", cmd.getOptionValue("t"))); + }; + identifiersPerFile.keySet().forEach(path -> { + String ruleFileString = RuleFileFactory.createFileFromIdentifiers(identifiersPerFile.get(path), applicationType); + + ruleFileString = escapeRuleStringIfOptionIsSet(cmd, ruleFileString); + Path output = outputDir.resolve(path); + output.getParent().toFile().mkdirs(); + try (var out = new FileOutputStream(output.toFile())) { + out.write(ruleFileString.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + System.out.println(e.getMessage()); + System.exit(1); + } + }); + } + + + private static String escapeRuleStringIfOptionIsSet(CommandLine finalCmd, String ruleFileString) { + + if (finalCmd.hasOption("e")) { + ruleFileString = RuleFileIO.escapeAndWrap(ruleFileString); + } + return ruleFileString; + } + + + private static Path getRelativizedPath(Path inputDirectory, File file) { + + return inputDirectory.relativize(Path.of(file.getAbsolutePath())); + } + + + @SneakyThrows + private static Map> translateIdentifiersFromOldFiles(Path inputDirectory, ApplicationType applicationType) { + + return RuleFileIO.streamAllRuleFilesInDirectory(inputDirectory) + .collect(Collectors.toMap(file -> getRelativizedPath(inputDirectory, file), e -> OldRulesParser.translateOldRulesFileToNewIdentifiers(e, applicationType))); + } + + + @SneakyThrows + private static Map> parseIdentifiersFromFiles(Path inputDirectory) { + + return RuleFileIO.streamAllRuleFilesInDirectory(inputDirectory) + .collect(Collectors.toMap(file -> getRelativizedPath(inputDirectory, file), RuleFileParser::parseRuleIdentifiersFromFile)); + } + + private static ApplicationType getApplicationType(final CommandLine cmd) { + + String application = cmd.hasOption("a") ? cmd.getOptionValue("a") : "RM"; + ApplicationType applicationType; + + if (application.equals("DM") || application.equals("RM")) { + applicationType = ApplicationType.valueOf(application); + } else { + throw new IllegalArgumentException(String.format("Application type \"%s\" is not valid, choose between \"RM\" or \"DM\"", cmd.getOptionValue("a"))); + } + + return applicationType; + } + + + private static Options buildOptions() { + + Options options = new Options(); + + Option type = new Option("t",// + "type",// + true,// + "Please specify the input type, defaults to \"list\", \n" +// + "current options:\n" +// + "\"list\" or \"l\" (List of Rule identifiers e.g. \"MAN, X, LDS, CBI.0, PII.0.1\")\n" +// + "\"file\" or \"f\" (Absolute Path to rule file, escaped or not, but the rule file must contain the current Rule Identifiers, the main use case is migrating rule files to the current state)\n" +// + "\"old\" or \"o\" (Absolute Path to old rule file, escaped or not, the script will attempt to translate the old rules to new rules based on \"main/resources/old_rules_with_translations.csv\")\n"); + options.addOption(type); + Option input = new Option("i", + "input", + true, + "Based on type expects: String of Rule identifiers, Absolute Path to a rules file or a folder containing rules files named rules.txt or rules.drl if \"recursive\" flag is set"); + input.setRequired(true); + options.addOption(input); + + Option output = new Option("o", "output", true, "output file path, if a directory is supplied, the folder structure is preserved"); + output.setRequired(true); + options.addOption(output); + + Option noTranslate = new Option("n", + "noop", + false, + "If this flag is set, the input file will not be attempted to be translated or migrated, use this to only escape files. Currently this only works with input type \"file\""); + options.addOption(noTranslate); + Option escapeOutput = new Option("e", "escape", false, "If this flag is set, the output files will be escaped and saved as \".txt\", like in the dossier templates"); + options.addOption(escapeOutput); + + Option recursive = new Option("r", + "recursive", + false, + "Can only be set in combination with type \"file\" or \"old\" Searches recursively for files named rules.txt or rules.drl and attempts to migrate/translate them."); + options.addOption(recursive); + + Option application = new Option("a", + "application", + true, + "What application to update the rules for. For Redact Manager specify: RM. For Documine specify: DM. Defaults to RM."); + options.addOption(application); + return options; + } + +} \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/RuleManagementResources.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/RuleManagementResources.java new file mode 100644 index 00000000..d3d5d3cc --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/RuleManagementResources.java @@ -0,0 +1,55 @@ +package com.knecon.fforesight.utility.rules.management; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; + +import lombok.SneakyThrows; + +public class RuleManagementResources { + + public static InputStream getAllRulesInputStream(ApplicationType applicationType) { + + if (applicationType == ApplicationType.RM) { + return RuleManagementResources.class.getClassLoader().getResourceAsStream("all_redact_manager_rules.drl"); + } + return RuleManagementResources.class.getClassLoader().getResourceAsStream("all_rules_documine.drl"); + } + + + public static InputStream getDefaultRuleIdentifiesInputStream(ApplicationType applicationType) { + + if (applicationType == ApplicationType.RM) { + return RuleManagementResources.class.getClassLoader().getResourceAsStream("default_rule_identifiers.txt"); + } else { + return RuleManagementResources.class.getClassLoader().getResourceAsStream("default_rule_identifiers_dm.txt"); + } + } + + + public static InputStream getTemplateInputStream() { + + return RuleManagementResources.class.getClassLoader().getResourceAsStream("order_template.txt"); + } + + + public static InputStream getOldRulesCsvInputStream() { + + return RuleManagementResources.class.getClassLoader().getResourceAsStream("old_rules_with_translations.csv"); + } + + + @SneakyThrows + public static String createTempOldRulesCsv(String formattedAsCsv) { + + File csvFile = File.createTempFile("old_rules_with_translations-", ".csv"); + try (var out = new FileOutputStream(csvFile)) { + out.write(formattedAsCsv.getBytes(StandardCharsets.UTF_8)); + } + return csvFile.toString(); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/DefaultRuleIdentifiersFactory.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/DefaultRuleIdentifiersFactory.java new file mode 100644 index 00000000..0019fd1f --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/DefaultRuleIdentifiersFactory.java @@ -0,0 +1,26 @@ +package com.knecon.fforesight.utility.rules.management.factory; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import com.knecon.fforesight.utility.rules.management.RuleManagementResources; +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; +import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class DefaultRuleIdentifiersFactory { + + @SneakyThrows + public Set getDefaultRuleIdentifiers(ApplicationType applicationType) { + + try (var defaultRuleIdentifiers = RuleManagementResources.getDefaultRuleIdentifiesInputStream(applicationType)) { + String ruleIdentifierString = new String(defaultRuleIdentifiers.readAllBytes()); + return Arrays.stream(ruleIdentifierString.split("\n")).map(RuleIdentifier::fromString).collect(Collectors.toSet()); + } + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactory.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactory.java new file mode 100644 index 00000000..9d107e0d --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactory.java @@ -0,0 +1,104 @@ +package com.knecon.fforesight.utility.rules.management.factory; + +import static java.util.Collections.emptySet; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.knecon.fforesight.utility.rules.management.RuleManagementResources; +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; +import com.knecon.fforesight.utility.rules.management.models.RuleClass; +import com.knecon.fforesight.utility.rules.management.models.RuleFileBluePrint; +import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier; +import com.knecon.fforesight.utility.rules.management.models.RuleType; +import com.knecon.fforesight.utility.rules.management.models.RuleUnit; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class RuleFileFactory { + + public String createFileFromIdentifiers(String identifiers, ApplicationType applicationType) { + if (identifiers.isBlank() || identifiers.isEmpty()) { + return createFileFromIdentifiers(emptySet(), applicationType); + } + return createFileFromIdentifiers(Arrays.stream(identifiers.split(",")).map(String::trim).map(RuleIdentifier::fromString).collect(Collectors.toSet()), applicationType); + } + + + public String createFileFromIdentifiers(Set identifiers, ApplicationType applicationType) { + + return createFileFromIdentifiers(identifiers, true, applicationType); + } + + public String createFileFromIdentifiers(Set identifiers, boolean includeDefaults, ApplicationType applicationType) { + + if (includeDefaults) { + identifiers.addAll(DefaultRuleIdentifiersFactory.getDefaultRuleIdentifiers(applicationType)); + } + RuleFileBluePrint bluePrint = RuleFileParser.buildBluePrintFromAllRulesFile(applicationType); + RuleFileBluePrint filteredBluePrint = bluePrint.buildFilteredBluePrintByRuleIdentifiers(identifiers); + return buildRuleFile(filteredBluePrint); + } + + + @SneakyThrows + public String buildRuleFile(RuleFileBluePrint bluePrint) { + + try (var templateInputStream = RuleManagementResources.getTemplateInputStream()) { + String template = new String(templateInputStream.readAllBytes(), StandardCharsets.UTF_8); + List templateRuleOrder = parseRuleOrder(template); + return buildBluePrintWithTemplateRuleOrder(bluePrint, templateRuleOrder); + } + } + + + private List parseRuleOrder(String template) { + + String[] values = template.split("\n"); + return Arrays.stream(values).map(RuleType::fromString).toList(); + } + + + private String buildBluePrintWithTemplateRuleOrder(RuleFileBluePrint bluePrint, List ruleOrder) { + + StringBuilder sb = new StringBuilder(); + sb.append(bluePrint.imports()); + sb.append("\n\n"); + sb.append(bluePrint.globals()); + sb.append("\n\n"); + sb.append("//------------------------------------ queries ------------------------------------"); + sb.append("\n\n"); + sb.append(bluePrint.queries()); + sb.append("\n\n"); + for (RuleType ruleBlockType : ruleOrder) { + if (!bluePrint.ruleClassExists(ruleBlockType)) { + continue; + } + sb.append("//------------------------------------ "); + sb.append(ruleBlockType); + sb.append(" rules ------------------------------------"); + sb.append("\n\n"); + RuleClass ruleClass = bluePrint.findRuleClassByType(ruleBlockType); + for (RuleUnit unit : ruleClass.ruleUnits()) { + if (unit.rules().isEmpty()) { + continue; + } + sb.append("// "); + sb.append(unit.rules().get(0).identifier().toRuleUnitString()); + sb.append("\n"); + unit.rules().forEach(rule -> { + sb.append(rule.code()); + sb.append("\n\n"); + }); + sb.append("\n"); + } + } + return sb.toString().trim() + "\n"; + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileParser.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileParser.java new file mode 100644 index 00000000..4fa8ca67 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileParser.java @@ -0,0 +1,97 @@ +package com.knecon.fforesight.utility.rules.management.factory; + +import static java.util.stream.Collectors.groupingBy; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.drools.drl.ast.descr.ImportDescr; +import org.drools.drl.ast.descr.PackageDescr; +import org.drools.drl.ast.descr.RuleDescr; +import org.drools.drl.parser.DrlParser; +import org.kie.internal.builder.conf.LanguageLevelOption; + +import com.knecon.fforesight.utility.rules.management.RuleManagementResources; +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; +import com.knecon.fforesight.utility.rules.management.models.BasicRule; +import com.knecon.fforesight.utility.rules.management.models.RuleClass; +import com.knecon.fforesight.utility.rules.management.models.RuleFileBluePrint; +import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier; +import com.knecon.fforesight.utility.rules.management.models.RuleType; +import com.knecon.fforesight.utility.rules.management.models.RuleUnit; +import com.knecon.fforesight.utility.rules.management.utils.RuleFileIO; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class RuleFileParser { + + @SneakyThrows + public RuleFileBluePrint buildBluePrintFromAllRulesFile(ApplicationType applicationType) { + + try (var rulesInputStream = RuleManagementResources.getAllRulesInputStream(applicationType)) { + String rulesString = new String(rulesInputStream.readAllBytes(), StandardCharsets.UTF_8); + return buildBluePrintFromRulesString(rulesString); + } + } + + + @SneakyThrows + public RuleFileBluePrint buildBluePrintFromRulesString(String rulesString) { + + DrlParser parser = new DrlParser(LanguageLevelOption.DRL6); + PackageDescr packageDescr = parser.parse(false, rulesString); + StringBuilder queryBuilder = new StringBuilder(); + List allRules = new LinkedList<>(); + for (RuleDescr rule : packageDescr.getRules()) { + if (rule.isQuery()) { + queryBuilder.append(rulesString, rule.getStartCharacter(), rule.getEndCharacter()); + queryBuilder.append("\n\n"); + continue; + } + allRules.add(BasicRule.fromRuleDescr(rule, rulesString)); + } + String imports = rulesString.substring(0, packageDescr.getImports().stream().mapToInt(ImportDescr::getEndCharacter).max().orElseThrow() + 1); + String globals = packageDescr.getGlobals() + .stream() + .map(globalDescr -> rulesString.substring(globalDescr.getStartCharacter(), globalDescr.getEndCharacter())) + .collect(Collectors.joining("\n")); + + List ruleClasses = buildRuleClasses(allRules); + + return new RuleFileBluePrint(imports.trim(), globals.trim(), queryBuilder.toString().trim(), ruleClasses); + } + + + private List buildRuleClasses(List allRules) { + + Map> rulesPerType = allRules.stream().collect(groupingBy(rule -> rule.identifier().type())); + return rulesPerType.keySet().stream().map(type -> new RuleClass(type, groupingByGroup(rulesPerType.get(type)))).collect(Collectors.toList()); + } + + + private List groupingByGroup(List rules) { + + Map> rulesPerUnit = rules.stream().collect(groupingBy(rule -> rule.identifier().unit())); + return rulesPerUnit.keySet().stream().sorted().map(unit -> new RuleUnit(unit, rulesPerUnit.get(unit))).collect(Collectors.toList()); + } + + + public Set parseRuleIdentifiersFromFile(String input) { + + return buildBluePrintFromRulesString(RuleFileIO.getRulesString(input)).getAllRuleIdentifiers(); + } + + + public Set parseRuleIdentifiersFromFile(File file) { + + return parseRuleIdentifiersFromFile(file.toString()); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleIdentifierMigrator.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleIdentifierMigrator.java new file mode 100644 index 00000000..cb9cbefd --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleIdentifierMigrator.java @@ -0,0 +1,152 @@ +package com.knecon.fforesight.utility.rules.management.migration; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.knecon.fforesight.utility.rules.management.RuleManagementResources; +import com.knecon.fforesight.utility.rules.management.factory.RuleFileFactory; +import com.knecon.fforesight.utility.rules.management.factory.RuleFileParser; +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; +import com.knecon.fforesight.utility.rules.management.models.BasicRule; +import com.knecon.fforesight.utility.rules.management.models.RuleClass; +import com.knecon.fforesight.utility.rules.management.models.RuleFileBluePrint; +import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier; +import com.knecon.fforesight.utility.rules.management.models.RuleUnit; +import com.knecon.fforesight.utility.rules.management.translation.OldRulesParser; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class RuleIdentifierMigrator { + + public static void main(String[] args) { + + migrateAllRuleIdentifiers(); + } + + + @SneakyThrows + public void migrateAllRuleIdentifiers() { + + List parsedRecords = OldRulesParser.getOldRulesCsvRecords(RuleManagementResources.getOldRulesCsvInputStream()); + RuleFileBluePrint bluePrint = getBluePrint(ApplicationType.RM); + +// migrateIdentifier(RuleIdentifier.fromString("PII.10.0"), RuleIdentifier.fromString("CBI.20.0"), bluePrint, parsedRecords); +// migrateIdentifier(RuleIdentifier.fromString("PII.10.1"), RuleIdentifier.fromString("CBI.20.1"), bluePrint, parsedRecords); +// migrateIdentifier(RuleIdentifier.fromString("PII.11.0"), RuleIdentifier.fromString("PII.10.0"), bluePrint, parsedRecords); +// migrateIdentifier(RuleIdentifier.fromString("PII.12.0"), RuleIdentifier.fromString("PII.11.0"), bluePrint, parsedRecords); +// migrateIdentifier(RuleIdentifier.fromString("PII.13.0"), RuleIdentifier.fromString("PII.12.0"), bluePrint, parsedRecords); + + bluePrint = migrateMatchedRuleForAllRules(bluePrint); + + String ruleString = RuleFileFactory.buildRuleFile(bluePrint); + try (var out = new FileOutputStream("/tmp/all_redact_manager_rules.drl")) { + out.write(ruleString.getBytes(StandardCharsets.UTF_8)); + } + String csvString = OldRulesParser.formatAsCsv(parsedRecords); + try (var out = new FileOutputStream("/tmp/old_rules_with_translations.csv")) { + out.write(csvString.getBytes(StandardCharsets.UTF_8)); + } + } + + + private static RuleFileBluePrint migrateMatchedRuleForAllRules(RuleFileBluePrint bluePrint) { + + List migratedRules = bluePrint.ruleClasses() + .stream() + .map(RuleClass::ruleUnits) + .flatMap(Collection::stream) + .map(RuleUnit::rules) + .flatMap(Collection::stream) + .map(RuleIdentifierMigrator::migrateMatchedRule) + .toList(); + RuleFileBluePrint migratedBluePrint = new RuleFileBluePrint(bluePrint.imports(), bluePrint.globals(), bluePrint.queries(), new LinkedList<>()); + migratedRules.forEach(migratedBluePrint::addRule); + return migratedBluePrint; + } + + + private static BasicRule migrateMatchedRule(BasicRule basicRule) { + + String redactionReason = findByRegex("\\.setRedactionReason\\(\"(.*)\"\\)", basicRule.code(), 1); + String legalBasis = findByRegex("\\.setLegalBasis\\(\"(.*)\"\\)", basicRule.code(), 1); + + String migratedCode = basicRule.code().replaceAll("\\.addMatchedRule\\(.*\\)", Matcher.quoteReplacement(String.format(".addMatchedRule(\"%s\", \"%s\", \"%s\")", basicRule.identifier().toString(), redactionReason, legalBasis))); + migratedCode = migratedCode.replaceAll("\\.setMatchedRule\\(.*\\)", Matcher.quoteReplacement(String.format(".addMatchedRule(\"%s\", \"%s\", \"%s\")", basicRule.identifier().toString(), redactionReason, legalBasis))); + migratedCode = migratedCode.replaceAll("\\.setRedactionReason\\(\".*\"\\)", ""); + migratedCode = migratedCode.replaceAll("\\.setLegalBasis\\(\".*\"\\)", ""); + migratedCode = migratedCode.replaceAll("\\$entity;\n", ""); + migratedCode = migratedCode.replaceAll("\\$redactionEntity;", ""); + migratedCode = migratedCode.replaceAll("\\$laboratoryEntity;", ""); + migratedCode = migratedCode.replaceAll("\\$contactEntity;", ""); + return new BasicRule(basicRule.identifier(), basicRule.name(), migratedCode); + } + + private static String findByRegex(String regex, String searchText, int group) { + + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(searchText); + if (matcher.find()) { + return searchText.substring(matcher.start(group), matcher.end(group)); + } + return ""; + } + + + private static RuleFileBluePrint getBluePrint(ApplicationType applicationType) throws IOException { + + RuleFileBluePrint bluePrint; + try (var inputStream = RuleManagementResources.getAllRulesInputStream(applicationType)) { + String rulesString = new String(inputStream.readAllBytes()); + bluePrint = RuleFileParser.buildBluePrintFromRulesString(rulesString); + } + return bluePrint; + } + + + public void migrateIdentifier(RuleIdentifier oldIdentifier, RuleIdentifier newIdentifier, RuleFileBluePrint bluePrint, List records) { + + BasicRule oldRule = bluePrint.findRuleClassByType(oldIdentifier.type()) + .findRuleUnitByInteger(oldIdentifier.unit()) + .rules() + .stream() + .filter(rule -> rule.identifier().equals(oldIdentifier)) + .findFirst() + .orElseThrow(); + + BasicRule newRule = new BasicRule(newIdentifier, oldRule.name(), migrateCode(oldRule.name(), oldRule.code(), oldIdentifier, newIdentifier)); + bluePrint.removeRule(oldIdentifier); + bluePrint.addRule(newRule); + + records.stream() + .filter(record -> record.translatesTo().contains(oldIdentifier)) + .forEach(record -> replaceOldIdentifier(oldIdentifier, newIdentifier, record.translatesTo())); + } + + + private String migrateCode(String name, String code, RuleIdentifier oldIdentifier, RuleIdentifier newIdentifier) { + + return code.replace(String.format(".addMatchedRule(%d);", oldIdentifier.unit()), String.format(".addMatchedRule(%d);", newIdentifier.unit())) + .replace(oldIdentifier.toString(), newIdentifier.toString()); + } + + + private static void replaceOldIdentifier(RuleIdentifier oldIdentifier, RuleIdentifier newIdentifier, List oldIdentifiers) { + + oldIdentifiers.remove(oldIdentifier); + oldIdentifiers.add(newIdentifier); + } + + + public record Context(RuleFileBluePrint bluePrint, List records) { + + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/ApplicationType.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/ApplicationType.java new file mode 100644 index 00000000..5383b80e --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/ApplicationType.java @@ -0,0 +1,17 @@ +package com.knecon.fforesight.utility.rules.management.models; + +import lombok.Getter; + +public enum ApplicationType { + + RM("RM"), + DM("DM"); + + @Getter + private final String value; + + ApplicationType(String value) { + + this.value = value; + } +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicRule.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicRule.java new file mode 100644 index 00000000..1ce158ed --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicRule.java @@ -0,0 +1,21 @@ +package com.knecon.fforesight.utility.rules.management.models; + +import org.drools.drl.ast.descr.RuleDescr; + +import com.knecon.fforesight.utility.rules.management.utils.RuleFileIO; + +public record BasicRule(RuleIdentifier identifier, String name, String code) { + public static BasicRule fromRuleDescr(RuleDescr rule, String rulesString) { + + RuleIdentifier identifier = RuleIdentifier.fromName(rule.getName()); + String nameWithoutIdentifier = rule.getName().replace(identifier + ":", ""); + String code = rulesString.substring(rule.getStartCharacter(), rule.getEndCharacter()); + return new BasicRule(identifier, nameWithoutIdentifier, code); + } + + public String toEscapedCode() { + + return RuleFileIO.escapeAndWrap(code()); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/OldRule.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/OldRule.java new file mode 100644 index 00000000..a6cdb0ab --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/OldRule.java @@ -0,0 +1,21 @@ +package com.knecon.fforesight.utility.rules.management.models; + +import org.drools.drl.ast.descr.RuleDescr; + +public record OldRule(String name, String code) { + + private static final int RULE_PREFIX_LENGTH = "rule \"\"".length(); + + + public static OldRule fromRuleDescr(RuleDescr ruleDescr, String oldRuleString) { + + return new OldRule(ruleDescr.getName(), getCodeSubstring(ruleDescr, oldRuleString)); + } + + + private static String getCodeSubstring(RuleDescr rule, String oldRuleString) { + + return oldRuleString.substring(rule.getStartCharacter() + rule.getName().length() + RULE_PREFIX_LENGTH, rule.getEndCharacter()); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleClass.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleClass.java new file mode 100644 index 00000000..03f64bc3 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleClass.java @@ -0,0 +1,22 @@ +package com.knecon.fforesight.utility.rules.management.models; + +import java.util.List; +import java.util.Objects; + +public record RuleClass(RuleType ruleType, List ruleUnits) { + + public RuleUnit findRuleUnitByInteger(Integer unit) { + + return ruleUnits.stream() + .filter(ruleUnit -> Objects.equals(ruleUnit.unit(), unit)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format("RuleUnit %d does not exist in class %s", unit, this))); + } + + + public boolean ruleUnitExists(Integer unit) { + + return ruleUnits.stream().anyMatch(ruleUnit -> Objects.equals(ruleUnit.unit(), unit)); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleFileBluePrint.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleFileBluePrint.java new file mode 100644 index 00000000..e6e63393 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleFileBluePrint.java @@ -0,0 +1,100 @@ +package com.knecon.fforesight.utility.rules.management.models; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +public record RuleFileBluePrint(String imports, String globals, String queries, List ruleClasses) { + + public RuleClass findRuleClassByType(RuleType ruleType) { + + return ruleClasses.stream() + .filter(ruleClass -> Objects.equals(ruleClass.ruleType(), ruleType)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format("RuleType %s does not exist in this BluePrint %s", ruleType.name(), this))); + } + + + public boolean ruleClassExists(RuleType ruleType) { + + return ruleClasses.stream().anyMatch(ruleClass -> Objects.equals(ruleClass.ruleType(), ruleType)); + } + + + public List findRuleByIdentifier(RuleIdentifier ruleIdentifier) { + + if (Objects.isNull(ruleIdentifier.unit())) { + return findRuleClassByType(ruleIdentifier.type()).ruleUnits().stream().map(RuleUnit::rules).flatMap(Collection::stream).toList(); + } + return findRuleClassByType(ruleIdentifier.type()).findRuleUnitByInteger(ruleIdentifier.unit()) + .rules() + .stream() + .filter(rule -> rule.identifier().matches(ruleIdentifier)) + .toList(); + } + + + public void addRule(BasicRule rule) { + + RuleClass ruleClass; + if (ruleClassExists(rule.identifier().type())) { + ruleClass = findRuleClassByType(rule.identifier().type()); + } else { + ruleClass = new RuleClass(rule.identifier().type(), new LinkedList<>()); + ruleClasses.add(ruleClass); + } + + RuleUnit ruleUnit; + if (ruleClass.ruleUnitExists(rule.identifier().unit())) { + ruleUnit = ruleClass.findRuleUnitByInteger(rule.identifier().unit()); + } else { + ruleUnit = new RuleUnit(rule.identifier().unit(), new LinkedList<>()); + ruleClass.ruleUnits().add(ruleUnit); + } + + ruleUnit.rules().add(rule); + } + + + public void removeRule(RuleIdentifier ruleIdentifier) { + + RuleClass ruleClass = findRuleClassByType(ruleIdentifier.type()); + RuleUnit ruleUnit = ruleClass.findRuleUnitByInteger(ruleIdentifier.unit()); + ruleUnit.rules().removeIf(rule -> rule.identifier().matches(ruleIdentifier)); + + if (ruleUnit.rules().isEmpty()) { + ruleClass.ruleUnits().remove(ruleUnit); + } + if (ruleClass.ruleUnits().isEmpty()) { + ruleClasses().remove(ruleClass); + } + } + + + public Set getAllRuleIdentifiers() { + + return ruleClasses().stream() + .map(RuleClass::ruleUnits) + .flatMap(Collection::stream) + .map(RuleUnit::rules) + .flatMap(Collection::stream) + .map(BasicRule::identifier) + .collect(Collectors.toSet()); + } + + + public RuleFileBluePrint buildFilteredBluePrintByRuleIdentifiers(Set identifiers) { + + RuleFileBluePrint filteredBluePrint = new RuleFileBluePrint(imports(), globals(), queries(), new LinkedList<>()); + ruleClasses().stream() + .flatMap(ruleClass -> ruleClass.ruleUnits().stream()) + .flatMap(ruleUnit -> ruleUnit.rules().stream()) + .filter(rule -> identifiers.stream().anyMatch(identifier -> rule.identifier().matches(identifier))) + .forEach(filteredBluePrint::addRule); + return filteredBluePrint; + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleIdentifier.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleIdentifier.java new file mode 100644 index 00000000..d98de961 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleIdentifier.java @@ -0,0 +1,90 @@ +package com.knecon.fforesight.utility.rules.management.models; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import lombok.NonNull; + +/* +This is used to filter rules, if the field Integer unit or Integer id is null, the field will match any other RuleIdentifier. +Therefore, to compare RuleIdentifiers one should always use the function matches. + */ +public record RuleIdentifier(@NonNull RuleType type, Integer unit, Integer id) { + + public static RuleIdentifier fromName(String name) { + + String identifier = name.split(":")[0]; + return fromString(identifier); + } + + public static RuleIdentifier fromRuleType(RuleType ruleType) { + + return new RuleIdentifier(ruleType, null, null); + } + + + public static RuleIdentifier fromString(String identifier) { + String[] values = identifier.split("\\."); + RuleType type = RuleType.fromString(values[0]); + Integer group = values.length > 1 ? parseIntOrStar(values[1]) : null; + Integer id = values.length > 2 ? parseIntOrStar(values[2]) : null; + return new RuleIdentifier(type, group, id); + } + + public static Set fromListOfIdentifiersString(String input) { + + return Arrays.stream(input.split(",")).map(String::trim).map(RuleIdentifier::fromString).collect(Collectors.toSet()); + } + + + private static Integer parseIntOrStar(String value) { + value = value.replaceAll("\r",""); + return !value.equals("*") ? Integer.parseInt(value) : null; + } + + + /** + * This is used to filter rules, if the field Integer unit or Integer id is null, the field will match any other RuleIdentifier. + * Therefore, to compare RuleIdentifiers one should always use the function matches. + * + * @param ruleIdentifier RuleIdentifier to check if this one matches it. + * @return true, if all fields match. If a field is null, it matches any field. + */ + public boolean matches(RuleIdentifier ruleIdentifier) { + + return ruleIdentifier.type().equals(this.type()) && // + (Objects.isNull(ruleIdentifier.unit()) || Objects.isNull(this.unit()) || Objects.equals(this.unit(), ruleIdentifier.unit())) && // + (Objects.isNull(ruleIdentifier.id()) || Objects.isNull(this.id()) || Objects.equals(this.id(), ruleIdentifier.id())); + + } + + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append(type().name()); + if (Objects.nonNull(unit())) { + sb.append("."); + sb.append(unit()); + } else { + sb.append(".*"); + } + if (Objects.nonNull(id())) { + sb.append("."); + sb.append(id()); + } else { + sb.append(".*"); + } + return sb.toString(); + } + + + public String toRuleUnitString() { + + return String.format("Rule unit: %s.%d", type.name(), unit); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleType.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleType.java new file mode 100644 index 00000000..e91c96ea --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleType.java @@ -0,0 +1,83 @@ +package com.knecon.fforesight.utility.rules.management.models; + +public enum RuleType { + SYN { + @Override + public String toString() { + + return "Syngenta specific"; + } + }, + CBI, + PII, + ETC { + @Override + public String toString() { + + return "Other"; + } + }, + AI, + MAN { + @Override + public String toString() { + + return "Manual redaction"; + } + }, + X { + @Override + public String toString() { + + return "Entity merging"; + } + }, + FA { + @Override + public String toString() { + + return "File attributes"; + } + }, + LDS { + @Override + public String toString() { + + return "Local dictionary search"; + } + }, + H { + @Override + public String toString() { + + return "H"; + } + }, + DOC { + + @Override + public String toString() { + + return "General documine"; + } + }; + + + public static RuleType fromString(String value) { + value = value.replaceAll("\r",""); + return switch (value) { + case "SYN" -> SYN; + case "CBI" -> CBI; + case "PII" -> PII; + case "ETC" -> ETC; + case "AI" -> AI; + case "MAN" -> MAN; + case "X" -> X; + case "FA" -> FA; + case "LDS" -> LDS; + case "H" -> H; + case "DOC" -> DOC; + default -> throw new IllegalStateException("Unexpected value: " + value); + }; + } +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleUnit.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleUnit.java new file mode 100644 index 00000000..ec23ad27 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleUnit.java @@ -0,0 +1,7 @@ +package com.knecon.fforesight.utility.rules.management.models; + +import java.util.List; + +public record RuleUnit(Integer unit, List rules) { + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/translation/OldRulesParser.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/translation/OldRulesParser.java new file mode 100644 index 00000000..cf040c09 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/translation/OldRulesParser.java @@ -0,0 +1,196 @@ +package com.knecon.fforesight.utility.rules.management.translation; + +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.CSVRecord; +import org.drools.drl.ast.descr.PackageDescr; +import org.drools.drl.ast.descr.RuleDescr; +import org.drools.drl.parser.DrlParser; +import org.kie.internal.builder.conf.LanguageLevelOption; + +import com.knecon.fforesight.utility.rules.management.RuleManagementResources; +import com.knecon.fforesight.utility.rules.management.factory.RuleFileParser; +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; +import com.knecon.fforesight.utility.rules.management.models.BasicRule; +import com.knecon.fforesight.utility.rules.management.models.OldRule; +import com.knecon.fforesight.utility.rules.management.models.RuleFileBluePrint; +import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier; +import com.knecon.fforesight.utility.rules.management.models.RuleType; +import com.knecon.fforesight.utility.rules.management.utils.RuleFileIO; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class OldRulesParser { + + final static List HEADERS = List.of("id", "old rule names", "old rule code", "translates to"); + + + @SneakyThrows + public void createSetOfTranslatedRuleIdentifiersForEachFileInTheCsv() { + + List allHeaders = List.of(); + List records = getOldRulesCsvRecords(RuleManagementResources.getOldRulesCsvInputStream()); + + } + + + public Set translateOldRulesFileToNewIdentifiers(File oldRulesFile, ApplicationType applicationType) { + + return translateOldRulesFileToNewIdentifiers(oldRulesFile.toString(), applicationType); + } + + + public Set translateOldRulesFileToNewIdentifiers(String oldRulesFile, ApplicationType applicationType) { + + return translateOldRulesStringToNewIdentifiers(RuleFileIO.getRulesString(oldRulesFile), applicationType); + } + + + public Set translateEscapedOldRulesStringToNewIdentifiers(String escapedOldRulesString, ApplicationType applicationType) { + + return translateOldRulesStringToNewIdentifiers(RuleFileIO.unescapeAndUnWrap(escapedOldRulesString), applicationType); + } + + + public Set translateOldRulesStringToNewIdentifiers(String oldRulesString, ApplicationType applicationType) { + + List oldRules = parseOldRules(oldRulesString); + List records = getOldRulesCsvRecords(RuleManagementResources.getOldRulesCsvInputStream()); + Map> translationPairs = new HashMap<>(); + for (OldRule oldRule : oldRules) { + List translatedIdentifiers = records.stream() + .filter(oldRulesCsvRecord -> oldRulesCsvRecord.code().equals(oldRule.code())) + .map(OldRulesCsvRecord::translatesTo) + .findAny() + .orElse(Collections.emptyList()); + translationPairs.put(oldRule, translatedIdentifiers); + } + boolean allTranslated = true; + for (OldRule oldRule : translationPairs.keySet()) { + if (translationPairs.get(oldRule).isEmpty()) { + allTranslated = false; + records.add(new OldRulesCsvRecord(records.size(), oldRule.code(), oldRule.name(), Collections.emptyList())); + System.out.printf( + "Rule %s has not been translated yet! %nIt has been added to the bottom of the old_rules_with_translations.csv file. To continue, translate the rule and enter its identifier in the csv, then try again!%n", + oldRule.name()); + } + } + if (!allTranslated) { + String formattedAsCsv = formatAsCsv(records); + String fileLocation = RuleManagementResources.createTempOldRulesCsv(formattedAsCsv); + System.out.printf("CSV File with updated values is located at %s%n", fileLocation); + throw new IllegalArgumentException("Non translated Rule found!"); + } + RuleFileBluePrint bluePrint = RuleFileParser.buildBluePrintFromAllRulesFile(applicationType); + + translationPairs.forEach((key, value) -> System.out.printf("Rule %s will be translated to %s \n", + key.name(), + String.format("%s: %s", value, value.stream().map(bluePrint::findRuleByIdentifier).flatMap(Collection::stream).map(BasicRule::name).toList()))); + + return Stream.concat(// + Stream.of(RuleIdentifier.fromRuleType(RuleType.X), + RuleIdentifier.fromRuleType(RuleType.FA), + RuleIdentifier.fromRuleType(RuleType.LDS), + RuleIdentifier.fromRuleType(RuleType.MAN)),// + translationPairs.values().stream().flatMap(Collection::stream)) + .map(ruleIdentifier -> new RuleIdentifier(ruleIdentifier.type(), ruleIdentifier.unit(), null)) + .collect(Collectors.toSet()); + } + + + @SneakyThrows + public String formatAsCsv(List records) { + + StringWriter sw = new StringWriter(); + CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setHeader(HEADERS.toArray(String[]::new)).build(); + try (final CSVPrinter printer = new CSVPrinter(sw, csvFormat)) { + for (OldRulesCsvRecord record : records) { + printer.printRecord(Stream.of(record.id, RuleFileIO.escapeAndWrap(record.names), RuleFileIO.escapeAndWrap(record.code), record.translatesTo)); + } + } + return sw.toString(); + } + + + @SneakyThrows + private List parseOldRules(String oldRuleString) { + + DrlParser parser = new DrlParser(LanguageLevelOption.DRL6); + PackageDescr packageDescr = parser.parse(false, oldRuleString); + List oldRules = new LinkedList<>(); + for (RuleDescr rule : packageDescr.getRules()) { + if (!rule.isRule()) { + continue; + } + oldRules.add(OldRule.fromRuleDescr(rule, oldRuleString)); + } + return oldRules; + } + + + @SneakyThrows + public List getOldRulesCsvRecords(InputStream allRulesCsvInputStream) { + + List parsedRecords = new LinkedList<>(); + try (Reader reader = new InputStreamReader(allRulesCsvInputStream)) { + Iterable records = CSVFormat.DEFAULT.withHeader().withSkipHeaderRecord().parse(reader); + for (CSVRecord record : records) { + String[] values = record.values(); + parsedRecords.add(new OldRulesCsvRecord(Long.parseLong(values[0]), + RuleFileIO.unescapeAndUnWrap(values[2]), + RuleFileIO.unescapeAndUnWrap(values[1]), + parseRuleIdentifiers(values[3]))); + } + } + return parsedRecords; + } + + + private List parseRuleIdentifiers(String value) { + + if (value.startsWith("[")) { + value = value.substring(1); + } + if (value.endsWith("]")) { + value = value.substring(0, value.length() - 1); + } + if (value.isEmpty() || value.isBlank()) { + return Collections.emptyList(); + } + List ruleIdentifiers = new LinkedList<>(); + for (String identifier : value.split(", ")) { + ruleIdentifiers.add(RuleIdentifier.fromString(identifier)); + } + return ruleIdentifiers; + } + + + private List parseBooleanList(String[] values) { + + return Arrays.stream(values).map(Boolean::parseBoolean).toList(); + } + + + public record OldRulesCsvRecord(long id, String code, String names, List translatesTo) { + + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/utils/RuleFileIO.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/utils/RuleFileIO.java new file mode 100644 index 00000000..dc738de0 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/utils/RuleFileIO.java @@ -0,0 +1,71 @@ +package com.knecon.fforesight.utility.rules.management.utils; + +import java.io.File; +import java.io.FileInputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import com.github.javaparser.utils.StringEscapeUtils; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + +@UtilityClass +public final class RuleFileIO { + + public String unescapeAndUnWrap(String rulesString) { + + if (rulesString.isBlank() || rulesString.isEmpty()) { + return rulesString; + } + rulesString = rulesString.substring(1, rulesString.length() - 1); + rulesString = rulesString.translateEscapes(); + return rulesString; + } + + + @SneakyThrows + public String unescapeAndUnWrap(File rulesFile) { + + try (var inputStream = new FileInputStream(rulesFile)) { + return unescapeAndUnWrap(new String(inputStream.readAllBytes())); + } + } + + + public String escape(String rulesString) { + + return StringEscapeUtils.escapeJava(rulesString); + } + + + public String escapeAndWrap(String rulesString) { + + rulesString = StringEscapeUtils.escapeJava(rulesString); + rulesString = "\"" + rulesString + "\""; + return rulesString; + } + + + @SneakyThrows + public String getRulesString(String input) { + + String rulesString; + try (var inputStream = new FileInputStream(input)) { + rulesString = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + if (input.endsWith(".txt") && rulesString.startsWith("\"")) { + rulesString = RuleFileIO.unescapeAndUnWrap(rulesString); + } + } + return rulesString; + } + + @SneakyThrows + public Stream streamAllRuleFilesInDirectory(Path input){ + + return Files.walk(input).filter(path -> path.getFileName().toString().equals("rules.txt") || path.getFileName().toString().equals("rules.drl")).map(Path::toFile); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/resources/all_redact_manager_rules.drl b/redaction-service-v1/rules-management/src/main/resources/all_redact_manager_rules.drl new file mode 100644 index 00000000..ad8fa6ba --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/resources/all_redact_manager_rules.drl @@ -0,0 +1,1550 @@ +package drools + +import static java.lang.String.format; +import static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.anyMatch; +import static com.iqser.red.service.redaction.v1.server.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.model.document.*; +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.EntityType; +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.MatchedRule +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.Table; +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.Document; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Paragraph; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.SectionIdentifier; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Footer; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Header; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType; +import com.iqser.red.service.redaction.v1.server.model.document.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.AtomicTextBlock; +import com.iqser.red.service.redaction.v1.server.model.document.textblock.ConcatenatedTextBlock; +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.DictionaryModel; +import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; +import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService; +import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; +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.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.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.AnnotationStatus; + +global Document document +global EntityCreationService entityCreationService +global ManualChangesApplicationService manualChangesApplicationService +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.HINT, $section), + entityCreationService.byString("BL", "must_redact", EntityType.HINT, $section) + ).forEach(entity -> entity.skip("SYN.0.0", "hint_only")); + end + + +// Rule unit: SYN.1 +rule "SYN.1.0: Recommend CTL/BL laboratory that start with BL or CTL" + when + $section: Section(containsString("CT") || containsString("BL")) + then + /* Regular expression: ((\b((([Cc]T(([1ILli\/])| L|~P))|(BL))[\. ]?([\dA-Ziltphz~\/.:!]| ?[\(',][Ppi](\(e)?|([\(-?']\/))+( ?[\(\/\dA-Znasieg]+)?)\b( ?\/? ?\d+)?)|(\bCT[L1i]\b)) */ + entityCreationService.byRegexIgnoreCase("((\\b((([Cc]T(([1ILli\\/])| L|~P))|(BL))[\\. ]?([\\dA-Ziltphz~\\/.:!]| ?[\\(',][Ppi](\\(e)?|([\\(-?']\\/))+( ?[\\(\\/\\dA-Znasieg]+)?)\\b( ?\\/? ?\\d+)?)|(\\bCT[L1i]\\b))", "CBI_address", EntityType.RECOMMENDATION, $section) + .forEach(entity -> entity.skip("SYN.1.0", "")); + end + + +//------------------------------------ CBI rules ------------------------------------ + +// Rule unit: CBI.0 +rule "CBI.0.0: Redact CBI Authors (non vertebrate Study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_author", dictionaryEntry) + then + $entity.redact("CBI.0.0", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"); + end + +rule "CBI.0.1: Redact CBI Authors (vertebrate Study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_author", dictionaryEntry) + then + $entity.redact("CBI.0.1", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + end + + +// Rule unit: CBI.1 +rule "CBI.1.0: Do not redact CBI Address (non vertebrate Study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_address", dictionaryEntry) + then + $entity.skip("CBI.1.0", "Address found for Non Vertebrate Study"); + end + +rule "CBI.1.1: Redact CBI Address (vertebrate Study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_address", dictionaryEntry) + then + $entity.redact("CBI.1.1", "Address found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + end + + +// Rule unit: CBI.2 +rule "CBI.2.0: Do not redact genitive CBI Author" + when + $entity: TextEntity(type == "CBI_author", anyMatch(textAfter, "['’’'ʼˈ´`‘′ʻ’']s")) + then + entityCreationService.byTextRange($entity.getTextRange(), "CBI_author", EntityType.FALSE_POSITIVE, document) + .ifPresent(falsePositive -> falsePositive.skip("CBI.2.0", "Genitive Author found")); + end + + +// Rule unit: CBI.3 +rule "CBI.3.0: Redacted because Section contains a 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 a 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: Do not redact because Section does not contain a 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: Do not redact because table row does not contain a 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: Do not 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: Do not 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.6 +rule "CBI.6.0: Do not redact Names and Addresses if vertebrate but also published_information is found in Section" + when + $section: Section(!hasTables(), + hasEntitiesOfType("vertebrate"), + hasEntitiesOfType("published_information"), + (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + then + $section.getEntitiesOfType(List.of("CBI_author", "CBI_address")) + .forEach(entity -> { + entity.skipWithReferences( + "CBI.6.0", + "vertebrate but also published_information found", + Stream.concat( + $section.getEntitiesOfType("vertebrate").stream(), + $section.getEntitiesOfType("published_information").stream()).toList() + ); + }); + end + +rule "CBI.6.1: Do not redact Names and Addresses if vertebrate but also published_information is found in table row" + when + $table: Table(hasEntitiesOfType("vertebrate"), + hasEntitiesOfType("published_information"), + (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + then + $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate", "published_information")) + .filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address")) + .forEach(entity -> { + entity.skipWithReferences( + "CBI.6.1", + "vertebrate but also published_information found", + Stream.concat( + $table.getEntitiesOfTypeInSameRow("vertebrate", entity).stream(), + $table.getEntitiesOfTypeInSameRow("published_information", entity).stream()).toList() + ); + }); + end + + +// Rule unit: CBI.7 +rule "CBI.7.0: Do not redact Names and Addresses if published information found in Section without tables" + when + $section: Section(!hasTables(), + hasEntitiesOfType("published_information"), + (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + then + $section.getEntitiesOfType(List.of("CBI_author", "CBI_address")) + .forEach(redactionEntity -> { + redactionEntity.skipWithReferences( + "CBI.7.0", + "Published Information found in section", + $section.getEntitiesOfType("published_information") + ); + }); + end + +rule "CBI.7.1: Do not redact Names and Addresses if published information found in same table row" + when + $table: Table(hasEntitiesOfType("published_information"), + (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + then + $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("CBI_author", "CBI_address")) + .forEach(redactionEntity -> { + redactionEntity.skipWithReferences( + "CBI.7.1", + "Published Information found in row", + $table.getEntitiesOfTypeInSameRow("published_information", redactionEntity) + ); + }); + 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 cells 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.redact("CBI.9.0", "Author(s) found", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + +rule "CBI.9.1: Redact all cells 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.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + + +// Rule unit: CBI.10 +rule "CBI.10.0: Redact all cells with Header Author(s) as CBI_author (vertebrate study)" + agenda-group "LOCAL_DICTIONARY_ADDS" + when + 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.redact("CBI.10.0", "Author(s) found", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + end + +rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)" + agenda-group "LOCAL_DICTIONARY_ADDS" + when + 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.redact("CBI.10.1", "Author found", "Article 39(e)(2) 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 cells 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: Do not 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.redact("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.13 +rule "CBI.13.0: Ignore CBI Address recommendations" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_address", entityType == EntityType.RECOMMENDATION) + then + $entity.ignore("CBI.13.0", "Ignore CBI Address Recommendations"); + retract($entity) + end + + +// Rule unit: CBI.14 +rule "CBI.14.0: Redact CBI_sponsor entities if preceded by \"batches produced at\"" + when + $sponsorEntity: TextEntity(type == "CBI_sponsor", textBefore.contains("batches produced at")) + then + $sponsorEntity.redact("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.redact("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.redact("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.redact("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.redact("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: TextEntity(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.addMatchedRules($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: TextEntity(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.addMatchedRules($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.redact("CBI.20.1", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + dictionary.recommendEverywhere(laboratoryEntity); + }); + end + +// Rule unit: CBI.21 +rule "CBI.21.0: Redact short Authors section (non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value == "Yes") + $section: Section(containsAnyStringIgnoreCase("author(s)", "author", "authors"), length() < 50, getTreeId().get(0) <= 20) //TODO: evaluate the reason of this rule + not TextEntity(type == "CBI_author", engines contains Engine.NER) from $section.getEntities() + then + entityCreationService.byRegexIgnoreCase("(?<=author\\(?s\\)?\\s\\n?)([\\p{Lu}\\p{L} ]{5,15}(,|\\n)?){1,3}", "CBI_author", EntityType.ENTITY, $section) + .forEach(entity -> { + entity.redact("CBI.21.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"); + }); + end + +rule "CBI.21.1: Redact short Authors section (vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value == "Yes") + $section: Section(containsAnyStringIgnoreCase("author(s)", "author", "authors"), length() < 50, getTreeId().get(0) <= 20) //TODO: evaluate the reason of this rule + not TextEntity(type == "CBI_author", engines contains Engine.NER) from $section.getEntities() + then + entityCreationService.byRegexIgnoreCase("(?<=author\\(?s\\)?\\s\\n?)([\\p{Lu}\\p{L} ]{5,15}(,|\\n)?){1,3}", "CBI_author", EntityType.ENTITY, $section) + .forEach(entity -> { + entity.redact("CBI.21.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + }); + end + + +// Rule unit: CBI.22 +rule "CBI.22.0: Redact Addresses in Reference Tables for vertebrate studies in non-vertebrate documents" + when + not FileAttribute(label == "Vertebrate Study", value == "Yes") + $table: Table(hasHeader("Vertebrate study Y/N"), 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.isType("CBI_address")) + .forEach(authorEntity -> authorEntity.redact("CBI.22.0", "Redacted because row is a vertebrate study", "Reg (EC) No 1107/2009 Art. 63 (2g)")); + 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: TextEntity(type == "PII", dictionaryEntry) + then + $pii.redact("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: TextEntity(type == "PII", dictionaryEntry) + then + $pii.redact("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.redact("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.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + end + +rule "PII.1.2: Redact typoed Emails with indicator" + when + $section: Section(containsString("@") || containsStringIgnoreCase("mail")) + 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, $section) + .forEach(emailEntity -> emailEntity.redact("PII.1.2", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + + +// Rule unit: PII.2 +rule "PII.2.0: Redact Phone and Fax by RegEx (non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $section: Section(containsString("Contact") || + containsString("Telephone") || + containsString("Phone") || + containsString("Ph.") || + containsString("Fax") || + containsString("Tel") || + containsString("Ter") || + containsString("Mobile") || + containsString("Fel") || + containsString("Fer")) + then + entityCreationService.byRegexIgnoreCase("\\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", "PII", EntityType.ENTITY, 2, $section) + .forEach(contactEntity -> contactEntity.redact("PII.2.0", "Found by Phone and Fax Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + +rule "PII.2.1: Redact Phone and Fax by RegEx (vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $section: Section(containsString("Contact") || + containsString("Telephone") || + containsString("Phone") || + containsString("Ph.") || + containsString("Fax") || + containsString("Tel") || + containsString("Ter") || + containsString("Mobile") || + containsString("Fel") || + containsString("Fer")) + then + entityCreationService.byRegexIgnoreCase("\\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", "PII", EntityType.ENTITY, 2, $section) + .forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + end + +rule "PII.2.2: Redact phone numbers without indicators" + when + $section: Section(containsString("+")) + then + entityCreationService.byRegex("(\\+[\\dO]{1,2} )(\\([\\dO]{1,3}\\))?[\\d\\-O ]{8,15}", "PII", EntityType.ENTITY, $section) + .forEach(entity -> entity.redact("PII.2.2", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + + +// Rule unit: PII.3 +rule "PII.3.0: Redact telephone numbers by RegEx (Non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $section: Section(matchesRegex("[+]\\d{1,}")) + 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) + .forEach(entity -> entity.redact("PII.3.0", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + +rule "PII.3.1: Redact telephone numbers by RegEx (vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $section: Section(matchesRegex("[+]\\d{1,}")) + 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) + .forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "Article 39(e)(2) 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.redact("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 (vertebrate study)" + when + 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.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)")); + end + + +// Rule unit: PII.5 +rule "PII.5.0: Redact line after contact information keywords reduced (non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $contactKeyword: String() from List.of("Contact point:", + "Contact:", + "Alternative contact:", + "European contact:") + $section: Section(containsString($contactKeyword)) + then + entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) + .forEach(contactEntity -> contactEntity.redact("PII.5.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + +rule "PII.5.1: Redact line after contact information keywords reduced (Vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $contactKeyword: String() from List.of("Contact point:", + "Contact:", + "Alternative contact:", + "European contact:") + $section: Section(containsString($contactKeyword)) + then + entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) + .forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + 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.redact("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 (vertebrate study)" + 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.redact("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.redact("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 (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.redact("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 (non vertebrate study)" + 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.redact("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 (vertebrate study)" + 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.redact("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 \"(STUDY) COMPLETION DATE\" (non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) + 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) + .forEach(authorEntity -> authorEntity.redact("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\" (vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) + 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) + .forEach(authorEntity -> authorEntity.redact("PII.9.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + end + + +// Rule unit: PII.10 +rule "PII.10.0: Redact study director abbreviation" + when + $section: Section(containsString("KATH") || containsString("BECH") || containsString("KML")) + then + entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section) + .forEach(entity -> entity.redact("PII.10.0", "Personal information found", "Article 39(e)(3) 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.redact("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: TextEntity(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.addMatchedRules($entityToExpand.getMatchedRuleList())); + end + +// Rule unit: PII.13 +rule "PII.13.0: Add recommendation for PII after Contact Person" + when + $section: Section(containsString("contact person:")) + then + entityCreationService.lineAfterStringsIgnoreCase(List.of("Contact Person", "Contact Person:"), "PII", EntityType.RECOMMENDATION, $section) + .forEach(entity -> entity.skip("PII.13.0", "Line after \"Source:\" in Test Animals Section")); + end + + +// Rule unit: PII.14 +rule "PII.14.0: Recommend title prefixed words as PII" + when + $section: Section(containsAnyString("Dr ", "PD Dr ", "Prof. Dr ", "Dr. med. vet ", "Dr. rer. nat ", "PhD ", "BSc ", "(FH) ", "Mr ", "Mrs ", "Ms ", "Miss ", "Dr.", "PD. Dr.", "Prof. Dr.", "Dr. med. vet.", "Dr. rer. nat.", "PhD.", "BSc.", "(FH).", "Mr.", "Mrs.", "Ms.", "Miss.")) + then + entityCreationService.byRegex("((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})", "PII", EntityType.ENTITY, 3, $section); + end + + +//------------------------------------ Other rules ------------------------------------ + +// Rule unit: ETC.0 +rule "ETC.0.0: Purity Hint" + when + $section: Section(containsStringIgnoreCase("purity")) + then + 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")); + end + + +// 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.redact("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.redact("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.redact("ETC.2.1", "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.redact("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.redact("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: TextEntity(type == "dossier_redaction") + then + $dossierRedaction.redact("ETC.4.0", "Specification of impurity found", "Article 39(e)(3) of Regulation (EC) No 178/2002"); + end + +rule "ETC.4.1: Redact dossier dictionary entries" + when + $dossierRedaction: TextEntity(type == "dossier_redaction") + then + $dossierRedaction.redact("ETC.4.1", "Dossier Redaction found", "Article 39(1)(2) of Regulation (EC) No 178/2002"); + end + +rule "ETC.4.2: Redact dossier dictionary entries" + when + $dossierRedaction: TextEntity(type == "dossier_redaction") + then + $dossierRedaction.redact("ETC.4.2", "Dossier redaction found", "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 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: TextEntity(type == "dossier_redaction") + then + $dossierRedaction.ignore("ETC.5.0", "Ignore dossier redactions, when not confidential"); + $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.redact("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(), containsAnyString("DATA REQUIREMENT(S):", "TEST GUIDELINE(S):") && containsAnyString("OECD", "EPA", "OPPTS")) + then + RedactionSearchUtility.findTextRangesByRegex("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.redact("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.redact("ETC.8.1", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + end + +// Rule unit: ETC.9 +rule "ETC.9.0: Redact skipped impurities" + when + FileAttribute(label == "Redact Skipped Impurities", value == "Yes") + $skippedImpurities: TextEntity(type == "skipped_impurities") + then + $skippedImpurities.redact("ETC.9.0", "Occasional Impurity found", "Article 63(2)(b) of Regulation (EC) No 1107/2009"); + end + +rule "ETC.9.1: Redact impurities" + when + FileAttribute(label == "Redact Impurities", value == "Yes") + $skippedImpurities: TextEntity(type == "impurities") + then + $skippedImpurities.redact("ETC.9.1", "Impurity found", "Article 63(2)(b) of Regulation (EC) No 1107/2009"); + end + +// Rule unit: ETC.10 +rule "ETC.10.0: Redact Product Composition Information" + when + $compositionInformation: TextEntity(type == "product_composition") + then + $compositionInformation.redact("ETC.10.0", "Product Composition Information found", "Article 63(2)(d) of Regulation (EC) No 1107/2009"); + end + +// Rule unit: ETC.11 +rule "ETC.11.0: Recommend first line in table cell with name and address of owner" + when + $table: Table(hasHeader("Name and Address of Owner / Tenant"), containsString("trial site")) + $header: TableCell(isHeader(), containsString("Name and Address of Owner / Tenant")) from $table.streamTableCells().toList() + $tableCell: TableCell(col == $header.col, row == 2) from $table.streamTableCells().toList() + then + entityCreationService.bySemanticNode($tableCell, "PII", EntityType.RECOMMENDATION) + .ifPresent(redactionEntity -> redactionEntity.redact("ETC.11.0", "Trial Site owner and address found", "Article 39(e)(3) 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 + + +// Rule unit: AI.2 +rule "AI.2.0: Add all NER Entities of any type except CBI_author" + salience 999 + when + nerEntities: NerEntities() + then + nerEntities.getNerEntityList().stream() + .filter(nerEntity -> !nerEntity.type().equals("CBI_author")) + .forEach(nerEntity -> entityCreationService.byNerEntity(nerEntity, nerEntity.type().toLowerCase(), EntityType.RECOMMENDATION, document)); + end + + +// Rule unit: AI.3 +rule "AI.3.0: Recommend authors from AI as PII" + salience 999 + when + nerEntities: NerEntities(hasEntitiesOfType("CBI_author")) + then + nerEntities.streamEntitiesOfType("CBI_author") + .forEach(nerEntity -> entityCreationService.byNerEntity(nerEntity, "PII", EntityType.RECOMMENDATION, document)); + end + +//------------------------------------ Manual redaction rules ------------------------------------ + +// Rule unit: MAN.0 +rule "MAN.0.0: Apply manual resize redaction" + salience 128 + when + $resizeRedaction: ManualResizeRedaction($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate)) + $entityToBeResized: TextEntity(matchesAnnotationId($id)) + then + manualChangesApplicationService.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, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate)) + $imageToBeResized: Image(id == $id) + then + manualChangesApplicationService.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($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToBeRemoved: TextEntity(matchesAnnotationId($id)) + then + $entityToBeRemoved.getManualOverwrite().addChange($idRemoval); + 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($id: annotationId, status == AnnotationStatus.APPROVED) + $imageEntityToBeRemoved: Image($id == id) + then + $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); + update($imageEntityToBeRemoved); + retract($idRemoval); + update($imageEntityToBeRemoved.getParent()); + end + + +// Rule unit: MAN.2 +rule "MAN.2.0: Apply force redaction" + salience 128 + when + $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToForce: TextEntity(matchesAnnotationId($id)) + then + $entityToForce.getManualOverwrite().addChange($force); + update($entityToForce); + $entityToForce.getIntersectingNodes().forEach(node -> update(node)); + retract($force); + end + +rule "MAN.2.1: Apply force redaction to images" + salience 128 + when + $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED) + $imageToForce: Image(id == $id) + then + $imageToForce.getManualOverwrite().addChange($force); + update($imageToForce); + update($imageToForce.getParent()); + retract($force); + end + + +// Rule unit: MAN.3 +rule "MAN.3.0: Apply entity recategorization" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type != $type) + then + $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node)); + manualChangesApplicationService.recategorize($entityToBeRecategorized, $recategorization); + retract($recategorization); + // Entity is copied and inserted, so the old entity needs to be retracted to avoid duplication. + retract($entityToBeRecategorized); + end + +rule "MAN.3.1: Apply entity recategorization of same type" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type == $type) + then + $entityToBeRecategorized.getManualOverwrite().addChange($recategorization); + retract($recategorization); + end + +rule "MAN.3.2: Apply image recategorization" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $imageToBeRecategorized: Image($id == id) + then + manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); + update($imageToBeRecategorized); + update($imageToBeRecategorized.getParent()); + retract($recategorization); + end + + +// Rule unit: MAN.4 +rule "MAN.4.0: Apply legal basis change" + salience 128 + when + $legalbasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED) + $imageToBeRecategorized: Image($id == id) + then + $imageToBeRecategorized.getManualOverwrite().addChange($legalbasisChange); + end + +rule "MAN.4.1: Apply legal basis change" + salience 128 + when + $legalBasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToBeChanged: TextEntity(matchesAnnotationId($id)) + then + $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange); + end + + +//------------------------------------ Entity merging rules ------------------------------------ + +// Rule unit: X.0 +rule "X.0.0: Remove Entity contained by Entity of same type" + salience 65 + when + $larger: TextEntity($type: type, $entityType: entityType, active()) + $contained: TextEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !hasManualChanges(), active()) + then + $contained.remove("X.0.0", "remove Entity contained by Entity of same type"); + retract($contained); + end + + +// Rule unit: X.1 +rule "X.1.0: Merge intersecting Entities of same type" + salience 64 + when + $first: TextEntity($type: type, $entityType: entityType, !resized(), active()) + $second: TextEntity(intersects($first), type == $type, entityType == $entityType, this != $first, !hasManualChanges(), active()) + then + TextEntity mergedEntity = entityCreationService.mergeEntitiesOfSameType(List.of($first, $second), $type, $entityType, document); + $first.remove("X.1.0", "merge intersecting Entities of same type"); + $second.remove("X.1.0", "merge intersecting Entities of same type"); + retract($first); + retract($second); + mergedEntity.getIntersectingNodes().forEach(node -> update(node)); + end + + +// Rule unit: X.2 +rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" + salience 64 + when + $falsePositive: TextEntity($type: type, entityType == EntityType.FALSE_POSITIVE, active()) + $entity: TextEntity(containedBy($falsePositive), type == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges(), active()) + then + $entity.getIntersectingNodes().forEach(node -> update(node)); + $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); + retract($entity) + end + + +// Rule unit: X.3 +rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION" + salience 64 + when + $falseRecommendation: TextEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION, active()) + $recommendation: TextEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); + retract($recommendation); + end + + +// Rule unit: X.4 +rule "X.4.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY with same type" + salience 256 + when + $entity: TextEntity($type: type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $recommendation: TextEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $entity.addEngines($recommendation.getEngines()); + $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY with same type"); + retract($recommendation); + end + + +// Rule unit: X.5 +rule "X.5.0: Remove Entity of type RECOMMENDATION when contained by ENTITY" + salience 256 + when + $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $recommendation: TextEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when contained by ENTITY"); + retract($recommendation); + end + + +// Rule unit: X.6 +rule "X.6.0: Remove Entity of lower rank, when contained by by entity of type ENTITY" + salience 32 + when + $higherRank: TextEntity($type: type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $lowerRank: TextEntity(containedBy($higherRank), type != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges(), active()) + then + $lowerRank.getIntersectingNodes().forEach(node -> update(node)); + $lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY"); + retract($lowerRank); + end + + +rule "X.6.1: remove Entity of higher rank, when intersected by entity of type ENTITY and length of lower rank Entity is bigger than the higher rank Entity" + salience 32 + when + $higherRank: TextEntity($type: type, $value: value, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $lowerRank: TextEntity(intersects($higherRank), type != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges(), active(), $lowerRank.getValue().length() > $value.length()) + then + $higherRank.getIntersectingNodes().forEach(node -> update(node)); + $higherRank.remove("X.6.1", "remove Entity of higher rank, when intersected by entity of type ENTITY and length of lower rank Entity is bigger than the higher rank Entity"); + retract($higherRank); + 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 matchedRules = $dictionaryModel.getLocalEntriesWithMatchedRules().get(entity.getValue()); + entity.addMatchedRules(matchedRules); + }); + end diff --git a/redaction-service-v1/rules-management/src/main/resources/all_rules_documine.drl b/redaction-service-v1/rules-management/src/main/resources/all_rules_documine.drl new file mode 100644 index 00000000..935ef3dc --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/resources/all_rules_documine.drl @@ -0,0 +1,1397 @@ +package drools + +import static java.lang.String.format; +import static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.anyMatch; +import static com.iqser.red.service.redaction.v1.server.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.model.document.*; +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.EntityType; +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.MatchedRule +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.Table; +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.Document; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Paragraph; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.SectionIdentifier; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Footer; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Header; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType; +import com.iqser.red.service.redaction.v1.server.model.document.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.AtomicTextBlock; +import com.iqser.red.service.redaction.v1.server.model.document.textblock.ConcatenatedTextBlock; +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.DictionaryModel; +import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; +import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService; +import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; +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.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.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.AnnotationStatus; + +global Document document +global EntityCreationService entityCreationService +global ManualChangesApplicationService manualChangesApplicationService +global Dictionary dictionary + +//------------------------------------ queries ------------------------------------ + +query "getFileAttributes" + $fileAttribute: FileAttribute() + end + +//------------------------------------ H rules ------------------------------------ + +// Rule unit: H.0 +rule "H.0.0: retract table of contents page" + when + $page: Page(getMainBodyTextBlock().getSearchText().contains("........") || (getMainBodyTextBlock().getSearchText().contains("APPENDICES") && getMainBodyTextBlock().getSearchText().contains("TABLES"))) + $node: SemanticNode(onPage($page.getNumber()), !onPage($page.getNumber() -1), getType() != NodeType.IMAGE) + then + retract($node); + end + + +// Rule unit: H.1 +rule "H.1.0: Ignore Table of Contents" + salience 10 + when + $tocHeadline: Headline(containsString("CONTENTS")) + $page: Page() from $tocHeadline.getParent().getPages() + $node: SemanticNode(this != $tocHeadline, getType() != NodeType.IMAGE, onPage($page.getNumber()), !onPage($page.getNumber() -1)) + then + retract($node); + end + + +// Rule unit: H.2 +rule "H.2.0: Show headlines" + when + $headline: Headline() + then + entityCreationService.bySemanticNode($headline, "headline", EntityType.HINT); + end + + +// Rule unit: H.3 +rule "H.3.0: Study Type File Attribute" + when + not FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","438","439","471","487")) + $section: Section( + (containsString("DATA REQUIREMENT") || containsString("TEST GUIDELINE") || containsString("MÉTODO(S) DE REFERÊNCIA(S):")) + ,(containsString("OECD") || containsString("EPA") || containsString("OPPTS")) + ) + then + Stream.of(RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|(?:.{5,40}(?:Number |Procedure |Guideline )))(4[\\d]{2})", 1, $section.getTextBlock()), + RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", 1, $section.getTextBlock()), + RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD) Guideline (4\\d{2})", 1, $section.getTextBlock())).flatMap(Collection::stream).findFirst() + .map(textRange -> $section.getTextBlock().subSequence(textRange).toString()) + .map(value -> FileAttribute.builder().label("OECD Number").value(value).build()) + .ifPresent(fileAttribute -> insert(fileAttribute)); + end + +rule "H.3.1: Study Type File Attribute in Headlines" + when + not FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","438","439","471","487")) + $page: Page($pageNumber:number, + getMainBodyTextBlock().getSearchText().contains("DATA REQUIREMENT") + || getMainBodyTextBlock().getSearchText().contains("TEST GUIDELINE") + || getMainBodyTextBlock().getSearchText().contains("MÉTODO(S) DE REFERÊNCIA(S):")) + $headline: Headline(onPage($pageNumber), containsString("OECD") || containsString("EPA")|| containsString("OPPTS")) + then + Stream.of(RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|(?:.{5,40}(?:Number |Procedure |Guideline )))(4[\\d]{2})", 1, $headline.getTextBlock()), + RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", 1, $headline.getTextBlock()), + RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD) Guideline (4\\d{2})", 1, $headline.getTextBlock())).flatMap(Collection::stream).findFirst() + .map(textRange -> $headline.getTextBlock().subSequence(textRange).toString()) + .map(value -> FileAttribute.builder().label("OECD Number").value(value).build()) + .ifPresent(fileAttribute -> insert(fileAttribute)); + end + +//------------------------------------ General documine rules ------------------------------------ + +// Rule unit: DOC.1 +rule "DOC.1.0: Guidelines" + when + $section: Section( + ( + containsString("DATA REQUIREMENT") + || containsString("TEST GUIDELINE") + || containsString("MÉTODO(S) DE REFERÊNCIA(S):") + ) + && ( + containsString("OECD") + || containsString("EPA") + || containsString("OPPTS") + ) + ) + then + entityCreationService.byRegex("OECD (No\\.? )?\\d{3}( \\(\\d{4}\\))?", "oecd_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline found", "n-a") + ); + entityCreationService.byRegex("OECD[\\s,]{1}(?:.{1,40}.(?>Procedure|Method).{1,20}\\d{3,4}(?>.{1,100}\\d{4}\\))?|\\[.{1,20}.Skin.{1,20}\\]|[\\d\\s,\\(\\)]{7,10}|[\\w\\.\\s]{1,15}[\\d]{3}\\s\\(\\d{4}\\)|.{0,20}[N|n]umber\\s\\d{3}.{0,1}|Test Guideline \\d{3})", "oecd_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline found", "n-a") + ); + entityCreationService.byRegex("EPA (OPPTS )?\\d{3}[. ]\\d{4}( \\(\\d{4}\\))?", "epa_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "EPA Guideline found", "n-a") + ); + entityCreationService.byRegex("EC (Directive )?(No\\.? )?\\d{3,4}\\/\\d{3,4}((,? B(\\.| )\\d{1,2}\\.?)? \\(\\d{4}\\))?", "ec_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "EC Guideline found", "n-a") + ); + entityCreationService.byRegex("Commission Regulation \\(EC\\) No \\d{3}\\/\\d{4}", "ec_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "EC Guideline found", "n-a") + ); + entityCreationService.byRegex("OECD Method 4\\d{2}.{5,40}\\(.{5,40}\\d{4}\\)", "oecd_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("OPPTS (Guideline Number )?\\d{3}\\.\\d{4}( \\(\\d{4}\\))?", "epa_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "EPA Guideline found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2})", "oecd_guideline_number", EntityType.ENTITY,1, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline no. found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2}),?\\s\\(?(\\d{4})\\)?", "oecd_guideline_year", EntityType.ENTITY,2, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)[\\w\\s,\\[\\]]{1,10}\\((\\d{4})\\)\\s(4[\\d]{2})", "oecd_guideline_year", EntityType.ENTITY,1, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_number", EntityType.ENTITY,1, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline number found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_year", EntityType.ENTITY,2, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD) Guideline (4\\d{2})", "oecd_guideline_number", EntityType.ENTITY,1, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline number found", "n-a") + ); + entityCreationService.byRegex("OECD Guideline 4\\d{2}", "oecd_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline found", "n-a") + ); + end + +rule "DOC.1.2: Guidelines" + when + $section: Section( + ( + containsString("DATA REQUIREMENT") + || containsString("TEST GUIDELINE") + || containsString("MÉTODO(S) DE REFERÊNCIA(S):") + ) + && ( + containsString("OECD") + || containsString("EPA") + || containsString("OPPTS") + ) + && ( + hasEntitiesOfType("oecd_guideline") + || hasEntitiesOfType("epa_guideline") + || hasEntitiesOfType("ec_guideline") + ) + ) + then + $section.getEntitiesOfType(List.of("oecd_guideline","ec_guideline", "epa_guideline")).forEach(entity -> { + entity.apply("DOC.1.2", "OECD guideline found.", "n-a"); + }); + end + +rule "DOC.1.3: Guidelines" + when + $section: Section( + ( + hasEntitiesOfType("oecd_guideline") + || hasEntitiesOfType("epa_guideline") + || hasEntitiesOfType("ec_guideline") + ) + && !( + ( + containsString("DATA REQUIREMENT") + || containsString("TEST GUIDELINE") + || containsString("MÉTODO(S) DE REFERÊNCIA(S):") + ) + && ( + containsString("OECD") + || containsString("EPA") + || containsString("OPPTS") + ) + ) + ) + then + $section.getEntitiesOfType(List.of("oecd_guideline", "ec_guideline", "epa_guideline")).forEach(entity -> { + entity.remove("DOC.1.3", "removed"); + retract(entity); + }); + end + +rule "DOC.1.4: Guideline in Headlines" + when + $page: Page($pageNumber:number, + getMainBodyTextBlock().getSearchText().contains("DATA REQUIREMENT") + || getMainBodyTextBlock().getSearchText().contains("TEST GUIDELINE") + || getMainBodyTextBlock().getSearchText().contains("MÉTODO(S) DE REFERÊNCIA(S):")) + $headline: Headline(onPage($pageNumber), containsString("OECD") || containsString("EPA")|| containsString("OPPTS")) + then + entityCreationService.byRegex("OECD (No\\.? )?\\d{3}( \\(\\d{4}\\))?", "oecd_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline found", "n-a") + ); + entityCreationService.byRegex("OECD[\\s,]{1}(?:.{1,40}.(?>Procedure|Method).{1,20}\\d{3,4}(?>.{1,100}\\d{4}\\))?|\\[.{1,20}.Skin.{1,20}\\]|[\\d\\s,\\(\\)]{7,10}|[\\w\\.\\s]{1,15}[\\d]{3}\\s\\(\\d{4}\\)|.{0,20}[N|n]umber\\s\\d{3}.{0,1}|Test Guideline \\d{3})", "oecd_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline found", "n-a") + ); + entityCreationService.byRegex("EPA (OPPTS )?\\d{3}[. ]\\d{4}( \\(\\d{4}\\))?", "epa_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "EPA Guideline found", "n-a") + ); + entityCreationService.byRegex("EC (Directive )?(No\\.? )?\\d{3,4}\\/\\d{3,4}((,? B(\\.| )\\d{1,2}\\.?)? \\(\\d{4}\\))?", "ec_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "EC Guideline found", "n-a") + ); + entityCreationService.byRegex("Commission Regulation \\(EC\\) No \\d{3}\\/\\d{4}", "ec_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "EC Guideline found", "n-a") + ); + entityCreationService.byRegex("OECD Method 4\\d{2}.{5,40}\\(.{5,40}\\d{4}\\)", "oecd_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("OPPTS (Guideline Number )?\\d{3}\\.\\d{4}( \\(\\d{4}\\))?", "epa_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "EPA Guideline found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2})", "oecd_guideline_number", EntityType.ENTITY,1, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline no. found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2}),?\\s\\(?(\\d{4})\\)?", "oecd_guideline_year", EntityType.ENTITY,2, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)[\\w\\s,\\[\\]]{1,10}\\((\\d{4})\\)\\s(4[\\d]{2})", "oecd_guideline_year", EntityType.ENTITY,1, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_number", EntityType.ENTITY,1, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline number found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_year", EntityType.ENTITY,2, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD) Guideline (4\\d{2})", "oecd_guideline_number", EntityType.ENTITY,1, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline number found", "n-a") + ); + entityCreationService.byRegex("OECD Guideline 4\\d{2}", "oecd_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline found", "n-a") + ); + end + +// Rule unit: DOC.2 +rule "DOC.2.0: Report number" + when + $section: Section(containsString("LABORATORY PROJECT ID") , containsString("Report Number:")) + then + entityCreationService.lineAfterString("Report Number:", "report_number", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> { + entity.apply("DOC.2.0", "Report number found", "n-a"); + }); + end + + +// Rule unit: DOC.3 +rule "DOC.3.0: Experimental Starting Date" + when + $section: Section(containsString("Experimental I. Starting Date:") || containsString("Experimental II. Starting Date:") || containsStringIgnoreCase("experimental start date") || containsStringIgnoreCase("experimental starting date")) + then + entityCreationService.lineAfterStrings( + List.of("Experimental start date", + "Experimental start date:", + "Experimental Starting Date", + "Experimental Starting Date:", + "Experimental starting date", + "Experimental starting date:", + "Experimental Start Date", + "Experimental Start Date:", + "Experimental I. Starting Date:", + "Experimental II. Starting Date:"), "experimental_start_date", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.3.0", "Experimental start date found", "n-a"); + }); + end + + +// Rule unit: DOC.4 +rule "DOC.4.0: Experimental Completion Date" + when + $section: Section(containsStringIgnoreCase("experimental termination date") || containsStringIgnoreCase("experimental completion date")) + then + entityCreationService.lineAfterStrings( + List.of("Experimental termination date", + "Experimental termination date:", + "Experimental Completion Date", + "Experimental Completion Date:", + "Experimental completion date", + "Experimental completion date:", + "Experimental Termination Date", + "Experimental Termination Date:"), "experimental_end_date", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.4.0", "Experimental end date found", "n-a"); + }); + end + + +// Rule unit: DOC.5 +rule "DOC.5.0: Ignore species and strain in irrelevant study types" + salience 1 + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("406","428","438","439","471","474","487")) + $section: Section(hasEntitiesOfType("species") || hasEntitiesOfType("strain")) + then + $section.getEntitiesOfType(List.of("species", "strain")).forEach(entity -> { + entity.remove("DOC.5.0", "removed"); + retract(entity); + }); + end + +rule "DOC.5.1: Hide all skipped species and strains except in the relevant sections" + salience 1 + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436")) + $section: Section( + (hasEntitiesOfType("species") || hasEntitiesOfType("strain")) + && !( + anyHeadlineContainsStringIgnoreCase("test system") + || anyHeadlineContainsStringIgnoreCase("animals") + || anyHeadlineContainsStringIgnoreCase("specification") + ) + ) + then + $section.getEntitiesOfType(List.of("species", "strain")).forEach(entity -> { + entity.remove("DOC.5.1", "removed"); + retract(entity); + }); + end + +rule "DOC.5.2: Species" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436")) + $section: Section(hasEntitiesOfType("species")) + then + $section.getEntitiesOfType("species").forEach(entity -> { + entity.apply("DOC.5.2", "Species found.", "n-a"); + entity.setValue(entity.getValue().toLowerCase()); + }); + end + +rule "DOC.5.3: Strain" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436")) + $section: Section( + hasEntitiesOfType("species") + && hasEntitiesOfType("strain") + && ( + anyHeadlineContainsStringIgnoreCase("test system") + || anyHeadlineContainsStringIgnoreCase("animals") + || anyHeadlineContainsStringIgnoreCase("specification") + ) + ) + then + $section.getEntitiesOfType("strain").forEach(entity -> { + entity.apply("DOC.5.3", "Strain found.", "n-a"); + }); + end + + +// Rule unit: DOC.6 +rule "DOC.6.0: study title by document structure" + when + $table: Table(onPage(1), + (containsString("Final Report") || containsString("SPL")), + numberOfRows == 1, + numberOfCols == 1) + $tableCell: TableCell(row == 1, col == 1) from $table.streamTableCells().toList() + $paragraph: Paragraph(previousSibling.isPresent(), nextSibling.isPresent()) from $tableCell.streamChildren().toList() + then + entityCreationService.bySemanticNode($paragraph, "title", EntityType.ENTITY).ifPresent(entity -> { + entity.apply("DOC.6.0", "Study title found", "n-a"); + }); + end + +rule "DOC.6.1: study title" + when + $table: Table(onPage(1), (containsString("Final Report") || containsString("SPL"))) + then + entityCreationService.byRegexWithLineBreaksIgnoreCase("(?<=\\n)[\\w\\W]{1,300}(?=\\nFinal Report)", "title", EntityType.ENTITY, $table).findFirst().ifPresent(entity -> { + entity.apply("DOC.6.1", "Title found", "n-a"); + }); + end + +rule "DOC.6.2: study title" + when + not Table(onPage(1), (containsString("Final Report") || containsString("SPL"))) + $section: Section(onPage(1), (containsString("Final Report") || containsString("SPL"))) + then + entityCreationService.byRegexWithLineBreaksIgnoreCase("(?<=\\n)[\\w\\W]{1,300}(?=\\nFinal Report)", "title", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> { + entity.apply("DOC.6.2", "Title found", "n-a"); + }); + end + + +// Rule unit: DOC.7 +rule "DOC.7.0: Performing Laboratory (Name)" + when + $section: Section(containsString("PERFORMING LABORATORY:")) + then + entityCreationService.lineAfterString("PERFORMING LABORATORY:", "laboratory_name", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> { + entity.apply("DOC.7.0", "Performing Laboratory found", "n-a"); + }); + end + +rule "DOC.7.1: Performing Laboratory (Country)" + when + nerEntities: NerEntities(hasEntitiesOfType("COUNTRY")) + $section: Section(containsString("PERFORMING LABORATORY:")) + then + nerEntities.streamEntitiesOfType("COUNTRY") + .filter(nerEntity -> $section.getTextRange().contains(nerEntity.textRange())) + .map(nerEntity -> entityCreationService.byNerEntity(nerEntity, "laboratory_country", EntityType.ENTITY, $section)) + .forEach(entity -> { + entity.apply("DOC.7.1", "Performing Laboratory found", "n-a"); + }); + end + +rule "DOC.7.2: Performing Laboratory (Country & Name) from dict" + when + $section: Section(containsString("PERFORMING LABORATORY:") || (containsString("PERFORMING") && containsString("LABORATORY:"))) + $countryOrNameFromDictionary: TextEntity(type == "laboratory_country" || type == "laboratory_name", $type: type, isDictionaryEntry()) from $section.getEntities() + then + $countryOrNameFromDictionary.apply("DOC.7.2", "Performing " + $type + " dictionary entry found."); + end + +rule "DOC.7.3: Performing Laboratory (Country) from dict" + when + $section: Section( + (hasEntitiesOfType("laboratory_country") || hasEntitiesOfType("laboratory_name")) + && !(containsString("PERFORMING LABORATORY:") || (containsString("PERFORMING") && containsString("LABORATORY:"))) + ) + then + $section.getEntitiesOfType(List.of("laboratory_country", "laboratory_name")).forEach(entity -> { + entity.remove("DOC.7.3", "removed"); + retract(entity); + }); + end + + +// Rule unit: DOC.8 +rule "DOC.8.0: GLP Study" + when + $headline: Headline(containsString("GOOD LABORATORY PRACTICE COMPLIANCE") + || containsString("GOOD LABORATORY PRACTICE COMPLIANCE STATEMENT") + || (containsString("DECLARACAO DE CONFORMIDADE") && containsString("PRATICAS DE LABORATORIO")) + || containsString("GLP Certificate") + || containsString("GLP Certificates") + || containsString("GOOD LABORATORY PRACTICE (GLP) CERTIFICATE") + || containsString("Good Laboratory Practice Certificate") + || containsString("STATEMENT OF GLP COMPLIANCE AND AUTHENTICATION")) + then + entityCreationService.bySemanticNode($headline, "glp_study", EntityType.ENTITY).ifPresent(entity -> { + entity.apply("DOC.8.0", "GLP Study found", "n-a"); + }); + end + + +// Rule unit: DOC.9 +rule "DOC.9.0: Batch number from CoA" + when + $section: Section( + ( + anyHeadlineContainsString("Analytical Report") + || anyHeadlineContainsStringIgnoreCase("Certificate of Analysis") + || containsStringIgnoreCase("Certificate of Analysis") + ) + && ( + containsStringIgnoreCase("batch") + || containsStringIgnoreCase("bath") + || containsStringIgnoreCase("barch") + || containsStringIgnoreCase("bateb") + ) + && ( + containsStringIgnoreCase("identification") + || containsStringIgnoreCase("ldentitfication") + || containsStringIgnoreCase("wentification") + || containsStringIgnoreCase("mentification") + || containsStringIgnoreCase("kientification") + || containsStringIgnoreCase("reference number") + || containsStringIgnoreCase("test substance") + ) + ) + then + entityCreationService.lineAfterStrings(List.of("Batch Identification", + "(Batch Identification):", + "Bateb Identification", + "Batch Wentification", + "Batch Mentification", + "Batch Kientification", + "Barch Identification", + "Bath ldentitfication", + "Batch of test substance :"), "batch_number", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.9.0", "Batch number found in CoA", "n-a"); + }); + end + +rule "DOC.9.1: Batch number" + when + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("Test Substance") + || anyHeadlineContainsStringIgnoreCase("Test and Control Substances") + || anyHeadlineContainsStringIgnoreCase("Test Item") + ) + && !( + anyHeadlineContainsString("component") + || anyHeadlineContainsString("reference") + || anyHeadlineContainsString("blank") + ) + && containsStringIgnoreCase("batch") + ) + then + Stream.of(entityCreationService.byRegex("Batch ID ([A-Z\\d\\-]{7,14})", "batch_number", EntityType.ENTITY, 1, $section), + entityCreationService.lineAfterStrings(List.of("Batch Identification", + "Batch number:", + "Batch reference number:", + "Batch:", + "Batch/Lot number:", + "Batch (Lot) Number:", + "Batch Number:", + "Batch Nº:", + "Batch no:" + ), "batch_number", EntityType.ENTITY, $section)).flatMap(a -> a) + .forEach(entity -> { + entity.apply("DOC.9.1", "Batch number found", "n-a"); + }); + end + +rule "DOC.9.2: Batch number" + when + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("Test Substance") + || anyHeadlineContainsStringIgnoreCase("Test and Control Substances") + || anyHeadlineContainsStringIgnoreCase("Test Item") + ) + && !( + anyHeadlineContainsString("component") + || anyHeadlineContainsString("reference") + || anyHeadlineContainsString("blank") + ) + && containsStringIgnoreCase("batch") + ) + $batchNumber: String() from List.of("Batch Identification", + "Batch number:", + "Batch reference number:", + "Batch:", + "Batch/Lot number:", + "Batch (Lot) Number:", + "Batch Number:", + "Batch Nº:", + "Batch no:") + $table: Table(containsStringIgnoreCase($batchNumber)) from $section.streamAllSubNodesOfType(NodeType.TABLE).toList() + then + entityCreationService.lineAfterStringAcrossColumnsIgnoreCase($batchNumber, "batch_number", EntityType.ENTITY, $table).forEach(entity -> { + entity.apply("DOC.9.2", "Batch number found", "n-a"); + }); + end + + +// Rule unit: DOC.10 +rule "DOC.10.0: Conclusions - LD50, LC50, Confidence" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","425","436")) + $section: Section( + (getHeadline().containsStringIgnoreCase("Conclusion") || anyHeadlineContainsStringIgnoreCase("Lethality")) + && (containsString("LD") || containsString("LC") || containsString("50") || containsString("LD50") || containsString("lethal concentration") || containsString("lethal dose")) + && ( + containsString("greater than") + || containsString("higher than") + || containsString("above") + || containsString("in excess") + || containsString("exceeds") + || containsString("was found to be") + || containsString("was calculated to be") + || containsString("estimated to be") + ) + ) + then + entityCreationService.byRegexIgnoreCase("(L[D|C]\\s?50|lethal concentration|lethal dose).{1,200}(greater than|considered to be above|in excess of|exceeds|higher than)", "ld50_greater", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.10.0", "LD50 greater than found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("\\b(?:(?:greater|higher) than|considered to be above|(?:was|is) (?:found|estimated) to be|was calculated to be|in excess of|exceeds|equal to)\\s?([\\d\\.]{1,6})\\s?mg\\/(?:kg|L)", "ld50_value", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.10.0", "LD50 value found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("confidence interval (?:is )?([\\d\\.]{2,6}).{0,20} to (?:greater than )?([\\d\\.]{2,6})", "confidence_minimal", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.10.0", "Minimal Confidence found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("confidence interval (?:is )?([\\d\\.]{2,6}).{0,20} to (?:greater than )?([\\d\\.]{2,6})", "confidence_maximal", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.10.0", "Maximal Confidence found", "n-a"); + }); + end + + +// Rule unit: DOC.11 +rule "DOC.11.0: Guideline Deviation" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","471")) + $section: Section( + (getHeadline().containsStringIgnoreCase("General Information") || containsString("GENERAL INFORMATION")) + && (containsStringIgnoreCase("from the") || containsStringIgnoreCase("to the")) + ) + then + entityCreationService.betweenRegexes("(?:Deviations? from the [G|g]uidelines?)(?: and| or)?( the)?(?: Study Plan)?", "(?:(?:Deviations? from the Study Plan)|(?:Performing laboratory test)|(?:Other)|(?:Retention of [S|s]amples)|(?:Amendments? to Final Protocol))", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Deviation from Guidelines found", "n-a"); + }); + entityCreationService.betweenRegexes("(?:Deviations? (?:from|to)(?: the)? [S|s]tudy [P|p]lan)", "(?:Regulatory Guidelines)|(?:Other)|(?:Distribution of the report)|(?:Performing laboratory test)|(?:Distribution of the report)|(?:Retention of [S|s]amples)", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Deviation from Study Plan found", "n-a"); + }); + entityCreationService.betweenStrings("Deviations from the study plan", "Regulatory Guidelines", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Deviation from the study plan found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("(?>Study plan adherence)(.{1,20}deviations.{1,20} to the study plan.{0,50}\\.)\\s", "guideline_deviation", EntityType.ENTITY, 1, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Guideline deviation found in text.", "n-a"); + }); + entityCreationService.betweenStringsIncludeEnd("Deviations from the study plan", "validity of the study.", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Deviation from the study plan found", "n-a"); + }); + end + +rule "DOC.11.1: Guideline Deviation in text" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","471")) + $section: Section( + getHeadline().containsStringIgnoreCase("Introduction") + && containsStringIgnoreCase("deviations from the protocol") + ) + then + entityCreationService.byRegex("There were no deviations from the protocol.{1,100}\\.\\s", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.1", "Guideline deviation found in text.", "n-a"); + }); + end + + +// Rule unit: DOC.12 +rule "DOC.12.0: Clinical Signs" + when + FileAttribute(label == "OECD Number", value == "425") + $headline: Headline(containsAnyStringIgnoreCase("Clinical Signs", "Macroscopic Findings") && !containsString("TABLE") && !getHeadline().containsStringIgnoreCase("3 - MACROSCOPIC FINDINGS")) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "clinical_signs", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.12.0", "Clinical Signs found", "n-a")); + end + + +// Rule unit: DOC.13 +rule "DOC.13.0: Dosages" + when + FileAttribute(label == "OECD Number", value == "425") + $section: Section( + (anyHeadlineContainsStringIgnoreCase("Dosages") || anyHeadlineContainsStringIgnoreCase("Study Design")) + && !getHeadline().containsString("TABLE") + ) + then + entityCreationService.betweenStringsIncludeStartAndEnd("The animals were treated", ".", "dosages", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.13.0", "Dosage found", "n-a"); + }); + entityCreationService.betweenStringsIncludeStartAndEnd("Animals were treated", ".", "dosages", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.13.0", "Dosage found", "n-a"); + }); + entityCreationService.byRegexWithLineBreaks("(?:\\.[\\s|\\n]|^.{5,20}\\n)([^\\.]{1,200}(?:animal|given|received)[^\\.]{1,200}dose\\s(?:levels?\\s)?(?:of|at)[^\\.]{1,200})(?:\\.[\\s|\\n|$])", "dosages", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.13.0", "Dosage found", "n-a"); + }); + end + + +// Rule unit: DOC.14 +rule "DOC.14.0: Mortality" + when + $headline: Headline(containsString("Mortality") && !containsString("TABLE")) + FileAttribute(label == "OECD Number", value == "425") + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "mortality", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.14.0", "Mortality found", "n-a")); + end + + +// Rule unit: DOC.15 +rule "DOC.15.0: Study Conclusion" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","471")) + $section: Section( + getHeadline().containsStringIgnoreCase("Conclusion") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "study_conclusion", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.15.0", "Study Conclusion found", "n-a")); + end + + +// Rule unit: DOC.16 +rule "DOC.16.0: Weight Behavior Changes" + when + FileAttribute(label == "OECD Number", value == "402") + $section: Section( + getHeadline().containsString("Results") + && ( + containsString("body weight") + || containsString("body weights") + || containsString("bodyweight") + || containsString("bodyweights") + ) + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "weight_behavior_changes", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.16.0", "Weight behavior changes found", "n-a")); + end + + +// Rule unit: DOC.17 +rule "DOC.17.0: Necropsy findings" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","436")) + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("Necropsy") + || getHeadline().containsStringIgnoreCase("Macroscopic Findings") + || getHeadline().containsStringIgnoreCase("Macroscopic examination") + ) + && !getHeadline().containsStringIgnoreCase("Table") + && !getHeadline().containsStringIgnoreCase("Appendix") + && !getHeadline().containsStringIgnoreCase("3 - MACROSCOPIC FINDINGS") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "necropsy_findings", EntityType.ENTITY) + .forEach( entity -> entity.apply("DOC.17.0", "Necropsy section found", "n-a")); + end + + +// Rule unit: DOC.18 +rule "DOC.18.0: Clinical observations" + when + FileAttribute(label == "OECD Number", value == "403") + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("Clinical Observations") + || anyHeadlineContainsStringIgnoreCase("Clinical observations") + || anyHeadlineContainsStringIgnoreCase("In-life Observations") + || anyHeadlineContainsStringIgnoreCase("Postmortem Observations") + ) + && !anyHeadlineContainsStringIgnoreCase("Appendix") + && !anyHeadlineContainsStringIgnoreCase("Table") + && !anyHeadlineContainsStringIgnoreCase("Mortality") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "clinical_observations", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.18.0", "Clinical observations section found", "n-a")); + end + + +// Rule unit: DOC.19 +rule "DOC.19.0: Bodyweight changes" + when + FileAttribute(label == "OECD Number", value == "403") + $headline: Headline(containsAnyStringIgnoreCase("Bodyweight", "Bodyweights", "Body Weights", "Body Weight"), !containsAnyStringIgnoreCase("Appendix", "TABLE")) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "bodyweight_changes", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.19.0", "Bodyweight section found", "n-a")); + end + + +// Rule unit: DOC.20 +rule "DOC.20.0: Study Design" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","404","405","406","428","429","438","439","474","487")) + $section: Section( + anyHeadlineContainsStringIgnoreCase("study design") + && !anyHeadlineContainsString("Preliminary screening test") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "study_design", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.20.0", "Study design section found", "n-a")); + end + +rule "DOC.20.1: Study Design" + when + Headline(containsStringIgnoreCase("Study Design"), $sectionIdentifier: getSectionIdentifier()) + $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifier)) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "study_design", EntityType.ENTITY) + .forEach(entity -> { + entity.apply("DOC.20.1", "Study design section found", "n-a"); + }); + end + + +// Rule unit: DOC.21 +rule "DOC.21.0: Results and Conclusion (406, 428, 438, 439, 474 & 487)" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("406","428","438","439","474","487")) + $parentHeadline: Headline( + containsAnyString("Results", "Conclusion"), + !containsAnyString("POSITIVE CONTROL", "Positive Control", "Evaluation", "Micronucleus", "TABLE", "DISCUSSION", "CONCLUSIONS", "Interpretation","Viability", "analysis"), + $sectionIdentifier: getSectionIdentifier() + ) + not Headline(getSectionIdentifier().isChildOf($sectionIdentifier)) + then + entityCreationService.bySemanticNodeParagraphsOnly($parentHeadline.getParent(), "results_and_conclusion", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.21.0", "Results and Conclusion found", "n-a")); + end + +rule "DOC.21.1: Results and Conclusion (406, 428, 438, 439, 474 & 487)" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("406","428","438","439","474","487")) + Headline( + containsAnyString("Results", "Conclusion"), + !containsAnyString("POSITIVE CONTROL", "Positive Control", "Evaluation", "Micronucleus", "TABLE", "DISCUSSION", "CONCLUSIONS", "Interpretation","Viability", "analysis"), + $sectionIdentifier: getSectionIdentifier() + ) + $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifier)) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "results_and_conclusion", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.21.1", "Results and Conclusion found", "n-a")); + end + + +// Rule unit: DOC.22 +rule "DOC.22.0: Detailing (404 & 405)" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("404","405")) + $section: Section( + anyHeadlineContainsStringIgnoreCase("Results") + && !getHeadline().containsStringIgnoreCase("Evaluation") + && !getHeadline().containsStringIgnoreCase("study") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "detailing", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.22.0", "Detailing found", "n-a")); + end + + +// Rule unit: DOC.23 +rule "DOC.23.0: Preliminary Test Results (429)" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + ((anyHeadlineContainsString("Preliminary Screening Test") && containsString("Clinical observations")) + || anyHeadlineContainsString("Pre-Experiment")) + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "preliminary_test_results", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.23.0", "Preliminary Test Results found", "n-a")); + end + + +// Rule unit: DOC.24 +rule "DOC.24.0: Test Results (429)" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section((getHeadline().containsString("RESULTS AND DISCUSSION") || getHeadline().containsString("Estimation of the proliferative response of lymph node cells") || getHeadline().containsString("Results in the Main Experiment"))) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "test_results", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.24.0", "Test Results found", "n-a")); + end + +rule "DOC.24.1: Test Results (429)" + when + Headline(containsStringIgnoreCase("RESULTS AND DISCUSSION"), $sectionIdentifierResultsAndDiscussion: getSectionIdentifier()) + $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifierResultsAndDiscussion)) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "test_results", EntityType.ENTITY) + .forEach(entity -> { + entity.apply("DOC.24.1", "Test Results found", "n-a"); + }); + end + + +// Rule unit: DOC.25 +rule "DOC.25.0: Approach used (429)" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + hasEntitiesOfType("species") + && (containsStringIgnoreCase("animals per") || containsStringIgnoreCase("animals /")) + ) + then + entityCreationService.byRegexIgnoreCase("\\banimals (?:per|\\/) .{0,15}(group)\\b", "approach_used", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.25.0", "Study animal approach found.", "n-a"); + }); + end + + +// Rule unit: DOC.26 +rule "DOC.26.0: Sex" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("405","429")) + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("animal") + || anyHeadlineContainsStringIgnoreCase("test system") + ) + && !getHeadline().containsStringIgnoreCase("selection") + && ( + containsStringIgnoreCase("sex:") + || containsStringIgnoreCase("male") + || containsStringIgnoreCase("female") + ) + ) + then + entityCreationService.byRegexIgnoreCase("([S|s]ex:)?[\\w\\s]{0,10}\\b(males?|females?)\\b", "sex", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.26.0", "Test animal sex found", "n-a"); + }); + end + + +// Rule unit: DOC.27 +rule "DOC.27.0: Animal Number 405" + when + FileAttribute(label == "OECD Number", value == "405") + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("animal") + || anyHeadlineContainsStringIgnoreCase("test system") + || anyHeadlineContainsStringIgnoreCase("reaction") + ) + && !getHeadline().containsString("selection") + && ( + containsStringIgnoreCase("number of animals") + || containsStringIgnoreCase("no.") + ) + ) + then + entityCreationService.byRegexIgnoreCase("(Number of animals:)[\\w\\s]{0,10}\\b([\\d]{1,3})\\b", "number_of_animals", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.27.0", "Number of animals found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("(?:.{1,10} No\\. )([\\d\\w\\-]{3,8})", "animal_numbers", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.27.0", "Number of animals found", "n-a"); + }); + end + + +// Rule unit: DOC.28 +rule "DOC.28.0: Animal Number 429" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("animal") + || anyHeadlineContainsStringIgnoreCase("test system") + ) + && !getHeadline().containsString("selection") + && containsStringIgnoreCase("number of animals") + && (containsStringIgnoreCase("per") || containsString("/")) + && containsStringIgnoreCase("group") + ) + then + entityCreationService.byRegexIgnoreCase("(Number of animals per group:)[\\w\\s]{0,10}\\b([\\d]{1,3})\\b", "number_of_animals", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.28.0", "Number of animals in group found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("(Number of animals per group:).{0,60}\\b([\\d]{1,3})\\sper group\\b", "number_of_animals", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.28.0", "Number of animals in group found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("([\\d]{1,3})[\\w\\s\\/]{0,20}(?:treatment )?group\\b", "number_of_animals", EntityType.ENTITY,1 , $section).forEach(entity -> { + entity.apply("DOC.28.0", "Number of animals in group found", "n-a"); + }); + end + +rule "DOC.28.1: No. Of animals - Fallback to appendix tables listing all individual animals for 429" + when + $keyword: String() from List.of("Animal Number", "Animal No.", "Animal number") + $table: Table(containsString($keyword) && getHeadline().containsString("TABLE") && getHeadline().containsString("Individual")) + FileAttribute(label == "OECD Number", value == "429") + then + $table.streamTableCellsWithHeader($keyword) + .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "animal_numbers", EntityType.ENTITY)) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(entity -> { + entity.apply("DOC.28.1", "Animal number found.", "n-a"); + }); + end + + +// Rule unit: DOC.29 +rule "DOC.29.0: 4h Exposure" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("403","436")) + $section: Section( + (containsStringIgnoreCase("4 hours") || containsStringIgnoreCase("four hours")) + ) + then + entityCreationService.byRegexIgnoreCase("((?<=\\.\\s\\b).{1,100}(4|four) hours.*?\\.) ", "4h_exposure", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.29.0", "4h exposure sentence found", "n-a"); + }); + end + + +// Rule unit: DOC.30 +rule "DOC.30.0: Dilution of the test substance" + when + FileAttribute(label == "OECD Number", value == "404") + $section: Section( + getHeadline().containsString("Formulation") + && containsString("dilution") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "dilution", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.30.0", "Dilution found.", "n-a")); + end + + +// Rule unit: DOC.31 +rule "DOC.31.0: Positive Control" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + getHeadline().containsStringIgnoreCase("Positive Control") + && !(getHeadline().containsStringIgnoreCase("Appendix") || getHeadline().containsStringIgnoreCase("Table")) + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "positive_control", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.31.0", "Positive control found.", "n-a")); + end + + +// Rule unit: DOC.32 +rule "DOC.32.0: Mortality Statement" + when + FileAttribute(label == "OECD Number", value == "402") + $headline: Headline(containsStringIgnoreCase("Mortality") && !containsString("TABLE")) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "mortality_statement", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.32.0", "Mortality Statement found", "n-a")); + end + + +// Rule unit: DOC.33 +rule "DOC.33.0: Dose Mortality" + when + FileAttribute(label == "OECD Number", value == "425") + $table: Table( + (hasHeader("Mortality") || hasHeader("Long Term Results") || hasHeader("LongTerm Outcome") || hasHeader("Long Term Outcome") || hasHeader("Comments") || hasHeader("Viability / Mortality") || hasHeader("Viability/Mortality")) + && + (hasHeader("Dose [mg/kg bodyweight]") || hasHeader("Dose [mg/kg body weight]") ||hasHeader("Dose (mg/kg)") || hasHeader("Dose levei (mg/kg)") || hasHeader("Dose Level (mg/kg)") || hasHeader("Dose level (mg/kg)") || hasHeader("Dosage [mg/kg body weight]")) + ) + then + Stream.of($table.streamTableCellsWithHeader("Mortality"), + $table.streamTableCellsWithHeader("Comments"), + $table.streamTableCellsWithHeader("Long Term Results"), + $table.streamTableCellsWithHeader("Long Term Outcome"), + $table.streamTableCellsWithHeader("LongTerm Outcome"), + $table.streamTableCellsWithHeader("Viability / Mortality"), + $table.streamTableCellsWithHeader("Viability/Mortality") + ).flatMap(a -> a) + .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "dose_mortality", EntityType.ENTITY)) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(entity -> { + entity.apply("DOC.33.0", "Dose Mortality Data found.", "n-a"); + }); + + Stream.of($table.streamTableCellsWithHeader("Dose [mg/kg bodyweight]"), + $table.streamTableCellsWithHeader("Dose [mg/kg body weight]"), + $table.streamTableCellsWithHeader("Dose levei (mg/kg)"), + $table.streamTableCellsWithHeader("Dose Level (mg/kg)"), + $table.streamTableCellsWithHeader("Dose level (mg/kg)"), + $table.streamTableCellsWithHeader("Dose (mg/kg)"), + $table.streamTableCellsWithHeader("Dosage [mg/kg body weight]") + ).flatMap(a -> a) + .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "dose_mortality_dose", EntityType.ENTITY)) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(entity -> { + entity.apply("DOC.33.0", "Dose Mortality Data found.", "n-a"); + }); + end + + +// Rule unit: DOC.34 +rule "DOC.34.0: Results (Main Study)" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + getHeadline().containsString("Results") + && getHeadline().getTextRange().length() < 20 + && !(getHeadline().containsString("Appendix") || getHeadline().containsString("Table")) + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "results_(main_study)", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.34.0", "Results for main study found.", "n-a")); + end + + +// Rule unit: DOC.35 +rule "DOC.35.0: Doses (mg/kg bodyweight)" + when + FileAttribute(label == "OECD Number", value == "402") + $section: Section( + anyHeadlineContainsStringIgnoreCase("study design") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "doses_(mg_kg_bw)", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.35.0", "Doses per bodyweight information found", "n-a")); + end + + +//------------------------------------ Manual redaction rules ------------------------------------ + +// Rule unit: MAN.0 +rule "MAN.0.0: Apply manual resize redaction" + salience 128 + when + $resizeRedaction: ManualResizeRedaction($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate)) + $entityToBeResized: TextEntity(matchesAnnotationId($id)) + then + manualChangesApplicationService.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, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate)) + $imageToBeResized: Image(id == $id) + then + manualChangesApplicationService.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($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToBeRemoved: TextEntity(matchesAnnotationId($id)) + then + $entityToBeRemoved.getManualOverwrite().addChange($idRemoval); + 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($id: annotationId, status == AnnotationStatus.APPROVED) + $imageEntityToBeRemoved: Image($id == id) + then + $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); + update($imageEntityToBeRemoved); + retract($idRemoval); + update($imageEntityToBeRemoved.getParent()); + end + + +// Rule unit: MAN.2 +rule "MAN.2.0: Apply force redaction" + salience 128 + when + $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToForce: TextEntity(matchesAnnotationId($id)) + then + $entityToForce.getManualOverwrite().addChange($force); + update($entityToForce); + $entityToForce.getIntersectingNodes().forEach(node -> update(node)); + retract($force); + end + +rule "MAN.2.1: Apply force redaction to images" + salience 128 + when + $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED) + $imageToForce: Image(id == $id) + then + $imageToForce.getManualOverwrite().addChange($force); + update($imageToForce); + update($imageToForce.getParent()); + retract($force); + end + + +// Rule unit: MAN.3 +rule "MAN.3.0: Apply entity recategorization" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type != $type) + then + $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node)); + manualChangesApplicationService.recategorize($entityToBeRecategorized, $recategorization); + retract($recategorization); + // Entity is copied and inserted, so the old entity needs to be retracted to avoid duplication. + retract($entityToBeRecategorized); + end + +rule "MAN.3.1: Apply entity recategorization of same type" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type == $type) + then + $entityToBeRecategorized.getManualOverwrite().addChange($recategorization); + retract($recategorization); + end + +rule "MAN.3.2: Apply image recategorization" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $imageToBeRecategorized: Image($id == id) + then + manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); + update($imageToBeRecategorized); + update($imageToBeRecategorized.getParent()); + retract($recategorization); + end + + +// Rule unit: MAN.4 +rule "MAN.4.0: Apply legal basis change" + salience 128 + when + $legalbasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED) + $imageToBeRecategorized: Image($id == id) + then + $imageToBeRecategorized.getManualOverwrite().addChange($legalbasisChange); + end + +rule "MAN.4.1: Apply legal basis change" + salience 128 + when + $legalBasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToBeChanged: TextEntity(matchesAnnotationId($id)) + then + $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange); + end + + +//------------------------------------ Entity merging rules ------------------------------------ + +// Rule unit: X.0 +rule "X.0.0: Remove Entity contained by Entity of same type" + salience 65 + when + $larger: TextEntity($type: type, $entityType: entityType, active()) + $contained: TextEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !hasManualChanges(), active()) + then + $contained.remove("X.0.0", "remove Entity contained by Entity of same type"); + retract($contained); + end + + +// Rule unit: X.2 +rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" + salience 64 + when + $falsePositive: TextEntity($type: type, entityType == EntityType.FALSE_POSITIVE, active()) + $entity: TextEntity(containedBy($falsePositive), type == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges(), active()) + then + $entity.getIntersectingNodes().forEach(node -> update(node)); + $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); + retract($entity) + end + + +// Rule unit: X.3 +rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION" + salience 64 + when + $falseRecommendation: TextEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION, active()) + $recommendation: TextEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); + retract($recommendation); + end + + +// Rule unit: X.4 +rule "X.4.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY with same type" + salience 256 + when + $entity: TextEntity($type: type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $recommendation: TextEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $entity.addEngines($recommendation.getEngines()); + $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY with same type"); + retract($recommendation); + end + + +// Rule unit: X.5 +rule "X.5.0: Remove Entity of type RECOMMENDATION when contained by ENTITY" + salience 256 + when + $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $recommendation: TextEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when contained by ENTITY"); + retract($recommendation); + end + + +// Rule unit: X.7 +rule "X.7.0: Remove all images" + salience 512 + when + $image: Image(imageType != ImageType.OCR, !hasManualChanges()) + then + $image.remove("X.7.0", "remove all images"); + retract($image); + 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 matchedRules = $dictionaryModel.getLocalEntriesWithMatchedRules().get(entity.getValue()); + entity.addMatchedRules(matchedRules); + }); + end diff --git a/redaction-service-v1/rules-management/src/main/resources/default_rule_identifiers.txt b/redaction-service-v1/rules-management/src/main/resources/default_rule_identifiers.txt new file mode 100644 index 00000000..730c694c --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/resources/default_rule_identifiers.txt @@ -0,0 +1,10 @@ +MAN.*.* +X.0.0 +X.1.0 +X.2.0 +X.3.0 +X.4.0 +X.5.0 +X.6.* +FA.*.* +LDS.*.* \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/main/resources/default_rule_identifiers_dm.txt b/redaction-service-v1/rules-management/src/main/resources/default_rule_identifiers_dm.txt new file mode 100644 index 00000000..7f2c2e5e --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/resources/default_rule_identifiers_dm.txt @@ -0,0 +1,9 @@ +MAN.*.* +X.0.0 +X.2.0 +X.3.0 +X.4.0 +X.5.0 +X.7.0 +FA.*.* +LDS.*.* \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/main/resources/old_rules_with_translations.csv b/redaction-service-v1/rules-management/src/main/resources/old_rules_with_translations.csv new file mode 100644 index 00000000..7b5d9569 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/resources/old_rules_with_translations.csv @@ -0,0 +1,234 @@ +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] diff --git a/redaction-service-v1/rules-management/src/main/resources/order_template.txt b/redaction-service-v1/rules-management/src/main/resources/order_template.txt new file mode 100644 index 00000000..0cbc4d7b --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/resources/order_template.txt @@ -0,0 +1,11 @@ +H +DOC +SYN +CBI +PII +ETC +AI +MAN +X +FA +LDS \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/DroolsCompilationTest.java b/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/DroolsCompilationTest.java new file mode 100644 index 00000000..9385b0ae --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/DroolsCompilationTest.java @@ -0,0 +1,80 @@ +package com.knecon.fforesight.utility.rules.management; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; +import org.kie.api.KieServices; +import org.kie.api.builder.KieBuilder; +import org.kie.api.builder.KieFileSystem; +import org.kie.api.builder.Message; +import org.kie.api.io.Resource; +import org.kie.api.io.ResourceType; + +import com.google.common.io.Resources; + +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class DroolsCompilationTest { + + @Test + public void testValidateRuleSyntax() throws IOException { + + URL rmURL = Resources.getResource("all_redact_manager_rules.drl"); + String rmRule = Resources.toString(rmURL, StandardCharsets.UTF_8); + + URL dmURL = Resources.getResource("all_rules_documine.drl"); + String dmRule = Resources.toString(dmURL, StandardCharsets.UTF_8); + + assertTrue(validateRuleSyntax(rmRule).isValid()); + assertTrue(validateRuleSyntax(dmRule).isValid()); + + // Invalidate rules + rmRule = rmRule.substring(0, rmRule.length()-3); + dmRule = dmRule.substring(0, dmRule.length()-3); + + var validateRM = validateRuleSyntax(rmRule); + var validateDM = validateRuleSyntax(dmRule); + assertFalse(validateRM.isValid()); + assertFalse(validateRM.getFailReason().contains("mismatched input ''")); + assertFalse(validateDM.isValid()); + assertFalse(validateDM.getFailReason().contains("mismatched input ''")); + } + + private RuleValidation validateRuleSyntax(String ruleContent) { + + RuleValidation.RuleValidationBuilder ruleValidationBuilder = RuleValidation.builder(); + KieServices kieServices = KieServices.Factory.get(); + KieFileSystem kfs = kieServices.newKieFileSystem(); + Resource resource = kieServices.getResources().newByteArrayResource(ruleContent.getBytes()); + resource.setResourceType(ResourceType.DRL); + kfs.write("src/main/resources/rules.drl", resource); + + KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll(); + + if (kieBuilder.getResults().hasMessages(Message.Level.ERROR)) { + log.info("Rule syntax validation failed:"); + for (Message result : kieBuilder.getResults().getMessages(Message.Level.ERROR)) { + ruleValidationBuilder.failReason(result.getText()); + } + return ruleValidationBuilder.isValid(false).build(); + } + + return ruleValidationBuilder.isValid(true).build(); + } + + @Data + @Builder + public static class RuleValidation { + + private boolean isValid; + private String failReason; + } +} diff --git a/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactoryTest.java b/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactoryTest.java new file mode 100644 index 00000000..018c216a --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactoryTest.java @@ -0,0 +1,277 @@ +package com.knecon.fforesight.utility.rules.management.factory; + +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import com.knecon.fforesight.utility.rules.management.RuleManagementResources; +import com.knecon.fforesight.utility.rules.management.factory.RuleFileFactory; +import com.knecon.fforesight.utility.rules.management.factory.RuleFileParser; +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; +import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier; +import com.knecon.fforesight.utility.rules.management.utils.RuleFileIO; + +import lombok.SneakyThrows; + +class RuleFileFactoryTest { + + @Test + @SneakyThrows + @Disabled + void migrateDossierTemplatesRepoToNewestRules() { + + String dossierTemplatesRepo = "/Users/aisvoran/dev/dossier-templates-v2/"; + migrate(ApplicationType.RM, dossierTemplatesRepo + "dev", dossierTemplatesRepo + "docu", dossierTemplatesRepo + "qa"); + } + + @Test + @SneakyThrows + @Disabled + public void migrateDocumineDossierTemplatesRepoToNewestRules() { + String dossierTemplatesRepo = "/Users/aisvoran/dev/documine/dossier-templates-v2/dev/"; + migrate(ApplicationType.DM, dossierTemplatesRepo + "Flora", dossierTemplatesRepo + "Basf-Demo"); + } + + @SneakyThrows + private void migrate(ApplicationType applicationType, String... paths) { + + Arrays.stream(paths).forEach(path -> { + try { + Stream.of(Files.walk(Path.of(path))) + .flatMap(Function.identity()) + .filter(p -> p.getFileName().toString().equals("rules.drl"))// + .map(Path::toFile)// + .forEach(e -> migrateFile(e, applicationType)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + } + + + @SneakyThrows + private void migrateFile(File oldRulesFile, ApplicationType applicationType) { + + Set identifiers = RuleFileParser.parseRuleIdentifiersFromFile(oldRulesFile); + String newRulesString = RuleFileFactory.createFileFromIdentifiers(identifiers, applicationType); + + try (FileOutputStream out = new FileOutputStream(oldRulesFile); + OutputStreamWriter outWrite = new OutputStreamWriter(out, StandardCharsets.UTF_8); + BufferedWriter writer = new BufferedWriter(outWrite)) { + writer.write(newRulesString); + } + } + + + @Test + @Disabled + void createFileFromIdentifiers() { + + String result = RuleFileFactory.createFileFromIdentifiers("MAN, X, LDS, CBI.0, PII.0.1", ApplicationType.RM); + System.out.println(result); + } + + + @Test + @Disabled + void generateRules() { + + String stem = """ + rule "DefaultComponents.4.klasjfidklasjf: Test Guideline 1" + when + $guidelineNumber: Entity(type == "oecd_guideline_number", klasjfnumberklasjf) + $guidelineYear: Entity(type == "oecd_guideline_year", klasjfyearklasjf) + then + componentCreationService.createComponent( + "DefaultComponents.4.klasjfidklasjf", + "Test_Guidelines_1", + "klasjfguidelineklasjf", + "OECD Number and guideline year mapped!" + ); + end + + """; + Map, String> guidelineMapping = new HashMap<>(); + guidelineMapping.put(List.of("425", "2008"), "Nº 425: Acute oral Toxicity - Up-and-Down Procedure (03/10/2008)"); + guidelineMapping.put(List.of("425", "2001"), "Nº 425: Acute oral Toxicity - Up-and-Down Procedure (17/12/2001)"); + guidelineMapping.put(List.of("402", "2017"), "Nº 402: Acute Dermal Toxicity (09/10/2017)"); + guidelineMapping.put(List.of("402", "1987"), "Nº 402: Acute Dermal Toxicity (24/02/1987)"); + guidelineMapping.put(List.of("403", "2009"), "Nº 403: Acute Inhalation Toxicity (08/09/2009)"); + guidelineMapping.put(List.of("403", "1981"), "Nº 403: Acute Inhalation Toxicity (12/05/1981)"); + guidelineMapping.put(List.of("433", "2018"), "Nº 433: Acute Inhalation Toxicity: Fixed Concentration Procedure (27/06/2018)"); + guidelineMapping.put(List.of("433", "2017"), "Nº 433: Acute Inhalation Toxicity: Fixed Concentration Procedure (09/10/2017)"); + guidelineMapping.put(List.of("436", "2009"), "Nº 436: Acute Inhalation Toxicity – Acute Toxic Class Method (08/09/2009)"); + guidelineMapping.put(List.of("404", "1981"), "Nº 404: Acute Dermal Irritation/Corrosion (12/05/1981)"); + guidelineMapping.put(List.of("404", "1992"), "Nº 404: Acute Dermal Irritation/Corrosion (17/07/1992)"); + guidelineMapping.put(List.of("404", "2002"), "Nº 404: Acute Dermal Irritation/Corrosion (24/04/2002)"); + guidelineMapping.put(List.of("404", "2015"), "Nº 404: Acute Dermal Irritation/Corrosion (28/07/2015)"); + guidelineMapping.put(List.of("405", "2017"), "Nº 405: Acute Eye Irritation/Corrosion (09/10/2017)"); + guidelineMapping.put(List.of("405", "2012"), "Nº 405: Acute Eye Irritation/Corrosion (02/10/2012)"); + guidelineMapping.put(List.of("405", "2002"), "Nº 405: Acute Eye Irritation/Corrosion (24/04/2002)"); + guidelineMapping.put(List.of("405", "1987"), "Nº 405: Acute Eye Irritation/Corrosion (24/02/1987)"); + guidelineMapping.put(List.of("429", "2002"), "Nº 429: Skin Sensitisation: Local Lymph Node Assay (24/04/2002)"); + guidelineMapping.put(List.of("429", "2010"), "Nº 429: Skin Sensitisation (23/07/2010)"); + guidelineMapping.put(List.of("442A", "2018"), "Nº 442A: Skin Sensitization (23/07/2018)"); + guidelineMapping.put(List.of("442B", "2018"), "Nº 442B: Skin Sensitization (27/06/2018)"); + guidelineMapping.put(List.of("471", "1997"), "Nº 471: Bacterial Reverse Mutation Test (21/07/1997)"); + guidelineMapping.put(List.of("471", "2020"), "Nº 471: Bacterial Reverse Mutation Test (26/06/2020)"); + guidelineMapping.put(List.of("406", "1992"), "Nº 406: Skin Sensitisation (1992)"); + guidelineMapping.put(List.of("428", "2004"), "Nº 428: Split-Thickness Skin test (2004)"); + guidelineMapping.put(List.of("438", "2018"), "Nº 438: Eye Irritation (26/06/2018)"); + guidelineMapping.put(List.of("439", "2019"), "Nº 439: Skin Irritation (2019)"); + guidelineMapping.put(List.of("474", "2016"), "Nº 474: Micronucleus Bone Marrow Cells Rat (2016)"); + guidelineMapping.put(List.of("487", "2016"), "Nº 487: Micronucleus Human Lymphocytes (2016)"); + Map>> mappings = guidelineMapping.entrySet() + .stream() + .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, toList()))); + int id = 1; + StringBuilder sb = new StringBuilder(); + for (String guideline : mappings.keySet()) { + String year = getString(mappings.get(guideline).stream().map(l -> l.get(0)).distinct().toList()); + String number = getString(mappings.get(guideline).stream().map(l -> l.get(1)).distinct().toList()); + sb.append(stem.replaceAll(("klasjfguidelineklasjf"), guideline) + .replaceAll(("klasjfidklasjf"), String.valueOf(id)) + .replaceAll(("klasjfnumberklasjf"), number) + .replaceAll(("klasjfyearklasjf"), year)); + id++; + } + System.out.println(sb); + + } + + + private static String getString(List strings) { + + if (strings.size() == 1) { + return String.format("value == \"%s\"", strings.get(0)); + } + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (String string : strings) { + sb.append(String.format("value == \"%s\"", strings)); + sb.append(" || "); + } + sb.delete(sb.length() - 4, sb.length()); + sb.append(")"); + return sb.toString(); + } + + + @Test + @SneakyThrows + @Disabled + void createFileFromIdentifiersForRedactionService() { + + // This is exactly the string used for the current rules.drl in the redaction-service + String identifiers = "CBI.5.*, CBI.9.*, CBI.11.*, AI.1.*, PII.4.*, ETC.8.*, PII.0.*, ETC.6.*, SYN.0.*, CBI.3.*, ETC.4.*, ETC.3.*, PII.12.*, ETC.1.*, PII.9.*, PII.7.*, CBI.12.*, X.*.*, CBI.14.*, CBI.16.*, CBI.18.*, CBI.4.*, AI.0.*, CBI.8.*, PII.1.*, ETC.7.*, LDS.*.*, MAN.*.*, ETC.5.*, PII.11.*, ETC.2.*, CBI.20.*, FA.*.*, PII.8.*, PII.6.*, CBI.15.*, CBI.17.*, CBI.19.*"; + String result = RuleFileFactory.createFileFromIdentifiers(identifiers, ApplicationType.RM); + System.out.println(result); + try (var out = new FileOutputStream( +// "/home/kschuettler/iqser/redaction/redaction-service/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/rules.drl")) { + "/home/aoezyetimoglu/repositories/rulesALI.drl")) { + out.write(result.getBytes(StandardCharsets.UTF_8)); + } + } + + + @Test + @SneakyThrows + @Disabled + void createFileFromEFSARegulationV1ForRedactionService() { + + URL url = getClass().getClassLoader().getResource("dossier_templates_new_format/qa/EFSA_sanitisation_GFL_v1/rules.txt"); + File file = new File(url.getFile()); + var identifiers = RuleFileParser.parseRuleIdentifiersFromFile(file); + var newRulesString = RuleFileFactory.createFileFromIdentifiers(identifiers, ApplicationType.RM); + try (var out = new FileOutputStream( +// "/home/kschuettler/iqser/redaction/redaction-service/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/acceptance_rules.drl")) { + "/home/aoezyetimoglu/repositories/rulesALI2.drl")) { + out.write(newRulesString.getBytes(StandardCharsets.UTF_8)); + } + } + + + @Test + @SneakyThrows + @Disabled + void assertAllRuleFileIsReconstructedExactlyIgnoringLineBreaks() { + + String identifiers = "SYN,CBI,PII,ETC,AI,MAN,X,FA,LDS"; + String actual = RuleFileFactory.createFileFromIdentifiers(identifiers, ApplicationType.RM).replace("\n", ""); + String expected = new String(RuleManagementResources.getAllRulesInputStream(ApplicationType.RM).readAllBytes()).replace("\n", ""); + assertEquals(expected, actual); + } + + + @Test + @Disabled + void createFileFromEmptyList() { + + String result = RuleFileFactory.createFileFromIdentifiers("", ApplicationType.RM); + System.out.println(result); + } + + /** + * Manual test to quickly unescape a rules file. Just change the path to whatever rule file you want to unescape + * and it will generate an 'output.txt' with the result. + */ + @Test + @SneakyThrows + @Disabled + public void unescapeRulesFile() { + + String pathToFile = "/Users/aisvoran/dev/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/factory/old.txt"; + unescapeRulesFile(pathToFile, "output.txt"); + } + + + /** + * Manual test to compare rules before and after updating them for easy checks. + */ + @Test + @Disabled + public void updateRulesAndCompare() { + + String pathToFile = "/Users/aisvoran/dev/dossier-templates-v2/dev/EFSA_sanitisation_pre_GFL_v1/rules.txt"; + unescapeRulesFile(pathToFile, "old.txt"); + + String dossierTemplatesRepo = "/Users/aisvoran/dev/dossier-templates-v2/dev/"; + migrate(ApplicationType.RM, dossierTemplatesRepo + "EFSA_sanitisation_pre_GFL_v1"); + + unescapeRulesFile(pathToFile, "new.txt"); + } + + @SneakyThrows + private void unescapeRulesFile(String pathToFile, String outputName) { + + String escapedRules = RuleFileIO.getRulesString(pathToFile); + + File output = new File(outputName); + FileOutputStream outputStream = new FileOutputStream(output.getName()); + byte[] strToBytes = escapedRules.getBytes(StandardCharsets.UTF_8); + outputStream.write(strToBytes); + outputStream.close(); + } + +} \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/translation/OldRulesParserTest.java b/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/translation/OldRulesParserTest.java new file mode 100644 index 00000000..3c20f3ec --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/translation/OldRulesParserTest.java @@ -0,0 +1,168 @@ +package com.knecon.fforesight.utility.rules.management.translation; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import com.knecon.fforesight.utility.rules.management.RuleManagementResources; +import com.knecon.fforesight.utility.rules.management.factory.RuleFileFactory; +import com.knecon.fforesight.utility.rules.management.factory.RuleFileParser; +import com.knecon.fforesight.utility.rules.management.models.ApplicationType; +import com.knecon.fforesight.utility.rules.management.models.BasicRule; +import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier; +import com.knecon.fforesight.utility.rules.management.models.RuleType; +import com.knecon.fforesight.utility.rules.management.models.RuleUnit; +import com.knecon.fforesight.utility.rules.management.utils.RuleFileIO; + +import lombok.SneakyThrows; + +class OldRulesParserTest { + + @Test + @SneakyThrows + @Disabled + public void translateOldRulesToNewIdentifiersTest() { + + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("efsa_regulation_rules.txt")) { + String oldRulesString = new String(inputStream.readAllBytes()); + Set identifiers = OldRulesParser.translateEscapedOldRulesStringToNewIdentifiers(oldRulesString, ApplicationType.RM); + System.out.println(identifiers); + } + } + + + @Test + @SneakyThrows + @Disabled + public void translateMoreOldRules() { + + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("basf/demo/table_demo.drl")) { + String oldRulesString = new String(inputStream.readAllBytes()); + Set identifiers = OldRulesParser.translateOldRulesStringToNewIdentifiers(oldRulesString, ApplicationType.RM); + System.out.println(identifiers); + } + } + + + @Test + @Disabled + public void printTranslationsTest() { + + List records = OldRulesParser.getOldRulesCsvRecords(RuleManagementResources.getOldRulesCsvInputStream()); + List.of(RuleType.SYN, RuleType.CBI, RuleType.PII, RuleType.ETC, RuleType.AI).forEach(type -> { + List rulesOfClass = RuleFileParser.buildBluePrintFromAllRulesFile(ApplicationType.RM).findRuleClassByType(type).ruleUnits(); + rulesOfClass.forEach(unit -> printOldRulesThatTranslatesToNewRule(unit, records)); + }); + } + + + private void printOldRulesThatTranslatesToNewRule(RuleUnit ruleUnit, List records) { + + if (ruleUnit.rules().isEmpty()) { + System.out.println("Rule unit empty, skipping!\n"); + return; + } + RuleIdentifier identifier = ruleUnit.rules().get(0).identifier(); + RuleIdentifier unitIdentifier = new RuleIdentifier(identifier.type(), identifier.unit(), null); + String oldNames = records.stream() + .filter(r -> r.translatesTo().stream().anyMatch(i -> i.matches(unitIdentifier))) + .map(OldRulesParser.OldRulesCsvRecord::names) + .map(OldRulesParserTest::removeIdFromName) + .distinct() + .collect(Collectors.joining(", ")); + System.out.println(unitIdentifier.toRuleUnitString() + " " + ruleUnit.rules().stream().map(BasicRule::name).collect(Collectors.joining(", "))); + System.out.println("translate from"); + System.out.println(oldNames); + System.out.println(); + System.out.println(); + } + + + private static String removeIdFromName(String name) { + + String[] values = name.split(":"); + return String.join(":", Arrays.copyOfRange(values, 1, values.length)); + } + + + + @Test + @Disabled + @SneakyThrows + public void findAllCustomerRuleFilesInDossierTemplatesRepoAndTranslate() { + + String dossierTemplatesRepo = getClass().getClassLoader().getResource("pilot/EFSA_sanitisation_GFL_v1").getFile(); + RuleFileIO.streamAllRuleFilesInDirectory(Path.of(dossierTemplatesRepo)) + .map(e -> OldRulesParser.translateOldRulesFileToNewIdentifiers(e, ApplicationType.RM)) + .map(e -> RuleFileFactory.createFileFromIdentifiers(e, ApplicationType.RM)) + .peek(System.out::println) + .map(RuleFileIO::escapeAndWrap) + .forEach(System.out::println); + + } + + + @Test + @Disabled + @SneakyThrows + public void findAllRuleFilesInDossierTemplatesRepoAndTranslate() { + + String dossierTemplatesRepo = getClass().getClassLoader().getResource("business-logic/Syngenta_RSS").getFile(); + RuleFileIO.streamAllRuleFilesInDirectory(Path.of(dossierTemplatesRepo)) + .map(e -> OldRulesParser.translateOldRulesFileToNewIdentifiers(e, ApplicationType.DM)) + .map(e -> RuleFileFactory.createFileFromIdentifiers(e, ApplicationType.DM)) + .peek(System.out::println) + .map(RuleFileIO::escapeAndWrap) + .forEach(System.out::println); + + } + + + @Test + @Disabled + @SneakyThrows + public void translateDossierTemplatesV2() { + +// String dossierTemplatesRepo = "/home/aoezyetimoglu/repositories/RED/dossier-templates-v2/"; +// Stream.of(Files.walk(Path.of(dossierTemplatesRepo + "dev")), Files.walk(Path.of(dossierTemplatesRepo + "docu")), Files.walk(Path.of(dossierTemplatesRepo + "qa"))) + String dossierTemplatesRepo = "/home/aoezyetimoglu/repositories/PROJECTMANAGEMENT/Syngenta/business-logic/"; + Stream.of( + Files.walk(Path.of(dossierTemplatesRepo + "dev")), + Files.walk(Path.of(dossierTemplatesRepo + "dev-v2")), + Files.walk(Path.of(dossierTemplatesRepo + "prod-cp-eu-reg")), + Files.walk(Path.of(dossierTemplatesRepo + "prod-cp-global-reg")), + Files.walk(Path.of(dossierTemplatesRepo + "prod-seeds-reg")) // + ) + .flatMap(Function.identity())// + .filter(path -> path.getFileName().toString().equals("rules.drl"))// + .map(Path::toFile)// + .forEach(this::translateOldRuleFile); + + } + + + @SneakyThrows + private void translateOldRuleFile(File oldRulesFile) { + + Set identifiers = OldRulesParser.translateOldRulesFileToNewIdentifiers(oldRulesFile, ApplicationType.RM); + String newRulesString = RuleFileFactory.createFileFromIdentifiers(identifiers, ApplicationType.RM); + String result = RuleFileIO.escapeAndWrap(newRulesString); + + try (var out = new FileOutputStream(oldRulesFile)) { + out.write(result.getBytes(StandardCharsets.UTF_8)); + } + } + +} \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/EFSA_sanitisation_GFL_v1/rules.txt b/redaction-service-v1/rules-management/src/test/resources/EFSA_sanitisation_GFL_v1/rules.txt new file mode 100644 index 00000000..92fa5519 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/EFSA_sanitisation_GFL_v1/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport static java.lang.String.format;\nimport static com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.RedactionSearchUtility.anyMatch;\nimport static com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.RedactionSearchUtility.exactMatch;\n\nimport java.util.List;\nimport java.util.LinkedList;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.Collection;\nimport java.util.stream.Stream;\nimport java.util.Optional;\n\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.*;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.*;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Section;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Table;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.SemanticNode;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Document;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Paragraph;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.Image;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.entity.*;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.textblock.*;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.entity.EntityType;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.nodes.ImageType;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Engine;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.services.EntityCreationService;\nimport com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.Dictionary;\nimport com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.DictionaryModel;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualImageRecategorization;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.services.ManualRedactionApplicationService;\nimport com.iqser.red.service.redaction.v1.server.client.model.EntityRecognitionEntity;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.Boundary;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.entity.RedactionEntity;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.Boundary;\nimport com.iqser.red.service.redaction.v1.server.redaction.adapter.NerEntitiesAdapter;\nimport com.iqser.red.service.redaction.v1.server.redaction.adapter.NerEntities;\nimport com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.RedactionSearchUtility;\n\nglobal Document document\nglobal EntityCreationService entityCreationService\nglobal ManualRedactionApplicationService manualRedactionApplicationService\nglobal NerEntitiesAdapter nerEntitiesAdapter\nglobal Dictionary dictionary\n\n//------------------------------------ queries ------------------------------------\n\nquery \"getFileAttributes\"\n $fileAttribute: FileAttribute()\n end\n\n//------------------------------------ Syngenta specific rules ------------------------------------\n\n// Rule unit: SYN.1\nrule \"SYN.1.0: Recommend CTL/BL laboratory that start with BL or CTL\"\n when\n $section: Section(containsString(\"CT\") || containsString(\"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 entityCreationService.byRegexIgnoreCase(\"((\\\\b((([Cc]T(([1ILli\\\\/])| L|~P))|(BL))[\\\\. ]?([\\\\dA-Ziltphz~\\\\/.:!]| ?[\\\\(',][Ppi](\\\\(e)?|([\\\\(-?']\\\\/))+( ?[\\\\(\\\\/\\\\dA-Znasieg]+)?)\\\\b( ?\\\\/? ?\\\\d+)?)|(\\\\bCT[L1i]\\\\b))\", \"CBI_address\", EntityType.RECOMMENDATION, $section)\n .forEach(entity -> {\n entity.skip(\"SYN.1.0\", \"\");\n entity.addEngine(Engine.RULE);\n insert(entity);\n });\n end\n\n\n//------------------------------------ CBI rules ------------------------------------\n\n// Rule unit: CBI.0\nrule \"CBI.0.0: Redact CBI Authors (Non Vertebrate Study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $entity: RedactionEntity(type == \"CBI_author\", dictionaryEntry)\n then\n $entity.apply(\"CBI.0.0\", \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end\n\nrule \"CBI.0.1: Redact CBI Authors (Vertebrate Study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $entity: RedactionEntity(type == \"CBI_author\", dictionaryEntry)\n then\n $entity.apply(\"CBI.0.1\", \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end\n\n\n// Rule unit: CBI.1\nrule \"CBI.1.0: Don't redact CBI Address (Non Vertebrate Study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $entity: RedactionEntity(type == \"CBI_address\", dictionaryEntry)\n then\n $entity.skip(\"CBI.1.0\", \"Address found for Non Vertebrate Study\");\n end\n\nrule \"CBI.1.1: Redact CBI Address (Vertebrate Study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $entity: RedactionEntity(type == \"CBI_address\", dictionaryEntry)\n then\n $entity.apply(\"CBI.1.1\", \"Address found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end\n\n\n// Rule unit: CBI.2\nrule \"CBI.2.0: Don't redact genitive CBI_author\"\n when\n $entity: RedactionEntity(type == \"CBI_author\", anyMatch(textAfter, \"['’’'ʼˈ´`‘′ʻ’']s\"), isApplied())\n then\n RedactionEntity falsePositive = entityCreationService.byBoundary($entity.getBoundary(), \"CBI_author\", EntityType.FALSE_POSITIVE, document);\n falsePositive.skip(\"CBI.2.0\", \"Genitive Author found\");\n insert(falsePositive);\n end\n\n\n// Rule unit: CBI.7\nrule \"CBI.7.0: Do not redact Names and Addresses if published information found in section without tables\"\n when\n $section: Section(!hasTables(),\n hasEntitiesOfType(\"published_information\"),\n (hasEntitiesOfType(\"CBI_author\") || hasEntitiesOfType(\"CBI_address\")))\n then\n $section.getEntitiesOfType(List.of(\"CBI_author\", \"CBI_address\"))\n .forEach(redactionEntity -> {\n redactionEntity.skipWithReferences(\n \"CBI.7.0\",\n \"Published Information found in section\",\n $section.getEntitiesOfType(\"published_information\")\n );\n });\n end\n\nrule \"CBI.7.1: Do not redact Names and Addresses if published information found in same table row\"\n when\n $table: Table(hasEntitiesOfType(\"published_information\"),\n (hasEntitiesOfType(\"CBI_author\") || hasEntitiesOfType(\"CBI_address\")))\n then\n $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of(\"CBI_author\", \"CBI_address\"))\n .forEach(redactionEntity -> {\n redactionEntity.skipWithReferences(\n \"CBI.7.1\",\n \"Published Information found in row\",\n $table.getEntitiesOfTypeInSameRow(\"published_information\", redactionEntity)\n );\n });\n end\n\n\n// Rule unit: CBI.9\nrule \"CBI.9.0: Redact all Cell's with Header Author(s) as CBI_author (non vertebrate study)\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $table: Table(hasHeader(\"Author(s)\"))\n then\n $table.streamTableCellsWithHeader(\"Author(s)\")\n .map(tableCell -> entityCreationService.bySemanticNode(tableCell, \"CBI_author\", EntityType.ENTITY))\n .filter(Optional::isPresent)\n .map(Optional::get)\n .forEach(redactionEntity -> {\n redactionEntity.apply(\"CBI.9.0\", \"Author(s) found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n redactionEntity.addEngine(Engine.RULE);\n insert(redactionEntity);\n });\n end\n\nrule \"CBI.9.1: Redact all Cell's with Header Author as CBI_author (non vertebrate study)\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $table: Table(hasHeader(\"Author\"))\n then\n $table.streamTableCellsWithHeader(\"Author\")\n .map(tableCell -> entityCreationService.bySemanticNode(tableCell, \"CBI_author\", EntityType.ENTITY))\n .filter(Optional::isPresent)\n .map(Optional::get)\n .forEach(redactionEntity -> {\n redactionEntity.apply(\"CBI.9.1\", \"Author found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n redactionEntity.addEngine(Engine.RULE);\n insert(redactionEntity);\n });\n end\n\n\n// Rule unit: CBI.10\nrule \"CBI.10.0: Redact all Cell's with Header Author(s) as CBI_author (vertebrate study)\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $table: Table(hasHeader(\"Author(s)\"))\n then\n $table.streamTableCellsWithHeader(\"Author(s)\")\n .map(tableCell -> entityCreationService.bySemanticNode(tableCell, \"CBI_author\", EntityType.ENTITY))\n .filter(Optional::isPresent)\n .map(Optional::get)\n .forEach(redactionEntity -> {\n redactionEntity.apply(\"CBI.10.0\", \"Author(s) found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n redactionEntity.addEngine(Engine.RULE);\n insert(redactionEntity);\n });\n end\n\nrule \"CBI.10.1: Redact all Cell's with Header Author as CBI_author (vertebrate study)\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $table: Table(hasHeader(\"Author\"))\n then\n $table.streamTableCellsWithHeader(\"Author\")\n .map(tableCell -> entityCreationService.bySemanticNode(tableCell, \"CBI_author\", EntityType.ENTITY))\n .filter(Optional::isPresent)\n .map(Optional::get)\n .forEach(redactionEntity -> {\n redactionEntity.apply(\"CBI.10.1\", \"Author found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n redactionEntity.addEngine(Engine.RULE);\n insert(redactionEntity);\n });\n end\n\n\n// Rule unit: CBI.11\nrule \"CBI.11.0: Recommend all CBI_author entities in Table with Vertebrate Study Y/N Header\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n salience -1\n when\n $table: Table(hasHeader(\"Author(s)\") && hasHeader(\"Vertebrate Study Y/N\"))\n then\n $table.getEntitiesOfType(\"CBI_author\").forEach(entity -> dictionary.addMultipleAuthorsAsRecommendation(entity));\n end\n\n\n// Rule unit: CBI.16\nrule \"CBI.16.0: Add CBI_author with \\\"et al.\\\" Regex (non vertebrate study)\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(containsString(\"et al.\"))\n then\n entityCreationService.byRegex(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", \"CBI_author\", EntityType.ENTITY, 1, $section)\n .forEach(entity -> {\n entity.apply(\"CBI.16.0\", \"Author found by \\\"et al\\\" regex\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n entity.addEngine(Engine.RULE);\n dictionary.addLocalDictionaryEntry(\"CBI_author\", entity.getValue(), false);\n insert(entity);\n });\n end\n\nrule \"CBI.16.1: Add CBI_author with \\\"et al.\\\" Regex (vertebrate study)\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(containsString(\"et al.\"))\n then\n entityCreationService.byRegex(\"\\\\b([A-ZÄÖÜ][^\\\\s\\\\.,]+( [A-ZÄÖÜ]{1,2}\\\\.?)?( ?[A-ZÄÖÜ]\\\\.?)?) et al\\\\.?\", \"CBI_author\", EntityType.ENTITY, 1, $section)\n .forEach(entity -> {\n entity.apply(\"CBI.16.1\", \"Author found by \\\"et al\\\" regex\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n entity.addEngine(Engine.RULE);\n insert(entity);\n dictionary.addLocalDictionaryEntry(\"CBI_author\", entity.getValue(), false);\n });\n end\n\n\n// Rule unit: CBI.17\nrule \"CBI.17.0: Add recommendation for Addresses in Test Organism sections, without colon\"\n when\n $section: Section(!hasTables(), containsString(\"Species\") && containsString(\"Source\") && !containsString(\"Species:\") && !containsString(\"Source:\"))\n then\n entityCreationService.lineAfterString(\"Source\", \"CBI_address\", EntityType.RECOMMENDATION, $section)\n .forEach(entity -> {\n entity.addEngine(Engine.RULE);\n entity.skip(\"CBI.17.0\", \"Line after \\\"Source\\\" in Test Organism Section\");\n insert(entity);\n });\n end\n\nrule \"CBI.17.1: Add recommendation for Addresses in Test Organism sections, with colon\"\n when\n $section: Section(!hasTables(), containsString(\"Species:\"), containsString(\"Source:\"))\n then\n entityCreationService.lineAfterString(\"Source:\", \"CBI_address\", EntityType.RECOMMENDATION, $section)\n .forEach(entity -> {\n entity.addEngine(Engine.RULE);\n entity.skip(\"CBI.17.1\", \"Line after \\\"Source:\\\" in Test Animals Section\");\n insert(entity);\n });\n end\n\n\n// Rule unit: CBI.20\nrule \"CBI.20.0: Redact between \\\"PERFORMING LABORATORY\\\" and \\\"LABORATORY PROJECT ID:\\\" (non vertebrate study)\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value == \"Yes\")\n $section: Section(!hasTables(), containsString(\"PERFORMING LABORATORY:\"), containsString(\"LABORATORY PROJECT ID:\"))\n then\n entityCreationService.betweenStrings(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", EntityType.ENTITY, $section)\n .forEach(laboratoryEntity -> {\n laboratoryEntity.skip(\"CBI.20.0\", \"PERFORMING LABORATORY was found for non vertebrate study\");\n laboratoryEntity.addEngine(Engine.RULE);\n dictionary.addLocalDictionaryEntry(laboratoryEntity);\n insert(laboratoryEntity);\n });\n end\n\nrule \"CBI.20.1: Redact between \\\"PERFORMING LABORATORY\\\" and \\\"LABORATORY PROJECT ID:\\\" (vertebrate study)\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n when\n FileAttribute(label == \"Vertebrate Study\", value == \"Yes\")\n $section: Section(!hasTables(), containsString(\"PERFORMING LABORATORY:\"), containsString(\"LABORATORY PROJECT ID:\"))\n then\n entityCreationService.betweenStrings(\"PERFORMING LABORATORY:\", \"LABORATORY PROJECT ID:\", \"CBI_address\", EntityType.ENTITY, $section)\n .forEach(laboratoryEntity -> {\n laboratoryEntity.apply(\"CBI.20.1\", \"PERFORMING LABORATORY was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n laboratoryEntity.addEngine(Engine.RULE);\n dictionary.addLocalDictionaryEntry(laboratoryEntity);\n insert(laboratoryEntity);\n });\n end\n\n\n//------------------------------------ PII rules ------------------------------------\n\n// Rule unit: PII.0\nrule \"PII.0.0: Redact all PII (non vertebrate study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $pii: RedactionEntity(type == \"PII\", dictionaryEntry)\n then\n $pii.apply(\"PII.0.0\", \"Personal Information found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end\n\nrule \"PII.0.1: Redact all PII (vertebrate study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $pii: RedactionEntity(type == \"PII\", dictionaryEntry)\n then\n $pii.apply(\"PII.0.1\", \"Personal Information found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end\n\n\n// Rule unit: PII.1\nrule \"PII.1.0: Redact Emails by RegEx (Non vertebrate study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(containsString(\"@\"))\n then\n 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)\n .forEach(emailEntity -> {\n emailEntity.addEngine(Engine.RULE);\n emailEntity.apply(\"PII.1.0\", \"Found by Email Regex\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n insert(emailEntity);\n });\n end\n\nrule \"PII.1.1: Redact Emails by RegEx (vertebrate study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(containsString(\"@\"))\n then\n 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)\n .forEach(emailEntity -> {\n emailEntity.addEngine(Engine.RULE);\n emailEntity.apply(\"PII.1.1\", \"Found by Email Regex\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n insert(emailEntity);\n });\n end\n\n\n// Rule unit: PII.2\nrule \"PII.2.0: Redact Phone and Fax by RegEx (non vertebrate study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(containsString(\"Contact\") ||\n containsString(\"Telephone\") ||\n containsString(\"Phone\") ||\n containsString(\"Ph.\") ||\n containsString(\"Fax\") ||\n containsString(\"Tel\") ||\n containsString(\"Ter\") ||\n containsString(\"Mobile\") ||\n containsString(\"Fel\") ||\n containsString(\"Fer\"))\n then\n entityCreationService.byRegexIgnoreCase(\"\\\\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\", \"PII\", EntityType.ENTITY, 2, $section)\n .forEach(contactEntity -> {\n contactEntity.addEngine(Engine.RULE);\n contactEntity.apply(\"PII.2.0\", \"Found by Phone and Fax Regex\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n insert(contactEntity);\n });\n end\n\nrule \"PII.2.1: Redact Phone and Fax by RegEx (vertebrate study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(containsString(\"Contact\") ||\n containsString(\"Telephone\") ||\n containsString(\"Phone\") ||\n containsString(\"Ph.\") ||\n containsString(\"Fax\") ||\n containsString(\"Tel\") ||\n containsString(\"Ter\") ||\n containsString(\"Mobile\") ||\n containsString(\"Fel\") ||\n containsString(\"Fer\"))\n then\n entityCreationService.byRegexIgnoreCase(\"\\\\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\", \"PII\", EntityType.ENTITY, 2, $section)\n .forEach(contactEntity -> {\n contactEntity.addEngine(Engine.RULE);\n contactEntity.apply(\"PII.2.1\", \"Found by Phone and Fax Regex\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n insert(contactEntity);\n });\n end\n\n\n// Rule unit: PII.9\nrule \"PII.9.0: Redact between \\\"AUTHOR(S)\\\" and \\\"COMPLETION DATE\\\" (non vertebrate study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(!hasTables(), containsString(\"AUTHOR(S):\"), containsString(\"COMPLETION DATE:\"), !containsString(\"STUDY COMPLETION DATE:\"))\n then\n entityCreationService.betweenStrings(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", EntityType.ENTITY, $section)\n .forEach(authorEntity -> {\n authorEntity.apply(\"PII.9.0\", \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n authorEntity.addEngine(Engine.RULE);\n insert(authorEntity);\n });\n end\n\nrule \"PII.9.1: Redact between \\\"AUTHOR(S)\\\" and \\\"STUDY COMPLETION DATE\\\" (non vertebrate study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(!hasTables(), containsString(\"AUTHOR(S):\"), containsString(\"COMPLETION DATE:\"), !containsString(\"STUDY COMPLETION DATE:\"))\n then\n entityCreationService.betweenStrings(\"AUTHOR(S):\", \"COMPLETION DATE:\", \"PII\", EntityType.ENTITY, $section)\n .forEach(authorEntity -> {\n authorEntity.apply(\"PII.9.1\", \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n authorEntity.addEngine(Engine.RULE);\n insert(authorEntity);\n });\n end\n\nrule \"PII.9.2: Redact between \\\"AUTHOR(S)\\\" and \\\"COMPLETION DATE\\\" (non vertebrate study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(!hasTables(), containsString(\"AUTHOR(S):\"), containsString(\"STUDY COMPLETION DATE:\"))\n then\n entityCreationService.betweenStrings(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", EntityType.ENTITY, $section)\n .forEach(authorEntity -> {\n authorEntity.apply(\"PII.9.2\", \"AUTHOR(S) was found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n authorEntity.addEngine(Engine.RULE);\n insert(authorEntity);\n });\n end\n\nrule \"PII.9.3: Redact between \\\"AUTHOR(S)\\\" and \\\"STUDY COMPLETION DATE\\\" (vertebrate study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value.toLowerCase() == \"yes\")\n $section: Section(!hasTables(), containsString(\"AUTHOR(S):\"), containsString(\"STUDY COMPLETION DATE:\"))\n then\n entityCreationService.betweenStrings(\"AUTHOR(S):\", \"STUDY COMPLETION DATE:\", \"PII\", EntityType.ENTITY, $section)\n .forEach(authorEntity -> {\n authorEntity.apply(\"PII.9.3\", \"AUTHOR(S) was found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n authorEntity.addEngine(Engine.RULE);\n insert(authorEntity);\n });\n end\n\n\n//------------------------------------ Other rules ------------------------------------\n\n// Rule unit: ETC.0\nrule \"ETC.0.0: Purity Hint\"\n when\n $section: Section(containsStringIgnoreCase(\"purity\"))\n then\n entityCreationService.byRegexIgnoreCase(\"(purity ?( of|\\\\(.{1,20}\\\\))?( ?:)?) .{0,5}[\\\\d\\\\.]+( .{0,4}\\\\.)? ?%\", \"hint_only\", EntityType.ENTITY, 1, $section)\n .forEach(hint -> {\n hint.addEngine(Engine.RULE);\n hint.skip(\"ETC.0.0\", \"\");\n });\n end\n\n\n// Rule unit: ETC.2\nrule \"ETC.2.0: Redact signatures (non vertebrate study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value == \"Yes\")\n $signature: Image(imageType == ImageType.SIGNATURE)\n then\n $signature.apply(\"ETC.2.0\", \"Signature Found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end\n\nrule \"ETC.2.0: Redact signatures (vertebrate study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value == \"Yes\")\n $signature: Image(imageType == ImageType.SIGNATURE)\n then\n $signature.apply(\"ETC.2.0\", \"Signature Found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end\n\n\n// Rule unit: ETC.3\nrule \"ETC.3.0: Redact logos (vertebrate study)\"\n when\n not FileAttribute(label == \"Vertebrate Study\", value == \"Yes\")\n $logo: Image(imageType == ImageType.LOGO)\n then\n $logo.apply(\"ETC.3.0\", \"Logo Found\", \"Article 39(e)(3) of Regulation (EC) No 178/2002\");\n end\n\nrule \"ETC.3.1: Redact logos (non vertebrate study)\"\n when\n FileAttribute(label == \"Vertebrate Study\", value == \"Yes\")\n $logo: Image(imageType == ImageType.LOGO)\n then\n $logo.apply(\"ETC.3.1\", \"Logo Found\", \"Article 39(e)(2) of Regulation (EC) No 178/2002\");\n end\n\n\n// Rule unit: ETC.5\nrule \"ETC.5.0: Ignore dossier_redaction entries if confidentiality is not 'confidential'\"\n when\n not FileAttribute(label == \"Confidentiality\", value == \"confidential\")\n $dossierRedaction: RedactionEntity(type == \"dossier_redaction\")\n then\n $dossierRedaction.removeFromGraph();\n retract($dossierRedaction);\n end\n\n\n//------------------------------------ AI rules ------------------------------------\n\n// Rule unit: AI.0\nrule \"AI.0.0: add all NER Entities of type CBI_author\"\n salience 999\n when\n nerEntities: NerEntities(hasEntitiesOfType(\"CBI_author\"))\n then\n nerEntities.streamEntitiesOfType(\"CBI_author\")\n .map(nerEntity -> entityCreationService.byNerEntity(nerEntity, EntityType.RECOMMENDATION, document))\n .forEach(entity -> insert(entity));\n end\n\n\n// Rule unit: AI.1\nrule \"AI.1.0: combine and add NER Entities as CBI_address\"\n salience 999\n when\n nerEntities: NerEntities(hasEntitiesOfType(\"ORG\") || hasEntitiesOfType(\"STREET\") || hasEntitiesOfType(\"CITY\"))\n then\n nerEntitiesAdapter.combineNerEntitiesToCbiAddressDefaults(nerEntities)\n .map(boundary -> entityCreationService.byBoundary(boundary, \"CBI_address\", EntityType.RECOMMENDATION, document))\n .forEach(entity -> {\n entity.addEngine(Engine.NER);\n insert(entity);\n });\n end\n\n\n//------------------------------------ Manual redaction rules ------------------------------------\n\n// Rule unit: MAN.0\nrule \"MAN.0.0: Apply manual resize redaction\"\n salience 128\n when\n $resizeRedaction: ManualResizeRedaction($id: annotationId)\n $entityToBeResized: RedactionEntity(matchesAnnotationId($id))\n then\n manualRedactionApplicationService.resizeEntityAndReinsert($entityToBeResized, $resizeRedaction);\n retract($resizeRedaction);\n update($entityToBeResized);\n end\n\n\n// Rule unit: MAN.1\nrule \"MAN.1.0: Apply id removals that are valid and not in forced redactions to Entity\"\n salience 128\n when\n IdRemoval(status == AnnotationStatus.APPROVED, !removeFromDictionary, requestDate != null, $id: annotationId)\n not ManualForceRedaction($id == annotationId, status == AnnotationStatus.APPROVED, requestDate != null)\n $entityToBeRemoved: RedactionEntity(matchesAnnotationId($id))\n then\n $entityToBeRemoved.setIgnored(true);\n end\n\nrule \"MAN.1.1: Apply id removals that are valid and not in forced redactions to Image\"\n salience 128\n when\n IdRemoval(status == AnnotationStatus.APPROVED, !removeFromDictionary, requestDate != null, $id: annotationId)\n not ManualForceRedaction($id == annotationId, status == AnnotationStatus.APPROVED, requestDate != null)\n $imageEntityToBeRemoved: Image($id == id)\n then\n $imageEntityToBeRemoved.setIgnored(true);\n end\n\n\n// Rule unit: MAN.2\nrule \"MAN.2.0: Apply force redaction\"\n salience 128\n when\n ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED, requestDate != null, $legalBasis: legalBasis)\n $entityToForce: RedactionEntity(matchesAnnotationId($id))\n then\n $entityToForce.apply(\"MAN.2.0\", \"Forced redaction\", $legalBasis);\n $entityToForce.setSkipRemoveEntitiesContainedInLarger(true);\n end\n\n\n// Rule unit: MAN.3\nrule \"MAN.3.0: Apply image recategorization\"\n salience 128\n when\n ManualImageRecategorization($id: annotationId, status == AnnotationStatus.APPROVED, $imageType: type)\n $image: Image($id == id)\n then\n $image.setImageType(ImageType.fromString($imageType));\n end\n\n\n//------------------------------------ Entity merging rules ------------------------------------\n\n// Rule unit: X.0\nrule \"X.0.0: remove Entity contained by Entity of same type\"\n salience 65\n when\n $larger: RedactionEntity($type: type, $entityType: entityType)\n $contained: RedactionEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !resized, !skipRemoveEntitiesContainedInLarger)\n then\n $contained.removeFromGraph();\n retract($contained);\n end\n\n\n// Rule unit: X.1\nrule \"X.1.0: merge intersecting Entities of same type\"\n salience 64\n when\n $first: RedactionEntity($type: type, $entityType: entityType, !resized, !skipRemoveEntitiesContainedInLarger)\n $second: RedactionEntity(intersects($first), type == $type, entityType == $entityType, this != $first, !resized, !skipRemoveEntitiesContainedInLarger)\n then\n $first.removeFromGraph();\n $second.removeFromGraph();\n RedactionEntity mergedEntity = entityCreationService.byEntities(List.of($first, $second), $type, $entityType, document);\n retract($first);\n retract($second);\n insert(mergedEntity);\n end\n\n\n// Rule unit: X.2\nrule \"X.2.0: remove Entity of type ENTITY when contained by FALSE_POSITIVE\"\n salience 64\n when\n $falsePositive: RedactionEntity($type: type, entityType == EntityType.FALSE_POSITIVE)\n $entity: RedactionEntity(containedBy($falsePositive), type == $type, entityType == EntityType.ENTITY, !resized, !skipRemoveEntitiesContainedInLarger)\n then\n $entity.removeFromGraph();\n retract($entity)\n end\n\n\n// Rule unit: X.3\nrule \"X.3.0: remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION\"\n salience 64\n when\n $falseRecommendation: RedactionEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION)\n $recommendation: RedactionEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !resized, !skipRemoveEntitiesContainedInLarger)\n then\n $recommendation.removeFromGraph();\n retract($recommendation);\n end\n\n\n// Rule unit: X.4\nrule \"X.4.0: remove Entity of type RECOMMENDATION when intersected by ENTITY with same type\"\n salience 256\n when\n $entity: RedactionEntity($type: type, entityType == EntityType.ENTITY)\n $recommendation: RedactionEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !resized, !skipRemoveEntitiesContainedInLarger)\n then\n $entity.addEngines($recommendation.getEngines());\n $recommendation.removeFromGraph();\n retract($recommendation);\n end\n\n\n// Rule unit: X.5\nrule \"X.5.0: remove Entity of type RECOMMENDATION when contained by ENTITY\"\n salience 256\n when\n $entity: RedactionEntity(entityType == EntityType.ENTITY)\n $recommendation: RedactionEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !resized, !skipRemoveEntitiesContainedInLarger)\n then\n $recommendation.removeFromGraph();\n retract($recommendation);\n end\n\n\n// Rule unit: X.6\nrule \"X.6.0: remove Entity of lower rank, when intersected by entity of type ENTITY\"\n salience 32\n when\n $higherRank: RedactionEntity($type: type, entityType == EntityType.ENTITY)\n $lowerRank: RedactionEntity(intersects($higherRank), type != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !resized, !skipRemoveEntitiesContainedInLarger)\n then\n $lowerRank.removeFromGraph();\n retract($lowerRank);\n end\n\n\n//------------------------------------ File attributes rules ------------------------------------\n\n// Rule unit: FA.1\nrule \"FA.1.0: remove duplicate FileAttributes\"\n salience 64\n when\n $fileAttribute: FileAttribute($label: label, $value: value)\n $duplicate: FileAttribute(this != $fileAttribute, label == $label, value == $value)\n then\n retract($duplicate);\n end\n\n\n//------------------------------------ Local dictionary search rules ------------------------------------\n\n// Rule unit: LDS.0\nrule \"LDS.0.0: run local dictionary search\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n salience -999\n when\n DictionaryModel(!localEntries.isEmpty(), $type: type, $searchImplementation: localSearch) from dictionary.getDictionaryModels()\n then\n entityCreationService.bySearchImplementation($searchImplementation, $type, EntityType.RECOMMENDATION, document)\n .forEach(entity -> {\n entity.addEngine(Engine.RULE);\n insert(entity);\n });\n end\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/addNewRulesHere.drl b/redaction-service-v1/rules-management/src/test/resources/addNewRulesHere.drl new file mode 100644 index 00000000..e69de29b diff --git a/redaction-service-v1/rules-management/src/test/resources/all_redact_manager_rules.drl b/redaction-service-v1/rules-management/src/test/resources/all_redact_manager_rules.drl new file mode 100644 index 00000000..ad8fa6ba --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/all_redact_manager_rules.drl @@ -0,0 +1,1550 @@ +package drools + +import static java.lang.String.format; +import static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.anyMatch; +import static com.iqser.red.service.redaction.v1.server.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.model.document.*; +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.EntityType; +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.MatchedRule +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.Table; +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.Document; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Paragraph; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.SectionIdentifier; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Footer; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Header; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType; +import com.iqser.red.service.redaction.v1.server.model.document.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.AtomicTextBlock; +import com.iqser.red.service.redaction.v1.server.model.document.textblock.ConcatenatedTextBlock; +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.DictionaryModel; +import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; +import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService; +import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; +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.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.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.AnnotationStatus; + +global Document document +global EntityCreationService entityCreationService +global ManualChangesApplicationService manualChangesApplicationService +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.HINT, $section), + entityCreationService.byString("BL", "must_redact", EntityType.HINT, $section) + ).forEach(entity -> entity.skip("SYN.0.0", "hint_only")); + end + + +// Rule unit: SYN.1 +rule "SYN.1.0: Recommend CTL/BL laboratory that start with BL or CTL" + when + $section: Section(containsString("CT") || containsString("BL")) + then + /* Regular expression: ((\b((([Cc]T(([1ILli\/])| L|~P))|(BL))[\. ]?([\dA-Ziltphz~\/.:!]| ?[\(',][Ppi](\(e)?|([\(-?']\/))+( ?[\(\/\dA-Znasieg]+)?)\b( ?\/? ?\d+)?)|(\bCT[L1i]\b)) */ + entityCreationService.byRegexIgnoreCase("((\\b((([Cc]T(([1ILli\\/])| L|~P))|(BL))[\\. ]?([\\dA-Ziltphz~\\/.:!]| ?[\\(',][Ppi](\\(e)?|([\\(-?']\\/))+( ?[\\(\\/\\dA-Znasieg]+)?)\\b( ?\\/? ?\\d+)?)|(\\bCT[L1i]\\b))", "CBI_address", EntityType.RECOMMENDATION, $section) + .forEach(entity -> entity.skip("SYN.1.0", "")); + end + + +//------------------------------------ CBI rules ------------------------------------ + +// Rule unit: CBI.0 +rule "CBI.0.0: Redact CBI Authors (non vertebrate Study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_author", dictionaryEntry) + then + $entity.redact("CBI.0.0", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002"); + end + +rule "CBI.0.1: Redact CBI Authors (vertebrate Study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_author", dictionaryEntry) + then + $entity.redact("CBI.0.1", "Author found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + end + + +// Rule unit: CBI.1 +rule "CBI.1.0: Do not redact CBI Address (non vertebrate Study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_address", dictionaryEntry) + then + $entity.skip("CBI.1.0", "Address found for Non Vertebrate Study"); + end + +rule "CBI.1.1: Redact CBI Address (vertebrate Study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_address", dictionaryEntry) + then + $entity.redact("CBI.1.1", "Address found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + end + + +// Rule unit: CBI.2 +rule "CBI.2.0: Do not redact genitive CBI Author" + when + $entity: TextEntity(type == "CBI_author", anyMatch(textAfter, "['’’'ʼˈ´`‘′ʻ’']s")) + then + entityCreationService.byTextRange($entity.getTextRange(), "CBI_author", EntityType.FALSE_POSITIVE, document) + .ifPresent(falsePositive -> falsePositive.skip("CBI.2.0", "Genitive Author found")); + end + + +// Rule unit: CBI.3 +rule "CBI.3.0: Redacted because Section contains a 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 a 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: Do not redact because Section does not contain a 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: Do not redact because table row does not contain a 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: Do not 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: Do not 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.6 +rule "CBI.6.0: Do not redact Names and Addresses if vertebrate but also published_information is found in Section" + when + $section: Section(!hasTables(), + hasEntitiesOfType("vertebrate"), + hasEntitiesOfType("published_information"), + (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + then + $section.getEntitiesOfType(List.of("CBI_author", "CBI_address")) + .forEach(entity -> { + entity.skipWithReferences( + "CBI.6.0", + "vertebrate but also published_information found", + Stream.concat( + $section.getEntitiesOfType("vertebrate").stream(), + $section.getEntitiesOfType("published_information").stream()).toList() + ); + }); + end + +rule "CBI.6.1: Do not redact Names and Addresses if vertebrate but also published_information is found in table row" + when + $table: Table(hasEntitiesOfType("vertebrate"), + hasEntitiesOfType("published_information"), + (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + then + $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate", "published_information")) + .filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address")) + .forEach(entity -> { + entity.skipWithReferences( + "CBI.6.1", + "vertebrate but also published_information found", + Stream.concat( + $table.getEntitiesOfTypeInSameRow("vertebrate", entity).stream(), + $table.getEntitiesOfTypeInSameRow("published_information", entity).stream()).toList() + ); + }); + end + + +// Rule unit: CBI.7 +rule "CBI.7.0: Do not redact Names and Addresses if published information found in Section without tables" + when + $section: Section(!hasTables(), + hasEntitiesOfType("published_information"), + (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + then + $section.getEntitiesOfType(List.of("CBI_author", "CBI_address")) + .forEach(redactionEntity -> { + redactionEntity.skipWithReferences( + "CBI.7.0", + "Published Information found in section", + $section.getEntitiesOfType("published_information") + ); + }); + end + +rule "CBI.7.1: Do not redact Names and Addresses if published information found in same table row" + when + $table: Table(hasEntitiesOfType("published_information"), + (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + then + $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("CBI_author", "CBI_address")) + .forEach(redactionEntity -> { + redactionEntity.skipWithReferences( + "CBI.7.1", + "Published Information found in row", + $table.getEntitiesOfTypeInSameRow("published_information", redactionEntity) + ); + }); + 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 cells 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.redact("CBI.9.0", "Author(s) found", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + +rule "CBI.9.1: Redact all cells 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.redact("CBI.9.1", "Author found", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + + +// Rule unit: CBI.10 +rule "CBI.10.0: Redact all cells with Header Author(s) as CBI_author (vertebrate study)" + agenda-group "LOCAL_DICTIONARY_ADDS" + when + 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.redact("CBI.10.0", "Author(s) found", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + end + +rule "CBI.10.1: Redact all cells with Header Author as CBI_author (vertebrate study)" + agenda-group "LOCAL_DICTIONARY_ADDS" + when + 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.redact("CBI.10.1", "Author found", "Article 39(e)(2) 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 cells 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: Do not 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.redact("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.13 +rule "CBI.13.0: Ignore CBI Address recommendations" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $entity: TextEntity(type == "CBI_address", entityType == EntityType.RECOMMENDATION) + then + $entity.ignore("CBI.13.0", "Ignore CBI Address Recommendations"); + retract($entity) + end + + +// Rule unit: CBI.14 +rule "CBI.14.0: Redact CBI_sponsor entities if preceded by \"batches produced at\"" + when + $sponsorEntity: TextEntity(type == "CBI_sponsor", textBefore.contains("batches produced at")) + then + $sponsorEntity.redact("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.redact("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.redact("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.redact("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.redact("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: TextEntity(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.addMatchedRules($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: TextEntity(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.addMatchedRules($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.redact("CBI.20.1", "PERFORMING LABORATORY was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + dictionary.recommendEverywhere(laboratoryEntity); + }); + end + +// Rule unit: CBI.21 +rule "CBI.21.0: Redact short Authors section (non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value == "Yes") + $section: Section(containsAnyStringIgnoreCase("author(s)", "author", "authors"), length() < 50, getTreeId().get(0) <= 20) //TODO: evaluate the reason of this rule + not TextEntity(type == "CBI_author", engines contains Engine.NER) from $section.getEntities() + then + entityCreationService.byRegexIgnoreCase("(?<=author\\(?s\\)?\\s\\n?)([\\p{Lu}\\p{L} ]{5,15}(,|\\n)?){1,3}", "CBI_author", EntityType.ENTITY, $section) + .forEach(entity -> { + entity.redact("CBI.21.0", "AUTHOR(S) was found", "Article 39(e)(3) of Regulation (EC) No 178/2002"); + }); + end + +rule "CBI.21.1: Redact short Authors section (vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value == "Yes") + $section: Section(containsAnyStringIgnoreCase("author(s)", "author", "authors"), length() < 50, getTreeId().get(0) <= 20) //TODO: evaluate the reason of this rule + not TextEntity(type == "CBI_author", engines contains Engine.NER) from $section.getEntities() + then + entityCreationService.byRegexIgnoreCase("(?<=author\\(?s\\)?\\s\\n?)([\\p{Lu}\\p{L} ]{5,15}(,|\\n)?){1,3}", "CBI_author", EntityType.ENTITY, $section) + .forEach(entity -> { + entity.redact("CBI.21.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + }); + end + + +// Rule unit: CBI.22 +rule "CBI.22.0: Redact Addresses in Reference Tables for vertebrate studies in non-vertebrate documents" + when + not FileAttribute(label == "Vertebrate Study", value == "Yes") + $table: Table(hasHeader("Vertebrate study Y/N"), 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.isType("CBI_address")) + .forEach(authorEntity -> authorEntity.redact("CBI.22.0", "Redacted because row is a vertebrate study", "Reg (EC) No 1107/2009 Art. 63 (2g)")); + 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: TextEntity(type == "PII", dictionaryEntry) + then + $pii.redact("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: TextEntity(type == "PII", dictionaryEntry) + then + $pii.redact("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.redact("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.redact("PII.1.1", "Found by Email Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + end + +rule "PII.1.2: Redact typoed Emails with indicator" + when + $section: Section(containsString("@") || containsStringIgnoreCase("mail")) + 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, $section) + .forEach(emailEntity -> emailEntity.redact("PII.1.2", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + + +// Rule unit: PII.2 +rule "PII.2.0: Redact Phone and Fax by RegEx (non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $section: Section(containsString("Contact") || + containsString("Telephone") || + containsString("Phone") || + containsString("Ph.") || + containsString("Fax") || + containsString("Tel") || + containsString("Ter") || + containsString("Mobile") || + containsString("Fel") || + containsString("Fer")) + then + entityCreationService.byRegexIgnoreCase("\\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", "PII", EntityType.ENTITY, 2, $section) + .forEach(contactEntity -> contactEntity.redact("PII.2.0", "Found by Phone and Fax Regex", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + +rule "PII.2.1: Redact Phone and Fax by RegEx (vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $section: Section(containsString("Contact") || + containsString("Telephone") || + containsString("Phone") || + containsString("Ph.") || + containsString("Fax") || + containsString("Tel") || + containsString("Ter") || + containsString("Mobile") || + containsString("Fel") || + containsString("Fer")) + then + entityCreationService.byRegexIgnoreCase("\\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", "PII", EntityType.ENTITY, 2, $section) + .forEach(contactEntity -> contactEntity.redact("PII.2.1", "Found by Phone and Fax Regex", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + end + +rule "PII.2.2: Redact phone numbers without indicators" + when + $section: Section(containsString("+")) + then + entityCreationService.byRegex("(\\+[\\dO]{1,2} )(\\([\\dO]{1,3}\\))?[\\d\\-O ]{8,15}", "PII", EntityType.ENTITY, $section) + .forEach(entity -> entity.redact("PII.2.2", "Personal information found", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + + +// Rule unit: PII.3 +rule "PII.3.0: Redact telephone numbers by RegEx (Non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $section: Section(matchesRegex("[+]\\d{1,}")) + 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) + .forEach(entity -> entity.redact("PII.3.0", "Telephone number found by regex", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + +rule "PII.3.1: Redact telephone numbers by RegEx (vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $section: Section(matchesRegex("[+]\\d{1,}")) + 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) + .forEach(entity -> entity.redact("PII.3.1", "Telephone number found by regex", "Article 39(e)(2) 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.redact("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 (vertebrate study)" + when + 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.redact("PII.4.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Reg (EC) No 1107/2009 Art. 63 (2e)")); + end + + +// Rule unit: PII.5 +rule "PII.5.0: Redact line after contact information keywords reduced (non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $contactKeyword: String() from List.of("Contact point:", + "Contact:", + "Alternative contact:", + "European contact:") + $section: Section(containsString($contactKeyword)) + then + entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) + .forEach(contactEntity -> contactEntity.redact("PII.5.0", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(3) of Regulation (EC) No 178/2002")); + end + +rule "PII.5.1: Redact line after contact information keywords reduced (Vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $contactKeyword: String() from List.of("Contact point:", + "Contact:", + "Alternative contact:", + "European contact:") + $section: Section(containsString($contactKeyword)) + then + entityCreationService.lineAfterString($contactKeyword, "PII", EntityType.ENTITY, $section) + .forEach(contactEntity -> contactEntity.redact("PII.5.1", "Found after \"" + $contactKeyword + "\" contact keyword", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + 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.redact("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 (vertebrate study)" + 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.redact("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.redact("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 (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.redact("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 (non vertebrate study)" + 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.redact("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 (vertebrate study)" + 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.redact("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 \"(STUDY) COMPLETION DATE\" (non vertebrate study)" + when + not FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) + 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) + .forEach(authorEntity -> authorEntity.redact("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\" (vertebrate study)" + when + FileAttribute(label == "Vertebrate Study", value.toLowerCase() == "yes") + $document: Document(containsStringIgnoreCase("AUTHOR(S)"), containsAnyStringIgnoreCase("COMPLETION DATE", "STUDY COMPLETION DATE")) + 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) + .forEach(authorEntity -> authorEntity.redact("PII.9.1", "AUTHOR(S) was found", "Article 39(e)(2) of Regulation (EC) No 178/2002")); + end + + +// Rule unit: PII.10 +rule "PII.10.0: Redact study director abbreviation" + when + $section: Section(containsString("KATH") || containsString("BECH") || containsString("KML")) + then + entityCreationService.byRegexIgnoreCase("((KATH)|(BECH)|(KML)) ?(\\d{4})","PII", EntityType.ENTITY, 1, $section) + .forEach(entity -> entity.redact("PII.10.0", "Personal information found", "Article 39(e)(3) 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.redact("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: TextEntity(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.addMatchedRules($entityToExpand.getMatchedRuleList())); + end + +// Rule unit: PII.13 +rule "PII.13.0: Add recommendation for PII after Contact Person" + when + $section: Section(containsString("contact person:")) + then + entityCreationService.lineAfterStringsIgnoreCase(List.of("Contact Person", "Contact Person:"), "PII", EntityType.RECOMMENDATION, $section) + .forEach(entity -> entity.skip("PII.13.0", "Line after \"Source:\" in Test Animals Section")); + end + + +// Rule unit: PII.14 +rule "PII.14.0: Recommend title prefixed words as PII" + when + $section: Section(containsAnyString("Dr ", "PD Dr ", "Prof. Dr ", "Dr. med. vet ", "Dr. rer. nat ", "PhD ", "BSc ", "(FH) ", "Mr ", "Mrs ", "Ms ", "Miss ", "Dr.", "PD. Dr.", "Prof. Dr.", "Dr. med. vet.", "Dr. rer. nat.", "PhD.", "BSc.", "(FH).", "Mr.", "Mrs.", "Ms.", "Miss.")) + then + entityCreationService.byRegex("((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})", "PII", EntityType.ENTITY, 3, $section); + end + + +//------------------------------------ Other rules ------------------------------------ + +// Rule unit: ETC.0 +rule "ETC.0.0: Purity Hint" + when + $section: Section(containsStringIgnoreCase("purity")) + then + 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")); + end + + +// 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.redact("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.redact("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.redact("ETC.2.1", "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.redact("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.redact("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: TextEntity(type == "dossier_redaction") + then + $dossierRedaction.redact("ETC.4.0", "Specification of impurity found", "Article 39(e)(3) of Regulation (EC) No 178/2002"); + end + +rule "ETC.4.1: Redact dossier dictionary entries" + when + $dossierRedaction: TextEntity(type == "dossier_redaction") + then + $dossierRedaction.redact("ETC.4.1", "Dossier Redaction found", "Article 39(1)(2) of Regulation (EC) No 178/2002"); + end + +rule "ETC.4.2: Redact dossier dictionary entries" + when + $dossierRedaction: TextEntity(type == "dossier_redaction") + then + $dossierRedaction.redact("ETC.4.2", "Dossier redaction found", "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 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: TextEntity(type == "dossier_redaction") + then + $dossierRedaction.ignore("ETC.5.0", "Ignore dossier redactions, when not confidential"); + $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.redact("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(), containsAnyString("DATA REQUIREMENT(S):", "TEST GUIDELINE(S):") && containsAnyString("OECD", "EPA", "OPPTS")) + then + RedactionSearchUtility.findTextRangesByRegex("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.redact("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.redact("ETC.8.1", "Logo Found", "Article 39(e)(2) of Regulation (EC) No 178/2002"); + end + +// Rule unit: ETC.9 +rule "ETC.9.0: Redact skipped impurities" + when + FileAttribute(label == "Redact Skipped Impurities", value == "Yes") + $skippedImpurities: TextEntity(type == "skipped_impurities") + then + $skippedImpurities.redact("ETC.9.0", "Occasional Impurity found", "Article 63(2)(b) of Regulation (EC) No 1107/2009"); + end + +rule "ETC.9.1: Redact impurities" + when + FileAttribute(label == "Redact Impurities", value == "Yes") + $skippedImpurities: TextEntity(type == "impurities") + then + $skippedImpurities.redact("ETC.9.1", "Impurity found", "Article 63(2)(b) of Regulation (EC) No 1107/2009"); + end + +// Rule unit: ETC.10 +rule "ETC.10.0: Redact Product Composition Information" + when + $compositionInformation: TextEntity(type == "product_composition") + then + $compositionInformation.redact("ETC.10.0", "Product Composition Information found", "Article 63(2)(d) of Regulation (EC) No 1107/2009"); + end + +// Rule unit: ETC.11 +rule "ETC.11.0: Recommend first line in table cell with name and address of owner" + when + $table: Table(hasHeader("Name and Address of Owner / Tenant"), containsString("trial site")) + $header: TableCell(isHeader(), containsString("Name and Address of Owner / Tenant")) from $table.streamTableCells().toList() + $tableCell: TableCell(col == $header.col, row == 2) from $table.streamTableCells().toList() + then + entityCreationService.bySemanticNode($tableCell, "PII", EntityType.RECOMMENDATION) + .ifPresent(redactionEntity -> redactionEntity.redact("ETC.11.0", "Trial Site owner and address found", "Article 39(e)(3) 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 + + +// Rule unit: AI.2 +rule "AI.2.0: Add all NER Entities of any type except CBI_author" + salience 999 + when + nerEntities: NerEntities() + then + nerEntities.getNerEntityList().stream() + .filter(nerEntity -> !nerEntity.type().equals("CBI_author")) + .forEach(nerEntity -> entityCreationService.byNerEntity(nerEntity, nerEntity.type().toLowerCase(), EntityType.RECOMMENDATION, document)); + end + + +// Rule unit: AI.3 +rule "AI.3.0: Recommend authors from AI as PII" + salience 999 + when + nerEntities: NerEntities(hasEntitiesOfType("CBI_author")) + then + nerEntities.streamEntitiesOfType("CBI_author") + .forEach(nerEntity -> entityCreationService.byNerEntity(nerEntity, "PII", EntityType.RECOMMENDATION, document)); + end + +//------------------------------------ Manual redaction rules ------------------------------------ + +// Rule unit: MAN.0 +rule "MAN.0.0: Apply manual resize redaction" + salience 128 + when + $resizeRedaction: ManualResizeRedaction($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate)) + $entityToBeResized: TextEntity(matchesAnnotationId($id)) + then + manualChangesApplicationService.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, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate)) + $imageToBeResized: Image(id == $id) + then + manualChangesApplicationService.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($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToBeRemoved: TextEntity(matchesAnnotationId($id)) + then + $entityToBeRemoved.getManualOverwrite().addChange($idRemoval); + 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($id: annotationId, status == AnnotationStatus.APPROVED) + $imageEntityToBeRemoved: Image($id == id) + then + $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); + update($imageEntityToBeRemoved); + retract($idRemoval); + update($imageEntityToBeRemoved.getParent()); + end + + +// Rule unit: MAN.2 +rule "MAN.2.0: Apply force redaction" + salience 128 + when + $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToForce: TextEntity(matchesAnnotationId($id)) + then + $entityToForce.getManualOverwrite().addChange($force); + update($entityToForce); + $entityToForce.getIntersectingNodes().forEach(node -> update(node)); + retract($force); + end + +rule "MAN.2.1: Apply force redaction to images" + salience 128 + when + $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED) + $imageToForce: Image(id == $id) + then + $imageToForce.getManualOverwrite().addChange($force); + update($imageToForce); + update($imageToForce.getParent()); + retract($force); + end + + +// Rule unit: MAN.3 +rule "MAN.3.0: Apply entity recategorization" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type != $type) + then + $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node)); + manualChangesApplicationService.recategorize($entityToBeRecategorized, $recategorization); + retract($recategorization); + // Entity is copied and inserted, so the old entity needs to be retracted to avoid duplication. + retract($entityToBeRecategorized); + end + +rule "MAN.3.1: Apply entity recategorization of same type" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type == $type) + then + $entityToBeRecategorized.getManualOverwrite().addChange($recategorization); + retract($recategorization); + end + +rule "MAN.3.2: Apply image recategorization" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $imageToBeRecategorized: Image($id == id) + then + manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); + update($imageToBeRecategorized); + update($imageToBeRecategorized.getParent()); + retract($recategorization); + end + + +// Rule unit: MAN.4 +rule "MAN.4.0: Apply legal basis change" + salience 128 + when + $legalbasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED) + $imageToBeRecategorized: Image($id == id) + then + $imageToBeRecategorized.getManualOverwrite().addChange($legalbasisChange); + end + +rule "MAN.4.1: Apply legal basis change" + salience 128 + when + $legalBasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToBeChanged: TextEntity(matchesAnnotationId($id)) + then + $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange); + end + + +//------------------------------------ Entity merging rules ------------------------------------ + +// Rule unit: X.0 +rule "X.0.0: Remove Entity contained by Entity of same type" + salience 65 + when + $larger: TextEntity($type: type, $entityType: entityType, active()) + $contained: TextEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !hasManualChanges(), active()) + then + $contained.remove("X.0.0", "remove Entity contained by Entity of same type"); + retract($contained); + end + + +// Rule unit: X.1 +rule "X.1.0: Merge intersecting Entities of same type" + salience 64 + when + $first: TextEntity($type: type, $entityType: entityType, !resized(), active()) + $second: TextEntity(intersects($first), type == $type, entityType == $entityType, this != $first, !hasManualChanges(), active()) + then + TextEntity mergedEntity = entityCreationService.mergeEntitiesOfSameType(List.of($first, $second), $type, $entityType, document); + $first.remove("X.1.0", "merge intersecting Entities of same type"); + $second.remove("X.1.0", "merge intersecting Entities of same type"); + retract($first); + retract($second); + mergedEntity.getIntersectingNodes().forEach(node -> update(node)); + end + + +// Rule unit: X.2 +rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" + salience 64 + when + $falsePositive: TextEntity($type: type, entityType == EntityType.FALSE_POSITIVE, active()) + $entity: TextEntity(containedBy($falsePositive), type == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges(), active()) + then + $entity.getIntersectingNodes().forEach(node -> update(node)); + $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); + retract($entity) + end + + +// Rule unit: X.3 +rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION" + salience 64 + when + $falseRecommendation: TextEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION, active()) + $recommendation: TextEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); + retract($recommendation); + end + + +// Rule unit: X.4 +rule "X.4.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY with same type" + salience 256 + when + $entity: TextEntity($type: type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $recommendation: TextEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $entity.addEngines($recommendation.getEngines()); + $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY with same type"); + retract($recommendation); + end + + +// Rule unit: X.5 +rule "X.5.0: Remove Entity of type RECOMMENDATION when contained by ENTITY" + salience 256 + when + $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $recommendation: TextEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when contained by ENTITY"); + retract($recommendation); + end + + +// Rule unit: X.6 +rule "X.6.0: Remove Entity of lower rank, when contained by by entity of type ENTITY" + salience 32 + when + $higherRank: TextEntity($type: type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $lowerRank: TextEntity(containedBy($higherRank), type != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges(), active()) + then + $lowerRank.getIntersectingNodes().forEach(node -> update(node)); + $lowerRank.remove("X.6.0", "remove Entity of lower rank, when contained by entity of type ENTITY"); + retract($lowerRank); + end + + +rule "X.6.1: remove Entity of higher rank, when intersected by entity of type ENTITY and length of lower rank Entity is bigger than the higher rank Entity" + salience 32 + when + $higherRank: TextEntity($type: type, $value: value, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $lowerRank: TextEntity(intersects($higherRank), type != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges(), active(), $lowerRank.getValue().length() > $value.length()) + then + $higherRank.getIntersectingNodes().forEach(node -> update(node)); + $higherRank.remove("X.6.1", "remove Entity of higher rank, when intersected by entity of type ENTITY and length of lower rank Entity is bigger than the higher rank Entity"); + retract($higherRank); + 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 matchedRules = $dictionaryModel.getLocalEntriesWithMatchedRules().get(entity.getValue()); + entity.addMatchedRules(matchedRules); + }); + end diff --git a/redaction-service-v1/rules-management/src/test/resources/all_rules_documine.drl b/redaction-service-v1/rules-management/src/test/resources/all_rules_documine.drl new file mode 100644 index 00000000..935ef3dc --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/all_rules_documine.drl @@ -0,0 +1,1397 @@ +package drools + +import static java.lang.String.format; +import static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.anyMatch; +import static com.iqser.red.service.redaction.v1.server.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.model.document.*; +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.EntityType; +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.MatchedRule +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.Table; +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.Document; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Paragraph; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Image; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.SectionIdentifier; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Footer; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Header; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType; +import com.iqser.red.service.redaction.v1.server.model.document.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.AtomicTextBlock; +import com.iqser.red.service.redaction.v1.server.model.document.textblock.ConcatenatedTextBlock; +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.DictionaryModel; +import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; +import com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService; +import com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; +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.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.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.AnnotationStatus; + +global Document document +global EntityCreationService entityCreationService +global ManualChangesApplicationService manualChangesApplicationService +global Dictionary dictionary + +//------------------------------------ queries ------------------------------------ + +query "getFileAttributes" + $fileAttribute: FileAttribute() + end + +//------------------------------------ H rules ------------------------------------ + +// Rule unit: H.0 +rule "H.0.0: retract table of contents page" + when + $page: Page(getMainBodyTextBlock().getSearchText().contains("........") || (getMainBodyTextBlock().getSearchText().contains("APPENDICES") && getMainBodyTextBlock().getSearchText().contains("TABLES"))) + $node: SemanticNode(onPage($page.getNumber()), !onPage($page.getNumber() -1), getType() != NodeType.IMAGE) + then + retract($node); + end + + +// Rule unit: H.1 +rule "H.1.0: Ignore Table of Contents" + salience 10 + when + $tocHeadline: Headline(containsString("CONTENTS")) + $page: Page() from $tocHeadline.getParent().getPages() + $node: SemanticNode(this != $tocHeadline, getType() != NodeType.IMAGE, onPage($page.getNumber()), !onPage($page.getNumber() -1)) + then + retract($node); + end + + +// Rule unit: H.2 +rule "H.2.0: Show headlines" + when + $headline: Headline() + then + entityCreationService.bySemanticNode($headline, "headline", EntityType.HINT); + end + + +// Rule unit: H.3 +rule "H.3.0: Study Type File Attribute" + when + not FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","438","439","471","487")) + $section: Section( + (containsString("DATA REQUIREMENT") || containsString("TEST GUIDELINE") || containsString("MÉTODO(S) DE REFERÊNCIA(S):")) + ,(containsString("OECD") || containsString("EPA") || containsString("OPPTS")) + ) + then + Stream.of(RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|(?:.{5,40}(?:Number |Procedure |Guideline )))(4[\\d]{2})", 1, $section.getTextBlock()), + RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", 1, $section.getTextBlock()), + RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD) Guideline (4\\d{2})", 1, $section.getTextBlock())).flatMap(Collection::stream).findFirst() + .map(textRange -> $section.getTextBlock().subSequence(textRange).toString()) + .map(value -> FileAttribute.builder().label("OECD Number").value(value).build()) + .ifPresent(fileAttribute -> insert(fileAttribute)); + end + +rule "H.3.1: Study Type File Attribute in Headlines" + when + not FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","438","439","471","487")) + $page: Page($pageNumber:number, + getMainBodyTextBlock().getSearchText().contains("DATA REQUIREMENT") + || getMainBodyTextBlock().getSearchText().contains("TEST GUIDELINE") + || getMainBodyTextBlock().getSearchText().contains("MÉTODO(S) DE REFERÊNCIA(S):")) + $headline: Headline(onPage($pageNumber), containsString("OECD") || containsString("EPA")|| containsString("OPPTS")) + then + Stream.of(RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|(?:.{5,40}(?:Number |Procedure |Guideline )))(4[\\d]{2})", 1, $headline.getTextBlock()), + RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", 1, $headline.getTextBlock()), + RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD) Guideline (4\\d{2})", 1, $headline.getTextBlock())).flatMap(Collection::stream).findFirst() + .map(textRange -> $headline.getTextBlock().subSequence(textRange).toString()) + .map(value -> FileAttribute.builder().label("OECD Number").value(value).build()) + .ifPresent(fileAttribute -> insert(fileAttribute)); + end + +//------------------------------------ General documine rules ------------------------------------ + +// Rule unit: DOC.1 +rule "DOC.1.0: Guidelines" + when + $section: Section( + ( + containsString("DATA REQUIREMENT") + || containsString("TEST GUIDELINE") + || containsString("MÉTODO(S) DE REFERÊNCIA(S):") + ) + && ( + containsString("OECD") + || containsString("EPA") + || containsString("OPPTS") + ) + ) + then + entityCreationService.byRegex("OECD (No\\.? )?\\d{3}( \\(\\d{4}\\))?", "oecd_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline found", "n-a") + ); + entityCreationService.byRegex("OECD[\\s,]{1}(?:.{1,40}.(?>Procedure|Method).{1,20}\\d{3,4}(?>.{1,100}\\d{4}\\))?|\\[.{1,20}.Skin.{1,20}\\]|[\\d\\s,\\(\\)]{7,10}|[\\w\\.\\s]{1,15}[\\d]{3}\\s\\(\\d{4}\\)|.{0,20}[N|n]umber\\s\\d{3}.{0,1}|Test Guideline \\d{3})", "oecd_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline found", "n-a") + ); + entityCreationService.byRegex("EPA (OPPTS )?\\d{3}[. ]\\d{4}( \\(\\d{4}\\))?", "epa_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "EPA Guideline found", "n-a") + ); + entityCreationService.byRegex("EC (Directive )?(No\\.? )?\\d{3,4}\\/\\d{3,4}((,? B(\\.| )\\d{1,2}\\.?)? \\(\\d{4}\\))?", "ec_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "EC Guideline found", "n-a") + ); + entityCreationService.byRegex("Commission Regulation \\(EC\\) No \\d{3}\\/\\d{4}", "ec_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "EC Guideline found", "n-a") + ); + entityCreationService.byRegex("OECD Method 4\\d{2}.{5,40}\\(.{5,40}\\d{4}\\)", "oecd_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("OPPTS (Guideline Number )?\\d{3}\\.\\d{4}( \\(\\d{4}\\))?", "epa_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "EPA Guideline found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2})", "oecd_guideline_number", EntityType.ENTITY,1, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline no. found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2}),?\\s\\(?(\\d{4})\\)?", "oecd_guideline_year", EntityType.ENTITY,2, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)[\\w\\s,\\[\\]]{1,10}\\((\\d{4})\\)\\s(4[\\d]{2})", "oecd_guideline_year", EntityType.ENTITY,1, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_number", EntityType.ENTITY,1, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline number found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_year", EntityType.ENTITY,2, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD) Guideline (4\\d{2})", "oecd_guideline_number", EntityType.ENTITY,1, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline number found", "n-a") + ); + entityCreationService.byRegex("OECD Guideline 4\\d{2}", "oecd_guideline", EntityType.ENTITY, $section).forEach(entity -> + entity.apply("DOC.1.0", "OECD Guideline found", "n-a") + ); + end + +rule "DOC.1.2: Guidelines" + when + $section: Section( + ( + containsString("DATA REQUIREMENT") + || containsString("TEST GUIDELINE") + || containsString("MÉTODO(S) DE REFERÊNCIA(S):") + ) + && ( + containsString("OECD") + || containsString("EPA") + || containsString("OPPTS") + ) + && ( + hasEntitiesOfType("oecd_guideline") + || hasEntitiesOfType("epa_guideline") + || hasEntitiesOfType("ec_guideline") + ) + ) + then + $section.getEntitiesOfType(List.of("oecd_guideline","ec_guideline", "epa_guideline")).forEach(entity -> { + entity.apply("DOC.1.2", "OECD guideline found.", "n-a"); + }); + end + +rule "DOC.1.3: Guidelines" + when + $section: Section( + ( + hasEntitiesOfType("oecd_guideline") + || hasEntitiesOfType("epa_guideline") + || hasEntitiesOfType("ec_guideline") + ) + && !( + ( + containsString("DATA REQUIREMENT") + || containsString("TEST GUIDELINE") + || containsString("MÉTODO(S) DE REFERÊNCIA(S):") + ) + && ( + containsString("OECD") + || containsString("EPA") + || containsString("OPPTS") + ) + ) + ) + then + $section.getEntitiesOfType(List.of("oecd_guideline", "ec_guideline", "epa_guideline")).forEach(entity -> { + entity.remove("DOC.1.3", "removed"); + retract(entity); + }); + end + +rule "DOC.1.4: Guideline in Headlines" + when + $page: Page($pageNumber:number, + getMainBodyTextBlock().getSearchText().contains("DATA REQUIREMENT") + || getMainBodyTextBlock().getSearchText().contains("TEST GUIDELINE") + || getMainBodyTextBlock().getSearchText().contains("MÉTODO(S) DE REFERÊNCIA(S):")) + $headline: Headline(onPage($pageNumber), containsString("OECD") || containsString("EPA")|| containsString("OPPTS")) + then + entityCreationService.byRegex("OECD (No\\.? )?\\d{3}( \\(\\d{4}\\))?", "oecd_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline found", "n-a") + ); + entityCreationService.byRegex("OECD[\\s,]{1}(?:.{1,40}.(?>Procedure|Method).{1,20}\\d{3,4}(?>.{1,100}\\d{4}\\))?|\\[.{1,20}.Skin.{1,20}\\]|[\\d\\s,\\(\\)]{7,10}|[\\w\\.\\s]{1,15}[\\d]{3}\\s\\(\\d{4}\\)|.{0,20}[N|n]umber\\s\\d{3}.{0,1}|Test Guideline \\d{3})", "oecd_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline found", "n-a") + ); + entityCreationService.byRegex("EPA (OPPTS )?\\d{3}[. ]\\d{4}( \\(\\d{4}\\))?", "epa_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "EPA Guideline found", "n-a") + ); + entityCreationService.byRegex("EC (Directive )?(No\\.? )?\\d{3,4}\\/\\d{3,4}((,? B(\\.| )\\d{1,2}\\.?)? \\(\\d{4}\\))?", "ec_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "EC Guideline found", "n-a") + ); + entityCreationService.byRegex("Commission Regulation \\(EC\\) No \\d{3}\\/\\d{4}", "ec_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "EC Guideline found", "n-a") + ); + entityCreationService.byRegex("OECD Method 4\\d{2}.{5,40}\\(.{5,40}\\d{4}\\)", "oecd_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("OPPTS (Guideline Number )?\\d{3}\\.\\d{4}( \\(\\d{4}\\))?", "epa_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "EPA Guideline found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2})", "oecd_guideline_number", EntityType.ENTITY,1, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline no. found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2}),?\\s\\(?(\\d{4})\\)?", "oecd_guideline_year", EntityType.ENTITY,2, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD)[\\w\\s,\\[\\]]{1,10}\\((\\d{4})\\)\\s(4[\\d]{2})", "oecd_guideline_year", EntityType.ENTITY,1, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_number", EntityType.ENTITY,1, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline number found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_year", EntityType.ENTITY,2, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline year found", "n-a") + ); + entityCreationService.byRegex("(?<=OECD) Guideline (4\\d{2})", "oecd_guideline_number", EntityType.ENTITY,1, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline number found", "n-a") + ); + entityCreationService.byRegex("OECD Guideline 4\\d{2}", "oecd_guideline", EntityType.ENTITY, $headline).forEach(entity -> + entity.apply("DOC.1.4", "OECD Guideline found", "n-a") + ); + end + +// Rule unit: DOC.2 +rule "DOC.2.0: Report number" + when + $section: Section(containsString("LABORATORY PROJECT ID") , containsString("Report Number:")) + then + entityCreationService.lineAfterString("Report Number:", "report_number", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> { + entity.apply("DOC.2.0", "Report number found", "n-a"); + }); + end + + +// Rule unit: DOC.3 +rule "DOC.3.0: Experimental Starting Date" + when + $section: Section(containsString("Experimental I. Starting Date:") || containsString("Experimental II. Starting Date:") || containsStringIgnoreCase("experimental start date") || containsStringIgnoreCase("experimental starting date")) + then + entityCreationService.lineAfterStrings( + List.of("Experimental start date", + "Experimental start date:", + "Experimental Starting Date", + "Experimental Starting Date:", + "Experimental starting date", + "Experimental starting date:", + "Experimental Start Date", + "Experimental Start Date:", + "Experimental I. Starting Date:", + "Experimental II. Starting Date:"), "experimental_start_date", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.3.0", "Experimental start date found", "n-a"); + }); + end + + +// Rule unit: DOC.4 +rule "DOC.4.0: Experimental Completion Date" + when + $section: Section(containsStringIgnoreCase("experimental termination date") || containsStringIgnoreCase("experimental completion date")) + then + entityCreationService.lineAfterStrings( + List.of("Experimental termination date", + "Experimental termination date:", + "Experimental Completion Date", + "Experimental Completion Date:", + "Experimental completion date", + "Experimental completion date:", + "Experimental Termination Date", + "Experimental Termination Date:"), "experimental_end_date", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.4.0", "Experimental end date found", "n-a"); + }); + end + + +// Rule unit: DOC.5 +rule "DOC.5.0: Ignore species and strain in irrelevant study types" + salience 1 + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("406","428","438","439","471","474","487")) + $section: Section(hasEntitiesOfType("species") || hasEntitiesOfType("strain")) + then + $section.getEntitiesOfType(List.of("species", "strain")).forEach(entity -> { + entity.remove("DOC.5.0", "removed"); + retract(entity); + }); + end + +rule "DOC.5.1: Hide all skipped species and strains except in the relevant sections" + salience 1 + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436")) + $section: Section( + (hasEntitiesOfType("species") || hasEntitiesOfType("strain")) + && !( + anyHeadlineContainsStringIgnoreCase("test system") + || anyHeadlineContainsStringIgnoreCase("animals") + || anyHeadlineContainsStringIgnoreCase("specification") + ) + ) + then + $section.getEntitiesOfType(List.of("species", "strain")).forEach(entity -> { + entity.remove("DOC.5.1", "removed"); + retract(entity); + }); + end + +rule "DOC.5.2: Species" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436")) + $section: Section(hasEntitiesOfType("species")) + then + $section.getEntitiesOfType("species").forEach(entity -> { + entity.apply("DOC.5.2", "Species found.", "n-a"); + entity.setValue(entity.getValue().toLowerCase()); + }); + end + +rule "DOC.5.3: Strain" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436")) + $section: Section( + hasEntitiesOfType("species") + && hasEntitiesOfType("strain") + && ( + anyHeadlineContainsStringIgnoreCase("test system") + || anyHeadlineContainsStringIgnoreCase("animals") + || anyHeadlineContainsStringIgnoreCase("specification") + ) + ) + then + $section.getEntitiesOfType("strain").forEach(entity -> { + entity.apply("DOC.5.3", "Strain found.", "n-a"); + }); + end + + +// Rule unit: DOC.6 +rule "DOC.6.0: study title by document structure" + when + $table: Table(onPage(1), + (containsString("Final Report") || containsString("SPL")), + numberOfRows == 1, + numberOfCols == 1) + $tableCell: TableCell(row == 1, col == 1) from $table.streamTableCells().toList() + $paragraph: Paragraph(previousSibling.isPresent(), nextSibling.isPresent()) from $tableCell.streamChildren().toList() + then + entityCreationService.bySemanticNode($paragraph, "title", EntityType.ENTITY).ifPresent(entity -> { + entity.apply("DOC.6.0", "Study title found", "n-a"); + }); + end + +rule "DOC.6.1: study title" + when + $table: Table(onPage(1), (containsString("Final Report") || containsString("SPL"))) + then + entityCreationService.byRegexWithLineBreaksIgnoreCase("(?<=\\n)[\\w\\W]{1,300}(?=\\nFinal Report)", "title", EntityType.ENTITY, $table).findFirst().ifPresent(entity -> { + entity.apply("DOC.6.1", "Title found", "n-a"); + }); + end + +rule "DOC.6.2: study title" + when + not Table(onPage(1), (containsString("Final Report") || containsString("SPL"))) + $section: Section(onPage(1), (containsString("Final Report") || containsString("SPL"))) + then + entityCreationService.byRegexWithLineBreaksIgnoreCase("(?<=\\n)[\\w\\W]{1,300}(?=\\nFinal Report)", "title", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> { + entity.apply("DOC.6.2", "Title found", "n-a"); + }); + end + + +// Rule unit: DOC.7 +rule "DOC.7.0: Performing Laboratory (Name)" + when + $section: Section(containsString("PERFORMING LABORATORY:")) + then + entityCreationService.lineAfterString("PERFORMING LABORATORY:", "laboratory_name", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> { + entity.apply("DOC.7.0", "Performing Laboratory found", "n-a"); + }); + end + +rule "DOC.7.1: Performing Laboratory (Country)" + when + nerEntities: NerEntities(hasEntitiesOfType("COUNTRY")) + $section: Section(containsString("PERFORMING LABORATORY:")) + then + nerEntities.streamEntitiesOfType("COUNTRY") + .filter(nerEntity -> $section.getTextRange().contains(nerEntity.textRange())) + .map(nerEntity -> entityCreationService.byNerEntity(nerEntity, "laboratory_country", EntityType.ENTITY, $section)) + .forEach(entity -> { + entity.apply("DOC.7.1", "Performing Laboratory found", "n-a"); + }); + end + +rule "DOC.7.2: Performing Laboratory (Country & Name) from dict" + when + $section: Section(containsString("PERFORMING LABORATORY:") || (containsString("PERFORMING") && containsString("LABORATORY:"))) + $countryOrNameFromDictionary: TextEntity(type == "laboratory_country" || type == "laboratory_name", $type: type, isDictionaryEntry()) from $section.getEntities() + then + $countryOrNameFromDictionary.apply("DOC.7.2", "Performing " + $type + " dictionary entry found."); + end + +rule "DOC.7.3: Performing Laboratory (Country) from dict" + when + $section: Section( + (hasEntitiesOfType("laboratory_country") || hasEntitiesOfType("laboratory_name")) + && !(containsString("PERFORMING LABORATORY:") || (containsString("PERFORMING") && containsString("LABORATORY:"))) + ) + then + $section.getEntitiesOfType(List.of("laboratory_country", "laboratory_name")).forEach(entity -> { + entity.remove("DOC.7.3", "removed"); + retract(entity); + }); + end + + +// Rule unit: DOC.8 +rule "DOC.8.0: GLP Study" + when + $headline: Headline(containsString("GOOD LABORATORY PRACTICE COMPLIANCE") + || containsString("GOOD LABORATORY PRACTICE COMPLIANCE STATEMENT") + || (containsString("DECLARACAO DE CONFORMIDADE") && containsString("PRATICAS DE LABORATORIO")) + || containsString("GLP Certificate") + || containsString("GLP Certificates") + || containsString("GOOD LABORATORY PRACTICE (GLP) CERTIFICATE") + || containsString("Good Laboratory Practice Certificate") + || containsString("STATEMENT OF GLP COMPLIANCE AND AUTHENTICATION")) + then + entityCreationService.bySemanticNode($headline, "glp_study", EntityType.ENTITY).ifPresent(entity -> { + entity.apply("DOC.8.0", "GLP Study found", "n-a"); + }); + end + + +// Rule unit: DOC.9 +rule "DOC.9.0: Batch number from CoA" + when + $section: Section( + ( + anyHeadlineContainsString("Analytical Report") + || anyHeadlineContainsStringIgnoreCase("Certificate of Analysis") + || containsStringIgnoreCase("Certificate of Analysis") + ) + && ( + containsStringIgnoreCase("batch") + || containsStringIgnoreCase("bath") + || containsStringIgnoreCase("barch") + || containsStringIgnoreCase("bateb") + ) + && ( + containsStringIgnoreCase("identification") + || containsStringIgnoreCase("ldentitfication") + || containsStringIgnoreCase("wentification") + || containsStringIgnoreCase("mentification") + || containsStringIgnoreCase("kientification") + || containsStringIgnoreCase("reference number") + || containsStringIgnoreCase("test substance") + ) + ) + then + entityCreationService.lineAfterStrings(List.of("Batch Identification", + "(Batch Identification):", + "Bateb Identification", + "Batch Wentification", + "Batch Mentification", + "Batch Kientification", + "Barch Identification", + "Bath ldentitfication", + "Batch of test substance :"), "batch_number", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.9.0", "Batch number found in CoA", "n-a"); + }); + end + +rule "DOC.9.1: Batch number" + when + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("Test Substance") + || anyHeadlineContainsStringIgnoreCase("Test and Control Substances") + || anyHeadlineContainsStringIgnoreCase("Test Item") + ) + && !( + anyHeadlineContainsString("component") + || anyHeadlineContainsString("reference") + || anyHeadlineContainsString("blank") + ) + && containsStringIgnoreCase("batch") + ) + then + Stream.of(entityCreationService.byRegex("Batch ID ([A-Z\\d\\-]{7,14})", "batch_number", EntityType.ENTITY, 1, $section), + entityCreationService.lineAfterStrings(List.of("Batch Identification", + "Batch number:", + "Batch reference number:", + "Batch:", + "Batch/Lot number:", + "Batch (Lot) Number:", + "Batch Number:", + "Batch Nº:", + "Batch no:" + ), "batch_number", EntityType.ENTITY, $section)).flatMap(a -> a) + .forEach(entity -> { + entity.apply("DOC.9.1", "Batch number found", "n-a"); + }); + end + +rule "DOC.9.2: Batch number" + when + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("Test Substance") + || anyHeadlineContainsStringIgnoreCase("Test and Control Substances") + || anyHeadlineContainsStringIgnoreCase("Test Item") + ) + && !( + anyHeadlineContainsString("component") + || anyHeadlineContainsString("reference") + || anyHeadlineContainsString("blank") + ) + && containsStringIgnoreCase("batch") + ) + $batchNumber: String() from List.of("Batch Identification", + "Batch number:", + "Batch reference number:", + "Batch:", + "Batch/Lot number:", + "Batch (Lot) Number:", + "Batch Number:", + "Batch Nº:", + "Batch no:") + $table: Table(containsStringIgnoreCase($batchNumber)) from $section.streamAllSubNodesOfType(NodeType.TABLE).toList() + then + entityCreationService.lineAfterStringAcrossColumnsIgnoreCase($batchNumber, "batch_number", EntityType.ENTITY, $table).forEach(entity -> { + entity.apply("DOC.9.2", "Batch number found", "n-a"); + }); + end + + +// Rule unit: DOC.10 +rule "DOC.10.0: Conclusions - LD50, LC50, Confidence" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","425","436")) + $section: Section( + (getHeadline().containsStringIgnoreCase("Conclusion") || anyHeadlineContainsStringIgnoreCase("Lethality")) + && (containsString("LD") || containsString("LC") || containsString("50") || containsString("LD50") || containsString("lethal concentration") || containsString("lethal dose")) + && ( + containsString("greater than") + || containsString("higher than") + || containsString("above") + || containsString("in excess") + || containsString("exceeds") + || containsString("was found to be") + || containsString("was calculated to be") + || containsString("estimated to be") + ) + ) + then + entityCreationService.byRegexIgnoreCase("(L[D|C]\\s?50|lethal concentration|lethal dose).{1,200}(greater than|considered to be above|in excess of|exceeds|higher than)", "ld50_greater", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.10.0", "LD50 greater than found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("\\b(?:(?:greater|higher) than|considered to be above|(?:was|is) (?:found|estimated) to be|was calculated to be|in excess of|exceeds|equal to)\\s?([\\d\\.]{1,6})\\s?mg\\/(?:kg|L)", "ld50_value", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.10.0", "LD50 value found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("confidence interval (?:is )?([\\d\\.]{2,6}).{0,20} to (?:greater than )?([\\d\\.]{2,6})", "confidence_minimal", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.10.0", "Minimal Confidence found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("confidence interval (?:is )?([\\d\\.]{2,6}).{0,20} to (?:greater than )?([\\d\\.]{2,6})", "confidence_maximal", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.10.0", "Maximal Confidence found", "n-a"); + }); + end + + +// Rule unit: DOC.11 +rule "DOC.11.0: Guideline Deviation" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","471")) + $section: Section( + (getHeadline().containsStringIgnoreCase("General Information") || containsString("GENERAL INFORMATION")) + && (containsStringIgnoreCase("from the") || containsStringIgnoreCase("to the")) + ) + then + entityCreationService.betweenRegexes("(?:Deviations? from the [G|g]uidelines?)(?: and| or)?( the)?(?: Study Plan)?", "(?:(?:Deviations? from the Study Plan)|(?:Performing laboratory test)|(?:Other)|(?:Retention of [S|s]amples)|(?:Amendments? to Final Protocol))", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Deviation from Guidelines found", "n-a"); + }); + entityCreationService.betweenRegexes("(?:Deviations? (?:from|to)(?: the)? [S|s]tudy [P|p]lan)", "(?:Regulatory Guidelines)|(?:Other)|(?:Distribution of the report)|(?:Performing laboratory test)|(?:Distribution of the report)|(?:Retention of [S|s]amples)", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Deviation from Study Plan found", "n-a"); + }); + entityCreationService.betweenStrings("Deviations from the study plan", "Regulatory Guidelines", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Deviation from the study plan found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("(?>Study plan adherence)(.{1,20}deviations.{1,20} to the study plan.{0,50}\\.)\\s", "guideline_deviation", EntityType.ENTITY, 1, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Guideline deviation found in text.", "n-a"); + }); + entityCreationService.betweenStringsIncludeEnd("Deviations from the study plan", "validity of the study.", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.0", "Deviation from the study plan found", "n-a"); + }); + end + +rule "DOC.11.1: Guideline Deviation in text" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","471")) + $section: Section( + getHeadline().containsStringIgnoreCase("Introduction") + && containsStringIgnoreCase("deviations from the protocol") + ) + then + entityCreationService.byRegex("There were no deviations from the protocol.{1,100}\\.\\s", "guideline_deviation", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.11.1", "Guideline deviation found in text.", "n-a"); + }); + end + + +// Rule unit: DOC.12 +rule "DOC.12.0: Clinical Signs" + when + FileAttribute(label == "OECD Number", value == "425") + $headline: Headline(containsAnyStringIgnoreCase("Clinical Signs", "Macroscopic Findings") && !containsString("TABLE") && !getHeadline().containsStringIgnoreCase("3 - MACROSCOPIC FINDINGS")) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "clinical_signs", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.12.0", "Clinical Signs found", "n-a")); + end + + +// Rule unit: DOC.13 +rule "DOC.13.0: Dosages" + when + FileAttribute(label == "OECD Number", value == "425") + $section: Section( + (anyHeadlineContainsStringIgnoreCase("Dosages") || anyHeadlineContainsStringIgnoreCase("Study Design")) + && !getHeadline().containsString("TABLE") + ) + then + entityCreationService.betweenStringsIncludeStartAndEnd("The animals were treated", ".", "dosages", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.13.0", "Dosage found", "n-a"); + }); + entityCreationService.betweenStringsIncludeStartAndEnd("Animals were treated", ".", "dosages", EntityType.ENTITY, $section).forEach(entity -> { + entity.apply("DOC.13.0", "Dosage found", "n-a"); + }); + entityCreationService.byRegexWithLineBreaks("(?:\\.[\\s|\\n]|^.{5,20}\\n)([^\\.]{1,200}(?:animal|given|received)[^\\.]{1,200}dose\\s(?:levels?\\s)?(?:of|at)[^\\.]{1,200})(?:\\.[\\s|\\n|$])", "dosages", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.13.0", "Dosage found", "n-a"); + }); + end + + +// Rule unit: DOC.14 +rule "DOC.14.0: Mortality" + when + $headline: Headline(containsString("Mortality") && !containsString("TABLE")) + FileAttribute(label == "OECD Number", value == "425") + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "mortality", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.14.0", "Mortality found", "n-a")); + end + + +// Rule unit: DOC.15 +rule "DOC.15.0: Study Conclusion" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","471")) + $section: Section( + getHeadline().containsStringIgnoreCase("Conclusion") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "study_conclusion", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.15.0", "Study Conclusion found", "n-a")); + end + + +// Rule unit: DOC.16 +rule "DOC.16.0: Weight Behavior Changes" + when + FileAttribute(label == "OECD Number", value == "402") + $section: Section( + getHeadline().containsString("Results") + && ( + containsString("body weight") + || containsString("body weights") + || containsString("bodyweight") + || containsString("bodyweights") + ) + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "weight_behavior_changes", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.16.0", "Weight behavior changes found", "n-a")); + end + + +// Rule unit: DOC.17 +rule "DOC.17.0: Necropsy findings" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","436")) + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("Necropsy") + || getHeadline().containsStringIgnoreCase("Macroscopic Findings") + || getHeadline().containsStringIgnoreCase("Macroscopic examination") + ) + && !getHeadline().containsStringIgnoreCase("Table") + && !getHeadline().containsStringIgnoreCase("Appendix") + && !getHeadline().containsStringIgnoreCase("3 - MACROSCOPIC FINDINGS") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "necropsy_findings", EntityType.ENTITY) + .forEach( entity -> entity.apply("DOC.17.0", "Necropsy section found", "n-a")); + end + + +// Rule unit: DOC.18 +rule "DOC.18.0: Clinical observations" + when + FileAttribute(label == "OECD Number", value == "403") + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("Clinical Observations") + || anyHeadlineContainsStringIgnoreCase("Clinical observations") + || anyHeadlineContainsStringIgnoreCase("In-life Observations") + || anyHeadlineContainsStringIgnoreCase("Postmortem Observations") + ) + && !anyHeadlineContainsStringIgnoreCase("Appendix") + && !anyHeadlineContainsStringIgnoreCase("Table") + && !anyHeadlineContainsStringIgnoreCase("Mortality") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "clinical_observations", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.18.0", "Clinical observations section found", "n-a")); + end + + +// Rule unit: DOC.19 +rule "DOC.19.0: Bodyweight changes" + when + FileAttribute(label == "OECD Number", value == "403") + $headline: Headline(containsAnyStringIgnoreCase("Bodyweight", "Bodyweights", "Body Weights", "Body Weight"), !containsAnyStringIgnoreCase("Appendix", "TABLE")) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "bodyweight_changes", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.19.0", "Bodyweight section found", "n-a")); + end + + +// Rule unit: DOC.20 +rule "DOC.20.0: Study Design" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","404","405","406","428","429","438","439","474","487")) + $section: Section( + anyHeadlineContainsStringIgnoreCase("study design") + && !anyHeadlineContainsString("Preliminary screening test") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "study_design", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.20.0", "Study design section found", "n-a")); + end + +rule "DOC.20.1: Study Design" + when + Headline(containsStringIgnoreCase("Study Design"), $sectionIdentifier: getSectionIdentifier()) + $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifier)) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "study_design", EntityType.ENTITY) + .forEach(entity -> { + entity.apply("DOC.20.1", "Study design section found", "n-a"); + }); + end + + +// Rule unit: DOC.21 +rule "DOC.21.0: Results and Conclusion (406, 428, 438, 439, 474 & 487)" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("406","428","438","439","474","487")) + $parentHeadline: Headline( + containsAnyString("Results", "Conclusion"), + !containsAnyString("POSITIVE CONTROL", "Positive Control", "Evaluation", "Micronucleus", "TABLE", "DISCUSSION", "CONCLUSIONS", "Interpretation","Viability", "analysis"), + $sectionIdentifier: getSectionIdentifier() + ) + not Headline(getSectionIdentifier().isChildOf($sectionIdentifier)) + then + entityCreationService.bySemanticNodeParagraphsOnly($parentHeadline.getParent(), "results_and_conclusion", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.21.0", "Results and Conclusion found", "n-a")); + end + +rule "DOC.21.1: Results and Conclusion (406, 428, 438, 439, 474 & 487)" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("406","428","438","439","474","487")) + Headline( + containsAnyString("Results", "Conclusion"), + !containsAnyString("POSITIVE CONTROL", "Positive Control", "Evaluation", "Micronucleus", "TABLE", "DISCUSSION", "CONCLUSIONS", "Interpretation","Viability", "analysis"), + $sectionIdentifier: getSectionIdentifier() + ) + $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifier)) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "results_and_conclusion", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.21.1", "Results and Conclusion found", "n-a")); + end + + +// Rule unit: DOC.22 +rule "DOC.22.0: Detailing (404 & 405)" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("404","405")) + $section: Section( + anyHeadlineContainsStringIgnoreCase("Results") + && !getHeadline().containsStringIgnoreCase("Evaluation") + && !getHeadline().containsStringIgnoreCase("study") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "detailing", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.22.0", "Detailing found", "n-a")); + end + + +// Rule unit: DOC.23 +rule "DOC.23.0: Preliminary Test Results (429)" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + ((anyHeadlineContainsString("Preliminary Screening Test") && containsString("Clinical observations")) + || anyHeadlineContainsString("Pre-Experiment")) + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "preliminary_test_results", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.23.0", "Preliminary Test Results found", "n-a")); + end + + +// Rule unit: DOC.24 +rule "DOC.24.0: Test Results (429)" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section((getHeadline().containsString("RESULTS AND DISCUSSION") || getHeadline().containsString("Estimation of the proliferative response of lymph node cells") || getHeadline().containsString("Results in the Main Experiment"))) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "test_results", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.24.0", "Test Results found", "n-a")); + end + +rule "DOC.24.1: Test Results (429)" + when + Headline(containsStringIgnoreCase("RESULTS AND DISCUSSION"), $sectionIdentifierResultsAndDiscussion: getSectionIdentifier()) + $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifierResultsAndDiscussion)) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "test_results", EntityType.ENTITY) + .forEach(entity -> { + entity.apply("DOC.24.1", "Test Results found", "n-a"); + }); + end + + +// Rule unit: DOC.25 +rule "DOC.25.0: Approach used (429)" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + hasEntitiesOfType("species") + && (containsStringIgnoreCase("animals per") || containsStringIgnoreCase("animals /")) + ) + then + entityCreationService.byRegexIgnoreCase("\\banimals (?:per|\\/) .{0,15}(group)\\b", "approach_used", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.25.0", "Study animal approach found.", "n-a"); + }); + end + + +// Rule unit: DOC.26 +rule "DOC.26.0: Sex" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("405","429")) + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("animal") + || anyHeadlineContainsStringIgnoreCase("test system") + ) + && !getHeadline().containsStringIgnoreCase("selection") + && ( + containsStringIgnoreCase("sex:") + || containsStringIgnoreCase("male") + || containsStringIgnoreCase("female") + ) + ) + then + entityCreationService.byRegexIgnoreCase("([S|s]ex:)?[\\w\\s]{0,10}\\b(males?|females?)\\b", "sex", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.26.0", "Test animal sex found", "n-a"); + }); + end + + +// Rule unit: DOC.27 +rule "DOC.27.0: Animal Number 405" + when + FileAttribute(label == "OECD Number", value == "405") + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("animal") + || anyHeadlineContainsStringIgnoreCase("test system") + || anyHeadlineContainsStringIgnoreCase("reaction") + ) + && !getHeadline().containsString("selection") + && ( + containsStringIgnoreCase("number of animals") + || containsStringIgnoreCase("no.") + ) + ) + then + entityCreationService.byRegexIgnoreCase("(Number of animals:)[\\w\\s]{0,10}\\b([\\d]{1,3})\\b", "number_of_animals", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.27.0", "Number of animals found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("(?:.{1,10} No\\. )([\\d\\w\\-]{3,8})", "animal_numbers", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.27.0", "Number of animals found", "n-a"); + }); + end + + +// Rule unit: DOC.28 +rule "DOC.28.0: Animal Number 429" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + ( + anyHeadlineContainsStringIgnoreCase("animal") + || anyHeadlineContainsStringIgnoreCase("test system") + ) + && !getHeadline().containsString("selection") + && containsStringIgnoreCase("number of animals") + && (containsStringIgnoreCase("per") || containsString("/")) + && containsStringIgnoreCase("group") + ) + then + entityCreationService.byRegexIgnoreCase("(Number of animals per group:)[\\w\\s]{0,10}\\b([\\d]{1,3})\\b", "number_of_animals", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.28.0", "Number of animals in group found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("(Number of animals per group:).{0,60}\\b([\\d]{1,3})\\sper group\\b", "number_of_animals", EntityType.ENTITY,2, $section).forEach(entity -> { + entity.apply("DOC.28.0", "Number of animals in group found", "n-a"); + }); + entityCreationService.byRegexIgnoreCase("([\\d]{1,3})[\\w\\s\\/]{0,20}(?:treatment )?group\\b", "number_of_animals", EntityType.ENTITY,1 , $section).forEach(entity -> { + entity.apply("DOC.28.0", "Number of animals in group found", "n-a"); + }); + end + +rule "DOC.28.1: No. Of animals - Fallback to appendix tables listing all individual animals for 429" + when + $keyword: String() from List.of("Animal Number", "Animal No.", "Animal number") + $table: Table(containsString($keyword) && getHeadline().containsString("TABLE") && getHeadline().containsString("Individual")) + FileAttribute(label == "OECD Number", value == "429") + then + $table.streamTableCellsWithHeader($keyword) + .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "animal_numbers", EntityType.ENTITY)) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(entity -> { + entity.apply("DOC.28.1", "Animal number found.", "n-a"); + }); + end + + +// Rule unit: DOC.29 +rule "DOC.29.0: 4h Exposure" + when + FileAttribute(label == "OECD Number", valueEqualsAnyOf("403","436")) + $section: Section( + (containsStringIgnoreCase("4 hours") || containsStringIgnoreCase("four hours")) + ) + then + entityCreationService.byRegexIgnoreCase("((?<=\\.\\s\\b).{1,100}(4|four) hours.*?\\.) ", "4h_exposure", EntityType.ENTITY,1, $section).forEach(entity -> { + entity.apply("DOC.29.0", "4h exposure sentence found", "n-a"); + }); + end + + +// Rule unit: DOC.30 +rule "DOC.30.0: Dilution of the test substance" + when + FileAttribute(label == "OECD Number", value == "404") + $section: Section( + getHeadline().containsString("Formulation") + && containsString("dilution") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "dilution", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.30.0", "Dilution found.", "n-a")); + end + + +// Rule unit: DOC.31 +rule "DOC.31.0: Positive Control" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + getHeadline().containsStringIgnoreCase("Positive Control") + && !(getHeadline().containsStringIgnoreCase("Appendix") || getHeadline().containsStringIgnoreCase("Table")) + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "positive_control", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.31.0", "Positive control found.", "n-a")); + end + + +// Rule unit: DOC.32 +rule "DOC.32.0: Mortality Statement" + when + FileAttribute(label == "OECD Number", value == "402") + $headline: Headline(containsStringIgnoreCase("Mortality") && !containsString("TABLE")) + then + entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), "mortality_statement", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.32.0", "Mortality Statement found", "n-a")); + end + + +// Rule unit: DOC.33 +rule "DOC.33.0: Dose Mortality" + when + FileAttribute(label == "OECD Number", value == "425") + $table: Table( + (hasHeader("Mortality") || hasHeader("Long Term Results") || hasHeader("LongTerm Outcome") || hasHeader("Long Term Outcome") || hasHeader("Comments") || hasHeader("Viability / Mortality") || hasHeader("Viability/Mortality")) + && + (hasHeader("Dose [mg/kg bodyweight]") || hasHeader("Dose [mg/kg body weight]") ||hasHeader("Dose (mg/kg)") || hasHeader("Dose levei (mg/kg)") || hasHeader("Dose Level (mg/kg)") || hasHeader("Dose level (mg/kg)") || hasHeader("Dosage [mg/kg body weight]")) + ) + then + Stream.of($table.streamTableCellsWithHeader("Mortality"), + $table.streamTableCellsWithHeader("Comments"), + $table.streamTableCellsWithHeader("Long Term Results"), + $table.streamTableCellsWithHeader("Long Term Outcome"), + $table.streamTableCellsWithHeader("LongTerm Outcome"), + $table.streamTableCellsWithHeader("Viability / Mortality"), + $table.streamTableCellsWithHeader("Viability/Mortality") + ).flatMap(a -> a) + .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "dose_mortality", EntityType.ENTITY)) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(entity -> { + entity.apply("DOC.33.0", "Dose Mortality Data found.", "n-a"); + }); + + Stream.of($table.streamTableCellsWithHeader("Dose [mg/kg bodyweight]"), + $table.streamTableCellsWithHeader("Dose [mg/kg body weight]"), + $table.streamTableCellsWithHeader("Dose levei (mg/kg)"), + $table.streamTableCellsWithHeader("Dose Level (mg/kg)"), + $table.streamTableCellsWithHeader("Dose level (mg/kg)"), + $table.streamTableCellsWithHeader("Dose (mg/kg)"), + $table.streamTableCellsWithHeader("Dosage [mg/kg body weight]") + ).flatMap(a -> a) + .map(tableCell -> entityCreationService.bySemanticNode(tableCell, "dose_mortality_dose", EntityType.ENTITY)) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(entity -> { + entity.apply("DOC.33.0", "Dose Mortality Data found.", "n-a"); + }); + end + + +// Rule unit: DOC.34 +rule "DOC.34.0: Results (Main Study)" + when + FileAttribute(label == "OECD Number", value == "429") + $section: Section( + getHeadline().containsString("Results") + && getHeadline().getTextRange().length() < 20 + && !(getHeadline().containsString("Appendix") || getHeadline().containsString("Table")) + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "results_(main_study)", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.34.0", "Results for main study found.", "n-a")); + end + + +// Rule unit: DOC.35 +rule "DOC.35.0: Doses (mg/kg bodyweight)" + when + FileAttribute(label == "OECD Number", value == "402") + $section: Section( + anyHeadlineContainsStringIgnoreCase("study design") + ) + then + entityCreationService.bySemanticNodeParagraphsOnly($section, "doses_(mg_kg_bw)", EntityType.ENTITY) + .forEach(entity -> entity.apply("DOC.35.0", "Doses per bodyweight information found", "n-a")); + end + + +//------------------------------------ Manual redaction rules ------------------------------------ + +// Rule unit: MAN.0 +rule "MAN.0.0: Apply manual resize redaction" + salience 128 + when + $resizeRedaction: ManualResizeRedaction($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate)) + $entityToBeResized: TextEntity(matchesAnnotationId($id)) + then + manualChangesApplicationService.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, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate)) + $imageToBeResized: Image(id == $id) + then + manualChangesApplicationService.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($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToBeRemoved: TextEntity(matchesAnnotationId($id)) + then + $entityToBeRemoved.getManualOverwrite().addChange($idRemoval); + 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($id: annotationId, status == AnnotationStatus.APPROVED) + $imageEntityToBeRemoved: Image($id == id) + then + $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval); + update($imageEntityToBeRemoved); + retract($idRemoval); + update($imageEntityToBeRemoved.getParent()); + end + + +// Rule unit: MAN.2 +rule "MAN.2.0: Apply force redaction" + salience 128 + when + $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToForce: TextEntity(matchesAnnotationId($id)) + then + $entityToForce.getManualOverwrite().addChange($force); + update($entityToForce); + $entityToForce.getIntersectingNodes().forEach(node -> update(node)); + retract($force); + end + +rule "MAN.2.1: Apply force redaction to images" + salience 128 + when + $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED) + $imageToForce: Image(id == $id) + then + $imageToForce.getManualOverwrite().addChange($force); + update($imageToForce); + update($imageToForce.getParent()); + retract($force); + end + + +// Rule unit: MAN.3 +rule "MAN.3.0: Apply entity recategorization" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type != $type) + then + $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node)); + manualChangesApplicationService.recategorize($entityToBeRecategorized, $recategorization); + retract($recategorization); + // Entity is copied and inserted, so the old entity needs to be retracted to avoid duplication. + retract($entityToBeRecategorized); + end + +rule "MAN.3.1: Apply entity recategorization of same type" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type == $type) + then + $entityToBeRecategorized.getManualOverwrite().addChange($recategorization); + retract($recategorization); + end + +rule "MAN.3.2: Apply image recategorization" + salience 128 + when + $recategorization: ManualRecategorization($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate) + not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate)) + $imageToBeRecategorized: Image($id == id) + then + manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization); + update($imageToBeRecategorized); + update($imageToBeRecategorized.getParent()); + retract($recategorization); + end + + +// Rule unit: MAN.4 +rule "MAN.4.0: Apply legal basis change" + salience 128 + when + $legalbasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED) + $imageToBeRecategorized: Image($id == id) + then + $imageToBeRecategorized.getManualOverwrite().addChange($legalbasisChange); + end + +rule "MAN.4.1: Apply legal basis change" + salience 128 + when + $legalBasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED) + $entityToBeChanged: TextEntity(matchesAnnotationId($id)) + then + $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange); + end + + +//------------------------------------ Entity merging rules ------------------------------------ + +// Rule unit: X.0 +rule "X.0.0: Remove Entity contained by Entity of same type" + salience 65 + when + $larger: TextEntity($type: type, $entityType: entityType, active()) + $contained: TextEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !hasManualChanges(), active()) + then + $contained.remove("X.0.0", "remove Entity contained by Entity of same type"); + retract($contained); + end + + +// Rule unit: X.2 +rule "X.2.0: Remove Entity of type ENTITY when contained by FALSE_POSITIVE" + salience 64 + when + $falsePositive: TextEntity($type: type, entityType == EntityType.FALSE_POSITIVE, active()) + $entity: TextEntity(containedBy($falsePositive), type == $type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), !hasManualChanges(), active()) + then + $entity.getIntersectingNodes().forEach(node -> update(node)); + $entity.remove("X.2.0", "remove Entity of type ENTITY when contained by FALSE_POSITIVE"); + retract($entity) + end + + +// Rule unit: X.3 +rule "X.3.0: Remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION" + salience 64 + when + $falseRecommendation: TextEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION, active()) + $recommendation: TextEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $recommendation.remove("X.3.0", "remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION"); + retract($recommendation); + end + + +// Rule unit: X.4 +rule "X.4.0: Remove Entity of type RECOMMENDATION when intersected by ENTITY with same type" + salience 256 + when + $entity: TextEntity($type: type, (entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $recommendation: TextEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $entity.addEngines($recommendation.getEngines()); + $recommendation.remove("X.4.0", "remove Entity of type RECOMMENDATION when intersected by ENTITY with same type"); + retract($recommendation); + end + + +// Rule unit: X.5 +rule "X.5.0: Remove Entity of type RECOMMENDATION when contained by ENTITY" + salience 256 + when + $entity: TextEntity((entityType == EntityType.ENTITY || entityType == EntityType.HINT), active()) + $recommendation: TextEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active()) + then + $recommendation.remove("X.5.0", "remove Entity of type RECOMMENDATION when contained by ENTITY"); + retract($recommendation); + end + + +// Rule unit: X.7 +rule "X.7.0: Remove all images" + salience 512 + when + $image: Image(imageType != ImageType.OCR, !hasManualChanges()) + then + $image.remove("X.7.0", "remove all images"); + retract($image); + 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 matchedRules = $dictionaryModel.getLocalEntriesWithMatchedRules().get(entity.getValue()); + entity.addMatchedRules(matchedRules); + }); + end diff --git a/redaction-service-v1/rules-management/src/test/resources/demo/table_demo.drl b/redaction-service-v1/rules-management/src/test/resources/demo/table_demo.drl new file mode 100644 index 00000000..02dd3b78 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/demo/table_demo.drl @@ -0,0 +1,134 @@ +package drools + +import com.iqser.red.service.redaction.v1.server.redaction.model.Section + +global Section section + + +// --------------------------------------- Your rules below this line-------------------------------------------------- + +rule "0a: Study Type File Attribute" + when + Section( + !fileAttributeContainsAnyOf("OECD Number","402","403","404","405","425","429","436","438","439","471","487") + && ( + text.contains("DATA REQUIREMENT") + || text.contains("TEST GUIDELINE") + || text.contains("MÉTODO(S) DE REFERÊNCIA(S):") + ) + && ( + text.contains("OECD") + || text.contains("EPA") + || text.contains("OPPTS") + ) + ) + then + section.addFileAttribute("OECD Number", "(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|(?:.{5,40}(?:Number |Procedure |Guideline )))(4[\\d]{2})", true, 1); + section.addFileAttribute("OECD Number", "(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", true, 1); + end + + +rule "1: Guidelines" + when + Section( + ( + text.contains("DATA REQUIREMENT") + || text.contains("TEST GUIDELINE") + || text.contains("MÉTODO(S) DE REFERÊNCIA(S):") + ) + && ( + text.contains("OECD") + || text.contains("EPA") + || text.contains("OPPTS") + ) + ) + then + section.redactByRegEx("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2})", true, 1, "oecd_guideline_number", 1, "OECD Guideline no. found", "n-a"); + section.redactByRegEx("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2}),?\\s\\(?(\\d{4})\\)?", true, 2, "oecd_guideline_year", 1, "OECD Guideline year found", "n-a"); + section.redactByRegEx("(?<=OECD)[\\w\\s,\\[\\]]{1,10}\\((\\d{4})\\)\\s(4[\\d]{2})", true, 1, "oecd_guideline_year", 1, "OECD Guideline year found", "n-a"); + section.redactByRegEx("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", true, 1, "oecd_guideline_number", 1, "OECD Guideline number found", "n-a"); + section.redactByRegEx("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", true, 2, "oecd_guideline_year", 1, "OECD Guideline year found", "n-a"); + end + + +rule "2: Full Table extraction (Guideline Deviation)" + when + Section( + fileAttributeByLabelEqualsIgnoreCase("OECD Number","425") + && headlineContainsWord("Full Table") + && hasTableHeader("Sex") + ) + then + section.redactSectionTextWithoutHeadLine("guideline_deviation",2,"Full table extraction into guideline deviation","n-a"); + end + +rule "3: Individual row extraction (Clinical Signs)" + when + Section( + fileAttributeByLabelEqualsIgnoreCase("OECD Number","425") + && headlineContainsWord("Individual Rows") + && hasTableHeader("Animal No.") + && (rowEquals("Animal No.","120-2") || rowEquals("Animal No.","120-5")) + ) + then + section.redactSectionTextWithoutHeadLine("clinical_signs",3,"Individual row based on animal number","n-a"); + end + +rule "4: Individual column extraction (Strain)" + when + Section( + fileAttributeByLabelEqualsIgnoreCase("OECD Number","425") + && headlineContainsWord("Individual Column") + && hasTableHeader("Sex") + ) + then + section.redactCell("Sex",4,"dosages",false,"Individual column based on column header","n-a"); + end + +rule "5: Dose Mortality" + when + Section( + fileAttributeByLabelEqualsIgnoreCase("OECD Number","425") + && headlineContainsWord("Combined Columns") + && hasTableHeader("Mortality") + && hasTableHeader("Dosage (mg/kg bw)") + ) + then + section.redactCell("Mortality",5,"dose_mortality",false,"Dose Mortality found.","n-a"); + section.redactCell("Dosage (mg/kg bw)",5,"dose_mortality_dose",false,"Dose Mortality dose found.","n-a"); + end + +rule "6: targeted cell extraction (Experimental Start date)" + when + Section( + fileAttributeByLabelEqualsIgnoreCase("OECD Number","425") + && headlineContainsWord("Value Extraction") + && hasTableHeader("Mortality") + && (rowEquals("Sex","male") || rowEquals("Sex","Male")) + && rowEquals("Mortality","Survived") + ) + then + section.redactCell("Treatment start",6,"experimental_start_date",false,"Female deaths date to experimental start date","n-a"); + end + +rule "7: targeted cell extraction (Experimental Stop date)" + when + Section( + isInTable() + && (searchText.contains("female") || searchText.contains("Female")) + && searchText.contains("Survived") + ) + then + section.redactCellBelow(7,"experimental_end_date",true,false,"Female deaths date to experimental start date","n-a", "Sex", "Group 2"); + end + +rule "8: Indicator (Species)" + when + Section( + fileAttributeByLabelEqualsIgnoreCase("OECD Number","425") + && headlineContainsWord("Entity-Based") + && matchesType("vertebrates") + ) + then + section.redactCell("Title",8,"study_design",false,"Vertebrate study found","n-a"); + end \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dev/Basf-Demo/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dev/Basf-Demo/rules.txt new file mode 100644 index 00000000..d0051b4e --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dev/Basf-Demo/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport static java.lang.String.format;\nimport static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.anyMatch;\nimport static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.exactMatch;\n\nimport java.util.List;\nimport java.util.LinkedList;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.Collection;\nimport java.util.stream.Stream;\nimport java.util.Optional;\n\nimport com.iqser.red.service.redaction.v1.server.model.document.*;\nimport com.iqser.red.service.redaction.v1.server.model.document.TextRange;\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.*;\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.*;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Paragraph;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.SectionIdentifier;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Footer;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Header;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.*;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.AtomicTextBlock;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.ConcatenatedTextBlock;\nimport com.iqser.red.service.redaction.v1.server.model.NerEntities;\nimport com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;\nimport com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryModel;\nimport com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;\nimport com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService;\nimport com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility;\n\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRecategorization;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;\n\nglobal Document document\nglobal EntityCreationService entityCreationService\nglobal ManualChangesApplicationService manualChangesApplicationService\nglobal Dictionary dictionary\n\n//------------------------------------ queries ------------------------------------\n\nquery \"getFileAttributes\"\n $fileAttribute: FileAttribute()\n end\n\n//---------------------------------------------------------------------------\n\n\n\nrule \"H.0.0: retract table of contents page\"\n when\n $page: Page(getMainBodyTextBlock().getSearchText().contains(\"........\") || (getMainBodyTextBlock().getSearchText().contains(\"APPENDICES\") && getMainBodyTextBlock().getSearchText().contains(\"TABLES\")))\n $node: SemanticNode(onPage($page.getNumber()), !onPage($page.getNumber() -1), getType() != NodeType.IMAGE)\n then\n retract($node);\n end\n\n\nrule \"H.0.1: Ignore Table of Contents\"\n salience 10\n when\n $tocHeadline: Headline(containsString(\"CONTENTS\"))\n\n then\n $tocHeadline.getParent().getPages()\n .forEach(page -> page.getMainBody().stream()\n .filter(node -> !node.getType().equals(NodeType.IMAGE))\n .filter(node -> node.getPages().stream().noneMatch(nodePage -> nodePage.getNumber() < page.getNumber()))\n .forEach(node -> retract(node))\n );\n end\n \n\n\n/*\nrule \"H.0.0: Show headlines\"\n when\n $headline: Headline()\n then\n entityCreationService.bySemanticNode($headline, \"headline\", EntityType.RECOMMENDATION);\n end\n*/\n\nrule \"DOC.0.0: Study Type File Attribute\"\n when\n not FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"404\",\"405\",\"408\",\"414\",\"425\",\"429\",\"436\",\"438\",\"439\",\"471\",\"487\"))\n $section: Section(\n onPage(1)\n ,(containsString(\"OECD\") || containsString(\"EPA\") || containsString(\"OPPTS\"))\n )\n then\n Stream.of(RedactionSearchUtility.findTextRangesByRegexIgnoreCase(\"(?<=OECD)(?:[\\\\w\\\\s,\\\\[\\\\]\\\\(\\\\)\\\\.]{1,10}|(?:.{5,40}(?:Number |Procedure |Guideline )))(4[\\\\d]{2})\", 1, $section.getTextBlock()),\n RedactionSearchUtility.findTextRangesByRegexIgnoreCase(\"(?<=OECD).{5,40}Method (4[\\\\d]{2}).{1,65}(\\\\d{4})\\\\)\", 1, $section.getTextBlock()),\n RedactionSearchUtility.findTextRangesByRegexIgnoreCase(\"(?<=OECD) Guideline (4\\\\d{2})\", 1, $section.getTextBlock()),\n RedactionSearchUtility.findTextRangesByRegexIgnoreCase(\"(?<=OECD) Guideline, Method No. (\\\\d{3})\", 1, $section.getTextBlock()) \n ).flatMap(Collection::stream).findFirst()\n .map(boundary -> $section.getTextBlock().subSequence(boundary).toString())\n .map(value -> FileAttribute.builder().label(\"OECD Number\").value(value).build())\n .ifPresent(fileAttribute -> insert(fileAttribute));\n end\n\n\nrule \"DOC.1.0: Guidelines\"\n when\n $section: Section(\n onPage(1)\n && (\n containsString(\"OECD\")\n || containsString(\"EPA\")\n || containsString(\"OPPTS\")\n )\n )\n then\n entityCreationService.byRegex(\"OECD (No\\\\.? )?\\\\d{3}( \\\\(\\\\d{4}\\\\))?\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline found\")\n );\n entityCreationService.byRegex(\"OECD[\\\\s,]{1}(?:.{1,40}.(?>Procedure|Method).{1,20}\\\\d{3,4}(?>.{1,100}\\\\d{4}\\\\))?|\\\\[.{1,20}.Skin.{1,20}\\\\]|[\\\\d\\\\s,\\\\(\\\\)]{7,10}|[\\\\w\\\\.\\\\s]{1,15}[\\\\d]{3}\\\\s\\\\(\\\\d{4}\\\\)|.{0,20}[N|n]umber\\\\s\\\\d{3}.{0,1}|Test Guideline \\\\d{3})\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline found\")\n );\n entityCreationService.byRegex(\"EPA (OPPTS )?\\\\d{3}[. ]\\\\d{4}( \\\\(\\\\d{4}\\\\))?\", \"epa_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"EPA Guideline found\")\n );\n entityCreationService.byRegex(\"EC (Directive )?(No\\\\.? )?\\\\d{3,4}\\\\/\\\\d{3,4}((,? B(\\\\.| )\\\\d{1,2}\\\\.?)? \\\\(\\\\d{4}\\\\))?\", \"ec_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"EC Guideline found\")\n );\n entityCreationService.byRegex(\"Commission Regulation \\\\(EC\\\\) No \\\\d{3}\\\\/\\\\d{4}\", \"ec_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"EC Guideline found\")\n );\n entityCreationService.byRegex(\"OECD Method 4\\\\d{2}.{5,40}\\\\(.{5,40}\\\\d{4}\\\\)\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline year found\")\n );\n// Examples found in PoC 1\n// entityCreationService.byRegex(\"((OECD Guidelines for Testing of Chemicals, Procedure)|(OECD Guidelines for the Testing of Chemicals No\\\\.)|(OECD Test Guideline)|(OECD \\\\[Test Guideline, Number)) \\\\d{3}( \\\\(\\\\d{4}\\\\))?\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n// entity.apply(\"DOC.1.0\", \"OECD Guideline year found\")\n// );\n entityCreationService.byRegex(\"OPPTS (Guideline Number )?\\\\d{3}\\\\.\\\\d{4}( \\\\(\\\\d{4}\\\\))?\", \"epa_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"EPA Guideline found\")\n );\n// new approach OECD Guideline\n entityCreationService.byRegex(\"(?<=OECD)(?:[\\\\w\\\\s,\\\\[\\\\]\\\\(\\\\)\\\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\\\d]{2})\", \"oecd_guideline_number\", EntityType.ENTITY,1, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline no. found\")\n );\n entityCreationService.byRegex(\"(?<=OECD)(?:[\\\\w\\\\s,\\\\[\\\\]\\\\(\\\\)\\\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\\\d]{2}),?\\\\s\\\\(?(\\\\d{4})\\\\)?\", \"oecd_guideline_year\", EntityType.ENTITY,2, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline year found\")\n );\n entityCreationService.byRegex(\"(?<=OECD)[\\\\w\\\\s,\\\\[\\\\]]{1,10}\\\\((\\\\d{4})\\\\)\\\\s(4[\\\\d]{2})\", \"oecd_guideline_year\", EntityType.ENTITY,1, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline year found\")\n );\n entityCreationService.byRegex(\"(?<=OECD).{5,40}Method (4[\\\\d]{2}).{1,65}(\\\\d{4})\\\\)\", \"oecd_guideline_number\", EntityType.ENTITY,1, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline number found\")\n );\n entityCreationService.byRegex(\"(?<=OECD).{5,40}Method (4[\\\\d]{2}).{1,65}(\\\\d{4})\\\\)\", \"oecd_guideline_year\", EntityType.ENTITY,2, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline year found\")\n );\n// missing OECD guideline rules for RFP demo file\n entityCreationService.byRegex(\"(?<=OECD) Guideline (4\\\\d{2})\", \"oecd_guideline_number\", EntityType.ENTITY,1, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline number found\")\n );\n entityCreationService.byRegex(\"OECD Guideline 4\\\\d{2}\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline found\")\n );\n end\n\n\nrule \"DOC.1.1: Guidelines\"\n when\n $headline: Headline(\n onPage(1),\n containsString(\"OECD\")\n )\n then\n entityCreationService.byRegex(\"(OECD (No\\\\.? )?(\\\\d{3})( \\\\(\\\\d{4}\\\\))?)\", \"oecd_guideline\", EntityType.ENTITY,1, $headline).forEach(entity ->\n entity.apply(\"DOC.1.1\", \"OECD Guideline found\")\n );\n end\n\n\nrule \"DOC.1.2: Guidelines\"\n when\n $section: Section(\n (\n containsString(\"DATA REQUIREMENT\")\n || containsString(\"TEST GUIDELINE\")\n || containsString(\"MÉTODO(S) DE REFERÊNCIA(S):\")\n )\n && (\n containsString(\"OECD\")\n || containsString(\"EPA\")\n || containsString(\"OPPTS\")\n )\n && (\n hasEntitiesOfType(\"oecd_guideline\")\n || hasEntitiesOfType(\"epa_guideline\")\n || hasEntitiesOfType(\"ec_guideline\")\n )\n )\n then\n $section.getEntitiesOfType(List.of(\"oecd_guideline\",\"ec_guideline\", \"epa_guideline\")).forEach(entity -> {\n entity.apply(\"DOC.1.2\", \"OECD guideline found.\");\n });\n end\n\n\nrule \"DOC.1.3: Remove guidelines from irrelevant sections\"\n when\n $section: Section(\n (\n hasEntitiesOfType(\"oecd_guideline\")\n || hasEntitiesOfType(\"epa_guideline\")\n || hasEntitiesOfType(\"ec_guideline\")\n )\n && !(\n (\n containsString(\"DATA REQUIREMENT\")\n || containsString(\"TEST GUIDELINE\")\n || containsString(\"MÉTODO(S) DE REFERÊNCIA(S):\")\n )\n && (\n containsString(\"OECD\")\n || containsString(\"EPA\")\n || containsString(\"OPPTS\")\n )\n )\n )\n then\n $section.getEntitiesOfType(List.of(\"oecd_guideline\", \"ec_guideline\", \"epa_guideline\")).forEach(entity -> {\n entity.remove(\"DOC.1.3\", \"remove guidelines from irrelevant sections\");\n retract(entity);\n });\n end\n\n\nrule \"DOC.3.2: Experimental Completion Date\"\n salience 10\n when \n $section: Section(onPage(1) && (containsString(\"STUDY COMPLETED ON\") || containsString(\"STUDY COMPLETION DATE\") || containsString(\"Report completion date\") || containsString(\"Date of Report\") || containsString(\"AMENDMENT COMPLETION DATE\") || containsString(\"AMENDMENT COMPLETED ON\")))\n \n then\n entityCreationService.byRegex(\"STUDY COMPLETED ON (.{5,20}\\\\d{4})\", \"experimental_end_date\", EntityType.ENTITY, 1, $section).forEach(entity -> {\n entity.apply(\"DOC.3.2\", \"Experimental end date found\");\n });\n entityCreationService.byRegex(\"STUDY COMPLETION DATE (.{5,20}\\\\d{4})\", \"experimental_end_date\", EntityType.ENTITY, 1, $section).forEach(entity -> {\n entity.apply(\"DOC.3.2\", \"Experimental end date found\");\n });\n entityCreationService.byRegex(\"Report completion date (.{5,20}\\\\d{4})\", \"experimental_end_date\", EntityType.ENTITY, 1, $section).forEach(entity -> {\n entity.apply(\"DOC.3.2\", \"Experimental end date found\");\n });\n entityCreationService.byRegex(\"Date of Report (.{5,20}\\\\d{4})\", \"experimental_end_date\", EntityType.ENTITY, 1, $section).forEach(entity -> {\n entity.apply(\"DOC.3.2\", \"Experimental end date found\");\n });\n entityCreationService.byRegex(\"AMENDMENT COMPLETION DATE (.{5,20}\\\\d{4})\", \"experimental_end_date\", EntityType.ENTITY, 1, $section).forEach(entity -> {\n entity.apply(\"DOC.3.2\", \"Experimental end date found\");\n });\n entityCreationService.byRegex(\"AMENDMENT COMPLETED ON (.{5,20}\\\\d{4})\", \"experimental_end_date\", EntityType.ENTITY, 1, $section).forEach(entity -> {\n entity.apply(\"DOC.3.2\", \"Experimental end date found\");\n });\n end\n\n\n\n\n\n // hide all skipped species and strains except in the relevant sections\n rule \"DOC.4.2: Species\"\n salience 1\n when\n $section: Section(\n (hasEntitiesOfType(\"species\") || hasEntitiesOfType(\"strain\"))\n && !(\n anyHeadlineContainsStringIgnoreCase(\"test system\")\n || anyHeadlineContainsStringIgnoreCase(\"Species and strain\")\n || anyHeadlineContainsStringIgnoreCase(\"specification\")\n )\n )\n then\n $section.getEntitiesOfType(List.of(\"species\", \"strain\")).forEach(entity -> {\n entity.remove(\"DOC.4.2\",\"n-a\");\n retract(entity);\n });\n end\n\n\nrule \"DOC.4.3: Species\"\n when\n $section: Section(hasEntitiesOfType(\"species\"))\n then\n $section.getEntitiesOfType(\"species\").forEach(entity -> {\n entity.apply(\"DOC.4.3\", \"Species found.\");\n entity.setValue(entity.getValue().toLowerCase());\n });\n end\n\n\nrule \"DOC.5.0: Strain\"\n when\n $section: Section(\n hasEntitiesOfType(\"species\")\n && hasEntitiesOfType(\"strain\")\n && (\n anyHeadlineContainsStringIgnoreCase(\"test system\")\n || anyHeadlineContainsStringIgnoreCase(\"Species and strain\")\n || anyHeadlineContainsStringIgnoreCase(\"specification\")\n )\n )\n then\n $section.getEntitiesOfType(\"strain\").forEach(entity -> {\n entity.apply(\"DOC.5.0\", \"Strain found.\");\n });\n end\n\n\n\n\nrule \"DOC.35.0: Sex\"\n when\n \n $section: Section(\n (\n anyHeadlineContainsStringIgnoreCase(\"animal\")\n || anyHeadlineContainsStringIgnoreCase(\"Species and strain\")\n || anyHeadlineContainsStringIgnoreCase(\"test system\")\n )\n && !getHeadline().containsStringIgnoreCase(\"selection\")\n && (\n containsStringIgnoreCase(\"sex:\")\n || containsStringIgnoreCase(\"male\")\n || containsStringIgnoreCase(\"female\")\n )\n )\n then\n entityCreationService.byRegexIgnoreCase(\"([S|s]ex:)?[\\\\w\\\\s]{0,10}\\\\b(males?|females?)\\\\b\", \"sex\", EntityType.ENTITY,2, $section).forEach(entity -> {\n entity.apply(\"DOC.35.0\", \"Test animal sex found\");\n });\n end\n\n\n\nrule \"DOC.6.0: Authors\"\n when\n $headline: Headline(onPage(1), containsString(\"AUTHOR\"))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"author\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.6.0\", \"Author found\"));\n end\n\n\n\nrule \"DOC.6.2: Authors\"\n when\n $page: Page(getNumber() == 1, getMainBodyTextBlock().getSearchText() (contains \"AUTHOR(S)\" || contains \"AUTHORS\" || contains \"Author\"), getMainBodyTextBlock().getSearchText() (contains \"STUDY COMPLETED ON\" || contains \"STUDY COMPLETION DATE\" || contains \"DATE OF INTERIM REPORT\" || contains \"Report completion date\" || contains \"Date of Report\" || contains \"AMENDMENT COMPLETION DATE\"))\n then\n entityCreationService.shortestBetweenAnyString(\n List.of(\"AUTHOR(S)\", \"AUTHORS\", \"Author\"),\n List.of(\"STUDY COMPLETED ON\", \"STUDY COMPLETION DATE\", \"DATE OF INTERIM REPORT\", \"Report completion date\", \"Date of Report\", \"AMENDMENT COMPLETION DATE\"),\n \"author\",\n EntityType.ENTITY,\n document)\n .forEach(entity -> entity.apply(\"DOC.6.2\", \"Author found\"));\n end\n\n\n\n\nrule \"DOC.6.6: laboratory_project_identification\"\n when\n $page: Page(getNumber() == 1, getMainBodyTextBlock().getSearchText() (contains \"LABORATORY PROJECT IDENTIFICATION\" || contains \"TEST FACILITY PROJECT IDENTIFICATION\" || contains \"Laboratory Project Identification\"))\n then\n entityCreationService.shortestBetweenAnyString(\n List.of(\"LABORATORY PROJECT IDENTIFICATION\", \"TEST FACILITY PROJECT IDENTIFICATION\"),\n List.of(\"SPONSOR\", \"VOLUME\", \"This\"),\n \"author\",\n EntityType.ENTITY,\n document)\n .forEach(entity -> entity.apply(\"DOC.6.6\", \"Laboratory Project Identification\"));\n end\n\n\n\nrule \"DOC.7.2: study title by document structure\"\n when\n $page: Page(getNumber() == 1, getMainBodyTextBlock().getSearchText() (contains \"STUDY TITLE\" || contains \"Study Title\" || contains \"STUDYTITLE\" || contains \"Report (Final)\"))\n then\n entityCreationService.shortestBetweenAnyString(\n List.of(\"STUDY TITLE\", \"STUDYTITLE\", \"Report (Final)\"),\n List.of(\"TEST GUIDELINES\", \"TEST GUIDELINE(S)\", \"Guidelines\", \"DATA REQUIREMENT\", \"AUTHOR(S)\", \"AUTHOR\"),\n \"author\",\n EntityType.ENTITY,\n document)\n .forEach(entity -> entity.apply(\"DOC.7.2\", \"Study title found\"));\n end\n\n\n\nrule \"DOC.8.1: Performing Laboratory\"\n when\n $page: Page(getNumber() == 1, getMainBodyTextBlock().getSearchText() (contains \"PERFORMING LABORATORY\" || contains \"TEST FACILITIES\" || contains \"TEST FACILITY\" || contains \"Test Facility\"), getMainBodyTextBlock().getSearchText() (contains \"LABORATORY PROJECT IDENTIFICATION\" || contains \"TEST FACILITY PROJECT IDENTIFICATION\" || contains \"Sponsor\"))\n then\n entityCreationService.shortestBetweenAnyString(\n List.of(\"PERFORMING LABORATORY\", \"TEST FACILITIES\", \"TEST FACILITY\"),\n List.of(\"LABORATORY PROJECT IDENTIFICATION\", \"TEST FACILITY PROJECT IDENTIFICATION\", \"Sponsor\", \"PROJECT IDENTIFICATION\"),\n \"author\",\n EntityType.ENTITY,\n document)\n .forEach(entity -> entity.apply(\"DOC.8.1\", \"Performing Laboratory found\"));\n end\n\n\n\n\nrule \"DOC.8.2: Summary Methods\"\n when\n $headline: Headline(containsString(\"1.1. METHODS\"))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"summary_methods\", EntityType.ENTITY)\n .filter(e -> !e.getValue().contains(\"Report; Project No\")) \n .filter(e -> !e.getValue().startsWith(\"This document\")) \n .filter(e -> !e.getValue().startsWith(\"Page\"))\n .filter(e -> !e.getValue().startsWith(\"2. INTRODUCTION\"))\n .filter(e -> !e.getValue().startsWith(\"BASF\"))\n .filter(e -> !e.getValue().startsWith(\"The Chemical Company\"))\n .filter(e -> !e.getValue().startsWith(\"We create chemistry\")) \n .forEach(entity -> entity.apply(\"DOC.8.2\", \"Summary Methods found\"));\n end\n\nrule \"DOC.8.3: Summary Observations Laboratory\"\n when\n $headline: Headline(containsString(\"1.2. OBSERVATIONS\"))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"summary_observations\", EntityType.ENTITY)\n .filter(e -> !e.getValue().contains(\"Report; Project No\"))\n .filter(e -> !e.getValue().startsWith(\"This document\")) \n .filter(e -> !e.getValue().startsWith(\"Page\"))\n .filter(e -> !e.getValue().startsWith(\"2. INTRODUCTION\"))\n .filter(e -> !e.getValue().startsWith(\"BASF\"))\n .filter(e -> !e.getValue().startsWith(\"The Chemical Company\"))\n .filter(e -> !e.getValue().startsWith(\"We create chemistry\")) \n .forEach(entity -> entity.apply(\"DOC.8.3\", \"Summary Observations found\"));\n end\n\n\nrule \"DOC.8.5: Summary Results\"\n when\n Headline((containsStringIgnoreCase(\"1.3. RESULTS\") || containsStringIgnoreCase(\"1.2. RESULTS\")), $sectionIdentifier: getSectionIdentifier())\n $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifier))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"summary_results\", EntityType.ENTITY)\n .filter(e -> !e.getValue().contains(\"Report; Project No\"))\n .filter(e -> !e.getValue().startsWith(\"This document\")) \n .filter(e -> !e.getValue().startsWith(\"Page\"))\n .filter(e -> !e.getValue().startsWith(\"2. INTRODUCTION\"))\n .filter(e -> !e.getValue().startsWith(\"BASF\"))\n .filter(e -> !e.getValue().startsWith(\"The Chemical Company\"))\n .filter(e -> !e.getValue().startsWith(\"We create chemistry\")) \n .forEach(entity -> {\n entity.apply(\"DOC.8.5\", \"Summary Results\");\n });\n end\n\nrule \"DOC.8.6: Summary Results 2\"\n when\n $headline: Headline(containsString(\"1.2. RESULTS\"))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"summary_results\", EntityType.ENTITY)\n .filter(e -> !e.getValue().contains(\"Report; Project No\"))\n .filter(e -> !e.getValue().startsWith(\"This document\")) \n .filter(e -> !e.getValue().startsWith(\"Page\"))\n .filter(e -> !e.getValue().startsWith(\"2. INTRODUCTION\"))\n .filter(e -> !e.getValue().startsWith(\"BASF\"))\n .filter(e -> !e.getValue().startsWith(\"The Chemical Company\")) \n .filter(e -> !e.getValue().startsWith(\"We create chemistry\")) \n .forEach(entity -> entity.apply(\"DOC.8.6\", \"Summary Results\"));\n end\n\n\n\nrule \"DOC.8.4: Summary Conclusion\"\n when\n $headline: Headline(containsString(\"1.4. CONCLUSION\") || containsString(\"1.3. CONCLUSION\"))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"study_conclusion\", EntityType.ENTITY)\n .filter(e -> !e.getValue().contains(\"Report; Project No\")) \n .filter(e -> !e.getValue().startsWith(\"This document\")) \n .filter(e -> !e.getValue().startsWith(\"Page\"))\n .filter(e -> !e.getValue().startsWith(\"2. INTRODUCTION\"))\n .filter(e -> !e.getValue().startsWith(\"BASF\"))\n .filter(e -> !e.getValue().startsWith(\"The Chemical Company\"))\n .filter(e -> !e.getValue().startsWith(\"We create chemistry\")) \n .forEach(entity -> entity.apply(\"DOC.8.4\", \"Summary Conculsion found\"));\n end\n\n\n\nrule \"DOC.9.0: GLP Study\"\n when\n $headline: Headline(containsString(\"GOOD LABORATORY PRACTICE COMPLIANCE\")\n || containsString(\"GOOD LABORATORY PRACTICE COMPLIANCE STATEMENT\")\n || (containsString(\"DECLARACAO DE CONFORMIDADE\") && containsString(\"PRATICAS DE LABORATORIO\"))\n || containsString(\"GLP Certificate\")\n || containsString(\"GLP Certificates\")\n || containsString(\"GOOD LABORATORY PRACTICE (GLP) CERTIFICATE\")\n || containsString(\"Good Laboratory Practice Certificate\")\n || containsString(\"STATEMENT OF GLP COMPLIANCE AND AUTHENTICATION\")\n || containsString(\"GLP CERTIFICATE (FROM THE COMPETENT AUTHORITY)\")\n || containsString(\"GLP COMPLIANCE STATEMENT\")\n || containsString(\"GLP STATEMENT\") \n )\n then\n entityCreationService.bySemanticNode($headline, \"glp_study\", EntityType.ENTITY).ifPresent(entity -> {\n entity.apply(\"DOC.9.0\", \"GLP Study found\");\n });\n end\n\n\n\nrule \"DOC.9.1: GLP Study\"\n when\n $paragraph: Paragraph(containsString(\"GLP COMPLIANCE STATEMENT\"))\n then\n entityCreationService.byRegex(\"GLP COMPLIANCE STATEMENT\", \"glp_study\", EntityType.ENTITY, $paragraph).forEach(entity -> {\n entity.apply(\"DOC.9.1\", \"GLP Study found\");\n });\n end\n\n\n\n//------------------------------------ Manual redaction rules ------------------------------------\n\n// Rule unit: MAN.0\nrule \"MAN.0.0: Apply manual resize redaction\"\n salience 128\n when\n $resizeRedaction: ManualResizeRedaction($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate))\n $entityToBeResized: TextEntity(matchesAnnotationId($id))\n then\n manualChangesApplicationService.resizeEntityAndReinsert($entityToBeResized, $resizeRedaction);\n retract($resizeRedaction);\n update($entityToBeResized);\n $entityToBeResized.getIntersectingNodes().forEach(node -> update(node));\n end\n\nrule \"MAN.0.1: Apply manual resize redaction\"\n salience 128\n when\n $resizeRedaction: ManualResizeRedaction($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate))\n $imageToBeResized: Image(id == $id)\n then\n manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);\n retract($resizeRedaction);\n update($imageToBeResized);\n update($imageToBeResized.getParent());\n end\n\n\n// Rule unit: MAN.1\nrule \"MAN.1.0: Apply id removals that are valid and not in forced redactions to Entity\"\n salience 128\n when\n $idRemoval: IdRemoval($id: annotationId, status == AnnotationStatus.APPROVED)\n $entityToBeRemoved: TextEntity(matchesAnnotationId($id))\n then\n $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);\n update($entityToBeRemoved);\n retract($idRemoval);\n $entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));\n end\n\nrule \"MAN.1.1: Apply id removals that are valid and not in forced redactions to Image\"\n salience 128\n when\n $idRemoval: IdRemoval($id: annotationId, status == AnnotationStatus.APPROVED)\n $imageEntityToBeRemoved: Image($id == id)\n then\n $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);\n update($imageEntityToBeRemoved);\n retract($idRemoval);\n update($imageEntityToBeRemoved.getParent());\n end\n\n\n// Rule unit: MAN.2\nrule \"MAN.2.0: Apply force redaction\"\n salience 128\n when\n $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED)\n $entityToForce: TextEntity(matchesAnnotationId($id))\n then\n $entityToForce.getManualOverwrite().addChange($force);\n update($entityToForce);\n $entityToForce.getIntersectingNodes().forEach(node -> update(node));\n retract($force);\n end\n\nrule \"MAN.2.1: Apply force redaction to images\"\n salience 128\n when\n $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED)\n $imageToForce: Image(id == $id)\n then\n $imageToForce.getManualOverwrite().addChange($force);\n update($imageToForce);\n update($imageToForce.getParent());\n retract($force);\n end\n\n\n// Rule unit: MAN.3\nrule \"MAN.3.0: Apply entity recategorization\"\n salience 128\n when\n $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))\n $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type != $type)\n then\n $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));\n manualChangesApplicationService.recategorize($entityToBeRecategorized, $recategorization);\n retract($recategorization);\n // Entity is copied and inserted, so the old entity needs to be retracted to avoid duplication.\n retract($entityToBeRecategorized);\n end\n\nrule \"MAN.3.1: Apply entity recategorization of same type\"\n salience 128\n when\n $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))\n $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type == $type)\n then\n $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);\n retract($recategorization);\n end\n\nrule \"MAN.3.2: Apply image recategorization\"\n salience 128\n when\n $recategorization: ManualRecategorization($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))\n $imageToBeRecategorized: Image($id == id)\n then\n manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);\n update($imageToBeRecategorized);\n update($imageToBeRecategorized.getParent());\n retract($recategorization);\n end\n\n\n// Rule unit: MAN.4\nrule \"MAN.4.0: Apply legal basis change\"\n salience 128\n when\n $legalbasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED)\n $imageToBeRecategorized: Image($id == id)\n then\n $imageToBeRecategorized.getManualOverwrite().addChange($legalbasisChange);\n end\n\nrule \"MAN.4.1: Apply legal basis change\"\n salience 128\n when\n $legalBasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED)\n $entityToBeChanged: TextEntity(matchesAnnotationId($id))\n then\n $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);\n end\n\n\n//------------------------------------ Entity merging rules ------------------------------------\n\n// Rule unit: X.0\nrule \"X.0.0: remove Entity contained by Entity of same type\"\n salience 65\n when\n $larger: TextEntity($type: type, $entityType: entityType, active())\n $contained: TextEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !hasManualChanges(), active())\n then\n $contained.remove(\"X.0.0\", \"remove Entity contained by Entity of same type\");\n retract($contained);\n end\n\n\n// Rule unit: X.1\nrule \"X.1.0: merge intersecting Entities of same type\"\n salience 64\n when\n $first: TextEntity($type: type, $entityType: entityType, !resized(), active())\n $second: TextEntity(intersects($first), type == $type, entityType == $entityType, this != $first, !hasManualChanges(), active())\n then\n TextEntity mergedEntity = entityCreationService.mergeEntitiesOfSameType(List.of($first, $second), $type, $entityType, document);\n $first.remove(\"X.1.0\", \"merge intersecting Entities of same type\");\n $second.remove(\"X.1.0\", \"merge intersecting Entities of same type\");\n retract($first);\n retract($second);\n mergedEntity.getIntersectingNodes().forEach(node -> update(node));\n end\n\n\n// Rule unit: X.2\nrule \"X.2.0: remove Entity of type ENTITY when contained by FALSE_POSITIVE\"\n salience 64\n when\n $falsePositive: TextEntity($type: type, entityType == EntityType.FALSE_POSITIVE, active())\n $entity: TextEntity(containedBy($falsePositive), type == $type, entityType == EntityType.ENTITY, !hasManualChanges(), active())\n then\n $entity.getIntersectingNodes().forEach(node -> update(node));\n $entity.remove(\"X.2.0\", \"remove Entity of type ENTITY when contained by FALSE_POSITIVE\");\n retract($entity)\n end\n\n\n// Rule unit: X.3\nrule \"X.3.0: remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION\"\n salience 64\n when\n $falseRecommendation: TextEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION, active())\n $recommendation: TextEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active())\n then\n $recommendation.remove(\"X.3.0\", \"remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION\");\n retract($recommendation);\n end\n\n\n// Rule unit: X.4\nrule \"X.4.0: remove Entity of type RECOMMENDATION when intersected by ENTITY with same type\"\n salience 256\n when\n $entity: TextEntity($type: type, entityType == EntityType.ENTITY, active())\n $recommendation: TextEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active())\n then\n $entity.addEngines($recommendation.getEngines());\n $recommendation.remove(\"X.4.0\", \"remove Entity of type RECOMMENDATION when intersected by ENTITY with same type\");\n retract($recommendation);\n end\n\n\n// Rule unit: X.5\nrule \"X.5.0: remove Entity of type RECOMMENDATION when contained by ENTITY\"\n salience 256\n when\n $entity: TextEntity(entityType == EntityType.ENTITY, active())\n $recommendation: TextEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active())\n then\n $recommendation.remove(\"X.5.0\", \"remove Entity of type RECOMMENDATION when contained by ENTITY\");\n retract($recommendation);\n end\n\n\n// Rule unit: X.6\nrule \"X.6.0: remove Entity of lower rank, when intersected by entity of type ENTITY\"\n salience 32\n when\n $higherRank: TextEntity($type: type, entityType == EntityType.ENTITY, active())\n $lowerRank: TextEntity(intersects($higherRank), type != $type, dictionary.getDictionaryRank(type) < dictionary.getDictionaryRank($type), !hasManualChanges(), active())\n then\n $lowerRank.getIntersectingNodes().forEach(node -> update(node));\n $lowerRank.remove(\"X.6.0\", \"remove Entity of lower rank, when intersected by entity of type ENTITY\");\n retract($lowerRank);\n end\n\n\n//------------------------------------ File attributes rules ------------------------------------\n\n// Rule unit: FA.1\nrule \"FA.1.0: remove duplicate FileAttributes\"\n salience 64\n when\n $fileAttribute: FileAttribute($label: label, $value: value)\n $duplicate: FileAttribute(this != $fileAttribute, label == $label, value == $value)\n then\n retract($duplicate);\n end\n\n\n//------------------------------------ Local dictionary search rules ------------------------------------\n\n// Rule unit: LDS.0\nrule \"LDS.0.0: run local dictionary search\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n salience -999\n when\n $dictionaryModel: DictionaryModel(!localEntriesWithMatchedRules.isEmpty()) from dictionary.getDictionaryModels()\n then\n entityCreationService.bySearchImplementation($dictionaryModel.getLocalSearch(), $dictionaryModel.getType(), EntityType.RECOMMENDATION, document)\n .forEach(entity -> {\n Collection matchedRules = $dictionaryModel.getLocalEntriesWithMatchedRules().get(entity.getValue());\n entity.addMatchedRules(matchedRules);\n });\n end\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dev/Flora/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dev/Flora/rules.txt new file mode 100644 index 00000000..c1514d61 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dev/Flora/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport static java.lang.String.format;\nimport static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.anyMatch;\nimport static com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility.exactMatch;\n\nimport java.util.List;\nimport java.util.LinkedList;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.Collection;\nimport java.util.stream.Stream;\nimport java.util.Optional;\n\nimport com.iqser.red.service.redaction.v1.server.model.document.*;\nimport com.iqser.red.service.redaction.v1.server.model.document.TextRange;\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.*;\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType;\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule;\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity\nimport com.iqser.red.service.redaction.v1.server.model.document.entity.MatchedRule\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.*;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Section;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Table;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.SemanticNode;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Document;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Paragraph;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Image;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.ImageType;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Page;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Headline;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.SectionIdentifier;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Footer;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.Header;\nimport com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.*;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlock;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.TextBlockCollector;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.AtomicTextBlock;\nimport com.iqser.red.service.redaction.v1.server.model.document.textblock.ConcatenatedTextBlock;\nimport com.iqser.red.service.redaction.v1.server.model.NerEntities;\nimport com.iqser.red.service.redaction.v1.server.model.dictionary.Dictionary;\nimport com.iqser.red.service.redaction.v1.server.model.dictionary.DictionaryModel;\nimport com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService;\nimport com.iqser.red.service.redaction.v1.server.service.ManualChangesApplicationService;\nimport com.iqser.red.service.redaction.v1.server.utils.RedactionSearchUtility;\n\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRecategorization;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange;\nimport com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;\n\nglobal Document document\nglobal EntityCreationService entityCreationService\nglobal ManualChangesApplicationService manualChangesApplicationService\nglobal Dictionary dictionary\n\n//------------------------------------ queries ------------------------------------\n\nquery \"getFileAttributes\"\n $fileAttribute: FileAttribute()\n end\n//---------------------------------------------------------------------------\n\nrule \"H.0.0: retract table of contents page\"\n when\n $page: Page(getMainBodyTextBlock().getSearchText().contains(\"........\") || (getMainBodyTextBlock().getSearchText().contains(\"APPENDICES\") && getMainBodyTextBlock().getSearchText().contains(\"TABLES\")))\n $node: SemanticNode(onPage($page.getNumber()), !onPage($page.getNumber() -1), getType() != NodeType.IMAGE)\n then\n retract($node);\n end\n\n\nrule \"H.1.0: Ignore Table of Contents\"\n salience 10\n when\n $tocHeadline: Headline(containsString(\"CONTENTS\"))\n $page: Page() from $tocHeadline.getParent().getPages()\n $node: SemanticNode(this != $tocHeadline, getType() != NodeType.IMAGE, onPage($page.getNumber()), !onPage($page.getNumber() -1))\n then\n retract($node);\n end\n\n\n// Rule unit: MAN.0\nrule \"H.2.0: Show headlines\"\n when\n $headline: Headline()\n then\n entityCreationService.bySemanticNode($headline, \"headline\", EntityType.ENTITY);\n end\n\n\nrule \"H.3.0: Study Type File Attribute\"\n when\n not FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"404\",\"405\",\"425\",\"429\",\"436\",\"438\",\"439\",\"471\",\"487\"))\n $section: Section(\n (containsString(\"DATA REQUIREMENT\") || containsString(\"TEST GUIDELINE\") || containsString(\"MÉTODO(S) DE REFERÊNCIA(S):\"))\n ,(containsString(\"OECD\") || containsString(\"EPA\") || containsString(\"OPPTS\"))\n )\n then\n Stream.of(RedactionSearchUtility.findTextRangesByRegexIgnoreCase(\"(?<=OECD)(?:[\\\\w\\\\s,\\\\[\\\\]\\\\(\\\\)\\\\.]{1,10}|(?:.{5,40}(?:Number |Procedure |Guideline )))(4[\\\\d]{2})\", 1, $section.getTextBlock()),\n RedactionSearchUtility.findTextRangesByRegexIgnoreCase(\"(?<=OECD).{5,40}Method (4[\\\\d]{2}).{1,65}(\\\\d{4})\\\\)\", 1, $section.getTextBlock()),\n RedactionSearchUtility.findTextRangesByRegexIgnoreCase(\"(?<=OECD) Guideline (4\\\\d{2})\", 1, $section.getTextBlock())).flatMap(Collection::stream).findFirst()\n .map(textRange -> $section.getTextBlock().subSequence(textRange).toString())\n .map(value -> FileAttribute.builder().label(\"OECD Number\").value(value).build())\n .ifPresent(fileAttribute -> insert(fileAttribute));\n end\n\n\nrule \"DOC.1.0: Guidelines\"\n when\n $section: Section(\n (\n containsString(\"DATA REQUIREMENT\")\n || containsString(\"TEST GUIDELINE\")\n || containsString(\"MÉTODO(S) DE REFERÊNCIA(S):\")\n )\n && (\n containsString(\"OECD\")\n || containsString(\"EPA\")\n || containsString(\"OPPTS\")\n )\n )\n then\n entityCreationService.byRegex(\"OECD (No\\\\.? )?\\\\d{3}( \\\\(\\\\d{4}\\\\))?\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline found\", \"n-a\")\n );\n entityCreationService.byRegex(\"OECD[\\\\s,]{1}(?:.{1,40}.(?>Procedure|Method).{1,20}\\\\d{3,4}(?>.{1,100}\\\\d{4}\\\\))?|\\\\[.{1,20}.Skin.{1,20}\\\\]|[\\\\d\\\\s,\\\\(\\\\)]{7,10}|[\\\\w\\\\.\\\\s]{1,15}[\\\\d]{3}\\\\s\\\\(\\\\d{4}\\\\)|.{0,20}[N|n]umber\\\\s\\\\d{3}.{0,1}|Test Guideline \\\\d{3})\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline found\", \"n-a\")\n );\n entityCreationService.byRegex(\"EPA (OPPTS )?\\\\d{3}[. ]\\\\d{4}( \\\\(\\\\d{4}\\\\))?\", \"epa_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"EPA Guideline found\", \"n-a\")\n );\n entityCreationService.byRegex(\"EC (Directive )?(No\\\\.? )?\\\\d{3,4}\\\\/\\\\d{3,4}((,? B(\\\\.| )\\\\d{1,2}\\\\.?)? \\\\(\\\\d{4}\\\\))?\", \"ec_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"EC Guideline found\", \"n-a\")\n );\n entityCreationService.byRegex(\"Commission Regulation \\\\(EC\\\\) No \\\\d{3}\\\\/\\\\d{4}\", \"ec_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"EC Guideline found\", \"n-a\")\n );\n entityCreationService.byRegex(\"OECD Method 4\\\\d{2}.{5,40}\\\\(.{5,40}\\\\d{4}\\\\)\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline year found\", \"n-a\")\n );\n entityCreationService.byRegex(\"OPPTS (Guideline Number )?\\\\d{3}\\\\.\\\\d{4}( \\\\(\\\\d{4}\\\\))?\", \"epa_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"EPA Guideline found\", \"n-a\")\n );\n entityCreationService.byRegex(\"(?<=OECD)(?:[\\\\w\\\\s,\\\\[\\\\]\\\\(\\\\)\\\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\\\d]{2})\", \"oecd_guideline_number\", EntityType.ENTITY,1, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline no. found\", \"n-a\")\n );\n entityCreationService.byRegex(\"(?<=OECD)(?:[\\\\w\\\\s,\\\\[\\\\]\\\\(\\\\)\\\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\\\d]{2}),?\\\\s\\\\(?(\\\\d{4})\\\\)?\", \"oecd_guideline_year\", EntityType.ENTITY,2, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline year found\", \"n-a\")\n );\n entityCreationService.byRegex(\"(?<=OECD)[\\\\w\\\\s,\\\\[\\\\]]{1,10}\\\\((\\\\d{4})\\\\)\\\\s(4[\\\\d]{2})\", \"oecd_guideline_year\", EntityType.ENTITY,1, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline year found\", \"n-a\")\n );\n entityCreationService.byRegex(\"(?<=OECD).{5,40}Method (4[\\\\d]{2}).{1,65}(\\\\d{4})\\\\)\", \"oecd_guideline_number\", EntityType.ENTITY,1, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline number found\", \"n-a\")\n );\n entityCreationService.byRegex(\"(?<=OECD).{5,40}Method (4[\\\\d]{2}).{1,65}(\\\\d{4})\\\\)\", \"oecd_guideline_year\", EntityType.ENTITY,2, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline year found\", \"n-a\")\n );\n entityCreationService.byRegex(\"(?<=OECD) Guideline (4\\\\d{2})\", \"oecd_guideline_number\", EntityType.ENTITY,1, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline number found\", \"n-a\")\n );\n entityCreationService.byRegex(\"OECD Guideline 4\\\\d{2}\", \"oecd_guideline\", EntityType.ENTITY, $section).forEach(entity ->\n entity.apply(\"DOC.1.0\", \"OECD Guideline found\", \"n-a\")\n );\n end\n\n\nrule \"DOC.1.2: Guidelines\"\n when\n $section: Section(\n (\n containsString(\"DATA REQUIREMENT\")\n || containsString(\"TEST GUIDELINE\")\n || containsString(\"MÉTODO(S) DE REFERÊNCIA(S):\")\n )\n && (\n containsString(\"OECD\")\n || containsString(\"EPA\")\n || containsString(\"OPPTS\")\n )\n && (\n hasEntitiesOfType(\"oecd_guideline\")\n || hasEntitiesOfType(\"epa_guideline\")\n || hasEntitiesOfType(\"ec_guideline\")\n )\n )\n then\n $section.getEntitiesOfType(List.of(\"oecd_guideline\",\"ec_guideline\", \"epa_guideline\")).forEach(entity -> {\n entity.apply(\"DOC.1.2\", \"OECD guideline found.\", \"n-a\");\n });\n end\n\n\nrule \"DOC.1.3: Guidelines\"\n when\n $section: Section(\n (\n hasEntitiesOfType(\"oecd_guideline\")\n || hasEntitiesOfType(\"epa_guideline\")\n || hasEntitiesOfType(\"ec_guideline\")\n )\n && !(\n (\n containsString(\"DATA REQUIREMENT\")\n || containsString(\"TEST GUIDELINE\")\n || containsString(\"MÉTODO(S) DE REFERÊNCIA(S):\")\n )\n && (\n containsString(\"OECD\")\n || containsString(\"EPA\")\n || containsString(\"OPPTS\")\n )\n )\n )\n then\n $section.getEntitiesOfType(List.of(\"oecd_guideline\", \"ec_guideline\", \"epa_guideline\")).forEach(entity -> {\n entity.removeFromGraph();\n retract(entity);\n });\n end\n\n\nrule \"DOC.2.0: Report number\"\n when\n $section: Section(containsString(\"LABORATORY PROJECT ID\") , containsString(\"Report Number:\"))\n then\n entityCreationService.lineAfterString(\"Report Number:\", \"report_number\", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> {\n entity.apply(\"DOC.2.0\", \"Report number found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.3.0: Experimental Starting Date\"\n when\n $section: Section(containsString(\"Experimental I. Starting Date:\") || containsString(\"Experimental II. Starting Date:\") || containsStringIgnoreCase(\"experimental start date\") || containsStringIgnoreCase(\"experimental starting date\"))\n then\n entityCreationService.lineAfterStrings(\n List.of(\"Experimental start date\",\n \"Experimental start date:\",\n \"Experimental Starting Date\",\n \"Experimental Starting Date:\",\n \"Experimental starting date\",\n \"Experimental starting date:\",\n \"Experimental Start Date\",\n \"Experimental Start Date:\",\n \"Experimental I. Starting Date:\",\n \"Experimental II. Starting Date:\"), \"experimental_start_date\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.3.0\", \"Experimental start date found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.4.0: Experimental Completion Date\"\n when\n $section: Section(containsStringIgnoreCase(\"experimental termination date\") || containsStringIgnoreCase(\"experimental completion date\"))\n then\n entityCreationService.lineAfterStrings(\n List.of(\"Experimental termination date\",\n \"Experimental termination date:\",\n \"Experimental Completion Date\",\n \"Experimental Completion Date:\",\n \"Experimental completion date\",\n \"Experimental completion date:\",\n \"Experimental Termination Date\",\n \"Experimental Termination Date:\"), \"experimental_end_date\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.4.0\", \"Experimental end date found\", \"n-a\");\n });\n end\n\n\n rule \"DOC.5.0: Ignore species and strain in irrelevant study types\"\n salience 1\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"406\",\"428\",\"438\",\"439\",\"471\",\"474\",\"487\"))\n $section: Section(hasEntitiesOfType(\"species\") || hasEntitiesOfType(\"strain\"))\n then\n $section.getEntitiesOfType(List.of(\"species\", \"strain\")).forEach(entity -> {\n entity.removeFromGraph();\n retract(entity);\n });\n end\n\n\n rule \"DOC.5.1: Hide all skipped species and strains except in the relevant sections\"\n salience 1\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"404\",\"405\",\"425\",\"429\",\"436\"))\n $section: Section(\n (hasEntitiesOfType(\"species\") || hasEntitiesOfType(\"strain\"))\n && !(\n anyHeadlineContainsStringIgnoreCase(\"test system\")\n || anyHeadlineContainsStringIgnoreCase(\"animals\")\n || anyHeadlineContainsStringIgnoreCase(\"specification\")\n )\n )\n then\n $section.getEntitiesOfType(List.of(\"species\", \"strain\")).forEach(entity -> {\n entity.removeFromGraph();\n retract(entity);\n });\n end\n\n\nrule \"DOC.5.2: Species\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"404\",\"405\",\"425\",\"429\",\"436\"))\n $section: Section(hasEntitiesOfType(\"species\"))\n then\n $section.getEntitiesOfType(\"species\").forEach(entity -> {\n entity.apply(\"DOC.5.2\", \"Species found.\", \"n-a\");\n entity.setValue(entity.getValue().toLowerCase());\n });\n end\n\n\nrule \"DOC.5.3: Strain\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"404\",\"405\",\"425\",\"429\",\"436\"))\n $section: Section(\n hasEntitiesOfType(\"species\")\n && hasEntitiesOfType(\"strain\")\n && (\n anyHeadlineContainsStringIgnoreCase(\"test system\")\n || anyHeadlineContainsStringIgnoreCase(\"animals\")\n || anyHeadlineContainsStringIgnoreCase(\"specification\")\n )\n )\n then\n $section.getEntitiesOfType(\"strain\").forEach(entity -> {\n entity.apply(\"DOC.5.3\", \"Strain found.\", \"n-a\");\n });\n end\n\n\nrule \"DOC.6.0: study title by document structure\"\n when\n $table: Table(onPage(1),\n (containsString(\"Final Report\") || containsString(\"SPL\")),\n numberOfRows == 1,\n numberOfCols == 1)\n then\n\n entityCreationService.bySemanticNode($table.getCell(0, 0).streamChildren().toList().get(1), \"title\", EntityType.ENTITY).ifPresent(entity -> {\n entity.apply(\"DOC.6.0\", \"Study title found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.6.1: study title\"\n when\n $table: Table(onPage(1), (containsString(\"Final Report\") || containsString(\"SPL\")))\n then\n entityCreationService.byRegexWithLineBreaksIgnoreCase(\"(?<=\\\\n)[\\\\w\\\\W]{1,300}(?=\\\\nFinal Report)\", \"title\", EntityType.ENTITY, $table).findFirst().ifPresent(entity -> {\n entity.apply(\"DOC.6.1\", \"Title found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.6.2: study title\"\n when\n not Table(onPage(1), (containsString(\"Final Report\") || containsString(\"SPL\")))\n $section: Section(onPage(1), (containsString(\"Final Report\") || containsString(\"SPL\")))\n then\n entityCreationService.byRegexWithLineBreaksIgnoreCase(\"(?<=\\\\n)[\\\\w\\\\W]{1,300}(?=\\\\nFinal Report)\", \"title\", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> {\n entity.apply(\"DOC.6.2\", \"Title found\", \"n-a\");\n });\n end\n\n\n\nrule \"DOC.7.0: Performing Laboratory (Name)\"\n when\n $section: Section(containsString(\"PERFORMING LABORATORY:\"))\n then\n entityCreationService.lineAfterString(\"PERFORMING LABORATORY:\", \"laboratory_name\", EntityType.ENTITY, $section).findFirst().ifPresent(entity -> {\n entity.apply(\"DOC.7.0\", \"Performing Laboratory found\", \"n-a\");\n });\n end\n\n\n rule \"DOC.7.1: Performing Laboratory (Country)\"\n when\n nerEntities: NerEntities(hasEntitiesOfType(\"COUNTRY\"))\n $section: Section(containsString(\"PERFORMING LABORATORY:\"))\n then\n nerEntities.streamEntitiesOfType(\"COUNTRY\")\n .filter(nerEntity -> $section.getTextRange().contains(nerEntity.textRange()))\n .map(nerEntity -> entityCreationService.byNerEntity(nerEntity, \"laboratory_country\", EntityType.ENTITY, $section))\n .forEach(entity -> {\n entity.apply(\"DOC.7.1\", \"Performing Laboratory found\", \"n-a\");\n insert(entity);\n });\n end\n\n\nrule \"DOC.7.2: Performing Laboratory (Country & Name) from dict\"\n when\n $section: Section(\n (hasEntitiesOfType(\"laboratory_country\") || hasEntitiesOfType(\"laboratory_name\"))\n && (containsString(\"PERFORMING LABORATORY:\") || (containsString(\"PERFORMING\") && containsString(\"LABORATORY:\")))\n )\n then\n $section.getEntitiesOfType(\"laboratory_country\").forEach(entity -> {\n entity.apply(\"DOC.7.2\", \"Performing laboratory country dictionary entry found.\", \"n-a\");\n });\n $section.getEntitiesOfType(\"laboratory_name\").forEach(entity -> {\n entity.apply(\"DOC.7.2\", \"Performing laboratory name dictionary entry found.\", \"n-a\");\n });\n end\n\n\nrule \"DOC.7.3: Performing Laboratory (Country) from dict\"\n when\n $section: Section(\n (hasEntitiesOfType(\"laboratory_country\") || hasEntitiesOfType(\"laboratory_name\"))\n && !(containsString(\"PERFORMING LABORATORY:\") || (containsString(\"PERFORMING\") && containsString(\"LABORATORY:\")))\n )\n then\n $section.getEntitiesOfType(List.of(\"laboratory_country\", \"laboratory_name\")).forEach(entity -> {\n entity.removeFromGraph();\n retract(entity);\n });\n end\n\n\nrule \"DOC.8.0: GLP Study\"\n when\n $headline: Headline(containsString(\"GOOD LABORATORY PRACTICE COMPLIANCE\")\n || containsString(\"GOOD LABORATORY PRACTICE COMPLIANCE STATEMENT\")\n || (containsString(\"DECLARACAO DE CONFORMIDADE\") && containsString(\"PRATICAS DE LABORATORIO\"))\n || containsString(\"GLP Certificate\")\n || containsString(\"GLP Certificates\")\n || containsString(\"GOOD LABORATORY PRACTICE (GLP) CERTIFICATE\")\n || containsString(\"Good Laboratory Practice Certificate\")\n || containsString(\"STATEMENT OF GLP COMPLIANCE AND AUTHENTICATION\"))\n then\n entityCreationService.bySemanticNode($headline, \"glp_study\", EntityType.ENTITY).ifPresent(entity -> {\n entity.apply(\"DOC.8.0\", \"GLP Study found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.9.0: Batch number from CoA\"\n when\n $section: Section(\n (\n anyHeadlineContainsString(\"Analytical Report\")\n || anyHeadlineContainsStringIgnoreCase(\"Certificate of Analysis\")\n || containsStringIgnoreCase(\"Certificate of Analysis\")\n )\n && (\n containsStringIgnoreCase(\"batch\")\n || containsStringIgnoreCase(\"bath\")\n || containsStringIgnoreCase(\"barch\")\n || containsStringIgnoreCase(\"bateb\")\n )\n && (\n containsStringIgnoreCase(\"identification\")\n || containsStringIgnoreCase(\"ldentitfication\")\n || containsStringIgnoreCase(\"wentification\")\n || containsStringIgnoreCase(\"mentification\")\n || containsStringIgnoreCase(\"kientification\")\n || containsStringIgnoreCase(\"reference number\")\n || containsStringIgnoreCase(\"test substance\")\n )\n )\n then\n entityCreationService.lineAfterStrings(List.of(\"Batch Identification\",\n \"(Batch Identification):\",\n \"Bateb Identification\",\n \"Batch Wentification\",\n \"Batch Mentification\",\n \"Batch Kientification\",\n \"Barch Identification\",\n \"Bath ldentitfication\",\n \"Batch of test substance :\"), \"batch_number\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.9.0\", \"Batch number found in CoA\", \"n-a\");\n });\n end\n\n\nrule \"DOC.9.1: Batch number\"\n when\n $section: Section(\n (\n anyHeadlineContainsStringIgnoreCase(\"Test Substance\")\n || anyHeadlineContainsStringIgnoreCase(\"Test and Control Substances\")\n || anyHeadlineContainsStringIgnoreCase(\"Test Item\")\n )\n && !(\n anyHeadlineContainsString(\"component\")\n || anyHeadlineContainsString(\"reference\")\n || anyHeadlineContainsString(\"blank\")\n )\n && containsStringIgnoreCase(\"batch\")\n )\n then\n Stream.of(entityCreationService.byRegex(\"Batch ID ([A-Z\\\\d\\\\-]{7,14})\", \"batch_number\", EntityType.ENTITY, 1, $section),\n entityCreationService.lineAfterStrings(List.of(\"Batch Identification\",\n \"Batch number:\",\n \"Batch reference number:\",\n \"Batch:\",\n \"Batch/Lot number:\",\n \"Batch (Lot) Number:\",\n \"Batch Number:\",\n \"Batch Nº:\",\n \"Batch no:\"\n ), \"batch_number\", EntityType.ENTITY, $section)).flatMap(a -> a)\n .forEach(entity -> {\n entity.apply(\"DOC.9.1\", \"Batch number found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.9.2: Batch number\"\n when\n $section: Section(\n (\n anyHeadlineContainsStringIgnoreCase(\"Test Substance\")\n || anyHeadlineContainsStringIgnoreCase(\"Test and Control Substances\")\n || anyHeadlineContainsStringIgnoreCase(\"Test Item\")\n )\n && !(\n anyHeadlineContainsString(\"component\")\n || anyHeadlineContainsString(\"reference\")\n || anyHeadlineContainsString(\"blank\")\n )\n && containsStringIgnoreCase(\"batch\")\n )\n $batchNumber: String() from List.of(\"Batch Identification\",\n \"Batch number:\",\n \"Batch reference number:\",\n \"Batch:\",\n \"Batch/Lot number:\",\n \"Batch (Lot) Number:\",\n \"Batch Number:\",\n \"Batch Nº:\",\n \"Batch no:\")\n $table: Table(containsStringIgnoreCase($batchNumber)) from $section.streamAllSubNodesOfType(NodeType.TABLE).toList()\n then\n entityCreationService.lineAfterStringAcrossColumnsIgnoreCase($batchNumber, \"batch_number\", EntityType.ENTITY, $table).forEach(entity -> {\n entity.apply(\"DOC.9.2\", \"Batch number found\", \"n-a\");\n });\n end\n\n\n\n\nrule \"DOC.10.0: Conclusions - LD50, LC50, Confidence\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"425\",\"436\"))\n $section: Section(\n (getHeadline().containsStringIgnoreCase(\"Conclusion\") || anyHeadlineContainsStringIgnoreCase(\"Lethality\"))\n && (containsString(\"LD\") || containsString(\"LC\") || containsString(\"50\") || containsString(\"LD50\") || containsString(\"lethal concentration\") || containsString(\"lethal dose\"))\n && (\n containsString(\"greater than\")\n || containsString(\"higher than\")\n || containsString(\"above\")\n || containsString(\"in excess\")\n || containsString(\"exceeds\")\n || containsString(\"was found to be\")\n || containsString(\"was calculated to be\")\n || containsString(\"estimated to be\")\n )\n )\n then\n entityCreationService.byRegexIgnoreCase(\"(L[D|C]\\\\s?50|lethal concentration|lethal dose).{1,200}(greater than|considered to be above|in excess of|exceeds|higher than)\", \"ld50_greater\", EntityType.ENTITY,2, $section).forEach(entity -> {\n entity.apply(\"DOC.10.0\", \"LD50 greater than found\", \"n-a\");\n });\n entityCreationService.byRegexIgnoreCase(\"\\\\b(?:(?:greater|higher) than|considered to be above|(?:was|is) (?:found|estimated) to be|was calculated to be|in excess of|exceeds|equal to)\\\\s?([\\\\d\\\\.]{1,6})\\\\s?mg\\\\/(?:kg|L)\", \"ld50_value\", EntityType.ENTITY,1, $section).forEach(entity -> {\n entity.apply(\"DOC.10.0\", \"LD50 value found\", \"n-a\");\n });\n entityCreationService.byRegexIgnoreCase(\"confidence interval (?:is )?([\\\\d\\\\.]{2,6}).{0,20} to (?:greater than )?([\\\\d\\\\.]{2,6})\", \"confidence_minimal\", EntityType.ENTITY,1, $section).forEach(entity -> {\n entity.apply(\"DOC.10.0\", \"Minimal Confidence found\", \"n-a\");\n });\n entityCreationService.byRegexIgnoreCase(\"confidence interval (?:is )?([\\\\d\\\\.]{2,6}).{0,20} to (?:greater than )?([\\\\d\\\\.]{2,6})\", \"confidence_maximal\", EntityType.ENTITY,2, $section).forEach(entity -> {\n entity.apply(\"DOC.10.0\", \"Maximal Confidence found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.11.0: Guideline Deviation\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"404\",\"405\",\"425\",\"429\",\"436\",\"471\"))\n $section: Section(\n (getHeadline().containsStringIgnoreCase(\"General Information\") || containsString(\"GENERAL INFORMATION\"))\n && (containsStringIgnoreCase(\"from the\") || containsStringIgnoreCase(\"to the\"))\n )\n then\n entityCreationService.betweenRegexes(\"(?:Deviations? from the [G|g]uidelines?)(?: and| or)?( the)?(?: Study Plan)?\", \"(?:(?:Deviations? from the Study Plan)|(?:Performing laboratory test)|(?:Other)|(?:Retention of [S|s]amples)|(?:Amendments? to Final Protocol))\", \"guideline_deviation\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.11.0\", \"Deviation from Guidelines found\", \"n-a\");\n });\n entityCreationService.betweenRegexes(\"(?:Deviations? (?:from|to)(?: the)? [S|s]tudy [P|p]lan)\", \"(?:Regulatory Guidelines)|(?:Other)|(?:Distribution of the report)|(?:Performing laboratory test)|(?:Distribution of the report)|(?:Retention of [S|s]amples)\", \"guideline_deviation\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.11.0\", \"Deviation from Study Plan found\", \"n-a\");\n });\n entityCreationService.betweenStrings(\"Deviations from the study plan\", \"Regulatory Guidelines\", \"guideline_deviation\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.11.0\", \"Deviation from the study plan found\", \"n-a\");\n });\n entityCreationService.byRegexIgnoreCase(\"(?>Study plan adherence)(.{1,20}deviations.{1,20} to the study plan.{0,50}\\\\.)\\\\s\", \"guideline_deviation\", EntityType.ENTITY, 1, $section).forEach(entity -> {\n entity.apply(\"DOC.11.0\", \"Guideline deviation found in text.\", \"n-a\");\n });\n entityCreationService.betweenStringsIncludeEnd(\"Deviations from the study plan\", \"validity of the study.\", \"guideline_deviation\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.11.0\", \"Deviation from the study plan found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.11.1: Guideline Deviation in text\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"404\",\"405\",\"425\",\"429\",\"436\",\"471\"))\n $section: Section(\n getHeadline().containsStringIgnoreCase(\"Introduction\")\n && containsStringIgnoreCase(\"deviations from the protocol\")\n )\n then\n entityCreationService.byRegex(\"There were no deviations from the protocol.{1,100}\\\\.\\\\s\", \"guideline_deviation\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.11.1\", \"Guideline deviation found in text.\", \"n-a\");\n });\n end\n\n\nrule \"DOC.12.0: Clinical Signs\"\n when\n FileAttribute(label == \"OECD Number\", value == \"425\")\n $headline: Headline(containsAnyStringIgnoreCase(\"Clinical Signs\", \"Macroscopic Findings\") && !containsString(\"TABLE\") && !getHeadline().containsStringIgnoreCase(\"3 - MACROSCOPIC FINDINGS\"))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"clinical_signs\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.12.0\", \"Clinical Signs found\", \"n-a\"));\n end\n\n\nrule \"DOC.13.0: Dosages\"\n when\n FileAttribute(label == \"OECD Number\", value == \"425\")\n $section: Section(\n (anyHeadlineContainsStringIgnoreCase(\"Dosages\") || anyHeadlineContainsStringIgnoreCase(\"Study Design\"))\n && !getHeadline().containsString(\"TABLE\")\n )\n then\n entityCreationService.betweenStringsIncludeStartAndEnd(\"The animals were treated\", \".\", \"dosages\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.13.0\", \"Dosage found\", \"n-a\");\n });\n entityCreationService.betweenStringsIncludeStartAndEnd(\"Animals were treated\", \".\", \"dosages\", EntityType.ENTITY, $section).forEach(entity -> {\n entity.apply(\"DOC.13.0\", \"Dosage found\", \"n-a\");\n });\n entityCreationService.byRegexWithLineBreaks(\"(?:\\\\.[\\\\s|\\\\n]|^.{5,20}\\\\n)([^\\\\.]{1,200}(?:animal|given|received)[^\\\\.]{1,200}dose\\\\s(?:levels?\\\\s)?(?:of|at)[^\\\\.]{1,200})(?:\\\\.[\\\\s|\\\\n|$])\", \"dosages\", EntityType.ENTITY,1, $section).forEach(entity -> {\n entity.apply(\"DOC.13.0\", \"Dosage found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.14.0: Mortality\"\n when\n $headline: Headline(containsString(\"Mortality\") && !containsString(\"TABLE\"))\n FileAttribute(label == \"OECD Number\", value == \"425\")\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"mortality\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.14.0\", \"Mortality found\", \"n-a\"));\n end\n\n\nrule \"DOC.15.0: Study Conclusion\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"404\",\"405\",\"425\",\"429\",\"436\",\"471\"))\n $section: Section(\n getHeadline().containsStringIgnoreCase(\"Conclusion\")\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"study_conclusion\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.15.0\", \"Study Conclusion found\", \"n-a\"));\n end\n\n\nrule \"DOC.16.0: Weight Behavior Changes\"\n when\n FileAttribute(label == \"OECD Number\", value == \"402\")\n $section: Section(\n getHeadline().containsString(\"Results\")\n && (\n containsString(\"body weight\")\n || containsString(\"body weights\")\n || containsString(\"bodyweight\")\n || containsString(\"bodyweights\")\n )\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"weight_behavior_changes\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.16.0\", \"Weight behavior changes found\", \"n-a\"));\n end\n\n\nrule \"DOC.17.0: Necropsy findings\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"403\",\"436\"))\n $section: Section(\n (\n anyHeadlineContainsStringIgnoreCase(\"Necropsy\")\n || getHeadline().containsStringIgnoreCase(\"Macroscopic Findings\")\n || getHeadline().containsStringIgnoreCase(\"Macroscopic examination\")\n )\n && !getHeadline().containsStringIgnoreCase(\"Table\")\n && !getHeadline().containsStringIgnoreCase(\"Appendix\")\n && !getHeadline().containsStringIgnoreCase(\"3 - MACROSCOPIC FINDINGS\")\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"necropsy_findings\", EntityType.ENTITY)\n .forEach( entity -> entity.apply(\"DOC.17.0\", \"Necropsy section found\", \"n-a\"));\n end\n\n\nrule \"DOC.18.0: Clinical observations\"\n when\n FileAttribute(label == \"OECD Number\", value == \"403\")\n $section: Section(\n (\n anyHeadlineContainsStringIgnoreCase(\"Clinical Observations\")\n || anyHeadlineContainsStringIgnoreCase(\"Clinical observations\")\n || anyHeadlineContainsStringIgnoreCase(\"In-life Observations\")\n || anyHeadlineContainsStringIgnoreCase(\"Postmortem Observations\")\n )\n && !anyHeadlineContainsStringIgnoreCase(\"Appendix\")\n && !anyHeadlineContainsStringIgnoreCase(\"Table\")\n && !anyHeadlineContainsStringIgnoreCase(\"Mortality\")\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"clinical_observations\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.18.0\", \"Clinical observations section found\", \"n-a\"));\n end\n\n\nrule \"DOC.19.0: Bodyweight changes\"\n when\n FileAttribute(label == \"OECD Number\", value == \"403\")\n $headline: Headline(containsAnyStringIgnoreCase(\"Bodyweight\", \"Bodyweights\", \"Body Weights\", \"Body Weight\"), !containsAnyStringIgnoreCase(\"Appendix\", \"TABLE\"))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"bodyweight_changes\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.19.0\", \"Bodyweight section found\", \"n-a\"));\n end\n\n\nrule \"DOC.20.0: Study Design\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"402\",\"404\",\"405\",\"406\",\"428\",\"429\",\"438\",\"439\",\"474\",\"487\"))\n $section: Section(\n anyHeadlineContainsStringIgnoreCase(\"study design\")\n && !anyHeadlineContainsString(\"Preliminary screening test\")\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"study_design\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.20.0\", \"Study design section found\", \"n-a\"));\n end\n\n\nrule \"DOC.20.1: Study Design\"\n when\n Headline(containsStringIgnoreCase(\"Study Design\"), $sectionIdentifier: getSectionIdentifier())\n $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifier))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"study_design\", EntityType.ENTITY)\n .forEach(entity -> {\n entity.apply(\"DOC.20.1\", \"Study design section found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.21.0: Results and Conclusion (406, 428, 438, 439, 474 & 487)\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"406\",\"428\",\"438\",\"439\",\"474\",\"487\"))\n $parentHeadline: Headline(\n containsAnyString(\"Results\", \"Conclusion\"),\n !containsAnyString(\"POSITIVE CONTROL\", \"Positive Control\", \"Evaluation\", \"Micronucleus\", \"TABLE\", \"DISCUSSION\", \"CONCLUSIONS\", \"Interpretation\",\"Viability\", \"analysis\"),\n $sectionIdentifier: getSectionIdentifier()\n )\n not Headline(getSectionIdentifier().isChildOf($sectionIdentifier))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($parentHeadline.getParent(), \"results_and_conclusion\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.21.0\", \"Results and Conclusion found\", \"n-a\"));\n end\n\n\nrule \"DOC.21.1: Results and Conclusion (406, 428, 438, 439, 474 & 487)\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"406\",\"428\",\"438\",\"439\",\"474\",\"487\"))\n Headline(\n containsAnyString(\"Results\", \"Conclusion\"),\n !containsAnyString(\"POSITIVE CONTROL\", \"Positive Control\", \"Evaluation\", \"Micronucleus\", \"TABLE\", \"DISCUSSION\", \"CONCLUSIONS\", \"Interpretation\",\"Viability\", \"analysis\"),\n $sectionIdentifier: getSectionIdentifier()\n )\n $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifier))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"results_and_conclusion\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.21.1\", \"Results and Conclusion found\", \"n-a\"));\n end\n\n\nrule \"DOC.22.0: Detailing (404 & 405)\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"404\",\"405\"))\n $section: Section(\n anyHeadlineContainsStringIgnoreCase(\"Results\")\n && !getHeadline().containsStringIgnoreCase(\"Evaluation\")\n && !getHeadline().containsStringIgnoreCase(\"study\")\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"detailing\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.22.0\", \"Detailing found\", \"n-a\"));\n end\n\n\nrule \"DOC.23.0: Preliminary Test Results (429)\"\n when\n FileAttribute(label == \"OECD Number\", value == \"429\")\n $section: Section(\n ((anyHeadlineContainsString(\"Preliminary Screening Test\") && containsString(\"Clinical observations\"))\n || anyHeadlineContainsString(\"Pre-Experiment\"))\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"preliminary_test_results\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.23.0\", \"Preliminary Test Results found\", \"n-a\"));\n end\n\n\nrule \"DOC.24.0: Test Results (429)\"\n when\n FileAttribute(label == \"OECD Number\", value == \"429\")\n $section: Section((getHeadline().containsString(\"RESULTS AND DISCUSSION\") || getHeadline().containsString(\"Estimation of the proliferative response of lymph node cells\") || getHeadline().containsString(\"Results in the Main Experiment\")))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"test_results\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.24.0\", \"Test Results found\", \"n-a\"));\n end\n\n\nrule \"DOC.24.1: Test Results (429)\"\n when\n Headline(containsStringIgnoreCase(\"RESULTS AND DISCUSSION\"), $sectionIdentifierResultsAndDiscussion: getSectionIdentifier())\n $headline: Headline(getSectionIdentifier().isChildOf($sectionIdentifierResultsAndDiscussion))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"test_results\", EntityType.ENTITY)\n .forEach(entity -> {\n entity.apply(\"DOC.24.1\", \"Test Results found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.25.0: Approach used (429)\"\n when\n FileAttribute(label == \"OECD Number\", value == \"429\")\n $section: Section(\n hasEntitiesOfType(\"species\")\n && (containsStringIgnoreCase(\"animals per\") || containsStringIgnoreCase(\"animals /\"))\n )\n then\n entityCreationService.byRegexIgnoreCase(\"\\\\banimals (?:per|\\\\/) .{0,15}(group)\\\\b\", \"approach_used\", EntityType.ENTITY,1, $section).forEach(entity -> {\n entity.apply(\"DOC.25.0\", \"Study animal approach found.\", \"n-a\");\n });\n end\n\n\nrule \"DOC.26.0: Sex\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"405\",\"429\"))\n $section: Section(\n (\n anyHeadlineContainsStringIgnoreCase(\"animal\")\n || anyHeadlineContainsStringIgnoreCase(\"test system\")\n )\n && !getHeadline().containsStringIgnoreCase(\"selection\")\n && (\n containsStringIgnoreCase(\"sex:\")\n || containsStringIgnoreCase(\"male\")\n || containsStringIgnoreCase(\"female\")\n )\n )\n then\n entityCreationService.byRegexIgnoreCase(\"([S|s]ex:)?[\\\\w\\\\s]{0,10}\\\\b(males?|females?)\\\\b\", \"sex\", EntityType.ENTITY,2, $section).forEach(entity -> {\n entity.apply(\"DOC.26.0\", \"Test animal sex found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.27.0: Animal Number 405\"\n when\n FileAttribute(label == \"OECD Number\", value == \"405\")\n $section: Section(\n (\n anyHeadlineContainsStringIgnoreCase(\"animal\")\n || anyHeadlineContainsStringIgnoreCase(\"test system\")\n || anyHeadlineContainsStringIgnoreCase(\"reaction\")\n )\n && !getHeadline().containsString(\"selection\")\n && (\n containsStringIgnoreCase(\"number of animals\")\n || containsStringIgnoreCase(\"no.\")\n )\n )\n then\n entityCreationService.byRegexIgnoreCase(\"(Number of animals:)[\\\\w\\\\s]{0,10}\\\\b([\\\\d]{1,3})\\\\b\", \"number_of_animals\", EntityType.ENTITY,2, $section).forEach(entity -> {\n entity.apply(\"DOC.27.0\", \"Number of animals found\", \"n-a\");\n });\n entityCreationService.byRegexIgnoreCase(\"(?:.{1,10} No\\\\. )([\\\\d\\\\w\\\\-]{3,8})\", \"animal_numbers\", EntityType.ENTITY,1, $section).forEach(entity -> {\n entity.apply(\"DOC.27.0\", \"Number of animals found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.28.0: Animal Number 429\"\n when\n FileAttribute(label == \"OECD Number\", value == \"429\")\n $section: Section(\n (\n anyHeadlineContainsStringIgnoreCase(\"animal\")\n || anyHeadlineContainsStringIgnoreCase(\"test system\")\n )\n && !getHeadline().containsString(\"selection\")\n && containsStringIgnoreCase(\"number of animals\")\n && (containsStringIgnoreCase(\"per\") || containsString(\"/\"))\n && containsStringIgnoreCase(\"group\")\n )\n then\n entityCreationService.byRegexIgnoreCase(\"(Number of animals per group:)[\\\\w\\\\s]{0,10}\\\\b([\\\\d]{1,3})\\\\b\", \"number_of_animals\", EntityType.ENTITY,2, $section).forEach(entity -> {\n entity.apply(\"DOC.28.0\", \"Number of animals in group found\", \"n-a\");\n });\n entityCreationService.byRegexIgnoreCase(\"(Number of animals per group:).{0,60}\\\\b([\\\\d]{1,3})\\\\sper group\\\\b\", \"number_of_animals\", EntityType.ENTITY,2, $section).forEach(entity -> {\n entity.apply(\"DOC.28.0\", \"Number of animals in group found\", \"n-a\");\n });\n entityCreationService.byRegexIgnoreCase(\"([\\\\d]{1,3})[\\\\w\\\\s\\\\/]{0,20}(?:treatment )?group\\\\b\", \"number_of_animals\", EntityType.ENTITY,1 , $section).forEach(entity -> {\n entity.apply(\"DOC.28.0\", \"Number of animals in group found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.28.1: No. Of animals - Fallback to appendix tables listing all individual animals for 429\"\n when\n $keyword: String() from List.of(\"Animal Number\", \"Animal No.\", \"Animal number\")\n $table: Table(containsString($keyword) && getHeadline().containsString(\"TABLE\") && getHeadline().containsString(\"Individual\"))\n FileAttribute(label == \"OECD Number\", value == \"429\")\n then\n $table.streamTableCellsWithHeader($keyword)\n .map(tableCell -> entityCreationService.bySemanticNode(tableCell, \"animal_numbers\", EntityType.ENTITY))\n .filter(Optional::isPresent)\n .map(Optional::get)\n .forEach(entity -> {\n entity.apply(\"DOC.28.1\", \"Animal number found.\", \"n-a\");\n insert(entity);\n });\n end\n\n\nrule \"DOC.29.0: 4h Exposure\"\n when\n FileAttribute(label == \"OECD Number\", valueEqualsAnyOf(\"403\",\"436\"))\n $section: Section(\n (containsStringIgnoreCase(\"4 hours\") || containsStringIgnoreCase(\"four hours\"))\n )\n then\n entityCreationService.byRegexIgnoreCase(\"((?<=\\\\.\\\\s\\\\b).{1,100}(4|four) hours.*?\\\\.) \", \"4h_exposure\", EntityType.ENTITY,1, $section).forEach(entity -> {\n entity.apply(\"DOC.29.0\", \"4h exposure sentence found\", \"n-a\");\n });\n end\n\n\nrule \"DOC.30.0: Dilution of the test substance\"\n when\n FileAttribute(label == \"OECD Number\", value == \"404\")\n $section: Section(\n getHeadline().containsString(\"Formulation\")\n && containsString(\"dilution\")\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"dilution\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.30.0\", \"Dilution found.\", \"n-a\"));\n end\n\n\nrule \"DOC.31.0: Positive Control\"\n when\n FileAttribute(label == \"OECD Number\", value == \"429\")\n $section: Section(\n getHeadline().containsStringIgnoreCase(\"Positive Control\")\n && !(getHeadline().containsStringIgnoreCase(\"Appendix\") || getHeadline().containsStringIgnoreCase(\"Table\"))\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"positive_control\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.31.0\", \"Positive control found.\", \"n-a\"));\n end\n\n\nrule \"DOC.32.0: Mortality Statement\"\n when\n FileAttribute(label == \"OECD Number\", value == \"402\")\n $headline: Headline(containsStringIgnoreCase(\"Mortality\") && !containsString(\"TABLE\"))\n then\n entityCreationService.bySemanticNodeParagraphsOnly($headline.getParent(), \"mortality_statement\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.32.0\", \"Mortality Statement found\", \"n-a\"));\n end\n\n\nrule \"DOC.33.0: Dose Mortality\"\n when\n FileAttribute(label == \"OECD Number\", value == \"425\")\n $table: Table(\n (hasHeader(\"Mortality\") || hasHeader(\"Long Term Results\") || hasHeader(\"LongTerm Outcome\") || hasHeader(\"Long Term Outcome\") || hasHeader(\"Comments\") || hasHeader(\"Viability / Mortality\") || hasHeader(\"Viability/Mortality\"))\n &&\n (hasHeader(\"Dose [mg/kg bodyweight]\") || hasHeader(\"Dose [mg/kg body weight]\") ||hasHeader(\"Dose (mg/kg)\") || hasHeader(\"Dose levei (mg/kg)\") || hasHeader(\"Dose Level (mg/kg)\") || hasHeader(\"Dose level (mg/kg)\") || hasHeader(\"Dosage [mg/kg body weight]\"))\n )\n then\n Stream.of($table.streamTableCellsWithHeader(\"Mortality\"),\n $table.streamTableCellsWithHeader(\"Comments\"),\n $table.streamTableCellsWithHeader(\"Long Term Results\"),\n $table.streamTableCellsWithHeader(\"Long Term Outcome\"),\n $table.streamTableCellsWithHeader(\"LongTerm Outcome\"),\n $table.streamTableCellsWithHeader(\"Viability / Mortality\"),\n $table.streamTableCellsWithHeader(\"Viability/Mortality\")\n ).flatMap(a -> a)\n .map(tableCell -> entityCreationService.bySemanticNode(tableCell, \"dose_mortality\", EntityType.ENTITY))\n .filter(Optional::isPresent)\n .map(Optional::get)\n .forEach(entity -> {\n entity.apply(\"DOC.33.0\", \"Dose Mortality Data found.\", \"n-a\");\n insert(entity);\n });\n\n Stream.of($table.streamTableCellsWithHeader(\"Dose [mg/kg bodyweight]\"),\n $table.streamTableCellsWithHeader(\"Dose [mg/kg body weight]\"),\n $table.streamTableCellsWithHeader(\"Dose levei (mg/kg)\"),\n $table.streamTableCellsWithHeader(\"Dose Level (mg/kg)\"),\n $table.streamTableCellsWithHeader(\"Dose level (mg/kg)\"),\n $table.streamTableCellsWithHeader(\"Dose (mg/kg)\"),\n $table.streamTableCellsWithHeader(\"Dosage [mg/kg body weight]\")\n ).flatMap(a -> a)\n .map(tableCell -> entityCreationService.bySemanticNode(tableCell, \"dose_mortality_dose\", EntityType.ENTITY))\n .filter(Optional::isPresent)\n .map(Optional::get)\n .forEach(entity -> {\n entity.apply(\"DOC.33.0\", \"Dose Mortality Data found.\", \"n-a\");\n insert(entity);\n });\n end\n\n\nrule \"DOC.34.0: Results (Main Study)\"\n when\n FileAttribute(label == \"OECD Number\", value == \"429\")\n $section: Section(\n getHeadline().containsString(\"Results\")\n && getHeadline().getTextRange().length() < 20\n && !(getHeadline().containsString(\"Appendix\") || getHeadline().containsString(\"Table\"))\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"results_(main_study)\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.34.0\", \"Results for main study found.\", \"n-a\"));\n end\n\n\nrule \"DOC.35.0: Doses (mg/kg bodyweight)\"\n when\n FileAttribute(label == \"OECD Number\", value == \"402\")\n $section: Section(\n anyHeadlineContainsStringIgnoreCase(\"study design\")\n )\n then\n entityCreationService.bySemanticNodeParagraphsOnly($section, \"doses_(mg_kg_bw)\", EntityType.ENTITY)\n .forEach(entity -> entity.apply(\"DOC.35.0\", \"Doses per bodyweight information found\", \"n-a\"));\n end\n\n//------------------------------------ Manual redaction rules ------------------------------------\n\n// Rule unit: MAN.0\nrule \"MAN.0.0: Apply manual resize redaction\"\n salience 128\n when\n $resizeRedaction: ManualResizeRedaction($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate))\n $entityToBeResized: TextEntity(matchesAnnotationId($id))\n then\n manualChangesApplicationService.resizeEntityAndReinsert($entityToBeResized, $resizeRedaction);\n retract($resizeRedaction);\n update($entityToBeResized);\n $entityToBeResized.getIntersectingNodes().forEach(node -> update(node));\n end\n\nrule \"MAN.0.1: Apply manual resize redaction\"\n salience 128\n when\n $resizeRedaction: ManualResizeRedaction($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualResizeRedaction(annotationId == $id, requestDate.isBefore($requestDate))\n $imageToBeResized: Image(id == $id)\n then\n manualChangesApplicationService.resizeImage($imageToBeResized, $resizeRedaction);\n retract($resizeRedaction);\n update($imageToBeResized);\n update($imageToBeResized.getParent());\n end\n\n\n// Rule unit: MAN.1\nrule \"MAN.1.0: Apply id removals that are valid and not in forced redactions to Entity\"\n salience 128\n when\n $idRemoval: IdRemoval($id: annotationId, status == AnnotationStatus.APPROVED)\n $entityToBeRemoved: TextEntity(matchesAnnotationId($id))\n then\n $entityToBeRemoved.getManualOverwrite().addChange($idRemoval);\n update($entityToBeRemoved);\n retract($idRemoval);\n $entityToBeRemoved.getIntersectingNodes().forEach(node -> update(node));\n end\n\nrule \"MAN.1.1: Apply id removals that are valid and not in forced redactions to Image\"\n salience 128\n when\n $idRemoval: IdRemoval($id: annotationId, status == AnnotationStatus.APPROVED)\n $imageEntityToBeRemoved: Image($id == id)\n then\n $imageEntityToBeRemoved.getManualOverwrite().addChange($idRemoval);\n update($imageEntityToBeRemoved);\n retract($idRemoval);\n update($imageEntityToBeRemoved.getParent());\n end\n\n\n// Rule unit: MAN.2\nrule \"MAN.2.0: Apply force redaction\"\n salience 128\n when\n $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED)\n $entityToForce: TextEntity(matchesAnnotationId($id))\n then\n $entityToForce.getManualOverwrite().addChange($force);\n update($entityToForce);\n $entityToForce.getIntersectingNodes().forEach(node -> update(node));\n retract($force);\n end\n\nrule \"MAN.2.1: Apply force redaction to images\"\n salience 128\n when\n $force: ManualForceRedaction($id: annotationId, status == AnnotationStatus.APPROVED)\n $imageToForce: Image(id == $id)\n then\n $imageToForce.getManualOverwrite().addChange($force);\n update($imageToForce);\n update($imageToForce.getParent());\n retract($force);\n end\n\n\n// Rule unit: MAN.3\nrule \"MAN.3.0: Apply entity recategorization\"\n salience 128\n when\n $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))\n $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type != $type)\n then\n $entityToBeRecategorized.getIntersectingNodes().forEach(node -> update(node));\n manualChangesApplicationService.recategorize($entityToBeRecategorized, $recategorization);\n retract($recategorization);\n // Entity is copied and inserted, so the old entity needs to be retracted to avoid duplication.\n retract($entityToBeRecategorized);\n end\n\nrule \"MAN.3.1: Apply entity recategorization of same type\"\n salience 128\n when\n $recategorization: ManualRecategorization($id: annotationId, $type: type, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))\n $entityToBeRecategorized: TextEntity(matchesAnnotationId($id), type == $type)\n then\n $entityToBeRecategorized.getManualOverwrite().addChange($recategorization);\n retract($recategorization);\n end\n\nrule \"MAN.3.2: Apply image recategorization\"\n salience 128\n when\n $recategorization: ManualRecategorization($id: annotationId, status == AnnotationStatus.APPROVED, $requestDate: requestDate)\n not ManualRecategorization($id == annotationId, requestDate.isBefore($requestDate))\n $imageToBeRecategorized: Image($id == id)\n then\n manualChangesApplicationService.recategorize($imageToBeRecategorized, $recategorization);\n update($imageToBeRecategorized);\n update($imageToBeRecategorized.getParent());\n retract($recategorization);\n end\n\n\n// Rule unit: MAN.4\nrule \"MAN.4.0: Apply legal basis change\"\n salience 128\n when\n $legalbasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED)\n $imageToBeRecategorized: Image($id == id)\n then\n $imageToBeRecategorized.getManualOverwrite().addChange($legalbasisChange);\n end\n\nrule \"MAN.4.1: Apply legal basis change\"\n salience 128\n when\n $legalBasisChange: ManualLegalBasisChange($id: annotationId, status == AnnotationStatus.APPROVED)\n $entityToBeChanged: TextEntity(matchesAnnotationId($id))\n then\n $entityToBeChanged.getManualOverwrite().addChange($legalBasisChange);\n end\n\n\n\n//------------------------------------ Entity merging rules ------------------------------------\n\n// Rule unit: X.0\nrule \"X.0.0: remove Entity contained by Entity of same type\"\n salience 65\n when\n $larger: TextEntity($type: type, $entityType: entityType, active())\n $contained: TextEntity(containedBy($larger), type == $type, entityType == $entityType, this != $larger, !hasManualChanges(), active())\n then\n $contained.remove(\"X.0.0\", \"remove Entity contained by Entity of same type\");\n retract($contained);\n end\n\n\n// Rule unit: X.2\nrule \"X.2.0: remove Entity of type ENTITY when contained by FALSE_POSITIVE\"\n salience 64\n when\n $falsePositive: TextEntity($type: type, entityType == EntityType.FALSE_POSITIVE, active())\n $entity: TextEntity(containedBy($falsePositive), type == $type, entityType == EntityType.ENTITY, !hasManualChanges(), active())\n then\n $entity.getIntersectingNodes().forEach(node -> update(node));\n $entity.remove(\"X.2.0\", \"remove Entity of type ENTITY when contained by FALSE_POSITIVE\");\n retract($entity)\n end\n\n\n// Rule unit: X.3\nrule \"X.3.0: remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION\"\n salience 64\n when\n $falseRecommendation: TextEntity($type: type, entityType == EntityType.FALSE_RECOMMENDATION, active())\n $recommendation: TextEntity(containedBy($falseRecommendation), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active())\n then\n $recommendation.remove(\"X.3.0\", \"remove Entity of type RECOMMENDATION when contained by FALSE_RECOMMENDATION\");\n retract($recommendation);\n end\n\n\n// Rule unit: X.4\nrule \"X.4.0: remove Entity of type RECOMMENDATION when intersected by ENTITY with same type\"\n salience 256\n when\n $entity: TextEntity($type: type, entityType == EntityType.ENTITY, active())\n $recommendation: TextEntity(intersects($entity), type == $type, entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active())\n then\n $entity.addEngines($recommendation.getEngines());\n $recommendation.remove(\"X.4.0\", \"remove Entity of type RECOMMENDATION when intersected by ENTITY with same type\");\n retract($recommendation);\n end\n\n\n// Rule unit: X.5\nrule \"X.5.0: remove Entity of type RECOMMENDATION when contained by ENTITY\"\n salience 256\n when\n $entity: TextEntity(entityType == EntityType.ENTITY, active())\n $recommendation: TextEntity(containedBy($entity), entityType == EntityType.RECOMMENDATION, !hasManualChanges(), active())\n then\n $recommendation.remove(\"X.5.0\", \"remove Entity of type RECOMMENDATION when contained by ENTITY\");\n retract($recommendation);\n end\n\n\n// Rule unit: X.7\nrule \"X.7.0: remove all images\"\n salience 512\n when\n $image: Image(imageType != ImageType.OCR, !hasManualChanges())\n then\n $image.remove(\"X.7.0\", \"remove all images\");\n retract($image);\n end\n\n\n//------------------------------------ File attributes rules ------------------------------------\n\n// Rule unit: FA.1\nrule \"FA.1.0: remove duplicate FileAttributes\"\n\n salience 64\n when\n $fileAttribute: FileAttribute($label: label, $value: value)\n $duplicate: FileAttribute(this != $fileAttribute, label == $label, value == $value)\n then\n retract($duplicate);\n end\n\n\n// Rule unit: LDS.0\nrule \"LDS.0.0: run local dictionary search\"\n agenda-group \"LOCAL_DICTIONARY_ADDS\"\n salience -999\n when\n $dictionaryModel: DictionaryModel(!localEntriesWithMatchedRules.isEmpty()) from dictionary.getDictionaryModels()\n then\n entityCreationService.bySearchImplementation($dictionaryModel.getLocalSearch(), $dictionaryModel.getType(), EntityType.RECOMMENDATION, document)\n .forEach(entity -> {\n Collection matchedRules = $dictionaryModel.getLocalEntriesWithMatchedRules().get(entity.getValue());\n entity.addMatchedRules(matchedRules);\n });\n end\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_Regulation_2021/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_Regulation_2021/rules.txt new file mode 100644 index 00000000..b5c5f258 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_Regulation_2021/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"34: 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\n\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_GFL_v1/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_GFL_v1/rules.txt new file mode 100644 index 00000000..64992263 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_GFL_v1/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n/* Syngenta specific laboratory recommendation */\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"33: 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\n\n\nrule \"34: Ignore dossier_redaction entries if confidentiality is not 'confidential'\"\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_GFL_v1_adress_parts/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_GFL_v1_adress_parts/rules.txt new file mode 100644 index 00000000..df4c7799 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_GFL_v1_adress_parts/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"0: Add CBI_author from ai 2\"\n when\n Section(aiMatchesType(\"STREET\"))\n then\n section.addAiEntities(\"STREET\", \"street\");\n end\n\nrule \"0: Add CBI_author from ai 3\"\n when\n Section(aiMatchesType(\"POSTAL\"))\n then\n section.addAiEntities(\"POSTAL\", \"postal\");\n end\n\nrule \"0: Add CBI_author from ai 4\"\n when\n Section(aiMatchesType(\"COUNTRY\"))\n then\n section.addAiEntities(\"COUNTRY\", \"country\");\n end\n\nrule \"0: Add CBI_author from ai 5\"\n when\n Section(aiMatchesType(\"CARDINAL\"))\n then\n section.addAiEntities(\"CARDINAL\", \"cardinal\");\n end\n\nrule \"0: Add CBI_author from ai 6\"\n when\n Section(aiMatchesType(\"CITY\"))\n then\n section.addAiEntities(\"CITY\", \"city\");\n end\n\nrule \"0: Add CBI_author from ai 7\"\n when\n Section(aiMatchesType(\"STATE\"))\n then\n section.addAiEntities(\"STATE\", \"state\");\n end\n\nrule \"0: Add CBI_author from ai 8\"\n when\n Section(aiMatchesType(\"ORG\"))\n then\n section.addAiEntities(\"ORG\", \"org\");\n end\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"16: 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\n\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_pre_GFL_v1/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_pre_GFL_v1/rules.txt new file mode 100644 index 00000000..1377a588 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/EFSA_sanitisation_pre_GFL_v1/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n/*************************************/\n/* Syngenta specific laboratory rule */\nrule \"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\n/*************************************/\n\nrule \"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\n\n\nrule \"14: 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\n\n\nrule \"15: 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\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"25: 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\n\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"29: Ignore dossier_redaction unless confidential\"\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/Manual_Redaction/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/Manual_Redaction/rules.txt new file mode 100644 index 00000000..4568fb38 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/dev/Manual_Redaction/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --- NO RULES HERE --- MANUAL REDACTION ONLY --- //\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/docu/EFSA_Regulation_2021/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/docu/EFSA_Regulation_2021/rules.txt new file mode 100644 index 00000000..e69de29b diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/docu/EFSA_sanitisation_GFL_v1/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/docu/EFSA_sanitisation_GFL_v1/rules.txt new file mode 100644 index 00000000..e69de29b diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/docu/EFSA_sanitisation_pre_GFL_v1/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/docu/EFSA_sanitisation_pre_GFL_v1/rules.txt new file mode 100644 index 00000000..e69de29b diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/docu/Manual_Redaction/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/docu/Manual_Redaction/rules.txt new file mode 100644 index 00000000..e69de29b diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_Regulation_2021/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_Regulation_2021/rules.txt new file mode 100644 index 00000000..d0813054 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_Regulation_2021/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"33: 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\n\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_Regulation_2022/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_Regulation_2022/rules.txt new file mode 100644 index 00000000..b5c5f258 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_Regulation_2022/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"34: 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\n\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_acceptance_test/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_acceptance_test/rules.txt new file mode 100644 index 00000000..64992263 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_acceptance_test/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n/* Syngenta specific laboratory recommendation */\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"33: 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\n\n\nrule \"34: Ignore dossier_redaction entries if confidentiality is not 'confidential'\"\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_function_tests/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_function_tests/rules.txt new file mode 100644 index 00000000..64992263 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_function_tests/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n/* Syngenta specific laboratory recommendation */\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"33: 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\n\n\nrule \"34: Ignore dossier_redaction entries if confidentiality is not 'confidential'\"\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_rule_test/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_rule_test/rules.txt new file mode 100644 index 00000000..c3888e74 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_rule_test/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\nrule \"1: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"7: 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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\nrule \"50: 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\n\n\nrule \"51: 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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_GFL_v1/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_GFL_v1/rules.txt new file mode 100644 index 00000000..64992263 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_GFL_v1/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n/* Syngenta specific laboratory recommendation */\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"33: 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\n\n\nrule \"34: Ignore dossier_redaction entries if confidentiality is not 'confidential'\"\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_GFL_v1_address_parts/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_GFL_v1_address_parts/rules.txt new file mode 100644 index 00000000..df4c7799 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_GFL_v1_address_parts/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"0: Add CBI_author from ai 2\"\n when\n Section(aiMatchesType(\"STREET\"))\n then\n section.addAiEntities(\"STREET\", \"street\");\n end\n\nrule \"0: Add CBI_author from ai 3\"\n when\n Section(aiMatchesType(\"POSTAL\"))\n then\n section.addAiEntities(\"POSTAL\", \"postal\");\n end\n\nrule \"0: Add CBI_author from ai 4\"\n when\n Section(aiMatchesType(\"COUNTRY\"))\n then\n section.addAiEntities(\"COUNTRY\", \"country\");\n end\n\nrule \"0: Add CBI_author from ai 5\"\n when\n Section(aiMatchesType(\"CARDINAL\"))\n then\n section.addAiEntities(\"CARDINAL\", \"cardinal\");\n end\n\nrule \"0: Add CBI_author from ai 6\"\n when\n Section(aiMatchesType(\"CITY\"))\n then\n section.addAiEntities(\"CITY\", \"city\");\n end\n\nrule \"0: Add CBI_author from ai 7\"\n when\n Section(aiMatchesType(\"STATE\"))\n then\n section.addAiEntities(\"STATE\", \"state\");\n end\n\nrule \"0: Add CBI_author from ai 8\"\n when\n Section(aiMatchesType(\"ORG\"))\n then\n section.addAiEntities(\"ORG\", \"org\");\n end\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"16: 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\n\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_pre_GFL_v1/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_pre_GFL_v1/rules.txt new file mode 100644 index 00000000..1377a588 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_sanitisation_pre_GFL_v1/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n/*************************************/\n/* Syngenta specific laboratory rule */\nrule \"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\n/*************************************/\n\nrule \"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\n\n\nrule \"14: 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\n\n\nrule \"15: 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\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"25: 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\n\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"29: Ignore dossier_redaction unless confidential\"\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_security_test/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_security_test/rules.txt new file mode 100644 index 00000000..64992263 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_security_test/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n/* Syngenta specific laboratory recommendation */\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"33: 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\n\n\nrule \"34: Ignore dossier_redaction entries if confidentiality is not 'confidential'\"\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_smoke_tests/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_smoke_tests/rules.txt new file mode 100644 index 00000000..64992263 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/EFSA_smoke_tests/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n/* Syngenta specific laboratory recommendation */\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"33: 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\n\n\nrule \"34: Ignore dossier_redaction entries if confidentiality is not 'confidential'\"\n when\n Section(!fileAttributeByLabelEqualsIgnoreCase(\"Confidentiality\",\"confidential\") && matchesType(\"dossier_redaction\"));\n then\n section.ignore(\"dossier_redaction\");\n end\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/Manual_Redaction/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/Manual_Redaction/rules.txt new file mode 100644 index 00000000..4568fb38 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/Manual_Redaction/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --- NO RULES HERE --- MANUAL REDACTION ONLY --- //\n" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/PII/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/PII/rules.txt new file mode 100644 index 00000000..1d865f18 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/PII/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"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" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/case_insensitive/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/case_insensitive/rules.txt new file mode 100644 index 00000000..7221fc0a --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/case_insensitive/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/no_redaction_indicator/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/no_redaction_indicator/rules.txt new file mode 100644 index 00000000..1d865f18 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/no_redaction_indicator/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n\nrule \"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\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"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" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/vertebrate_only/rules.txt b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/vertebrate_only/rules.txt new file mode 100644 index 00000000..7221fc0a --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/dossier_templates_v2/qa/vertebrate_only/rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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" \ No newline at end of file diff --git a/redaction-service-v1/rules-management/src/test/resources/efsa_regulation_rules.txt b/redaction-service-v1/rules-management/src/test/resources/efsa_regulation_rules.txt new file mode 100644 index 00000000..b5c5f258 --- /dev/null +++ b/redaction-service-v1/rules-management/src/test/resources/efsa_regulation_rules.txt @@ -0,0 +1 @@ +"package drools\n\nimport com.iqser.red.service.redaction.v1.server.redaction.model.Section\n\nglobal Section section\n\n\n// --------------------------------------- AI rules -------------------------------------------------------------------\n\nrule \"0: Add CBI_author from ai\"\n when\n Section(aiMatchesType(\"CBI_author\"))\n then\n section.addAiEntities(\"CBI_author\", \"CBI_author\");\n end\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- CBI rules -------------------------------------------------------------------\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"5: 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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"16: 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\n\nrule \"17: 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\n\n\nrule \"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\n\n\n// --------------------------------------- PII rules -------------------------------------------------------------------\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n\nrule \"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\n\nrule \"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\n\n\n// --------------------------------------- other rules -------------------------------------------------------------------\n\nrule \"34: 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\n\n\n\nrule \"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\n\nrule \"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\n\n\nrule \"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\n" \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 032804b9..4fd3292f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,7 @@ rootProject.name = "redaction-service" include(":redaction-service-api-v1") include(":redaction-service-server-v1") +include(":rules-management") project(":redaction-service-api-v1").projectDir = file("redaction-service-v1/redaction-service-api-v1") project(":redaction-service-server-v1").projectDir = file("redaction-service-v1/redaction-service-server-v1") +project(":rules-management").projectDir = file("redaction-service-v1/rules-management")