Compare commits

...
82 Commits
Author SHA1 Message Date
deiflaender d050070dc7 RED-3616: Do not remove annotation that intersects with skipped imported redactions 2022-04-04 12:14:36 +02:00
Christoph Schabert 49d47a0682 Pull request #365: Change Major Version to 3.x
Merge in RED/redaction-service from cschabert/pomxml-1649055501527 to master

* commit '9bab1205b711d94888a6b3364a41a89d07d310b6':
  Change Major Version to 3.x
2022-04-04 09:21:17 +02:00
Christoph Schabert 9bab1205b7 Change Major Version to 3.x 2022-04-04 08:58:35 +02:00
Kresnadi Budisantoso 7963c57552 Pull request #364: RED-3761 add possibility to expand dictionary match with a prefix regex pattern
Merge in RED/redaction-service from kbudisantoso/RED-3761-expandByPrefixRegEx to master

* commit '1427087d8fa29a218b8b378f83c345c497a26b77':
  RED-3761 add possibility to expand dictionary match with a prefix regex pattern
2022-04-01 15:35:45 +02:00
Kresnadi Budisantoso 1427087d8f RED-3761 add possibility to expand dictionary match with a prefix regex pattern 2022-04-01 14:21:47 +02:00
Christoph Schabert b83400ffa3 Pull request #362: automate releases
Merge in RED/redaction-service from autoRelease to master

* commit 'f05f2b21a46b8a4f15bd933570d5a107351aa7b7':
  automate releases
2022-04-01 14:12:09 +02:00
cschabert f05f2b21a4 automate releases 2022-04-01 13:47:26 +02:00
Philipp Schramm 02d9ebc24c Pull request #360: RED-3568: Do not ignore textblocks if only rotated pages are in the document
Merge in RED/redaction-service from RED-3568 to master

* commit 'ae3e4570c15dbc2c46373ce68d55cec760e7da6b':
  RED-3568: Do not ignore textblocks if only rotated pages are in the document
2022-03-29 14:00:20 +02:00
deiflaender ae3e4570c1 RED-3568: Do not ignore textblocks if only rotated pages are in the document 2022-03-29 13:54:23 +02:00
Dominique Eiflaender a975596446 Pull request #359: RED-3699 master
Merge in RED/redaction-service from RED-3699-master to master

* commit 'a5b96f148950e28618028daef1e91aecdadc89e3':
  Fixed unknown type for manual type
  Fixed bug in getColors
  RED-3699:  Do not call updateDictionary on merge RedactionLog
2022-03-25 09:20:52 +01:00
deiflaender a5b96f1489 Fixed unknown type for manual type 2022-03-25 09:12:32 +01:00
deiflaender 4814ce3650 Fixed bug in getColors 2022-03-25 09:12:21 +01:00
deiflaender 7fccdb32c5 RED-3699: Do not call updateDictionary on merge RedactionLog 2022-03-25 09:12:00 +01:00
Timo Bejan 1d389b78fd Pull request #357: processed date fix
Merge in RED/redaction-service from processed-date-fix-3.3.x to master

* commit 'b4916b65b661c80f0924c163b8795205b9c8e6c3':
  processed date fix
2022-03-24 11:52:29 +01:00
Timo Bejan b4916b65b6 processed date fix 2022-03-24 10:16:49 +02:00
Dominique Eiflaender e1db383463 Pull request #356: RED-2836: Fixed calculating dictionary increments with false_positive and false_recommendation
Merge in RED/redaction-service from RED-2836-3 to master

* commit '4d2a7d454d916e7eee65f7cc155323924c2cb229':
  RED-2836: Fixed calculating dictionary increments with false_positive and false_recommendation
2022-03-22 10:31:05 +01:00
deiflaender 4d2a7d454d RED-2836: Fixed calculating dictionary increments with false_positive and false_recommendation 2022-03-22 10:24:26 +01:00
Dominique Eiflaender 3758720683 Pull request #355: Renamed result field in research services to data
Merge in RED/redaction-service from ResearchRename to master

* commit '22bb0b44db7a22f8088512570d4b2be6e742a887':
  Renamed result field in research services to data
2022-03-21 16:02:45 +01:00
deiflaender 22bb0b44db Renamed result field in research services to data 2022-03-21 15:54:37 +01:00
Dominique Eiflaender e93f5d147f Pull request #354: RED-2836: Enabled false positives per dictionary
Merge in RED/redaction-service from RED-2836 to master

* commit '88e6ac3d2291ced8bb2229c3e7daeb8d78d6da93':
  RED-2836: Enabled false positives per dictionary
2022-03-21 12:54:48 +01:00
deiflaender 88e6ac3d22 RED-2836: Enabled false positives per dictionary 2022-03-21 12:47:27 +01:00
Timo Bejan 17b885d901 Pull request #353: pending add to dict
Merge in RED/redaction-service from pending-master-fix to master

* commit '50d9f4fec22768310232988ee01f9e9aa3ba9dc8':
  pending add to dict
2022-03-21 11:49:16 +01:00
Timo Bejan 50d9f4fec2 pending add to dict 2022-03-21 11:19:26 +02:00
Philipp Schramm 1bcf0a3f07 Pull request #351: RED-3553 If image overlaps entity and the entity will be removed
Merge in RED/redaction-service from RED-3553 to master

* commit '1f6a9aa14deb7ec65fee03e28758f8e3157dbd5f':
  RED-3553 If image overlaps entity and the entity will be removed
2022-03-17 14:17:16 +01:00
Philipp Schramm 1f6a9aa14d RED-3553 If image overlaps entity and the entity will be removed 2022-03-17 14:04:59 +01:00
Dominique Eiflaender c8cf0078eb Pull request #352: PortsFrom3.2.x
Merge in RED/redaction-service from portsFrom3.2.x to master

* commit 'b13a695c64280a97ece198a18605c0f1b5cf753f':
  RED-3611: Added priority mode that only listens to priority queue
  RED-3620: Fixed remove from dictionary
2022-03-17 11:16:03 +01:00
deiflaender b13a695c64 RED-3611: Added priority mode that only listens to priority queue 2022-03-17 10:50:48 +01:00
deiflaender 32a907f697 RED-3620: Fixed remove from dictionary 2022-03-17 10:49:01 +01:00
Timo Bejan 1d1de6caba Pull request #350: Removed processed date filter for add to dictionary entries
Merge in RED/redaction-service from RED-3538 to master

* commit '7b2295393c1c2dcbcecbb11c64929517cb13a6de':
  Removed processed date filter for add to dictionary entries
2022-03-16 15:46:13 +01:00
Timo Bejan 7b2295393c Removed processed date filter for add to dictionary entries 2022-03-16 16:29:30 +02:00
Timo Bejan d0ba3f51c1 Pull request #347: From version master
Merge in RED/redaction-service from from-version-master to master

* commit 'c432c56a05ce58d711286bbbcc636ff1445c652d':
  Updated Persistence Service Version
  Release 3.80.x branch creation + incremental entry loading based on version for dictionaries
2022-03-14 16:36:14 +01:00
Timo Bejan c432c56a05 Updated Persistence Service Version 2022-03-14 17:29:04 +02:00
Timo Bejan e73da002d5 Release 3.80.x branch creation + incremental entry loading based on version for dictionaries 2022-03-14 17:27:52 +02:00
Philipp Schramm 0ceb8ae266 Pull request #346: RED-3403 Extended Section with negated Methods
Merge in RED/redaction-service from RED-3403 to master

* commit '97fe7383cd9737f25d22903a6124dfe8361f7a84':
  RED-3403 Extended Section with negated Methods
2022-02-28 14:51:21 +01:00
Philipp Schramm 97fe7383cd RED-3403 Extended Section with negated Methods 2022-02-28 14:40:40 +01:00
Dominique Eiflaender 7d132cbb88 Pull request #345: Fixed aws s3 config
Merge in RED/redaction-service from s3FixConf to master

* commit 'd18b5dfd138595f0cbf7cc2a99dc708994141425':
  Fixed aws s3 config
2022-02-25 13:04:36 +01:00
deiflaender d18b5dfd13 Fixed aws s3 config 2022-02-25 12:58:09 +01:00
Dominique Eiflaender e2451a08f4 Pull request #344: RED-3282: Do not add overlapping ai recommendactions
Merge in RED/redaction-service from RED-3282 to master

* commit '537ccb83d799cef0eb437a6bbe1b78da661b2b6b':
  RED-3282: Do not add overlapping ai recommendactions
2022-02-25 09:37:15 +01:00
deiflaender 537ccb83d7 RED-3282: Do not add overlapping ai recommendactions 2022-02-25 09:29:08 +01:00
Ali Oezyetimoglu fd027efa5c Pull request #343: RED-3407: Remove obsolete recommendation dictionaries
Merge in RED/redaction-service from RED-3407-rs1 to master

* commit 'c3984e054482acab3acde740b8dce437d97e9ffb':
  RED-3407: Remove obsolete recommendation dictionaries
2022-02-23 14:59:53 +01:00
aoezyetimoglu c3984e0544 RED-3407: Remove obsolete recommendation dictionaries 2022-02-23 14:29:01 +01:00
Timo Bejan 77feedcc8e Pull request #342: added imported flag
Merge in RED/redaction-service from imported-redaction to master

* commit 'de92117b35fcc2f3979450027acf1f7edf04befa':
  added imported flag
2022-02-11 11:41:32 +01:00
Timo Bejan de92117b35 added imported flag 2022-02-11 11:33:34 +02:00
Philipp Schramm 143f4cfb0d Pull request #341: RED-3350 Communication with entity recognition via queues
Merge in RED/redaction-service from feature/RED-3350 to master

* commit '68727178e79224b7ca7f9f293e48ca21b4ce2f91':
  RED-3350 Communication with entity recognition via queues
2022-02-09 15:24:47 +01:00
Philipp Schramm 68727178e7 RED-3350 Communication with entity recognition via queues 2022-02-09 15:19:55 +01:00
Dominique Eiflaender 90fe282313 Pull request #339: RED-3327: Ignore recommendations if touches other type
Merge in RED/redaction-service from RED-3327-master to master

* commit '8689125ed9b09f73781d3babfd65537c0b1f4afe':
  RED-3327: Ignore recommendations if touches other type
2022-02-01 11:19:26 +01:00
deiflaender 8689125ed9 RED-3327: Ignore recommendations if touches other type 2022-02-01 11:06:32 +01:00
Dominique Eiflaender e68c6798be Pull request #337: RED-3243: Improved imported redactions logic
Merge in RED/redaction-service from RED-3243-8 to master

* commit '58b1db66cc2cc67b3b4e26295478824e5052d6a3':
  RED-3243: Improved imported redactions logic
2022-01-31 14:47:54 +01:00
deiflaender 58b1db66cc RED-3243: Improved imported redactions logic 2022-01-31 14:42:49 +01:00
deiflaender 0072d38d68 Use base image to speedup build 2022-01-31 12:05:10 +01:00
deiflaender 25a409d3a5 RED-3243: Added ImportedRedaction model classes 2022-01-31 11:11:32 +01:00
Timo Bejan ca1da20ba3 Pull request #335: immutable field fix
Merge in RED/redaction-service from RED-3242 to master

* commit '3c691a1afc9ce67845aad5d15bbc99a0113f6a6b':
  immutable field fix
2022-01-28 10:57:04 +01:00
Timo Bejan 3c691a1afc immutable field fix 2022-01-28 11:29:43 +02:00
Timo Bejan 6813fbc3cb Pull request #333: since no more manual changes are fields of redactionLog, we can safely always merge, even legacy ones
Merge in RED/redaction-service from RED-3242 to master

* commit 'c9a75152fb9b4e02b3421dd16ae54056889b0aa9':
  since no more manual changes are fields of redactionLog, we can safely always merge, even legacy ones
2022-01-27 15:59:39 +01:00
Dominique Eiflaender a8a6fef07a Pull request #334: RED-3239: Protoyped logic for imported redactions
Merge in RED/redaction-service from RED-3239 to master

* commit '26661fa87beeb20b32f2be5a8b1b025e8d2d29fd':
  RED-3239: Protoyped logic for imported redactions
2022-01-27 15:31:16 +01:00
deiflaender 26661fa87b RED-3239: Protoyped logic for imported redactions 2022-01-27 15:21:33 +01:00
Timo Bejan c9a75152fb since no more manual changes are fields of redactionLog, we can safely always merge, even legacy ones 2022-01-27 15:30:43 +02:00
Timo Bejan 0da2a1ab26 Pull request #332: RED-3242 - excluded from automatic analysis - reworked redactionLog architecture
Merge in RED/redaction-service from RED-3242 to master

* commit 'd92a723fea21280d7bf482c3d74c7b177dbaa030':
  RED-3242 - excluded from automatic analysis - reworked redactionLog architecture
2022-01-27 14:06:35 +01:00
Timo Bejan d92a723fea RED-3242 - excluded from automatic analysis - reworked redactionLog architecture 2022-01-27 14:58:14 +02:00
Dominique Eiflaender 3109e3e4f0 Pull request #331: RED-3133: Ignore min length for values from ai
Merge in RED/redaction-service from RED-3133-Fix4 to master

* commit 'a1544588e8a3cfa0479d5d31d82463c5434674a1':
  RED-3133: Ignore min length for values from ai
2022-01-26 16:32:23 +01:00
deiflaender a1544588e8 RED-3133: Ignore min length for values from ai 2022-01-26 16:18:41 +01:00
Dominique Eiflaender 8ba2bd776d Pull request #330: RED-3133: Fixed wrong ai entries again
Merge in RED/redaction-service from RED-3133-Fix2 to master

* commit '8ac7c309d981c8899eca40872a49198284d15944':
  RED-3133: Fixed wrong ai entries again
2022-01-26 15:42:13 +01:00
deiflaender 8ac7c309d9 RED-3133: Fixed wrong ai entries again 2022-01-26 15:31:13 +01:00
Dominique Eiflaender b8f7be28e7 Pull request #329: RED-3133: Fixed wrong ai entries
Merge in RED/redaction-service from RED-3133-Fix to master

* commit '921ae1839cb424a4d82afd88401fddc9cf855f8c':
  RED-3133: Fixed wrong ai entries
2022-01-26 15:09:04 +01:00
deiflaender 921ae1839c RED-3133: Fixed wrong ai entries 2022-01-26 14:43:54 +01:00
Dominique Eiflaender 38a386bc87 Pull request #328: RED-3133: Fixed bug in address combination rule
Merge in RED/redaction-service from RED-3133-4 to master

* commit 'c21cf3dd5cdf70dfb838bb18f78a7c32715f8db8':
  RED-3133: Fixed bug in address combination rule
2022-01-26 13:09:13 +01:00
deiflaender c21cf3dd5c RED-3133: Fixed bug in address combination rule 2022-01-26 12:57:02 +01:00
Dominique Eiflaender 29ad27dea6 Pull request #326: RED-3243: Fixed bug in combining address parts
Merge in RED/redaction-service from RED-3243 to master

* commit '6a78d798912afd4d625455d62d2ec416a311873b':
  RED-3243: Fixed bug in combining address parts
2022-01-24 12:53:40 +01:00
deiflaender 6a78d79891 RED-3243: Fixed bug in combining address parts 2022-01-24 12:35:38 +01:00
Dominique Eiflaender 726c272e61 Pull request #325: Improved rule to combine AI matches
Merge in RED/redaction-service from RED-3133-2 to master

* commit 'f32d9a526787a6a74179a0e8f5589763a2080fca':
  Improved rule to combine AI matches
2022-01-21 13:15:39 +01:00
deiflaender f32d9a5267 Improved rule to combine AI matches 2022-01-21 12:53:33 +01:00
Dominique Eiflaender c4c96c1712 Pull request #324: RED-3133: Add and combine AI Entities in rules
Merge in RED/redaction-service from RED-3133 to master

* commit '6e720a15c2b9c9d46b52b94f63f0af157ac5d81a':
  RED-3133: Add and combine AI Entities in rules
2022-01-20 12:28:17 +01:00
deiflaender 6e720a15c2 RED-3133: Add and combine AI Entities in rules 2022-01-20 12:11:45 +01:00
Dominique Eiflaender 3687a7e5a9 Pull request #323: RED-3190: Ignore if surrounding text can not be calculate because of different parsing order from ui and backend
Merge in RED/redaction-service from RED-3190-master to master

* commit '6a31ae642d34c61865b55b6b138ef4406c27ab63':
  RED-3190: Ignore if surrounding text can not be calculate because of different parsing order from ui and backend
2022-01-14 12:10:42 +01:00
deiflaender 6a31ae642d RED-3190: Ignore if surrounding text can not be calculate because of different parsing order from ui and backend 2022-01-14 12:04:50 +01:00
Ali Oezyetimoglu e01d49754d Pull request #321: RED-3183: Upgrade log4j to 2.17.1
Merge in RED/redaction-service from RED-3183-rs1 to master

* commit '5de4702d5ac0c05ff23ef89396028be121474600':
  RED-3183: Upgrade log4j to 2.17.1
2022-01-13 15:38:28 +01:00
aoezyetimoglu 5de4702d5a RED-3183: Upgrade log4j to 2.17.1 2022-01-13 15:24:31 +01:00
Dominique Eiflaender c621eb9f04 Pull request #320: RED-3173: Fixed wrong skipped redaction afer remove and readd to dictionary
Merge in RED/redaction-service from RED-3173-master to master

* commit 'eb9fe0e3ddfc9b86cb3b0664e8bec5ef5b4d34c7':
  RED-3173: Fixed wrong skipped redaction afer remove and readd to dictionary
2022-01-13 11:20:56 +01:00
deiflaender eb9fe0e3dd RED-3173: Fixed wrong skipped redaction afer remove and readd to dictionary 2022-01-13 11:14:45 +01:00
Timo Bejan f05977b7ae Pull request #318: analysis version in redactionlog and changes
Merge in RED/redaction-service from analysis-number-in-redaction-log-m to master

* commit '1cad386db83a2d2057701989c0c179b770248f5e':
  analysis version in redactionlog and changes
