tests: CRH sections + DP diag bonus + case 74 regression + fusion propagation
- test_extraction: +21 tests (sections diag_sortie/diag_principal/synthese, variantes titres, terminaisons, faux positifs mid-sentence, biosynthèse) - test_dp_selector: +55 tests (flags, candidates, scoring, hardening DIM, bonus +4/+2, evidence excerpt, cas 74 D50→I25.1 corrigé) - test_fusion: +39 tests (propagation dp_selection evidence/reason/verdict, source 2e dossier, pas de crash si aucun DP) - fixtures: case_74_min.json + 3 fixtures DP existantes Aucun mock utilisé — données synthétiques uniquement. Le test cas 74 passe : I25.1 gagne sur D50 grâce au bonus diag_sortie +4. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,285 @@ Pancréatite aiguë"""
|
||||
assert any("AUDEMAR" in m for m in result["medecins"])
|
||||
|
||||
|
||||
class TestCRHSectionsDiagnostic:
|
||||
"""Tests Patch 0 — nouvelles sections CRH à fort signal DP."""
|
||||
|
||||
def test_parse_diag_sortie(self):
|
||||
"""'Diagnostic de sortie :' capturé dans sections."""
|
||||
text = """Au total :
|
||||
Syndrome coronarien aigu traité par angioplastie.
|
||||
|
||||
Diagnostic de sortie :
|
||||
SCA ST+ antérieur I25.1
|
||||
|
||||
Traitement de sortie :
|
||||
Aspirine 100mg"""
|
||||
result = parse_crh(text)
|
||||
assert "diag_sortie" in result["sections"]
|
||||
assert "SCA" in result["sections"]["diag_sortie"]
|
||||
|
||||
def test_parse_diagnostics_retenus(self):
|
||||
"""'Diagnostics retenus :' capturé comme diag_sortie."""
|
||||
text = """Au total :
|
||||
Bilan.
|
||||
|
||||
Diagnostics retenus :
|
||||
- Embolie pulmonaire I26.9
|
||||
- Thrombose veineuse profonde I80.2
|
||||
|
||||
Devenir :
|
||||
Retour à domicile"""
|
||||
result = parse_crh(text)
|
||||
assert "diag_sortie" in result["sections"]
|
||||
assert "Embolie pulmonaire" in result["sections"]["diag_sortie"]
|
||||
|
||||
def test_parse_diagnostics_retenus_a_la_sortie(self):
|
||||
"""'Diagnostics retenus à la sortie :' capturé."""
|
||||
text = """Conclusion :
|
||||
Amélioration.
|
||||
|
||||
Diagnostics retenus à la sortie :
|
||||
Pneumopathie J18.9
|
||||
|
||||
TTT de sortie :
|
||||
Amoxicilline"""
|
||||
result = parse_crh(text)
|
||||
assert "diag_sortie" in result["sections"]
|
||||
assert "Pneumopathie" in result["sections"]["diag_sortie"]
|
||||
|
||||
def test_parse_diag_principal(self):
|
||||
"""'Diagnostic principal :' capturé."""
|
||||
text = """Examen clinique :
|
||||
Patient fébrile.
|
||||
|
||||
Diagnostic principal :
|
||||
Pancréatite aiguë biliaire K85.1
|
||||
|
||||
Diagnostics associés :
|
||||
Lithiase vésiculaire K80.2"""
|
||||
result = parse_crh(text)
|
||||
assert "diag_principal" in result["sections"]
|
||||
assert "Pancréatite" in result["sections"]["diag_principal"]
|
||||
|
||||
def test_parse_probleme_principal(self):
|
||||
"""'Problème principal :' capturé comme diag_principal."""
|
||||
text = """Antécédents :
|
||||
HTA, diabète.
|
||||
|
||||
Problème principal :
|
||||
Insuffisance cardiaque décompensée I50.0
|
||||
|
||||
Devenir :
|
||||
Hospitalisation prolongée"""
|
||||
result = parse_crh(text)
|
||||
assert "diag_principal" in result["sections"]
|
||||
assert "Insuffisance cardiaque" in result["sections"]["diag_principal"]
|
||||
|
||||
def test_parse_synthese(self):
|
||||
"""'Synthèse :' capturé."""
|
||||
text = """Au total :
|
||||
Bilan complet.
|
||||
|
||||
Synthèse :
|
||||
Patient de 65 ans admis pour SCA. Angioplastie réalisée.
|
||||
|
||||
Traitement de sortie :
|
||||
Brilique"""
|
||||
result = parse_crh(text)
|
||||
assert "synthese" in result["sections"]
|
||||
assert "SCA" in result["sections"]["synthese"]
|
||||
|
||||
def test_existing_sections_preserved(self):
|
||||
"""Les 7 sections existantes sont toujours captées (non-régression)."""
|
||||
text = """pour le motif suivant:
|
||||
Douleur abdominale
|
||||
|
||||
Antécédents :
|
||||
HTA traitée
|
||||
|
||||
Histoire de la maladie :
|
||||
Douleur depuis 3 jours
|
||||
|
||||
Examen clinique :
|
||||
Abdomen souple
|
||||
|
||||
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_conclusion_does_not_overflow_into_diag_sortie(self):
|
||||
"""La section conclusion s'arrête avant 'Diagnostic de sortie'."""
|
||||
text = """Au total :
|
||||
Bilan complet réalisé. Évolution favorable.
|
||||
|
||||
Diagnostic de sortie :
|
||||
SCA ST+ antérieur"""
|
||||
result = parse_crh(text)
|
||||
assert "conclusion" in result["sections"]
|
||||
assert "diag_sortie" in result["sections"]
|
||||
# La conclusion ne doit PAS contenir le texte du diagnostic de sortie
|
||||
assert "SCA" not in result["sections"]["conclusion"]
|
||||
assert "SCA" in result["sections"]["diag_sortie"]
|
||||
|
||||
|
||||
class TestCRHSectionsRobustness:
|
||||
"""Tests robustesse Patch 0 — variantes de titres, terminaisons, cas pièges."""
|
||||
|
||||
# --- A1 : Variantes de titres ---
|
||||
|
||||
def test_diagnostics_de_sortie_plural(self):
|
||||
"""'Diagnostics de sortie' (pluriel) capturé."""
|
||||
text = "Diagnostics de sortie :\nSCA I25.1\n\nDevenir :\nRAD"
|
||||
result = parse_crh(text)
|
||||
assert "diag_sortie" in result["sections"]
|
||||
assert "SCA" in result["sections"]["diag_sortie"]
|
||||
|
||||
def test_diagnostics_retenus_en_sortie(self):
|
||||
"""'Diagnostics retenus en sortie' capturé."""
|
||||
text = "Diagnostics retenus en sortie :\nPneumopathie J18.9\n\nDevenir :\nRAD"
|
||||
result = parse_crh(text)
|
||||
assert "diag_sortie" in result["sections"]
|
||||
assert "Pneumopathie" in result["sections"]["diag_sortie"]
|
||||
|
||||
def test_en_resume(self):
|
||||
"""'En résumé' capturé comme synthese."""
|
||||
text = "En résumé :\nPatient opéré avec succès.\n\nTraitement de sortie :\nParacétamol"
|
||||
result = parse_crh(text)
|
||||
assert "synthese" in result["sections"]
|
||||
assert "opéré" in result["sections"]["synthese"]
|
||||
|
||||
def test_en_synthese(self):
|
||||
"""'En synthèse' capturé comme synthese."""
|
||||
text = "En synthèse :\nAmélioration clinique rapide.\n\nDevenir :\nRAD"
|
||||
result = parse_crh(text)
|
||||
assert "synthese" in result["sections"]
|
||||
assert "Amélioration" in result["sections"]["synthese"]
|
||||
|
||||
def test_titre_avec_deux_points_colles(self):
|
||||
"""'Diagnostic principal:' (sans espace avant ':') fonctionne."""
|
||||
text = "Diagnostic principal:\nPancréatite aiguë K85.1\n\nDevenir :\nRAD"
|
||||
result = parse_crh(text)
|
||||
assert "diag_principal" in result["sections"]
|
||||
assert "Pancréatite" in result["sections"]["diag_principal"]
|
||||
|
||||
def test_titre_synthese_sans_deux_points(self):
|
||||
"""'Synthèse' suivi d'un saut de ligne (sans ':') fonctionne."""
|
||||
text = "Synthèse\nBilan favorable, retour à domicile.\n\nDevenir :\nRAD"
|
||||
result = parse_crh(text)
|
||||
assert "synthese" in result["sections"]
|
||||
assert "favorable" in result["sections"]["synthese"]
|
||||
|
||||
# --- A2 : Terminaisons correctes ---
|
||||
|
||||
def test_antecedents_stop_before_diag_principal(self):
|
||||
"""Antécédents s'arrêtent avant 'Diagnostic principal'."""
|
||||
text = """Antécédents :
|
||||
HTA traitée depuis 10 ans.
|
||||
Diabète type 2.
|
||||
|
||||
Diagnostic principal :
|
||||
Embolie pulmonaire I26.9"""
|
||||
result = parse_crh(text)
|
||||
assert "antecedents" in result["sections"]
|
||||
assert "diag_principal" in result["sections"]
|
||||
assert "Embolie" not in result["sections"]["antecedents"]
|
||||
assert "Embolie" in result["sections"]["diag_principal"]
|
||||
|
||||
def test_histoire_maladie_stop_before_synthese(self):
|
||||
"""Histoire de la maladie s'arrête avant 'Synthèse'."""
|
||||
text = """Histoire de la maladie :
|
||||
Douleur abdominale depuis 3 jours.
|
||||
|
||||
Synthèse :
|
||||
Pancréatite aiguë biliaire confirmée."""
|
||||
result = parse_crh(text)
|
||||
assert "histoire_maladie" in result["sections"]
|
||||
assert "synthese" in result["sections"]
|
||||
assert "confirmée" not in result["sections"]["histoire_maladie"]
|
||||
|
||||
def test_examen_clinique_stop_before_diag_sortie(self):
|
||||
"""Examen clinique s'arrête avant 'Diagnostic de sortie'."""
|
||||
text = """Examen clinique :
|
||||
Abdomen souple, pas de défense.
|
||||
|
||||
Diagnostic de sortie :
|
||||
Cholécystite aiguë K81.0"""
|
||||
result = parse_crh(text)
|
||||
assert "examen_clinique" in result["sections"]
|
||||
assert "diag_sortie" in result["sections"]
|
||||
assert "Cholécystite" not in result["sections"]["examen_clinique"]
|
||||
|
||||
def test_diag_sortie_multiline_not_truncated(self):
|
||||
"""Section diag_sortie multi-lignes : contenu complet capturé."""
|
||||
text = """Diagnostic de sortie :
|
||||
- SCA ST+ antérieur I25.1
|
||||
- Anémie ferriprive D50
|
||||
- HTA essentielle I10
|
||||
|
||||
Traitement de sortie :
|
||||
Aspirine 100mg"""
|
||||
result = parse_crh(text)
|
||||
assert "diag_sortie" in result["sections"]
|
||||
content = result["sections"]["diag_sortie"]
|
||||
assert "I25.1" in content
|
||||
assert "D50" in content
|
||||
assert "I10" in content
|
||||
assert len(content) > 0
|
||||
|
||||
def test_diag_principal_stop_before_diag_associes(self):
|
||||
"""'Diagnostic principal' s'arrête avant 'Diagnostics associés'."""
|
||||
text = """Diagnostic principal :
|
||||
Pancréatite aiguë biliaire K85.1
|
||||
|
||||
Diagnostics associés :
|
||||
Lithiase vésiculaire K80.2"""
|
||||
result = parse_crh(text)
|
||||
assert "diag_principal" in result["sections"]
|
||||
content = result["sections"]["diag_principal"]
|
||||
assert "Pancréatite" in content
|
||||
assert "Lithiase" not in content
|
||||
|
||||
# --- A3 : Cas pièges (faux positifs) ---
|
||||
|
||||
def test_diagnostic_in_phrase_not_captured(self):
|
||||
"""'diagnostic' dans une phrase courante ne déclenche PAS une section."""
|
||||
text = """Au total :
|
||||
Pas de diagnostic retenu pour l'instant. Bilan complémentaire en cours.
|
||||
|
||||
Devenir :
|
||||
Consultation dans 3 semaines"""
|
||||
result = parse_crh(text)
|
||||
# "diag_sortie" et "diag_principal" ne doivent PAS apparaître
|
||||
assert "diag_sortie" not in result["sections"]
|
||||
assert "diag_principal" not in result["sections"]
|
||||
# Mais conclusion doit capturer le texte
|
||||
assert "conclusion" in result["sections"]
|
||||
|
||||
def test_synthese_in_word_not_captured(self):
|
||||
"""'synthèse' dans un mot composé ('biosynthèse') ne déclenche PAS la section."""
|
||||
text = """Au total :
|
||||
Déficit de biosynthèse hépatique probable.
|
||||
|
||||
Devenir :
|
||||
Surveillance"""
|
||||
result = parse_crh(text)
|
||||
assert "synthese" not in result["sections"]
|
||||
assert "conclusion" in result["sections"]
|
||||
|
||||
|
||||
class TestTrackareParser:
|
||||
def test_parse_patient_info(self):
|
||||
text = """Nom de naissance: CLIER IPP: 01306172
|
||||
|
||||
Reference in New Issue
Block a user