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.
- gdpr_pseudonymizer-2.3.0/README.md → gdpr_pseudonymizer-2.3.1/PKG-INFO +83 -14
- gdpr_pseudonymizer-2.3.0/PKG-INFO → gdpr_pseudonymizer-2.3.1/README.md +33 -64
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/batch.py +18 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/delete_mapping.py +2 -2
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/import_mappings.py +21 -1
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/process.py +7 -0
- gdpr_pseudonymizer-2.3.1/gdpr_pseudonymizer/cli/commands/replace_pseudonym.py +300 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/validate_mappings.py +83 -11
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/main.py +51 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/custom_pseudonym.py +183 -29
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/document_processor.py +50 -14
- gdpr_pseudonymizer-2.3.1/gdpr_pseudonymizer/core/saved_own_words.py +549 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/database.py +3 -3
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/repositories/audit_repository.py +2 -2
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/repositories/mapping_repository.py +128 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/accessibility/focus_manager.py +16 -5
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/app.py +15 -2
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/en.qm +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/en.ts +108 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/fr.ts +104 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/main_window.py +29 -1
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/batch.py +16 -1
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/database.py +303 -8
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/processing.py +19 -1
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/validation.py +4 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/passphrase_dialog.py +33 -2
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/toast.py +2 -1
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/batch_worker.py +29 -1
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/database_worker.py +84 -5
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/detection_worker.py +29 -2
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/finalization_worker.py +2 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/processing_worker.py +3 -0
- gdpr_pseudonymizer-2.3.1/gdpr_pseudonymizer/locale/fr/LC_MESSAGES/cli.mo +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/locale/fr/LC_MESSAGES/cli.po +14 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/assignment_engine.py +12 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/library_manager.py +70 -13
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/pseudonyms/lotr.json +318 -469
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/pseudonyms/neutral.json +146 -182
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/pseudonyms/star_wars.json +358 -656
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/pyproject.toml +11 -11
- gdpr_pseudonymizer-2.3.0/gdpr_pseudonymizer/locale/fr/LC_MESSAGES/cli.mo +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/LICENSE +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/config_show.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/destroy_table.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/export.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/init.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/list_entities.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/list_mappings.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/stats.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/config.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/formatters.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/i18n.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/passphrase.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/progress.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/validation_stub.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/validators.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/legacy_names.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/mapping_key.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/core/naive_processor.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/encryption.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/models.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/naive_data.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/repositories/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/data/repositories/metadata_repository.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/exceptions.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/accessibility/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/accessibility/shortcuts.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/config.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/dialogs/shortcuts_help.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/error_handler.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/fr.qm +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/i18n/translator.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/models/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/models/validation_state.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/app.ico +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_16.png +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_256.png +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_32.png +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_48.png +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/icons/icon_512.png +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/themes/dark.qss +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/themes/high-contrast.qss +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/resources/themes/light.qss +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/home.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/results.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/settings.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/screens/stub.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/confirm_dialog.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/drop_zone.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/entity_editor.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/entity_panel.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/widgets/step_indicator.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/model_manager.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/gui/workers/signals.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/locale/en/LC_MESSAGES/cli.mo +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/locale/en/LC_MESSAGES/cli.po +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/entity_detector.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/entity_grouping.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/geography_dictionary.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/hybrid_detector.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/model_names.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/name_dictionary.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/regex_matcher.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/spacy_detector.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/nlp/stanza_detector.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/gender_detector.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/pseudonym/neutral_id_generator.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/detection_patterns.yaml +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/french_gender_lookup.json +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/french_geography.json +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/french_names.json +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/location_noise_filter.yaml +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/org_role_filter.yaml +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/person_boundaries.yaml +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/place_abbreviations.yaml +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/resources/salutations.yaml +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/config_manager.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/file_handler.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/french_patterns.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/logger.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/tabular_reader.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/tabular_writer.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/utils/text_form.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/__init__.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/actions.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/context_precomputer.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/models.py +0 -0
- {gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/validation/ui.py +0 -0
- {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.
|
|
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.
|
|
51
|
-
| **macOS (Apple Silicon)** | `gdpr-pseudonymizer-2.
|
|
52
|
-
| **Linux** | `gdpr-pseudonymizer-2.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
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.
|
|
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.
|
|
100
|
-
| **macOS (Apple Silicon)** | `gdpr-pseudonymizer-2.
|
|
101
|
-
| **Linux** | `gdpr-pseudonymizer-2.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
815
|
-
|
|
784
|
+
**Last Updated:** 2026-11-03 (v2.3.0 — detection precision, corrections at every place, complete erasure)
|
{gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/batch.py
RENAMED
|
@@ -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
|
|
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(
|
|
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
|
)
|
{gdpr_pseudonymizer-2.3.0 → gdpr_pseudonymizer-2.3.1}/gdpr_pseudonymizer/cli/commands/process.py
RENAMED
|
@@ -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,
|