feat(finess): whitelist de mono-mots distinctifs courts (EMBRUNS, etc.)

Le matcher Aho-Corasick FINESS rejetait tous les mono-mots < 10 chars pour
éviter les faux positifs. Conséquence : EMBRUNS (7 chars), présent dans
etablissements_distinctifs.txt, était ignoré et devait être forcé en YAML
(LES EMBRUNS, REED LES EMBRUNS, EMBRUNS BIDART, regex [Ee]mbruns).

Nouveau fichier data/finess/mono_mots_distinctifs.txt contenant la whitelist
curée des mono-mots courts considérés comme distinctifs. Maintenance manuelle
(un mot par ligne, commentaires autorisés). Le matcher accepte un mono-mot
< 10 chars uniquement s'il est dans cette whitelist.

Initialisation : embruns, embrun (documents CHCB "Les Embruns").

Validation :
- _FINESS_AC matche maintenant "les embruns quelque part" et "embruns seul"
- Pas de régression sur trackare-18007562 (122 hits)

Après ce fix + futurs, on pourra retirer LES EMBRUNS / REED LES EMBRUNS /
EMBRUNS BIDART et regex [Ee]mbruns de force_mask_terms du YAML.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-15 09:35:16 +02:00
parent fd95ae5f2a
commit e6f3853426
2 changed files with 34 additions and 8 deletions

View File

@@ -3120,6 +3120,21 @@ def _build_finess_ac():
"le bourg", "le val", "le clos", "le mas",
"les pins", "les chenes", "les oliviers",
}
# Whitelist explicite de mono-mots < 10 chars considérés comme distinctifs
# (sinon rejetés par le filtre général). Exemple : EMBRUNS (7 chars).
# Alimentée depuis data/finess/mono_mots_distinctifs.txt — curation manuelle.
mono_file = data_dir / "mono_mots_distinctifs.txt"
mono_whitelist: set = set()
if mono_file.exists():
try:
for _line in mono_file.read_text(encoding="utf-8").splitlines():
_w = _line.strip()
if _w and not _w.startswith("#"):
mono_whitelist.add(_w.lower())
log.info(f"FINESS mono-mots distinctifs whitelist: {len(mono_whitelist)} entrées")
except Exception as _exc:
log.warning(f"Erreur chargement mono_mots_distinctifs.txt : {_exc}")
try:
ac = _ahocorasick.Automaton()
count = 0
@@ -3142,20 +3157,20 @@ def _build_finess_ac():
_PRENOM_PREFIXES = {"jean", "marie", "louis", "pierre", "saint", "sainte"}
if len(words) == 2 and words[0] in _PRENOM_PREFIXES and len(words[1]) < 10:
continue
# Filtrer : >= 8 chars et >= 2 mots, OU >= 10 chars pour 1 mot
# Les noms courts sont gérés par RE_HOPITAL_VILLE
# Filtrer : >= 8 chars et >= 2 mots, OU >= 10 chars pour 1 mot,
# OU présent dans la whitelist explicite de mono-mots distinctifs.
if len(words) >= 2 and len(name) >= 8:
# Exclure les multi-mots dont TOUS les mots sont dans le stop words médical
if all(w in _MEDICAL_STOP_WORDS_SET or len(w) <= 2 for w in words):
continue
ac.add_word(name, name)
count += 1
elif (len(words) == 1 and len(name) >= 10
and name not in _ac_generic_blacklist
and name not in _MEDICAL_STOP_WORDS_SET
and _normalize_for_matching(name) not in _MEDICAL_STOP_WORDS_SET):
ac.add_word(name, name)
count += 1
elif len(words) == 1 and (len(name) >= 10 or name in mono_whitelist):
if (name not in _ac_generic_blacklist
and name not in _MEDICAL_STOP_WORDS_SET
and _normalize_for_matching(name) not in _MEDICAL_STOP_WORDS_SET):
ac.add_word(name, name)
count += 1
ac.make_automaton()
_FINESS_AC = ac
log.info(f"Gazetteer FINESS Aho-Corasick: {count} patterns chargés")