fix(frozen): data/*.txt dans bundle, feedback UI pendant chargement modèles
Plantages signalés sous Windows : causes identifiées et corrigées.
1. anonymisation_onefile.spec : les fichiers data/stopwords_manuels.txt,
villes_blacklist.txt, dpi_labels_blacklist.txt, companion_blacklist.txt
n'étaient PAS inclus dans le bundle PyInstaller (seuls les sous-dossiers
data/bdpm, data/finess, data/insee l'étaient). Résultat en frozen : sets
vides, qualité dégradée, plus de faux positifs.
2. anonymizer_core_refactored_onnx.py : chargements robustifiés.
- Helper _load_txt_set avec try/except et logging WARNING si fichier absent
- Fallbacks intégrés (_DPI_LABELS_FALLBACK, _COMPANION_BLACKLIST_FALLBACK)
pour continuer à fonctionner si bundle partiel
- try/except sur stopwords_manuels.txt, villes_blacklist.txt, BDPM
3. launcher.py : UX repensée pour le chargement des modèles.
- SetupWindow (premier lancement) : auto-démarrage (plus de clic nécessaire),
progress bar avec étapes visuelles (⏳/✓/✗ par modèle), bouton relance si
échec, bouton "continuer malgré tout" pour modèles optionnels.
- Splash screen ajouté dans launch_gui() : le chargement des gazetteers
(INSEE 200k+ noms, FINESS 100k+ établissements) prend 15-30 s au démarrage
normal. Sans feedback, l'utilisateur croyait l'app plantée. Le splash
tourne pendant l'import (thread séparé, poll avec splash.after).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -251,11 +251,16 @@ _VILLE_BLACKLIST = {
|
||||
# Enrichissement depuis fichier externe (modifiable sans toucher au code)
|
||||
_villes_bl_file = Path(__file__).parent / "data" / "villes_blacklist.txt"
|
||||
if _villes_bl_file.exists():
|
||||
for _line in _villes_bl_file.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
_VILLE_BLACKLIST.add(_w)
|
||||
log.info("Villes blacklist chargées : %d entrées", len(_VILLE_BLACKLIST))
|
||||
try:
|
||||
for _line in _villes_bl_file.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
_VILLE_BLACKLIST.add(_w)
|
||||
log.info("Villes blacklist chargées : %d entrées", len(_VILLE_BLACKLIST))
|
||||
except Exception as _exc:
|
||||
log.error("Villes blacklist : erreur de lecture %s — %s", _villes_bl_file, _exc)
|
||||
else:
|
||||
log.warning("Villes blacklist : fichier introuvable %s — défauts intégrés utilisés", _villes_bl_file)
|
||||
|
||||
try:
|
||||
import ahocorasick as _ahocorasick
|
||||
@@ -827,24 +832,34 @@ _MEDICAL_STOP_WORDS_SET.update(_load_edsnlp_drug_names())
|
||||
# Enrichissement depuis fichier externe (modifiable sans toucher au code)
|
||||
_stopwords_file = Path(__file__).parent / "data" / "stopwords_manuels.txt"
|
||||
if _stopwords_file.exists():
|
||||
_sw_count = 0
|
||||
for _line in _stopwords_file.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
_MEDICAL_STOP_WORDS_SET.add(_w)
|
||||
_sw_count += 1
|
||||
log.info("Stop-words manuels chargés : %d mots depuis %s", _sw_count, _stopwords_file.name)
|
||||
try:
|
||||
_sw_count = 0
|
||||
for _line in _stopwords_file.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
_MEDICAL_STOP_WORDS_SET.add(_w)
|
||||
_sw_count += 1
|
||||
log.info("Stop-words manuels chargés : %d mots depuis %s", _sw_count, _stopwords_file.name)
|
||||
except Exception as _exc:
|
||||
log.error("Stop-words manuels : erreur de lecture %s — %s", _stopwords_file, _exc)
|
||||
else:
|
||||
log.warning("Stop-words manuels : fichier introuvable %s — qualité dégradée", _stopwords_file)
|
||||
|
||||
# Enrichissement BDPM : ~7300 noms commerciaux + DCI/substances actives
|
||||
_bdpm_path = Path(__file__).parent / "data" / "bdpm" / "medicaments_stopwords.txt"
|
||||
if _bdpm_path.exists():
|
||||
_bdpm_count = 0
|
||||
for _line in _bdpm_path.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
_MEDICAL_STOP_WORDS_SET.add(_w)
|
||||
_bdpm_count += 1
|
||||
log.info("BDPM stop-words chargés : %d mots", _bdpm_count)
|
||||
try:
|
||||
_bdpm_count = 0
|
||||
for _line in _bdpm_path.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
_MEDICAL_STOP_WORDS_SET.add(_w)
|
||||
_bdpm_count += 1
|
||||
log.info("BDPM stop-words chargés : %d mots", _bdpm_count)
|
||||
except Exception as _exc:
|
||||
log.error("BDPM stop-words : erreur de lecture %s — %s", _bdpm_path, _exc)
|
||||
else:
|
||||
log.warning("BDPM stop-words : fichier introuvable %s — qualité dégradée", _bdpm_path)
|
||||
|
||||
_MEDICAL_STOP_WORDS = (
|
||||
r"(?:" + "|".join(re.escape(w) for w in _MEDICAL_STOP_WORDS_SET) + r")"
|
||||
@@ -1133,30 +1148,60 @@ class NameCandidate:
|
||||
_WHITELIST_NEVER_MASK_TOKENS: set = set()
|
||||
_WHITELIST_NEVER_MASK_PHRASES: set = set()
|
||||
|
||||
# Safe-guards pour les défauts intégrés quand les fichiers data/*.txt sont absents
|
||||
# (mode frozen où le bundle aurait omis de les inclure). Contenu minimal pour
|
||||
# garantir un comportement de masquage correct même en mode dégradé.
|
||||
_DPI_LABELS_FALLBACK = {
|
||||
"date", "note", "heure", "type", "soin", "soins", "surv",
|
||||
"page", "presc", "saint", "sainte",
|
||||
}
|
||||
_COMPANION_BLACKLIST_FALLBACK = {
|
||||
"CANCEROLOGIE", "ONCOLOGIE", "REANIMATION", "RADIOLOGIE",
|
||||
"CARDIOLOGIE", "NEUROLOGIE", "PNEUMOLOGIE", "UROLOGIE",
|
||||
"MEDECINE", "DOSSIER", "CONTENTION", "ISOLEMENT", "ELIMINATION",
|
||||
"ZONE", "PARTI", "PLAN", "MAIN", "FORT", "FORTE",
|
||||
}
|
||||
|
||||
|
||||
def _load_txt_set(path: Path, transform=str.lower, label: str = "file") -> set:
|
||||
"""Charge un fichier .txt ligne par ligne. Robuste aux erreurs (frozen exe)."""
|
||||
result: set = set()
|
||||
if not path.exists():
|
||||
log.warning("%s introuvable : %s — utilisation des défauts intégrés", label, path)
|
||||
return result
|
||||
try:
|
||||
for _line in path.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
result.add(transform(_w))
|
||||
log.info("%s chargé : %d entrées depuis %s", label, len(result), path.name)
|
||||
except Exception as exc:
|
||||
log.error("%s : erreur de lecture %s — %s", label, path, exc)
|
||||
return result
|
||||
|
||||
|
||||
# Labels DPI structurels à ne JAMAIS masquer comme noms (Date, Note, Heure...)
|
||||
# Stocké en LOWERCASE — la comparaison est case-insensitive.
|
||||
# Chargé depuis data/dpi_labels_blacklist.txt + cfg["additional_dpi_labels"].
|
||||
_DPI_LABELS_SET: set = set()
|
||||
_dpi_file = Path(__file__).parent / "data" / "dpi_labels_blacklist.txt"
|
||||
if _dpi_file.exists():
|
||||
for _line in _dpi_file.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
_DPI_LABELS_SET.add(_w.lower())
|
||||
log.info("DPI labels blacklist chargés : %d entrées", len(_DPI_LABELS_SET))
|
||||
_DPI_LABELS_SET: set = _load_txt_set(
|
||||
Path(__file__).parent / "data" / "dpi_labels_blacklist.txt",
|
||||
transform=str.lower,
|
||||
label="DPI labels blacklist",
|
||||
)
|
||||
if not _DPI_LABELS_SET:
|
||||
_DPI_LABELS_SET = set(_DPI_LABELS_FALLBACK)
|
||||
|
||||
# Companion blacklist : termes EN MAJUSCULES qui ne sont JAMAIS des noms
|
||||
# (spécialités, labos pharma, mots courants ambigus).
|
||||
# Stocké en UPPERCASE — la comparaison est faite contre des candidats déjà uppercase.
|
||||
# Chargé depuis data/companion_blacklist.txt + cfg["additional_companion_blacklist"].
|
||||
_COMPANION_BLACKLIST_SET: set = set()
|
||||
_comp_file = Path(__file__).parent / "data" / "companion_blacklist.txt"
|
||||
if _comp_file.exists():
|
||||
for _line in _comp_file.read_text(encoding="utf-8").splitlines():
|
||||
_w = _line.strip()
|
||||
if _w and not _w.startswith("#"):
|
||||
_COMPANION_BLACKLIST_SET.add(_w.upper())
|
||||
log.info("Companion blacklist chargée : %d entrées", len(_COMPANION_BLACKLIST_SET))
|
||||
_COMPANION_BLACKLIST_SET: set = _load_txt_set(
|
||||
Path(__file__).parent / "data" / "companion_blacklist.txt",
|
||||
transform=str.upper,
|
||||
label="Companion blacklist",
|
||||
)
|
||||
if not _COMPANION_BLACKLIST_SET:
|
||||
_COMPANION_BLACKLIST_SET = set(_COMPANION_BLACKLIST_FALLBACK)
|
||||
|
||||
|
||||
_WHITELIST_FUNCTION_WORDS = {
|
||||
|
||||
Reference in New Issue
Block a user