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:
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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éèêëàâäùûüôöîïç\-]+)*)",
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user