2022-01-13 09:23:34 +01:00
Timo Bejan 1cad386db8 analysis version in redactionlog and changes 2022-01-13 10:08:34 +02:00
cschabert 21a8af5ca5 add build before sonar job 2022-01-12 13:32:47 +01:00
69 changed files with 2959 additions and 1681 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.atlassian.bamboo</groupId>
<artifactId>bamboo-specs-parent</artifactId>
<version>8.0.3</version>
<version>8.1.3</version>
<relativePath/>
</parent>
@@ -17,6 +17,7 @@ import com.atlassian.bamboo.specs.api.builders.plan.branches.BranchCleanup;
import com.atlassian.bamboo.specs.api.builders.plan.branches.PlanBranchManagement;
import com.atlassian.bamboo.specs.api.builders.project.Project;
import com.atlassian.bamboo.specs.builders.task.CheckoutItem;
import com.atlassian.bamboo.specs.api.builders.Variable;
import com.atlassian.bamboo.specs.builders.task.InjectVariablesTask;
import com.atlassian.bamboo.specs.builders.task.ScriptTask;
import com.atlassian.bamboo.specs.builders.task.VcsCheckoutTask;
@@ -83,6 +84,7 @@ public class PlanSpec {
project(),
SERVICE_NAME, new BambooKey(SERVICE_KEY))
.description("Plan created from (enter repository url of your plan)")
.variables(new Variable("maven_add_param", ""))
.stages(new Stage("Default Stage")
.jobs(new Job("Default Job",
new BambooKey("JOB1"))
@@ -94,6 +96,7 @@ public class PlanSpec {
"rm -rf ./*"),
new VcsCheckoutTask()
.description("Checkout Default Repository")
.cleanCheckout(true)
.checkoutItems(new CheckoutItem().defaultRepository()),
new ScriptTask()
.description("Build")
@@ -115,7 +118,7 @@ public class PlanSpec {
.defaultRepository())
.dockerConfiguration(
new DockerConfiguration()
.image("nexus.iqser.com:5001/infra/maven:3.6.2-jdk-13-3.0.0")
.image("nexus.iqser.com:5001/infra/maven:3.8.4-openjdk-17-slim")
.volume("/etc/maven/settings.xml", "/usr/share/maven/ref/settings.xml")
.volume("/var/run/docker.sock", "/var/run/docker.sock")
)
@@ -147,6 +150,7 @@ public class PlanSpec {
"rm -rf ./*"),
new VcsCheckoutTask()
.description("Checkout Default Repository")
.cleanCheckout(true)
.checkoutItems(new CheckoutItem().defaultRepository()),
new ScriptTask()
.description("Sonar")
@@ -155,9 +159,9 @@ public class PlanSpec {
.argument(SERVICE_NAME))
.dockerConfiguration(
new DockerConfiguration()
.image("nexus.iqser.com:5001/infra/maven:3.6.2-jdk-13-3.0.0")
.image("nexus.iqser.com:5001/infra/maven:3.8.4-openjdk-17-slim")
.dockerRunArguments("--net=host")
.volume("/etc/maven/settings.xml", "/usr/share/maven/ref/settings.xml")
.volume("/etc/maven/settings.xml", "/usr/share/maven/conf/settings.xml")
.volume("/var/run/docker.sock", "/var/run/docker.sock")
)
)
@@ -165,8 +169,7 @@ public class PlanSpec {
.linkedRepositories("RED / " + SERVICE_NAME)
.triggers(
new ScheduledTrigger()
.scheduleOnceDaily(LocalTime.of(12, 00)),
new BitbucketServerTrigger())
.scheduleOnceDaily(LocalTime.of(23, 00)))
.planBranchManagement(new PlanBranchManagement()
.createForVcsBranchMatching("release.*")
.notificationForCommitters());
@@ -3,49 +3,57 @@ set -e
SERVICE_NAME=$1
if [[ "${bamboo_version_tag}" = "dev" ]]
then
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
if [[ "$bamboo_planRepository_branchName" == "master" ]]
then
branchVersion=$(cat pom.xml | grep -Eo "<version>.*" | sed -s 's|<version>\(.*\)\..*\(-*.*\)</version>|\1|')
latestVersion=$( semver $(git tag -l $branchVersion.*) | tail -n1)
newVersion="$(semver $latestVersion -p -i minor)"
elif [[ "$bamboo_planRepository_branchName" == release* ]]
then
branchVersion=$(echo $bamboo_planRepository_branchName | sed -s 's|release\/\([0-9]\+\.[0-9]\+\)\.x|\1|')
latestVersion=$( semver $(git tag -l $branchVersion.*) | tail -n1)
newVersion="$(semver $latestVersion -p -i patch)"
elif [[ "${bamboo_version_tag}" != "dev" ]]
then
newVersion="${bamboo_version_tag}"
else
mvn -f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
--no-transfer-progress \
${bamboo_maven_add_param} \
clean install \
-Djava.security.egd=file:/dev/./urandomelse
else
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
--no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
versions:set \
-DnewVersion=${bamboo_version_tag}
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
--no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-image-v1/pom.xml \
versions:set \
-DnewVersion=${bamboo_version_tag}
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
--no-transfer-progress \
clean deploy \
-e \
-DdeployAtEnd=true \
-Dmaven.wagon.http.ssl.insecure=true \
-Dmaven.wagon.http.ssl.allowall=true \
-Dmaven.wagon.http.ssl.ignore.validity.dates=true \
-DaltDeploymentRepository=iqser_release::default::https://nexus.iqser.com/repository/red-platform-releases
echo "gitTag=${bamboo_planRepository_1_branch}_${bamboo_buildNumber}" > git.tag
exit 0
fi
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
echo "gitTag=${newVersion}" > git.tag
mvn --no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
versions:set \
-DnewVersion=${newVersion}
mvn --no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-image-v1/pom.xml \
versions:set \
-DnewVersion=${newVersion}
mvn -f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
--no-transfer-progress \
clean deploy \
${bamboo_maven_add_param} \
-e \
-DdeployAtEnd=true \
-Dmaven.wagon.http.ssl.insecure=true \
-Dmaven.wagon.http.ssl.allowall=true \
-Dmaven.wagon.http.ssl.ignore.validity.dates=true \
-DaltDeploymentRepository=iqser_release::default::https://nexus.iqser.com/repository/red-platform-releases
mvn --no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-image-v1/pom.xml \
package
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
--no-transfer-progress \
mvn --no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-image-v1/pom.xml \
docker:push
if [[ "${bamboo_version_tag}" = "dev" ]]
then
echo "gitTag=${bamboo_planRepository_1_branch}_${bamboo_buildNumber}" > git.tag
else
echo "gitTag=${bamboo_version_tag}" > git.tag
fi
@@ -3,40 +3,42 @@ set -e
SERVICE_NAME=$1
echo "dependency-check:aggregate"
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
echo "build jar binaries"
mvn -f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
--no-transfer-progress \
clean install \
-Djava.security.egd=file:/dev/./urandomelse
echo "dependency-check:aggregate"
mvn --no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
org.owasp:dependency-check-maven:aggregate
if [[ -z "${bamboo_repository_pr_key}" ]]
then
echo "Sonar Scan for branch: ${bamboo_planRepository_1_branch}"
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
--no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
sonar:sonar \
-Dsonar.projectKey=RED_$SERVICE_NAME \
-Dsonar.host.url=https://sonarqube.iqser.com \
-Dsonar.login=${bamboo_sonarqube_api_token_secret} \
-Dsonar.branch.name=${bamboo_planRepository_1_branch} \
-Dsonar.dependencyCheck.jsonReportPath=target/dependency-check-report.json \
-Dsonar.dependencyCheck.xmlReportPath=target/dependency-check-report.xml \
-Dsonar.dependencyCheck.htmlReportPath=target/dependency-check-report.html
mvn --no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
sonar:sonar \
-Dsonar.projectKey=RED_$SERVICE_NAME \
-Dsonar.host.url=https://sonarqube.iqser.com \
-Dsonar.login=${bamboo_sonarqube_api_token_secret} \
-Dsonar.branch.name=${bamboo_planRepository_1_branch} \
-Dsonar.dependencyCheck.jsonReportPath=target/dependency-check-report.json \
-Dsonar.dependencyCheck.xmlReportPath=target/dependency-check-report.xml \
-Dsonar.dependencyCheck.htmlReportPath=target/dependency-check-report.html
else
echo "Sonar Scan for PR with key1: ${bamboo_repository_pr_key}"
${bamboo_capability_system_builder_mvn3_Maven_3}/bin/mvn \
--no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
sonar:sonar \
-Dsonar.projectKey=RED_$SERVICE_NAME \
-Dsonar.host.url=https://sonarqube.iqser.com \
-Dsonar.login=${bamboo_sonarqube_api_token_secret} \
-Dsonar.pullrequest.key=${bamboo_repository_pr_key} \
-Dsonar.pullrequest.branch=${bamboo_repository_pr_sourceBranch} \
-Dsonar.pullrequest.base=${bamboo_repository_pr_targetBranch} \
-Dsonar.dependencyCheck.jsonReportPath=target/dependency-check-report.json \
-Dsonar.dependencyCheck.xmlReportPath=target/dependency-check-report.xml \
-Dsonar.dependencyCheck.htmlReportPath=target/dependency-check-report.html
fi
mvn --no-transfer-progress \
-f ${bamboo_build_working_directory}/$SERVICE_NAME-v1/pom.xml \
sonar:sonar \
-Dsonar.projectKey=RED_$SERVICE_NAME \
-Dsonar.host.url=https://sonarqube.iqser.com \
-Dsonar.login=${bamboo_sonarqube_api_token_secret} \
-Dsonar.pullrequest.key=${bamboo_repository_pr_key} \
-Dsonar.pullrequest.branch=${bamboo_repository_pr_sourceBranch} \
-Dsonar.pullrequest.base=${bamboo_repository_pr_targetBranch} \
-Dsonar.dependencyCheck.jsonReportPath=target/dependency-check-report.json \
-Dsonar.dependencyCheck.xmlReportPath=target/dependency-check-report.xml \
-Dsonar.dependencyCheck.htmlReportPath=target/dependency-check-report.html
fi
+2 -2
View File
@@ -7,7 +7,7 @@
<artifactId>redaction-service</artifactId>
<groupId>com.iqser.red.service</groupId>
<version>1.0-SNAPSHOT</version>
<version>3.0-SNAPSHOT</version>
<packaging>pom</packaging>
@@ -18,4 +18,4 @@
<module>redaction-service-image-v1</module>
</modules>
</project>
</project>
@@ -1,4 +1,4 @@
FROM red/base-image:1.0.0
FROM red/redaction-service-base-v1:1.0.0
ARG PLATFORM_JAR
@@ -7,12 +7,3 @@ ENV PLATFORM_JAR ${PLATFORM_JAR}
ENV USES_ELASTICSEARCH false
COPY ["${PLATFORM_JAR}", "/"]
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
wget cabextract xfonts-utils fonts-liberation \
&& rm -rf /var/lib/apt/lists/*
RUN curl http://ftp.debian.org/debian/pool/contrib/m/msttcorefonts/ttf-mscorefonts-installer_3.8_all.deb -o /tmp/ttf-mscorefonts-installer_3.8_all.deb \
&& dpkg -i /tmp/ttf-mscorefonts-installer_3.8_all.deb \
&& rm /tmp/ttf-mscorefonts-installer_3.8_all.deb \
+2 -2
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>platform-dependency</artifactId>
<groupId>com.iqser.red</groupId>
<version>1.5.0</version>
<version>1.6.0</version>
<relativePath />
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -32,7 +32,7 @@
<dependency>
<groupId>com.iqser.red</groupId>
<artifactId>platform-commons-dependency</artifactId>
<version>1.7.0</version>
<version>1.11.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
@@ -12,7 +12,7 @@
<artifactId>redaction-service-api-v1</artifactId>
<properties>
<persistence-service.version>0.151.0</persistence-service.version>
<persistence-service.version>1.85.0</persistence-service.version>
</properties>
<dependencies>
@@ -1,17 +1,17 @@
package com.iqser.red.service.redaction.v1.model;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@@ -26,6 +26,7 @@ public class AnalyzeRequest {
private String dossierTemplateId;
private ManualRedactions manualRedactions;
private OffsetDateTime lastProcessed;
private int analysisNumber;
@Builder.Default
private Set<Integer> excludedPages = new HashSet<>();
@@ -28,6 +28,7 @@ public class AnalyzeResult {
private boolean wasReanalyzed;
private int analysisVersion;
private int analysisNumber;
private ManualRedactions manualRedactions;
@@ -13,6 +13,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class Change {
private int analysisNumber;
private ChangeType type;
private OffsetDateTime dateTime;
}
@@ -0,0 +1,21 @@
package com.iqser.red.service.redaction.v1.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImportedRedaction {
private String id;
@Builder.Default
private List<Rectangle> positions = new ArrayList<>();
}
@@ -0,0 +1,20 @@
package com.iqser.red.service.redaction.v1.model;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImportedRedactions {
@Builder.Default
private Map<Integer, List<ImportedRedaction>> importedRedactions = new HashMap<>();
}
@@ -0,0 +1,50 @@
package com.iqser.red.service.redaction.v1.model;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.BaseAnnotation;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ManualChange {
private AnnotationStatus annotationStatus;
private ManualRedactionType manualRedactionType;
private OffsetDateTime processedDate;
private OffsetDateTime requestedDate;
private String userId;
private Map<String, Object> propertyChanges = new HashMap<>();
public boolean isProcessed() {
return processedDate != null;
}
public static ManualChange from(BaseAnnotation baseAnnotation) {
ManualChange manualChange = new ManualChange();
manualChange.annotationStatus = baseAnnotation.getStatus();
manualChange.processedDate = baseAnnotation.getProcessedDate();
manualChange.requestedDate = baseAnnotation.getRequestDate();
manualChange.userId = baseAnnotation.getUser();
return manualChange;
}
public ManualChange withManualRedactionType(ManualRedactionType manualRedactionType) {
this.manualRedactionType = manualRedactionType;
return this;
}
public ManualChange withChange(String property, Object value) {
this.propertyChanges.put(property, value);
return this;
}
}
@@ -1,5 +1,13 @@
package com.iqser.red.service.redaction.v1.model;
public enum ManualRedactionType {
ADD, REMOVE, FORCE_REDACT, RECATEGORIZE, LEGAL_BASIS_CHANGE, RESIZE
ADD_LOCALLY,
ADD_TO_DICTIONARY,
REMOVE_LOCALLY,
REMOVE_FROM_DICTIONARY,
FORCE_REDACT,
FORCE_HINT,
RECATEGORIZE,
LEGAL_BASIS_CHANGE,
RESIZE
}
@@ -2,6 +2,6 @@ package com.iqser.red.service.redaction.v1.model;
public enum MessageType {
FULL_ANALYSE, REANALYSE, SURROUNDING_TEXT
ANALYSE, REANALYSE, STRUCTURE_ANALYSE, SURROUNDING_TEXT
}
@@ -18,6 +18,11 @@ public class RedactionLog {
*/
private long analysisVersion;
/**
* Which analysis created this redactionLog.
*/
private int analysisNumber;
private List<RedactionLogEntry> redactionLogEntry;
private List<LegalBasis> legalBasis;
@@ -26,4 +31,5 @@ public class RedactionLog {
private long rulesVersion = -1;
private long legalBasisVersion = -1;
}
@@ -2,16 +2,9 @@ package com.iqser.red.service.redaction.v1.model;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.Comment;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
@Data
@@ -28,15 +21,13 @@ public class RedactionLogEntry {
private int matchedRule;
private boolean rectangle;
private String legalBasis;
private boolean imported;
private boolean redacted;
private boolean isHint;
private boolean isRecommendation;
private boolean hasBeenResized;
private boolean hasBeenRecategorized;
private boolean hasLegalBasisChanged;
private boolean hasBeenForced;
private boolean hasBeenRemovedByManualOverride;
private boolean isFalsePositive;
private String section;
private float[] color;
@@ -44,11 +35,7 @@ public class RedactionLogEntry {
@Builder.Default
private List<Rectangle> positions = new ArrayList<>();
private int sectionNumber;
private boolean manual;
private AnnotationStatus status;
private ManualRedactionType manualRedactionType;
private String manualRedactionUserId;
private boolean isDictionaryEntry;
private String textBefore;
private String textAfter;
@@ -62,21 +49,39 @@ public class RedactionLogEntry {
private boolean isImage;
private boolean imageHasTransparency;
private boolean isDictionaryEntry;
private boolean isDossierDictionaryEntry;
private boolean excluded;
private String recategorizationType;
private String legalBasisChangeValue;
@EqualsAndHashCode.Exclude
@Builder.Default
private List<Change> changes = new ArrayList<>();
private Set<Engine> engines= new HashSet<>();
@EqualsAndHashCode.Exclude
@Builder.Default
private List<ManualChange> manualChanges = new ArrayList<>();
private Set<Engine> engines = new HashSet<>();
private Set<String> reference = new HashSet<>();
public boolean lastChangeIsRemoved() {
return last(changes).map(c -> c.getType() == ChangeType.REMOVED).orElse(false);
}
public boolean isLocalManualRedaction() {
return manualChanges.stream().anyMatch(mc -> mc.getManualRedactionType() == ManualRedactionType.ADD_LOCALLY &&
mc.getAnnotationStatus() == AnnotationStatus.APPROVED);
}
public boolean isManuallyRemoved() {
return manualChanges.stream().anyMatch(mc -> mc.getManualRedactionType() == ManualRedactionType.REMOVE_LOCALLY &&
mc.getAnnotationStatus() == AnnotationStatus.APPROVED);
}
private <T> Optional<T> last(List<T> list) {
return list.isEmpty() ? Optional.empty() : Optional.of(list.get(list.size() - 1));
}
}
@@ -1,10 +1,15 @@
package com.iqser.red.service.redaction.v1.model;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -22,4 +27,9 @@ public class RedactionRequest {
private ManualRedactions manualRedactions;
@Builder.Default
private Set<Integer> excludedPages = new HashSet<>();
private Colors colors;
private List<Type> types;
private boolean includeFalsePositives;
}
@@ -52,7 +52,7 @@ public class ClassificationService {
List<Float> headlineFontSizes) {
if (document.getFontSizeCounter().getMostPopular() == null) {
// TODO Figure out why this happens.
textBlock.setClassification("Other");
return;
}
if (PositionUtils.isOverBodyTextFrame(bodyTextFrame, textBlock, page.isRotated()) && (document.getFontSizeCounter()
@@ -1,13 +1,9 @@
package com.iqser.red.service.redaction.v1.server.client;
import java.util.List;
import java.util.Map;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import com.iqser.red.service.redaction.v1.server.client.model.EntityRecogintionEntity;
import com.iqser.red.service.redaction.v1.server.client.model.EntityRecognitionRequest;
import com.iqser.red.service.redaction.v1.server.client.model.NerEntities;
@@ -16,4 +12,5 @@ public interface EntityRecognitionClient {
@PostMapping(value = "/find_authors", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
NerEntities findAuthors(EntityRecognitionRequest entityRecognitionRequest);
}
@@ -16,6 +16,6 @@ import lombok.NoArgsConstructor;
public class NerEntities {
@Builder.Default
private Map<Integer, List<EntityRecogintionEntity>> result = new HashMap<>();
private Map<Integer, List<EntityRecogintionEntity>> data = new HashMap<>();
}
@@ -55,8 +55,7 @@ public class RedactionController implements RedactionResource {
public AnnotateResponse annotate(@RequestBody AnnotateRequest annotateRequest) {
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(annotateRequest
.getDossierId(), annotateRequest.getFileId(), FileType.ORIGIN));
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(annotateRequest.getDossierId(), annotateRequest.getFileId(), FileType.ORIGIN));
var mergedRedactionLog = getRedactionLog(RedactionRequest.builder()
.fileId(annotateRequest.getFileId())
.manualRedactions(annotateRequest.getManualRedactions())
@@ -85,13 +84,11 @@ public class RedactionController implements RedactionResource {
@Override
public RedactionResult classify(@RequestBody RedactionRequest redactionRequest) {
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest
.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
try {
Document classifiedDoc = pdfSegmentationService.parseDocument(storedObjectStream, null);
storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest
.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
try (PDDocument pdDocument = PDDocument.load(storedObjectStream)) {
pdDocument.setAllSecurityToBeRemoved(true);
@@ -113,13 +110,11 @@ public class RedactionController implements RedactionResource {
@Override
public RedactionResult sections(@RequestBody RedactionRequest redactionRequest) {
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest
.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
try {
Document classifiedDoc = pdfSegmentationService.parseDocument(storedObjectStream, null);
storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest
.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
try (PDDocument pdDocument = PDDocument.load(storedObjectStream)) {
pdDocument.setAllSecurityToBeRemoved(true);
@@ -143,8 +138,7 @@ public class RedactionController implements RedactionResource {
Document classifiedDoc;
try {
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest
.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(redactionRequest.getDossierId(), redactionRequest.getFileId(), FileType.ORIGIN));
classifiedDoc = pdfSegmentationService.parseDocument(storedObjectStream, null);
} catch (Exception e) {
throw new RedactionException(e);
@@ -175,8 +169,7 @@ public class RedactionController implements RedactionResource {
@Override
public RedactionLog getRedactionLog(RedactionRequest redactionRequest) {
log.info("Requested preview for: {}", redactionRequest);
dictionaryService.updateDictionary(redactionRequest.getDossierTemplateId(), redactionRequest.getDossierId());
log.debug("Requested preview for: {}", redactionRequest);
var redactionLog = redactionStorageService.getRedactionLog(redactionRequest.getDossierId(), redactionRequest.getFileId());
@@ -186,8 +179,7 @@ public class RedactionController implements RedactionResource {
log.info("Loaded redaction log with computationalVersion: {}", redactionLog.getAnalysisVersion());
SectionGrid sectionGrid = redactionStorageService.getSectionGrid(redactionRequest.getDossierId(), redactionRequest
.getFileId());
SectionGrid sectionGrid = redactionStorageService.getSectionGrid(redactionRequest.getDossierId(), redactionRequest.getFileId());
if (sectionGrid.getSections().isEmpty()) {
log.info("SectionGrid does not have headlines set. Computing headlines now!");
@@ -196,8 +188,7 @@ public class RedactionController implements RedactionResource {
// enhance section grid with headline data
for (var sectionText : text.getSectionTexts()) {
sectionGrid.getSections()
.add(new SectionGrid.SectionGridSection(sectionText.getSectionNumber(), sectionText.getHeadline(), sectionText
.getSectionAreas()
.add(new SectionGrid.SectionGridSection(sectionText.getSectionNumber(), sectionText.getHeadline(), sectionText.getSectionAreas()
.stream()
.map(SectionArea::getPage)
.collect(Collectors.toSet()), sectionText.getSectionAreas()));
@@ -206,13 +197,11 @@ public class RedactionController implements RedactionResource {
}
log.info("Loaded redaction log with computationalVersion: {}", redactionLog.getAnalysisVersion());
if (redactionLog.getAnalysisVersion() == 0) {
// old redaction logs are returned directly
return redactionLog;
} else {
return redactionLogMergeService.mergeRedactionLogData(redactionLog, sectionGrid, redactionRequest.getDossierTemplateId(), redactionRequest
.getManualRedactions(), redactionRequest.getExcludedPages());
}
var merged = redactionLogMergeService.mergeRedactionLogData(redactionLog, sectionGrid, redactionRequest.getDossierTemplateId(), redactionRequest.getManualRedactions(), redactionRequest.getExcludedPages(), redactionRequest.getTypes(), redactionRequest.getColors());
merged.getRedactionLogEntry().removeIf(e -> e.isFalsePositive() && !redactionRequest.isIncludeFalsePositives());
return merged;
}
@@ -234,11 +223,9 @@ public class RedactionController implements RedactionResource {
@PathVariable("fileId") String fileId,
@RequestBody ManualRedactions manualRedactions) {
long start = System.currentTimeMillis();
var result = manualRedactionSurroundingTextService.addSurroundingText(dossierId, fileId, manualRedactions);
log.info("add surrounding text for manual redaction in dossierId {} and fileId {} took: {}", dossierId, fileId, System
.currentTimeMillis() - start);
return result;
log.info("Added surrounding text for manual redaction in dossierId {} and fileId {} took: {}", dossierId, fileId, result.getDuration());
return result.getManualRedactions();
}
}
@@ -0,0 +1,25 @@
package com.iqser.red.service.redaction.v1.server.queue;
import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfiguration.REDACTION_QUEUE;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
public class MessageReceiver {
private final RedactionMessageReceiver redactionMessageReceiver;
@RabbitHandler
@RabbitListener(queues = REDACTION_QUEUE)
public void receiveAnalyzeRequest(String in) throws JsonProcessingException {
redactionMessageReceiver.receiveAnalyzeRequest(in, false);
}
}
@@ -1,8 +1,10 @@
package com.iqser.red.service.redaction.v1.server.queue;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -10,11 +12,24 @@ import org.springframework.context.annotation.Configuration;
@RequiredArgsConstructor
public class MessagingConfiguration {
public static final String REDACTION_QUEUE = "redactionQueue";
public static final String REDACTION_DQL = "redactionDQL";
public static final String REDACTION_PRIORITY_QUEUE = "redactionPriorityQueue";
@Bean
@ConditionalOnProperty(prefix = "redaction-service", name = "priorityMode", havingValue = "false")
public MessageReceiver messageReceiver(RedactionMessageReceiver redactionMessageReceiver){
return new MessageReceiver(redactionMessageReceiver);
}
@Bean
@ConditionalOnProperty(prefix = "redaction-service", name = "priorityMode", havingValue = "true")
public PriorityMessageReceiver priorityMessageReceiver(RedactionMessageReceiver redactionMessageReceiver){
return new PriorityMessageReceiver(redactionMessageReceiver);
}
@Bean
public Queue redactionQueue() {
@@ -27,9 +42,21 @@ public class MessagingConfiguration {
}
@Bean
public Queue redactionPriorityQueue() {
return QueueBuilder.durable(REDACTION_PRIORITY_QUEUE)
.withArgument("x-dead-letter-exchange", "")
.withArgument("x-dead-letter-routing-key", REDACTION_DQL)
.maxPriority(2)
.build();
}
@Bean
public Queue redactionDeadLetterQueue() {
return QueueBuilder.durable(REDACTION_DQL).build();
}
}
@@ -0,0 +1,27 @@
package com.iqser.red.service.redaction.v1.server.queue;
import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfiguration.REDACTION_PRIORITY_QUEUE;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
public class PriorityMessageReceiver {
private final RedactionMessageReceiver redactionMessageReceiver;
@RabbitHandler
@RabbitListener(queues = REDACTION_PRIORITY_QUEUE)
public void receiveAnalyzeRequest(String in) throws JsonProcessingException {
redactionMessageReceiver.receiveAnalyzeRequest(in, true);
}
}
@@ -1,5 +1,12 @@
package com.iqser.red.service.redaction.v1.server.queue;
import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfiguration.REDACTION_DQL;
import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfiguration.REDACTION_QUEUE;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.redaction.v1.model.AnalyzeRequest;
@@ -8,18 +15,10 @@ import com.iqser.red.service.redaction.v1.model.StructureAnalyzeRequest;
import com.iqser.red.service.redaction.v1.server.client.FileStatusProcessingUpdateClient;
import com.iqser.red.service.redaction.v1.server.redaction.service.AnalyzeService;
import com.iqser.red.service.redaction.v1.server.redaction.service.ManualRedactionSurroundingTextService;
import com.iqser.red.service.redaction.v1.server.redaction.service.NerAnalyserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfiguration.REDACTION_DQL;
import static com.iqser.red.service.redaction.v1.server.queue.MessagingConfiguration.REDACTION_QUEUE;
@Slf4j
@Service
@RequiredArgsConstructor
@@ -28,43 +27,35 @@ public class RedactionMessageReceiver {
private final ObjectMapper objectMapper;
private final AnalyzeService analyzeService;
private final FileStatusProcessingUpdateClient fileStatusProcessingUpdateClient;
private final NerAnalyserService nerAnalyserService;
private final ManualRedactionSurroundingTextService manualRedactionSurroundingTextService;
@RabbitHandler
@RabbitListener(queues = REDACTION_QUEUE)
public void receiveAnalyzeRequest(String in) throws JsonProcessingException {
public void receiveAnalyzeRequest(String in, boolean priority) throws JsonProcessingException {
var analyzeRequest = objectMapper.readValue(in, AnalyzeRequest.class);
log.info("Processing analyze request for file: {}", analyzeRequest.getFileId());
log.info("Processing priority: {} analyze request for file: {}", priority, analyzeRequest.getFileId());
AnalyzeResult result = null;
switch (analyzeRequest.getMessageType()){
switch (analyzeRequest.getMessageType()) {
case REANALYSE:
result = analyzeService.reanalyze(analyzeRequest);
log.info("Successfully reanalyzed dossier {} file {} took: {}", analyzeRequest.getDossierId(), analyzeRequest.getFileId(), result.getDuration());
break;
case FULL_ANALYSE:
// TODO Seperate stucture analysis by other queue
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(analyzeRequest.getDossierId(), analyzeRequest.getFileId()));
// TODO NerEntities should be computed and stored in entity-recognition-service, should be triggered by a seperate queue after structure analysis
nerAnalyserService.computeNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
result = analyzeService.analyze(analyzeRequest);
log.info("Successfully analyzed dossier {} file {} took: {}", analyzeRequest.getDossierId(), analyzeRequest.getFileId(), result
.getDuration());
case STRUCTURE_ANALYSE:
result = analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(analyzeRequest.getDossierId(), analyzeRequest.getFileId()));
log.info("Successfully analyzed structure dossier {} file {} took: {}", analyzeRequest.getDossierId(), analyzeRequest.getFileId(), result.getDuration());
break;
case ANALYSE:
result = analyzeService.analyze(analyzeRequest);
log.info("Successfully analyzed dossier {} file {} took: {}", analyzeRequest.getDossierId(), analyzeRequest.getFileId(), result.getDuration());
break;
case SURROUNDING_TEXT:
var manualRedactions = manualRedactionSurroundingTextService.addSurroundingText(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), analyzeRequest.getManualRedactions());
result = AnalyzeResult.builder()
.dossierId(analyzeRequest.getDossierId())
.fileId(analyzeRequest.getFileId())
.manualRedactions(manualRedactions)
.build();
result = manualRedactionSurroundingTextService.addSurroundingText(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), analyzeRequest.getManualRedactions());
log.info("Successfully added surrounding text for manual redaction in dossierId {} and fileId {} took: {}", analyzeRequest.getDossierId(), analyzeRequest.getFileId(), result.getDuration());
break;
}
@@ -1,22 +1,16 @@
package com.iqser.red.service.redaction.v1.server.redaction.model;
import static java.util.stream.Collectors.toSet;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntry;
import lombok.Data;
import lombok.Getter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.Data;
import lombok.Getter;
@Data
public class Dictionary {
public static final String RECOMMENDATION_PREFIX = "recommendation_";
@Getter
private List<DictionaryModel> dictionaryModels;
private Map<String, DictionaryModel> localAccessMap = new HashMap<>();
@@ -26,6 +20,7 @@ public class Dictionary {
public Dictionary(List<DictionaryModel> dictionaryModels, DictionaryVersion version) {
this.dictionaryModels = dictionaryModels;
this.dictionaryModels.forEach(dm -> localAccessMap.put(dm.getType(), dm));
this.version = version;
@@ -33,6 +28,7 @@ public class Dictionary {
public int getDictionaryRank(String type) {
if (!localAccessMap.containsKey(type)) {
return 0;
}
@@ -40,16 +36,6 @@ public class Dictionary {
}
public boolean isRecommendation(String type) {
DictionaryModel model = localAccessMap.get(type);
if (model != null) {
return model.isRecommendation();
}
return false;
}
public boolean hasLocalEntries() {
return dictionaryModels.stream().anyMatch(dm -> !dm.getLocalEntries().isEmpty());
@@ -62,16 +48,18 @@ public class Dictionary {
}
public DictionaryModel getType(String type) {
return localAccessMap.get(type);
}
public boolean containsValue(String type, String value) {
return localAccessMap.containsKey(type) && localAccessMap.get(type)
.getValues(false)
.contains(value) || localAccessMap.containsKey(type) && localAccessMap.get(type)
.getValues(true)
.contains(value) || localAccessMap.containsKey(RECOMMENDATION_PREFIX + type) && localAccessMap.get(RECOMMENDATION_PREFIX + type)
.getValues(false)
.contains(value) || localAccessMap.containsKey(RECOMMENDATION_PREFIX + type) && localAccessMap.get(RECOMMENDATION_PREFIX + type)
.getValues(true)
.contains(value);
}
@@ -85,6 +73,7 @@ public class Dictionary {
return false;
}
public boolean isCaseInsensitiveDictionary(String type) {
DictionaryModel dictionaryModel = localAccessMap.get(type);
@@ -0,0 +1,26 @@
package com.iqser.red.service.redaction.v1.server.redaction.model;
import java.util.HashSet;
import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntry;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DictionaryEntries {
@Builder.Default
Set<DictionaryEntry> entries = new HashSet<>();
@Builder.Default
Set<DictionaryEntry> falsePositives = new HashSet<>();
@Builder.Default
Set<DictionaryEntry> falseRecommendations = new HashSet<>();
}
@@ -19,8 +19,9 @@ public class DictionaryModel implements Serializable {
private float[] color;
private boolean caseInsensitive;
private boolean hint;
private boolean recommendation;
private Set<DictionaryEntry> entries;
private Set<DictionaryEntry> falsePositives;
private Set<DictionaryEntry> falseRecommendations;
private Set<String> localEntries;
private boolean isDossierDictionary;
@@ -29,4 +30,14 @@ public class DictionaryModel implements Serializable {
.toSet());
}
public Set<String> getFalsePositiveValues() {
return falsePositives.stream().filter(e -> !e.isDeleted()).map(e -> e.getValue()).collect(Collectors
.toSet());
}
public Set<String> getFalseRecommendationValues() {
return falseRecommendations.stream().filter(e -> !e.isDeleted()).map(e -> e.getValue()).collect(Collectors
.toSet());
}
}
@@ -0,0 +1,23 @@
package com.iqser.red.service.redaction.v1.server.redaction.model;
import java.util.HashSet;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Entities {
@Builder.Default
private Set<Entity> entities = new HashSet<>();
@Builder.Default
private Set<Entity> nerEntities = new HashSet<>();
}
@@ -15,8 +15,8 @@ import java.util.Set;
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Entity implements ReasonHolder {
private final String word;
private final String type;
private String word;
private String type;
private boolean redaction;
private String redactionReason;
private String legalBasis;
@@ -48,11 +48,13 @@ public class Entity implements ReasonHolder {
private Set<Entity> references = new HashSet<>();
private EntityType entityType;
public Entity(String word, String type, boolean redaction, String redactionReason,
List<EntityPositionSequence> positionSequences, String headline, int matchedRule, int sectionNumber,
String legalBasis, boolean isDictionaryEntry, String textBefore, String textAfter, Integer start,
Integer end, boolean isDossierDictionaryEntry, Set<Engine> engines, Set<Entity> references) {
Integer end, boolean isDossierDictionaryEntry, Set<Engine> engines, Set<Entity> references, EntityType entityType) {
this.word = word;
this.type = type;
@@ -71,11 +73,12 @@ public class Entity implements ReasonHolder {
this.isDossierDictionaryEntry = isDossierDictionaryEntry;
this.engines = engines;
this.references = references;
this.entityType = entityType;
}
public Entity(String word, String type, Integer start, Integer end, String headline, int sectionNumber,
boolean isDictionaryEntry, boolean isDossierDictionaryEntry, Engine engine) {
boolean isDictionaryEntry, boolean isDossierDictionaryEntry, Engine engine, EntityType entityType) {
this.word = word;
this.type = type;
@@ -86,6 +89,7 @@ public class Entity implements ReasonHolder {
this.isDictionaryEntry = isDictionaryEntry;
this.isDossierDictionaryEntry = isDossierDictionaryEntry;
this.engines.add(engine);
this.entityType = entityType;
}
@@ -0,0 +1,5 @@
package com.iqser.red.service.redaction.v1.server.redaction.model;
public enum EntityType {
ENTITY, RECOMMENDATION, FALSE_POSITIVE, FALSE_RECOMMENDATION
}
@@ -10,6 +10,6 @@ public class ImageServiceResponse {
private String dossierId;
private String fileId;
private List<ImageMetadata> imageMetadata = new ArrayList<>();
private List<ImageMetadata> data = new ArrayList<>();
}
@@ -1,5 +1,7 @@
package com.iqser.red.service.redaction.v1.server.redaction.service;
import static com.iqser.red.service.redaction.v1.server.redaction.service.ImportedRedactionService.IMPORTED_REDACTION_TYPE;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -8,18 +10,29 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.*;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.redaction.v1.model.*;
import com.iqser.red.service.redaction.v1.model.Rectangle;
import org.kie.api.runtime.KieContainer;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.IdRemoval;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualForceRedaction;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualImageRecategorization;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualLegalBasisChange;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.redaction.v1.model.AnalyzeRequest;
import com.iqser.red.service.redaction.v1.model.AnalyzeResult;
import com.iqser.red.service.redaction.v1.model.Rectangle;
import com.iqser.red.service.redaction.v1.model.RedactionLog;
import com.iqser.red.service.redaction.v1.model.RedactionLogEntry;
import com.iqser.red.service.redaction.v1.model.SectionArea;
import com.iqser.red.service.redaction.v1.model.SectionGrid;
import com.iqser.red.service.redaction.v1.model.StructureAnalyzeRequest;
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
import com.iqser.red.service.redaction.v1.server.classification.model.SectionText;
import com.iqser.red.service.redaction.v1.server.classification.model.Text;
import com.iqser.red.service.redaction.v1.server.client.LegalBasisClient;
import com.iqser.red.service.redaction.v1.server.client.model.NerEntities;
import com.iqser.red.service.redaction.v1.server.exception.RedactionException;
import com.iqser.red.service.redaction.v1.server.redaction.model.Dictionary;
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryIncrement;
@@ -54,11 +67,11 @@ public class AnalyzeService {
private final RedactionServiceSettings redactionServiceSettings;
private final SectionTextBuilderService sectionTextBuilderService;
private final SectionGridCreatorService sectionGridCreatorService;
private final NerAnalyserService nerAnalyserService;
private final ImageService imageService;
private final ImportedRedactionService importedRedactionService;
public void analyzeDocumentStructure(StructureAnalyzeRequest analyzeRequest) {
public AnalyzeResult analyzeDocumentStructure(StructureAnalyzeRequest analyzeRequest) {
long startTime = System.currentTimeMillis();
@@ -66,12 +79,10 @@ public class AnalyzeService {
Document classifiedDoc;
try {
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(analyzeRequest
.getDossierId(), analyzeRequest.getFileId(), FileType.ORIGIN));
var storedObjectStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.ORIGIN));
Map<Integer, List<PdfImage>> pdfImages = null;
if(redactionServiceSettings.isEnableImageClassification()) {
if (redactionServiceSettings.isEnableImageClassification()) {
pdfImages = imageService.convertImages(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
}
classifiedDoc = pdfSegmentationService.parseDocument(storedObjectStream, pdfImages);
@@ -83,54 +94,27 @@ public class AnalyzeService {
List<SectionText> sectionTexts = sectionTextBuilderService.buildSectionText(classifiedDoc);
sectionGridCreatorService.createSectionGrid(classifiedDoc, pageCount);
Text text = new Text(pageCount,sectionTexts);
Text text = new Text(pageCount, sectionTexts);
// enhance section grid with headline data
sectionTexts.forEach(sectionText -> classifiedDoc
.getSectionGrid().getSections().add(new SectionGrid.SectionGridSection(sectionText.getSectionNumber(),
sectionText.getHeadline(),
sectionText.getSectionAreas().stream().map(SectionArea::getPage).collect(Collectors.toSet()),
sectionText.getSectionAreas())));
sectionTexts.forEach(sectionText -> classifiedDoc.getSectionGrid()
.getSections()
.add(new SectionGrid.SectionGridSection(sectionText.getSectionNumber(), sectionText.getHeadline(), sectionText.getSectionAreas()
.stream()
.map(SectionArea::getPage)
.collect(Collectors.toSet()), sectionText.getSectionAreas())));
log.info("Store text and section grid for file {} in dossier {}", analyzeRequest.getFileId(), analyzeRequest.getDossierId());
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.TEXT, text);
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.SECTION_GRID, classifiedDoc
.getSectionGrid());
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.SECTION_GRID, classifiedDoc.getSectionGrid());
log.info("Document structure analysis successful, took: {}", System.currentTimeMillis() - startTime);
}
public AnalyzeResult analyze(AnalyzeRequest analyzeRequest) {
long startTime = System.currentTimeMillis();
var text = redactionStorageService.getText(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
var nerEntities = redactionStorageService.getNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
if(redactionServiceSettings.isEnableEntityRecognition() && nerEntities == null){
nerAnalyserService.computeNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
nerEntities = redactionStorageService.getNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
}
dictionaryService.updateDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
KieContainer kieContainer = droolsExecutionService.updateRules(analyzeRequest.getDossierTemplateId());
long rulesVersion = droolsExecutionService.getRulesVersion(analyzeRequest.getDossierTemplateId());
Dictionary dictionary = dictionaryService.getDeepCopyDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest
.getDossierId());
PageEntities pageEntities = entityRedactionService.findEntities(dictionary, text.getSectionTexts(), kieContainer, analyzeRequest, nerEntities);
dictionaryService.updateExternalDictionary(dictionary, analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
List<RedactionLogEntry> redactionLogEntries = redactionLogCreatorService.createRedactionLog(pageEntities, text.getNumberOfPages(), analyzeRequest
.getDossierTemplateId());
var legalBasis = legalBasisClient.getLegalBasisMapping(analyzeRequest.getDossierTemplateId());
var redactionLog = new RedactionLog(redactionServiceSettings.getAnalysisVersion(), redactionLogEntries, legalBasis, dictionary
.getVersion()
.getDossierTemplateVersion(), dictionary.getVersion()
.getDossierVersion(), rulesVersion, legalBasisClient.getVersion(analyzeRequest.getDossierTemplateId()));
return finalizeAnalysis(analyzeRequest, startTime, redactionLog, text, dictionary.getVersion(), false);
return AnalyzeResult.builder()
.dossierId(analyzeRequest.getDossierId())
.fileId(analyzeRequest.getFileId())
.duration(System.currentTimeMillis() - startTime)
.numberOfPages(text.getNumberOfPages())
.analysisVersion(redactionServiceSettings.getAnalysisVersion())
.build();
}
@@ -147,8 +131,7 @@ public class AnalyzeService {
return analyze(analyzeRequest);
}
DictionaryIncrement dictionaryIncrement = dictionaryService.getDictionaryIncrements(analyzeRequest.getDossierTemplateId(), new DictionaryVersion(redactionLog
.getDictionaryVersion(), redactionLog.getDossierDictionaryVersion()), analyzeRequest.getDossierId());
DictionaryIncrement dictionaryIncrement = dictionaryService.getDictionaryIncrements(analyzeRequest.getDossierTemplateId(), new DictionaryVersion(redactionLog.getDictionaryVersion(), redactionLog.getDossierDictionaryVersion()), analyzeRequest.getDossierId());
Set<Integer> sectionsToReanalyse = !analyzeRequest.getSectionsToReanalyse()
.isEmpty() ? analyzeRequest.getSectionsToReanalyse() : findSectionsToReanalyse(dictionaryIncrement, redactionLog, text, analyzeRequest);
@@ -157,10 +140,11 @@ public class AnalyzeService {
return finalizeAnalysis(analyzeRequest, startTime, redactionLog, text, dictionaryIncrement.getDictionaryVersion(), true);
}
var nerEntities = redactionStorageService.getNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
if(redactionServiceSettings.isEnableEntityRecognition() && nerEntities == null){
nerAnalyserService.computeNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
NerEntities nerEntities;
if (redactionServiceSettings.isNerServiceEnabled()) {
nerEntities = redactionStorageService.getNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
} else {
nerEntities = NerEntities.builder().build();
}
List<SectionText> reanalysisSections = text.getSectionTexts()
@@ -170,19 +154,54 @@ public class AnalyzeService {
KieContainer kieContainer = droolsExecutionService.updateRules(analyzeRequest.getDossierTemplateId());
Dictionary dictionary = dictionaryService.getDeepCopyDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest
.getDossierId());
Dictionary dictionary = dictionaryService.getDeepCopyDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
PageEntities pageEntities = entityRedactionService.findEntities(dictionary, reanalysisSections, kieContainer, analyzeRequest, nerEntities);
var newRedactionLogEntries = redactionLogCreatorService.createRedactionLog(pageEntities, text.getNumberOfPages(), analyzeRequest
.getDossierTemplateId());
var newRedactionLogEntries = redactionLogCreatorService.createRedactionLog(pageEntities, text.getNumberOfPages(), analyzeRequest.getDossierTemplateId());
var importedRedactionFilteredEntries = importedRedactionService.processImportedRedactions(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId(), analyzeRequest.getFileId(), newRedactionLogEntries, false, analyzeRequest.getManualRedactions());
redactionLog.getRedactionLogEntry()
.removeIf(entry -> sectionsToReanalyse.contains(entry.getSectionNumber()) && !entry.getType()
.equals(IMPORTED_REDACTION_TYPE));
redactionLog.getRedactionLogEntry().addAll(importedRedactionFilteredEntries);
redactionLog.getRedactionLogEntry().removeIf(entry -> sectionsToReanalyse.contains(entry.getSectionNumber()));
redactionLog.getRedactionLogEntry().addAll(newRedactionLogEntries);
return finalizeAnalysis(analyzeRequest, startTime, redactionLog, text, dictionaryIncrement.getDictionaryVersion(), true);
}
public AnalyzeResult analyze(AnalyzeRequest analyzeRequest) {
long startTime = System.currentTimeMillis();
var text = redactionStorageService.getText(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
NerEntities nerEntities;
if (redactionServiceSettings.isNerServiceEnabled()) {
nerEntities = redactionStorageService.getNerEntities(analyzeRequest.getDossierId(), analyzeRequest.getFileId());
} else {
nerEntities = NerEntities.builder().build();
}
dictionaryService.updateDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
KieContainer kieContainer = droolsExecutionService.updateRules(analyzeRequest.getDossierTemplateId());
long rulesVersion = droolsExecutionService.getRulesVersion(analyzeRequest.getDossierTemplateId());
Dictionary dictionary = dictionaryService.getDeepCopyDictionary(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId());
PageEntities pageEntities = entityRedactionService.findEntities(dictionary, text.getSectionTexts(), kieContainer, analyzeRequest, nerEntities);
List<RedactionLogEntry> redactionLogEntries = redactionLogCreatorService.createRedactionLog(pageEntities, text.getNumberOfPages(), analyzeRequest.getDossierTemplateId());
var legalBasis = legalBasisClient.getLegalBasisMapping(analyzeRequest.getDossierTemplateId());
var redactionLog = new RedactionLog(redactionServiceSettings.getAnalysisVersion(), analyzeRequest.getAnalysisNumber(), redactionLogEntries, legalBasis, dictionary.getVersion()
.getDossierTemplateVersion(), dictionary.getVersion()
.getDossierVersion(), rulesVersion, legalBasisClient.getVersion(analyzeRequest.getDossierTemplateId()));
var importedRedactionFilteredEntries = importedRedactionService.processImportedRedactions(analyzeRequest.getDossierTemplateId(), analyzeRequest.getDossierId(), analyzeRequest.getFileId(), redactionLog.getRedactionLogEntry(), true, analyzeRequest.getManualRedactions());
redactionLog.setRedactionLogEntry(importedRedactionFilteredEntries);
return finalizeAnalysis(analyzeRequest, startTime, redactionLog, text, dictionary.getVersion(), false);
}
private Set<Integer> findSectionsToReanalyse(DictionaryIncrement dictionaryIncrement, RedactionLog redactionLog,
Text text, AnalyzeRequest analyzeRequest) {
@@ -192,7 +211,7 @@ public class AnalyzeService {
Set<Integer> sectionsToReanalyse = new HashSet<>();
Map<Integer, Set<Image>> imageEntries = new HashMap<>();
for (RedactionLogEntry entry : redactionLog.getRedactionLogEntry()) {
if (entry.isManual() || relevantManuallyModifiedAnnotationIds.contains(entry.getId())) {
if (entry.isLocalManualRedaction() || relevantManuallyModifiedAnnotationIds.contains(entry.getId())) {
sectionsToReanalyse.add(entry.getSectionNumber());
}
if (entry.isImage()) {
@@ -223,9 +242,8 @@ public class AnalyzeService {
excludeExcludedPages(redactionLog, analyzeRequest.getExcludedPages());
var redactionLogChange = redactionChangeLogService.computeChanges(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), redactionLog);
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.REDACTION_LOG, redactionLogChange
.getRedactionLog());
var redactionLogChange = redactionChangeLogService.computeChanges(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), redactionLog, analyzeRequest.getAnalysisNumber());
redactionStorageService.storeObject(analyzeRequest.getDossierId(), analyzeRequest.getFileId(), FileType.REDACTION_LOG, redactionLogChange.getRedactionLog());
long duration = System.currentTimeMillis() - startTime;
@@ -236,11 +254,13 @@ public class AnalyzeService {
.numberOfPages(text.getNumberOfPages())
.hasUpdates(redactionLogChange.isHasChanges())
.analysisVersion(redactionServiceSettings.getAnalysisVersion())
.analysisNumber(analyzeRequest.getAnalysisNumber())
.rulesVersion(redactionLog.getRulesVersion())
.dictionaryVersion(redactionLog.getDictionaryVersion())
.legalBasisVersion(redactionLog.getLegalBasisVersion())
.dossierDictionaryVersion(redactionLog.getDossierDictionaryVersion())
.wasReanalyzed(isReanalysis)
.manualRedactions(analyzeRequest.getManualRedactions())
.build();
}
@@ -257,8 +277,9 @@ public class AnalyzeService {
.stream()
.map(ManualImageRecategorization::getAnnotationId), Stream.concat(manualRedactions.getIdsToRemove()
.stream()
.map(IdRemoval::getAnnotationId), manualRedactions.getForceRedactions().stream().map(ManualForceRedaction::getAnnotationId))))
.collect(Collectors.toSet());
.map(IdRemoval::getAnnotationId), manualRedactions.getForceRedactions()
.stream()
.map(ManualForceRedaction::getAnnotationId)))).collect(Collectors.toSet());
}
@@ -1,6 +1,7 @@
package com.iqser.red.service.redaction.v1.server.redaction.service;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.Comment;
import com.iqser.red.service.redaction.v1.model.Rectangle;
import com.iqser.red.service.redaction.v1.model.*;
import lombok.RequiredArgsConstructor;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -15,13 +16,10 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationText;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup;
import org.springframework.stereotype.Service;
import java.awt.Color;
import java.awt.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
@Service
@@ -107,10 +105,10 @@ public class AnnotationService {
private String createAnnotationContent(RedactionLogEntry redactionLogEntry) {
if (redactionLogEntry.isManual()) {
if (redactionLogEntry.isLocalManualRedaction()) {
return "\nManual Redaction\n\nIn Section : \"" + redactionLogEntry.getSection() + "\"";
}
return "\nRule " + redactionLogEntry.getMatchedRule() + " matched\n\n" + redactionLogEntry.getReason() + "\n\nLegal basis:" + redactionLogEntry
return redactionLogEntry.getType() + " \nRule " + redactionLogEntry.getMatchedRule() + " matched\n\n" + redactionLogEntry.getReason() + "\n\nLegal basis:" + redactionLogEntry
.getLegalBasis() + "\n\nIn section: \"" + redactionLogEntry.getSection() + "\"";
}
@@ -5,8 +5,6 @@ import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ty
import com.iqser.red.service.redaction.v1.server.client.DictionaryClient;
import com.iqser.red.service.redaction.v1.server.redaction.model.Dictionary;
import com.iqser.red.service.redaction.v1.server.redaction.model.*;
import com.iqser.red.service.redaction.v1.server.redaction.utils.IdBuilder;
import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -14,7 +12,8 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.stereotype.Service;
import java.awt.Color;
import java.awt.*;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
@@ -36,13 +35,13 @@ public class DictionaryService {
long dossierTemplateDictionaryVersion = dictionaryClient.getVersion(dossierTemplateId);
var dossierTemplateDictionary = dictionariesByDossierTemplate.get(dossierTemplateId);
if (dossierTemplateDictionary == null || dossierTemplateDictionaryVersion > dossierTemplateDictionary.getDictionaryVersion()) {
updateDictionaryEntry(dossierTemplateId, dossierTemplateDictionaryVersion, null);
updateDictionaryEntry(dossierTemplateId, dossierTemplateDictionaryVersion, getVersion(dossierTemplateDictionary), null);
}
long dossierDictionaryVersion = dictionaryClient.getVersionForDossier(dossierId);
var dossierDictionary = dictionariesByDossier.get(dossierId);
if (dossierDictionary == null || dossierDictionaryVersion > dossierDictionary.getDictionaryVersion()) {
updateDictionaryEntry(dossierTemplateId, dossierDictionaryVersion, dossierId);
updateDictionaryEntry(dossierTemplateId, dossierDictionaryVersion, getVersion(dossierDictionary), dossierId);
}
return DictionaryVersion.builder().dossierTemplateVersion(dossierTemplateDictionaryVersion).dossierVersion(dossierDictionaryVersion).build();
@@ -61,9 +60,19 @@ public class DictionaryService {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
dictionaryModel.getFalsePositives().forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierTemplateVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
dictionaryModel.getFalseRecommendations().forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierTemplateVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
});
if(dictionariesByDossier.containsKey(dossierId)) {
if (dictionariesByDossier.containsKey(dossierId)) {
dictionaryModels = dictionariesByDossier.get(dossierId).getDictionary();
dictionaryModels.forEach(dictionaryModel -> {
dictionaryModel.getEntries().forEach(dictionaryEntry -> {
@@ -71,6 +80,16 @@ public class DictionaryService {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
dictionaryModel.getFalsePositives().forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
dictionaryModel.getFalseRecommendations().forEach(dictionaryEntry -> {
if (dictionaryEntry.getVersion() > fromVersion.getDossierVersion()) {
newValues.add(new DictionaryIncrementValue(dictionaryEntry.getValue(), dictionaryModel.isCaseInsensitive()));
}
});
});
}
@@ -78,7 +97,7 @@ public class DictionaryService {
}
private void updateDictionaryEntry(String dossierTemplateId, long version, String dossierId) {
private void updateDictionaryEntry(String dossierTemplateId, long version, Long currentVersion, String dossierId) {
try {
DictionaryRepresentation dictionaryRepresentation = new DictionaryRepresentation();
@@ -88,8 +107,46 @@ public class DictionaryService {
List<DictionaryModel> dictionary = typeResponse
.stream()
.map(t -> new DictionaryModel(t.getType(), t.getRank(), convertColor(t.getHexColor()), t.isCaseInsensitive(), t
.isHint(), t.isRecommendation(), convertEntries(t.getId()), new HashSet<>(), dossierId != null))
.map(t -> {
Optional<DictionaryModel> oldModel;
if (dossierId == null) {
var representation = dictionariesByDossierTemplate.get(dossierTemplateId);
oldModel = representation != null ? representation.getDictionary().stream().filter(f -> f.getType().equals(t.getType())).findAny() : Optional.empty();
} else {
var representation = dictionariesByDossier.get(dossierId);
oldModel = representation != null ? representation.getDictionary().stream().filter(f -> f.getType().equals(t.getType())).findAny() : Optional.empty();
}
Set<DictionaryEntry> entries = new HashSet<>();
Set<DictionaryEntry> falsePositives = new HashSet<>();
Set<DictionaryEntry> falseRecommendations = new HashSet<>();
DictionaryEntries newEntries = getEntries(t.getId(), currentVersion);
var newValues = newEntries.getEntries().stream().map(v -> v.getValue()).collect(Collectors.toSet());
var newFalsePositivesValues = newEntries.getFalsePositives().stream().map(v -> v.getValue()).collect(Collectors.toSet());
var newFalseRecommendationsValues = newEntries.getFalseRecommendations().stream().map(v -> v.getValue()).collect(Collectors.toSet());
// add old entries from existing DictionaryModel
oldModel.ifPresent(dictionaryModel -> entries.addAll(dictionaryModel.getEntries().stream().filter(
f -> !newValues.contains(f.getValue())).collect(Collectors.toList())
));
oldModel.ifPresent(dictionaryModel -> falsePositives.addAll(dictionaryModel.getFalsePositives().stream().filter(
f -> !newFalsePositivesValues.contains(f.getValue())).collect(Collectors.toList())
));
oldModel.ifPresent(dictionaryModel -> falseRecommendations.addAll(dictionaryModel.getFalseRecommendations().stream().filter(
f -> !newFalseRecommendationsValues.contains(f.getValue())).collect(Collectors.toList())
));
// Add Increments
entries.addAll(newEntries.getEntries());
falsePositives.addAll(newEntries.getFalsePositives());
falseRecommendations.addAll(newEntries.getFalseRecommendations());
return new DictionaryModel(t.getType(), t.getRank(), convertColor(t.getHexColor()), t.isCaseInsensitive(), t
.isHint(), entries, falsePositives, falseRecommendations, new HashSet<>(), dossierId != null);
})
.sorted(Comparator.comparingInt(DictionaryModel::getRank).reversed())
.collect(Collectors.toList());
@@ -105,7 +162,7 @@ public class DictionaryService {
dictionaryRepresentation.setDictionaryVersion(version);
dictionaryRepresentation.setDictionary(dictionary);
if(dossierId == null) {
if (dossierId == null) {
dictionariesByDossierTemplate.put(dossierTemplateId, dictionaryRepresentation);
} else {
dictionariesByDossier.put(dossierId, dictionaryRepresentation);
@@ -118,31 +175,20 @@ public class DictionaryService {
}
public void updateExternalDictionary(Dictionary dictionary, String dossierTemplateId, String dossierId) {
private DictionaryEntries getEntries(String typeId, Long fromVersion) {
dictionary.getDictionaryModels().forEach(dm -> {
if (dm.isRecommendation() && !dm.getLocalEntries().isEmpty()) {
dictionaryClient.addEntries(IdBuilder.getTypeId(dm.getType(), dossierTemplateId, dm.isDossierDictionary() ? dossierId : null), new ArrayList<>(dm.getLocalEntries()), false, true);
long externalVersion = dictionaryClient.getVersion(dossierTemplateId);
if (externalVersion == dictionary.getVersion().getDossierTemplateVersion() + 1) {
dictionary.getVersion().setDossierTemplateVersion(externalVersion);
}
}
});
}
var type = dictionaryClient.getDictionaryForType(typeId, fromVersion);
private Set<DictionaryEntry> convertEntries(String typeId) {
var type = dictionaryClient.getDictionaryForType(typeId);
Set<DictionaryEntry> entries = new HashSet<>(type
.getEntries());
Set<DictionaryEntry> entries = type.getEntries() != null ? new HashSet<>(type.getEntries()) : new HashSet<>();
Set<DictionaryEntry> falsePositives = type.getFalsePositiveEntries() != null ? new HashSet<>(type.getFalsePositiveEntries()) : new HashSet<>();
Set<DictionaryEntry> falseRecommendations = type.getFalseRecommendationEntries() != null ? new HashSet<>(type.getFalseRecommendationEntries()) : new HashSet<>();
if (type.isCaseInsensitive()) {
entries.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT)));
falsePositives.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT)));
falseRecommendations.forEach(entry -> entry.setValue(entry.getValue().toLowerCase(Locale.ROOT)));
}
return entries;
return new DictionaryEntries(entries, falsePositives, falseRecommendations);
}
@@ -183,16 +229,6 @@ public class DictionaryService {
}
public boolean isRecommendation(String type, String dossierTemplateId) {
DictionaryModel model = dictionariesByDossierTemplate.get(dossierTemplateId).getLocalAccessMap().get(type);
if (model != null) {
return model.isRecommendation();
}
return false;
}
public Dictionary getDeepCopyDictionary(String dossierTemplateId, String dossierId) {
List<DictionaryModel> copy = new ArrayList<>();
@@ -204,7 +240,7 @@ public class DictionaryService {
//TODO merge dictionaries if they have same names
long dossierDictionaryVersion = -1;
if(dictionariesByDossier.containsKey(dossierId)) {
if (dictionariesByDossier.containsKey(dossierId)) {
var dossierRepresentation = dictionariesByDossier.get(dossierId);
dossierRepresentation.getDictionary().forEach(dm -> {
copy.add(SerializationUtils.clone(dm));
@@ -233,4 +269,11 @@ public class DictionaryService {
return dictionariesByDossierTemplate.get(dossierTemplateId).getRequestAddColor();
}
private Long getVersion(DictionaryRepresentation dictionaryRepresentation) {
if (dictionaryRepresentation == null) {
return null;
} else {
return dictionaryRepresentation.getDictionaryVersion();
}
}
}
@@ -9,23 +9,23 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.IdRemoval;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualImageRecategorization;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.kie.api.runtime.KieContainer;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.IdRemoval;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualImageRecategorization;
import com.iqser.red.service.redaction.v1.model.AnalyzeRequest;
import com.iqser.red.service.redaction.v1.model.Engine;
import com.iqser.red.service.redaction.v1.server.classification.model.SectionText;
import com.iqser.red.service.redaction.v1.server.client.model.NerEntities;
import com.iqser.red.service.redaction.v1.server.redaction.model.Dictionary;
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryModel;
import com.iqser.red.service.redaction.v1.server.redaction.model.Entities;
import com.iqser.red.service.redaction.v1.server.redaction.model.Entity;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityPositionSequence;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityType;
import com.iqser.red.service.redaction.v1.server.redaction.model.Image;
import com.iqser.red.service.redaction.v1.server.redaction.model.PageEntities;
import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
@@ -61,7 +61,9 @@ public class EntityRedactionService {
EntitySearchUtils.removeEntitiesContainedInLarger(entities);
}
Map<Integer, List<Entity>> entitiesPerPage = convertToEnititesPerPage(entities);
Map<Integer, List<Entity>> entitiesPerPage = convertToEntitiesPerPage(entities);
EntitySearchUtils.removeEntitiesContainedInRedactedLogos(imagesPerPage, entitiesPerPage);
return new PageEntities(entitiesPerPage, imagesPerPage);
}
@@ -74,14 +76,14 @@ public class EntityRedactionService {
List<SectionSearchableTextPair> sectionSearchableTextPairs = new ArrayList<>();
for (SectionText reanalysisSection : reanalysisSections) {
Set<Entity> entities = findEntities(reanalysisSection.getSearchableText(), reanalysisSection.getHeadline(), reanalysisSection
Entities entities = findEntities(reanalysisSection.getSearchableText(), reanalysisSection.getHeadline(), reanalysisSection
.getSectionNumber(), dictionary, local, nerEntities, reanalysisSection.getCellStarts());
if (reanalysisSection.getCellStarts() != null && !reanalysisSection.getCellStarts().isEmpty()) {
surroundingWordsService.addSurroundingText(entities, reanalysisSection.getSearchableText(), dictionary, reanalysisSection
surroundingWordsService.addSurroundingText(entities.getEntities(), reanalysisSection.getSearchableText(), dictionary, reanalysisSection
.getCellStarts());
} else {
surroundingWordsService.addSurroundingText(entities, reanalysisSection.getSearchableText(), dictionary);
surroundingWordsService.addSurroundingText(entities.getEntities(), reanalysisSection.getSearchableText(), dictionary);
}
if (!local && analyzeRequest.getManualRedactions() != null) {
@@ -92,7 +94,7 @@ public class EntityRedactionService {
.collect(Collectors.toList());
// only approved id removals, that haven't been forced back afterwards
var idsToRemove = analyzeRequest.getManualRedactions().getIdsToRemove().stream()
.filter(idr -> idr.getStatus() == AnnotationStatus.APPROVED)
.filter(idr -> idr.getStatus() == AnnotationStatus.APPROVED && !idr.isRemoveFromDictionary())
.filter(idr -> idr.getRequestDate() != null)
.filter(idr -> approvedForceRedactions.stream().noneMatch(forceRedact -> forceRedact.getRequestDate().isAfter(idr.getRequestDate())))
.map(IdRemoval::getAnnotationId).collect(Collectors.toSet());
@@ -115,7 +117,7 @@ public class EntityRedactionService {
}
}
entities.forEach(entity -> entity.getPositionSequences().forEach(ps -> {
entities.getEntities().forEach(entity -> entity.getPositionSequences().forEach(ps -> {
if (idsToRemove.contains(ps.getId())) {
entity.setIgnored(true);
}
@@ -126,9 +128,10 @@ public class EntityRedactionService {
.isLocal(false)
.dictionaryTypes(dictionary.getTypes())
.entities(hintsPerSectionNumber != null && hintsPerSectionNumber.containsKey(reanalysisSection.getSectionNumber()) ? Stream
.concat(entities.stream(), hintsPerSectionNumber.get(reanalysisSection.getSectionNumber())
.concat(entities.getEntities().stream(), hintsPerSectionNumber.get(reanalysisSection.getSectionNumber())
.stream())
.collect(Collectors.toSet()) : entities)
.collect(Collectors.toSet()) : entities.getEntities())
.nerEntities(entities.getNerEntities())
.text(reanalysisSection.getSearchableText().getAsStringWithLinebreaks())
.searchText(reanalysisSection.getSearchableText().toString())
.headline(reanalysisSection.getHeadline())
@@ -159,7 +162,7 @@ public class EntityRedactionService {
}
private Map<Integer, List<Entity>> convertToEnititesPerPage(Set<Entity> entities) {
private Map<Integer, List<Entity>> convertToEntitiesPerPage(Set<Entity> entities) {
Map<Integer, List<Entity>> entitiesPerPage = new HashMap<>();
for (Entity entity : entities) {
@@ -175,7 +178,7 @@ public class EntityRedactionService {
.getValue(), entity.getHeadline(), entity.getMatchedRule(), entity.getSectionNumber(), entity
.getLegalBasis(), entity.isDictionaryEntry(), entity.getTextBefore(), entity.getTextAfter(), entity
.getStart(), entity.getEnd(), entity.isDossierDictionaryEntry(), entity.getEngines(), entity
.getReferences()));
.getReferences(), entity.getEntityType()));
}
}
return entitiesPerPage;
@@ -197,13 +200,6 @@ public class EntityRedactionService {
private void addLocalValuesToDictionary(Section analysedSection, Dictionary dictionary) {
analysedSection.getLocalDictionaryAdds().keySet().forEach(key -> {
if (dictionary.isRecommendation(key)) {
analysedSection.getLocalDictionaryAdds().get(key).forEach(value -> {
if (!dictionary.containsValue(key, value)) {
dictionary.getLocalAccessMap().get(key).getLocalEntries().add(value);
}
});
} else {
analysedSection.getLocalDictionaryAdds().get(key).forEach(value -> {
if (dictionary.getLocalAccessMap().get(key) == null) {
@@ -216,70 +212,62 @@ public class EntityRedactionService {
dictionary.getLocalAccessMap().get(key).getLocalEntries().add(value);
});
}
});
}
private Set<Entity> findEntities(SearchableText searchableText, String headline, int sectionNumber,
Dictionary dictionary, boolean local, NerEntities nerEntities,
List<Integer> cellstarts) {
private Entities findEntities(SearchableText searchableText, String headline, int sectionNumber,
Dictionary dictionary, boolean local, NerEntities nerEntities,
List<Integer> cellStarts) {
Set<Entity> found = new HashSet<>();
String searchableString = searchableText.toString();
if (StringUtils.isEmpty(searchableString)) {
return found;
return new Entities(new HashSet<>(), new HashSet<>());
}
String lowercaseInputString = searchableString.toLowerCase();
for (DictionaryModel model : dictionary.getDictionaryModels()) {
if (model.isCaseInsensitive()) {
EntitySearchUtils.addOrAddEngine(found, EntitySearchUtils.find(lowercaseInputString, model.getValues(local), model
.getType(), headline, sectionNumber, !local, model.isDossierDictionary(), local ? Engine.RULE : Engine.DICTIONARY));
EntitySearchUtils.addOrAddEngine(found, EntitySearchUtils.findEntities(lowercaseInputString, model.getValues(local), model, headline, sectionNumber, !local, model.isDossierDictionary(), local ? Engine.RULE : Engine.DICTIONARY, false, local ? true : false));
} else {
EntitySearchUtils.addOrAddEngine(found, EntitySearchUtils.find(searchableString, model.getValues(local), model
.getType(), headline, sectionNumber, !local, model.isDossierDictionary(), local ? Engine.RULE : Engine.DICTIONARY));
EntitySearchUtils.addOrAddEngine(found, EntitySearchUtils.findEntities(searchableString, model.getValues(local), model, headline, sectionNumber, !local, model.isDossierDictionary(), local ? Engine.RULE : Engine.DICTIONARY, false, local ? true : false));
}
}
Set<Entity> nerFound = new HashSet<>();
if (!local) {
Map<String, Set<String>> nerValuesPerType = getNerValues(sectionNumber, nerEntities, cellstarts);
nerValuesPerType.entrySet().forEach(entry -> {
EntitySearchUtils.addEntitiesWithHigherRank(found, EntitySearchUtils.find(searchableString, entry.getValue(), entry
.getKey(), headline, sectionNumber, false, false, Engine.NER), dictionary);
});
nerFound.addAll(getNerValues(sectionNumber, nerEntities, cellStarts, headline));
}
return EntitySearchUtils.clearAndFindPositions(found, searchableText, dictionary);
return new Entities(EntitySearchUtils.clearAndFindPositions(found, searchableText, dictionary), nerFound);
}
private Map<String, Set<String>> getNerValues(int sectionNumber, NerEntities nerEntities,
List<Integer> cellstarts) {
private Set<Entity> getNerValues(int sectionNumber, NerEntities nerEntities, List<Integer> cellStarts,
String headline) {
Map<String, Set<String>> nerValuesPerType = new HashMap<>();
Set<Entity> entities = new HashSet<>();
if (redactionServiceSettings.isEnableEntityRecognition() && nerEntities.getResult()
.containsKey(sectionNumber)) {
nerEntities.getResult().get(sectionNumber).forEach(res -> {
if (cellstarts == null || cellstarts.isEmpty()) {
nerValuesPerType.computeIfAbsent(res.getType(), (a) -> new HashSet<>())
.add(new String(Base64.decodeBase64(res.getValue().getBytes())));
if (redactionServiceSettings.isNerServiceEnabled() && nerEntities.getData().containsKey(sectionNumber)) {
nerEntities.getData().get(sectionNumber).forEach(res -> {
if (cellStarts == null || cellStarts.isEmpty()) {
entities.add(new Entity(res.getValue(), res.getType(), res.getStartOffset(), res.getEndOffset(), headline, sectionNumber, false, false, Engine.NER, EntityType.RECOMMENDATION));
} else {
boolean intersectsCellStart = false;
for (Integer cellStart : cellstarts) {
for (Integer cellStart : cellStarts) {
if (res.getStartOffset() < cellStart && cellStart < res.getEndOffset()) {
intersectsCellStart = true;
break;
}
}
if (!intersectsCellStart) {
nerValuesPerType.computeIfAbsent(res.getType(), (a) -> new HashSet<>())
.add(new String(Base64.decodeBase64(res.getValue().getBytes())));
entities.add(new Entity(res.getValue(), res.getType(), res.getStartOffset(), res.getEndOffset(), headline, sectionNumber, false, false, Engine.NER, EntityType.RECOMMENDATION));
}
}
});
}
return nerValuesPerType;
return entities;
}
}
@@ -0,0 +1,146 @@
package com.iqser.red.service.redaction.v1.server.redaction.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
import com.iqser.red.service.redaction.v1.model.ImportedRedaction;
import com.iqser.red.service.redaction.v1.model.ImportedRedactions;
import com.iqser.red.service.redaction.v1.model.Point;
import com.iqser.red.service.redaction.v1.model.Rectangle;
import com.iqser.red.service.redaction.v1.model.RedactionLogEntry;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class ImportedRedactionService {
public static final String IMPORTED_REDACTION_TYPE = "imported_redaction";
private final DictionaryService dictionaryService;
private final RedactionStorageService redactionStorageService;
public List<RedactionLogEntry> processImportedRedactions(String dossierTemplateId, String dossierId, String fileId,
List<RedactionLogEntry> redactionLogEntries,
boolean addImportedRedactions,
ManualRedactions manualRedactions) {
var importedRedactions = redactionStorageService.getImportedRedactions(dossierId, fileId);
if (importedRedactions == null || importedRedactions.getImportedRedactions().isEmpty()) {
return redactionLogEntries;
}
var approvedRemoveRedactions = manualRedactions == null ? new HashSet<String>() : manualRedactions.getIdsToRemove()
.stream()
.filter(idRemoval -> idRemoval.getStatus().equals(AnnotationStatus.APPROVED))
.map(idRemoval -> idRemoval.getAnnotationId())
.collect(Collectors.toSet());
var approvedResizeRedactions = new HashMap<String, List<Rectangle>>();
if (manualRedactions != null) {
manualRedactions.getResizeRedactions().forEach(resizeRedaction -> {
if (resizeRedaction.getStatus().equals(AnnotationStatus.APPROVED)) {
approvedResizeRedactions.put(resizeRedaction.getAnnotationId(), convert(resizeRedaction.getPositions()));
}
});
}
redactionLogEntries.removeIf(redactionLogEntry -> !(redactionLogEntry.isImage() && !redactionLogEntry.isRedacted()) && hasIntersections(redactionLogEntry, importedRedactions, approvedRemoveRedactions, approvedResizeRedactions));
if (addImportedRedactions) {
return addImportedRedactionsRedactionLogEntries(dossierTemplateId, redactionLogEntries, importedRedactions, approvedRemoveRedactions);
}
return redactionLogEntries;
}
private List<RedactionLogEntry> addImportedRedactionsRedactionLogEntries(String dossierTemplateId,
List<RedactionLogEntry> redactionLogEntries,
ImportedRedactions importedRedactions,
Set<String> approvedRemoveRedactions) {
for (List<ImportedRedaction> importedRedactionsValues : importedRedactions.getImportedRedactions().values()) {
for (ImportedRedaction importedRedaction : importedRedactionsValues) {
RedactionLogEntry redactionLogEntry = RedactionLogEntry.builder()
.id(importedRedaction.getId())
.type(IMPORTED_REDACTION_TYPE)
.imported(true)
.redacted(approvedRemoveRedactions.contains(importedRedaction.getId()) ? false : true)
.positions(importedRedaction.getPositions())
.color(getColor(IMPORTED_REDACTION_TYPE, dossierTemplateId))
.build();
redactionLogEntries.add(redactionLogEntry);
}
}
return redactionLogEntries;
}
private boolean hasIntersections(RedactionLogEntry redactionLogEntry, ImportedRedactions importedRedactions,
Set<String> approvedRemoveRedactions,
Map<String, List<Rectangle>> approvedResizeRedactions) {
for (Rectangle rectangle : redactionLogEntry.getPositions()) {
if (importedRedactions.getImportedRedactions().containsKey(rectangle.getPage())) {
var importedRedactionsOnPage = importedRedactions.getImportedRedactions().get(rectangle.getPage());
for (ImportedRedaction importedRedaction : importedRedactionsOnPage) {
if (approvedRemoveRedactions.contains(importedRedaction.getId())) {
continue;
}
for (Rectangle importedRedactionPosition : approvedResizeRedactions.containsKey(importedRedaction.getId()) ? approvedResizeRedactions.get(importedRedaction.getId()) : importedRedaction.getPositions()) {
if (intersects(importedRedactionPosition, rectangle)) {
return true;
}
}
}
}
}
return false;
}
private boolean intersects(Rectangle importedPosition, Rectangle redactionLogPosition) {
return redactionLogPosition.getTopLeft()
.getX() + redactionLogPosition.getWidth() > importedPosition.getTopLeft()
.getX() && redactionLogPosition.getTopLeft()
.getY() + redactionLogPosition.getHeight() > importedPosition.getTopLeft()
.getY() && redactionLogPosition.getTopLeft().getX() < importedPosition.getTopLeft()
.getX() + importedPosition.getWidth() && redactionLogPosition.getTopLeft()
.getY() < importedPosition.getTopLeft().getY() + importedPosition.getHeight();
}
private float[] getColor(String type, String dossierTemplateId) {
return dictionaryService.getColor(type, dossierTemplateId);
}
private List<Rectangle> convert(
List<com.iqser.red.service.persistence.service.v1.api.model.annotations.Rectangle> positions) {
return positions.stream()
.map(pos -> new Rectangle(new Point(pos.getTopLeftX(), pos.getTopLeftY()), pos.getWidth(), pos.getHeight(), pos.getPage()))
.collect(Collectors.toList());
}
}
@@ -7,10 +7,11 @@ import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualResizeRedaction;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualResizeRedaction;
import com.iqser.red.service.redaction.v1.model.AnalyzeResult;
import com.iqser.red.service.redaction.v1.model.Engine;
import com.iqser.red.service.redaction.v1.model.SectionArea;
import com.iqser.red.service.redaction.v1.server.classification.model.SectionText;
@@ -18,11 +19,14 @@ import com.iqser.red.service.redaction.v1.server.classification.model.Text;
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
import com.iqser.red.service.redaction.v1.server.redaction.model.Entity;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityPositionSequence;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityType;
import com.iqser.red.service.redaction.v1.server.redaction.utils.EntitySearchUtils;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class ManualRedactionSurroundingTextService {
@@ -31,8 +35,9 @@ public class ManualRedactionSurroundingTextService {
private final SurroundingWordsService surroundingWordsService;
public ManualRedactions addSurroundingText(String dossierId, String fileId, ManualRedactions manualRedactions) {
public AnalyzeResult addSurroundingText(String dossierId, String fileId, ManualRedactions manualRedactions) {
long startTime = System.currentTimeMillis();
Text text = redactionStorageService.getText(dossierId, fileId);
List<ManualRedactionEntry> processedAddRedactions = new ArrayList<>();
List<ManualResizeRedaction> processedResizeRedactions = new ArrayList<>();
@@ -49,8 +54,7 @@ public class ManualRedactionSurroundingTextService {
while (addItty.hasNext()) {
var manualAddRedaction = addItty.next();
if (sectionContainsEntry(sectionArea, manualAddRedaction.getPositions())) {
var surroundingText = findSurroundingText(sectionText, manualAddRedaction.getValue(), manualAddRedaction
.getPositions());
var surroundingText = findSurroundingText(sectionText, manualAddRedaction.getValue(), manualAddRedaction.getPositions());
manualAddRedaction.setTextBefore(surroundingText.getLeft());
manualAddRedaction.setTextAfter(surroundingText.getRight());
processedAddRedactions.add(manualAddRedaction);
@@ -62,8 +66,7 @@ public class ManualRedactionSurroundingTextService {
while (resizeItty.hasNext()) {
var manualResizeRedaction = resizeItty.next();
if (sectionContainsEntry(sectionArea, manualResizeRedaction.getPositions())) {
var surroundingText = findSurroundingText(sectionText, manualResizeRedaction.getValue(), manualResizeRedaction
.getPositions());
var surroundingText = findSurroundingText(sectionText, manualResizeRedaction.getValue(), manualResizeRedaction.getPositions());
manualResizeRedaction.setTextBefore(surroundingText.getLeft());
manualResizeRedaction.setTextAfter(surroundingText.getRight());
processedResizeRedactions.add(manualResizeRedaction);
@@ -75,22 +78,31 @@ public class ManualRedactionSurroundingTextService {
manualRedactions.getEntriesToAdd().addAll(processedAddRedactions);
manualRedactions.getResizeRedactions().addAll(processedResizeRedactions);
return manualRedactions;
return AnalyzeResult.builder()
.dossierId(dossierId)
.fileId(fileId)
.manualRedactions(manualRedactions)
.duration(System.currentTimeMillis() - startTime)
.build();
}
private Pair<String, String> findSurroundingText(SectionText sectionText, String value,
List<Rectangle> toFindPositions) {
Set<Entity> entities = EntitySearchUtils.find(sectionText.getText(), Set.of(value), "dummy", sectionText.getHeadline(), sectionText
.getSectionNumber(), false, false, Engine.DICTIONARY);
Set<Entity> entities = EntitySearchUtils.find(sectionText.getText(), Set.of(value), "dummy", sectionText.getHeadline(), sectionText.getSectionNumber(), false, false, Engine.DICTIONARY, false, EntityType.ENTITY);
Set<Entity> entitiesWithPositions = EntitySearchUtils.clearAndFindPositions(entities, sectionText.getSearchableText(), null);
Entity correctEntity = getEntityOnCorrectPosition(entitiesWithPositions, toFindPositions);
if (correctEntity == null) {
log.warn("Could not calculate surrounding text");
return Pair.of("", "");
}
if (sectionText.getCellStarts() != null && !sectionText.getCellStarts().isEmpty()) {
surroundingWordsService.addSurroundingText(Set.of(correctEntity), sectionText.getSearchableText(), null, sectionText
.getCellStarts());
surroundingWordsService.addSurroundingText(Set.of(correctEntity), sectionText.getSearchableText(), null, sectionText.getCellStarts());
} else {
surroundingWordsService.addSurroundingText(Set.of(correctEntity), sectionText.getSearchableText(), null);
}
@@ -1,53 +0,0 @@
package com.iqser.red.service.redaction.v1.server.redaction.service;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.redaction.v1.server.client.EntityRecognitionClient;
import com.iqser.red.service.redaction.v1.server.client.model.EntityRecognitionRequest;
import com.iqser.red.service.redaction.v1.server.client.model.EntityRecognitionSection;
import com.iqser.red.service.redaction.v1.server.settings.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class NerAnalyserService {
private final RedactionStorageService redactionStorageService;
private final EntityRecognitionClient entityRecognitionClient;
private final RedactionServiceSettings redactionServiceSettings;
public void computeNerEntities(String dossierId, String fileId) {
if (redactionServiceSettings.isEnableEntityRecognition()) {
var text = redactionStorageService.getText(dossierId, fileId);
long start = System.currentTimeMillis();
if (text != null) {
var nerRequest = EntityRecognitionRequest.builder()
.data(text.getSectionTexts()
.stream()
.map(sectionText -> new EntityRecognitionSection(sectionText.getSectionNumber(), new String(Base64
.encodeBase64(sectionText
.getText().getBytes()))))
.collect(Collectors.toList()))
.build();
var nerResponse = entityRecognitionClient.findAuthors(nerRequest);
log.info("Computing NER entities took: {} ms for dossierId {} and fileId {}", System.currentTimeMillis() - start, dossierId, fileId);
redactionStorageService.storeObject(dossierId, fileId, FileType.NER_ENTITIES, nerResponse);
} else {
log.warn("Warning, text for file: {} in dossier: {} is null", fileId, dossierId);
}
}
}
}
@@ -1,25 +1,14 @@
package com.iqser.red.service.redaction.v1.server.redaction.service;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.v1.model.Change;
import com.iqser.red.service.redaction.v1.model.ChangeType;
import com.iqser.red.service.redaction.v1.model.RedactionLog;
import com.iqser.red.service.redaction.v1.model.RedactionLogChanges;
import com.iqser.red.service.redaction.v1.model.RedactionLogEntry;
import com.iqser.red.service.redaction.v1.model.*;
import com.iqser.red.service.redaction.v1.server.storage.RedactionStorageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
@@ -29,7 +18,7 @@ public class RedactionChangeLogService {
private final RedactionStorageService redactionStorageService;
public RedactionLogChanges computeChanges(String dossierId, String fileId, RedactionLog currentRedactionLog) {
public RedactionLogChanges computeChanges(String dossierId, String fileId, RedactionLog currentRedactionLog, int analysisNumber) {
long start = System.currentTimeMillis();
@@ -37,30 +26,23 @@ public class RedactionChangeLogService {
if (previousRedactionLog == null) {
currentRedactionLog.getRedactionLogEntry().forEach(entry -> {
entry.getChanges().add(new Change(ChangeType.ADDED, OffsetDateTime.now()));
entry.getChanges().add(new Change(analysisNumber, ChangeType.ADDED, OffsetDateTime.now()));
});
return new RedactionLogChanges(currentRedactionLog, false);
}
List<RedactionLogEntry> notRemovedPreviousEntries = previousRedactionLog.getRedactionLogEntry()
.stream()
.filter(entry -> !entry.getChanges()
.get(entry.getChanges().size() - 1)
.getType()
.equals(ChangeType.REMOVED))
.filter(entry -> !entry.lastChangeIsRemoved())
.collect(Collectors.toList());
Set<RedactionLogEntry> added = new HashSet<>(currentRedactionLog.getRedactionLogEntry().stream()
.filter(entry -> entry.getChanges().isEmpty() || !entry.getChanges()
.get(entry.getChanges().size() - 1)
.getType()
.equals(ChangeType.REMOVED))
.collect(Collectors.toList()));
Set<RedactionLogEntry> added = currentRedactionLog.getRedactionLogEntry().stream()
.filter(entry -> entry.getChanges().isEmpty() || !entry.lastChangeIsRemoved()).collect(Collectors.toSet());
added.removeAll(notRemovedPreviousEntries);
notRemovedPreviousEntries.forEach(added::remove);
Set<RedactionLogEntry> removed = new HashSet<>(notRemovedPreviousEntries);
removed.removeAll(currentRedactionLog.getRedactionLogEntry());
currentRedactionLog.getRedactionLogEntry().forEach(removed::remove);
Map<String, RedactionLogEntry> addedIds = new HashMap<>();
added.forEach(entry -> {
@@ -78,16 +60,16 @@ public class RedactionChangeLogService {
newRedactionLogEntries.forEach(entry -> {
if (removedIds.contains(entry.getId()) && addedIds.containsKey(entry.getId())) {
List<Change> changes = entry.getChanges();
changes.add(new Change(ChangeType.CHANGED, OffsetDateTime.now()));
changes.add(new Change(analysisNumber, ChangeType.CHANGED, OffsetDateTime.now()));
var newEntry = addedIds.get(entry.getId());
newEntry.setChanges(changes);
addedIds.put(entry.getId(), newEntry);
toRemove.add(entry);
} else if (removedIds.contains(entry.getId())) {
entry.getChanges().add(new Change(ChangeType.REMOVED, OffsetDateTime.now()));
entry.getChanges().add(new Change(analysisNumber, ChangeType.REMOVED, OffsetDateTime.now()));
} else if (addedIds.containsKey(entry.getId())) {
List<Change> changes = entry.getChanges();
changes.add(new Change(ChangeType.ADDED, OffsetDateTime.now()));
changes.add(new Change(analysisNumber, ChangeType.ADDED, OffsetDateTime.now()));
var newEntry = addedIds.get(entry.getId());
newEntry.setChanges(changes);
addedIds.put(entry.getId(), newEntry);
@@ -98,8 +80,8 @@ public class RedactionChangeLogService {
newRedactionLogEntries.removeAll(toRemove);
addedIds.forEach((k, v) -> {
if(v.getChanges().isEmpty()) {
v.getChanges().add(new Change(ChangeType.ADDED, OffsetDateTime.now()));
if (v.getChanges().isEmpty()) {
v.getChanges().add(new Change(analysisNumber, ChangeType.ADDED, OffsetDateTime.now()));
}
newRedactionLogEntries.add(v);
});
@@ -17,6 +17,7 @@ import com.iqser.red.service.redaction.v1.server.parsing.model.RedTextPosition;
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
import com.iqser.red.service.redaction.v1.server.redaction.model.Entity;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityPositionSequence;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityType;
import com.iqser.red.service.redaction.v1.server.redaction.model.Image;
import com.iqser.red.service.redaction.v1.server.redaction.model.PageEntities;
import com.iqser.red.service.redaction.v1.server.redaction.utils.IdBuilder;
@@ -70,7 +71,6 @@ public class RedactionLogCreatorService {
.legalBasis(image.getLegalBasis())
.matchedRule(image.getMatchedRule())
.isHint(dictionaryService.isHint(image.getType(), dossierTemplateId))
.manual(false)
.isDictionaryEntry(false)
.isRecommendation(false)
.positions(List.of(new Rectangle(new Point((float) image.getPosition()
@@ -92,7 +92,7 @@ public class RedactionLogCreatorService {
List<RedactionLogEntry> redactionLogEntities = new ArrayList<>();
// Duplicates can exist due table extraction colums over multiple rows.
// Duplicates can exist due table extraction columns over multiple rows.
Set<String> processedIds = new HashSet<>();
entityLoop:
@@ -173,10 +173,7 @@ public class RedactionLogCreatorService {
private boolean isCharClose(float x, float xDirAdj, float width) {
float max = x + (5 * width);
if (xDirAdj < max) {
return true;
}
return false;
return xDirAdj < max;
}
@@ -190,11 +187,7 @@ public class RedactionLogCreatorService {
float minY = y - offsetY;
float maxY = y + offsetY;
if (yCompare > minY && yCompare < maxY && heightCompare > minHeight && heightCompare < maxHeight) {
return true;
}
return false;
return yCompare > minY && yCompare < maxY && heightCompare > minHeight && heightCompare < maxHeight;
}
@@ -211,7 +204,8 @@ public class RedactionLogCreatorService {
.type(entity.getType())
.redacted(entity.isRedaction())
.isHint(isHint(entity.getType(), dossierTemplateId))
.isRecommendation(isRecommendation(entity.getType(), dossierTemplateId))
.isRecommendation(entity.getEntityType().equals(EntityType.RECOMMENDATION))
.isFalsePositive(entity.getEntityType().equals(EntityType.FALSE_POSITIVE) || entity.getEntityType().equals(EntityType.FALSE_RECOMMENDATION))
.section(entity.getHeadline())
.sectionNumber(entity.getSectionNumber())
.matchedRule(entity.getMatchedRule())
@@ -241,10 +235,4 @@ public class RedactionLogCreatorService {
return dictionaryService.isHint(type, dossierTemplateId);
}
private boolean isRecommendation(String type, String dossierTemplateId) {
return dictionaryService.isRecommendation(type, dossierTemplateId);
}
}
@@ -1,7 +1,11 @@
package com.iqser.red.service.redaction.v1.server.redaction.service;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.*;
import com.iqser.red.service.redaction.v1.model.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.AnnotationStatus;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.Comment;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.*;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
import com.iqser.red.service.redaction.v1.model.*;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -9,6 +13,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.awt.Color;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.stream.Collectors;
@@ -18,19 +23,18 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class RedactionLogMergeService {
private final DictionaryService dictionaryService;
private final SectionTextService sectionTextService;
public RedactionLog mergeRedactionLogData(RedactionLog redactionLog, SectionGrid sectionGrid,
String dossierTemplateId, ManualRedactions manualRedactions,
Set<Integer> excludedPages) {
Set<Integer> excludedPages, List<Type> types, Colors colors) {
log.info("Merging Redaction log with manual redactions");
if (manualRedactions != null) {
var manualRedactionLogEntries = addManualAddEntries(sectionGrid, manualRedactions.getEntriesToAdd(), manualRedactions.getComments(), dossierTemplateId);
var manualRedactionLogEntries = addManualAddEntries(sectionGrid, manualRedactions.getEntriesToAdd(), manualRedactions.getComments(), colors, types);
redactionLog.getRedactionLogEntry().addAll(manualRedactionLogEntries);
@@ -40,13 +44,13 @@ public class RedactionLogMergeService {
processRedactionLogEntry(manualRedactionWrappers.stream()
.filter(mr -> entry.getId().equals(mr.getId()))
.collect(Collectors.toList()), dossierTemplateId, entry);
.collect(Collectors.toList()), entry, types, colors);
entry.setComments(manualRedactions.getComments().get(entry.getId()));
if (excludedPages != null && !excludedPages.isEmpty()) {
entry.getPositions().forEach(pos -> {
if (!entry.isManual() && excludedPages.contains(pos.getPage())) {
if (!entry.isLocalManualRedaction() && excludedPages.contains(pos.getPage())) {
entry.setExcluded(true);
}
});
@@ -109,7 +113,7 @@ public class RedactionLogMergeService {
private void processRedactionLogEntry(List<ManualRedactionWrapper> manualRedactionWrappers,
String dossierTemplateId, RedactionLogEntry redactionLogEntry) {
RedactionLogEntry redactionLogEntry, List<Type> types, Colors colors) {
manualRedactionWrappers.forEach(mrw -> {
@@ -117,12 +121,12 @@ public class RedactionLogMergeService {
var imageRecategorization = (ManualImageRecategorization) mrw.getItem();
String manualOverrideReason = null;
if (imageRecategorization.getStatus().equals(AnnotationStatus.APPROVED)) {
redactionLogEntry.setStatus(AnnotationStatus.APPROVED);
redactionLogEntry.setType(imageRecategorization.getType());
redactionLogEntry.setHasBeenRecategorized(true);
redactionLogEntry.setSection("Image:" + redactionLogEntry.getType());
if (dictionaryService.isHint(imageRecategorization.getType(), dossierTemplateId)) {
if (isHint(types, imageRecategorization.getType())) {
redactionLogEntry.setRedacted(false);
redactionLogEntry.setHint(true);
} else {
@@ -132,77 +136,65 @@ public class RedactionLogMergeService {
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", recategorized by manual override");
} else if (imageRecategorization.getStatus().equals(AnnotationStatus.REQUESTED)) {
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to recategorize");
redactionLogEntry.setStatus(AnnotationStatus.REQUESTED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, false, redactionLogEntry.isRedacted(), false));
redactionLogEntry.setRecategorizationType(imageRecategorization.getType());
} else {
redactionLogEntry.setStatus(AnnotationStatus.DECLINED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, false, redactionLogEntry.isRedacted(), false, types));
}
redactionLogEntry.setManualRedactionUserId(imageRecategorization.getUser());
redactionLogEntry.setReason(manualOverrideReason);
redactionLogEntry.setManual(true);
redactionLogEntry.setManualRedactionType(ManualRedactionType.RECATEGORIZE);
redactionLogEntry.getManualChanges().add(ManualChange.from(imageRecategorization)
.withManualRedactionType(ManualRedactionType.RECATEGORIZE)
.withChange("type", imageRecategorization.getType()));
}
if (mrw.getItem() instanceof IdRemoval) {
var manualRemoval = (IdRemoval) mrw.getItem();
if (manualRemoval.getStatus().equals(AnnotationStatus.APPROVED) && manualRemoval.isRemoveFromDictionary()) {
log.info("Skipping merge for dictionary-modifying entry");
log.debug("Skipping merge for dictionary-modifying entry");
} else {
String manualOverrideReason = null;
if (manualRemoval.getStatus().equals(AnnotationStatus.APPROVED)) {
redactionLogEntry.setRedacted(false);
redactionLogEntry.setStatus(AnnotationStatus.APPROVED);
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", removed by manual override");
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, false, redactionLogEntry.isRedacted(), true));
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, false, redactionLogEntry.isRedacted(), true, types));
redactionLogEntry.setHint(false);
redactionLogEntry.setHasBeenRemovedByManualOverride(true);
} else if (manualRemoval.getStatus().equals(AnnotationStatus.REQUESTED)) {
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to remove");
redactionLogEntry.setStatus(AnnotationStatus.REQUESTED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, true, redactionLogEntry.isRedacted(), false));
} else {
redactionLogEntry.setStatus(AnnotationStatus.DECLINED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, true, redactionLogEntry.isRedacted(), false, types));
}
redactionLogEntry.setReason(manualOverrideReason);
redactionLogEntry.setManual(true);
redactionLogEntry.setManualRedactionUserId(manualRemoval.getUser());
redactionLogEntry.setManualRedactionType(ManualRedactionType.REMOVE);
redactionLogEntry.setDictionaryEntry(manualRemoval.isRemoveFromDictionary());
redactionLogEntry.setDossierDictionaryEntry(manualRemoval.isRemoveFromDictionary());
redactionLogEntry.getManualChanges().add(ManualChange.from(manualRemoval)
.withManualRedactionType(manualRemoval.isRemoveFromDictionary() ?
ManualRedactionType.REMOVE_FROM_DICTIONARY : ManualRedactionType.REMOVE_LOCALLY));
}
}
if (mrw.getItem() instanceof ManualForceRedaction) {
var manualForceRedact = (ManualForceRedaction) mrw.getItem();
String manualOverrideReason = null;
var dictionaryIsHint = isHint(types, redactionLogEntry.getType());
if (manualForceRedact.getStatus().equals(AnnotationStatus.APPROVED)) {
// Forcing a skipped hint should result in a hint
if (dictionaryService.isHint(redactionLogEntry.getType(), dossierTemplateId)) {
if (dictionaryIsHint) {
redactionLogEntry.setHint(true);
} else {
redactionLogEntry.setRedacted(true);
}
redactionLogEntry.setStatus(AnnotationStatus.APPROVED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, false, redactionLogEntry.isRedacted(), false));
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, false, redactionLogEntry.isRedacted(), false, types));
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", forced by manual override");
redactionLogEntry.setHasBeenForced(true);
redactionLogEntry.setLegalBasis(manualForceRedact.getLegalBasis());
} else if (manualForceRedact.getStatus().equals(AnnotationStatus.REQUESTED)) {
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to force redact");
redactionLogEntry.setStatus(AnnotationStatus.REQUESTED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, true, redactionLogEntry.isRedacted(), false));
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to force " + (dictionaryIsHint ? "hint" : "redact"));
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, true, redactionLogEntry.isRedacted(), false, types));
redactionLogEntry.setLegalBasis(manualForceRedact.getLegalBasis());
} else {
redactionLogEntry.setStatus(AnnotationStatus.DECLINED);
}
redactionLogEntry.setManualRedactionUserId(manualForceRedact.getUser());
redactionLogEntry.setReason(manualOverrideReason);
redactionLogEntry.setManual(true);
redactionLogEntry.setManualRedactionType(ManualRedactionType.FORCE_REDACT);
var manualChange = ManualChange.from(manualForceRedact)
.withManualRedactionType(dictionaryIsHint ? ManualRedactionType.FORCE_HINT : ManualRedactionType.FORCE_REDACT);
redactionLogEntry.getManualChanges().add(manualChange);
}
@@ -210,11 +202,9 @@ public class RedactionLogMergeService {
var manualLegalBasisChange = (ManualLegalBasisChange) mrw.getItem();
String manualOverrideReason = null;
if (manualLegalBasisChange.getStatus().equals(AnnotationStatus.APPROVED)) {
redactionLogEntry.setStatus(AnnotationStatus.APPROVED);
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", legal basis was manually changed");
redactionLogEntry.setLegalBasis(manualLegalBasisChange.getLegalBasis());
redactionLogEntry.setRedacted(true);
redactionLogEntry.setHasLegalBasisChanged(true);
if (manualLegalBasisChange.getSection() != null) {
redactionLogEntry.setSection(manualLegalBasisChange.getSection());
}
@@ -223,47 +213,45 @@ public class RedactionLogMergeService {
}
} else if (manualLegalBasisChange.getStatus().equals(AnnotationStatus.REQUESTED)) {
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", legal basis change requested");
redactionLogEntry.setStatus(AnnotationStatus.REQUESTED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, true, redactionLogEntry.isRedacted(), false));
redactionLogEntry.setLegalBasisChangeValue(manualLegalBasisChange.getLegalBasis());
} else {
redactionLogEntry.setStatus(AnnotationStatus.DECLINED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, true, redactionLogEntry.isRedacted(), false, types));
}
redactionLogEntry.setManualRedactionUserId(manualLegalBasisChange.getUser());
redactionLogEntry.setReason(manualOverrideReason);
redactionLogEntry.setManual(true);
redactionLogEntry.setManualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE);
var manualChange = ManualChange.from(manualLegalBasisChange)
.withManualRedactionType(ManualRedactionType.LEGAL_BASIS_CHANGE);
manualChange.withChange("legalBasis", manualLegalBasisChange.getLegalBasis());
if (manualLegalBasisChange.getSection() != null) {
manualChange.withChange("section", manualLegalBasisChange.getSection());
}
if (redactionLogEntry.isRectangle() && manualLegalBasisChange.getValue() != null) {
manualChange.withChange("value", manualLegalBasisChange.getValue());
}
redactionLogEntry.getManualChanges().add(manualChange);
}
if (mrw.getItem() instanceof ManualResizeRedaction) {
var manualResizeRedact = (ManualResizeRedaction) mrw.getItem();
String manualOverrideReason = null;
if (manualResizeRedact.getStatus().equals(AnnotationStatus.APPROVED)) {
redactionLogEntry.setStatus(AnnotationStatus.APPROVED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, false, redactionLogEntry.isRedacted(), false));
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, false, redactionLogEntry.isRedacted(), false, types));
redactionLogEntry.setPositions(convertPositions(manualResizeRedact.getPositions()));
redactionLogEntry.setValue(manualResizeRedact.getValue());
redactionLogEntry.setHasBeenResized(true);
redactionLogEntry.setTextBefore(manualResizeRedact.getTextBefore());
redactionLogEntry.setTextAfter(manualResizeRedact.getTextAfter());
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", resized by manual override");
} else if (manualResizeRedact.getStatus().equals(AnnotationStatus.REQUESTED)) {
manualOverrideReason = mergeReasonIfNecessary(redactionLogEntry.getReason(), ", requested to resize redact");
redactionLogEntry.setStatus(AnnotationStatus.REQUESTED);
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), dossierTemplateId, true, redactionLogEntry.isRedacted(), false));
redactionLogEntry.setColor(getColor(redactionLogEntry.getType(), colors, true, redactionLogEntry.isRedacted(), false, types));
redactionLogEntry.setPositions(convertPositions(manualResizeRedact.getPositions()));
redactionLogEntry.setTextBefore(manualResizeRedact.getTextBefore());
redactionLogEntry.setTextAfter(manualResizeRedact.getTextAfter());
} else {
redactionLogEntry.setStatus(AnnotationStatus.DECLINED);
}
redactionLogEntry.setManualRedactionUserId(manualResizeRedact.getUser());
redactionLogEntry.setReason(manualOverrideReason);
redactionLogEntry.setManual(true);
redactionLogEntry.setManualRedactionType(ManualRedactionType.RESIZE);
redactionLogEntry.getManualChanges().add(ManualChange.from(manualResizeRedact)
.withManualRedactionType(ManualRedactionType.RESIZE)
.withChange("value", manualResizeRedact.getValue()));
}
});
@@ -285,14 +273,14 @@ public class RedactionLogMergeService {
public List<RedactionLogEntry> addManualAddEntries(SectionGrid sectionGrid, Set<ManualRedactionEntry> manualAdds,
Map<String, List<Comment>> comments, String dossierTemplateId) {
Map<String, List<Comment>> comments, Colors colors, List<Type> types) {
List<RedactionLogEntry> redactionLogEntries = new ArrayList<>();
for (ManualRedactionEntry manualRedactionEntry : manualAdds) {
if (!approvedAndShouldBeInDictionary(manualRedactionEntry)) {
RedactionLogEntry redactionLogEntry = createRedactionLogEntry(manualRedactionEntry, manualRedactionEntry.getAnnotationId(), dossierTemplateId);
if (shouldCreateManualEntry(manualRedactionEntry)) {
RedactionLogEntry redactionLogEntry = createRedactionLogEntry(manualRedactionEntry, manualRedactionEntry.getAnnotationId(), colors, types);
redactionLogEntry.setPositions(convertPositions(manualRedactionEntry.getPositions()));
redactionLogEntry.setComments(comments.get(manualRedactionEntry.getAnnotationId()));
redactionLogEntry.setTextBefore(manualRedactionEntry.getTextBefore());
@@ -317,19 +305,26 @@ public class RedactionLogMergeService {
}
private boolean approvedAndShouldBeInDictionary(ManualRedactionEntry manualRedactionEntry) {
return manualRedactionEntry.getStatus()
.equals(AnnotationStatus.APPROVED) && (manualRedactionEntry.isAddToDictionary() || manualRedactionEntry.isAddToDossierDictionary());
@SuppressWarnings("PMD.UselessParentheses")
private boolean shouldCreateManualEntry(ManualRedactionEntry manualRedactionEntry) {
return (!manualRedactionEntry.isAddToDictionary() && !manualRedactionEntry.isAddToDossierDictionary())
|| ((manualRedactionEntry.isAddToDictionary() || manualRedactionEntry.isAddToDictionary()) && manualRedactionEntry.getProcessedDate() == null);
}
private RedactionLogEntry createRedactionLogEntry(ManualRedactionEntry manualRedactionEntry, String id,
String dossierTemplateId) {
Colors colors, List<Type> types) {
var addToDictionary = manualRedactionEntry.isAddToDictionary() || manualRedactionEntry.isAddToDossierDictionary();
var change = ManualChange.from(manualRedactionEntry)
.withManualRedactionType(addToDictionary ? ManualRedactionType.ADD_TO_DICTIONARY : ManualRedactionType.ADD_LOCALLY);
List<ManualChange> changeList = new ArrayList<>();
changeList.add(change);
return RedactionLogEntry.builder()
.id(id)
.color(getColorForManualAdd(manualRedactionEntry.getType(), dossierTemplateId, manualRedactionEntry.getStatus()))
.color(getColorForManualAdd(manualRedactionEntry.getType(), colors, manualRedactionEntry.getStatus(), types))
.reason(manualRedactionEntry.getReason())
.isDictionaryEntry(manualRedactionEntry.isAddToDictionary())
.isDossierDictionaryEntry(manualRedactionEntry.isAddToDossierDictionary())
@@ -340,42 +335,59 @@ public class RedactionLogMergeService {
.redacted(true)
.isHint(false)
.sectionNumber(-1)
.manual(true)
.status(manualRedactionEntry.getStatus())
.manualRedactionType(ManualRedactionType.ADD)
.manualRedactionUserId(manualRedactionEntry.getUser())
.rectangle(manualRedactionEntry.isRectangle())
.manualChanges(changeList)
.build();
}
private float[] getColor(String type, String dossierTemplateId, boolean requested, boolean isRedaction,
boolean skipped) {
private float[] getColor(String type, Colors colors, boolean requested, boolean isRedaction,
boolean skipped, List<Type> types) {
if (requested) {
return dictionaryService.getRequestRemoveColor(dossierTemplateId);
return convertColor(colors.getRequestRemove());
}
if (skipped || !isRedaction && !dictionaryService.isHint(type, dossierTemplateId)) {
return dictionaryService.getNotRedactedColor(dossierTemplateId);
if (skipped || !isRedaction && !isHint(types, type)) {
return convertColor(colors.getNotRedacted());
}
return dictionaryService.getColor(type, dossierTemplateId);
return getColor(types, type);
}
private float[] getColorForManualAdd(String type, String dossierTemplateId, AnnotationStatus status) {
private float[] getColorForManualAdd(String type, Colors colors, AnnotationStatus status, List<Type> types) {
if (status.equals(AnnotationStatus.REQUESTED)) {
return dictionaryService.getRequestAddColor(dossierTemplateId);
return convertColor(colors.getRequestAdd());
} else if (status.equals(AnnotationStatus.DECLINED)) {
return dictionaryService.getNotRedactedColor(dossierTemplateId);
return convertColor(colors.getNotRedacted());
}
return getColor(type, dossierTemplateId);
return getColor(types, type);
}
private float[] getColor(String type, String dossierTemplateId) {
private float[] getColor(List<Type> types, String type) {
return dictionaryService.getColor(type, dossierTemplateId);
Optional<Type> foundType = types.stream().filter(t -> t.getType().equals(type)).findFirst();
if (foundType.isPresent()){
return convertColor(foundType.get().getHexColor());
}
return convertColor("#9398a0");
}
boolean isHint(List<Type> types, String type){
Optional<Type> foundType = types.stream().filter(t -> t.getType().equals(type)).findFirst();
if (foundType.isPresent()){
return foundType.get().isHint();
}
return false;
}
private float[] convertColor(String hex) {
Color color = Color.decode(hex);
return new float[]{color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f};
}
@@ -1,10 +1,5 @@
package com.iqser.red.service.redaction.v1.server.redaction.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.v1.model.CellRectangle;
import com.iqser.red.service.redaction.v1.model.Point;
import com.iqser.red.service.redaction.v1.model.SectionRectangle;
@@ -14,8 +9,11 @@ import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Cell;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Table;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
@RequiredArgsConstructor
@@ -1,35 +1,19 @@
package com.iqser.red.service.redaction.v1.server.redaction.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.v1.model.Point;
import com.iqser.red.service.redaction.v1.model.SectionArea;
import com.iqser.red.service.redaction.v1.server.classification.model.Document;
import com.iqser.red.service.redaction.v1.server.classification.model.Footer;
import com.iqser.red.service.redaction.v1.server.classification.model.Header;
import com.iqser.red.service.redaction.v1.server.classification.model.Paragraph;
import com.iqser.red.service.redaction.v1.server.classification.model.SectionText;
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
import com.iqser.red.service.redaction.v1.server.classification.model.UnclassifiedText;
import com.iqser.red.service.redaction.v1.server.redaction.model.CellValue;
import com.iqser.red.service.redaction.v1.server.redaction.model.Image;
import com.iqser.red.service.redaction.v1.server.redaction.model.ImageType;
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
import com.iqser.red.service.redaction.v1.server.classification.model.*;
import com.iqser.red.service.redaction.v1.server.redaction.model.*;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Cell;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Table;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Slf4j
@Service
@@ -15,8 +15,11 @@ import java.util.stream.Collectors;
import com.iqser.red.service.redaction.v1.model.Engine;
import com.iqser.red.service.redaction.v1.server.redaction.model.Dictionary;
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryIncrementValue;
import com.iqser.red.service.redaction.v1.server.redaction.model.DictionaryModel;
import com.iqser.red.service.redaction.v1.server.redaction.model.Entity;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityPositionSequence;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityType;
import com.iqser.red.service.redaction.v1.server.redaction.model.Image;
import com.iqser.red.service.redaction.v1.server.redaction.model.SearchableText;
import lombok.experimental.UtilityClass;
@@ -54,16 +57,34 @@ public class EntitySearchUtils {
}
public Set<Entity> find(String inputString, Set<String> values, String type, String headline, int sectionNumber,
boolean isDictionaryEntry, boolean isDossierDictionary, Engine engine) {
public Set<Entity> findEntities(String inputString, Set<String> values, DictionaryModel type, String headline, int sectionNumber, boolean isDictionaryEntry, boolean isDossierDictionary,
Engine engine, boolean ignoreMinLength, boolean asRecommendation) {
Set<Entity> found = find(inputString, values, type.getType(), headline, sectionNumber, isDictionaryEntry, isDossierDictionary, engine, ignoreMinLength, asRecommendation ? EntityType.RECOMMENDATION : EntityType.ENTITY);
if(asRecommendation){
Set<Entity> falseRecommendations = find(inputString, type.getFalseRecommendationValues(), type.getType(), headline, sectionNumber, isDictionaryEntry, isDossierDictionary, engine, ignoreMinLength, EntityType.FALSE_RECOMMENDATION);
removeFalsePositives(found, falseRecommendations);
found.addAll(falseRecommendations);
} else {
Set<Entity> falsePositives = find(inputString, type.getFalsePositiveValues(), type.getType(), headline, sectionNumber, isDictionaryEntry, isDossierDictionary, engine, ignoreMinLength, EntityType.FALSE_POSITIVE);
removeFalsePositives(found, falsePositives);
found.addAll(falsePositives);
}
return found;
}
public Set<Entity> find(String inputString, Set<String> values, String type, String headline, int sectionNumber, boolean isDictionaryEntry, boolean isDossierDictionary,
Engine engine, boolean ignoreMinLength, EntityType entityType) {
Set<Entity> found = new HashSet<>();
for (String value : values) {
String cleanValue = value.trim();
if (cleanValue.length() <= 2) {
if (!ignoreMinLength && cleanValue.length() <= 2) {
continue;
}
@@ -74,7 +95,7 @@ public class EntitySearchUtils {
stopIndex = startIndex + cleanValue.length();
if (startIndex > -1 && (startIndex == 0 || Character.isWhitespace(inputString.charAt(startIndex - 1)) || isSeparator(inputString.charAt(startIndex - 1))) && (stopIndex == inputString.length() || isSeparator(inputString.charAt(stopIndex)))) {
found.add(new Entity(inputString.substring(startIndex, stopIndex), type, startIndex, stopIndex, headline, sectionNumber, isDictionaryEntry, isDossierDictionary, engine));
found.add(new Entity(inputString.substring(startIndex, stopIndex), type, startIndex, stopIndex, headline, sectionNumber, isDictionaryEntry, isDossierDictionary, engine, entityType));
}
} while (startIndex > -1);
}
@@ -82,6 +103,7 @@ public class EntitySearchUtils {
}
private boolean isSeparator(char c) {
return Character.isWhitespace(c) || Pattern.matches("\\p{Punct}", String.valueOf(c)) || c == '\"' || c == '‘' || c == '’';
@@ -98,13 +120,10 @@ public class EntitySearchUtils {
for (String word : entitiesByWord.keySet()) {
List<Entity> orderedEntities = entitiesByWord.get(word)
.stream()
.sorted(Comparator.comparing(Entity::getStart))
.collect(Collectors.toList());
List<Entity> orderedEntities = entitiesByWord.get(word).stream().sorted(Comparator.comparing(Entity::getStart)).collect(Collectors.toList());
Entity firstEntity = orderedEntities.get(0);
List<EntityPositionSequence> positionSequences = text.getSequences(firstEntity.getWord()
.trim(), dictionary == null ? true : dictionary.isCaseInsensitiveDictionary(firstEntity.getType()), firstEntity.getTargetSequences());
.trim(), dictionary == null || dictionary.isCaseInsensitiveDictionary(firstEntity.getType()), firstEntity.getTargetSequences());
for (int i = 0; i <= orderedEntities.size() - 1; i++) {
try {
@@ -121,6 +140,25 @@ public class EntitySearchUtils {
}
public void removeFalsePositives(Set<Entity> entities, Set<Entity> falsePositives) {
List<Entity> wordsToRemove = new ArrayList<>();
for (Entity word : falsePositives) {
for (Entity inner : entities) {
if (inner.getWord().length() < word.getWord()
.length() && inner.getStart() >= word.getStart() && inner.getEnd() <= word.getEnd() && word != inner && word.getSectionNumber() == inner.getSectionNumber()) {
wordsToRemove.add(inner);
}
}
}
entities.removeAll(wordsToRemove);
entities.removeAll(falsePositives);
}
public void removeEntitiesContainedInLarger(Set<Entity> entities) {
List<Entity> wordsToRemove = new ArrayList<>();
@@ -128,11 +166,43 @@ public class EntitySearchUtils {
for (Entity inner : entities) {
if (inner.getWord().length() < word.getWord()
.length() && inner.getStart() >= word.getStart() && inner.getEnd() <= word.getEnd() && word != inner && word.getSectionNumber() == inner.getSectionNumber()) {
wordsToRemove.add(inner);
if (word.getEntityType().equals(EntityType.RECOMMENDATION) && word.getEntityType().equals(EntityType.ENTITY)) {
wordsToRemove.add(word);
} else {
wordsToRemove.add(inner);
}
}
}
}
entities.removeAll(wordsToRemove);
wordsToRemove.stream().filter(e -> !e.getEntityType().equals(EntityType.FALSE_POSITIVE) && !e.getEntityType().equals(EntityType.FALSE_RECOMMENDATION)).forEach(entities::remove);
}
public static void removeEntitiesContainedInRedactedLogos(Map<Integer, Set<Image>> imagesPerPage, Map<Integer, List<Entity>> entitiesPerPage) {
if (entitiesPerPage != null && imagesPerPage != null) {
for (int page : imagesPerPage.keySet()) {
Set<Entity> removeEntitiesFromPage = new HashSet<>();
if (!entitiesPerPage.containsKey(page)) {
continue;
}
imagesPerPage.get(page).forEach(image -> {
if (image.isRedaction()) {
entitiesPerPage.get(page).forEach(entity -> {
if (isImageOverlappingEntity(image, entity)) {
log.info("Logo overlaps entity and entity will be removed");
removeEntitiesFromPage.add(entity);
}
});
}
});
entitiesPerPage.get(page).removeAll(removeEntitiesFromPage);
}
}
}
@@ -170,22 +240,17 @@ public class EntitySearchUtils {
}
public void addOrAddEngine(Set<Entity> existing, Set<Entity> toBeAdded) {
private boolean isImageOverlappingEntity(Image image, Entity entity) {
for (Entity toAdd : toBeAdded) {
if (existing.contains(toAdd)) {
Optional<Entity> existingOptional = existing.stream()
.filter(entity -> entity.equals(toAdd))
.findFirst();
if (!existingOptional.isPresent()) {
return;
}
var existingEntity = existingOptional.get();
existingEntity.getEngines().addAll(toAdd.getEngines());
} else {
existing.add(toAdd);
}
}
return image.getPosition() != null && entity.getPositionSequences() != null && entity.getPositionSequences().get(0) != null && entity.getPositionSequences()
.get(0)
.getSequences() != null && entity.getPositionSequences().get(0).getSequences().get(0) != null && image.getPosition().getX() < entity.getPositionSequences()
.get(0)
.getSequences()
.get(0)
.getX1() && image.getPosition().getX() + image.getPosition().getWidth() > entity.getPositionSequences().get(0).getSequences().get(0).getX2() && image.getPosition()
.getY() < entity.getPositionSequences().get(0).getSequences().get(0).getY1() && image.getPosition().getY() + image.getPosition()
.getHeight() > entity.getPositionSequences().get(0).getSequences().get(0).getY2();
}
@@ -205,11 +270,28 @@ public class EntitySearchUtils {
}
public void addOrAddEngine(Set<Entity> existing, Set<Entity> toBeAdded) {
for (Entity toAdd : toBeAdded) {
if (existing.contains(toAdd)) {
Optional<Entity> existingOptional = existing.stream().filter(entity -> entity.equals(toAdd)).findFirst();
if (!existingOptional.isPresent()) {
return;
}
var existingEntity = existingOptional.get();
existingEntity.getEngines().addAll(toAdd.getEngines());
} else {
existing.add(toAdd);
}
}
}
private boolean overlaps(Set<Entity> existingEntities, Entity found) {
for (Entity existing : existingEntities) {
if(existing.getStart().equals(found.getStart())){
if (existing.getStart().equals(found.getStart())) {
continue;
}
@@ -31,10 +31,12 @@ public class ImageService {
public Map<Integer, List<PdfImage>> convertImages(String dossierId, String fileId){
var imageClassificationStream = redactionStorageService.getStoredObject(RedactionStorageService.StorageIdUtils.getStorageId(dossierId, fileId, FileType.IMAGE_INFO));
ImageServiceResponse imageServiceResponse = objectMapper.readValue(imageClassificationStream, ImageServiceResponse.class);
Map<Integer, List<PdfImage>> images = new HashMap<>();
imageServiceResponse.getImageMetadata().stream().forEach(imageMetadata -> {
imageServiceResponse.getData().stream().forEach(imageMetadata -> {
var classification = imageMetadata.getFilters().isAllPassed() ? ImageType.valueOf(imageMetadata.getClassification().getLabel().toUpperCase(Locale.ROOT)) : ImageType.OTHER;
images.computeIfAbsent(imageMetadata.getPosition().getPageNumber() ,x -> new ArrayList<>())
.add(new PdfImage(new RedRectangle2D(imageMetadata.getPosition().getX1(), imageMetadata.getPosition().getY1(), imageMetadata.getGeometry().getWidth(), imageMetadata.getGeometry().getHeight()), classification, imageMetadata.getPosition().getPageNumber()));
@@ -1,8 +1,9 @@
package com.iqser.red.service.redaction.v1.server.settings;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
@Data
@ConfigurationProperties("redaction-service")
public class RedactionServiceSettings {
@@ -17,6 +18,8 @@ public class RedactionServiceSettings {
private int analysisVersion = 1;
private boolean enableEntityRecognition = true;
private boolean nerServiceEnabled = true;
private boolean priorityMode;
}
@@ -8,6 +8,7 @@ import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.redaction.v1.model.ImportedRedactions;
import com.iqser.red.service.redaction.v1.model.RedactionLog;
import com.iqser.red.service.redaction.v1.model.SectionGrid;
import com.iqser.red.service.redaction.v1.server.classification.model.Text;
@@ -44,6 +45,24 @@ public class RedactionStorageService {
}
public ImportedRedactions getImportedRedactions(String dossierId, String fileId) {
InputStreamResource inputStreamResource;
try {
inputStreamResource = storageService.getObject(StorageIdUtils.getStorageId(dossierId, fileId, FileType.IMPORTED_REDACTIONS));
} catch (StorageObjectDoesNotExist e) {
log.debug("Imported redactions not available.");
return null;
}
try {
return objectMapper.readValue(inputStreamResource.getInputStream(), ImportedRedactions.class);
} catch (IOException e) {
throw new RuntimeException("Could not imported redactions", e);
}
}
public RedactionLog getRedactionLog(String dossierId, String fileId) {
InputStreamResource inputStreamResource;
@@ -86,14 +105,13 @@ public class RedactionStorageService {
try {
inputStreamResource = storageService.getObject(StorageIdUtils.getStorageId(dossierId, fileId, FileType.NER_ENTITIES));
} catch (StorageObjectDoesNotExist e) {
log.debug("NER Entities not available.");
return null;
throw new NotFoundException("NER Entities are not available.");
}
try {
return objectMapper.readValue(inputStreamResource.getInputStream(), NerEntities.class);
} catch (IOException e) {
throw new RuntimeException("Could not convert NerEntities", e);
throw new RuntimeException("Could not convert NER Entities", e);
}
}
@@ -127,8 +145,6 @@ public class RedactionStorageService {
return dossierId + "/" + fileId + "." + fileType.name() + fileType.getExtension();
}
}
}
@@ -9,6 +9,7 @@ import org.junit.Test;
import com.iqser.red.service.redaction.v1.model.Engine;
import com.iqser.red.service.redaction.v1.server.redaction.model.Entity;
import com.iqser.red.service.redaction.v1.server.redaction.model.EntityType;
public class EntitySearchUtilsTest {
@@ -16,8 +17,8 @@ public class EntitySearchUtilsTest {
public void testNestedEntitiesRemoval() {
Set<Entity> entities = new HashSet<>();
Entity nested = new Entity("nested", "fake type", 10, 16, "fake headline", 0, false, false, Engine.RULE);
Entity nesting = new Entity("nesting nested", "fake type", 2, 16, "fake headline", 0, false, false, Engine.RULE);
Entity nested = new Entity("nested", "fake type", 10, 16, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity nesting = new Entity("nesting nested", "fake type", 2, 16, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
entities.add(nested);
entities.add(nesting);
EntitySearchUtils.removeEntitiesContainedInLarger(entities);
@@ -39,14 +40,14 @@ public class EntitySearchUtilsTest {
// Arrange
Set<Entity> existingEntities = new HashSet<>();
Entity existingEntity1 = new Entity("Batman", "fake type", 0, 5, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity1 = new Entity("Batman", "fake type", 0, 5, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity existingEntity2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
existingEntities.add(existingEntity1);
existingEntities.add(existingEntity2);
Set<Entity> foundEntities = new HashSet<>();
Entity foundEntities1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities2 = new Entity("Superman Y.", "fake type", 10, 20, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity foundEntities2 = new Entity("Superman Y.", "fake type", 10, 20, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
foundEntities.add(foundEntities1);
foundEntities.add(foundEntities2);
@@ -72,14 +73,14 @@ public class EntitySearchUtilsTest {
// Arrange
Set<Entity> existingEntities = new HashSet<>();
Entity existingEntity1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity existingEntity2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
existingEntities.add(existingEntity1);
existingEntities.add(existingEntity2);
Set<Entity> foundEntities = new HashSet<>();
Entity foundEntities1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities2 = new Entity("X. Superman Y.", "fake type", 7, 20, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity foundEntities2 = new Entity("X. Superman Y.", "fake type", 7, 20, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
foundEntities.add(foundEntities1);
foundEntities.add(foundEntities2);
@@ -104,14 +105,14 @@ public class EntitySearchUtilsTest {
// Arrange
Set<Entity> existingEntities = new HashSet<>();
Entity existingEntity1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE,EntityType.ENTITY);
Entity existingEntity2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
existingEntities.add(existingEntity1);
existingEntities.add(existingEntity2);
Set<Entity> foundEntities = new HashSet<>();
Entity foundEntities1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities2 = new Entity("X. Superman", "fake type", 7, 17, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity foundEntities2 = new Entity("X. Superman", "fake type", 7, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
foundEntities.add(foundEntities1);
foundEntities.add(foundEntities2);
@@ -136,14 +137,14 @@ public class EntitySearchUtilsTest {
// Arrange
Set<Entity> existingEntities = new HashSet<>();
Entity existingEntity1 = new Entity("X. Superman", "fake type", 7, 17, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity2 = new Entity("Batman", "fake type", 0, 5, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity1 = new Entity("X. Superman", "fake type", 7, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity existingEntity2 = new Entity("Batman", "fake type", 0, 5, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
existingEntities.add(existingEntity1);
existingEntities.add(existingEntity2);
Set<Entity> foundEntities = new HashSet<>();
Entity foundEntities1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities1 = new Entity("Batman X.", "fake type", 0, 8, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity foundEntities2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
foundEntities.add(foundEntities1);
foundEntities.add(foundEntities2);
@@ -166,14 +167,14 @@ public class EntitySearchUtilsTest {
// Arrange
Set<Entity> existingEntities = new HashSet<>();
Entity existingEntity1 = new Entity("X. Superman", "fake type", 7, 17, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity2 = new Entity("Batman", "fake type", 0, 5, "fake headline", 0, false, false, Engine.RULE);
Entity existingEntity1 = new Entity("X. Superman", "fake type", 7, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity existingEntity2 = new Entity("Batman", "fake type", 0, 5, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
existingEntities.add(existingEntity1);
existingEntities.add(existingEntity2);
Set<Entity> foundEntities = new HashSet<>();
Entity foundEntities1 = new Entity("Batman X. Superman", "fake type", 0, 17, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE);
Entity foundEntities1 = new Entity("Batman X. Superman", "fake type", 0, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
Entity foundEntities2 = new Entity("Superman", "fake type", 10, 17, "fake headline", 0, false, false, Engine.RULE, EntityType.ENTITY);
foundEntities.add(foundEntities1);
foundEntities.add(foundEntities2);
@@ -94,7 +94,7 @@ public class PdfSegmentationServiceTest {
ImageServiceResponse imageServiceResponse = objectMapper.readValue(responseJson.getInputStream(), ImageServiceResponse.class);
Map<Integer, List<PdfImage>> images = new HashMap<>();
imageServiceResponse.getImageMetadata().stream().forEach(imageMetadata -> {
imageServiceResponse.getData().stream().forEach(imageMetadata -> {
images.computeIfAbsent(imageMetadata.getPosition().getPageNumber() ,x -> new ArrayList<>())
.add(new PdfImage(new RedRectangle2D(imageMetadata.getPosition().getX1(), imageMetadata.getPosition().getY1(), imageMetadata.getGeometry().getWidth(), imageMetadata.getGeometry().getHeight()), ImageType.valueOf(imageMetadata.getClassification().getLabel().toUpperCase(Locale.ROOT)), imageMetadata.getPosition().getPageNumber()));
});
@@ -17,7 +17,7 @@ platform.multi-tenancy:
redaction-service:
enable-image-classification: false
enable-entity-recognition: false
enable-entity-recognition: true
storage:
signer-type: 'AWSS3V4SignerType'
@@ -1660,4 +1660,4 @@ Zyma SA
Zyma SA, Nyon, Switzerland
Mambo-Tox Ltd. Biomedical Sciences Building Bassett Crescent East Southampton SO16 7PX UK
Syngenta Environmental Sciences Jealott’s Hill International Research Centre Bracknell, Berkshire RG42 6EY UK
Test Ignored Hint CBI_ADDRESS
Test Ignored Hint CBI_ADDRESS
@@ -3,3 +3,9 @@ Report Number: 33168
Page
Report Number: BFI0714
Tesh Consultants International
B. Rahim
C. J. Alfred
Naka-27 Aomachi, Nomi, Ishikawa 923-1101, Japan, JP
Özgür U. Reyhan
Sude Halide Nurullah
Xinyi Y. Tao
@@ -1,239 +0,0 @@
(Parent
ADME - Bioanalyses, Vergèze,
Aeration: Gentle
Andrews mean
Andrews pKi
BASF study code
Billington –
Bond elute
Brown Liquid
Brown Orange
Brown Solid
Brown disco
Brown discoloured
Brown fat
Brown liquid
Brown solid
Buehler (1980),
Buehler (1980).
Buehler 9
Buehler Test.
Buehler assay
Buehler test
Buehler)
Buehler-like :
Calandra lark
Cary, NC,
Chambersas for
Class 4
Class III’
Class III’)
Class method,
Class2 (effectively
ClassMethod) Vischim
Clay =
Cochran-Armitage Test.
Cochran-armitage test.
Dunnett 2
Dunnett's "t''
Dunnett's Test
Dunnett's multiple
Dunnett's t-test
Dunnett's test
Dunnett) **
Dunnett) Dose
Dunnett) Food
Dunnett) Lower
Dunnett, 1964)
Dunnett’s Multiple
Dunnett’s T
Dunnett’s Test)
Dunnett’s adjustment
Dunnett’s multiple
Dunnett’s post
Dunnett’s t-test)
Fine vacuolation
Fine white
Finewhite powder
Fischer 344
Fischer 344)
Fischer F344
Fischer rat
Fischer) Contaminants:
Fischer-344 acceptability
Fischer-344/
Fischer-344/DuCrj acceptability
Fischer344 rats.
Fischer344) :
Fischerrat embryo
Fischer’s exact
Fisher 344
Fisher Exact Test
Fisher Exact test
Fisher exact test
Fisher's Exact
Fisher-344 rat
Fisher344 (F344/
Fisher344 rat
Fisher344 rats
Fisher344 rats.
Fisher344 rats/
Fisher344) group
Fisher’s exact.
Freeman-Tukey transform
Gentle aeration
Grade 2:
Grade 3)
Grade 3:
Green
Green Alga
Green as
Green-, blue-,
GreenAlga, Selenastrum
GreenAlga.. Springborn
Gross External
Gross Necropsy
Gross and
Gross necropsies
Gross necropsy
Gross pathology
Gross pathology:
Hall Farm
Hall Farm)
HallFarm (396
Ham's F12
Ham’s F10
Hand-held
HarlanWinkelmann, Borchem,
Hill International,
Hill Top
Hill criteria
Hill, Bracknell,
HillTop Chamber,
Hillmodel was
Hill’s model
Japanese White
Jerneplaque assay
Kent. Blood
Klimisch score
Litchfield and Wilcoxon
Long term
Long-Term Toxicity
Long-term
Longbw/
Major Macroscopic findings
Major biliary
Major defects
Major deviations
Major fetal
Major metabolic
Major metabolites
Major route
Major treatment-related
Mallard (Anas
Mallard with
Manual-Hand held
Marsh Frog
Marsh harrier
Masonjars (500
Meier formula)
Meier survival
Miles anti-Canine
Miles anti-dog
Netherlands, 2011
Netherlands, published:
Netherlands. Report
Penn State
Porewater:
Rattray study
Read across
Reed Bunting
Reichert-Jung Cryo
RifconGmbH. Oxon
Ritz and
S.P.-U/
San Diego,
Shaw's Farm,
Sprague
Strain JapaneseWhite
Strong morphological
Weight -
Weight 2744
Weight Gain
Weight On
Weight Range
Weight and
Weight and length
Weight at
Weight gain
Weight loss
Weight of
Weight only
Weight range
Weight, age
Weight, feed
Weight-of-evidence approach
Weight:
Weight; Adult
Weight; Whole
WeightGain 0-1
WeightRange:
Weightat dosing:
Weightrange 200-250g.
White (Tif:
White 10
White 3
White Age
White Powder
White Strain
White blood
White cell
White crystalline
White guideline
White guinea-pigs
White hygroscopic
White powder
White solid
White stork
White swan
White to off-white
White wagtail
White, solid
WhitePowder Lot/
Whiteguideline :
Whitehygroscopic solid
Whitepowder Lot/
Whitepowder Stability
Whitesolid Batch
Whiteswan)
Whiteto off-white
Wildlife International specifications
Wildlife Internationalspecifications Water:
Williams E
Williams medium
Williams' medium
WilliamsE medium
Williams’ test
Wilson's method.
Wilson. All foetal
Wilson.All foetal
Wood pigeons
York.
Zeneca dossier);
Zenecadossier); see
green algae
CTL
No details reported
Not given
Not indicated
Not reported
Not reported.
Not stated
Syngenta
Syngenta AG
N/A
No details reported
Not available
Test facility
TBD
David Chubb
@@ -9,18 +9,31 @@ global Section section
//rule "0: Expand CBI Authors with firstname initials"
// when
// Section(matchesType("CBI_author") || matchesType("recommendation_CBI_author"))
// Section(matchesType("CBI_author"))
// then
// section.expandByRegEx("CBI_author", "(,? [A-Z]\\.?( ?[A-Z]\\.?)?( ?[A-Z]\\.?)?\\b\\.?)", false, 1);
// section.expandByRegEx("recommendation_CBI_author", "(,? [A-Z]\\.?( ?[A-Z]\\.?)?( ?[A-Z]\\.?)?\\b\\.?)", false, 1);
// end
rule "0: Add CBI_author from ai"
when
Section(aiMatchesType("CBI_author"))
then
section.addAiEntities("CBI_author", "CBI_author");
end
rule "0: Combine ai types CBI_author from ai"
when
Section(aiMatchesType("ORG"))
then
section.combineAiTypes("ORG", "STREET,POSTAL,COUNTRY,CARDINAL,CITY,STATE", 20, "CBI_address", 3, false);
end
rule "0: Expand CBI Authors with firstname initials"
when
Section(matchesType("CBI_author") || matchesType("recommendation_CBI_author"))
Section(matchesType("CBI_author"))
then
section.expandByRegEx("CBI_author", "(,? [A-Z]\\.?( ?[A-Z]\\.?)?( ?[A-Z]\\.?)?\\b\\.?)", false, 1, "[^\\s]+");
section.expandByRegEx("recommendation_CBI_author", "(,? [A-Z]\\.?( ?[A-Z]\\.?)?( ?[A-Z]\\.?)?\\b\\.?)", false, 1, "[^\\s]+");
end
@@ -0,0 +1 @@
{"importedRedactions":{"1":[{"id":"755273b5ddc6933eff7f6b6e055c803f","positions":[{"topLeft":{"x":275.0,"y":520.504},"width":55.675,"height":15.582,"page":1}]},{"id":"5e517d2c890272fc140578a26971b459","positions":[{"topLeft":{"x":164.0,"y":316.492},"width":50.7,"height":15.658,"page":1},{"topLeft":{"x":127.0,"y":302.613},"width":119.675,"height":14.902,"page":1},{"topLeft":{"x":81.0,"y":289.622},"width":210.025,"height":14.846,"page":1},{"topLeft":{"x":136.0,"y":275.912},"width":94.2,"height":13.033,"page":1},{"topLeft":{"x":158.0,"y":261.402},"width":52.793,"height":16.218,"page":1},{"topLeft":{"x":177.0,"y":247.413},"width":15.507,"height":16.148,"page":1}]}]}}
@@ -0,0 +1,872 @@
{
"result": {
"1": [
{
"value": "Lastname, J.",
"startOffset": 54,
"endOffset": 66,
"type": "CBI_author"
},
{
"value": "Doe, M.",
"startOffset": 67,
"endOffset": 74,
"type": "CBI_author"
},
{
"value": "Mustermann Lastname M.",
"startOffset": 75,
"endOffset": 97,
"type": "CBI_author"
},
{
"value": "Doe J. Mustermann M.",
"startOffset": 99,
"endOffset": 119,
"type": "CBI_author"
}
],
"2": [
{
"value": "Eikenboom Charalampos",
"startOffset": 148,
"endOffset": 169,
"type": "ORG"
},
{
"value": "Schenk Tanja Schmitt ←",
"startOffset": 170,
"endOffset": 192,
"type": "ORG"
}
],
"3": [
{
"value": "Rue Jean Baffier",
"startOffset": 214,
"endOffset": 230,
"type": "CBI_author"
},
{
"value": "7232",
"startOffset": 155,
"endOffset": 159,
"type": "POSTAL"
},
{
"value": "CX",
"startOffset": 160,
"endOffset": 162,
"type": "COUNTRY"
},
{
"value": "Warnsveld",
"startOffset": 163,
"endOffset": 172,
"type": "CITY"
},
{
"value": "Netherlands",
"startOffset": 174,
"endOffset": 185,
"type": "COUNTRY"
},
{
"value": "NL",
"startOffset": 187,
"endOffset": 189,
"type": "COUNTRY"
},
{
"value": "Institut Industries",
"startOffset": 190,
"endOffset": 209,
"type": "ORG"
},
{
"value": "33",
"startOffset": 211,
"endOffset": 213,
"type": "CARDINAL"
},
{
"value": "Rue Jean Baffier",
"startOffset": 214,
"endOffset": 230,
"type": "STREET"
},
{
"value": "18000",
"startOffset": 232,
"endOffset": 237,
"type": "CARDINAL"
},
{
"value": "Bourges",
"startOffset": 238,
"endOffset": 245,
"type": "CITY"
},
{
"value": "France",
"startOffset": 247,
"endOffset": 253,
"type": "COUNTRY"
},
{
"value": "18300",
"startOffset": 282,
"endOffset": 287,
"type": "CARDINAL"
},
{
"value": "Saint-Satur",
"startOffset": 288,
"endOffset": 299,
"type": "CITY"
},
{
"value": "France",
"startOffset": 301,
"endOffset": 307,
"type": "COUNTRY"
},
{
"value": "Lesdo Industries",
"startOffset": 312,
"endOffset": 328,
"type": "ORG"
},
{
"value": "Chäppelisträssli",
"startOffset": 330,
"endOffset": 346,
"type": "ORG"
},
{
"value": "6078",
"startOffset": 348,
"endOffset": 352,
"type": "POSTAL"
},
{
"value": "Lungern",
"startOffset": 353,
"endOffset": 360,
"type": "STREET"
},
{
"value": "Switzerland",
"startOffset": 362,
"endOffset": 373,
"type": "COUNTRY"
},
{
"value": "Shlissel'burgskaya Ulitsa",
"startOffset": 374,
"endOffset": 399,
"type": "ORG"
},
{
"value": "Nizhny Novgorod Oblast",
"startOffset": 401,
"endOffset": 423,
"type": "STREET"
},
{
"value": "Russia",
"startOffset": 425,
"endOffset": 431,
"type": "CITY"
},
{
"value": "603034",
"startOffset": 433,
"endOffset": 439,
"type": "POSTAL"
},
{
"value": "RU",
"startOffset": 441,
"endOffset": 443,
"type": "COUNTRY"
},
{
"value": "Karl Johans Gate",
"startOffset": 444,
"endOffset": 460,
"type": "STREET"
},
{
"value": "11",
"startOffset": 461,
"endOffset": 463,
"type": "CARDINAL"
},
{
"value": "0154",
"startOffset": 465,
"endOffset": 469,
"type": "POSTAL"
},
{
"value": "Oslo",
"startOffset": 470,
"endOffset": 474,
"type": "CITY"
},
{
"value": "Norway",
"startOffset": 476,
"endOffset": 482,
"type": "COUNTRY"
}
],
"4": [
{
"value": "Expand",
"startOffset": 67,
"endOffset": 73,
"type": "STATE"
},
{
"value": "Hint Clarissa",
"startOffset": 77,
"endOffset": 90,
"type": "ORG"
},
{
"value": "Dict",
"startOffset": 114,
"endOffset": 118,
"type": "ORG"
},
{
"value": "Authors-Dict",
"startOffset": 171,
"endOffset": 183,
"type": "ORG"
}
],
"6": [
{
"value": "Michael N.",
"startOffset": 7,
"endOffset": 17,
"type": "CBI_author"
},
{
"value": "Weyland Industries",
"startOffset": 76,
"endOffset": 94,
"type": "ORG"
}
],
"7": [
{
"value": "Funnarie B.",
"startOffset": 7,
"endOffset": 18,
"type": "CBI_author"
},
{
"value": "Authentic Diagnostics",
"startOffset": 73,
"endOffset": 94,
"type": "ORG"
}
],
"8": [
{
"value": "Feuer A.",
"startOffset": 6,
"endOffset": 14,
"type": "CBI_author"
},
{
"value": "Tyrell Corporation",
"startOffset": 74,
"endOffset": 92,
"type": "ORG"
}
],
"12": [
{
"value": "Melanie",
"startOffset": 292,
"endOffset": 299,
"type": "CBI_author"
}
],
"13": [
{
"value": "Stark Industries",
"startOffset": 184,
"endOffset": 200,
"type": "ORG"
}
],
"14": [
{
"value": "Omni Consumer Products Do",
"startOffset": 197,
"endOffset": 222,
"type": "ORG"
}
],
"15": [
{
"value": "Omni Consumer Products Do",
"startOffset": 122,
"endOffset": 147,
"type": "ORG"
}
],
"16": [
{
"value": "Asya Lyon",
"startOffset": 253,
"endOffset": 262,
"type": "CBI_author"
},
{
"value": "Carina Madsen",
"startOffset": 264,
"endOffset": 277,
"type": "CBI_author"
},
{
"value": "Alexandra Häusler",
"startOffset": 279,
"endOffset": 296,
"type": "CBI_author"
},
{
"value": "Hanke Mendel",
"startOffset": 298,
"endOffset": 310,
"type": "CBI_author"
},
{
"value": "Kwok, Jun K.",
"startOffset": 444,
"endOffset": 456,
"type": "CBI_author"
},
{
"value": "Tu Wong",
"startOffset": 458,
"endOffset": 465,
"type": "CBI_author"
},
{
"value": "Qiang Suen",
"startOffset": 467,
"endOffset": 477,
"type": "CBI_author"
},
{
"value": "Zhou Mah",
"startOffset": 479,
"endOffset": 487,
"type": "CBI_author"
},
{
"value": "Lei W. Huang",
"startOffset": 499,
"endOffset": 511,
"type": "CBI_author"
},
{
"value": "Ru X.",
"startOffset": 513,
"endOffset": 518,
"type": "CBI_author"
},
{
"value": "Oxford University Press",
"startOffset": 166,
"endOffset": 189,
"type": "ORG"
},
{
"value": "Iakovos Geiger",
"startOffset": 222,
"endOffset": 236,
"type": "ORG"
},
{
"value": "Julian Ritter",
"startOffset": 238,
"endOffset": 251,
"type": "CITY"
},
{
"value": "Asya Lyon",
"startOffset": 253,
"endOffset": 262,
"type": "ORG"
},
{
"value": "Carina Madsen",
"startOffset": 264,
"endOffset": 277,
"type": "CITY"
},
{
"value": "Alexandra Häusler",
"startOffset": 279,
"endOffset": 296,
"type": "ORG"
},
{
"value": "Hanke Mendel",
"startOffset": 298,
"endOffset": 310,
"type": "ORG"
},
{
"value": "Ranya",
"startOffset": 312,
"endOffset": 317,
"type": "COUNTRY"
},
{
"value": "Eikenboom",
"startOffset": 318,
"endOffset": 327,
"type": "ORG"
},
{
"value": "Min Kwok",
"startOffset": 440,
"endOffset": 448,
"type": "ORG"
},
{
"value": "Qiang Suen",
"startOffset": 467,
"endOffset": 477,
"type": "CITY"
},
{
"value": "Zhou Mah",
"startOffset": 479,
"endOffset": 487,
"type": "ORG"
},
{
"value": "Ning Liu",
"startOffset": 489,
"endOffset": 497,
"type": "STREET"
},
{
"value": "Lei W. Huang, Ru X. Wu",
"startOffset": 499,
"endOffset": 521,
"type": "ORG"
}
],
"17": [
{
"value": "Nurullah Özgür",
"startOffset": 210,
"endOffset": 224,
"type": "CBI_author"
},
{
"value": "Reyhan B.",
"startOffset": 228,
"endOffset": 237,
"type": "CBI_author"
},
{
"value": "Alfred Xinyi Y.",
"startOffset": 250,
"endOffset": 265,
"type": "CBI_author"
},
{
"value": "Redacted",
"startOffset": 12,
"endOffset": 20,
"type": "ORG"
},
{
"value": "Aomachi",
"startOffset": 154,
"endOffset": 161,
"type": "ORG"
},
{
"value": "Nomi",
"startOffset": 163,
"endOffset": 167,
"type": "ORG"
},
{
"value": "Ishikawa",
"startOffset": 169,
"endOffset": 177,
"type": "CITY"
},
{
"value": "923-1101",
"startOffset": 178,
"endOffset": 186,
"type": "CARDINAL"
},
{
"value": "Japan",
"startOffset": 188,
"endOffset": 193,
"type": "COUNTRY"
},
{
"value": "JP",
"startOffset": 195,
"endOffset": 197,
"type": "COUNTRY"
},
{
"value": "Sude Halide Nurullah Özgür U. Reyhan B. Rahim C. J. Alfred Xinyi Y. Tao Clara Siegfried",
"startOffset": 198,
"endOffset": 285,
"type": "ORG"
},
{
"value": "Dict",
"startOffset": 301,
"endOffset": 305,
"type": "ORG"
}
],
"18": [
{
"value": "Redact Emails",
"startOffset": 12,
"endOffset": 25,
"type": "ORG"
}
],
"19": [
{
"value": "Central Research Industry",
"startOffset": 274,
"endOffset": 299,
"type": "ORG"
},
{
"value": "Maximiliam Schmitt",
"startOffset": 530,
"endOffset": 548,
"type": "ORG"
},
{
"value": "European Central Institute",
"startOffset": 727,
"endOffset": 753,
"type": "ORG"
},
{
"value": "Emilia Lockhart Alternative",
"startOffset": 775,
"endOffset": 802,
"type": "ORG"
},
{
"value": "Cyberdyne Systems Tower",
"startOffset": 812,
"endOffset": 835,
"type": "ORG"
},
{
"value": "121a",
"startOffset": 844,
"endOffset": 848,
"type": "CARDINAL"
},
{
"value": "Hong Kong",
"startOffset": 849,
"endOffset": 858,
"type": "COUNTRY"
},
{
"value": "BT",
"startOffset": 860,
"endOffset": 862,
"type": "COUNTRY"
}
],
"21": [
{
"value": "Central Research Industry",
"startOffset": 270,
"endOffset": 295,
"type": "ORG"
},
{
"value": "Maximiliam Schmitt",
"startOffset": 526,
"endOffset": 544,
"type": "ORG"
},
{
"value": "European Central Institute",
"startOffset": 723,
"endOffset": 749,
"type": "ORG"
},
{
"value": "Emilia Lockhart Alternative",
"startOffset": 771,
"endOffset": 798,
"type": "ORG"
},
{
"value": "Cyberdyne Systems Tower",
"startOffset": 808,
"endOffset": 831,
"type": "ORG"
},
{
"value": "121a",
"startOffset": 840,
"endOffset": 844,
"type": "CARDINAL"
},
{
"value": "Hong Kong",
"startOffset": 845,
"endOffset": 854,
"type": "COUNTRY"
},
{
"value": "BT",
"startOffset": 856,
"endOffset": 858,
"type": "COUNTRY"
}
],
"22": [
{
"value": "Riddley",
"startOffset": 236,
"endOffset": 243,
"type": "ORG"
},
{
"value": "359-21",
"startOffset": 259,
"endOffset": 265,
"type": "CARDINAL"
},
{
"value": "Huam-dong",
"startOffset": 266,
"endOffset": 275,
"type": "STATE"
},
{
"value": "Yongsan-gu",
"startOffset": 276,
"endOffset": 286,
"type": "CITY"
},
{
"value": "Seoul",
"startOffset": 287,
"endOffset": 292,
"type": "CITY"
},
{
"value": "South Korea Phone",
"startOffset": 294,
"endOffset": 311,
"type": "COUNTRY"
},
{
"value": "Soylent Corporation",
"startOffset": 500,
"endOffset": 519,
"type": "ORG"
}
],
"23": [
{
"value": "Umbrella Corporation",
"startOffset": 208,
"endOffset": 228,
"type": "ORG"
},
{
"value": "Jill",
"startOffset": 238,
"endOffset": 242,
"type": "ORG"
},
{
"value": "359-21",
"startOffset": 262,
"endOffset": 268,
"type": "CARDINAL"
},
{
"value": "Huam-dong",
"startOffset": 269,
"endOffset": 278,
"type": "STATE"
},
{
"value": "Yongsan-gu",
"startOffset": 279,
"endOffset": 289,
"type": "CITY"
},
{
"value": "Seoul",
"startOffset": 290,
"endOffset": 295,
"type": "CITY"
},
{
"value": "South Korea Phone",
"startOffset": 297,
"endOffset": 314,
"type": "COUNTRY"
}
],
"26": [
{
"value": "Umbrella Corporation",
"startOffset": 209,
"endOffset": 229,
"type": "ORG"
}
],
"28": [
{
"value": "Purity Hint",
"startOffset": 9,
"endOffset": 20,
"type": "ORG"
},
{
"value": "Hint",
"startOffset": 35,
"endOffset": 39,
"type": "ORG"
}
],
"29": [
{
"value": "Ignore",
"startOffset": 9,
"endOffset": 15,
"type": "STREET"
}
],
"30": [
{
"value": "Redact Signatures Redact",
"startOffset": 12,
"endOffset": 36,
"type": "ORG"
},
{
"value": "Dilara",
"startOffset": 139,
"endOffset": 145,
"type": "ORG"
},
{
"value": "Tobias Müller",
"startOffset": 197,
"endOffset": 210,
"type": "ORG"
}
],
"31": [
{
"value": "Redact Logo Redact",
"startOffset": 9,
"endOffset": 27,
"type": "ORG"
}
],
"41": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
],
"42": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
],
"43": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
],
"44": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
],
"45": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
],
"46": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
],
"47": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
],
"48": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
],
"49": [
{
"value": "Page-Footer",
"startOffset": 10,
"endOffset": 21,
"type": "STREET"
}
]
}
}
@@ -1,7 +1,7 @@
{
"dossierId": "f889853e-4bf8-49a9-aae5-c38605c6ef40",
"fileId": "22ef63e29bb2a27db8497272336f6b32",
"imageMetadata": [
"data": [
{
"classification": {
"probabilities": {
@@ -0,0 +1,12 @@
{
"result": {
"1": [
{
"value": "Mannheim",
"startOffset": 0,
"endOffset": 8,
"type": "CITY"
}
]
}
}