fix: filtre DAS=DP + correction D55.9→D64.9 + enrichissement supplements CIM-10

- Filtre DAS identique au DP (violation règle PMSI) dans extracteur et fusion
- Correction automatique D55.9 → D64.9 pour "Anémie" non qualifiée (70 cas)
- 17 codes ajoutés aux supplements (K59.0, Z93.1, H92.0, A87.0, D64.9, etc.)
- 436 tests OK (+14 nouveaux)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-02-13 14:03:10 +01:00
parent 0d3cb83f12
commit 837bdaca76
7 changed files with 179 additions and 3 deletions

View File

@@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
from .cim10_dict import lookup as dict_lookup, normalize_text, normalize_code, validate_code as cim10_validate
from .ccam_dict import lookup as ccam_lookup, validate_code as ccam_validate
from .das_filter import clean_diagnostic_text, is_valid_diagnostic_text
from .das_filter import clean_diagnostic_text, is_valid_diagnostic_text, correct_known_miscodes
from ..config import (
ActeCCAM,
BiologieCle,
@@ -125,6 +125,9 @@ def extract_medical_info(
# Post-processing : validation des codes CIM-10 contre le dictionnaire
_validate_cim10(dossier)
# Post-processing : correction des codes systématiquement mal attribués
_apply_code_corrections(dossier)
# Post-processing : exclusions symptôme vs diagnostic précis
_apply_exclusion_rules(dossier)
@@ -134,6 +137,9 @@ def extract_medical_info(
# Post-processing : détection non-cumul actes CCAM
_apply_noncumul_rules(dossier)
# Post-processing : retirer DAS dont le code est identique au DP
_remove_das_equal_dp(dossier)
return dossier
@@ -855,6 +861,36 @@ def _apply_severity_rules(dossier: DossierMedical) -> None:
logger.warning("Erreur lors de l'évaluation de sévérité", exc_info=True)
def _apply_code_corrections(dossier: DossierMedical) -> None:
"""Corrige les codes CIM-10 systématiquement mal attribués par le LLM."""
all_diags = []
if dossier.diagnostic_principal:
all_diags.append(dossier.diagnostic_principal)
all_diags.extend(dossier.diagnostics_associes)
for diag in all_diags:
if not diag.cim10_suggestion:
continue
corrected = correct_known_miscodes(diag.cim10_suggestion, diag.texte)
if corrected:
logger.info(" Code corrigé : %s%s pour « %s »", diag.cim10_suggestion, corrected, diag.texte)
diag.cim10_suggestion = corrected
def _remove_das_equal_dp(dossier: DossierMedical) -> None:
"""Retire les DAS dont le code CIM-10 est identique au DP (violation règle PMSI)."""
dp_code = dossier.diagnostic_principal.cim10_suggestion if dossier.diagnostic_principal else None
if not dp_code:
return
before = len(dossier.diagnostics_associes)
dossier.diagnostics_associes = [
d for d in dossier.diagnostics_associes if d.cim10_suggestion != dp_code
]
removed = before - len(dossier.diagnostics_associes)
if removed:
logger.info(" DAS=DP : %d DAS retiré(s) (code %s identique au DP)", removed, dp_code)
def _apply_noncumul_rules(dossier: DossierMedical) -> None:
"""Détecte les incompatibilités de non-cumul entre actes CCAM."""
try:

View File

@@ -3,6 +3,16 @@
import re
import unicodedata
# Corrections de codes CIM-10 systématiquement mal attribués par le LLM
# D55.9 (anémie enzymatique) est proposé pour "Anémie" non qualifiée → D64.9
CODE_CORRECTIONS: dict[str, dict] = {
"D55.9": {
"correct_code": "D64.9",
"condition_texte": r"^an[ée]mie$", # uniquement si texte = "Anémie" seul
"reason": "Anémie non qualifiée → D64.9 (sans précision), pas D55.9 (enzymatique)",
},
}
def clean_diagnostic_text(text: str) -> str:
"""Nettoie un texte de diagnostic (newlines, ponctuation trailing, espaces)."""
@@ -74,3 +84,17 @@ def is_valid_diagnostic_text(text: str) -> bool:
return False
return True
def correct_known_miscodes(code: str, texte: str) -> str | None:
"""Corrige les codes CIM-10 systématiquement mal attribués par le LLM.
Returns:
Le code corrigé, ou None si pas de correction nécessaire.
"""
correction = CODE_CORRECTIONS.get(code)
if not correction:
return None
if re.match(correction["condition_texte"], texte.strip(), re.IGNORECASE):
return correction["correct_code"]
return None

View File

@@ -168,6 +168,13 @@ def merge_dossiers(dossiers: list[DossierMedical]) -> DossierMedical:
merged.diagnostics_associes = _dedup_diagnostics(all_das)
# Retirer les DAS dont le code est identique au DP (violation règle PMSI)
dp_code = merged.diagnostic_principal.cim10_suggestion if merged.diagnostic_principal else None
if dp_code:
merged.diagnostics_associes = [
d for d in merged.diagnostics_associes if d.cim10_suggestion != dp_code
]
# Actes CCAM
all_actes: list[ActeCCAM] = []
for d in dossiers: