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

251 lines
8.3 KiB
Markdown

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