fix(finess): inclure les entjur + supprimer code mort _FINESS_ETAB_NAMES
Deux corrections exploitant mieux les gazetteers FINESS/INSEE pour réduire la
dépendance au YAML force_mask_terms.
1. scripts/build_finess_gazetteers.py : ne lisait que col 1 (finess_et) du CSV.
Les col 2 (entjur, entité juridique) étaient ignorés. ~48k numéros
juridiques manqués, dont 640780417 (CHCB entjur) forcé en YAML à cause
de cette lacune. Fix : lecture col 1 + col 2 avec déduplication.
Régénération : 101 941 → 150 436 numéros (+48 495).
2. anonymizer_core_refactored_onnx.py :
- _FINESS_ETAB_NAMES (122k noms) chargé mais jamais consulté après le
refactoring NER-first (le matching passe par l'Aho-Corasick sur
etablissements_distinctifs.txt). Suppression → -122k entrées RAM.
- _INSEE_PRENOMS (lowercase) et _INSEE_PRENOMS_SET (uppercase sans accents)
lisaient deux fois le même fichier prenoms_france.txt. Fusion en une
seule passe disque, les deux formes dérivées en mémoire. -36k lectures.
Validation :
- 640780417 présent dans _FINESS_NUMBERS après rebuild
- 122 hits sur trackare-18007562 (non-régression)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -115,24 +115,49 @@ def _load_bdpm_medication_names() -> set:
|
|||||||
return set()
|
return set()
|
||||||
|
|
||||||
|
|
||||||
# ----------------- Gazetteers INSEE (prénoms + communes) -----------------
|
# ----------------- Gazetteers INSEE (prénoms + communes + noms de famille) -----------------
|
||||||
_INSEE_PRENOMS: set = set()
|
# Prénoms et noms de famille sont utilisés sous deux formes :
|
||||||
_INSEE_COMMUNES: set = set()
|
# - _INSEE_PRENOMS (lowercase) : check rapide "w.lower() in _INSEE_PRENOMS"
|
||||||
|
# - _INSEE_PRENOMS_SET (uppercase sans accents, normalisé NFKD) : cross-validation NER
|
||||||
|
# Une seule lecture fichier alimente les deux sets (avant : 2 passes disque pour
|
||||||
|
# le même fichier prenoms_france.txt, reliquat du refactoring NER-first).
|
||||||
|
_INSEE_PRENOMS: set = set() # lowercase
|
||||||
|
_INSEE_PRENOMS_SET: set = set() # uppercase sans accents
|
||||||
|
_INSEE_COMMUNES: set = set() # uppercase
|
||||||
|
_INSEE_NOMS_FAMILLE: set = set() # uppercase sans accents
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_nfkd_upper(s: str) -> str:
|
||||||
|
"""Supprime les accents et met en majuscules (pour matching INSEE)."""
|
||||||
|
import unicodedata
|
||||||
|
return "".join(
|
||||||
|
c for c in unicodedata.normalize("NFD", s)
|
||||||
|
if unicodedata.category(c) != "Mn"
|
||||||
|
).upper()
|
||||||
|
|
||||||
|
|
||||||
def _load_insee_gazetteers():
|
def _load_insee_gazetteers():
|
||||||
"""Charge les gazetteers INSEE (prénoms français + communes)."""
|
"""Charge les gazetteers INSEE en une seule passe par fichier.
|
||||||
global _INSEE_PRENOMS, _INSEE_COMMUNES
|
Alimente _INSEE_PRENOMS (lowercase) et _INSEE_PRENOMS_SET (uppercase sans accents)
|
||||||
|
depuis le même fichier prenoms_france.txt."""
|
||||||
|
global _INSEE_PRENOMS, _INSEE_PRENOMS_SET, _INSEE_COMMUNES, _INSEE_NOMS_FAMILLE
|
||||||
data_dir = Path(__file__).parent / "data" / "insee"
|
data_dir = Path(__file__).parent / "data" / "insee"
|
||||||
|
|
||||||
# Prénoms (lowercase, >= 3 chars)
|
# Prénoms : lecture unique, deux formes dérivées
|
||||||
prenoms_path = data_dir / "prenoms_france.txt"
|
prenoms_path = data_dir / "prenoms_france.txt"
|
||||||
if prenoms_path.exists():
|
if prenoms_path.exists():
|
||||||
try:
|
try:
|
||||||
_INSEE_PRENOMS = {
|
prenoms_lc = set()
|
||||||
line.strip().lower() for line in prenoms_path.read_text(encoding="utf-8").splitlines()
|
prenoms_nfkd = set()
|
||||||
if line.strip() and len(line.strip()) >= 3
|
for line in prenoms_path.read_text(encoding="utf-8").splitlines():
|
||||||
}
|
raw = line.strip()
|
||||||
log.info(f"Gazetteers INSEE prénoms: {len(_INSEE_PRENOMS)} entrées")
|
if raw and len(raw) >= 3:
|
||||||
|
prenoms_lc.add(raw.lower())
|
||||||
|
prenoms_nfkd.add(_normalize_nfkd_upper(raw))
|
||||||
|
_INSEE_PRENOMS = prenoms_lc
|
||||||
|
_INSEE_PRENOMS_SET = prenoms_nfkd
|
||||||
|
log.info(f"Gazetteers INSEE prénoms: {len(_INSEE_PRENOMS)} entrées "
|
||||||
|
f"(lowercase + uppercase-nfkd)")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"Erreur chargement prénoms INSEE: {e}")
|
log.warning(f"Erreur chargement prénoms INSEE: {e}")
|
||||||
|
|
||||||
@@ -148,26 +173,7 @@ def _load_insee_gazetteers():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"Erreur chargement communes INSEE: {e}")
|
log.warning(f"Erreur chargement communes INSEE: {e}")
|
||||||
|
|
||||||
_load_insee_gazetteers()
|
# Noms de famille (uppercase sans accents)
|
||||||
|
|
||||||
|
|
||||||
# ----------------- Gazetteers INSEE noms de famille + prénoms (uppercase, sans accents) ---------
|
|
||||||
_INSEE_NOMS_FAMILLE: set = set()
|
|
||||||
_INSEE_PRENOMS_SET: set = set()
|
|
||||||
|
|
||||||
def _normalize_nfkd_upper(s: str) -> str:
|
|
||||||
"""Supprime les accents et met en majuscules (pour matching INSEE)."""
|
|
||||||
import unicodedata
|
|
||||||
return "".join(
|
|
||||||
c for c in unicodedata.normalize("NFD", s)
|
|
||||||
if unicodedata.category(c) != "Mn"
|
|
||||||
).upper()
|
|
||||||
|
|
||||||
def _load_insee_noms_prenoms():
|
|
||||||
"""Charge noms de famille et prénoms INSEE, normalisés uppercase sans accents."""
|
|
||||||
global _INSEE_NOMS_FAMILLE, _INSEE_PRENOMS_SET
|
|
||||||
data_dir = Path(__file__).parent / "data" / "insee"
|
|
||||||
|
|
||||||
noms_path = data_dir / "noms_famille_france.txt"
|
noms_path = data_dir / "noms_famille_france.txt"
|
||||||
if noms_path.exists():
|
if noms_path.exists():
|
||||||
try:
|
try:
|
||||||
@@ -180,24 +186,12 @@ def _load_insee_noms_prenoms():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"Erreur chargement noms de famille INSEE: {e}")
|
log.warning(f"Erreur chargement noms de famille INSEE: {e}")
|
||||||
|
|
||||||
prenoms_path = data_dir / "prenoms_france.txt"
|
|
||||||
if prenoms_path.exists():
|
|
||||||
try:
|
|
||||||
_INSEE_PRENOMS_SET = {
|
|
||||||
_normalize_nfkd_upper(line.strip())
|
|
||||||
for line in prenoms_path.read_text(encoding="utf-8").splitlines()
|
|
||||||
if line.strip() and len(line.strip()) >= 3
|
|
||||||
}
|
|
||||||
log.info(f"Gazetteers INSEE prénoms (set): {len(_INSEE_PRENOMS_SET)} entrées")
|
|
||||||
except Exception as e:
|
|
||||||
log.warning(f"Erreur chargement prénoms INSEE (set): {e}")
|
|
||||||
|
|
||||||
_load_insee_noms_prenoms()
|
_load_insee_gazetteers()
|
||||||
|
|
||||||
|
|
||||||
# ----------------- Gazetteer FINESS (établissements de santé) -----------------
|
# ----------------- Gazetteer FINESS (établissements de santé) -----------------
|
||||||
_FINESS_NUMBERS: set = set() # numéros FINESS 9 chiffres
|
_FINESS_NUMBERS: set = set() # numéros FINESS 9 chiffres (structure + entjur)
|
||||||
_FINESS_ETAB_NAMES: set = set() # noms d'établissements (lowercase)
|
|
||||||
_FINESS_TELEPHONES: set = set() # téléphones 10 chiffres
|
_FINESS_TELEPHONES: set = set() # téléphones 10 chiffres
|
||||||
_FINESS_VILLES: set = set() # villes FINESS (uppercase)
|
_FINESS_VILLES: set = set() # villes FINESS (uppercase)
|
||||||
_FINESS_AC = None # Automate Aho-Corasick pour noms distinctifs
|
_FINESS_AC = None # Automate Aho-Corasick pour noms distinctifs
|
||||||
@@ -281,8 +275,8 @@ def _normalize_for_matching(s: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _load_finess_gazetteers():
|
def _load_finess_gazetteers():
|
||||||
"""Charge les gazetteers FINESS (établissements, numéros, téléphones, villes, Aho-Corasick)."""
|
"""Charge les gazetteers FINESS (numéros, téléphones, villes, Aho-Corasick)."""
|
||||||
global _FINESS_NUMBERS, _FINESS_ETAB_NAMES, _FINESS_TELEPHONES, _FINESS_VILLES, _FINESS_AC
|
global _FINESS_NUMBERS, _FINESS_TELEPHONES, _FINESS_VILLES, _FINESS_AC
|
||||||
data_dir = Path(__file__).parent / "data" / "finess"
|
data_dir = Path(__file__).parent / "data" / "finess"
|
||||||
|
|
||||||
# Numéros FINESS
|
# Numéros FINESS
|
||||||
@@ -297,20 +291,9 @@ def _load_finess_gazetteers():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"Erreur chargement FINESS numéros: {e}")
|
log.warning(f"Erreur chargement FINESS numéros: {e}")
|
||||||
|
|
||||||
# Noms d'établissements complets (pour debug/référence)
|
# etablissements_noms.txt volontairement PAS chargé — utilisé uniquement pour
|
||||||
noms_path = data_dir / "etablissements_noms.txt"
|
# debug/inspection. Le matching des noms passe par l'Aho-Corasick construit
|
||||||
if noms_path.exists():
|
# sur etablissements_distinctifs.txt (chargement différé).
|
||||||
try:
|
|
||||||
_FINESS_ETAB_NAMES = {
|
|
||||||
line.strip().lower() for line in noms_path.read_text(encoding="utf-8").splitlines()
|
|
||||||
if line.strip() and len(line.strip()) >= 6
|
|
||||||
}
|
|
||||||
log.info(f"Gazetteer FINESS noms: {len(_FINESS_ETAB_NAMES)} entrées")
|
|
||||||
except Exception as e:
|
|
||||||
log.warning(f"Erreur chargement FINESS noms: {e}")
|
|
||||||
|
|
||||||
# Noms distinctifs : chargement différé (Aho-Corasick construit au premier appel,
|
|
||||||
# car _MEDICAL_STOP_WORDS_SET n'est pas encore défini à ce stade du module)
|
|
||||||
|
|
||||||
# Villes FINESS
|
# Villes FINESS
|
||||||
villes_path = data_dir / "villes_finess.txt"
|
villes_path = data_dir / "villes_finess.txt"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -109,8 +109,12 @@ def main():
|
|||||||
if len(row) < 16:
|
if len(row) < 16:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Numéro FINESS (col 1)
|
# Numéros FINESS : col 1 = finess_et (structure), col 2 = entjur (entité juridique).
|
||||||
finess = row[1].strip()
|
# Les deux sont des identifiants 9 chiffres réels du référentiel FINESS et doivent
|
||||||
|
# être masqués. Avant ce fix, seul finess_et était extrait (~102k), et les ~48k
|
||||||
|
# entjur étaient manqués — provoquant des fuites (ex: 640780417 entjur CHCB).
|
||||||
|
for col_idx in (1, 2):
|
||||||
|
finess = row[col_idx].strip() if col_idx < len(row) else ""
|
||||||
if re.match(r"^\d{9}$", finess):
|
if re.match(r"^\d{9}$", finess):
|
||||||
finess_numbers.add(finess)
|
finess_numbers.add(finess)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user