Ajout des règles d'exclusion symptôme (R00-R99) vs diagnostic précis (Chapitres I-XIV), détection heuristique de sévérité CMA sur 25 racines CIM-10, et affichage des alertes de codage dans le viewer Flask. 153 tests, 0 régression. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
124 lines
4.9 KiB
Python
124 lines
4.9 KiB
Python
"""Tests pour les règles d'exclusion diagnostique (symptôme vs diagnostic précis)."""
|
|
|
|
import pytest
|
|
|
|
from src.config import Diagnostic
|
|
from src.medical.exclusion_rules import (
|
|
is_symptom_code,
|
|
is_precise_diagnosis,
|
|
check_exclusions,
|
|
EXCLUSION_MAP,
|
|
)
|
|
|
|
|
|
class TestIsSymptomCode:
|
|
def test_r_codes(self):
|
|
assert is_symptom_code("R10.4") is True
|
|
assert is_symptom_code("R17") is True
|
|
assert is_symptom_code("R50.9") is True
|
|
|
|
def test_non_symptom(self):
|
|
assert is_symptom_code("K85.1") is False
|
|
assert is_symptom_code("I10") is False
|
|
assert is_symptom_code("E11.9") is False
|
|
|
|
def test_empty_none(self):
|
|
assert is_symptom_code("") is False
|
|
assert is_symptom_code(None) is False
|
|
|
|
|
|
class TestIsPreciseDiagnosis:
|
|
def test_precise(self):
|
|
assert is_precise_diagnosis("K85.1") is True
|
|
assert is_precise_diagnosis("A41.9") is True
|
|
assert is_precise_diagnosis("I10") is True
|
|
|
|
def test_not_precise(self):
|
|
assert is_precise_diagnosis("R10.4") is False
|
|
assert is_precise_diagnosis("Z03") is False # Chapitre XXI
|
|
|
|
|
|
class TestCheckExclusions:
|
|
def test_symptom_excluded_when_precise_present(self):
|
|
"""R10.4 (douleur abdo) exclu quand K85.1 (pancréatite) est présent."""
|
|
dp = Diagnostic(texte="Pancréatite aiguë biliaire", cim10_suggestion="K85.1")
|
|
das = [
|
|
Diagnostic(texte="Douleur abdominale", cim10_suggestion="R10.4"),
|
|
Diagnostic(texte="Obésité", cim10_suggestion="E66.0"),
|
|
]
|
|
result = check_exclusions(dp, das)
|
|
assert len(result.cleaned_das) == 1
|
|
assert result.cleaned_das[0].cim10_suggestion == "E66.0"
|
|
assert len(result.excluded) == 1
|
|
assert result.excluded[0].cim10_suggestion == "R10.4"
|
|
assert len(result.warnings) >= 1
|
|
|
|
def test_symptom_kept_when_alone(self):
|
|
"""R10.4 (douleur abdo) conservé si aucun diagnostic précis l'expliquant."""
|
|
dp = Diagnostic(texte="Douleur abdominale", cim10_suggestion="R10.4")
|
|
das = [
|
|
Diagnostic(texte="Hypertension", cim10_suggestion="I10"),
|
|
]
|
|
result = check_exclusions(dp, das)
|
|
assert len(result.cleaned_das) == 1
|
|
assert result.cleaned_das[0].cim10_suggestion == "I10"
|
|
assert len(result.excluded) == 0
|
|
|
|
def test_multiple_exclusions(self):
|
|
"""Plusieurs symptômes exclus par différents diagnostics précis."""
|
|
dp = Diagnostic(texte="Pancréatite aiguë", cim10_suggestion="K85.9")
|
|
das = [
|
|
Diagnostic(texte="Douleur abdominale", cim10_suggestion="R10.4"),
|
|
Diagnostic(texte="Nausées", cim10_suggestion="R11"),
|
|
Diagnostic(texte="Diabète type 2", cim10_suggestion="E11.9"),
|
|
]
|
|
result = check_exclusions(dp, das)
|
|
# R10.4 exclu par K85, R11 exclu par K85
|
|
assert len(result.excluded) == 2
|
|
assert len(result.cleaned_das) == 1
|
|
assert result.cleaned_das[0].cim10_suggestion == "E11.9"
|
|
|
|
def test_non_symptom_never_excluded(self):
|
|
"""Un diagnostic précis (non R-code) n'est jamais exclu."""
|
|
dp = Diagnostic(texte="Pancréatite aiguë", cim10_suggestion="K85.1")
|
|
das = [
|
|
Diagnostic(texte="Lithiase cholédoque", cim10_suggestion="K80.5"),
|
|
Diagnostic(texte="Cholécystite", cim10_suggestion="K81.0"),
|
|
]
|
|
result = check_exclusions(dp, das)
|
|
assert len(result.cleaned_das) == 2
|
|
assert len(result.excluded) == 0
|
|
|
|
def test_dp_symptom_triggers_warning(self):
|
|
"""Alerte si le DP est un symptôme alors qu'un diagnostic précis existe."""
|
|
dp = Diagnostic(texte="Douleur abdominale", cim10_suggestion="R10.4")
|
|
das = [
|
|
Diagnostic(texte="Pancréatite aiguë", cim10_suggestion="K85.1"),
|
|
]
|
|
result = check_exclusions(dp, das)
|
|
# Le DAS K85.1 n'est pas un symptôme, il est conservé
|
|
assert len(result.cleaned_das) == 1
|
|
# Mais un warning est émis sur le DP
|
|
assert any("ALERTE DP" in w for w in result.warnings)
|
|
|
|
def test_warnings_generated(self):
|
|
"""Vérifie que les warnings contiennent le texte et les codes."""
|
|
dp = Diagnostic(texte="Cholécystite", cim10_suggestion="K81.0")
|
|
das = [
|
|
Diagnostic(texte="Fièvre", cim10_suggestion="R50.9"),
|
|
]
|
|
result = check_exclusions(dp, das)
|
|
assert len(result.excluded) == 1
|
|
assert "R50.9" in result.warnings[0]
|
|
assert "K81.0" in result.warnings[0]
|
|
|
|
def test_ictere_excluded_by_lithiase(self):
|
|
"""R17 (ictère) exclu quand K80 (lithiase biliaire) est présent."""
|
|
dp = Diagnostic(texte="Lithiase cholédoque", cim10_suggestion="K80.5")
|
|
das = [
|
|
Diagnostic(texte="Ictère", cim10_suggestion="R17"),
|
|
]
|
|
result = check_exclusions(dp, das)
|
|
assert len(result.excluded) == 1
|
|
assert result.excluded[0].cim10_suggestion == "R17"
|