Fix faux positifs PDF (EDS_TEL, EDS_VILLE) + détection noms Notes IDE
- Skip EDS_TEL dans PDF (valeurs Pouls détectées comme N° de téléphone) - Ajout EDS_VILLE au whole-word matching (évite "GEL" dans "GELULE") - Filtre stop words étendu à EDS_HOPITAL et EDS_VILLE dans la détection NER - Détection noms soignants dans "Note IDE\nPrenom NOM" (BARGAIN, LACOTE, etc.) - Stop words : semaine, jour, matin, soir, nuit, midi Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -301,6 +301,8 @@ _MEDICAL_STOP_WORDS_SET = {
|
|||||||
"axa", "ttt", "anionique", "abdomino", "cod", "omi", "urg", "med",
|
"axa", "ttt", "anionique", "abdomino", "cod", "omi", "urg", "med",
|
||||||
"10mg", "20mg", "40mg", "100mg", "300ui", "500ml", "innohep", "coaprovel",
|
"10mg", "20mg", "40mg", "100mg", "300ui", "500ml", "innohep", "coaprovel",
|
||||||
"actiskenan", "simvastatine", "forlax",
|
"actiskenan", "simvastatine", "forlax",
|
||||||
|
# Mots temporels / contextuels détectés comme EDS_HOPITAL
|
||||||
|
"semaine", "jour", "matin", "soir", "nuit", "midi",
|
||||||
# Mots clés de contexte document
|
# Mots clés de contexte document
|
||||||
"compétences", "maladies", "inflammatoires", "systémiques", "rares",
|
"compétences", "maladies", "inflammatoires", "systémiques", "rares",
|
||||||
"fret", "fax", "contexte", "résultat", "resultat", "résultats", "resultats",
|
"fret", "fax", "contexte", "résultat", "resultat", "résultats", "resultats",
|
||||||
@@ -888,11 +890,19 @@ def _extract_trackare_identity(full_text: str) -> Tuple[set, List[PiiHit]]:
|
|||||||
if m.group(2):
|
if m.group(2):
|
||||||
_add_name(m.group(2))
|
_add_name(m.group(2))
|
||||||
|
|
||||||
# --- Noms soignants dans les Notes d'évolution ---
|
# --- Noms soignants dans les Notes d'évolution / Notes IDE / Notes médicales ---
|
||||||
# Pattern: "Note d'évolution PRENOM NOM" ou "NOM HH:MM texte..."
|
# Pattern: "Note IDE\nPrenom NOM" ou "Note d'évolution\nPrenom NOM"
|
||||||
for m in re.finditer(r"Note\s+d'[ée]volution\s+([A-ZÉÈÀÙÂÊÎÔÛ][a-zéèàùâêîôû]+)\s+([A-ZÉÈÀÙÂÊÎÔÛ]{2,})", full_text):
|
for m in re.finditer(
|
||||||
_add_name(m.group(1))
|
r"Note\s+(?:IDE|AS|d'[ée]volution|m[ée]dicale|kin[ée])\s*\n\s*"
|
||||||
_add_name(m.group(2))
|
r"([A-ZÉÈÀÙÂÊÎÔÛa-zéèàùâêîôû][a-zéèàùâêîôûäëïöüç]+)\s+"
|
||||||
|
r"([A-ZÉÈÀÙÂÊÎÔÛ][A-ZÉÈÀÙÂÊÎÔÛa-zéèàùâêîôûäëïöüç\-]+)",
|
||||||
|
full_text
|
||||||
|
):
|
||||||
|
prenom, nom = m.group(1), m.group(2)
|
||||||
|
if prenom.lower() not in _MEDICAL_STOP_WORDS_SET:
|
||||||
|
_add_name(prenom)
|
||||||
|
if nom.lower() not in _MEDICAL_STOP_WORDS_SET:
|
||||||
|
_add_name(nom)
|
||||||
|
|
||||||
# --- Noms soignants multi-lignes : "Prénom\nNOM" dans les tableaux de prescriptions/soins ---
|
# --- Noms soignants multi-lignes : "Prénom\nNOM" dans les tableaux de prescriptions/soins ---
|
||||||
for m in re.finditer(
|
for m in re.finditer(
|
||||||
@@ -1174,7 +1184,7 @@ def _mask_with_eds_pseudo(text: str, ents: List[Dict[str, Any]], cfg: Dict[str,
|
|||||||
continue
|
continue
|
||||||
# Filtrer les faux positifs NOM/PRENOM (médicaments, acronymes médicaux)
|
# Filtrer les faux positifs NOM/PRENOM (médicaments, acronymes médicaux)
|
||||||
label = e.get("entity_group", "EDS")
|
label = e.get("entity_group", "EDS")
|
||||||
if label in ("NOM", "PRENOM"):
|
if label in ("NOM", "PRENOM", "HOPITAL", "VILLE"):
|
||||||
if w.lower() in _MEDICAL_STOP_WORDS_SET:
|
if w.lower() in _MEDICAL_STOP_WORDS_SET:
|
||||||
continue
|
continue
|
||||||
# Filtrer aussi les tokens multi-mots dont un composant est un stop word
|
# Filtrer aussi les tokens multi-mots dont un composant est un stop word
|
||||||
@@ -1309,10 +1319,10 @@ def redact_pdf_vector(original_pdf: Path, audit: List[PiiHit], out_pdf: Path) ->
|
|||||||
by_page.setdefault(h.page, []).append(h)
|
by_page.setdefault(h.page, []).append(h)
|
||||||
# Kinds à ne pas chercher dans le PDF (dates masquées uniquement dans le texte,
|
# Kinds à ne pas chercher dans le PDF (dates masquées uniquement dans le texte,
|
||||||
# pas dans le PDF où elles rendent les tableaux illisibles)
|
# pas dans le PDF où elles rendent les tableaux illisibles)
|
||||||
_VECTOR_SKIP_KINDS = {"EDS_DATE", "EDS_DATE_NAISSANCE", "EDS_SECU"}
|
_VECTOR_SKIP_KINDS = {"EDS_DATE", "EDS_DATE_NAISSANCE", "EDS_SECU", "EDS_TEL"}
|
||||||
# Kinds dont les tokens courts (< 5) risquent le substring matching via page.search_for()
|
# Kinds dont les tokens courts (< 5) risquent le substring matching via page.search_for()
|
||||||
_VECTOR_SHORT_TOKEN_KINDS = {"NOM_GLOBAL", "NOM_EXTRACTED", "EDS_NOM", "EDS_PRENOM",
|
_VECTOR_SHORT_TOKEN_KINDS = {"NOM_GLOBAL", "NOM_EXTRACTED", "EDS_NOM", "EDS_PRENOM",
|
||||||
"EDS_HOPITAL", "ETAB", "ETAB_GLOBAL"}
|
"EDS_HOPITAL", "EDS_VILLE", "ETAB", "ETAB_GLOBAL"}
|
||||||
for pno in range(len(doc)):
|
for pno in range(len(doc)):
|
||||||
page = doc[pno]
|
page = doc[pno]
|
||||||
hits = by_page.get(pno, []) + by_page.get(-1, [])
|
hits = by_page.get(pno, []) + by_page.get(-1, [])
|
||||||
@@ -1365,9 +1375,9 @@ def redact_pdf_raster(original_pdf: Path, audit: List[PiiHit], out_pdf: Path, dp
|
|||||||
for pno in range(len(doc)):
|
for pno in range(len(doc)):
|
||||||
page = doc[pno]
|
page = doc[pno]
|
||||||
rects = []
|
rects = []
|
||||||
_RASTER_SKIP_KINDS = {"EDS_DATE", "EDS_DATE_NAISSANCE", "EDS_SECU"}
|
_RASTER_SKIP_KINDS = {"EDS_DATE", "EDS_DATE_NAISSANCE", "EDS_SECU", "EDS_TEL"}
|
||||||
_RASTER_SHORT_TOKEN_KINDS = {"NOM_GLOBAL", "NOM_EXTRACTED", "EDS_NOM", "EDS_PRENOM",
|
_RASTER_SHORT_TOKEN_KINDS = {"NOM_GLOBAL", "NOM_EXTRACTED", "EDS_NOM", "EDS_PRENOM",
|
||||||
"EDS_HOPITAL", "ETAB", "ETAB_GLOBAL"}
|
"EDS_HOPITAL", "EDS_VILLE", "ETAB", "ETAB_GLOBAL"}
|
||||||
hits = [x for x in audit if x.page in {pno, -1}]
|
hits = [x for x in audit if x.page in {pno, -1}]
|
||||||
for h in hits:
|
for h in hits:
|
||||||
token = h.original.strip()
|
token = h.original.strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user