- Nouveau module dp_scoring.py : shortlist, scoring multi-critères, select_dp, LLM one-shot fallback avec garde-fous (négation, comorbidité, Z/R-codes) - Parser CPAM : auto-détection format legacy/ucr_extract, 6 nouveaux champs ControleCPAM (codes_etablissement, libelle, codes_retenus, ghm_ghs) - CRH parser : 3 nouvelles sections (diag_sortie, diag_principal, synthese) - Prompt DP_LLM_ONESHOT externalisé dans templates.py - Propagation dp_selection dans fusion.py - 808 tests passent (dont 21 nouveaux CPAM + 77 dp_scoring + 8 CRH) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
260 lines
7.9 KiB
Python
260 lines
7.9 KiB
Python
"""Tests pour le module d'extraction."""
|
|
|
|
import pytest
|
|
|
|
from src.extraction.document_classifier import classify
|
|
from src.extraction.crh_parser import parse_crh
|
|
from src.extraction.trackare_parser import parse_trackare, _clean_person_name
|
|
|
|
|
|
class TestDocumentClassifier:
|
|
def test_classify_trackare(self):
|
|
text = """CENTRE HOSPITALIER COTE BASQUE
|
|
Dossier Patient
|
|
Détails des patients
|
|
Nom de naissance: CLIER IPP: 01306172
|
|
Détails épisode
|
|
Episode No: 23042753
|
|
Signes Vitaux"""
|
|
assert classify(text) == "trackare"
|
|
|
|
def test_classify_crh(self):
|
|
text = """N° Finess CENTRE HOSPITALIER COTE BASQUE
|
|
Pôle Spécialités Médicales
|
|
Service de Gastro-Entérologie
|
|
Mon cher confrère,
|
|
Votre patiente a été hospitalisée"""
|
|
assert classify(text) == "crh"
|
|
|
|
def test_classify_trackare_by_ipp(self):
|
|
text = "IPP: 12345678 Episode No: 87654321"
|
|
assert classify(text) == "trackare"
|
|
|
|
|
|
class TestCRHParser:
|
|
def test_parse_patient_info(self):
|
|
text = """MME NARBAIS AUDREY
|
|
MAISON IRREXELAIA
|
|
64430 ST ETIENNE DE BAIGORRY
|
|
|
|
Mon cher confrère,
|
|
Votre patiente NARBAIS Audrey née le 23/02/1980 a été hospitalisée
|
|
du 25/02/2023 au 03/03/2023 pour le motif suivant:
|
|
Pancréatite aiguë lithiasique"""
|
|
result = parse_crh(text)
|
|
|
|
assert result["patient"]["nom_complet"] == "NARBAIS AUDREY"
|
|
assert result["patient"]["sexe"] == "F"
|
|
assert result["patient"]["date_naissance"] == "23/02/1980"
|
|
|
|
def test_parse_sejour(self):
|
|
text = """Votre patiente née le 23/02/1980 a été hospitalisée
|
|
du 25/02/2023 au 03/03/2023 pour le motif suivant:
|
|
Pancréatite aiguë"""
|
|
result = parse_crh(text)
|
|
|
|
assert result["sejour"]["date_entree"] == "25/02/2023"
|
|
assert result["sejour"]["date_sortie"] == "03/03/2023"
|
|
|
|
def test_parse_medecins(self):
|
|
text = "Dr PUJOS. Dr F. AUDEMAR. Docteur DUTREY Sarah."
|
|
result = parse_crh(text)
|
|
|
|
assert any("PUJOS" in m for m in result["medecins"])
|
|
assert any("AUDEMAR" in m for m in result["medecins"])
|
|
|
|
|
|
class TestTrackareParser:
|
|
def test_parse_patient_info(self):
|
|
text = """Nom de naissance: CLIER IPP: 01306172
|
|
Nom et Prénom: NARBAIS AUDREY Date de naissance: 23/02/1980
|
|
Sexe: Féminin Lieu de naissance: CHAMPIGNY SUR MARNE
|
|
Adresse: MAISON IRREXELAIA QUARTIER AUZO TTIPI Ville de résidence: ST ETIENNE DE BAIGORRY
|
|
Code Postal: 64430
|
|
Episode No: 23042753
|
|
Date d'admission: 25/02/2023 Heure d'admission: 03:07
|
|
Date de sortie: 03/03/2023
|
|
Taille: 162 cm - Poids: 90.2 kg - IMC: 34.370"""
|
|
result = parse_trackare(text)
|
|
|
|
assert result["patient"]["nom_naissance"] == "CLIER"
|
|
assert result["patient"]["nom_prenom"] == "NARBAIS AUDREY"
|
|
assert result["patient"]["ipp"] == "01306172"
|
|
assert result["patient"]["sexe"] == "F"
|
|
assert result["patient"]["date_naissance"] == "23/02/1980"
|
|
assert result["patient"]["imc"] == 34.370
|
|
assert result["sejour"]["episode"] == "23042753"
|
|
assert result["sejour"]["date_entree"] == "25/02/2023"
|
|
|
|
def test_parse_diagnostics(self):
|
|
text = """Diagnostic aux urgences
|
|
Type Etat Code Date
|
|
Principal actif K80.5 Calcul des canaux biliaires (sans angiocholite ni cholécystite) [CMA2] 25/02/2023 05:27"""
|
|
result = parse_trackare(text)
|
|
|
|
assert len(result["diagnostics"]) >= 1
|
|
assert result["diagnostics"][0]["code_cim10"] == "K80.5"
|
|
assert result["diagnostics"][0]["type"] == "Principal"
|
|
|
|
def test_parse_vitals(self):
|
|
text = """Poids/Taille
|
|
Taille [cm] 162,00
|
|
Poids [kg] 90,20
|
|
Indice
|
|
de masse 34.370"""
|
|
result = parse_trackare(text)
|
|
|
|
assert result["signes_vitaux"]["taille_cm"] == 162.0
|
|
assert result["signes_vitaux"]["poids_kg"] >= 90.0
|
|
assert result["signes_vitaux"]["imc"] == 34.370
|
|
|
|
|
|
class TestCRHParserDiagSections:
|
|
"""Tests pour les nouvelles sections à fort signal DP."""
|
|
|
|
def test_parse_diag_sortie(self):
|
|
text = """Mon cher confrère,
|
|
Votre patient a été hospitalisé du 01/01/2024 au 05/01/2024.
|
|
|
|
Diagnostic de sortie :
|
|
Pancréatite aiguë biliaire (K85.1)
|
|
|
|
Traitement de sortie :
|
|
Paracétamol"""
|
|
result = parse_crh(text)
|
|
assert "diag_sortie" in result["sections"]
|
|
assert "K85.1" in result["sections"]["diag_sortie"]
|
|
|
|
def test_parse_diagnostics_retenus(self):
|
|
text = """Conclusion :
|
|
Bonne évolution.
|
|
|
|
Diagnostics retenus :
|
|
- Cholécystite aiguë lithiasique
|
|
- Lithiase vésiculaire
|
|
|
|
Traitement de sortie :
|
|
Paracétamol"""
|
|
result = parse_crh(text)
|
|
assert "diag_sortie" in result["sections"]
|
|
assert "Cholécystite" in result["sections"]["diag_sortie"]
|
|
|
|
def test_parse_diag_principal(self):
|
|
text = """Examen clinique :
|
|
Abdomen souple.
|
|
|
|
Diagnostic principal :
|
|
Embolie pulmonaire segmentaire droite
|
|
|
|
Diagnostics de sortie :
|
|
EP + TVP"""
|
|
result = parse_crh(text)
|
|
assert "diag_principal" in result["sections"]
|
|
assert "Embolie pulmonaire" in result["sections"]["diag_principal"]
|
|
|
|
def test_parse_probleme_principal(self):
|
|
text = """Examen clinique :
|
|
Patient stable.
|
|
|
|
Problème principal :
|
|
Insuffisance cardiaque décompensée
|
|
|
|
Devenir : retour à domicile."""
|
|
result = parse_crh(text)
|
|
assert "diag_principal" in result["sections"]
|
|
assert "Insuffisance cardiaque" in result["sections"]["diag_principal"]
|
|
|
|
def test_parse_synthese(self):
|
|
text = """Examen clinique :
|
|
RAS.
|
|
|
|
Synthèse :
|
|
Patient de 75 ans hospitalisé pour AVC ischémique sylvien droit.
|
|
|
|
Traitement de sortie :
|
|
Aspirine"""
|
|
result = parse_crh(text)
|
|
assert "synthese" in result["sections"]
|
|
assert "AVC" in result["sections"]["synthese"]
|
|
|
|
def test_existing_sections_preserved(self):
|
|
"""Les 7 sections existantes sont toujours capturées."""
|
|
text = """pour le motif suivant:
|
|
Pancréatite aiguë
|
|
|
|
Antécédents :
|
|
HTA, diabète
|
|
|
|
Histoire de la maladie
|
|
Douleur abdominale brutale
|
|
|
|
Examen clinique
|
|
Abdomen défense en HCD
|
|
|
|
Au total :
|
|
Pancréatite aiguë biliaire
|
|
|
|
TTT de sortie :
|
|
Paracétamol
|
|
|
|
Devenir :
|
|
Retour à domicile"""
|
|
result = parse_crh(text)
|
|
assert "motif_hospitalisation" in result["sections"]
|
|
assert "antecedents" in result["sections"]
|
|
assert "histoire_maladie" in result["sections"]
|
|
assert "examen_clinique" in result["sections"]
|
|
assert "conclusion" in result["sections"]
|
|
assert "traitement_sortie" in result["sections"]
|
|
assert "devenir" in result["sections"]
|
|
|
|
def test_diag_sortie_multiline(self):
|
|
text = """Au total :
|
|
Bonne évolution.
|
|
|
|
Diagnostic de sortie :
|
|
- Pancréatite aiguë biliaire K85.1
|
|
- Lithiase vésiculaire K80.2
|
|
- Obésité E66.0
|
|
|
|
Traitement de sortie :
|
|
Paracétamol"""
|
|
result = parse_crh(text)
|
|
assert "diag_sortie" in result["sections"]
|
|
section = result["sections"]["diag_sortie"]
|
|
assert "K85.1" in section
|
|
assert "K80.2" in section
|
|
assert "E66.0" in section
|
|
|
|
def test_conclusion_does_not_overflow_into_diag_sortie(self):
|
|
text = """Au total :
|
|
Pancréatite aiguë biliaire, évolution favorable.
|
|
|
|
Diagnostic de sortie :
|
|
Pancréatite aiguë biliaire K85.1
|
|
|
|
Traitement de sortie :
|
|
Paracétamol"""
|
|
result = parse_crh(text)
|
|
assert "conclusion" in result["sections"]
|
|
assert "diag_sortie" in result["sections"]
|
|
# La conclusion ne doit PAS contenir le texte de diag_sortie
|
|
assert "K85.1" not in result["sections"]["conclusion"]
|
|
|
|
|
|
class TestCleanPersonName:
|
|
def test_clean_simple(self):
|
|
assert _clean_person_name("Sarah DUTREY") == "Sarah DUTREY"
|
|
|
|
def test_clean_with_noise(self):
|
|
assert _clean_person_name("Sarah DUTREY une complication") == "Sarah DUTREY"
|
|
|
|
def test_clean_multiline(self):
|
|
assert _clean_person_name("Sarah\nDUTREY") == "Sarah DUTREY"
|
|
|
|
def test_clean_medical_term(self):
|
|
assert _clean_person_name("Bilirubine") == ""
|
|
|
|
def test_clean_empty(self):
|
|
assert _clean_person_name("") == ""
|