251 lines
8.3 KiB
Markdown
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/`
|