gdpr-pseudonymizer 2.3.0__tar.gz → 2.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. gdpr_pseudonymizer-2.3.0/README.md → gdpr_pseudonymizer-2.3.1/PKG-INFO +83 -14
  2. gdpr_pseudonymizer-2.3.0/PKG-INFO → gdpr_pseudonymizer-2.3.1/README.md +33 -64
  3. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/batch.py +18 -0
  4. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/delete_mapping.py +2 -2
  5. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/import_mappings.py +21 -1
  6. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/process.py +7 -0
  7. gdpr_pseudonymizer-2.3.1/gdpr_pseudonymizer/cli/commands/replace_pseudonym.py +300 -0
  8. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/validate_mappings.py +83 -11
  9. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/main.py +51 -0
  10. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/custom_pseudonym.py +183 -29
  11. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/document_processor.py +50 -14
  12. gdpr_pseudonymizer-2.3.1/gdpr_pseudonymizer/core/saved_own_words.py +549 -0
  13. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/database.py +3 -3
  14. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/repositories/audit_repository.py +2 -2
  15. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/repositories/mapping_repository.py +128 -0
  16. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/accessibility/focus_manager.py +16 -5
  17. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/app.py +15 -2
  18. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/en.qm +0 -0
  19. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/en.ts +108 -0
  20. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/fr.ts +104 -0
  21. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/main_window.py +29 -1
  22. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/batch.py +16 -1
  23. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/database.py +303 -8
  24. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/processing.py +19 -1
  25. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/validation.py +4 -0
  26. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/passphrase_dialog.py +33 -2
  27. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/toast.py +2 -1
  28. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/batch_worker.py +29 -1
  29. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/database_worker.py +84 -5
  30. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/detection_worker.py +29 -2
  31. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/finalization_worker.py +2 -0
  32. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/processing_worker.py +3 -0
  33. gdpr_pseudonymizer-2.3.1/gdpr_pseudonymizer/locale/fr/LC_MESSAGES/cli.mo +0 -0
  34. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/locale/fr/LC_MESSAGES/cli.po +14 -0
  35. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/assignment_engine.py +12 -0
  36. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/library_manager.py +70 -13
  37. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/pseudonyms/lotr.json +318 -469
  38. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/pseudonyms/neutral.json +146 -182
  39. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/pseudonyms/star_wars.json +358 -656
  40. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/pyproject.toml +11 -11
  41. gdpr_pseudonymizer-2.3.0/gdpr_pseudonymizer/locale/fr/LC_MESSAGES/cli.mo +0 -0
  42. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/LICENSE +0 -0
  43. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/__init__.py +0 -0
  44. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/__init__.py +0 -0
  45. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/__init__.py +0 -0
  46. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/config_show.py +0 -0
  47. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/destroy_table.py +0 -0
  48. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/export.py +0 -0
  49. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/init.py +0 -0
  50. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/list_entities.py +0 -0
  51. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/list_mappings.py +0 -0
  52. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/stats.py +0 -0
  53. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/config.py +0 -0
  54. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/formatters.py +0 -0
  55. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/i18n.py +0 -0
  56. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/passphrase.py +0 -0
  57. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/progress.py +0 -0
  58. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/validation_stub.py +0 -0
  59. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/validators.py +0 -0
  60. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/__init__.py +0 -0
  61. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/legacy_names.py +0 -0
  62. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/mapping_key.py +0 -0
  63. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/naive_processor.py +0 -0
  64. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/__init__.py +0 -0
  65. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/encryption.py +0 -0
  66. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/models.py +0 -0
  67. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/naive_data.py +0 -0
  68. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/repositories/__init__.py +0 -0
  69. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/repositories/metadata_repository.py +0 -0
  70. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/exceptions.py +0 -0
  71. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/__init__.py +0 -0
  72. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/accessibility/__init__.py +0 -0
  73. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/accessibility/shortcuts.py +0 -0
  74. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/config.py +0 -0
  75. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/dialogs/shortcuts_help.py +0 -0
  76. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/error_handler.py +0 -0
  77. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/__init__.py +0 -0
  78. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/fr.qm +0 -0
  79. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/translator.py +0 -0
  80. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/models/__init__.py +0 -0
  81. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/models/validation_state.py +0 -0
  82. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/app.ico +0 -0
  83. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_16.png +0 -0
  84. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_256.png +0 -0
  85. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_32.png +0 -0
  86. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_48.png +0 -0
  87. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_512.png +0 -0
  88. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/themes/dark.qss +0 -0
  89. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/themes/high-contrast.qss +0 -0
  90. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/themes/light.qss +0 -0
  91. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/__init__.py +0 -0
  92. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/home.py +0 -0
  93. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/results.py +0 -0
  94. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/settings.py +0 -0
  95. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/stub.py +0 -0
  96. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/__init__.py +0 -0
  97. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/confirm_dialog.py +0 -0
  98. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/drop_zone.py +0 -0
  99. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/entity_editor.py +0 -0
  100. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/entity_panel.py +0 -0
  101. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/step_indicator.py +0 -0
  102. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/__init__.py +0 -0
  103. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/model_manager.py +0 -0
  104. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/signals.py +0 -0
  105. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/locale/en/LC_MESSAGES/cli.mo +0 -0
  106. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/locale/en/LC_MESSAGES/cli.po +0 -0
  107. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/__init__.py +0 -0
  108. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/entity_detector.py +0 -0
  109. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/entity_grouping.py +0 -0
  110. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/geography_dictionary.py +0 -0
  111. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/hybrid_detector.py +0 -0
  112. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/model_names.py +0 -0
  113. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/name_dictionary.py +0 -0
  114. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/regex_matcher.py +0 -0
  115. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/spacy_detector.py +0 -0
  116. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/stanza_detector.py +0 -0
  117. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/__init__.py +0 -0
  118. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/gender_detector.py +0 -0
  119. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/neutral_id_generator.py +0 -0
  120. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/__init__.py +0 -0
  121. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/detection_patterns.yaml +0 -0
  122. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/french_gender_lookup.json +0 -0
  123. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/french_geography.json +0 -0
  124. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/french_names.json +0 -0
  125. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/location_noise_filter.yaml +0 -0
  126. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/org_role_filter.yaml +0 -0
  127. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/person_boundaries.yaml +0 -0
  128. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/place_abbreviations.yaml +0 -0
  129. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/salutations.yaml +0 -0
  130. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/__init__.py +0 -0
  131. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/config_manager.py +0 -0
  132. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/file_handler.py +0 -0
  133. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/french_patterns.py +0 -0
  134. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/logger.py +0 -0
  135. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/tabular_reader.py +0 -0
  136. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/tabular_writer.py +0 -0
  137. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/text_form.py +0 -0
  138. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/__init__.py +0 -0
  139. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/actions.py +0 -0
  140. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/context_precomputer.py +0 -0
  141. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/models.py +0 -0
  142. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/ui.py +0 -0
  143. {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/workflow.py +0 -0
@@ -1,3 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: gdpr-pseudonymizer
3
+ Version: 2.3.1
4
+ Summary: CLI and GUI tool for GDPR-compliant pseudonymization of French text documents using NLP-based entity detection and reversible mapping
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: gdpr,pseudonymization,nlp,privacy,data-protection
8
+ Author: GDPR Pseudonymizer Team
9
+ Requires-Python: >=3.11,<3.14
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Legal Industry
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Natural Language :: French
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: Security
23
+ Classifier: Topic :: Text Processing :: Linguistic
24
+ Provides-Extra: all
25
+ Provides-Extra: docx
26
+ Provides-Extra: excel
27
+ Provides-Extra: formats
28
+ Provides-Extra: gui
29
+ Provides-Extra: pdf
30
+ Requires-Dist: PySide6-Essentials (>=6.8.3,<6.9.0) ; (python_version >= "3.11" and python_version < "3.14") and (extra == "gui" or extra == "all")
31
+ Requires-Dist: PyYAML (>=6.0,<7.0)
32
+ Requires-Dist: SQLAlchemy (>=2.0.0,<2.1.0)
33
+ Requires-Dist: click (>=8.0.0,<8.2.0)
34
+ Requires-Dist: cryptography (>=50.0.1,<51.0.0)
35
+ Requires-Dist: defusedxml (>=0.7.1) ; extra == "excel" or extra == "formats" or extra == "all"
36
+ Requires-Dist: markdown-it-py (>=3.0.0,<4.0.0)
37
+ Requires-Dist: openpyxl (>=3.1.0,<4.0.0) ; extra == "excel" or extra == "formats" or extra == "all"
38
+ Requires-Dist: pdfplumber (>=0.11.0,<0.12.0) ; extra == "pdf" or extra == "formats" or extra == "all"
39
+ Requires-Dist: python-docx (>=1.1.0,<2.0.0) ; extra == "docx" or extra == "formats" or extra == "all"
40
+ Requires-Dist: readchar (>=4.2.0,<5.0.0)
41
+ Requires-Dist: rich (>=13.7.0,<14.0.0)
42
+ Requires-Dist: spacy (>=3.7.0,<4.0.0)
43
+ Requires-Dist: structlog (>=23.2.0,<24.0.0)
44
+ Requires-Dist: typer (>=0.9.0,<0.10.0)
45
+ Project-URL: Documentation, https://liochandayo.github.io/RGPDpseudonymizer/
46
+ Project-URL: Homepage, https://github.com/LioChanDaYo/RGPDpseudonymizer
47
+ Project-URL: Repository, https://github.com/LioChanDaYo/RGPDpseudonymizer
48
+ Description-Content-Type: text/markdown
49
+
1
50
  > 🇬🇧 **English** | 🇫🇷 [Français](README.fr.md)
2
51
 
3
52
  # GDPR Pseudonymizer
@@ -14,7 +63,19 @@ Transform sensitive French documents for safe AI analysis with local processing,
14
63
 
15
64
  ---
16
65
 
17
- ## What's New in v2.2
66
+ ## What's New in v2.3
67
+
68
+ - **Names you add are replaced everywhere** — A name you add by hand (`[A]` in the command line, "Ajouter comme entité" in the desktop app) is now replaced at every place it appears, also where it is written in capitals or in capitals without accents ("ZORBAL", "HELENE"), never in a word written in small letters ("Petit" leaves "petit"). A name of two words or more is found whatever its accents and capitals.
69
+ - **Corrections cover the whole name** — Correcting a name or changing its type now applies at every place, in the desktop app as in the command line, in Excel and CSV cells too, and with the full name you type: correcting "Élorine" to "Élorine du Prélis" no longer leaves "du Prélis" in clear.
70
+ - **Your own pseudonym is used** — A pseudonym you choose ("Changer le pseudonyme", `[C]`) is now written in the file and kept for later documents. Before, a generated one was used instead.
71
+ - **Complete erasure** — `delete-mapping "Name"` deletes every saved pseudonym of that name: each type, and each way it was written with capitals or accents. The erasure log no longer keeps the erased name, and the names earlier versions wrote there are removed the first time v2.3 opens the database.
72
+ - **Safer batch runs** — In desktop batch review, each document keeps its own decisions and result when you move between documents, and a running batch can no longer mix with another run. When several documents of a `batch` meet the same new name at the same moment, it gets one pseudonym.
73
+ - **No name left in clear without warning** — Names you neither accepted nor rejected are announced before the file is written. When the detector finds no new name, the command line asks whether to add one it missed.
74
+ - **Same name, two types** — "Zorbaville" the town and "Zorbaville" the company now get separate pseudonyms. Your mapping database is converted once, the first time v2.3 opens it; after that, do not open it with v2.2 or older (copy it first if you may need to go back).
75
+ - **More precise detection** — One candidate per name instead of fragments; job titles (CTO, DRH, "VP Europe") are no longer offered as organisations; names no longer run into the next line; fewer false places; names with particles ("van der", "Le") stay whole; people in greetings, organisations followed by their country, short forms and aliases defined in a contract are now offered; accents stored as a separate mark are handled. On a blind final exam of 30 unseen documents, detection scores F1 61.4% (see [Detection accuracy](#detection-accuracy-epic-10-october-2026)).
76
+
77
+ <details>
78
+ <summary>v2.2 highlights</summary>
18
79
 
19
80
  - **Choose the spaCy model** — `--model` / `pseudonymization.model` accept any spaCy package name (e.g. `en_core_web_trf` for English); `spacy` still selects the French `fr_core_news_lg`. The model only drives NLP detection: regex patterns, dictionaries and pseudonym libraries stay French. See the [CLI Reference](docs/CLI-REFERENCE.md).
20
81
  - **Retype entities during CLI validation (`[T]`)** — Switch a detected entity between PERSON, LOCATION and ORG for every occurrence, instead of rejecting and re-adding it
@@ -22,6 +83,8 @@ Transform sensitive French documents for safe AI analysis with local processing,
22
83
  - **Fresh `pip install` fixed** — v2.1.3 crashed on import when pip resolved SQLAlchemy 2.1; SQLAlchemy is now capped below 2.1
23
84
  - **Audit log records the real model** — e.g. `fr_core_news_lg-3.8.0` instead of `spacy-unknown`
24
85
 
86
+ </details>
87
+
25
88
  <details>
26
89
  <summary>v2.1 highlights</summary>
27
90
 
@@ -47,9 +110,9 @@ Pre-built standalone executables are available for Windows, macOS, and Linux. No
47
110
 
48
111
  | Platform | File | Notes |
49
112
  |----------|------|-------|
50
- | **Windows** | `gdpr-pseudonymizer-2.2.0-windows-setup.exe` | Run the installer. Adds Start Menu shortcut. |
51
- | **macOS (Apple Silicon)** | `gdpr-pseudonymizer-2.2.0-macos-arm64.dmg` | Open DMG, drag to Applications. |
52
- | **Linux** | `gdpr-pseudonymizer-2.2.0-linux.AppImage` | `chmod +x` then run. |
113
+ | **Windows** | `gdpr-pseudonymizer-2.3.0-windows-setup.exe` | Run the installer. Adds Start Menu shortcut. |
114
+ | **macOS (Apple Silicon)** | `gdpr-pseudonymizer-2.3.0-macos-arm64.dmg` | Open DMG, drag to Applications. |
115
+ | **Linux** | `gdpr-pseudonymizer-2.3.0-linux.AppImage` | `chmod +x` then run. |
53
116
 
54
117
  > **Intel Macs:** no DMG is published. GitHub retired its Intel macOS runners, and an
55
118
  > Apple Silicon runner cannot cross-build a working x86_64 bundle. Install with
@@ -127,7 +190,7 @@ GDPR Pseudonymizer is a **privacy-first CLI and GUI tool** that combines AI effi
127
190
 
128
191
  ## 🚀 Quick Start
129
192
 
130
- **Status:** 🎉 **v2.2.0** (September 2026) — spaCy Model Choice, Entity Retyping & Span Fixes
193
+ **Status:** 🎉 **v2.3.0** (November 2026) — Detection Precision, Corrections at Every Place & Complete Erasure
131
194
 
132
195
  ### Getting Started
133
196
 
@@ -149,7 +212,7 @@ pip install gdpr-pseudonymizer[excel]
149
212
  pip install gdpr-pseudonymizer[formats]
150
213
  ```
151
214
 
152
- ### What v2.2 Delivers
215
+ ### What v2.3 Delivers
153
216
 
154
217
  - 🖥️ **Desktop GUI** — Visual entity validation with drag-and-drop, batch dashboard, and database management
155
218
  - 📦 **Standalone executables** — Windows .exe, macOS .dmg (Apple Silicon), Linux AppImage — no Python required
@@ -164,8 +227,10 @@ pip install gdpr-pseudonymizer[formats]
164
227
  - 🎯 **NER accuracy** — LOCATION false-negative rate reduced from 27% to 13% via regex expansion & POS disambiguation
165
228
  - 🧠 **Choice of spaCy model** — French by default; any spaCy model for detection (e.g. `--model en_core_web_trf`)
166
229
  - 🏷️ **Entity retyping** — Change an entity's type (PERSON/LOCATION/ORG) during CLI validation with `[T]`
230
+ - ✍️ **Corrections at every place** — Names you add, correct or retype are replaced wherever they appear, also where written in capitals
231
+ - 🗑️ **Complete erasure** — `delete-mapping` erases every saved pseudonym of a name, and the erasure log keeps no name
167
232
 
168
- **What v2.2 does NOT deliver:**
233
+ **What v2.3 does NOT deliver:**
169
234
  - ❌ Fully automatic "set and forget" processing
170
235
  - ❌ 85%+ AI accuracy (current: F1 61.4% on unseen documents, 82.6% on our development corpus — we plan to raise it)
171
236
  - ❌ Optional validation mode (validation is mandatory)
@@ -180,7 +245,9 @@ pip install gdpr-pseudonymizer[formats]
180
245
 
181
246
  **v2.1 (Q1 2026):** GUI polish, Excel/CSV support, neutral ID theme, NER accuracy improvements, keyboard shortcuts help
182
247
 
183
- **v2.2 (Q3 2026) — CURRENT RELEASE:** Choice of spaCy model, entity retyping in CLI validation, entity-span accuracy fixes, fresh-install fix (SQLAlchemy < 2.1)
248
+ **v2.2 (Q3 2026):** Choice of spaCy model, entity retyping in CLI validation, entity-span accuracy fixes, fresh-install fix (SQLAlchemy < 2.1)
249
+
250
+ **v2.3 (Q4 2026) — CURRENT RELEASE:** Detection precision (Epic 10), benchmark repair and blind final exam, names added or corrected replaced at every place, custom pseudonyms used, complete erasure with no name in the erasure log, safer batch runs
184
251
 
185
252
  **v3.0 (2027+):** NLP accuracy & automation
186
253
  - Fine-tuned French NER model (70-85% F1 target, up from 61.4% on unseen documents today)
@@ -195,7 +262,7 @@ pip install gdpr-pseudonymizer[formats]
195
262
  See [Installation Guide](https://liochandayo.github.io/RGPDpseudonymizer/installation/) for detailed platform-specific instructions.
196
263
 
197
264
  ### Prerequisites
198
- - **Python 3.10, 3.11, or 3.12** (validated in CI/CD — 3.13+ not yet tested)
265
+ - **Python 3.11, 3.12 or 3.13** (validated in CI/CD — 3.14+ not yet tested)
199
266
 
200
267
  ### Install from PyPI (Recommended)
201
268
 
@@ -404,7 +471,7 @@ The validation UI provides an intuitive keyboard-driven interface for reviewing
404
471
 
405
472
  | Component | Technology | Version | Purpose |
406
473
  |-----------|------------|---------|---------|
407
- | **Runtime** | Python | 3.10-3.12 | Validated in CI/CD (3.13+ not yet tested) |
474
+ | **Runtime** | Python | 3.11-3.13 | Validated in CI/CD (3.14+ not yet tested) |
408
475
  | **NLP Library** | spaCy | 3.8.0 | French entity detection (fr_core_news_lg) |
409
476
  | **CLI Framework** | Typer | 0.9+ | Command-line interface |
410
477
  | **Database** | SQLite | 3.35+ | Local mapping table storage with WAL mode |
@@ -511,7 +578,7 @@ The validation UI provides an intuitive keyboard-driven interface for reviewing
511
578
 
512
579
  ## 🛠️ Development Status
513
580
 
514
- **Epics 1-7 Complete** — current release v2.2.0 (September 2026): spaCy model choice, entity retyping, entity-span fixes. v2.1.0 (March 2026) closed Epic 7.
581
+ **Epics 1-7 and 10 Complete** — current release v2.3.0 (November 2026) closed Epic 10: detection precision, corrections at every place, complete erasure. v2.1.0 (March 2026) closed Epic 7.
515
582
 
516
583
  - ✅ **Epic 1:** Foundation & NLP Validation (9 stories) — spaCy integration, validation UI, hybrid detection, entity deduplication
517
584
  - ✅ **Epic 2:** Core Pseudonymization Engine (9 stories) — pseudonym libraries, encryption, audit logging, batch processing, GDPR 1:1 mapping
@@ -539,6 +606,7 @@ The validation UI provides an intuitive keyboard-driven interface for reviewing
539
606
  - ✅ Story 7.5: NER Accuracy Regex Expansion & POS Disambiguation
540
607
  - ✅ Story 7.6: Quality & Compatibility Integration Tests
541
608
  - ✅ Story 7.7: v2.1 Release Preparation
609
+ - ✅ **Epic 10:** v2.3 Detection Precision Quick Wins — benchmark repair and held-out set, blind final exam with confidence ranges, overlap and boundary fixes, fewer false places, greetings and organisation-plus-country handling, type-aware mappings, names added or corrected replaced at every place, custom pseudonyms, erasure log without names, batch fixes, v2.3 release
542
610
  - **Total:** 60 stories, 1670+ tests, 86%+ coverage, all quality gates green
543
611
 
544
612
  ---
@@ -689,7 +757,7 @@ poetry run pytest tests/integration/test_validation_workflow_integration.py -v
689
757
  - **Performance tests:** 19 tests validating all NFR targets — single-document benchmarks (NFR1), entity-detection benchmarks, batch performance (NFR2), memory profiling (NFR4), startup time (NFR5), stability/error rate (NFR6), stress testing (Story 4.5)
690
758
  - **Current coverage:** 86%+ across all modules (100% for progress module, 91.41% for AuditRepository)
691
759
  - **Total tests:** 1670+ tests
692
- - **CI/CD:** Tests run on Python 3.10-3.12 across Windows, macOS, and Linux
760
+ - **CI/CD:** Tests run on Python 3.11-3.13 across Windows, macOS, and Linux
693
761
  - **Quality gates:** All pass (Black, Ruff, mypy, pytest)
694
762
 
695
763
  ### Key Integration Test Scenarios
@@ -722,7 +790,7 @@ The integration test suite covers:
722
790
 
723
791
  | Metric | Value | Status |
724
792
  |--------|-------|--------|
725
- | **Development Progress** | v2.2.0 | ✅ Epics 1-7 complete |
793
+ | **Development Progress** | v2.3.0 | ✅ Epics 1-7 and 10 complete |
726
794
  | **Stories Complete** | 60 (Epics 1-7) | ✅ All epics complete |
727
795
  | **LLM Utility (NFR10)** | 4.27/5.0 (85.4%) | ✅ PASSED (threshold: 80%) |
728
796
  | **Installation Success (NFR3)** | 87.5% (7/8 platforms) | ✅ PASSED (threshold: 85%) |
@@ -762,4 +830,5 @@ The integration test suite covers:
762
830
 
763
831
  ---
764
832
 
765
- **Last Updated:** 2026-10-01 (v2.2.0 — spaCy model choice, entity retyping, entity-span fixes)
833
+ **Last Updated:** 2026-11-03 (v2.3.0 — detection precision, corrections at every place, complete erasure)
834
+
@@ -1,52 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: gdpr-pseudonymizer
3
- Version: 2.3.0
4
- Summary: CLI and GUI tool for GDPR-compliant pseudonymization of French text documents using NLP-based entity detection and reversible mapping
5
- License: MIT
6
- License-File: LICENSE
7
- Keywords: gdpr,pseudonymization,nlp,privacy,data-protection
8
- Author: GDPR Pseudonymizer Team
9
- Requires-Python: >=3.10,<3.14
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Intended Audience :: Legal Industry
13
- Classifier: Intended Audience :: Science/Research
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Natural Language :: French
16
- Classifier: Operating System :: OS Independent
17
- Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Classifier: Programming Language :: Python :: 3.12
21
- Classifier: Programming Language :: Python :: 3.13
22
- Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
- Classifier: Topic :: Security
24
- Classifier: Topic :: Text Processing :: Linguistic
25
- Provides-Extra: all
26
- Provides-Extra: docx
27
- Provides-Extra: excel
28
- Provides-Extra: formats
29
- Provides-Extra: gui
30
- Provides-Extra: pdf
31
- Requires-Dist: PySide6-Essentials (>=6.7.0,<6.8.0) ; (python_version >= "3.10" and python_version < "3.13") and (extra == "gui" or extra == "all")
32
- Requires-Dist: PyYAML (>=6.0,<7.0)
33
- Requires-Dist: SQLAlchemy (>=2.0.0,<2.1.0)
34
- Requires-Dist: click (>=8.0.0,<8.2.0)
35
- Requires-Dist: cryptography (>=50.0.1,<51.0.0)
36
- Requires-Dist: markdown-it-py (>=3.0.0,<4.0.0)
37
- Requires-Dist: openpyxl (>=3.1.0,<4.0.0) ; extra == "excel" or extra == "formats" or extra == "all"
38
- Requires-Dist: pdfplumber (>=0.11.0,<0.12.0) ; extra == "pdf" or extra == "formats" or extra == "all"
39
- Requires-Dist: python-docx (>=1.1.0,<2.0.0) ; extra == "docx" or extra == "formats" or extra == "all"
40
- Requires-Dist: readchar (>=4.2.0,<5.0.0)
41
- Requires-Dist: rich (>=13.7.0,<14.0.0)
42
- Requires-Dist: spacy (>=3.7.0,<4.0.0)
43
- Requires-Dist: structlog (>=23.2.0,<24.0.0)
44
- Requires-Dist: typer (>=0.9.0,<0.10.0)
45
- Project-URL: Documentation, https://liochandayo.github.io/RGPDpseudonymizer/
46
- Project-URL: Homepage, https://github.com/LioChanDaYo/RGPDpseudonymizer
47
- Project-URL: Repository, https://github.com/LioChanDaYo/RGPDpseudonymizer
48
- Description-Content-Type: text/markdown
49
-
50
1
  > 🇬🇧 **English** | 🇫🇷 [Français](README.fr.md)
51
2
 
52
3
  # GDPR Pseudonymizer
@@ -63,7 +14,19 @@ Transform sensitive French documents for safe AI analysis with local processing,
63
14
 
64
15
  ---
65
16
 
66
- ## What's New in v2.2
17
+ ## What's New in v2.3
18
+
19
+ - **Names you add are replaced everywhere** — A name you add by hand (`[A]` in the command line, "Ajouter comme entité" in the desktop app) is now replaced at every place it appears, also where it is written in capitals or in capitals without accents ("ZORBAL", "HELENE"), never in a word written in small letters ("Petit" leaves "petit"). A name of two words or more is found whatever its accents and capitals.
20
+ - **Corrections cover the whole name** — Correcting a name or changing its type now applies at every place, in the desktop app as in the command line, in Excel and CSV cells too, and with the full name you type: correcting "Élorine" to "Élorine du Prélis" no longer leaves "du Prélis" in clear.
21
+ - **Your own pseudonym is used** — A pseudonym you choose ("Changer le pseudonyme", `[C]`) is now written in the file and kept for later documents. Before, a generated one was used instead.
22
+ - **Complete erasure** — `delete-mapping "Name"` deletes every saved pseudonym of that name: each type, and each way it was written with capitals or accents. The erasure log no longer keeps the erased name, and the names earlier versions wrote there are removed the first time v2.3 opens the database.
23
+ - **Safer batch runs** — In desktop batch review, each document keeps its own decisions and result when you move between documents, and a running batch can no longer mix with another run. When several documents of a `batch` meet the same new name at the same moment, it gets one pseudonym.
24
+ - **No name left in clear without warning** — Names you neither accepted nor rejected are announced before the file is written. When the detector finds no new name, the command line asks whether to add one it missed.
25
+ - **Same name, two types** — "Zorbaville" the town and "Zorbaville" the company now get separate pseudonyms. Your mapping database is converted once, the first time v2.3 opens it; after that, do not open it with v2.2 or older (copy it first if you may need to go back).
26
+ - **More precise detection** — One candidate per name instead of fragments; job titles (CTO, DRH, "VP Europe") are no longer offered as organisations; names no longer run into the next line; fewer false places; names with particles ("van der", "Le") stay whole; people in greetings, organisations followed by their country, short forms and aliases defined in a contract are now offered; accents stored as a separate mark are handled. On a blind final exam of 30 unseen documents, detection scores F1 61.4% (see [Detection accuracy](#detection-accuracy-epic-10-october-2026)).
27
+
28
+ <details>
29
+ <summary>v2.2 highlights</summary>
67
30
 
68
31
  - **Choose the spaCy model** — `--model` / `pseudonymization.model` accept any spaCy package name (e.g. `en_core_web_trf` for English); `spacy` still selects the French `fr_core_news_lg`. The model only drives NLP detection: regex patterns, dictionaries and pseudonym libraries stay French. See the [CLI Reference](docs/CLI-REFERENCE.md).
69
32
  - **Retype entities during CLI validation (`[T]`)** — Switch a detected entity between PERSON, LOCATION and ORG for every occurrence, instead of rejecting and re-adding it
@@ -71,6 +34,8 @@ Transform sensitive French documents for safe AI analysis with local processing,
71
34
  - **Fresh `pip install` fixed** — v2.1.3 crashed on import when pip resolved SQLAlchemy 2.1; SQLAlchemy is now capped below 2.1
72
35
  - **Audit log records the real model** — e.g. `fr_core_news_lg-3.8.0` instead of `spacy-unknown`
73
36
 
37
+ </details>
38
+
74
39
  <details>
75
40
  <summary>v2.1 highlights</summary>
76
41
 
@@ -96,9 +61,9 @@ Pre-built standalone executables are available for Windows, macOS, and Linux. No
96
61
 
97
62
  | Platform | File | Notes |
98
63
  |----------|------|-------|
99
- | **Windows** | `gdpr-pseudonymizer-2.2.0-windows-setup.exe` | Run the installer. Adds Start Menu shortcut. |
100
- | **macOS (Apple Silicon)** | `gdpr-pseudonymizer-2.2.0-macos-arm64.dmg` | Open DMG, drag to Applications. |
101
- | **Linux** | `gdpr-pseudonymizer-2.2.0-linux.AppImage` | `chmod +x` then run. |
64
+ | **Windows** | `gdpr-pseudonymizer-2.3.0-windows-setup.exe` | Run the installer. Adds Start Menu shortcut. |
65
+ | **macOS (Apple Silicon)** | `gdpr-pseudonymizer-2.3.0-macos-arm64.dmg` | Open DMG, drag to Applications. |
66
+ | **Linux** | `gdpr-pseudonymizer-2.3.0-linux.AppImage` | `chmod +x` then run. |
102
67
 
103
68
  > **Intel Macs:** no DMG is published. GitHub retired its Intel macOS runners, and an
104
69
  > Apple Silicon runner cannot cross-build a working x86_64 bundle. Install with
@@ -176,7 +141,7 @@ GDPR Pseudonymizer is a **privacy-first CLI and GUI tool** that combines AI effi
176
141
 
177
142
  ## 🚀 Quick Start
178
143
 
179
- **Status:** 🎉 **v2.2.0** (September 2026) — spaCy Model Choice, Entity Retyping & Span Fixes
144
+ **Status:** 🎉 **v2.3.0** (November 2026) — Detection Precision, Corrections at Every Place & Complete Erasure
180
145
 
181
146
  ### Getting Started
182
147
 
@@ -198,7 +163,7 @@ pip install gdpr-pseudonymizer[excel]
198
163
  pip install gdpr-pseudonymizer[formats]
199
164
  ```
200
165
 
201
- ### What v2.2 Delivers
166
+ ### What v2.3 Delivers
202
167
 
203
168
  - 🖥️ **Desktop GUI** — Visual entity validation with drag-and-drop, batch dashboard, and database management
204
169
  - 📦 **Standalone executables** — Windows .exe, macOS .dmg (Apple Silicon), Linux AppImage — no Python required
@@ -213,8 +178,10 @@ pip install gdpr-pseudonymizer[formats]
213
178
  - 🎯 **NER accuracy** — LOCATION false-negative rate reduced from 27% to 13% via regex expansion & POS disambiguation
214
179
  - 🧠 **Choice of spaCy model** — French by default; any spaCy model for detection (e.g. `--model en_core_web_trf`)
215
180
  - 🏷️ **Entity retyping** — Change an entity's type (PERSON/LOCATION/ORG) during CLI validation with `[T]`
181
+ - ✍️ **Corrections at every place** — Names you add, correct or retype are replaced wherever they appear, also where written in capitals
182
+ - 🗑️ **Complete erasure** — `delete-mapping` erases every saved pseudonym of a name, and the erasure log keeps no name
216
183
 
217
- **What v2.2 does NOT deliver:**
184
+ **What v2.3 does NOT deliver:**
218
185
  - ❌ Fully automatic "set and forget" processing
219
186
  - ❌ 85%+ AI accuracy (current: F1 61.4% on unseen documents, 82.6% on our development corpus — we plan to raise it)
220
187
  - ❌ Optional validation mode (validation is mandatory)
@@ -229,7 +196,9 @@ pip install gdpr-pseudonymizer[formats]
229
196
 
230
197
  **v2.1 (Q1 2026):** GUI polish, Excel/CSV support, neutral ID theme, NER accuracy improvements, keyboard shortcuts help
231
198
 
232
- **v2.2 (Q3 2026) — CURRENT RELEASE:** Choice of spaCy model, entity retyping in CLI validation, entity-span accuracy fixes, fresh-install fix (SQLAlchemy < 2.1)
199
+ **v2.2 (Q3 2026):** Choice of spaCy model, entity retyping in CLI validation, entity-span accuracy fixes, fresh-install fix (SQLAlchemy < 2.1)
200
+
201
+ **v2.3 (Q4 2026) — CURRENT RELEASE:** Detection precision (Epic 10), benchmark repair and blind final exam, names added or corrected replaced at every place, custom pseudonyms used, complete erasure with no name in the erasure log, safer batch runs
233
202
 
234
203
  **v3.0 (2027+):** NLP accuracy & automation
235
204
  - Fine-tuned French NER model (70-85% F1 target, up from 61.4% on unseen documents today)
@@ -244,7 +213,7 @@ pip install gdpr-pseudonymizer[formats]
244
213
  See [Installation Guide](https://liochandayo.github.io/RGPDpseudonymizer/installation/) for detailed platform-specific instructions.
245
214
 
246
215
  ### Prerequisites
247
- - **Python 3.10, 3.11, or 3.12** (validated in CI/CD — 3.13+ not yet tested)
216
+ - **Python 3.11, 3.12 or 3.13** (validated in CI/CD — 3.14+ not yet tested)
248
217
 
249
218
  ### Install from PyPI (Recommended)
250
219
 
@@ -453,7 +422,7 @@ The validation UI provides an intuitive keyboard-driven interface for reviewing
453
422
 
454
423
  | Component | Technology | Version | Purpose |
455
424
  |-----------|------------|---------|---------|
456
- | **Runtime** | Python | 3.10-3.12 | Validated in CI/CD (3.13+ not yet tested) |
425
+ | **Runtime** | Python | 3.11-3.13 | Validated in CI/CD (3.14+ not yet tested) |
457
426
  | **NLP Library** | spaCy | 3.8.0 | French entity detection (fr_core_news_lg) |
458
427
  | **CLI Framework** | Typer | 0.9+ | Command-line interface |
459
428
  | **Database** | SQLite | 3.35+ | Local mapping table storage with WAL mode |
@@ -560,7 +529,7 @@ The validation UI provides an intuitive keyboard-driven interface for reviewing
560
529
 
561
530
  ## 🛠️ Development Status
562
531
 
563
- **Epics 1-7 Complete** — current release v2.2.0 (September 2026): spaCy model choice, entity retyping, entity-span fixes. v2.1.0 (March 2026) closed Epic 7.
532
+ **Epics 1-7 and 10 Complete** — current release v2.3.0 (November 2026) closed Epic 10: detection precision, corrections at every place, complete erasure. v2.1.0 (March 2026) closed Epic 7.
564
533
 
565
534
  - ✅ **Epic 1:** Foundation & NLP Validation (9 stories) — spaCy integration, validation UI, hybrid detection, entity deduplication
566
535
  - ✅ **Epic 2:** Core Pseudonymization Engine (9 stories) — pseudonym libraries, encryption, audit logging, batch processing, GDPR 1:1 mapping
@@ -588,6 +557,7 @@ The validation UI provides an intuitive keyboard-driven interface for reviewing
588
557
  - ✅ Story 7.5: NER Accuracy Regex Expansion & POS Disambiguation
589
558
  - ✅ Story 7.6: Quality & Compatibility Integration Tests
590
559
  - ✅ Story 7.7: v2.1 Release Preparation
560
+ - ✅ **Epic 10:** v2.3 Detection Precision Quick Wins — benchmark repair and held-out set, blind final exam with confidence ranges, overlap and boundary fixes, fewer false places, greetings and organisation-plus-country handling, type-aware mappings, names added or corrected replaced at every place, custom pseudonyms, erasure log without names, batch fixes, v2.3 release
591
561
  - **Total:** 60 stories, 1670+ tests, 86%+ coverage, all quality gates green
592
562
 
593
563
  ---
@@ -738,7 +708,7 @@ poetry run pytest tests/integration/test_validation_workflow_integration.py -v
738
708
  - **Performance tests:** 19 tests validating all NFR targets — single-document benchmarks (NFR1), entity-detection benchmarks, batch performance (NFR2), memory profiling (NFR4), startup time (NFR5), stability/error rate (NFR6), stress testing (Story 4.5)
739
709
  - **Current coverage:** 86%+ across all modules (100% for progress module, 91.41% for AuditRepository)
740
710
  - **Total tests:** 1670+ tests
741
- - **CI/CD:** Tests run on Python 3.10-3.12 across Windows, macOS, and Linux
711
+ - **CI/CD:** Tests run on Python 3.11-3.13 across Windows, macOS, and Linux
742
712
  - **Quality gates:** All pass (Black, Ruff, mypy, pytest)
743
713
 
744
714
  ### Key Integration Test Scenarios
@@ -771,7 +741,7 @@ The integration test suite covers:
771
741
 
772
742
  | Metric | Value | Status |
773
743
  |--------|-------|--------|
774
- | **Development Progress** | v2.2.0 | ✅ Epics 1-7 complete |
744
+ | **Development Progress** | v2.3.0 | ✅ Epics 1-7 and 10 complete |
775
745
  | **Stories Complete** | 60 (Epics 1-7) | ✅ All epics complete |
776
746
  | **LLM Utility (NFR10)** | 4.27/5.0 (85.4%) | ✅ PASSED (threshold: 80%) |
777
747
  | **Installation Success (NFR3)** | 87.5% (7/8 platforms) | ✅ PASSED (threshold: 85%) |
@@ -811,5 +781,4 @@ The integration test suite covers:
811
781
 
812
782
  ---
813
783
 
814
- **Last Updated:** 2026-10-01 (v2.2.0 — spaCy model choice, entity retyping, entity-span fixes)
815
-
784
+ **Last Updated:** 2026-11-03 (v2.3.0 — detection precision, corrections at every place, complete erasure)
@@ -41,6 +41,7 @@ from gdpr_pseudonymizer.cli.validators import (
41
41
  validate_theme_or_exit,
42
42
  )
43
43
  from gdpr_pseudonymizer.core.document_processor import DocumentProcessor
44
+ from gdpr_pseudonymizer.core.saved_own_words import notice_line, own_word_rows_of
44
45
  from gdpr_pseudonymizer.data.database import open_database
45
46
  from gdpr_pseudonymizer.exceptions import DatabaseConversionError
46
47
  from gdpr_pseudonymizer.utils.logger import configure_logging, get_logger
@@ -68,6 +69,9 @@ class BatchResult:
68
69
  reused_entities: int = 0
69
70
  total_time_seconds: float = 0.0
70
71
  errors: list[str] = field(default_factory=list)
72
+ # Story 11.7: the largest count of saved pseudonyms holding a word of
73
+ # their own name after a document of this run was written
74
+ own_word_rows: int = 0
71
75
 
72
76
 
73
77
  def _process_single_document_worker(
@@ -92,6 +96,7 @@ def _process_single_document_worker(
92
96
  - entities_reused: int (if success)
93
97
  - processing_time: float (if success)
94
98
  - error: str (if failure)
99
+ - own_word_rows: int (Story 11.7, the notice's count)
95
100
  """
96
101
  input_path, output_path, db_path, passphrase, theme, model, entity_types_csv = args
97
102
 
@@ -126,6 +131,7 @@ def _process_single_document_worker(
126
131
  "entities_reused": result.entities_reused,
127
132
  "processing_time": result.processing_time_seconds,
128
133
  "error": result.error_message if not result.success else None,
134
+ "own_word_rows": own_word_rows_of(result) if result.success else 0,
129
135
  }
130
136
  except Exception as e:
131
137
  return {
@@ -136,6 +142,7 @@ def _process_single_document_worker(
136
142
  "entities_reused": 0,
137
143
  "processing_time": 0.0,
138
144
  "error": str(e),
145
+ "own_word_rows": 0,
139
146
  }
140
147
 
141
148
 
@@ -246,6 +253,10 @@ def _process_batch_parallel(
246
253
  batch_result.total_entities += result["entities_detected"]
247
254
  batch_result.new_entities += result["entities_new"]
248
255
  batch_result.reused_entities += result["entities_reused"]
256
+ batch_result.own_word_rows = max(
257
+ batch_result.own_word_rows,
258
+ result.get("own_word_rows", 0) or 0,
259
+ )
249
260
  else:
250
261
  batch_result.failed_files += 1
251
262
  error_msg = result.get("error", "Unknown error")
@@ -615,6 +626,9 @@ def batch_command(
615
626
  batch_result.total_entities += result.entities_detected
616
627
  batch_result.new_entities += result.entities_new
617
628
  batch_result.reused_entities += result.entities_reused
629
+ batch_result.own_word_rows = max(
630
+ batch_result.own_word_rows, own_word_rows_of(result)
631
+ )
618
632
 
619
633
  # Update progress tracker
620
634
  progress_tracker.update_file_complete(
@@ -692,6 +706,10 @@ def batch_command(
692
706
  # Display summary report
693
707
  _display_batch_summary(batch_result)
694
708
 
709
+ # Story 11.7: one note per run, the largest count of its documents
710
+ if batch_result.own_word_rows > 0:
711
+ console.print(notice_line(batch_result.own_word_rows), markup=False)
712
+
695
713
  logger.info(
696
714
  "batch_complete",
697
715
  total_files=batch_result.total_files,
@@ -14,7 +14,7 @@ from __future__ import annotations
14
14
  import sys
15
15
  import time
16
16
  import uuid
17
- from datetime import datetime, timezone
17
+ from datetime import UTC, datetime
18
18
  from pathlib import Path
19
19
  from typing import Optional
20
20
 
@@ -188,7 +188,7 @@ def delete_mapping_command(
188
188
  for deleted in deleted_rows:
189
189
  operation = Operation(
190
190
  id=str(uuid.uuid4()),
191
- timestamp=datetime.now(timezone.utc),
191
+ timestamp=datetime.now(UTC),
192
192
  operation_type="ERASURE",
193
193
  files_processed=[],
194
194
  user_modifications={
@@ -20,7 +20,7 @@ from gdpr_pseudonymizer.data.database import open_database
20
20
  from gdpr_pseudonymizer.data.repositories.mapping_repository import (
21
21
  SQLiteMappingRepository,
22
22
  )
23
- from gdpr_pseudonymizer.exceptions import DuplicateEntityError
23
+ from gdpr_pseudonymizer.exceptions import DatabaseError, DuplicateEntityError
24
24
  from gdpr_pseudonymizer.utils.logger import configure_logging, get_logger
25
25
 
26
26
  # Configure logging
@@ -119,6 +119,7 @@ def import_mappings_command(
119
119
 
120
120
  # Import statistics
121
121
  imported = 0
122
+ replaced = 0
122
123
  skipped = 0
123
124
  errors = 0
124
125
 
@@ -183,6 +184,22 @@ def import_mappings_command(
183
184
  skipped += 1
184
185
  continue
185
186
 
187
+ # Story 11.2: a yes replaces the existing row's mapping
188
+ # in one transaction; the row keeps its id, its name as
189
+ # stored and its first-seen time
190
+ try:
191
+ target_repo.replace_mapping(existing.id, entity)
192
+ replaced += 1
193
+ except DatabaseError as e:
194
+ errors += 1
195
+ # The type and the error's kind, never the name
196
+ logger.error(
197
+ "import_entity_error",
198
+ entity_type=entity.entity_type,
199
+ error_type=type(e).__name__,
200
+ )
201
+ continue
202
+
186
203
  try:
187
204
  # Create new entity without ID (let DB generate new one)
188
205
  from gdpr_pseudonymizer.data.models import Entity as EntityModel
@@ -228,6 +245,8 @@ def import_mappings_command(
228
245
  # Display results
229
246
  console.print("\n[bold]Import Complete[/bold]\n")
230
247
  console.print(f" Imported: [green]{imported}[/green]")
248
+ if replaced:
249
+ console.print(f" Replaced: [green]{replaced}[/green]")
231
250
  console.print(f" Skipped: [yellow]{skipped}[/yellow]")
232
251
  if errors:
233
252
  console.print(f" Errors: [red]{errors}[/red]")
@@ -236,6 +255,7 @@ def import_mappings_command(
236
255
  "import_complete",
237
256
  source=str(source_db),
238
257
  imported=imported,
258
+ replaced=replaced,
239
259
  skipped=skipped,
240
260
  errors=errors,
241
261
  )
@@ -29,6 +29,7 @@ from gdpr_pseudonymizer.cli.validators import (
29
29
  validate_theme_or_exit,
30
30
  )
31
31
  from gdpr_pseudonymizer.core.document_processor import DocumentProcessor
32
+ from gdpr_pseudonymizer.core.saved_own_words import notice_line, own_word_rows_of
32
33
  from gdpr_pseudonymizer.exceptions import FileProcessingError
33
34
  from gdpr_pseudonymizer.utils.logger import configure_logging, get_logger
34
35
 
@@ -235,6 +236,12 @@ def process_command(
235
236
  f"[cyan]ℹ {result.entities_reused} entity mapping(s) reused from database (idempotent processing)[/cyan]\n"
236
237
  )
237
238
 
239
+ # Story 11.7: the database holds saved pseudonyms that hold a word
240
+ # of their own name (the rows this document saved included)
241
+ own_word_rows = own_word_rows_of(result)
242
+ if own_word_rows > 0:
243
+ console.print(notice_line(own_word_rows), markup=False)
244
+
238
245
  logger.info(
239
246
  "processing_complete",
240
247
  entities_detected=result.entities_detected,