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:
@@ -6,6 +6,8 @@ from src.config import (
|
||||
ActeCCAM,
|
||||
Diagnostic,
|
||||
DossierMedical,
|
||||
DPCandidate,
|
||||
DPSelection,
|
||||
Sejour,
|
||||
Traitement,
|
||||
BiologieCle,
|
||||
@@ -491,3 +493,161 @@ class TestSemanticDedup:
|
||||
das_codes = {d.cim10_suggestion for d in result.diagnostics_associes}
|
||||
assert "I10" not in das_codes
|
||||
assert "I11.9" in das_codes
|
||||
|
||||
|
||||
class TestDPSelectionPropagation:
|
||||
"""Vérifie que dp_selection est propagée depuis le dossier source du DP retenu."""
|
||||
|
||||
def test_dp_selection_propagated_multi_dossier(self):
|
||||
"""Fusion 2 dossiers : dp_selection vient du dossier dont le DP est retenu."""
|
||||
sel = DPSelection(
|
||||
chosen_index=0,
|
||||
chosen_term="Pancréatite aiguë biliaire",
|
||||
chosen_code="K85.1",
|
||||
verdict="CONFIRMED",
|
||||
confidence="high",
|
||||
evidence=["Score 8.0 — source: regex (section forte)"],
|
||||
reason="Écart net",
|
||||
candidates=[DPCandidate(index=0, term="Pancréatite", code="K85.1",
|
||||
section_strength=3, confidence="high")],
|
||||
)
|
||||
d1 = DossierMedical(
|
||||
document_type="crh",
|
||||
diagnostic_principal=Diagnostic(texte="Pancréatite aiguë biliaire",
|
||||
cim10_suggestion="K85.1"),
|
||||
dp_selection=sel,
|
||||
)
|
||||
d2 = DossierMedical(
|
||||
document_type="trackare",
|
||||
diagnostic_principal=Diagnostic(texte="Lithiase vésiculaire",
|
||||
cim10_suggestion="K80.2"),
|
||||
)
|
||||
result = merge_dossiers([d1, d2])
|
||||
# DP = K85.1 (plus spécifique) → dp_selection propagée depuis d1
|
||||
assert result.diagnostic_principal.cim10_suggestion == "K85.1"
|
||||
assert result.dp_selection is not None
|
||||
assert result.dp_selection.chosen_code == "K85.1"
|
||||
assert result.dp_selection.verdict == "CONFIRMED"
|
||||
|
||||
def test_dp_selection_none_when_no_source(self):
|
||||
"""Si aucun dossier n'a de dp_selection, le fusionné non plus."""
|
||||
d1 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="HTA", cim10_suggestion="I10"),
|
||||
)
|
||||
d2 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="HTA", cim10_suggestion="I10"),
|
||||
)
|
||||
result = merge_dossiers([d1, d2])
|
||||
assert result.dp_selection is None
|
||||
|
||||
def test_dp_selection_single_dossier(self):
|
||||
"""Dossier unique : dp_selection est conservée via model_copy."""
|
||||
sel = DPSelection(
|
||||
chosen_index=0,
|
||||
chosen_term="Pneumopathie",
|
||||
chosen_code="J18.9",
|
||||
verdict="REVIEW",
|
||||
confidence="medium",
|
||||
)
|
||||
d1 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="Pneumopathie", cim10_suggestion="J18.9"),
|
||||
dp_selection=sel,
|
||||
)
|
||||
result = merge_dossiers([d1])
|
||||
assert result.dp_selection is not None
|
||||
assert result.dp_selection.verdict == "REVIEW"
|
||||
|
||||
def test_dp_selection_preserves_evidence_reason_verdict(self):
|
||||
"""Fusion multi-docs : evidence, reason et verdict sont préservés intégralement."""
|
||||
sel = DPSelection(
|
||||
chosen_index=0,
|
||||
chosen_term="Embolie pulmonaire",
|
||||
chosen_code="I26.9",
|
||||
verdict="CONFIRMED",
|
||||
confidence="high",
|
||||
evidence=[
|
||||
"Score 9.0 — source: edsnlp",
|
||||
"Diagnostic de sortie: «EP massive bilatérale»",
|
||||
"Delta +5.0 vs Thrombose (I80.2)",
|
||||
],
|
||||
reason="Écart score 5.0 >= seuil 3.0",
|
||||
candidates=[
|
||||
DPCandidate(index=0, term="Embolie pulmonaire", code="I26.9",
|
||||
section_strength=2, confidence="high", score=9.0,
|
||||
score_details={"section": 2, "confidence": 3, "diag_section_bonus": 4}),
|
||||
DPCandidate(index=1, term="Thrombose veineuse", code="I80.2",
|
||||
section_strength=1, confidence="high", score=4.0),
|
||||
],
|
||||
debug_scores={"top1": 9.0, "top2": 4.0, "delta": 5.0},
|
||||
)
|
||||
d1 = DossierMedical(
|
||||
document_type="crh",
|
||||
diagnostic_principal=Diagnostic(texte="Embolie pulmonaire", cim10_suggestion="I26.9"),
|
||||
dp_selection=sel,
|
||||
)
|
||||
d2 = DossierMedical(
|
||||
document_type="trackare",
|
||||
diagnostic_principal=Diagnostic(texte="TVP", cim10_suggestion="I80.2"),
|
||||
)
|
||||
result = merge_dossiers([d1, d2])
|
||||
|
||||
assert result.dp_selection is not None
|
||||
rs = result.dp_selection
|
||||
# Verdict/confidence/reason intacts
|
||||
assert rs.verdict == "CONFIRMED"
|
||||
assert rs.confidence == "high"
|
||||
assert "5.0" in rs.reason
|
||||
# Evidence complète (3 éléments)
|
||||
assert len(rs.evidence) == 3
|
||||
assert any("Diagnostic de sortie" in e for e in rs.evidence)
|
||||
assert any("Delta" in e for e in rs.evidence)
|
||||
# Candidates préservés avec score_details
|
||||
assert len(rs.candidates) == 2
|
||||
assert rs.candidates[0].score_details.get("diag_section_bonus") == 4
|
||||
# Debug scores
|
||||
assert rs.debug_scores["delta"] == 5.0
|
||||
|
||||
def test_dp_selection_from_second_dossier(self):
|
||||
"""Si le DP retenu vient du 2e dossier, sa dp_selection est prise."""
|
||||
sel_d2 = DPSelection(
|
||||
chosen_index=0,
|
||||
chosen_term="Sepsis",
|
||||
chosen_code="A41.9",
|
||||
verdict="CONFIRMED",
|
||||
confidence="high",
|
||||
evidence=["Score 7.0"],
|
||||
reason="Candidat unique",
|
||||
)
|
||||
d1 = DossierMedical(
|
||||
document_type="trackare",
|
||||
diagnostic_principal=Diagnostic(texte="HTA", cim10_suggestion="I10"),
|
||||
# Pas de dp_selection
|
||||
)
|
||||
d2 = DossierMedical(
|
||||
document_type="crh",
|
||||
diagnostic_principal=Diagnostic(texte="Sepsis à staphylocoque",
|
||||
cim10_suggestion="A41.9"),
|
||||
dp_selection=sel_d2,
|
||||
)
|
||||
result = merge_dossiers([d1, d2])
|
||||
# A41.9 (5 chars) > I10 (3 chars) → DP = A41.9 venant de d2
|
||||
assert result.diagnostic_principal.cim10_suggestion == "A41.9"
|
||||
assert result.dp_selection is not None
|
||||
assert result.dp_selection.chosen_code == "A41.9"
|
||||
assert result.dp_selection.verdict == "CONFIRMED"
|
||||
|
||||
def test_dp_selection_no_crash_empty_dossiers(self):
|
||||
"""Fusion de dossiers sans DP et sans dp_selection → pas de crash."""
|
||||
d1 = DossierMedical(
|
||||
diagnostics_associes=[
|
||||
Diagnostic(texte="HTA", cim10_suggestion="I10"),
|
||||
],
|
||||
)
|
||||
d2 = DossierMedical(
|
||||
diagnostics_associes=[
|
||||
Diagnostic(texte="Diabète", cim10_suggestion="E11.9"),
|
||||
],
|
||||
)
|
||||
result = merge_dossiers([d1, d2])
|
||||
assert result.dp_selection is None
|
||||
assert result.diagnostic_principal is None
|
||||
|
||||
Reference in New Issue
Block a user