fix: anonymisation — sur-anonymisation + fuites PHI + patterns sécurisés

- DR_NAME_PATTERN limité à 2 mots (évite capture "CHARLANNE Traitements")
- CIVILITE_NAME_PATTERN et DESTINATAIRE_PATTERN : chaque mot doit commencer
  par majuscule (évite capture de phrases entières comme noms)
- DATE_NAISSANCE_PATTERN : colon optionnel après "le" ("Né(e) le : DD/MM/YYYY")
- N_CSULT_PATTERN ajouté pour numéros de consultation anesthésie
- CONTACT_RELATION_PATTERN : +15 relations familiales (Neveu, Nièce, Oncle...)
- MEDICAL_TERMS_WHITELIST : +30 termes hospitaliers (scanner, traitement,
  viscerale, radiologie, consultation, etc.)
- FRENCH_STOP_WORDS : +20 mots courts (fort, aide, suite, avant, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-03-03 12:38:13 +01:00
parent 99069f150a
commit 795110d2e6
3 changed files with 36 additions and 7 deletions

View File

@@ -64,6 +64,15 @@ MEDICAL_TERMS_WHITELIST = {
"coordonnateur", "fédération", "federation",
"institut", "cancérologie",
"palais",
# Termes médicaux/hospitaliers à risque de sur-anonymisation
"traitement", "traitements", "scanner", "imagerie",
"viscerale", "viscérale", "thoracique", "abdominale",
"vasculaire", "cérébral", "cérébrale", "cardiaque",
"médecine", "medecine", "réanimation", "reanimation",
"pédiatrie", "pediatrie", "gynécologie", "gynecologie",
"urologie", "ophtalmologie", "dermatologie", "rhumatologie",
"radiologie", "anesthésie", "anesthesie", "consultation",
"biologie", "laboratoire", "pharmacie", "kinésithérapie",
}
# Noms d'établissement à préserver si configuré
@@ -204,6 +213,8 @@ class Anonymizer:
count += n
text, n = self._replace_pattern(text, patterns.N_IPP_PATTERN, "ipp")
count += n
text, n = self._replace_pattern(text, patterns.N_CSULT_PATTERN, "episode")
count += n
text, n = self._replace_pattern(
text, patterns.CONSULT_ADRESSE_PATTERN, "adresse",
skip_establishment_check=True,

View File

@@ -16,6 +16,12 @@ FRENCH_STOP_WORDS = {
"peu", "très", "trop", "tout", "tous", "rien", "fait",
"été", "sont", "ont", "qui", "que", "dont", "peut",
"cette", "être", "avoir", "faire", "dire", "aussi",
# Mots courts (4-5 chars) trop courants comme sous-parties
"fort", "mars", "long", "beau", "noir", "gros",
"aide", "note", "date", "heure", "type", "code",
"état", "etat", "mise", "prise", "point", "place",
"suite", "avant", "après", "apres", "autre",
"comme", "entre", "même", "meme", "seul",
}

View File

@@ -77,10 +77,11 @@ ADDRESS_BLOCK_PATTERN = regex.compile(
# --- Dates de naissance ---
# Toutes les variantes : "né(e) le", "née le", "né le", "Né(e) le", "Date de naissance:"
# Toutes les variantes : "né(e) le", "née le", "né le", "Né(e) le", "Né(e) le :",
# "Date de naissance:", "N° Csult" (consultation anesthésie)
# Accepte les séparateurs / et - (DD/MM/YYYY ou DD-MM-YYYY)
DATE_NAISSANCE_PATTERN = regex.compile(
r"(?:[Nn][ée]+(?:\(e\))?\s+le\s+|Date de naissance\s*[:=]?\s*)(\d{2}[/\-]\d{2}[/\-]\d{4})"
r"(?:[Nn][ée]+(?:\(e\))?\s+le\s*:?\s*|Date de naissance\s*[:=]?\s*)(\d{2}[/\-]\d{2}[/\-]\d{4})"
)
# --- Noms structurés ---
@@ -102,8 +103,9 @@ PATIENT_NAME_PATTERN = regex.compile(
)
# "MME/Mme/M./MR/Madame/Monsieur" suivi du nom
# Chaque mot du nom doit commencer par une majuscule (évite de capturer des phrases)
CIVILITE_NAME_PATTERN = regex.compile(
r"(?:MME|Mme|Madame|M\.|Mr|MR|Monsieur)\s+([A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\s\.\-]+?)(?:\s+[Nn]é|\s+Date|\n|,)"
r"(?:MME|Mme|Madame|M\.|Mr|MR|Monsieur)\s+([A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\.\-]+(?:\s+[A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\.\-]+){0,3})(?:\s+[Nn]é|\s+Date|\n|,)"
)
# "DR." / "Dr" / "Docteur" suivi du nom du médecin
@@ -113,7 +115,7 @@ CIVILITE_NAME_PATTERN = regex.compile(
# Negative lookahead empêche de capturer "Dr" comme partie du nom
# (cas multi-docteurs sur même ligne : "Dr LEYSSENE David Dr BENARD Yohan").
DR_NAME_PATTERN = regex.compile(
r"(?:DR\.?|Dr\.?|Docteur)\s+([A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\.\-']+(?:[ \t\-'](?!DR\.?\s|Dr\.?\s|Docteur\s)[A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-Za-zéèêëàâäùûüôöîïç\.\-']+){0,2})"
r"(?:DR\.?|Dr\.?|Docteur)\s+([A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\.\-']+(?:[ \t\-'](?!DR\.?\s|Dr\.?\s|Docteur\s)[A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-Za-zéèêëàâäùûüôöîïç\.\-']+){0,1})"
)
# "Rédigé par" en pied de page CRH
@@ -122,8 +124,9 @@ REDIGE_PAR_PATTERN = regex.compile(
)
# "Liste des destinataires:" suivi de noms
# Chaque mot doit commencer par une majuscule (nom propre)
DESTINATAIRE_PATTERN = regex.compile(
r"(?:Madame|Monsieur|DR\.?|Dr\.?)\s+([A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\s\.\-]+?)(?:\n|$)"
r"(?:Madame|Monsieur|DR\.?|Dr\.?)\s+([A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\.\-]+(?:\s+[A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\.\-]+){0,3})(?:\n|$)"
)
# Noms d'auteurs dans Trackare : "Note d'évolution Prénom NOM DD/MM/YYYY"
@@ -205,6 +208,11 @@ N_IPP_PATTERN = regex.compile(
r"N\s+Ipp\s*:\s*(\d{6,10})"
)
# "N° Csult : 23117170" ou "N°Csult :23117170" (numéro de consultation)
N_CSULT_PATTERN = regex.compile(
r"N°?\s*Csult\s*:?\s*(\d{6,10})"
)
# "Adresse : 15 rue des Lilas 64100 BAYONNE" (consultation anesthésie)
CONSULT_ADRESSE_PATTERN = regex.compile(
r"Adresse\s*:\s*(.+?)(?:\n|$)"
@@ -223,9 +231,13 @@ PERSONNE_PREVENIR_PATTERN = regex.compile(
r"Personne\s+[àa]\s+pr[ée]venir\s+([A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ][A-Za-zéèêëàâäùûüôöîïç\s\-]+?)(?:\s+\d{2}[\s.]|\s*$|\s*\n)",
)
# Contacts : "Concubine/Conjoint/Époux/Épouse NOM Prénom"
# Contacts : "Concubine/Conjoint/Époux/Neveu/Oncle... NOM Prénom"
CONTACT_RELATION_PATTERN = regex.compile(
r"(?:Concubin[e]?|Conjoint[e]?|[ÉE]poux|[ÉE]pouse|Compagnon|Compagne|Fils|Fille|Père|Mère|Frère|Sœur|Soeur)\s+"
r"(?:Concubin[e]?|Conjoint[e]?|[ÉE]poux|[ÉE]pouse|Compagnon|Compagne"
r"|Fils|Fille|Père|Mère|Frère|Sœur|Soeur"
r"|Neveu|Nièce|Niece|Oncle|Tante|Cousin[e]?"
r"|Beau[\s\-]?père|Belle[\s\-]?mère|Beau[\s\-]?frère|Belle[\s\-]?sœur|Belle[\s\-]?soeur"
r"|Ami[e]?|Voisin[e]?|Tuteur|Tutrice|Curateur|Curatrice)\s+"
r"([A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇ]{2,}(?:\s+[A-ZÉÈÊËÀÂÄÙÛÜÔÖÎÏÇa-zéèêëàâäùûüôöîïç\-]+)*)",
)