Améliore la qualité du codage CIM-10 sur 3 axes : - Contexte clinique enrichi (interprétations bio, traitements indicatifs, marqueurs sévérité) - Preuves cliniques structurées par diagnostic (evidence linking dans le prompt LLM) - Validation batch post-codage (1 appel LLM/dossier, ajustement confiance, alertes QC) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
246 lines
8.9 KiB
Python
246 lines
8.9 KiB
Python
"""Tests pour la validation batch des justifications (QC post-codage)."""
|
|
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
import pytest
|
|
|
|
from src.config import (
|
|
Diagnostic,
|
|
DossierMedical,
|
|
PreuveClinique,
|
|
Sejour,
|
|
BiologieCle,
|
|
)
|
|
|
|
|
|
class TestPreuveClinique:
|
|
def test_create(self):
|
|
p = PreuveClinique(type="biologie", element="CRP 180 mg/L", interpretation="syndrome inflammatoire majeur")
|
|
assert p.type == "biologie"
|
|
assert p.element == "CRP 180 mg/L"
|
|
assert p.interpretation == "syndrome inflammatoire majeur"
|
|
|
|
def test_diagnostic_with_preuves(self):
|
|
d = Diagnostic(
|
|
texte="Pancréatite aiguë",
|
|
cim10_suggestion="K85.9",
|
|
preuves_cliniques=[
|
|
PreuveClinique(type="biologie", element="Lipasémie 450 UI/L", interpretation="pancréatite biologique"),
|
|
PreuveClinique(type="imagerie", element="TDM: pancréatite stade D", interpretation="confirmation"),
|
|
],
|
|
)
|
|
assert len(d.preuves_cliniques) == 2
|
|
assert d.preuves_cliniques[0].type == "biologie"
|
|
|
|
def test_diagnostic_default_empty_preuves(self):
|
|
d = Diagnostic(texte="Test")
|
|
assert d.preuves_cliniques == []
|
|
|
|
def test_serialization_round_trip(self):
|
|
d = Diagnostic(
|
|
texte="Test",
|
|
preuves_cliniques=[
|
|
PreuveClinique(type="clinique", element="fièvre 39°C", interpretation="syndrome infectieux"),
|
|
],
|
|
)
|
|
data = d.model_dump()
|
|
assert data["preuves_cliniques"][0]["type"] == "clinique"
|
|
d2 = Diagnostic(**data)
|
|
assert d2.preuves_cliniques[0].element == "fièvre 39°C"
|
|
|
|
|
|
class TestApplyLlmResultPreuves:
|
|
"""Teste le stockage des preuves cliniques dans _apply_llm_result_diagnostic."""
|
|
|
|
def test_preuves_stored(self):
|
|
from src.medical.rag_search import _apply_llm_result_diagnostic
|
|
|
|
diag = Diagnostic(texte="Pneumopathie")
|
|
llm_result = {
|
|
"code": "J18.9",
|
|
"confidence": "high",
|
|
"justification": "Pneumopathie confirmée",
|
|
"preuves_cliniques": [
|
|
{"type": "biologie", "element": "CRP 120 mg/L", "interpretation": "syndrome inflammatoire"},
|
|
{"type": "imagerie", "element": "Radio thorax: opacité", "interpretation": "foyer pulmonaire"},
|
|
],
|
|
}
|
|
_apply_llm_result_diagnostic(diag, llm_result)
|
|
assert len(diag.preuves_cliniques) == 2
|
|
assert diag.preuves_cliniques[0].type == "biologie"
|
|
assert diag.preuves_cliniques[1].element == "Radio thorax: opacité"
|
|
|
|
def test_preuves_empty_list(self):
|
|
from src.medical.rag_search import _apply_llm_result_diagnostic
|
|
|
|
diag = Diagnostic(texte="Test")
|
|
llm_result = {"code": "K85.9", "confidence": "medium", "preuves_cliniques": []}
|
|
_apply_llm_result_diagnostic(diag, llm_result)
|
|
assert diag.preuves_cliniques == []
|
|
|
|
def test_preuves_missing(self):
|
|
from src.medical.rag_search import _apply_llm_result_diagnostic
|
|
|
|
diag = Diagnostic(texte="Test")
|
|
llm_result = {"code": "K85.9", "confidence": "medium"}
|
|
_apply_llm_result_diagnostic(diag, llm_result)
|
|
assert diag.preuves_cliniques == []
|
|
|
|
def test_preuves_malformed_skipped(self):
|
|
from src.medical.rag_search import _apply_llm_result_diagnostic
|
|
|
|
diag = Diagnostic(texte="Test")
|
|
llm_result = {
|
|
"code": "K85.9",
|
|
"confidence": "high",
|
|
"preuves_cliniques": [
|
|
{"type": "bio"}, # manque 'element' → ignoré
|
|
{"type": "imagerie", "element": "TDM ok", "interpretation": "normal"},
|
|
"not a dict", # ignoré
|
|
],
|
|
}
|
|
_apply_llm_result_diagnostic(diag, llm_result)
|
|
assert len(diag.preuves_cliniques) == 1
|
|
assert diag.preuves_cliniques[0].element == "TDM ok"
|
|
|
|
|
|
class TestValidateJustifications:
|
|
"""Teste la fonction _validate_justifications."""
|
|
|
|
@patch("src.medical.ollama_client.call_ollama")
|
|
def test_confidence_adjusted(self, mock_ollama):
|
|
from src.medical.cim10_extractor import _validate_justifications
|
|
|
|
mock_ollama.return_value = {
|
|
"validations": [
|
|
{
|
|
"numero": 1,
|
|
"code": "K85.9",
|
|
"verdict": "maintenir",
|
|
"confidence_recommandee": "high",
|
|
"commentaire": "bien justifié",
|
|
},
|
|
{
|
|
"numero": 2,
|
|
"code": "I10",
|
|
"verdict": "maintenir",
|
|
"confidence_recommandee": "low",
|
|
"commentaire": "pas de preuve tensionnelle",
|
|
},
|
|
],
|
|
"alertes_globales": [],
|
|
}
|
|
|
|
dossier = DossierMedical(
|
|
sejour=Sejour(sexe="M", age=60),
|
|
diagnostic_principal=Diagnostic(
|
|
texte="Pancréatite aiguë",
|
|
cim10_suggestion="K85.9",
|
|
cim10_confidence="medium",
|
|
),
|
|
diagnostics_associes=[
|
|
Diagnostic(
|
|
texte="HTA",
|
|
cim10_suggestion="I10",
|
|
cim10_confidence="high",
|
|
),
|
|
],
|
|
)
|
|
|
|
_validate_justifications(dossier)
|
|
|
|
# DP: medium → high
|
|
assert dossier.diagnostic_principal.cim10_confidence == "high"
|
|
# DAS: high → low
|
|
assert dossier.diagnostics_associes[0].cim10_confidence == "low"
|
|
# Alertes de confiance
|
|
assert any("QC:" in a and "I10" in a for a in dossier.alertes_codage)
|
|
|
|
@patch("src.medical.ollama_client.call_ollama")
|
|
def test_das_supprimer_alerte(self, mock_ollama):
|
|
from src.medical.cim10_extractor import _validate_justifications
|
|
|
|
mock_ollama.return_value = {
|
|
"validations": [
|
|
{
|
|
"numero": 1,
|
|
"code": "K85.9",
|
|
"verdict": "maintenir",
|
|
"confidence_recommandee": "high",
|
|
"commentaire": "ok",
|
|
},
|
|
{
|
|
"numero": 2,
|
|
"code": "R10.4",
|
|
"verdict": "supprimer",
|
|
"confidence_recommandee": "low",
|
|
"commentaire": "symptôme couvert par le DP",
|
|
},
|
|
],
|
|
"alertes_globales": ["Vérifier la spécificité du DP"],
|
|
}
|
|
|
|
dossier = DossierMedical(
|
|
diagnostic_principal=Diagnostic(
|
|
texte="Pancréatite aiguë",
|
|
cim10_suggestion="K85.9",
|
|
cim10_confidence="high",
|
|
),
|
|
diagnostics_associes=[
|
|
Diagnostic(
|
|
texte="Douleur abdominale",
|
|
cim10_suggestion="R10.4",
|
|
cim10_confidence="medium",
|
|
),
|
|
],
|
|
)
|
|
|
|
_validate_justifications(dossier)
|
|
|
|
# Le DAS n'est pas supprimé automatiquement, mais une alerte est ajoutée
|
|
assert any("à reconsidérer" in a for a in dossier.alertes_codage)
|
|
assert any("Vérifier la spécificité" in a for a in dossier.alertes_codage)
|
|
|
|
@patch("src.medical.ollama_client.call_ollama")
|
|
def test_ollama_returns_none(self, mock_ollama):
|
|
from src.medical.cim10_extractor import _validate_justifications
|
|
|
|
mock_ollama.return_value = None
|
|
dossier = DossierMedical(
|
|
diagnostic_principal=Diagnostic(
|
|
texte="Test",
|
|
cim10_suggestion="K85.9",
|
|
cim10_confidence="high",
|
|
),
|
|
)
|
|
_validate_justifications(dossier)
|
|
assert dossier.alertes_codage == []
|
|
|
|
def test_no_diags(self):
|
|
from src.medical.cim10_extractor import _validate_justifications
|
|
|
|
dossier = DossierMedical()
|
|
_validate_justifications(dossier)
|
|
assert dossier.alertes_codage == []
|
|
|
|
@patch("src.medical.ollama_client.call_ollama")
|
|
def test_invalid_validation_nums_skipped(self, mock_ollama):
|
|
from src.medical.cim10_extractor import _validate_justifications
|
|
|
|
mock_ollama.return_value = {
|
|
"validations": [
|
|
{"numero": 0, "code": "X", "verdict": "supprimer", "confidence_recommandee": "low", "commentaire": "oob"},
|
|
{"numero": 99, "code": "Y", "verdict": "supprimer", "confidence_recommandee": "low", "commentaire": "oob"},
|
|
{"numero": "abc", "code": "Z", "verdict": "supprimer", "confidence_recommandee": "low", "commentaire": "type"},
|
|
],
|
|
"alertes_globales": [],
|
|
}
|
|
|
|
dossier = DossierMedical(
|
|
diagnostic_principal=Diagnostic(texte="T", cim10_suggestion="A00", cim10_confidence="high"),
|
|
)
|
|
_validate_justifications(dossier)
|
|
# Aucune modification, tous les numéros sont invalides
|
|
assert dossier.diagnostic_principal.cim10_confidence == "high"
|
|
assert dossier.alertes_codage == []
|