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>
This commit is contained in:
2026-04-28 11:32:45 +02:00
parent ffb8006e91
commit f85659d103
4 changed files with 67 additions and 20 deletions

View File

@@ -25,7 +25,7 @@ réflexes ostéotendineux normaux. Force motrice cotée 4/5 sur le
quadriceps droit.
Conduite à tenir :
- EMG demandé en urgence au laboratoire de neurophysiologie du [ETABLISSEMENT] (Dr [NOM] [NOM], neurophysiologiste) ;
- EMG demandé en urgence au laboratoire de neurophysiologie du [ETABLISSEMENT] (Dr [NOM], neurophysiologiste) ;
- IRM médullaire programmée à la [ETABLISSEMENT] ;
- la classification internationale CIM-10 retient G56.8.

View File

@@ -30,8 +30,28 @@ Points critiques :
`neuropathie périphérique`, `réflexes ostéotendineux`,
`rééducation fonctionnelle`, `EMG`, `IRM médullaire`.
Écart spécifique à observer :
- détection des établissements non FINESS publics (cliniques privées) ;
- détection variante longue vs sigle (`Centre Hospitalier Universitaire
de Bordeaux` vs `CHU de Bordeaux`) — la version sigle peut échapper au
matcher si `CHU` n'est pas en règle force_term ou contextuelle.
Écarts résolus dans la session 2026-04-28 :
- **`Centre Hospitalier Universitaire de Bordeaux` coupé sur deux lignes** :
pré-passe `RE_ETAB_LINEBREAK` ajoutée dans la phase 1 — fusionne
strictement les coupures `<TYPE>\n<connecteur+nom>` (avec un seul nom
propre maximum derrière) sans grignoter la ligne suivante non liée.
Couvre aussi `CHU\nde Bordeaux`.
- **`CHCB` non masqué en fin de phrase suivi de ` ;`** : le `SPLITTER`
(`\s*[:|;\t]\s*`) splittait la ligne narrative sur le `;` final,
produisant une "value" vide. La key (qui contenait CHCB) n'était pas
passée à `_mask_line_by_regex` (donc pas aux force_terms). Fix dans
`_kv_value_only_mask` : si la value est vide ou la key dépasse 5 mots,
fallback sur `_mask_line_by_regex(line)` complet.
- **`Biarritz` non masqué après `[ETABLISSEMENT] à Biarritz`** : le
garde-fou `_mask_ville_gazetteers` skippait toute ville après un
placeholder établissement précédé de `de/du/d'/à`. Restreint à
`de/du/d'` (parties de nom d'établissement) — `à` reste actif pour
les vraies localisations.
- **`Beñat` partiellement masqué** : déjà résolu via l'extension Ñ/ñ
dans les classes de caractères (commit c24b7f6).
Variantes établissement gérées :
- forme longue `Centre Hospitalier Universitaire de Bordeaux` ;
- forme sigle `CHU de Bordeaux` ;
- coupures de ligne sur les deux ;
- établissement privé `Clinique Aguilera` détecté via `RE_HOPITAL_VILLE`.

View File

@@ -29,13 +29,7 @@ from tools.run_synthetic_review_corpus import ( # noqa: E402
)
KNOWN_FAILURES: dict[str, str] = {
"009_multi_etablissements": (
"Fuites résiduelles : suffixe `de Bordeaux` après "
"[ETABLISSEMENT], CHCB en fin de phrase. À traiter via "
"admin_rules (étape B suivante)."
),
}
KNOWN_FAILURES: dict[str, str] = {}
def _case_dirs() -> list[Path]: