fix: sync texte↔raster + GUI listes whitelist/blacklist améliorées

Bug critique corrigé : les noms forcés (contexte Dr/Mme) comme "MASSE"
étaient masqués dans le texte mais pas dans le PDF raster car filtrés
par les stop-words médicaux. Nouveau kind "NOM_FORCE" qui bypass le
filtre stop-words dans les fonctions de redaction vector et raster.

GUI : remplacement des zones texte brut par des listes interactives
avec champ de saisie + bouton Ajouter + bouton Supprimer, fond coloré
(vert pour whitelist, rose pour blacklist).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 17:34:51 +02:00
parent f9fbae1f27
commit 106f1fcd2e
2 changed files with 109 additions and 38 deletions

View File

@@ -2154,8 +2154,11 @@ def _apply_extracted_names(text: str, names: set, audit: List[PiiHit], force_nam
safe_names = {n for n in names if len(n) >= 4 and (n in _force or n.lower() not in _MEDICAL_STOP_WORDS_SET)}
# Ajouter un hit global (page=-1) par nom pour la redaction PDF raster
# (un seul hit suffit — redact_pdf_raster cherche le token sur chaque page)
# Les noms forcés (contexte Dr/Mme) utilisent NOM_FORCE pour bypasser
# le filtre stop-words dans le raster
for token in sorted(safe_names, key=len, reverse=True):
audit.append(PiiHit(-1, "NOM_GLOBAL", token, placeholder))
kind = "NOM_FORCE" if token in _force else "NOM_GLOBAL"
audit.append(PiiHit(-1, kind, token, placeholder))
for token in sorted(safe_names, key=len, reverse=True):
pattern = re.compile(rf"\b{re.escape(token)}\b", re.IGNORECASE)
new_text = []
@@ -3390,8 +3393,8 @@ def redact_pdf_vector(original_pdf: Path, audit: List[PiiHit], out_pdf: Path, oc
seen_tokens.add(dedup_key)
# --- Kinds de type nom/entité : whole-word search pour éviter le
# substring matching (ex: "TATIN" dans "ATORVASTATINE") ---
if h.kind in _VECTOR_WHOLEWORD_KINDS:
if token.lower() in _MEDICAL_STOP_WORDS_SET:
if h.kind in _VECTOR_WHOLEWORD_KINDS or h.kind == "NOM_FORCE":
if h.kind != "NOM_FORCE" and token.lower() in _MEDICAL_STOP_WORDS_SET:
continue
if " " not in token:
rects = _search_whole_word(page, token)
@@ -3535,8 +3538,9 @@ def redact_pdf_raster(original_pdf: Path, audit: List[PiiHit], out_pdf: Path, dp
seen_tokens.add(token)
# --- Kinds de type nom/entité : whole-word search pour éviter le
# substring matching (ex: "TATIN" dans "ATORVASTATINE") ---
if h.kind in _RASTER_WHOLEWORD_KINDS:
if token.lower() in _MEDICAL_STOP_WORDS_SET:
if h.kind in _RASTER_WHOLEWORD_KINDS or h.kind == "NOM_FORCE":
# NOM_FORCE bypass le filtre stop-words (nom confirmé par contexte Dr/Mme)
if h.kind != "NOM_FORCE" and token.lower() in _MEDICAL_STOP_WORDS_SET:
continue
if " " not in token:
# Token mono-mot : chercher comme mot entier