Fix 2 fuites PII : prénom REGINA + nom composé NOCENT-EJNAINI cross-line

- Retirer "regina" de _MEDICAL_STOP_WORDS_SET (bloquait la détection du prénom)
- Ajouter regex "Prénom de naissance" / "Prénom utilisé" dans _extract_document_names
- Substitution tolérante aux sauts de ligne pour noms composés (tiret + \s*)
- Conserver les parties longues (>=5 chars) des noms composés dans _global_name_tokens
  au lieu de les supprimer (le texte PDF peut les scinder sur des lignes séparées)

Vérifié : REGINA 33→0, NOCENT 90→0, EJNAINI 90→0 occurrences en clair

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 23:41:40 +01:00
parent 28da29f521
commit 4583283bd4

View File

@@ -322,7 +322,7 @@ _MEDICAL_STOP_WORDS_SET = {
"depakote", "versatis", "humalog", "forxiga", "durogesic", "depakote", "versatis", "humalog", "forxiga", "durogesic",
"montelukast", "rosuvastatine", "montelukast", "rosuvastatine",
# Abréviations pharma courtes # Abréviations pharma courtes
"cpr", "sol", "bic", "agt", "poche", "inhal", "regina", "cpr", "sol", "bic", "agt", "poche", "inhal",
# Faux positifs EDS supplémentaires # Faux positifs EDS supplémentaires
"psy", "inhales", "inhalés", "kwikpen", "lansoprazole", "tiorfan", "smecta", "psy", "inhales", "inhalés", "kwikpen", "lansoprazole", "tiorfan", "smecta",
"axa", "ttt", "anionique", "abdomino", "cod", "omi", "urg", "med", "axa", "ttt", "anionique", "abdomino", "cod", "omi", "urg", "med",
@@ -891,6 +891,10 @@ def _extract_trackare_identity(full_text: str) -> Tuple[set, List[PiiHit]]:
for m in re.finditer(r"Nom\s+et\s+Pr[ée]nom\s*:\s*(.+?)(?:\s+Date\s+de\s+naissance|\s*$)", full_text, re.MULTILINE): for m in re.finditer(r"Nom\s+et\s+Pr[ée]nom\s*:\s*(.+?)(?:\s+Date\s+de\s+naissance|\s*$)", full_text, re.MULTILINE):
_add_name(m.group(1).strip()) _add_name(m.group(1).strip())
# Prénom de naissance / Prénom utilisé : REGINA
for m in re.finditer(r"Pr[ée]nom\s+(?:de\s+naissance|utilis[ée])\s*:\s*([A-ZÉÈÀÙÂÊÎÔÛÄËÏÖÜÇ][A-ZÉÈÀÙÂÊÎÔÛÄËÏÖÜÇa-zéèàùâêîôûäëïöüç\s\-']+?)(?:\s*$)", full_text, re.MULTILINE):
_add_name(m.group(1).strip())
# Lieu de naissance: BAYONNE → masquer comme VILLE # Lieu de naissance: BAYONNE → masquer comme VILLE
for m in re.finditer(r"Lieu\s+de\s+naissance\s*:\s*([A-ZÉÈÀÙÂÊÎÔÛ][A-ZÉÈÀÙÂÊÎÔÛa-zéèàùâêîôû\s\-']+?)(?:\s*$)", full_text, re.MULTILINE): for m in re.finditer(r"Lieu\s+de\s+naissance\s*:\s*([A-ZÉÈÀÙÂÊÎÔÛ][A-ZÉÈÀÙÂÊÎÔÛa-zéèàùâêîôû\s\-']+?)(?:\s*$)", full_text, re.MULTILINE):
val = m.group(1).strip() val = m.group(1).strip()
@@ -1698,13 +1702,14 @@ def process_pdf(
_companion_tokens.add(candidate) _companion_tokens.add(candidate)
_global_name_tokens.update(_companion_tokens) _global_name_tokens.update(_companion_tokens)
# Retirer les sous-parties de noms composés (JEAN, PIERRE si JEAN-PIERRE existe) # Retirer les sous-parties COURTES de noms composés (JEAN si JEAN-PIERRE existe)
# Garder les parties longues (>=5 chars) car le texte peut les scinder sur des lignes séparées
_compound = {t for t in _global_name_tokens if "-" in t} _compound = {t for t in _global_name_tokens if "-" in t}
_parts_to_drop = set() _parts_to_drop = set()
for comp in _compound: for comp in _compound:
for part in comp.split("-"): for part in comp.split("-"):
part = part.strip() part = part.strip()
if len(part) >= 2 and part in _global_name_tokens: if len(part) >= 2 and len(part) < 5 and part in _global_name_tokens:
_parts_to_drop.add(part) _parts_to_drop.add(part)
_global_name_tokens -= _parts_to_drop _global_name_tokens -= _parts_to_drop
@@ -1755,7 +1760,11 @@ def process_pdf(
if anon.is_trackare and h.kind == "NOM_GLOBAL" and len(token) <= 3: if anon.is_trackare and h.kind == "NOM_GLOBAL" and len(token) <= 3:
continue continue
try: try:
final_text = re.sub(rf"\b{re.escape(token)}\b", h.placeholder, final_text) pat = re.escape(token)
# Noms composés : tolérer les sauts de ligne/espaces autour du tiret
if "-" in token:
pat = pat.replace(r"\-", r"\-\s*")
final_text = re.sub(rf"\b{pat}\b", h.placeholder, final_text)
except re.error: except re.error:
final_text = final_text.replace(token, h.placeholder) final_text = final_text.replace(token, h.placeholder)