# 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()`

```javascript
// 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

```javascript
// 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**:
```javascript
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
```bash
# 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
```bash
# 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
```bash
# 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:

```python
# 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:
```javascript
// 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:
```javascript
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/`
