Système d'Analyse des Tendances des Indicateurs de Bilan de Santé
Objectif
Analyser les bilans de santé pluriannuels (PDF électroniques, scans, photos) en les convertissant en séries temporelles d'indicateurs standardisés. Produit un rapport d'analyse de tendances et un tableau de bord interactif de gestion de la santé. Traitement entièrement local, anonymisation automatique, extensible dynamiquement (ajout d'un nouveau rapport en réexécutant le pipeline).
Mots de déclenchement
- Bilans de santé des années précédentes / bilans pluriannuels / bilans des années passées
- Numérisation des bilans de santé / extraction des données de bilan / analyse des bilans
- Tendances des indicateurs de bilan / analyse des tendances d'indicateurs
- Comparaison des indicateurs sur les années / comparaison annuelle / évolution annuelle
- Analyse des résultats de laboratoire / interprétation des analyses / analyse des rapports de laboratoire
- Scan de bilan de santé / bilan de santé scanné / OCR de scan
- Photo de bilan de santé / bilan de santé photographié / bilan pris en photo
- Tableau de bord de gestion de la santé / tableau de bord de données de santé / tableau de bord visuel
- Tendance de la glycémie / variation de la glycémie / acide urique élevé / tendance des lipides / tendance de la tension
- Indicateurs anormaux du bilan / interprétation des anomalies / indicateurs anormaux
- Ajout d'un nouveau bilan / intégration d'un nouveau rapport / ajout d'une nouvelle année de bilan
- Anonymisation des données de bilan / jeu de données anonymisé / version anonymisée / jeu de données partageable
Préparation de l'environnement
# Dépendances (ignorer si déjà installées) : pymupdf est obligatoire ; Pillow (pour les photos), pyobjc-framework-Vision/Quartz (pour l'OCR, macOS uniquement) sont optionnels
export REPORT_DIR=/chemin/vers/repertoire-bilans # Répertoire des bilans (contenant les PDF ou les dossiers annuels + JPG)
export WORK_DIR=/chemin/vers/repertoire-travail # Répertoire de travail (création automatique de data/ et output/)
export PY=/chemin/vers/python # Utiliser l'interpréteur Python avec les dépendances
Étape 0 (obligatoire au début de chaque nouvelle session) :
$PY scripts/check_deps.py # Vérification rapide : ce qui manque, quels scripts sont affectés, comment installer
$PY scripts/check_deps.py --json # Sortie machine, pour l'automatisation
Code de sortie 0 = dépendances obligatoires présentes, prêt à travailler ; 1 = dépendances obligatoires manquantes (le flux principal ne peut pas être exécuté, suivre les instructions pour installer) ; les dépendances optionnelles manquantes n'affectent que le format d'entrée correspondant, ne bloquent pas le chemin principal pour les PDF électroniques.
Flux d'exécution
Tous les scripts supportent --help ; les paramètres prévalent sur les variables d'environnement ; exécuter sans paramètre = utiliser le comportement par défaut des variables d'environnement.
- Identifier le format d'entrée : Parcourir
REPORT_DIR- Fichiers
*.pdfà la racine avec calque de texte → version électronique (traité parparse_reports.py) - Fichiers
*.pdfà la racine sans calque de texte → scan (traité parvision_ocr.py+extract.py) - Sous-dossiers annuels
YYYY体检报告/*.JPG→ version photo (fusionné parmake_year_pdfs.py→ OCR →extract.py)
- Fichiers
- Analyser le PDF électronique (méthode de repérage des coordonnées, voir
references/解析与口径.mdpour les limites des colonnes) :$PY scripts/parse_reports.py # → data/reports_raw.json $PY scripts/parse_reports.py --year 2027 --dry-run # Test pour une seule année, sans écrire sur le disque - Fusionner les photos en PDF + OCR + extraction (si des répertoires JPG ou des scans existent) :
$PY scripts/make_year_pdfs.py --year 2015-2020 # Plusieurs JPG d'une année → un seul PDF (correction automatique de rotation des photos en mode paysage) $PY scripts/vision_ocr.py <pdf> data/ocr_2022.json # OCR du scan/photo → JSON avec coordonnées $PY scripts/extract.py --year 2022 # Résultat OCR → indicateurs (stratégie automatique) $PY scripts/extract.py --years 2015-2020 # OCR de type dict pour plusieurs années (abréviations + plages de référence déduites)extract.pyparamétré par année, pas besoin de modifier le code pour une nouvelle année : placer dansdata/ocr_<année>.jsonpuis exécuter directement--year <année>. Stratégie par défautauto(si le JSON d'entrée est un dict au niveau supérieur →abbr, s'il est une liste →row), utiliser--strategypour forcer. - Normaliser + construire le jeu de données (mappage des noms/unités, filtrage des données sales, injection automatique de l'OCR, génération de la version anonymisée) :
Produit :$PY scripts/build_dataset.pydata/dataset_std.json(version complète, locale) +data/anonymized/anonymized_dataset_anon_shareable.json(version anonymisée, sous-répertoire dédié + lecture seule 444 + partageable) - Analyse des tendances (détection des anomalies selon la plage de référence de l'année, variations significatives ≥20% ou ≥30% de la largeur de la plage, amélioration/détérioration, focalisation sur les 3 dernières années) :
$PY scripts/trend_analysis.py # Par défaut, 3 dernières années $PY scripts/trend_analysis.py --focus-years 2020,2023,2026 - Génération des livrables :
$PY scripts/report_generator.py # output/Rapport_analyse_tendances_bilans_sante.md (par défaut avec anonymisation du texte) $PY scripts/build_dashboard.py # output/health_dashboard.html (fichier unique hors ligne)
Guide d'ajout d'un nouveau rapport
Pour ajouter un nouveau bilan de santé d'une année, suivre la branche correspondant au format (voir references/Nouveau_rapport_guide_ajout.md) :
- Placer le rapport : mettre un
*.pdfà la racine deREPORT_DIR, ou créer un sous-dossierYYYY体检报告/avec des JPG - Identifier le format : vérifier le calque de texte du PDF (
len(get_text()) > 0= version électronique)- Version électronique → exécuter directement
parse_reports.py; en cas d'anomalie de colonne, imprimer d'abord un échantillon de coordonnées sur 1 page - Scan/photo →
make_year_pdfs.py→vision_ocr.py→extract.py --year <année>(ne pas créer de nouveau script par année)
- Version électronique → exécuter directement
- Compléter le dictionnaire (si des indicateurs ne sont pas encore répertoriés) : ajouter une ligne de mappage dans
NAME_MAPdeindicator_dict.py(voir le journaldata/unrecognized)$PY scripts/indicator_dict.py --search 尿酸 # Vérifier si déjà répertorié (y compris les alias) $PY scripts/indicator_dict.py --validate # Auto-vérification de l'intégrité du dictionnaire (alias orphelins/clés en double) $PY scripts/indicator_dict.py --count # Nombre total d'indicateurs standard actuels - Réexécuter le pipeline : build_dataset → trend_analysis → report_generator → build_dashboard
- Validation : vérifier que la nouvelle année apparaît dans
trend_analysis.json["years"]et dans la chronologie du tableau de bord ; vérifier les valeurs de 3 indicateurs clés par rapport au rapport ; générer et vérifier la version anonymisée
Règles clés
- Contrainte stricte de confidentialité (tolérance zéro) : entièrement local, aucune appels réseau sortant dans la compétence ; à la fin de la tâche, la version anonymisée doit être générée et vérifiée (sans nom/numéro de pièce d'identité/téléphone/adresse/hôpital), partager uniquement
data/anonymized/anonymized_dataset_anon_shareable.json(isolé en répertoire distinct de la version complètedataset_std.json, lecture seule 444, pour éviter un partage accidentel de la version complète) - Vérification de l'anonymisation avant écriture :
_write_anon_verified()debuild_dataset.pyest le seul point de sortie pour la version anonymisée — vérification en mémoire d'abord, écriture sur disque seulement après validation. La version anonymisée est mise en lecture seule 444 dès l'écriture. Si la vérification avait lieu après l'écriture, un fichier contenant des PII serait déjà sur le disque et ne pourrait pas être modifié, le[FAIL]ne serait qu'une alerte a posteriori. Interdiction de contourner ce point de sortie en appelant directement_write_anon();--skip-verifyest réservé au débogage, les livrables ne doivent pas être partagés - Anonymisation du texte du rapport :
mask_identity_textdereport_generator.pyest activé par défaut (masquage des noms d'établissements, noms des médecins, numéros de téléphone, numéros de pièce d'identité) ;--no-maskdésactive, réservé à la vérification locale du texte original, ne pas partager les livrables - Ajout d'un nouveau rapport annuel : placer dans
REPORT_DIR→ réexécuter les étapes 2 à 6 ; les indicateurs non répertoriés sont enregistrés dansdata/unrecognized, ajouter une ligne dansNAME_MAPdeindicator_dict.py - Positionnement des données OCR : les indicateurs extraits par OCR sont utilisés uniquement pour l'affichage graphique (marqués en gris Ⓞ), ne participent pas à la détection des variations significatives pour éviter de contaminer les statistiques
- Focalisation des tendances : la tendance globale et la comparaison début/fin sont basées uniquement sur les années focales (par défaut les 3 dernières années, peut être modifié par
--focus-years), les données historiques ne servent que de contexte d'affichage - Cohérence des plages de référence : la détection des anomalies utilise la plage de référence du rapport de l'année concernée (adaptation automatique aux changements de réactif, par exemple la bilirubine directe passe de 0.0–4.0 à 1.7–6.8 à partir de 2025)
Livrables
output/Rapport_analyse_tendances_bilans_sante.md— Tableau récapitulatif des indicateurs, surlignage des anomalies, interprétation des autorités, recommandations par niveau (mode de vie/alimentation/exercice/consultation médicale), méthodologieoutput/health_dashboard.html— Vue d'ensemble de la santé, filtres par amélioration/détérioration, graphiques linéaires avec plages de référence, sélection de la période (fichier unique, aucune dépendance externe)data/anonymized/anonymized_dataset_anon_shareable.json— Jeu de données anonymisé partageable
Tableau des scripts
| Script | Objectif | Paramètres principaux |
|---|---|---|
check_deps.py | Auto-vérification des dépendances (Étape 0) | --json --quiet |
parse_reports.py | Analyse des PDF électroniques | --report-dir --data-dir --year --out --dry-run |
make_year_pdfs.py | Fusion JPG annuels en PDF | --report-dir --year --out-dir --dry-run |
vision_ocr.py | OCR des scans/photos | pdf out (paramètres positionnels) --dpi --quiet |
extract.py | Résultat OCR → indicateurs (paramétré) | --year --strategy --ocr-json --out --data-dir --quiet |
build_dataset.py | Normalisation + version anonymisée | --work-dir --data-dir --out --skip-verify --dry-run |
trend_analysis.py | Détection de tendances | --work-dir --focus-years --out --dry-run |
report_generator.py | Rapport Markdown | --work-dir --out --no-mask --dry-run |
build_dashboard.py | Tableau de bord HTML | --work-dir --data-dir --out --quiet |
indicator_dict.py | Dictionnaire des indicateurs (importé) | --list --search --count --validate --category (utilisé uniquement en exécution directe) |
Code de sortie uniforme à trois états : 0 succès / 1 échec à l'exécution / 2 paramètres ou prérequis non satisfaits.
Documents de référence
| Document | Contenu |
|---|---|
references/Nouveau_rapport_guide_ajout.md | Procédure complète d'ajout d'un nouveau rapport (format ou année) et dépannage |
references/解析与口径.md | Coordonnées des colonnes des PDF électroniques, définitions des plages de référence |
references/OCR_methodologie.md | Stratégie OCR pour scans/photos, maintenance du tableau de mappage des abréviations |
evals/ | 8 tests de régression (dont 3 lignes de défense de confidentialité), eval_loop.py --run pour exécuter |
Exemple d'utilisation (session complète)
Utilisateur : « Ajoute le bilan de santé 2027 et mets à jour l'analyse de tendances. »
- Étape 0 :
python3 scripts/check_deps.py→ dépendances obligatoires présentes - Vérifier
REPORT_DIR: nouveau fichier trouvéBilan_sante_270510004567.pdf(électronique, calque de texte complet) - Échantillon pour confirmer les limites des colonnes : imprimer les coordonnées d'une page, cohérent avec
references/解析与口径.md→ aucun ajustement nécessaire - Exécuter le pipeline :
export REPORT_DIR=Bilans_Sante WORK_DIR=/chemin/vers/travail python3 scripts/parse_reports.py python3 scripts/build_dataset.py python3 scripts/trend_analysis.py python3 scripts/report_generator.py python3 scripts/build_dashboard.py - Le journal d'analyse montre : 98 indicateurs pour la nouvelle année, 1 nom non reconnu →
indicator_dict.py --searchconfirme qu'il n'est pas répertorié, ajouter 1 ligne dansNAME_MAPpuis réexécuter build_dataset - Validation :
trend_analysis.json["years"]contient 2027 ; la chronologie du tableau de bord affiche 2027 ; vérifier les valeurs de la glycémie, des lipides, de l'IMC par rapport au rapport ;grepsur la version anonymisée ne trouve aucune information personnelle - Livrables : donner le chemin du rapport et du tableau de bord à l'utilisateur ; expliquer les variations et recommandations pour chaque indicateur anormal en 2027
Discipline de réduction des coûts (obligatoire lors de l'exécution)
- Échantillon d'abord : avant d'analyser un nouveau format, imprimer d'abord un échantillon de coordonnées d'une page pour confirmer les limites des colonnes, puis traiter la totalité (éviter de déverser l'intégralité du fichier)
- Compression des sorties : les commandes de débogage doivent être uniformément limitées par
| head -N/python -cpour une extraction précise, éviter de fairecatd'un gros fichier JSON entier - Isolation des sous-agents : la lecture de gros fichiers / la détection de format doivent être confiées au sous-agent Explore, le thread principal ne reçoit que la conclusion
- La validation ne fait qu'une seule capture d'écran finale, les itérations intermédiaires utilisent des vérifications DOM/JS
Journal des modifications
| Version | Date | Changement |
|---|---|---|
| 1.1.0 | 2026-08-29 | Réorganisation centralisée de l'ingénierie : ① Tous les 10 scripts supportent --help + argparse + code de sortie à trois états (avant : 0/10, pire score) ; ② Nouveau check_deps.py pour l'auto-vérification des dépendances, ajouté à l'Étape 0 ; ③ Fusion des scripts codés en dur par année extract_2015_2020.py / extract_2022.py en un seul script paramétré extract.py --year (ajout de nouvelle année sans modifier le code, format de sortie identique champ par champ aux anciens scripts) ; ④ Nouveau chapitre ## Mots de déclenchement avec 12 entrées + 8 tests de régression dans evals/ (dont 3 cas de test de la ligne de défense de confidentialité) ; ⑤ Correction de confidentialité : la version anonymisée passe de « écriture puis vérification » à « vérification en mémoire, puis écriture seulement si validée » (auparavant, l'écriture avait lieu avant la vérification, donc un fichier avec des PII était déjà sur le disque et en lecture seule, le [FAIL] n'était qu'une alerte a posteriori) ; ⑥ Correction du code mort dans trend_analysis.py où les éléments de mesure étaient perdus (measurement_items était réinitialisé après ajout, changement pour dédoublonner par clé) |
| 1.0.3 | 2026-08-23 | Correction P2 M-1 (Sécurité de la compétence) : Isolation renforcée de la version anonymisée — anonymized_dataset.json → data/anonymized/anonymized_dataset_anon_shareable.json (sous-répertoire dédié + lecture seule 444 + marquage _anon_shareable), pour éviter que la version complète et la version anonymisée ne soient mélangées et partagées accidentellement ; build_dataset.py ajoute _write_anon() (écriture atomique tmp+replace + chmod 444) ; synchronisation du chemin de la déclaration de confidentialité dans report_generator |
| 1.0.2 | 2026-08-17 | Correction P1 : ajout de verify_anonymized à la fin de build_dataset.py pour une vérification automatique (scan regex des numéros d'identité/téléphone/email/noms d'hôpitaux, sortie avec erreur en cas de détection) |
| 1.0.1 | 2026-08-17 | Correction P0 : report_generator.py ajoute la fonction d'anonymisation mask_identity_text (masquage des numéros de téléphone/pièce d'identité/noms des médecins + généralisation des établissements), anonymisation de la couche d'affichage du texte des résumés/conclusions ; alignement de la déclaration de confidentialité avec la sortie réelle |
| 1.0.0 | 2026-08-16 | Version initiale : Système d'analyse des tendances des indicateurs de bilan de santé |