Commit Graph

40 Commits

Author SHA1 Message Date
60fb41c2e7 fix(gui): clarifier aide et disponibilite moteurs
Passe theme clair, libelles utilisateur, aides conteneurs, recherche de mise a jour et indication honnete des moteurs optionnels non embarques. Tests GUI unitaires: 126 passed.
2026-06-17 18:01:25 +02:00
d18ca919fa fix(core): renforcer detection PII et FINESS Corse
Couvre les corrections PII batch A/A-2, le NIR multi-ligne en flux reel, le gazetteer FINESS Corse derive depuis la base locale, et les tests de regression associes. Aucun build ni diffusion.
2026-06-17 17:59:27 +02:00
536ab81184 feat(gui): garde-fou runtime — désactive un moteur optionnel non embarqué
Condition du GO-CONDITIONNEL Qwen sur le lot engine capabilities
(cb3b767/890edb3/5e5f0bd) : un profil YAML forçant enable_eds/enable_gliner
ne doit pas déclencher un chargement voué à l'échec silencieux.

NerManagers.ensure_loaded() applique désormais un garde-fou via la sonde
engine_capabilities.capabilities_map() (injectable) AVANT toute tentative
de load EDS/GLiNER : si le moteur optionnel demandé est indisponible dans
le build courant → warning + désactivation forcée dans les réglages runtime.
Best-effort (sonde en échec ⇒ réglages inchangés, les try/except de load
protègent déjà). Sonde légère (find_spec), aucun import lourd.

CamemBERT (requis) inchangé. Diff limité au garde-fou + tests cibles.

TDD : 4 tests (test_gui_v6_engine_bridge.py) — eds/gliner indispo désactivés
et jamais chargés, moteur dispo conservé, fail-safe sonde. 282 unit passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:56:47 +02:00
5e5f0bd341 feat(gui): n'afficher comme disponibles que les moteurs embarqués dans le build GUI
Axe application GUI (utilisateur final) : cohérence UI/moteurs propre au build
GUI, sans présumer du build CLI. EDS-Pseudo / GLiNER désactivés (switch disabled
+ « non embarqué dans cette version ») et `enable_eds/gliner` forcés à False quand
indisponibles ; CamemBERT-bio reste le moteur standard actif. Note Moteurs des
Profils rendue honnête. `_mini_toggle` gère `disabled`/`disabled_hint` + `.switch`.

