Files
aivanov_CIM/CORRECTIONS_HIGHLIGHTS_ET_PATIENT.md
2026-03-05 01:20:14 +01:00

8.3 KiB

Corrections Interface Web - Highlights et Infos Patient

Date

12 février 2026

Problèmes Identifiés

1. Highlights mal positionnés

Symptôme: Les mots/phrases surlignés ne correspondaient pas aux textes exacts dans le document (ex: "AROSCOPIE" au lieu de "LAPAROSCOPIE", "Diagnostic : cholécystite" coupé).

Cause racine: Le HighlightManager.applyHighlights() utilisait container.textContent pour extraire le contenu, ce qui normalise les espaces et les retours à la ligne, décalant ainsi toutes les positions de span.

Solution: Modifier DocumentsPanel.highlightCodeEvidence() pour passer le contenu original du document (depuis activeDocument.content) au lieu de laisser HighlightManager utiliser textContent.

2. Informations patient manquantes

Symptôme: L'en-tête patient affichait "Non renseigné" pour l'âge et le sexe.

Cause racine:

  1. L'API tim_api.py extrayait déjà correctement les infos depuis les documents (âge: 76, sexe: M, date de naissance: 1949-09-22)
  2. Le PatientHeader.render() utilisait une logique incorrecte pour récupérer l'âge: stay.age || this.calculateAge(...) qui retournait false si age === 0

Solution: Utiliser une vérification stricte stay.age !== null && stay.age !== undefined pour permettre l'âge 0.

Fichiers Modifiés

1. src/pipeline_mco_pmsi/api/static/js/components/documents-panel.js

Ligne modifiée: Méthode highlightCodeEvidence()

// AVANT
this.highlightManager.applyHighlights(contentContainer, documentEvidence, code.type);

// APRÈS
this.highlightManager.applyHighlights(
  contentContainer, 
  documentEvidence, 
  code.type,
  activeDocument.content  // Contenu original du document
);

Explication: Le 4ème paramètre originalContent est maintenant passé à applyHighlights() pour utiliser le contenu original du document au lieu du textContent normalisé du DOM.

2. src/pipeline_mco_pmsi/api/static/js/components/patient-header.js

Ligne modifiée: Méthode render(), calcul de l'âge

// AVANT
const age = stay.age || this.calculateAge(patient.birthDate, stay.age);

// APRÈS
const age = stay.age !== null && stay.age !== undefined ? stay.age : this.calculateAge(patient.birthDate);

Ajout de logs de debug:

console.log('PatientHeader.render() called with stay:', stay);
console.log('PatientHeader values:', {
    age: age,
    sex: patient.sex,
    bmi: bmi,
    duration: duration,
    anonymizedId: anonymizedId
});

Vérification

Test API

# Vérifier que l'API retourne les bonnes données
curl http://localhost:8001/stays/15_23096332/coding-proposal | jq '.age, .sex, .birth_date'
# Résultat attendu:
# 76
# "M"
# "1949-09-22"

Test Document

# Vérifier que le document a le bon contenu
curl http://localhost:8001/documents/15_23096332_DOC001 | jq '.content | length'
# Résultat attendu: 3425 caractères

Test Highlight Positioning

# Vérifier qu'une position de preuve correspond au texte
curl http://localhost:8001/documents/15_23096332_DOC001 | \
  python3 -c "import sys, json; data = json.load(sys.stdin); print(data['content'][523:599])"
# Résultat attendu: "    Diagnostic : cholécystite \n    Compte rendu opératoire du 18/05/2023 : \n"

Test Interface Web

  1. Ouvrir http://localhost:8001
  2. Entrer l'ID de séjour: 15_23096332
  3. Cliquer sur "Charger le séjour"
  4. Vérifier:
    • Infos patient: Âge: 76 ans, Sexe: M
    • Highlights: Les mots surlignés correspondent exactement aux textes dans le document
    • Colorisation: Couleurs douces et transparentes (bleu pour DP, vert pour DR, orange pour DAS, violet pour CCAM)

Test Automatisé

Ouvrir test_interface_corrections.html dans un navigateur pour exécuter les tests automatisés:

  • Test 1: Données patient depuis l'API
  • Test 2: Contenu du document
  • Test 3: Positionnement des highlights

Architecture de la Solution

Flux de données pour les highlights

1. API /stays/{stay_id}/coding-proposal
   └─> Retourne codes avec evidence (document_id, span.start, span.end)

