From 5f67efc8c7b60a62a455f84405df382110ea1af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kilian=20Sch=C3=BCttler?= Date: Fri, 23 Feb 2024 14:30:48 +0100 Subject: [PATCH] hotfix: streamEntitiesWhereRowContainsEntityOfType is broken --- .../build.gradle.kts | 3 +- .../v1/server/model/document/nodes/Table.java | 117 +++++++++---- .../v1/server/document/graph/TableTest.java | 162 ++++++++++++++++++ .../utils/EntityVisualizationUtility.java | 61 +++++++ .../utils/LayoutParsingRequestProvider.java | 1 + .../drools/all_redact_manager_rules.drl | 67 ++++---- .../src/test/resources/drools/rules.drl | 67 ++++---- .../files/Minimal Examples/BasicTable.pdf | Bin 0 -> 19603 bytes .../resources/all_redact_manager_rules.drl | 68 ++++---- 9 files changed, 405 insertions(+), 141 deletions(-) create mode 100644 redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/document/graph/TableTest.java create mode 100644 redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/utils/EntityVisualizationUtility.java create mode 100644 redaction-service-v1/redaction-service-server-v1/src/test/resources/files/Minimal Examples/BasicTable.pdf diff --git a/redaction-service-v1/redaction-service-server-v1/build.gradle.kts b/redaction-service-v1/redaction-service-server-v1/build.gradle.kts index 9f605928..c8582faa 100644 --- a/redaction-service-v1/redaction-service-server-v1/build.gradle.kts +++ b/redaction-service-v1/redaction-service-server-v1/build.gradle.kts @@ -12,7 +12,7 @@ plugins { description = "redaction-service-server-v1" -val layoutParserVersion = "0.86.0" +val layoutParserVersion = "0.91.0" val jacksonVersion = "2.15.2" val droolsVersion = "9.44.0.Final" val pdfBoxVersion = "3.0.0" @@ -67,6 +67,7 @@ dependencies { testImplementation("org.apache.pdfbox:pdfbox-tools:${pdfBoxVersion}") testImplementation("org.springframework.boot:spring-boot-starter-test:${springBootStarterVersion}") + testImplementation("com.knecon.fforesight:viewer-doc-processor:${layoutParserVersion}") testImplementation("com.knecon.fforesight:layoutparser-service-processor:${layoutParserVersion}") { exclude( group = "com.iqser.red.service", diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/document/nodes/Table.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/document/nodes/Table.java index 100f3fe9..4d56c729 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/document/nodes/Table.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/document/nodes/Table.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -64,8 +65,7 @@ public class Table implements SemanticNode { */ public Stream streamEntitiesWhereRowContainsStringsIgnoreCase(List strings) { - return IntStream.range(0, numberOfRows) - .boxed() + return IntStream.range(0, numberOfRows).boxed() .filter(row -> rowContainsStringsIgnoreCase(row, strings)) .flatMap(this::streamRow) .map(TableCell::getEntities) @@ -82,8 +82,11 @@ public class Table implements SemanticNode { */ public boolean rowContainsStringsIgnoreCase(Integer row, List strings) { - String rowText = streamRow(row).map(TableCell::getTextBlock).collect(new TextBlockCollector()).getSearchText().toLowerCase(Locale.ROOT); - return strings.stream().map(String::toLowerCase).allMatch(rowText::contains); + String rowText = streamRow(row).map(TableCell::getTextBlock) + .collect(new TextBlockCollector()).getSearchText().toLowerCase(Locale.ROOT); + return strings.stream() + .map(String::toLowerCase) + .allMatch(rowText::contains); } @@ -96,9 +99,13 @@ public class Table implements SemanticNode { */ public Stream streamEntitiesWhereRowHasHeaderAndValue(String header, String value) { - List vertebrateStudyCols = streamHeaders().filter(headerNode -> headerNode.containsString(header)).map(TableCell::getCol).toList(); + List vertebrateStudyCols = streamHeaders().filter(headerNode -> headerNode.containsString(header)) + .map(TableCell::getCol) + .toList(); return streamTableCells().filter(tableCellNode -> vertebrateStudyCols.stream() - .anyMatch(vertebrateStudyCol -> getCell(tableCellNode.getRow(), vertebrateStudyCol).containsString(value))).map(TableCell::getEntities).flatMap(Collection::stream); + .anyMatch(vertebrateStudyCol -> getCell(tableCellNode.getRow(), vertebrateStudyCol).containsString(value))) + .map(TableCell::getEntities) + .flatMap(Collection::stream); } @@ -111,9 +118,13 @@ public class Table implements SemanticNode { */ public Stream streamEntitiesWhereRowHasHeaderAndAnyValue(String header, List values) { - List colsWithHeader = streamHeaders().filter(headerNode -> headerNode.containsString(header)).map(TableCell::getCol).toList(); + List colsWithHeader = streamHeaders().filter(headerNode -> headerNode.containsString(header)) + .map(TableCell::getCol) + .toList(); return streamTableCells().filter(tableCellNode -> colsWithHeader.stream() - .anyMatch(colWithHeader -> getCell(tableCellNode.getRow(), colWithHeader).containsAnyString(values))).map(TableCell::getEntities).flatMap(Collection::stream); + .anyMatch(colWithHeader -> getCell(tableCellNode.getRow(), colWithHeader).containsAnyString(values))) + .map(TableCell::getEntities) + .flatMap(Collection::stream); } @@ -126,16 +137,33 @@ public class Table implements SemanticNode { */ public Stream streamEntitiesWhereRowContainsEntitiesOfType(List types) { - List rowsWithEntityOfType = getEntities().stream() - .filter(TextEntity::active) - .filter(redactionEntity -> types.stream().anyMatch(type -> type.equals(redactionEntity.type()))) - .map(TextEntity::getIntersectingNodes) - .filter(node -> node instanceof TableCell) - .map(node -> (TableCell) node) - .map(TableCell::getRow) - .toList(); + return IntStream.range(0, numberOfRows).boxed() + .filter(rowNumber -> streamTextEntitiesInRow(rowNumber).map(TextEntity::type) + .anyMatch(types::contains)) + .flatMap(this::streamRow) + .map(TableCell::getEntities) + .flatMap(Collection::stream); + } - return rowsWithEntityOfType.stream().flatMap(this::streamRow).map(TableCell::getEntities).flatMap(Collection::stream); + + /** + * Streams all entities in this table, that appear in a row, which contains at least one entity of each of the provided types. + * Ignores Entity with ignored == true or removed == true. + * + * @param types type strings to check whether a row contains an entity like them + * @return Stream of all entities in this table, that appear in a row, which contains at least one entity of each of the provided types. + */ + public Stream streamEntitiesWhereRowContainsEntitiesOfEachType(List types) { + + return IntStream.range(0, numberOfRows).boxed() + .filter(rowNumber -> { + Set entityTypes = streamTextEntitiesInRow(rowNumber).map(TextEntity::type) + .collect(Collectors.toSet()); + return entityTypes.containsAll(types); + }) + .flatMap(this::streamRow) + .map(TableCell::getEntities) + .flatMap(Collection::stream); } @@ -148,18 +176,43 @@ public class Table implements SemanticNode { */ public Stream streamEntitiesWhereRowContainsNoEntitiesOfType(List types) { - return IntStream.range(0, numberOfRows) - .boxed() - .filter(rowNumber -> streamRow(rowNumber).map(TableCell::getEntities) - .flatMap(Collection::stream) - .filter(TextEntity::active) - .noneMatch(entity -> types.contains(entity.type()))) + return IntStream.range(0, numberOfRows).boxed() + .filter(rowNumber -> streamTextEntitiesInRow(rowNumber).map(TextEntity::type) + .noneMatch(types::contains)) .flatMap(this::streamRow) .map(TableCell::getEntities) .flatMap(Collection::stream); } + /** + * Streams all Entities in the given row. + * + * @param rowNumber the row number to look for + * @return stream of TextEntities occurring in row + */ + public Stream streamTextEntitiesInRow(int rowNumber) { + + return streamRow(rowNumber).map(TableCell::getEntities) + .flatMap(Collection::stream) + .filter(TextEntity::active); + } + + + /** + * Streams all Entities in the given col. + * + * @param colNumber the column number to look for + * @return stream of TextEntities occurring in row + */ + public Stream streamTextEntitiesInCol(int colNumber) { + + return streamCol(colNumber).map(TableCell::getEntities) + .flatMap(Collection::stream) + .filter(TextEntity::active); + } + + /** * Returns a TableCell at the provided row and column location. * @@ -173,7 +226,8 @@ public class Table implements SemanticNode { throw new IllegalArgumentException(format("row %d, col %d is out of bounds for number of rows of %d and number of cols %d", row, col, numberOfRows, numberOfCols)); } int idx = row * numberOfCols + col; - return (TableCell) documentTree.getEntryById(treeId).getChildren().get(idx).getNode(); + return (TableCell) documentTree.getEntryById(treeId).getChildren() + .get(idx).getNode(); } @@ -196,7 +250,7 @@ public class Table implements SemanticNode { */ public Stream streamTableCellsWhichContainType(String type) { - return streamTableCells().filter(tableCell -> tableCell.getEntities().stream().filter(TextEntity::active).anyMatch(entity -> entity.type().equals(type))); + return streamTableCells().filter(tableCell -> tableCell.hasEntitiesOfType(type)); } @@ -222,7 +276,8 @@ public class Table implements SemanticNode { */ public Stream streamCol(int col) { - return IntStream.range(0, numberOfRows).boxed().map(row -> getCell(row, col)); + return IntStream.range(0, numberOfRows).boxed() + .map(row -> getCell(row, col)); } @@ -234,7 +289,8 @@ public class Table implements SemanticNode { */ public Stream streamRow(int row) { - return IntStream.range(0, numberOfCols).boxed().map(col -> getCell(row, col)); + return IntStream.range(0, numberOfCols).boxed() + .map(col -> getCell(row, col)); } @@ -258,7 +314,8 @@ public class Table implements SemanticNode { */ public Stream streamHeadersForCell(int row, int col) { - return Stream.concat(streamRow(row), streamCol(col)).filter(TableCell::isHeader); + return Stream.concat(streamRow(row), streamCol(col)) + .filter(TableCell::isHeader); } @@ -348,7 +405,9 @@ public class Table implements SemanticNode { public TextBlock getTextBlock() { if (textBlock == null) { - textBlock = streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector()); + textBlock = streamAllSubNodes().filter(SemanticNode::isLeaf) + .map(SemanticNode::getLeafTextBlock) + .collect(new TextBlockCollector()); } return textBlock; } diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/document/graph/TableTest.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/document/graph/TableTest.java new file mode 100644 index 00000000..fad981c5 --- /dev/null +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/document/graph/TableTest.java @@ -0,0 +1,162 @@ +package com.iqser.red.service.redaction.v1.server.document.graph; + +import static com.iqser.red.service.redaction.v1.server.utils.EntityVisualizationUtility.ENTITY_LAYER; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.awt.Color; +import java.io.File; +import java.io.FileOutputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.drools.io.ClassPathResource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType; +import com.iqser.red.service.redaction.v1.server.model.document.entity.EntityType; +import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Document; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.NodeType; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Table; +import com.iqser.red.service.redaction.v1.server.service.document.EntityCreationService; +import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService; +import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService; +import com.iqser.red.service.redaction.v1.server.utils.EntityVisualizationUtility; +import com.knecon.fforesight.service.viewerdoc.model.Visualizations; +import com.knecon.fforesight.service.viewerdoc.service.ViewerDocumentService; +import com.knecon.fforesight.tenantcommons.TenantContext; + +import lombok.SneakyThrows; + +public class TableTest extends BuildDocumentIntegrationTest { + + private static final boolean DRAW_FILE = false; + + @Autowired + private EntityEnrichmentService entityEnrichmentService; + + private EntityCreationService entityCreationService; + + private static final String TYPE_1 = "type1"; + private static final String TYPE_2 = "type2"; + private static final String TYPE_3 = "type3"; + private static final String TYPE_4 = "type4"; + + private Table table; + + private Set entities; + + + @SneakyThrows + @BeforeEach + public void createTable() { + + entityCreationService = new EntityCreationService(entityEnrichmentService); + + String fileName = "files/Minimal Examples/BasicTable.pdf"; + + Document document = buildGraph(fileName); + + table = (Table) document.streamAllSubNodesOfType(NodeType.TABLE) + .findAny() + .orElseThrow(); + + entities = List.of(// + entityCreationService.byString("Cell11", TYPE_1, EntityType.ENTITY, document), + entityCreationService.byString("Cell21", TYPE_1, EntityType.ENTITY, document), + entityCreationService.byString("Cell31", TYPE_1, EntityType.ENTITY, document), + entityCreationService.byString("Cell41", TYPE_1, EntityType.ENTITY, document), + entityCreationService.byString("Cell51", TYPE_1, EntityType.ENTITY, document), + + entityCreationService.byString("Cell12", TYPE_2, EntityType.ENTITY, document), + entityCreationService.byString("Cell32", TYPE_2, EntityType.ENTITY, document), + entityCreationService.byString("Cell42", TYPE_2, EntityType.ENTITY, document), + + entityCreationService.byString("Cell23", TYPE_3, EntityType.ENTITY, document), + entityCreationService.byString("Cell53", TYPE_3, EntityType.ENTITY, document), + + entityCreationService.byString("Cell14", TYPE_4, EntityType.ENTITY, document), + entityCreationService.byString("Cell34", TYPE_4, EntityType.ENTITY, document)) + .stream() + .flatMap(Function.identity()) + .collect(Collectors.toSet()); + + if (DRAW_FILE) { + File file = new File("/tmp/" + Path.of(fileName).getFileName().toString()); + storageService.downloadTo(TenantContext.getTenantId(), + RedactionStorageService.StorageIdUtils.getStorageId(TEST_DOSSIER_ID, TEST_FILE_ID, FileType.VIEWER_DOCUMENT), + file); + ViewerDocumentService viewerDocumentService = new ViewerDocumentService(null); + + var visualizationsOnPage = EntityVisualizationUtility.createVisualizationsOnPage(document.getEntities(), Color.MAGENTA); + + viewerDocumentService.addVisualizationsOnPage(file, + file, + Visualizations.builder() + .layer(ENTITY_LAYER) + .visualizationsOnPages(visualizationsOnPage) + .layerVisibilityDefaultValue(true) + .build()); + } + + } + + + @Test + public void testStreamEntitiesWhereRowContainsEntitiesOfType() { + + int type_2_count = table.getEntitiesOfType(TYPE_2).size(); + + assertEquals(type_2_count, + table.streamEntitiesWhereRowContainsEntitiesOfType(List.of(TYPE_1)) + .filter(textEntity -> textEntity.type().equals(TYPE_2)) + .count()); + + assertEquals(type_2_count, + table.streamEntitiesWhereRowContainsEntitiesOfType(List.of(TYPE_1, TYPE_4)) + .filter(textEntity -> textEntity.type().equals(TYPE_2)) + .count()); + + assertEquals(2, + table.streamEntitiesWhereRowContainsEntitiesOfEachType(List.of(TYPE_1, TYPE_4)) + .filter(textEntity -> textEntity.type().equals(TYPE_2)) + .count()); + + assertEquals(0, + table.streamEntitiesWhereRowContainsEntitiesOfEachType(List.of(TYPE_1, TYPE_3)) + .filter(textEntity -> textEntity.type().equals(TYPE_2)) + .count()); + + assertEquals(0, + table.streamEntitiesWhereRowContainsEntitiesOfEachType(List.of(TYPE_1, TYPE_3, TYPE_4)) + .filter(textEntity -> textEntity.type().equals(TYPE_2)) + .count()); + + assertEquals(type_2_count, + table.streamEntitiesWhereRowContainsEntitiesOfEachType(List.of()) + .filter(textEntity -> textEntity.type().equals(TYPE_2)) + .count()); + + assertEquals(3, + table.streamTextEntitiesInRow(1) + .count()); + + assertEquals(2, + table.streamTextEntitiesInRow(4) + .count()); + + assertEquals(5, + table.streamTextEntitiesInCol(1) + .count()); + + assertEquals(3, + table.streamTextEntitiesInRow(3) + .count()); + } + +} diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/utils/EntityVisualizationUtility.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/utils/EntityVisualizationUtility.java new file mode 100644 index 00000000..7fae90bc --- /dev/null +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/utils/EntityVisualizationUtility.java @@ -0,0 +1,61 @@ +package com.iqser.red.service.redaction.v1.server.utils; + +import java.awt.Color; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.pdfbox.cos.COSName; + +import com.iqser.red.service.redaction.v1.server.model.document.entity.PositionOnPage; +import com.iqser.red.service.redaction.v1.server.model.document.entity.TextEntity; +import com.iqser.red.service.redaction.v1.server.model.document.nodes.Page; +import com.knecon.fforesight.service.viewerdoc.ContentStreams; +import com.knecon.fforesight.service.viewerdoc.model.ColoredRectangle; +import com.knecon.fforesight.service.viewerdoc.model.VisualizationsOnPage; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class EntityVisualizationUtility { + + public static final ContentStreams.Identifier ENTITY_LAYER = new ContentStreams.Identifier("Entities", COSName.getPDFName("KNECON_ENTITIES"), true); + + + public Map createVisualizationsOnPage(Collection entity, Color color) { + + Map visualizations = new HashMap<>(); + Set pages = entity.stream() + .map(TextEntity::getPages) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + + pages.forEach(page -> visualizations.put(page.getNumber() - 1, buildVisualizationsOnPage(color, page))); + + return visualizations; + } + + + private static VisualizationsOnPage buildVisualizationsOnPage(Color color, Page page) { + + return VisualizationsOnPage.builder().coloredRectangles(getEntityRectangles(color, page)).build(); + } + + + private static List getEntityRectangles(Color color, Page page) { + + return page.getEntities() + .stream() + .map(TextEntity::getPositionsOnPagePerPage) + .flatMap(Collection::stream) + .filter(p -> p.getPage().equals(page)) + .map(PositionOnPage::getRectanglePerLine) + .flatMap(Collection::stream) + .map(r -> new ColoredRectangle(r, color, 1)) + .toList(); + } + +} diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/utils/LayoutParsingRequestProvider.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/utils/LayoutParsingRequestProvider.java index f9eaa926..f4625012 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/utils/LayoutParsingRequestProvider.java +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/utils/LayoutParsingRequestProvider.java @@ -34,6 +34,7 @@ public class LayoutParsingRequestProvider { .positionBlockFileStorageId(positionBlockFileStorageId) .pageFileStorageId(pageFileStorageId) .simplifiedTextStorageId(simplifiedTextStorageId).viewerDocumentStorageId(viewerDocumentStorageId) + .visualLayoutParsingFileId(Optional.empty()) .build(); } diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/all_redact_manager_rules.drl b/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/all_redact_manager_rules.drl index 95e4bf27..b1fbf077 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/all_redact_manager_rules.drl +++ b/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/all_redact_manager_rules.drl @@ -157,18 +157,17 @@ rule "CBI.3.0: Redacted because Section contains a vertebrate" rule "CBI.3.1: Redacted because table row contains a vertebrate" when - $table: Table(hasEntitiesOfType("vertebrate"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("vertebrate"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + $cellsWithvertebrate: TableCell() from $table.streamTableCellsWhichContainType("vertebrate").toList() + $tableCell: TableCell(row == $cellsWithvertebrate.row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() then - $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate")) - .filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address")) - .forEach(entity -> { - entity.applyWithReferences( + $authorOrAddress.applyWithReferences( "CBI.3.1", "Vertebrate found", "Reg (EC) No 1107/2009 Art. 63 (2g)", - $table.getEntitiesOfTypeInSameRow("vertebrate", entity) + $table.getEntitiesOfTypeInSameRow("vertebrate", $authorOrAddress) ); - }); end rule "CBI.3.2: Do not redact because Section does not contain a vertebrate" @@ -207,23 +206,21 @@ rule "CBI.4.0: Do not redact Names and Addresses if no_redaction_indicator is fo }); end -rule "CBI.4.1: Do not redact Names and Addresses if no_redaction_indicator is found in table row" +rule "CBI.4.1: Redacted because table row contains a vertebrate" when - $table: Table(hasEntitiesOfType("no_redaction_indicator"), - hasEntitiesOfType("vertebrate"), - (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("no_redaction_indicator"), hasEntitiesOfType("vertebrate"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + TableCell($row: row) from $table.streamTableCellsWhichContainType("vertebrate").toList() + TableCell(row == $row) from $table.streamTableCellsWhichContainType("no_redaction_indicator").toList() + $tableCell: TableCell(row == $row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() 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( + $authorOrAddress.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() + $table.getEntitiesOfTypeInSameRow("vertebrate", $authorOrAddress).stream(), + $table.getEntitiesOfTypeInSameRow("no_redaction_indicator", $authorOrAddress).stream()).toList() ); - }); end @@ -250,22 +247,20 @@ rule "CBI.5.0: Redact Names and Addresses if no_redaction_indicator but also red 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"))) + $table: Table(hasEntitiesOfType("no_redaction_indicator"), hasEntitiesOfType("redaction_indicator"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + TableCell($row: row) from $table.streamTableCellsWhichContainType("redaction_indicator").toList() + TableCell(row == $row) from $table.streamTableCellsWhichContainType("no_redaction_indicator").toList() + $tableCell: TableCell(row == $row) from $table.streamTableCells().toList() + $entity: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() 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( + $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() + $table.getEntitiesOfTypeInSameRow("redaction_indicator", $entity).stream(), + $table.getEntitiesOfTypeInSameRow("no_redaction_indicator", $entity).stream()).toList() ); - }); end @@ -355,18 +350,17 @@ rule "CBI.8.0: Redacted because Section contains must_redact entity" rule "CBI.8.1: Redacted because table row contains must_redact entity" when - $table: Table(hasEntitiesOfType("must_redact"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("must_redact"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + $cellsWithMustRedact: TableCell() from $table.streamTableCellsWhichContainType("must_redact").toList() + $tableCell: TableCell(row == $cellsWithMustRedact.row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() then - $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("must_redact")) - .filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address")) - .forEach(entity -> { - entity.applyWithReferences( + $authorOrAddress.applyWithReferences( "CBI.8.1", - "must_redact entity found", + "Must_redact found", "Reg (EC) No 1107/2009 Art. 63 (2g)", - $table.getEntitiesOfTypeInSameRow("must_redact", entity) + $table.getEntitiesOfTypeInSameRow("must_redact", $authorOrAddress) ); - }); end @@ -448,7 +442,6 @@ rule "CBI.12.0: Redact and recommend TableCell with header 'Author' or 'Author(s TableCell(!header, containsAnyString("Yes", "Y"), $rowWithYes: row) from $table.streamCol($vertebrateCol).toList() $authorCell: TableCell(row == $rowWithYes) from $table.streamCol($authorCol).toList() then - entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY) .ifPresent(authorEntity -> { authorEntity.redact("CBI.12.0", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(3) of Regulation (EC) No 178/2002"); diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/rules.drl b/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/rules.drl index fdd704b7..aed445b2 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/rules.drl +++ b/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/rules.drl @@ -100,18 +100,17 @@ rule "CBI.3.0: Redacted because Section contains a vertebrate" rule "CBI.3.1: Redacted because table row contains a vertebrate" when - $table: Table(hasEntitiesOfType("vertebrate"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("vertebrate"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + $cellsWithvertebrate: TableCell() from $table.streamTableCellsWhichContainType("vertebrate").toList() + $tableCell: TableCell(row == $cellsWithvertebrate.row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() then - $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate")) - .filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address")) - .forEach(entity -> { - entity.applyWithReferences( + $authorOrAddress.applyWithReferences( "CBI.3.1", "Vertebrate found", "Reg (EC) No 1107/2009 Art. 63 (2g)", - $table.getEntitiesOfTypeInSameRow("vertebrate", entity) + $table.getEntitiesOfTypeInSameRow("vertebrate", $authorOrAddress) ); - }); end rule "CBI.3.2: Do not redact because Section does not contain a vertebrate" @@ -150,23 +149,21 @@ rule "CBI.4.0: Do not redact Names and Addresses if no_redaction_indicator is fo }); end -rule "CBI.4.1: Do not redact Names and Addresses if no_redaction_indicator is found in table row" +rule "CBI.4.1: Redacted because table row contains a vertebrate" when - $table: Table(hasEntitiesOfType("no_redaction_indicator"), - hasEntitiesOfType("vertebrate"), - (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("no_redaction_indicator"), hasEntitiesOfType("vertebrate"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + TableCell($row: row) from $table.streamTableCellsWhichContainType("vertebrate").toList() + TableCell(row == $row) from $table.streamTableCellsWhichContainType("no_redaction_indicator").toList() + $tableCell: TableCell(row == $row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() 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( + $authorOrAddress.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() + $table.getEntitiesOfTypeInSameRow("vertebrate", $authorOrAddress).stream(), + $table.getEntitiesOfTypeInSameRow("no_redaction_indicator", $authorOrAddress).stream()).toList() ); - }); end @@ -193,22 +190,20 @@ rule "CBI.5.0: Redact Names and Addresses if no_redaction_indicator but also red 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"))) + $table: Table(hasEntitiesOfType("no_redaction_indicator"), hasEntitiesOfType("redaction_indicator"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + TableCell($row: row) from $table.streamTableCellsWhichContainType("redaction_indicator").toList() + TableCell(row == $row) from $table.streamTableCellsWhichContainType("no_redaction_indicator").toList() + $tableCell: TableCell(row == $row) from $table.streamTableCells().toList() + $entity: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() 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( + $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() + $table.getEntitiesOfTypeInSameRow("redaction_indicator", $entity).stream(), + $table.getEntitiesOfTypeInSameRow("no_redaction_indicator", $entity).stream()).toList() ); - }); end @@ -230,18 +225,17 @@ rule "CBI.8.0: Redacted because Section contains must_redact entity" rule "CBI.8.1: Redacted because table row contains must_redact entity" when - $table: Table(hasEntitiesOfType("must_redact"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("must_redact"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + $cellsWithMustRedact: TableCell() from $table.streamTableCellsWhichContainType("must_redact").toList() + $tableCell: TableCell(row == $cellsWithMustRedact.row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() then - $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("must_redact")) - .filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address")) - .forEach(entity -> { - entity.applyWithReferences( + $authorOrAddress.applyWithReferences( "CBI.8.1", - "must_redact entity found", + "Must_redact found", "Reg (EC) No 1107/2009 Art. 63 (2g)", - $table.getEntitiesOfTypeInSameRow("must_redact", entity) + $table.getEntitiesOfTypeInSameRow("must_redact", $authorOrAddress) ); - }); end @@ -295,7 +289,6 @@ rule "CBI.12.0: Redact and recommend TableCell with header 'Author' or 'Author(s TableCell(!header, containsAnyString("Yes", "Y"), $rowWithYes: row) from $table.streamCol($vertebrateCol).toList() $authorCell: TableCell(row == $rowWithYes) from $table.streamCol($authorCol).toList() then - entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY) .ifPresent(authorEntity -> { authorEntity.redact("CBI.12.0", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(3) of Regulation (EC) No 178/2002"); diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/resources/files/Minimal Examples/BasicTable.pdf b/redaction-service-v1/redaction-service-server-v1/src/test/resources/files/Minimal Examples/BasicTable.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f692fccb1d60b7f4f0f57be31f8f7eb6c9c3d338 GIT binary patch literal 19603 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 literal 0 HcmV?d00001 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 index ec55a2e6..57124c27 100644 --- 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 @@ -157,20 +157,20 @@ rule "CBI.3.0: Redacted because Section contains a vertebrate" rule "CBI.3.1: Redacted because table row contains a vertebrate" when - $table: Table(hasEntitiesOfType("vertebrate"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("vertebrate"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + $cellsWithvertebrate: TableCell() from $table.streamTableCellsWhichContainType("vertebrate").toList() + $tableCell: TableCell(row == $cellsWithvertebrate.row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() then - $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("vertebrate")) - .filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address")) - .forEach(entity -> { - entity.applyWithReferences( + $authorOrAddress.applyWithReferences( "CBI.3.1", "Vertebrate found", "Reg (EC) No 1107/2009 Art. 63 (2g)", - $table.getEntitiesOfTypeInSameRow("vertebrate", entity) + $table.getEntitiesOfTypeInSameRow("vertebrate", $authorOrAddress) ); - }); 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"))) @@ -207,23 +207,21 @@ rule "CBI.4.0: Do not redact Names and Addresses if no_redaction_indicator is fo }); end -rule "CBI.4.1: Do not redact Names and Addresses if no_redaction_indicator is found in table row" +rule "CBI.4.1: Redacted because table row contains a vertebrate" when - $table: Table(hasEntitiesOfType("no_redaction_indicator"), - hasEntitiesOfType("vertebrate"), - (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("no_redaction_indicator"), hasEntitiesOfType("vertebrate"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + TableCell($row: row) from $table.streamTableCellsWhichContainType("vertebrate").toList() + TableCell(row == $row) from $table.streamTableCellsWhichContainType("no_redaction_indicator").toList() + $tableCell: TableCell(row == $row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() 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( + $authorOrAddress.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() + $table.getEntitiesOfTypeInSameRow("vertebrate", $authorOrAddress).stream(), + $table.getEntitiesOfTypeInSameRow("no_redaction_indicator", $authorOrAddress).stream()).toList() ); - }); end @@ -250,22 +248,20 @@ rule "CBI.5.0: Redact Names and Addresses if no_redaction_indicator but also red 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"))) + $table: Table(hasEntitiesOfType("no_redaction_indicator"), hasEntitiesOfType("redaction_indicator"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + TableCell($row: row) from $table.streamTableCellsWhichContainType("redaction_indicator").toList() + TableCell(row == $row) from $table.streamTableCellsWhichContainType("no_redaction_indicator").toList() + $tableCell: TableCell(row == $row) from $table.streamTableCells().toList() + $entity: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() 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( + $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() + $table.getEntitiesOfTypeInSameRow("redaction_indicator", $entity).stream(), + $table.getEntitiesOfTypeInSameRow("no_redaction_indicator", $entity).stream()).toList() ); - }); end @@ -355,18 +351,17 @@ rule "CBI.8.0: Redacted because Section contains must_redact entity" rule "CBI.8.1: Redacted because table row contains must_redact entity" when - $table: Table(hasEntitiesOfType("must_redact"), (hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address"))) + $table: Table(hasEntitiesOfType("must_redact"), hasEntitiesOfType("CBI_author") || hasEntitiesOfType("CBI_address")) + $cellsWithMustRedact: TableCell() from $table.streamTableCellsWhichContainType("must_redact").toList() + $tableCell: TableCell(row == $cellsWithMustRedact.row) from $table.streamTableCells().toList() + $authorOrAddress: TextEntity(type() == "CBI_author" || type() == "CBI_address", active()) from $tableCell.getEntities() then - $table.streamEntitiesWhereRowContainsEntitiesOfType(List.of("must_redact")) - .filter(entity -> entity.getType().equals("CBI_author") || entity.getType().equals("CBI_address")) - .forEach(entity -> { - entity.applyWithReferences( + $authorOrAddress.applyWithReferences( "CBI.8.1", - "must_redact entity found", + "Must_redact found", "Reg (EC) No 1107/2009 Art. 63 (2g)", - $table.getEntitiesOfTypeInSameRow("must_redact", entity) + $table.getEntitiesOfTypeInSameRow("must_redact", $authorOrAddress) ); - }); end @@ -448,7 +443,6 @@ rule "CBI.12.0: Redact and recommend TableCell with header 'Author' or 'Author(s TableCell(!header, containsAnyString("Yes", "Y"), $rowWithYes: row) from $table.streamCol($vertebrateCol).toList() $authorCell: TableCell(row == $rowWithYes) from $table.streamCol($authorCol).toList() then - entityCreationService.bySemanticNode($authorCell, "CBI_author", EntityType.ENTITY) .ifPresent(authorEntity -> { authorEntity.redact("CBI.12.0", "Redacted because it's row belongs to a vertebrate study", "Article 39(e)(3) of Regulation (EC) No 178/2002");