2 tests GUI (toggles désactivés si indispo + état forcé False ; actifs si dispo).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 17:38:56 +02:00
890edb360e feat(cli): option --engines, diagnostic honnête des moteurs du build CLI
Axe CLI (intégration dans d'autres programmes) : contrat stable, codes retour
fiables. `--engines` liste les moteurs réellement disponibles dans CET
exécutable CLI (`[OUI]/[NON] Label (requis/optionnel) — raison`) et sort 0, sans
traiter. `input` devient optionnel pour ce mode (sinon code 2).

Le fail-closed CamemBERT (code 3) et le best-effort EDS/GLiNER (jamais déclarés
actifs si le chargement échoue) restent inchangés. Ne présume pas du build GUI.

2 tests (--engines → code 0 + moteurs listés ; absence d'input → code 2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 17:38:56 +02:00
cb3b7675bb feat(engines): fondation 'capabilities moteurs' testable et partagée
Utilitaire neutre (ni CLI ni GUI) qui dit la vérité sur les moteurs réellement
disponibles dans le build COURANT (la sonde reflète l'exécutable qui tourne, sans
présumer d'un autre build). Consommé séparément par chaque axe produit.

- `EngineCapability(key, label, available, required, reason)`.
- Sondes légères `importlib.util.find_spec` (pas d'import lourd au démarrage) +
  présence du modèle ONNX pour CamemBERT (gère _MEIPASS en frozen).
- camembert=requis ; eds (edsnlp+spacy) / gliner=optionnels. Sondes injectables,
  fail-closed. `capabilities_map()` / `available_engines()`.

6 tests (sondes injectables dispo/indispo, required, reasons, sondes réelles).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 17:38:56 +02:00
764cf00581 refactor(gui): intégrer les Règles dans Administration > Profils
Retour Dom : « les règles du profil doivent être dans le menu profil, pas à
part ! ». Même logique que le Masquage — les règles qui influencent
l'anonymisation appartiennent au profil ; un sous-onglet séparé crée la
même confusion.

- Retrait du sous-onglet « Administration > Règles » (_SUBTABS, builder,
  méthode _build_regles supprimée). Sous-onglets restants : Réglages /
  Profils / Partage.
- Section « Profils > Règles du profil » enrichie : wording clair (règles
  d'anonymisation portées par le profil), aperçu illustratif de la table
  des règles (réutilise _rule_row + _HELP_REGLES), édition fine annoncée
  « à venir ».
- Abandon du « Testeur de règle » (écran outil global) pour ne pas
  réintroduire un second réglage métier.

Cible UX : Réglages / Profils (Général・Masquage・Mots・Moteurs・Règles du
profil) / Partage. Test obsolète test_rules_subtab_has_no_unexplained_2
remplacé par test_no_separate_rules_subtab.

262 tests unit OK (0 régression), self-test OK, nav 3 sous-onglets + section
Règles dans Profils + thème OK. Préserve d8bc0cd + GO Qwen. Aucun build/push
sans GO Dom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 12:00:10 +02:00
d8bc0cd8c8 refactor(gui): intégrer le Masquage dans Administration > Profils
Retour Dom : le sous-onglet Masquage séparé créait de la confusion. Le
masquage fait partie de la manière d'anonymiser associée au profil.

- Retrait du sous-onglet « Administration > Masquage » (_SUBTABS, builder,
  méthode _build_masquage).
- Section « Profils > Masquage » enrichie : masque manuel requis, template
  de masque (lié au profil édité), bouton « Ouvrir l'éditeur de masque »
  (fenêtre dédiée) + dossier des templates, et apparence du masque
  (couleur, style des marqueurs + aperçu, marges H/V, coins arrondis).
- Le template enregistré depuis l'éditeur remplit désormais le champ
  Template du profil (preferred_manual_mask_template via _pro_template_var).
- Profils devient le centre des réglages métier (général/masquage/mots/
  moteurs/règles). Réglages inchangé (pas de pastilles, pas de grosse
  refonte). Nettoyage du code mort (_REPLACEMENT_CODES, _HELP_MASQUAGE).

261 tests unit OK (0 régression), self-test OK, nav 4 sous-onglets + éditeur
de masque depuis Profils + thème OK. Préserve 72841ed/GO Qwen. Aucun build/
push sans GO Dom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 10:24:49 +02:00
72841ed7b3 feat(gui): onglet Profils éditable (création/modification/persistance)
Retour Dom : remplacer la page vitrine par un vrai éditeur de profils.

- gui_v6/profile_editor.py : couche logique (build_profile_spec,
  profile_is_editable runtime vs defaut, list_profile_choices, slug_for_copy,
  save/set_default/delete) au-dessus de profile_defaults — persistance dans
  config/profiles.yml.
- gui_v6/editable_list.py : EditableTermList (tableau scrollable de termes,
  ajout/suppression, pas de pastilles) — reste lisible à 50+ termes.
- tab_config : sous-onglet « 👤 Profils » réintroduit comme éditeur — menu
  déroulant « Profil à modifier », boutons Nouveau / Dupliquer / Enregistrer /
  Annuler / Définir par défaut, sections Identité, Masquage (require_manual_mask,
  template), Moteurs (force_disable_vlm), Mots (à masquer/conserver/ignorer
  éditables), Règles « à venir ». Profils défaut = lecture seule (dupliquer
  pour modifier). Confirmation non bloquante (pas de modale).
- Réglages : bouton « ✏️ Modifier le profil… » → ouvre Profils sur le profil
  actif. Pas de pastilles inline.

Persiste : label, description, require_manual_mask, force_disable_vlm,
preferred_manual_mask_template, param_lists (3 listes). 260 tests unit OK
(0 régression), self-test OK, nav 5 sous-onglets + thème OK. Préserve
1bbe70a/d30f7b7. Aucun build/push sans GO Dom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:09:01 +02:00
1bbe70a911 feat(gui): câbler l'envoi de la télémétrie d'usage en fin de run
Le module usage_telemetry est maintenant réellement branché : la GUI V6
envoie les statistiques au portail après chaque run (les stats web
restaient vides sans cela).

- processing_runner : RunSummary porte une liste DocResult (ordinal,
  page_count via page_count_for, status, duration_ms, extension) — peuplée
  dans la boucle. Aucun nom/chemin de fichier.
- usage_telemetry : report_run_summary(summary, base_url, license_ref,
  machine_id, session, ...) construit le payload depuis le RunSummary et
  l'envoie (non bloquant). N'envoie RIEN sans license_ref. Spool JSONL si
  échec réseau.
- tab_usage : _finish() déclenche l'envoi en thread daemon (jamais bloquant
  pour l'UI ni le run).
- app : fournit le reporter à UsageTab avec le contexte licence (base_url du
  LicenseClient, license_ref via local_status, machine_id, app_version).

Tests : RunSummary.documents peuplé (0 chemin) ; report_run_summary (payload
correct, réseau KO → spool sans crash, pas d'envoi sans licence) ; _finish
appelle le reporter. 252 tests unit OK (0 régression), self-test OK.
V5/moteur/app_aivanov intacts, 0 dépendance. Aucun build/push sans GO Dom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 21:24:43 +02:00
d30f7b74ef refactor(gui): Réglages — tableau des termes en accès direct, retrait du doublon Profils
Retour Dom après validation visuelle : simplifier.

- Réglages > Listes locales : suppression des pastilles de termes et des
  éditeurs inline (_compact_tag_editor). Remplacés par un texte court +
  compteurs (À conserver/À masquer/À ignorer du profil actif) + bouton
  « Ouvrir le tableau des termes » qui ouvre DIRECTEMENT TermsTableWindow.
- Retrait du bouton « Voir le profil » (son rôle = accéder au tableau).
- Retrait du sous-onglet « Profils » (doublon non câblé) : _SUBTABS,
  builders, _build_profils/_rebuild_profils. Les helpers profil
  (_active_profile_summary/_open_terms_table) sont conservés pour Réglages.
- Nettoyage du code mort associé : _compact_tag_editor, constantes
  _PRESERVE_TERMS/_MASK_TERMS/_STOPWORDS, textes d'aide qui référençaient
  l'onglet Profils.

Chemin utilisateur : Administration > Réglages > Ouvrir le tableau des
termes. 247 tests unit OK (0 régression), self-test OK. Préserve a9e8b2c
(thème, bêta, aide ?, fenêtre tableau). Aucun build/push sans GO Dom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 17:45:43 +02:00
ab2ca8a552 feat(gui): module télémétrie d'usage (client, RGPD-safe, non bloquant)
Phase A de la mission télémétrie d'usage par client.

- gui_v6/usage_telemetry.py :
  - page_count_for(path) : PDF→fitz, image→1, autres→None ; best-effort, ne
    lève jamais, ne lit que l'extension (jamais le nom).
  - build_usage_payload(...) : compteurs (document/succeeded/failed/total_pages)
    + documents filtrés aux seules clés autorisées (ordinal/page_count/status/
    duration_ms/extension) → aucun nom/chemin de fichier ne peut fuir.
  - UsageTelemetryClient(session injectée) : report() non bloquant (capture
    tout, False en cas d'échec réseau) vers POST /api/v1/usage/report.
  - spool JSONL local (spool_payload/flush_spool) pour rejouer les échecs.

Module isolé, non câblé au runner pour l'instant (le branchement fin-de-run
viendra après le backend, hors validation visuelle GUI en cours). Aucun
build/push sans GO Dom. 10 tests unitaires (payload sans nom de fichier,
réseau indispo ne crashe pas, compteurs, page_count PDF mockable).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 17:13:57 +02:00
a9e8b2c2e6 feat(gui): addenda Dom GUI V6 — sous-onglet Profils, libellés, aide, bêta
Suite des retours Dom sur la GUI V6 (par-dessus 6a0a581).

Addendum Profils / Réglages :
- Nouveau sous-onglet Administration « 👤 Profils » : le profil actif devient
  un objet lisible (nom, description, masque requis, template, listes locales
  avec compteurs) — données réelles lues depuis profile_defaults.
- Fenêtre « Tableau des termes » (terms_table_window.py) : table scrollable
  avec recherche/filtre, colonnes Type/Terme/Source ; reste lisible à 50+
  termes. Ajouter/éditer/supprimer désactivés « (à venir) » (écriture par
  profil non câblée).
- Réglages : « Profil métier » → « Profil d'anonymisation », « Sortie… » →
  « Dossier de sortie… » (+ infobulle), hints moteurs (standard/optionnel/
  plus lent), bouton « Voir le profil », « Ouvrir le tableau des termes ».
- Aide « ? » + infobulles (ui_kit.attach_tooltip) près des éléments ambigus.
- profile_view.py : logique pure (résumé profil + lignes du tableau),
  testable sans display.

Addendum bêta : en-tête « aivanonym » + badge « bêta », titre fenêtre
« … — bêta ». Détail version conservé dans À propos.

tests/unit/test_gui_v6_profiles.py + ajouts shell. 237 tests unit OK
(228 → 237, 0 régression), self-test GUI V6 OK, navigation des 5 sous-onglets
+ thème OK. V5/moteur/app_aivanov/profile_defaults non touchés, 0 dépendance.
Aucun build/push sans GO Dom — validation visuelle Dom attendue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 17:02:54 +02:00
6a0a5811a5 fix(gui): retours Dom GUI V6 — thème, Administration, Règles, aide
Cinq retours utilisateur sur l'exécutable Windows GUI V6.

- Thème : `_render()` vidait les widgets mais conservait le cache
  `_tab_frames`/`_visible_tab` → l'onglet Utilisation se vidait (TclError
  sur widget détruit) au changement de thème. Reset du cache dans
  `_render()` → onglet actif recréé proprement.
- Onglet principal « Configuration » → « Administration » (clé interne
  inchangée).
- Sous-onglet « Règles  2 » → « Règles » (le « 2 » était un badge non
  câblé).
- Actions de maquette non câblées (Partage Export/Import, Règles Nouvelle
  règle/Recharger/Tester/Fermer) désactivées + suffixe « (à venir) » via
  `_mockup_button` : plus aucune action morte qui semble fonctionner.
- Aide « ? » restaurée (façon V5) : `ui_kit.HelpButton`/`help_button`
  réutilisable ouvrant une fenêtre d'aide en français simple, posée sur
  Utilisation, Administration (Réglages/Masquage/Partage/Règles) et
  À propos. Partage : phrase visible + aide expliquant qu'on partage les
  réglages, jamais les documents patients.

`tests/unit/test_gui_v6_app_shell.py` : régression thème, libellés,
présence d'aide, navigation. 228 tests unit OK (0 régression), self-test
GUI V6 OK. V5/moteur/app_aivanov non touchés, aucune dépendance ajoutée.
Verdict Qwen requis avant push/build/diffusion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:39:53 +02:00
13b79db417 feat(gui): éditeur de masques en fenêtre dédiée (GUI V6)
Remplace l'éditeur de masquage encastré dans l'onglet Configuration —
jugé inutilisable par Dom (document trop à l'étroit, non défilable) —
par une fenêtre dédiée où le document est majoritaire et réellement
navigable.

- gui_v6/mask_editor_model.py : couche logique pure (rectangles par
  page, conversions écran↔PDF, hit-test, sérialisation template)
  testable sans display ; réutilise MaskRect/Template de
  pdf_mask_designer → format de template inchangé (compat moteur).
- gui_v6/mask_editor_window.py : MaskEditorWindow (CTkToplevel)
  redimensionnable — canvas + scrollbars H+V câblées + molette (le
  manque qui rendait l'éditeur inutilisable), zoom + ajuster
  largeur/page, navigation pages, rectangles au glisser-déposer,
  sélection (clic) + suppression (Suppr / clic-droit), templates
  JSON/YAML, mode aperçu d'exemple sans PDF.
- tab_config.py : l'onglet Masquage lance la fenêtre dédiée ; retrait
  du canvas encastré et de ~290 lignes de code mort associé.
- tests/unit/test_gui_v6_mask_editor.py : 13 tests logique + 3 smoke
  headless (scrollbars, ajout/sélection/suppression, save/load
  roundtrip, câblage onglet→fenêtre).

Sans nouvelle dépendance. V5, moteur et app_aivanov non touchés.
221 tests unit OK (0 régression), self-test GUI V6 OK.
Verdict Qwen requis avant push/build/diffusion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:05:57 +02:00
696f6bf27c fix(gui): make admin config responsive and mask editor usable 2026-06-15 09:53:56 +02:00
269b9e0e13 fix(gui): complete V6 admin configuration mockup 2026-06-15 09:19:43 +02:00
fff4a2d902 fix(cli): avoid duplicate ONNX native load in Windows frozen 2026-06-12 16:49:11 +02:00
1bced55b81 feat(gui): GUI V6 G4 — alignement visuel sur la maquette v6 (option A)
Refonte de la couche présentation pour reprendre docs/ui_mockup_v6.html, sans
changer de techno UI ni la logique G1-G3.

- theme.py : 4 thèmes aux tokens EXACTS de la maquette (sombre #1a1a2e/#16213e/
  #e94560, clair, médical, neutre), palette complète + status_color.
- ui_kit.py (nouveau) : composants stylés (Card titrée, boutons primary/secondary/
  success/pilule, StatCard, ToggleRow) appliquant la palette.
- app.py : shell étroit, header identité + version + statut licence + liseré accent,
  barre d'onglets custom (plus de CTkTabview brut), navigation par recréation,
  changement de thème à chaud.
- tab_usage : carte Apparence (sélecteur de thème), dropzone stylée, grille formats,
  barre d'actions, progression à étapes + journal, résultats en cartes statistiques.
- tab_config : sous-navigation Réglages/Masquage/Partage/Règles ; Réglages câblé au
  ConfigState (profil, moteurs NER, dossier sortie).
- tab_about : grille d'informations + bloc licence (logique inchangée).

Logique inchangée : engine_bridge, config_state, license_client/store, runner.
Tests : +9 (theme). self-test exit 0, 55 tests gui_v6, 202 tests/unit (0 régression).
Smoke construction headless (Xvfb) : 3 onglets × 4 thèmes rendus sans erreur.
Pas de pywebview, aucun .exe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:06:05 +02:00
9575714ae2 feat(gui): GUI V6 G3 — câblage moteur, Configuration, licence UI, build-prep
G3-A câblage moteur réel (engine_bridge.py) : EngineSettings + NerManagers à
chargement paresseux (aucun manager à l'import), kwargs alignés CLI/V5
(make_vector_redaction=False, also_make_raster_burn=True, config_path, use_hf,
ner/gliner/camembert_manager, ogc_label) ; make_process_fn engine injectable ;
état managers not_loaded/loading/ready/unavailable, échecs optionnels tolérés.

G3-B Configuration (config_state.py + tabs/tab_config.py) : ConfigState →
EngineSettings, profils via profile_defaults (path injectable), options
raster/NER local/profil/sortie, état managers, sections admin-only via admin_mode.

G3-C Licence UI (machine_id.py + tab_about) : activation par clef
(LicenseClient.activate), bouton vérifier (check), affichage statut, aucun token
loggé, aucun appel réseau au démarrage (local_status seul).

Intégration : tab_usage exécute via le moteur réel selon ConfigState
(make_process_fn), anti double-lancement UI. app.py câble Config↔Usage↔licence.

G3-D build-prep : anonymisation_gui_v6_onefile.spec (entry V6, customtkinter +
modules gui_v6 en hiddenimports). Installateur Anonymisation.iss produit déjà la
cible Anonymisation-Setup.exe. Aucun artefact .exe commité ; build Windows à part.

Tests +14 (engine_bridge 8, config_state 6). self-test exit 0, 46 tests gui_v6,
193 tests/unit (0 régression). Moteur/V5/specs CLI intacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:53:47 +02:00
9bc6537233 feat(gui): add GUI V6 G2 — onglet Utilisation + runner injectable
Onglet Utilisation fonctionnel (couche présentation only) :
- processing_runner: runner testable sans display/moteur lourd, process_fn
  injectable (défaut = process_document en import paresseux), découverte
  fichier/dossier, sorties anonymise/ comme V5 (arbo préservée), progression,
  journal, résumé OK/KO, arrêt coopératif entre documents, anti double-lancement
- tabs/tab_usage: sélection fichier/dossier + nb PDF détectés, dossier sortie
  (défaut anonymise/), Lancer/Arrêter, barre de progression, statut, journal,
  résumé ; worker threadé, file d'événements drainée par after() ; aucun réseau
- app.py: onglet Utilisation câblé (placeholder G2 retiré)
- self-test: couvre processing_runner + tab_usage

Tests: +11 (runner) — discovery, sorties, échec partiel, arrêt, anti-double-run,
callbacks. self-test exit 0, 32 tests gui_v6, 179 tests/unit (0 régression).
Moteur/V5/managers/specs intacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 18:58:10 +02:00
a6ee68a8a3 feat(gui): add GUI V6 G1 foundation (license client/store, shell, About tab)
Socle de la refonte GUI V6 (couche présentation uniquement, aucune logique de
détection) :
- license_store: stockage licence hors dépôt (%LOCALAPPDATA%/Aivanov | XDG),
  read/write atomique/delete, ne journalise aucun token
- license_client: LicenseStatus + activate/check/local_status, session HTTP
  injectable, serveur indisponible géré sans crash, aucune clé privée
- theme: 4 thèmes + couleurs de statut licence
- app + tab_about: shell customtkinter minimal (header, bandeau licence,
  3 onglets), onglet À propos étoffé
- Pseudonymisation_Gui_V6.py: point d'entrée + --self-test (exit 0 sans fenêtre)
- requirements.txt: customtkinter==5.2.2

Tests: 20 nouveaux (store sur vrais fichiers, client sur session injectée).
Suite tests/unit: 167 passed, 0 régression. V5/moteur/managers/specs intacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 18:50:23 +02:00
0e44cd4543 feat(anonymizer): add v11.5 P0 layout-aware detectors
Trois détecteurs simples « layout/context-aware » (chantier v11.5 P0),
validés par 2 revues Codex + 10 tests adversariaux Qwen, 0 régression :

- RE_ADRESSE réécrit en grammaire de tokens (_RE_VOIE_TYPE + _RE_VOIE_TOKEN) :
  capture initiales (« J. Loeb »), voies commémoratives à chiffres
  (« 8 Mai 1945 »), apostrophes ' et ’, bornage à la ligne courante,
  arrêt sur point post-mot (anti-débordement clinique).
- _mask_ville_gazetteers : retourne toujours un tuple (texte, liste) même
  sans Aho-Corasick ; masque les communes Saint/St/Sainte/Ste multi-mots à
  espaces (« St Martin de Hinx ») entièrement, sans exiger de contexte géo.
- DATE_NAISSANCE retiré de la propagation globale + DATE_NAISSANCE_GLOBAL
  ajouté aux skip vector/raster : on ne masque plus une date nue sur tout le
  document. La DDN reste masquée en contexte fort, page par page. Les dates
  cliniques identiques à la DDN hors contexte sont préservées.

tests/unit/test_p0_layout_detectors.py : 38 tests dédiés (matrice adresse
générique, anti-FP, communes Saint, propagation DDN, 10 tests adversariaux
Qwen). Suite tests/unit complète : 147 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 10:28:18 +02:00
c582c13a08 fix(anonymizer): cover CHCB real-world staff layouts 2026-06-08 12:44:09 +02:00
94f7903af3 fix(anonymizer): handle FC14 practitioner OGC rules 2026-06-08 12:03:51 +02:00
21a408a9e4 fix(perf): apply MVP threading hotfix
Configure numerical library and torch threading for H1, keep raster threading/timing instrumentation, remove CONCERTATION from forced masks after real PDF FP testing, and record coordination archive state.
2026-06-08 10:41:15 +02:00
f2375d6be2 test: non-régression F5 + batch paths + masquage manuel + layouts réels
- test_f5_nom_compose_orphelin.py : 13 tests (regex F5, application, scénario Trackare EJNAINI)
- test_gui_batch_paths.py / test_manual_masking.py : couverture des modules
- test_real_world_identifier_layouts.py : non-régression layouts réels (D-15)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 16:30:56 +02:00
65d6c8c603 test(T-G): réparer corpus synthétique post-cleanup CHCB + dégel 009
- Fixtures 001/003/004/005/010 : CHCB → CHUXX (D-12)
- 009 : Biarritz désormais masqué [VILLE] (bug connu résolu par F1-F4),
  retrait de KNOWN_FAILURES + restauration de Biarritz dans must_not_contain
- test_q1_quarantine.py : tests réels B-3/D2/D3/M5/INDEX/errors.log
  (ex-squelette xfail)

Suite tests/unit : 85 passed, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:31:38 +02:00
92557d4e74 chore(rgpd): replace CHCB/Bayonne/Saint-Denis/Réunion refs in source + configs (D-12)
Anonymise toutes les références à des entités réelles (CHCB, Bayonne, Saint-Denis,
Réunion, etc.) dans le code source, les configurations YAML, les scripts/outils,
et les tests unitaires. Conserve les tests synthétiques (cases) intentionnels.

- profile key chcb_strict → chuxx_strict
- CHCB → CHUXX, Bayonne → Chicago, Saint-Denis → Springfield,
  Réunion → Province Bêta, 64100/97400 → 12345, FINESS → 999999999,
  préfixe tél 05.59.44 → 0X.XX.XX
- renomme tools/test_chcb_leak.py → tools/test_force_term_leak.py

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 14:39:21 +02:00
3c9d68b49e feat(gui): apply WIP profils+masques+build-windows from stash (2026-04-27)
Application du stash@{0} resté en WIP depuis le 27/04 :
  "On main: wip-gui-profils-masque-manuel-build-windows-2026-04-27"

## Apport

- Pseudonymisation_Gui_V5.py (+1208 lignes) : profils, panneau paramètres
  avancés, éditeur de masques intégré, gestion whitelist/blacklist
- launcher.py (+315) : splash natif PyInstaller, single-instance,
  téléchargement modèles
- anonymisation_onefile.spec : config PyInstaller mise à jour
- pdf_mask_designer.py (+114) : éditeur de masques amélioré
- config_defaults.py (+23) : constantes nouvelles
- tests/unit/test_config_externalization.py (+12) : tests config
- .gitignore (+5)

## Pourquoi

La version courante de la GUI sur la branche feature manquait :
- L'éditeur de masques
- Les profils
- Le panneau paramètres avancés
- Le splash natif au démarrage

Aucun conflit avec mes 10 commits Q-1 (pas de chevauchement de fichiers).

## Validation

75 passed, 10 xfailed sur pytest tests/unit/.

## Note

Le stash reste disponible dans `git stash list` jusqu'à drop explicite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 11:09:46 +02:00
73fa9aab08 test(q1): add test_q1_quarantine.py — 11 tests (1 actif, 10 xfail strict)
Squelette de tests TDD pour Q-1 quarantaine différentielle.

État au commit :
- test_happy_path_no_quarantine_created_if_no_failure  actif (passe)
- 10 tests en xfail strict, à dégeler au fur et à mesure :
  * B-3 préflight (2 tests)
  * Q-1 quarantine flow (3 tests)
  * B-1 metadata (2 tests)
  * B-2 logs (2 tests)
  * INDEX.md (1 test)

Validation : 74 passed, 10 xfailed sur tests/unit/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 10:45:00 +02:00
f85659d103 fix(detect): établissements multi-ligne, CHCB en fin de phrase, ville après [ETAB] (#3 #4 #5)
Trois fixes qui font passer 009_multi_etablissements en vert et
ferment la liste des fuites identifiées par la couche 2.

#3 — `Centre Hospitalier Universitaire de Bordeaux` coupé sur deux lignes
Nouveau pattern `RE_ETAB_LINEBREAK` (strict) en pré-passe sur la page
entière, juste avant le découpage en lignes. Match `<TYPE>\n<suite>`
avec :
- TYPE limité (Centre Hospitalier, Hôpital, Clinique, Polyclinique,
  CHU, CHRU, CHS) ;
- un seul `\n` autorisé entre TYPE et suite ;
- la suite démarre obligatoirement par un connecteur typique
  (Universitaire, de, d', du, des, la, le, les) puis UN nom propre.
Évite le FP `CENTRE HOSPITALIER COTE BASQUE\nService d'anesthésie`
(le `\n` n'est pas immédiat après le type, donc pas de match).

#4 — `CHCB` en fin de phrase suivi de ` ;`
`_kv_value_only_mask` splittait `transféré au CHCB pour la rééducation ;`
sur le `;` du `SPLITTER` (`\s*[:|;\t]\s*`), produisant une value vide.
La key contenait CHCB mais n'était passée qu'à `_mask_critical_in_key`
qui ne couvre pas les force_terms admin_rules.
Fix : fallback sur `_mask_line_by_regex(line)` (qui appelle
`_apply_overrides` → force_terms) si la value est vide ou la key
dépasse 5 mots (heuristique narrative).

#5 — `Biarritz` non masqué après `[ETABLISSEMENT] à Biarritz`
`_mask_ville_gazetteers` skippait par sécurité toute ville détectée
juste après un placeholder établissement précédé de `de/du/d'/à`. Le
`à` était inclus pour éviter les FP, mais c'est la préposition de
LOCALISATION par excellence : `Clinique Aguilera à Biarritz` perd
Biarritz à tort. Restreint le skip à `de/du/d'` (qui sont des parties
de nom d'établissement type `CHU de Bordeaux`). `à` reste actif.

Couche 2 entièrement verte : 73 passed, 0 xfailed (avant : 72 + 1
xfailed). KNOWN_FAILURES vidé. La gate pytest est désormais le
contrat de non-régression sur 10 documents complets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:32:45 +02:00
ffb8006e91 fix(detect): RPPS avec qualificateur (RPPS prescripteur :, RPPS de garde :…) (#1)
Étend `RE_RPPS` pour tolérer 0 à 3 mots qualificateurs entre `RPPS`
et le séparateur `:` ou `-`. Couvre les variantes observées :
- RPPS prescripteur :
- RPPS du médecin signataire :
- RPPS de garde -
- N° RPPS :

Si un qualificateur est présent, le séparateur (`:` ou `-`) devient
obligatoire pour éviter d'aspirer du narratif (faux positif type
"Le RPPS est consulté pour vérifier 12345678901 dans la base").

La lambda `_repl_rpps` reconstruit `RPPS : [RPPS]` en sortie : le
qualificateur est consommé mais perdu (pas de fuite, choix cosmétique).

Cas 005_bacterio_complete passe désormais (retiré de KNOWN_FAILURES).
La fuite `10101010101` derrière `RPPS prescripteur :` est masquée.

Cohérent avec le cadrage section 10.1 (règle cœur générique
applicable à tout établissement de santé français — pas de
spécificité locale).

Tests : 72 passed, 1 xfailed (avant : 71 passed, 2 xfailed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:33:01 +02:00
9b431494a5 fix(detect): labels structurels Nom de jeune fille / Prénom / Ville (#7 #8 #9)
Trois nouveaux patterns cœur dans `_mask_structured_line` pour des
labels génériques qui n'étaient pas couverts par le pipeline kv_value
(le split key:value laissait fuir la valeur quand le label dépassait
les patterns existants `RE_EXTRACT_NOM_NAISSANCE`, `RE_EXTRACT_PRENOM`,
`RE_EXTRACT_VILLE_RESIDENCE`).

`RE_LABEL_NOM_VARIANTES` capture :
- Nom de jeune fille / de famille / de naissance(.)
- Nom d'usage / Nom marital / Nom marié

`RE_LABEL_PRENOM` capture :
- Prénom : / Prénoms : / Prénom de naissance / utilisé(e) / usuel
- Capture jusqu'à fin de ligne pour les énumérations virgulées
  (Prénoms : Sabine, Marie → tout masqué).

`RE_LABEL_VILLE` capture :
- Ville : / Ville de résidence : / Ville de naissance :
- Capture jusqu'à fin de ligne (gère "Saint-Jean-de-Luz",
  "Saint-Denis (974)", composés multi-tokens).

Effets de bord positifs :
- Le bug "Saint-Jean-de-Luz → [ETABLISSEMENT]-de-Luz" est corrigé :
  le matcher `RE_LABEL_VILLE` masque toute la valeur en `[VILLE]`
  AVANT que le gazetteer FINESS Aho-Corasick ne grignote "Saint-Jean".
  Cas 006_trackare_soignants et 008_anesthesie_complete : alignement
  des expected.txt sur cette amélioration.

Choix d'architecture (cf cadrage docs/cadrage-projet-anonymisation.md
section 10.1) : ces labels sont des règles cœur génériques applicables
à tout établissement de santé français. Légitimes en hardcodé. Les
patterns layout-specific (Bordeaux suffixe, CHCB en fin de phrase,
email cassé par force_term) seront branchés via admin_rules dans
l'étape suivante.

Cas 010_fiche_admission_minimale passe désormais (retiré de
KNOWN_FAILURES). Le xfail strict aurait signalé xpass.

Tests : 9 passed, 2 xfailed (avant : 8 passed, 3 xfailed sur
test_synthetic_review).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:30:40 +02:00
93338b6b72 test(review): étendre couche 2 à 10 cas et brancher gate pytest avec xfail strict
Couche 2 (revue humaine sur documents complets) : ajout de 6 cas
synthétiques pour atteindre la cible cadrage produit (10 cas).

Cas ajoutés :
- 005_bacterio_complete : layout BACTERIO N° venue rejeté avant IPP
  + RPPS prescripteur (pattern qualifié non détecté).
- 006_trackare_soignants : export Trackare avec activités HH:MM NOM,
  Note IDE/médicale, Signé — médicament greedy.
- 007_lettre_sortie_complete : courrier médecin→médecin, multi-villes,
  email institutionnel @chcb.fr (cassé par le force_term CHCB).
- 008_anesthesie_complete : protocole anesthésique avec molécules
  BDPM, prénoms basques rares (Maddi, Pantxoa).
- 009_multi_etablissements : 3 établissements distincts (CHCB, CHU
  Bordeaux, Clinique Aguilera), prénoms basques avec ñ (Beñat).
- 010_fiche_admission_minimale : fiche administrative dense, labels
  variés (Nom de jeune fille :, Prénom :, Ville :, Mutuelle :).

Gate pytest (tests/unit/test_synthetic_review.py) :
- vérifie l'inventaire (10 cas) et fait passer chaque cas via run_case.
- 3 cas marqués xfail(strict=True) pour révéler 9 fuites de PII et
  2 patterns partiels que le moteur ne couvre pas aujourd'hui :
  * 005 — RPPS avec qualificateur (RPPS prescripteur :)
  * 009 — Bordeaux résiduel après [ETAB], CHCB en fin de phrase,
          Biarritz sur ligne Ville :, ñ qui casse Beñat → [NOM]ñat
  * 010 — Nom de jeune fille / Prénom / Ville sans label "Patient :",
          NIR au format espacé partiellement consommé en TEL,
          numéro de mutuelle MGEN non couvert
- xfail strict force pytest à signaler un xpass quand un fix passe :
  rappel automatique de retirer l'entrée de KNOWN_FAILURES.

Le runner tools/run_synthetic_review_corpus.py reste utilisable en
direct (sortie diff/audit/summary) pour la revue humaine. Les sorties
actual/ sont gitignorées (régénérées à chaque exécution).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:46:22 +02:00
bc24a21fea Wire admin rules into ONNX anonymizer 2026-04-21 12:10:17 +02:00
e9dccdfad6 Add human review protocol and admin rules contract 2026-04-21 10:59:02 +02:00
34dcf8f360 Externalize dictionaries and add anonymization review corpus 2026-04-21 10:32:57 +02:00
b6ddce3af1 demo: Ajout script de démonstration et correction tests
- Script demo_evaluation.py montrant tous les outils
- Correction test flottant dans test_quality_evaluator.py
- Installation pytest/pytest-cov
- Tous les tests passent (16/16)
2026-03-02 10:14:56 +01:00
6d01b7c452 feat: Phase 1 - Système d'évaluation de la qualité
- Sélection et copie de 27 documents représentatifs (10 simples, 12 moyens, 5 complexes)
- Outil d'annotation CLI complet (tools/annotation_tool.py)
- Guide d'annotation détaillé (docs/annotation_guide.md)
- Évaluateur de qualité (evaluation/quality_evaluator.py)
  * Calcul Précision, Rappel, F1-Score
  * Identification faux positifs/négatifs
  * Métriques par type de PII
  * Export JSON et rapports texte
- Scanner de fuite (evaluation/leak_scanner.py)
  * Détection PII résiduels (CRITIQUE)
  * Détection nouveaux PII (HAUTE)
  * Scan métadonnées PDF (MOYENNE)
- Benchmark de performance (evaluation/benchmark.py)
  * Mesure temps de traitement
  * Mesure CPU/RAM
  * Export JSON/CSV
- Tests unitaires complets pour tous les composants
- Documentation complète du module d'évaluation

Tâches complétées:
- 1.1.1 Sélection de 27 documents (au lieu de 30)
- 1.1.2 Outil d'annotation CLI
- 1.2.1 Évaluateur de qualité
- 1.2.2 Scanner de fuite
- 1.2.3 Benchmark de performance

Prochaines étapes:
- 1.1.3 Annotation des 27 documents (manuel)
- 1.1.4 Enrichissement stopwords médicaux
- 1.3 Mesure de la baseline
2026-03-02 10:07:41 +01:00