refactor: réorganisation référentiels, nouveaux modules extraction, nettoyage code obsolète

- Réorganisation data/referentiels/ : pdfs/, dicts/, user/ (structure unifiée)
- Fix badges "Source absente" sur page admin référentiels
- Ré-indexation COCOA 2025 (555 → 1451 chunks, couverture 94%)
- Fix VRAM OOM : embeddings forcés CPU via T2A_EMBED_CPU
- Nouveaux modules : document_router, docx_extractor, image_extractor, ocr_engine
- Module complétude (quality/completude.py + config YAML)
- Template DIM (synthèse dimensionnelle)
- Gunicorn config + systemd service t2a-viewer
- Suppression t2a_install_rag_cleanup/ (copie obsolète)
- Suppression scripts/ et scripts_t2a_v2/ (anciens benchmarks)
- Suppression 81 fichiers _doc.txt de test
- Cache Ollama : TTL configurable, corrections loader YAML
- Dashboard : améliorations templates (base, index, detail, cpam, validation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-03-07 16:48:10 +01:00
parent 2578afb6ff
commit 4e2b4bd946
210 changed files with 6939 additions and 22104 deletions

View File

@@ -104,7 +104,9 @@ _DAS_PATTERNS: list[tuple[str, str, str]] = [
(r"diabete\s+(?:sucre\s+)?(?:de\s+)?type\s+2|diabete\s+type\s*2", "Diabète de type 2", "E11.9"),
(r"diabete\s+(?:sucre\s+)?(?:de\s+)?type\s+1|diabete\s+type\s*1", "Diabète de type 1", "E10.9"),
(r"dyslipidemie|hypercholesterolemie", "Dyslipidémie", "E78.5"),
(r"denutrition|malnutrition", "Dénutrition", "E46"),
(r"denutrition\s+severe|malnutrition\s+severe|denutrition\s+grade\s+(?:3|iii|III)", "Dénutrition sévère", "E43"),
(r"denutrition\s+moderee?|malnutrition\s+moderee?|denutrition\s+grade\s+(?:2|ii|II)", "Dénutrition modérée", "E44.0"),
(r"denutrition|malnutrition|hypoalbuminemie\s+severe", "Dénutrition", "E46"),
# Infectieux
(r"pneumopathie|pneumonie", "Pneumopathie", "J18.9"),
(r"infection\s+urinaire|pyelonephrite", "Infection urinaire", "N39.0"),
@@ -271,6 +273,91 @@ def _find_diagnostics_associes(
return das
def _detect_nutrition_has2021(dossier: DossierMedical) -> None:
"""Détecte la dénutrition selon les critères HAS/FFN novembre 2021.
Logique déterministe basée sur données structurées (IMC + âge + albumine).
- Critère phénotypique : IMC < seuil (âge-dépendant)
- Critère de sévérité : albumine < 30 g/L → sévère, 30-35 → modéré
- Code final : max(sévérité IMC, sévérité albumine) → E43 ou E44.0
Ref: HAS/FFN nov 2021 « Diagnostic de la dénutrition chez l'enfant,
l'adulte, et la personne de 70 ans et plus »
"""
# 1. Vérifier qu'aucun code E40-E46 n'est déjà codé
existing_codes: set[str] = set()
if dossier.diagnostic_principal and dossier.diagnostic_principal.cim10_suggestion:
existing_codes.add(dossier.diagnostic_principal.cim10_suggestion)
for d in dossier.diagnostics_associes:
if d.cim10_suggestion:
existing_codes.add(d.cim10_suggestion)
for code in existing_codes:
if code.startswith(("E4",)) and code[:3] in ("E40", "E41", "E42", "E43", "E44", "E45", "E46"):
return # Déjà codé
# 2. Vérifier qu'on a un IMC (critère phénotypique obligatoire)
imc = dossier.sejour.imc if dossier.sejour else None
if imc is None:
return
age = dossier.sejour.age if dossier.sejour else None
# 3. Seuils IMC HAS 2021 (âge-dépendants)
if age is not None and age >= 70:
# Personne âgée ≥ 70 ans
if imc >= 22:
return # Au-dessus du seuil
imc_severe = imc < 20
imc_moderate = not imc_severe # 20 ≤ IMC < 22
else:
# Adulte 18-69 ans (ou âge inconnu → seuils adulte par défaut)
if imc >= 18.5:
return # Au-dessus du seuil
imc_severe = imc <= 17
imc_moderate = not imc_severe # 17 < IMC < 18.5
# 4. Critère de sévérité : albumine
albumine_val = None
for bio in dossier.biologie_cle:
if bio.test == "Albumine" and bio.valeur_num is not None:
if bio.quality != "discarded":
albumine_val = bio.valeur_num
break
albumine_severe = albumine_val is not None and albumine_val < 30
albumine_moderate = albumine_val is not None and 30 <= albumine_val < 35
# 5. Code final : max(sévérité IMC, sévérité albumine)
is_severe = imc_severe or albumine_severe
is_moderate = imc_moderate or albumine_moderate
if is_severe:
code = "E43"
label = "Dénutrition sévère"
elif is_moderate:
code = "E44.0"
label = "Dénutrition modérée"
else:
return # Ne devrait pas arriver vu les checks précédents
# 6. Construire l'alerte explicative
parts = []
if age is not None and age >= 70:
parts.append(f"IMC {imc} (seuil ≥70 ans : <22 modéré, <20 sévère)")
else:
parts.append(f"IMC {imc} (seuil adulte : <18.5 modéré, ≤17 sévère)")
if albumine_val is not None:
parts.append(f"Albumine {albumine_val} g/L (<30 sévère, 30-35 modéré)")
alerte = f"HAS 2021 — {label} ({code}) : {' ; '.join(parts)}"
dossier.diagnostics_associes.append(
Diagnostic(texte=label, cim10_suggestion=code, source="has2021")
)
dossier.alertes_codage.append(alerte)
logger.info("HAS 2021 dénutrition : %s ajouté (%s)", code, alerte)
def _extract_actes(text: str, dossier: DossierMedical) -> None:
"""Extrait les actes CCAM."""
text_lower = text.lower()