feat: anonymisation qualité++ — 15 patterns, subparts tirets, fix entity registry

Bloc A: fix sous-parties dans _mappings, filtre NER anti-tag,
intégration patterns manquants (DESTINATAIRE, PRESCRIPTION_AUTHOR),
whitelist médicaments élargie (+60), villes retirées de whitelist.

Bloc B: CRH dedup chars 200-1000, CP_VILLE vrais codes postaux FR,
DR_NAME capital par mot, BACTERIO header tolère ligne vide.

Bloc C: DR_NAME negative lookahead multi-docteurs même ligne,
entity_registry split tirets (RITZ-QUILLACQ), fix early return
subparts dans _find_matching_entity, PRESCRIPTION_AUTHOR élargi
(Révisé/Traité, variable.), NOTE_AUTHOR élargi (Diététicienne,
Kiné, Ergo), + 8 nouveaux patterns (CONTACT_RELATION, MOD_PAR,
AIDE_NAME, SIGNATURE_LINE, VALIDE_PAR, INTERNE_SIGNATURE,
FOIS_NAME, MALADIE_NAME), adresses inline +ALLEE/IMP,
text_cleaner préserve abréviations médicales.

Validé sur 6 cas (21, 11, 104, 160, 50, 200). 70 tests OK.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-03-03 11:11:47 +01:00
parent f4a23a5f43
commit 99069f150a
7 changed files with 492 additions and 60 deletions

View File

@@ -131,7 +131,8 @@ def _split_crh(text: str) -> list[str]:
def _dedup_chunks(chunks: list[str], threshold: float = 0.85) -> list[str]:
"""Supprime les chunks quasi-identiques (copies pour destinataires multiples).
Compare les 500 premiers caractères de chaque paire.
Compare les caractères 200-1000 de chaque paire (en sautant l'en-tête
patient qui est identique entre séjours différents du même patient).
Si le ratio de similarité > threshold, le doublon est supprimé.
"""
if len(chunks) <= 1:
@@ -144,11 +145,14 @@ def _dedup_chunks(chunks: list[str], threshold: float = 0.85) -> list[str]:
for j in range(i + 1, len(chunks)):
if j in duplicates:
continue
ratio = SequenceMatcher(
None,
chunks[i][:500],
chunks[j][:500],
).ratio()
# Comparer le corps du document (après l'en-tête patient)
sample_i = chunks[i][200:1000]
sample_j = chunks[j][200:1000]
# Si un chunk est trop court, comparer ce qui est disponible
if len(sample_i) < 50 or len(sample_j) < 50:
sample_i = chunks[i]
sample_j = chunks[j]
ratio = SequenceMatcher(None, sample_i, sample_j).ratio()
if ratio > threshold:
duplicates.add(j)
logger.info(" CRH chunk %d doublon de %d (ratio=%.2f), supprimé", j + 1, i + 1, ratio)