2. API /documents/{document_id}
   └─> Retourne le contenu original du document (3425 chars)

3. index.html loadStay()
   └─> Charge les documents et crée l'objet stay avec documents[]

4. StateManager.setCurrentStay(stay)
   └─> Émet l'événement 'stayChanged'

5. DocumentsPanel.loadDocuments(stay)
   └─> Stocke stay.documents dans this.documents

6. DocumentsPanel.renderDocument(document)
   └─> Affiche le contenu dans #document-text-content

7. CodesPanel sélectionne un code
   └─> StateManager émet 'codeSelected'

8. DocumentsPanel.highlightCodeEvidence(code)
   └─> Appelle highlightManager.applyHighlights() avec:
       - container: #document-text-content
       - evidence: code.evidence filtrées par document_id
       - codeType: 'dp', 'dr', 'das', 'ccam'
       - originalContent: activeDocument.content ← CLEF!

9. HighlightManager.applyHighlights()
   └─> Utilise originalContent au lieu de container.textContent
   └─> Les positions span correspondent maintenant exactement

Flux de données pour les infos patient

1. API /stays/{stay_id}/coding-proposal
   └─> Extrait age, sex, birth_date depuis les documents
   └─> Retourne: { age: 76, sex: "M", birth_date: "1949-09-22", ... }

2. index.html loadStay()
   └─> Crée l'objet stay avec:
       - stay.age = data.age (76)
       - stay.patient.sex = data.sex ("M")
       - stay.patient.birthDate = data.birth_date ("1949-09-22")

3. StateManager.setCurrentStay(stay)
   └─> Émet l'événement 'stayChanged'

4. PatientHeader.render(stay)
   └─> Utilise stay.age !== null && stay.age !== undefined
   └─> Affiche correctement l'âge même si age === 0

Extraction des Infos Patient (Backend)

L'API tim_api.py extrait automatiquement les informations patient depuis les documents si elles ne sont pas disponibles dans la base:

# Extraire la date de naissance
birth_match = re.search(r'(?:Né|Date de naissance).*?(\d{2})/(\d{2})/(\d{4})', content, re.IGNORECASE)
if birth_match:
    day, month, year = birth_match.groups()
    birth_date = f"{year}-{month}-{day}"
    
    # Calculer l'âge
    if stay.admission_date:
        birth_dt = datetime.strptime(birth_date, "%Y-%m-%d")
        admission_dt = stay.admission_date
        age = admission_dt.year - birth_dt.year
        if (admission_dt.month, admission_dt.day) < (birth_dt.month, birth_dt.day):
            age -= 1

# Extraire le sexe
if re.search(r'\bMonsieur\b', content, re.IGNORECASE):
    sex = "M"
elif re.search(r'\bMadame\b', content, re.IGNORECASE):
    sex = "F"

Résultat Final

Highlights correctement positionnés: Les spans correspondent exactement aux positions dans le contenu original du document

Infos patient affichées: Âge: 76 ans, Sexe: M, Date de naissance: 22/09/1949

Colorisation douce: Couleurs transparentes avec bordures colorées pour une meilleure lisibilité

Performance: Pas de dégradation, le contenu original est déjà chargé en mémoire

Notes Techniques

Pourquoi textContent normalise les espaces?

Le DOM textContent normalise automatiquement:

  • Les espaces multiples → un seul espace
  • Les retours à la ligne → espaces
  • Les tabulations → espaces

Exemple:

// HTML
<div>Texte    avec\n\nespaces</div>

// textContent
"Texte avec espaces"  // Positions décalées!

// innerHTML (préservé)
"Texte    avec\n\nespaces"  // Positions correctes

Pourquoi age || calculateAge() ne fonctionnait pas?

En JavaScript, 0 || fallback retourne fallback car 0 est falsy:

const age = 0;
console.log(age || 100);  // 100 (incorrect!)
console.log(age !== null && age !== undefined ? age : 100);  // 0 (correct!)

Prochaines Étapes

  1. Highlights correctement positionnés
  2. Infos patient affichées
  3. 🔄 Tester avec d'autres séjours pour valider la robustesse
  4. 🔄 Ajouter des tests unitaires pour HighlightManager.applyHighlights()
  5. 🔄 Documenter l'API d'extraction des infos patient

Références

  • Fichier de test: test_interface_corrections.html
  • Documentation précédente: CORRECTIONS_INTERFACE_FINALE.md
  • Spec interface: .kiro/specs/amelioration-interface-tim/