Sang et hémolyse
L'hémoglobine par photo, le jaune de l'œil, l'hémolyse, les transfusions et le fer.
Chaque algorithme est décrit avec ce qu’il lit, ses étapes, les valeurs qu’il utilise (avec leur emplacement dans le code), ce qu’il affiche et ses limites.
Saisie et conversion des analyses de laboratoire
Quand tu écris une valeur de laboratoire, ou quand Trévor en lit une dans un document, il la ramène à l'unité de son catalogue, vérifie qu'elle est plausible et qu'elle n'est pas déjà au dossier. Ces règles servent à l'hémolyse, au fer, à l'hémoglobine fœtale et à la créatinine. L'unité d'origine est gardée à côté de la valeur convertie.
Où tu le vois : Messages de confirmation (« Noté : LDH 320 U/L le 07.10.2026 »), refus avec raison, et affichage de chaque analyse dans l'unité que tu as choisie dans les réglages, avec l'unité d'origine entre parenthèses.
Ce que l’algorithme lit
- Valeur écrite (« 2,5 mg/dL », « 112 », « indosable »), unité facultative, date (déclaration, import, document lu)
- Unité d'analyses choisie dans les réglages (standard, SI ou conventionnelle) (réglages)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
Hémoglobine | 3 à 20 g/dL ; g/L × 0,1 | Bornes et facteur. | src/trevor/units.py:83-84 |
Bilirubine totale | 1 à 600 µmol/L ; mg/dL × 17,1 | Bornes (1 à 600) et facteur. | src/trevor/units.py:85-86 |
Bilirubine indirecte | 0,5 à 600 µmol/L ; mg/dL × 17,1 | Bornes et facteur. | src/trevor/units.py:87-88 |
Créatinine | 10 à 1500 µmol/L ; mg/dL × 88,4 | Bornes et facteur. | src/trevor/units.py:89-90; src/trevor/royal/labs.py:22 |
Ferritine | 1 à 60000 µg/L ; ng/mL × 1 ; mg/L × 1000 | Bornes et facteurs. | src/trevor/units.py:91-92 |
Haptoglobine | 0 à 8 g/L ; mg/dL × 0,01 ; mg/L × 0,001 | Bornes et facteurs. | src/trevor/units.py:93-94 |
LDH | 30 à 6000 U/L ; UI/L × 1 | Bornes. | src/trevor/units.py:95-96 |
Réticulocytes | 0,5 à 1500 ×10⁹/L ; /µL × 0,001 ; G/L × 1 | Bornes et facteurs. | src/trevor/units.py:97-99 |
Hémoglobine S, hémoglobine fœtale | 0 à 100 % | Bornes en pourcentage. | src/trevor/units.py:100-101; src/trevor/royal/labs.py:21 |
MAX_BACK_DAYS | 5 ans (hémolyse, HbF, créatinine) ; 10 ans (fer) | Ancienneté maximale d'une date saisie. | src/trevor/hemolysis.py:262; src/trevor/royal/labs.py:19; src/trevor/iron.py:35 |
Hémoglobine sans unité | au-dessus de 25 : divisée par 10 | Heuristique g/L sans unité. | src/trevor/hemolysis.py:289 |
Ce qui est affiché
Pour chaque saisie : soit la valeur enregistrée dans l'unité du catalogue avec une phrase de confirmation, soit un refus qui dit pourquoi en une phrase (pas de nombre, unité non reconnue, pourcentage refusé, hors bornes, date future ou trop ancienne, dossier non ouvert), soit « Déjà dans ton dossier ». Pas de niveau ni de seuil clinique ici.
Un exemple chiffré
« 2,5 mg/dL » de bilirubine totale devient 2,5 × 17,1 = 42,75 µmol/L. « 112 » d'hémoglobine sans unité devient 11,2 g/dL. « 2 % » de réticulocytes est refusé. « indosable » d'haptoglobine devient 0 g/L. « 95 g/L » d'hémoglobine devient 9,5 g/dL (résultats obtenus en exécutant normalize_lab).
Ce que cet algorithme ne sait pas faire
- Trois fonctions écrivent presque la même règle (hemolysis.normalize_lab, iron.normalize, royal/labs.normalize). Seule celle du fer accepte les milliers écrits avec une espace (« 1 450 ») ; les deux autres liraient « 1 ».
- Le seuil de lecture de l'hémoglobine en g/L diffère selon l'endroit : 25 à la saisie sans unité, 40 pour les valeurs HemoSnap de l'hémolyse, 30 pour la réponse à la transfusion.
- La date « aujourd'hui » vient de l'heure de la personne pour l'hémolyse et le fer, mais de l'heure du serveur pour l'HbF et la créatinine (royal/labs.py).
- Le texte « < 0,1 » pour l'haptoglobine est reconnu seulement avec ce libellé exact (avec espace et virgule) ; « <0.1 » est lu comme 0,1.
- Un nombre sans unité dans un texte comportant une unité ambiguë est refusé ou lu dans l'unité des réglages, ce qui peut changer la valeur d'un facteur 17,1 si le réglage n'est pas celui du laboratoire.
- Les facteurs de conversion sont des arrondis d'usage clinique (17,1 et non 17,104), indiqués comme tels dans units.py.
Code : src/trevor/hemolysis.py (LAB_ENTRY, normalize_lab, add_lab, items_from_parsed), src/trevor/iron.py (normalize, add), src/trevor/royal/labs.py (LAB_EXTRA, normalize, add_lab), src/trevor/units.py (LABS, lab_factors, entry_unit, original_reading, lab_to_unit, lab, lab_pair)
Évaluation de l'hémolyse : six marqueurs face à ta ligne de base
Trévor compare ta dernière valeur de chaque marqueur (bilirubine totale et indirecte, LDH, réticulocytes, haptoglobine, hémoglobine) à la médiane de tes valeurs précédentes. Il compte combien de marqueurs récents s'écartent dans le sens de l'hémolyse et en tire un niveau : stable, à surveiller ou évoque une poussée. Il décrit tes données et ne pose aucun diagnostic.
Où tu le vois : Écran Hémolyse du bot, page web du dossier et application : une phrase de niveau, la liste des marqueurs signalés (« • LDH : 410 U/L (ta base : 300) »), ce qui manque pour conclure et un rappel que ce ne sont pas des diagnostics.
Ce que l’algorithme lit
- Résultats de laboratoire du dossier : bilirubine, bilirubine indirecte, LDH, réticulocytes (nombre absolu), haptoglobine, hémoglobine, avec leur date (dossier, import, déclaration)
- Valeurs d'hémoglobine de laboratoire saisies dans HemoSnap (hors estimations) (déclaration)
- Date de la dernière transfusion (dossier)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
PANEL_DAYS | 21 | Fenêtre, autour de la mesure la plus récente, dans laquelle les marqueurs comptent comme un même bilan. | src/trevor/hemolysis.py:36 |
MIN_BASELINE | 2 | Valeurs antérieures nécessaires. | src/trevor/hemolysis.py:37 |
ROBUST_FROM | 4 | Antécédents à partir desquels le test à 2 écarts-types robustes s'ajoute. | src/trevor/hemolysis.py:38 |
AFTER_TRANSFUSION_DAYS | 28 | Durée après transfusion qui déclenche l'avertissement. | src/trevor/hemolysis.py:39 |
MARKERS bilirubine / indirecte | +30 % / +30 % | Seuils relatifs de hausse. | src/trevor/hemolysis.py:55-56 |
MARKERS LDH | +25 % | Seuil relatif de hausse. | src/trevor/hemolysis.py:57 |
MARKERS réticulocytes | +30 % | Seuil relatif de hausse (nombre absolu). | src/trevor/hemolysis.py:58 |
MARKERS haptoglobine | baisse de 50 % | Seuil relatif de baisse. | src/trevor/hemolysis.py:59 |
MARKERS hémoglobine | baisse de 1,0 g/dL | Baisse absolue. | src/trevor/hemolysis.py:60 |
HAPTO_UNDETECTABLE | 0.1 g/L | Base en dessous de laquelle l'haptoglobine n'est plus comparée. | src/trevor/hemolysis.py:62 |
Plancher de l'écart-type robuste | 0,08 × |base| | L'écart-type robuste ne descend pas sous 8 % de la base. | src/trevor/hemolysis.py:95-97 |
Ce qui est affiché
Cinq niveaux : « aucun » (pas de marqueur au dossier), « pas assez de données » (moins de 2 marqueurs actuels), « stable » (0 signalé), « à surveiller » (1 signalé) et « évoque une poussée d'hémolyse » (2 signalés ou plus). Les niveaux « à surveiller » et « poussée » invitent à en parler à l'équipe soignante ; « à surveiller » précise qu'un seul marqueur ne suffit pas pour conclure. La mention de réaction retardée apparaît seulement pour « à surveiller » et « poussée » dans les 28 jours après une transfusion.
Un exemple chiffré
Bilirubine totale : valeurs 50, 55, 52 puis 80 µmol/L. Base (médiane de 50, 55, 52) = 52. Seuil = 52 × 1,30 = 67,6. 80 dépasse ce seuil, et comme il y a seulement 3 antécédents, le test robuste ne s'applique pas : statut « signalé », +54 %. Avec LDH signalée aussi dans les 21 jours, le niveau devient « évoque une poussée d'hémolyse » (calculé en exécutant judge).
Ce que cet algorithme ne sait pas faire
- Un marqueur actuel sans base (moins de 2 valeurs antérieures) ou avec une haptoglobine indétectable compte quand même parmi les marqueurs actuels : deux marqueurs sans base donnent le niveau « stable », alors que rien n'a été comparé.
- La bilirubine totale et la bilirubine indirecte sont deux marqueurs séparés : une même hausse de bilirubine, mesurée sous ces deux formes, compte pour deux signalements et suffit à « évoque une poussée ».
- Les seuils sont les mêmes pour tout le monde : ni l'âge, ni le sexe, ni la maladie, ni la fiabilité du dosage ne les modifient.
- Le module ne distingue pas une hémolyse d'une autre cause : le texte du module rappelle que la LDH monte aussi avec le muscle ou le foie et les réticulocytes avec une hémorragie. Seule la transfusion récente (28 jours) est prise en compte, par un message.
- Les valeurs de laboratoire sont celles que tu as saisies ou importées ; une valeur fausse ou mal datée fausse la comparaison.
Code : src/trevor/hemolysis.py (series, judge, lab_view, missing_markers), src/trevor/conversation/hemolysis_screens.py (_hemolysis_lines), src/trevor/transfusion.py (last_transfusion)
HemoSnap : hémoglobine estimée par photo du doigt
HemoSnap estime ton hémoglobine à partir de trois photos du bout du doigt posé sur l'objectif et le flash. Le chiffre n'apparaît qu'après un calibrage sur une de tes valeurs de laboratoire. C'est une fonction expérimentale, sans validation clinique, qui ne remplace pas une prise de sang.
Où tu le vois : Dans le bot, menu Ma santé, entrée HemoSnap : avertissement, écran d'accueil, prise des trois photos, résultat avec fourchette, puis calibrage (valeur de laboratoire, date, lien avec une transfusion). Le même calcul sert à l'application du téléphone (signals.py).
Ce que l’algorithme lit
- Trois photos du bout du doigt, flash allumé, doigt couvrant l'objectif et le flash (photo)
- Une ou plusieurs valeurs d'hémoglobine de laboratoire (3 à 20 g/dL) avec leur date (déclaration)
- Réponse à la question « juste avant, juste après ou sans rapport avec une transfusion » (déclaration)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
POP_SLOPE | 9.0 | Pente de population (g/dL par unité d'indice), utilisée avec un seul point. | src/trevor/media/hemosnap.py:30 |
POP_INTERCEPT | 6.0 | Ordonnée de population. Elle n'entre dans aucun calcul d'estimation tant qu'il n'y a pas de point : sans point, aucun chiffre n'est donné. | src/trevor/media/hemosnap.py:31 |
HB_MIN, HB_MAX | 3.0 et 20.0 g/dL | Bornes de la valeur de laboratoire saisie et de la fourchette affichée. | src/trevor/media/hemosnap.py:32 |
PLAUSIBLE | (4.0, 19.0) g/dL | Une estimation hors de cette plage n'est pas affichée. | src/trevor/media/hemosnap.py:33 |
GREEN_MIN | 35.0 | Vert médian minimal (0 à 255) : en dessous, l'indice explose. | src/trevor/media/hemosnap.py:34 |
SLOPE_RANGE | (3.0, 18.0) | Pente personnelle plausible. | src/trevor/media/hemosnap.py:35 |
MODEL_BAND_1PT / 2PT / 4PT | 1.4 / 0.8 / 0.6 g/dL | Part « modèle » de l'incertitude selon le nombre de points (1, 2 à 3, 4 ou plus). | src/trevor/media/hemosnap.py:42-44 |
MEAS_BAND_1SHOT | 0.8 g/dL | Bruit d'un cliché, divisé par la racine du nombre de clichés. | src/trevor/media/hemosnap.py:45 |
HEMO_SHOTS_NEEDED | 3 | Nombre de clichés d'affilée dans le bot. | src/trevor/conversation/wearables_screen.py:792 |
Seuils de qualité | luminosité 40 à 240, part rouge 0,5, vert 35, marge rouge 8 | Conditions de quality_check ; un pixel compte comme rouge si le rouge dépasse le vert et le bleu de plus de 8. | src/trevor/media/hemosnap.py:91,105-111 |
Ce qui est affiché
Le résultat dit : « Estimation : environ X g/dL, dans une fourchette de A à B g/dL », suivi du nombre de valeurs de laboratoire du calibrage et d'une note disant que la fonction est expérimentale, non validée et moins fiable sur peau foncée. Niveaux internes : non calibré (aucun chiffre), hors plage (aucun chiffre), calibré avec 1, 2 ou plus de points. Chaque estimation chiffrée est enregistrée avec son indice.
Un exemple chiffré
Un point de laboratoire : indice 0,5 pour 9,0 g/dL. La pente reste 9,0 et l'ordonnée devient 4,5. Trois clichés donnent l'indice médian 0,6 : 9,0 × 0,6 + 4,5 = 9,9 g/dL. Demi-largeur : racine de (1,4² + 0,8²/3) = 1,5. Affichage : 9,9 g/dL, fourchette 8,4 à 11,4 (calculé en exécutant le code).
Ce que cet algorithme ne sait pas faire
- Ce n'est pas un dispositif médical validé : aucune étude de précision n'est citée dans le code ou les textes du dépôt.
- Le calibrage ignore la date de la valeur de laboratoire et le lien avec une transfusion : ces deux réponses sont enregistrées mais n'entrent pas dans la droite (fit_calibration ne reçoit que des couples indice et valeur).
- Le texte qui suit une valeur « juste après une transfusion » affirme que Trévor tient compte de la redescente de l'hémoglobine. Le code de HemoSnap n'applique aucune correction de ce genre.
- Deux points proches en hémoglobine donnent une pente instable ; la borne de 3 à 18 la limite sans la corriger.
- L'incertitude dépend du nombre de points, pas de leur écart de niveau. Deux points au même niveau d'hémoglobine reçoivent quand même la bande de 0,8 g/dL.
- Sur le téléphone (signals.py), l'indice est calculé hors du serveur : le serveur vérifie seulement qu'il est entre -1 et 5,6, sans contrôle de qualité de photo.
- L'affirmation que la transillumination réduit le biais de la mélanine vient de la documentation du module, sans mesure dans le dépôt.
Code : src/trevor/media/hemosnap.py (extract_features, quality_check, fit_calibration, uncertainty_band, estimate), src/trevor/conversation/wearables_screen.py (hemosnap_process, on_hemosnap_calibrate, on_hemosnap_cal_date, on_hemosnap_cal_transf), src/trevor/storage/database.py (hemo_calibration_points), src/trevor/signals.py (_hemoglobin, hemosnap_reading)
Jaune de la sclère : indice et bilirubine estimée
Trévor mesure le jaune du blanc de ton œil sur une photo et en tire un indice. Après plusieurs couples photo et prise de sang, il traduit l'indice en bilirubine estimée avec un intervalle. Sans calibrage, il indique seulement si tu es plus jaune, pareil ou moins jaune que d'habitude.
Où tu le vois : Dans l'écran Hémolyse du bot (ligne « Jaune de l'œil ») et dans la page web : indice enregistré, bilirubine estimée avec fourchette, « pas de jaune net », tendance, et le nombre de couples de calibrage sur 5.
Ce que l’algorithme lit
- Photo du blanc de l'œil, sans flash (photo)
- Photo d'une feuille de papier blanc dans la même lumière (facultative) (photo)
- Bilirubine de laboratoire (µmol/L ou mg/dL, entre 3 et 400 µmol/L), saisie sur la photo ou trouvée au dossier à 2 jours près (déclaration, dossier)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
MIN_POINTS_PROVISIONAL | 3 | Couples minimaux pour un chiffre provisoire. | src/trevor/media/scleral.py:37 |
MIN_POINTS_CALIBRATED | 5 | Couples pour un chiffre calibré. | src/trevor/media/scleral.py:38 |
MIN_R_CALIBRATED | 0.6 | Corrélation minimale pour « calibré » (en dessous de 0,3 ou pente négative : « insuffisant »). | src/trevor/media/scleral.py:39,218-224 |
BAND_FLOOR | 8.0 µmol/L | Demi-largeur minimale de l'intervalle. | src/trevor/media/scleral.py:40 |
VISIBLE_ICTERUS | 35.0 µmol/L | Sous cette bilirubine, la sclère reste blanche à l'œil. | src/trevor/media/scleral.py:41 |
BILI_MIN, BILI_MAX | 3.0 et 400.0 µmol/L | Bornes d'affichage et de saisie. | src/trevor/media/scleral.py:42 |
MG_DL_TO_UMOL | 17.1 | Conversion mg/dL vers µmol/L. | src/trevor/media/scleral.py:43 |
TREND_MIN_SHIFT, TREND_MIN_HISTORY | 1.5 unité de b* ; 3 mesures | Écart minimal et historique minimal pour la tendance. | src/trevor/media/scleral.py:44-45 |
MIN_SCLERA_FRACTION | 0.02 | Part minimale de l'image occupée par le blanc de l'œil. | src/trevor/media/scleral.py:46 |
EYE_MIN_L, BLOWN_L, EYE_MIN_LIGHT | 50 ; 97 ; 58 | Luminosité minimale d'un pixel, seuil de brûlé, luminosité médiane minimale. | src/trevor/media/scleral.py:47-49 |
MAX_BLOWN_IMAGE, MAX_GLARE, MAX_SPREAD | 0.30 ; 0.25 ; 8.0 | Part d'image brûlée, part de reflet autour du blanc, étalement maximal de b*. | src/trevor/media/scleral.py:50-52 |
EYE_PAIR_DAYS | 2 | Écart maximal entre la photo et une bilirubine du dossier. | src/trevor/hemolysis.py:41 |
Ce qui est affiché
Trois niveaux de calibrage : aucun (moins de 3 couples, tendance seule), provisoire (3 ou 4 couples), calibré (5 couples ou plus, corrélation d'au moins 0,6), plus le palier « insuffisant » (pente négative ou corrélation sous 0,3, aucun chiffre). Le chiffre est la bilirubine estimée arrondie en µmol/L avec ses bornes. Sans estimation, la tendance dit « plus jaune », « comme d'habitude » ou « moins jaune ». Un texte rappelle que la photo n'estime que la bilirubine et pose aucun diagnostic.
Un exemple chiffré
Cinq couples (indice, bilirubine) : (1, 20), (2, 30), (3, 41), (4, 52), (5, 60). Pente 10,2, ordonnée 10, corrélation 0,999 : palier calibré. Nouvel indice 3,5 : 10,2 × 3,5 + 10 = 45,7, soit 46 µmol/L. L'intervalle calculé est d'environ 3,4, donc le plancher de 8 s'applique : de 38 à 54 µmol/L. Indice 0 : 10 µmol/L, sous 35, donc « pas de jaune net » (exécuté avec le code).
Ce que cet algorithme ne sait pas faire
- Ce n'est pas un dispositif médical validé. Le document du dépôt (docs/CALIBRAGE_HEMOLYSE.md) liste encore à faire une étude de précision, un accord d'hématologie et une qualification réglementaire.
- L'indice dépend de l'appareil, de la lumière et de la pigmentation : le calibrage personnel absorbe un décalage stable, pas un changement de téléphone ou de lieu.
- Les couples de calibrage de photos prises sans feuille et avec feuille sont mélangés dans la même droite.
- Avec exactement 3 couples, l'intervalle de Student (12,7 pour 1 degré de liberté) est très large avant le plancher.
- Sur le téléphone (signals.py eye_numbers), les nombres arrivent déjà calculés : le serveur vérifie seulement des bornes (b* de -20 à 80, luminosité 0 à 100) et l'étalement est mis à 0.
- Le signal « pas de jaune net » repose sur le seuil de 35 µmol/L, valeur de l'ictère visible posée dans le code.
Code : src/trevor/media/scleral.py (extract_eye, extract_paper, index_of, fit, estimate, trend, to_umol), src/trevor/hemolysis.py (record_eye, save_eye, calibration_points, eye_view, set_lab), src/trevor/conversation/hemolysis_screens.py (_hemolysis_lines), src/trevor/signals.py (eye_numbers)
Lecture d'un PDF de laboratoire
Trévor lit les mots et leur position dans un PDF de laboratoire qui contient du texte, retrouve les marqueurs d'hémolyse et rattache chaque valeur à sa date de prélèvement. Rien n'est inventé : une valeur n'est retenue que si elle est alignée sous une date. Tu relis le résultat avant tout enregistrement.
Où tu le vois : Écran de relecture après l'envoi du PDF : une case par valeur lue, que tu coches ou décoches avant l'enregistrement.
Ce que l’algorithme lit
- PDF de laboratoire avec texte (rapport HUG, rapport de suivi alémanique) (import)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
MAX_PAGES | 300 | Au-delà, refus. | src/trevor/media/lab_pdf.py:36 |
Tolérance de ligne | 3.0 points | Écart vertical maximal dans une ligne. | src/trevor/media/lab_pdf.py:121 |
Tolérance de colonne | max(14, 0,45 × écart minimal entre colonnes) | Distance maximale entre une valeur et sa colonne. | src/trevor/media/lab_pdf.py:166-167 |
Part de dates d'en-tête | au moins 2 dates et 80 % de la ligne | Condition d'une ligne d'en-tête. | src/trevor/media/lab_pdf.py:155 |
Dédoublonnage des colonnes | 8 points | Deux dates à moins de 8 points de centre forment une seule colonne. | src/trevor/media/lab_pdf.py:158 |
Plafond de valeurs | 60 | Valeurs gardées au retour. | src/trevor/hemolysis.py:365 |
Ce qui est affiché
Une liste de valeurs (marqueur, valeur dans l'unité du catalogue, unité, date, indicateur « inférieur à », unité d'origine) à relire. Aucun niveau ni seuil clinique.
Un exemple chiffré
aucun (la lecture dépend de la mise en page d'un vrai PDF, qui n'est pas dans le dépôt). Seule la reconnaissance du libellé a été exécutée : « Bilirubine totale » en µmol/l devient bilirubine ; « Réticulocytes nb.abs. » en o/oo et « Hémoglobine libre » ne donnent rien.
Ce que cet algorithme ne sait pas faire
- Seules deux mises en page sont reconnues : tableau à colonnes datées et résultat courant avec résultat antérieur sur une ligne. Une autre disposition peut donner une liste vide ou des valeurs mal alignées, d'où la relecture.
- Les libellés doivent correspondre exactement aux motifs prévus (français, allemand pour la bilirubine, l'hémoglobine et la LDH) : une autre formulation est ignorée sans avertissement.
- Un signe « < » est mémorisé (indicateur « inférieur à ») mais la valeur enregistrée reste le nombre écrit, converti : « < 73 mg/L » d'haptoglobine devient 0,073 g/L.
- Si deux valeurs du même marqueur tombent le même jour, la dernière lue gagne sans que le conflit soit signalé.
Code : src/trevor/media/lab_pdf.py (read_lab_pdf, marker_of, _read_table, _read_inline, _columns, _split), src/trevor/hemolysis.py (read_pdf_items, normalize_lab)
Réponse de l'hémoglobine aux transfusions
Trévor compare ton hémoglobine d'avant et d'après chaque transfusion pour estimer ce que te donne un culot, combien de temps le gain se maintient, et si les échanges respectent la dose et l'intervalle de leur protocole. Il décrit tes données et ne recommande aucune dose.
Où tu le vois : Carte « Réponse à tes transfusions » : page web du tableau de bord, application et export.
Ce que l’algorithme lit
- Séances avec date, type, nombre de culots (déclaration)
- Hémoglobine de laboratoire (dossier et valeurs HemoSnap de labo) et estimations HemoSnap (dossier, photo)
- Hémoglobine S en % (dossier) (dossier)
- Poids de la carte de soins (25 à 250 kg) (dossier)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
PRIOR_PER_UNIT | 1.0 g/dL par culot | Repère d'adulte. | src/trevor/transfusion_response.py:35 |
PRIOR_SD | 0.4 | Incertitude du repère. | src/trevor/transfusion_response.py:36 |
LAB_SD, ESTIMATE_SD | 0.25 ; 1.0 g/dL | Erreur de mesure d'un laboratoire et d'une estimation HemoSnap. | src/trevor/transfusion_response.py:37 |
HBS_SD | 2.0 points de % | Erreur d'un dosage d'hémoglobine S. | src/trevor/transfusion_response.py:38 |
HBS_TARGET | 30.0 % | Repère sous lequel on cherche à garder l'hémoglobine S entre deux échanges ; le code précise « à confirmer avec l'hématologie ». | src/trevor/transfusion_response.py:39 |
PRE_WINDOW, POST_WINDOW | 7 jours avant ; jours 1 à 7 après | Fenêtres de recherche. | src/trevor/transfusion_response.py:40-41 |
MIN_PAIRS_INDIVIDUAL | 3 | Paires pour un chiffre personnel non provisoire. | src/trevor/transfusion_response.py:42 |
RECENT_EXCHANGES | 6 | Échanges pris en compte pour la part dans la cible. | src/trevor/transfusion_response.py:43 |
PROTOCOLS exchange | culots 6 à 9 (pour 77 kg), semaines 5 à 6 | Cibles de l'échange automatique. | src/trevor/transfusion_response.py:49 |
PROTOCOLS exchange_manual | culots 3 à 5 (pour 77 kg), semaines 2 à 3 | Cibles de l'échange manuel. | src/trevor/transfusion_response.py:50 |
Demi-vie | jours 1 à 60, excès > 0,3 g/dL, décroissance > 0,0001 par jour | Conditions du calcul. | src/trevor/transfusion_response.py:216-225 |
Ce qui est affiché
Gain par culot en g/dL avec son erreur standard, la pente brute, le nombre de paires et le drapeau « provisoire » ; demi-vie en jours ; pour chaque modalité d'échange : dernier nombre de culots et statut (en dessous, dans la cible, au-dessus), cible en culots, part des 6 derniers échanges dans la cible, intervalle médian et son statut (court, dans la cible, long), culots par kg ; hémoglobine S : dernier dosage, repère de 30 %, pente par culot. Les chiffres personnels ne sont déclarés « assez » qu'avec 3 paires de transfusions simples.
Un exemple chiffré
Trois transfusions simples : 2 culots (7,0 puis 9,0 g/dL), 2 culots (7,4 puis 9,1), 3 culots (6,8 puis 10,1). Pente brute 1,018 g/dL par culot, erreur standard 0,086. Après rapprochement vers 1,0 (écart-type 0,4) : 1,017 avec erreur 0,084. Échange automatique pour 70 kg : cible 5,5 à 8,2 culots ; 6 culots est dans la cible ; intervalle médian de 7 semaines : « long » (plus de 6,5) ; 6,5 semaines : dans la cible (exécuté avec le code).
Ce que cet algorithme ne sait pas faire
- Les cibles de dose et d'intervalle sont des valeurs de départ « données par David (hématologie) : à faire confirmer » d'après le code. Ce ne sont pas des recommandations validées.
- Le repère de 1 g/dL par culot est un repère d'adulte : le code ne l'adapte pas à l'enfant ni au poids.
- Le module n'ajuste ni le volume plasmatique, ni l'hémolyse, ni le délai du prélèvement, ni la rate (docs/ANALYSE_REPONSE_TRANSFUSION.md).
- La fonction pooled_within_slope (pente de cohorte) existe dans le fichier mais n'est appelée nulle part dans le code lu : elle n'influence aucun chiffre affiché.
- La demi-vie ne tient compte que de la dernière transfusion simple et suppose une décroissance exponentielle.
- Les échanges sont exclus du saut par culot (le gain net y est faible) ; leur efficacité se lit seulement sur l'hémoglobine S, s'il y en a.
- La médiane d'intervalle mélange tous les échanges du type, y compris les premiers, sans fenêtre récente.
- Le poids vient d'un texte libre de la carte (« 77 kg ») lu par expression régulière.
Code : src/trevor/transfusion_response.py (hb_readings, make_pairs, slope_through_origin, shrink, half_life_days, units_target, dose_status, interval_status, build), src/trevor/transfusion.py (payload, _response), src/trevor/dashboard/data.py
Suivi du fer : ferritine, IRM et fraction d'éjection
Trévor note les mesures de fer que ton équipe de soins te donne (ferritine, T2* du cœur et du foie, fer du foie, fraction d'éjection), les met en courbe et les lit face à des repères de la Thalassaemia International Federation. Chaque saisie remet à jour le rappel de l'examen annuel. Il ne donne aucune consigne de traitement.
Où tu le vois : Écran et page du suivi du fer pour les maladies concernées (thalassémie) : une ligne par type de mesure avec la dernière valeur, sa date, une phrase de repère, le sens depuis la mesure précédente et une petite courbe.
Ce que l’algorithme lit
- Ferritine (µg/L), T2* du cœur et du foie (ms), fer du foie LIC (mg/g), fraction d'éjection (%) (déclaration, dossier)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
FERRITIN_THAL_WATCH | 1000.0 µg/L | Ferritine à surveiller. | src/trevor/value_alerts.py:48 |
FERRITIN_THAL_REPORT | 2500.0 µg/L | Ferritine à signaler. | src/trevor/value_alerts.py:49 |
CARDIAC_T2_ABNORMAL_MS | 20.0 ms | T2* du cœur anormal en dessous. | src/trevor/iron.py:30 |
CARDIAC_T2_HIGH_RISK_MS | 10.0 ms | T2* du cœur à risque élevé en dessous. | src/trevor/iron.py:31 |
LIC_MODERATE, LIC_SEVERE | 7.0 ; 15.0 mg/g | Seuils de surcharge modérée et sévère. | src/trevor/iron.py:32-33 |
FLAT_PERCENT | 10.0 | Écart sous lequel deux mesures se lisent « stable ». | src/trevor/iron.py:34 |
MAX_BACK_DAYS | 3650 | Ancienneté maximale d'une date saisie. | src/trevor/iron.py:35 |
Ce qui est affiché
Par type de mesure : dernière valeur, date, phrase de repère, sens et courbe. Ferritine : 3 niveaux (sous 1 000, 1 000 à 2 499, 2 500 ou plus). T2* du cœur : 3 niveaux (20 ou plus, 10 à moins de 20, sous 10). LIC : 3 niveaux (sous 7, 7 à moins de 15, 15 ou plus). T2* du foie et fraction d'éjection : aucun repère, valeur seulement notée et mise en courbe. Les phrases invitent à en parler avec l'équipe de soins.
Un exemple chiffré
Ferritine 1 450 µg/L : niveau « à surveiller » (entre 1 000 et 2 500). Deux dernières ferritines 1 000 puis 1 080 : écart 8 %, sous 10 %, donc « stable » ; 1 000 puis 1 200 : 20 %, donc « en hausse ». T2* du cœur 15 ms : « anormal ». Courbe de 1, 2, 3, 5 : ▁▃▅█ (exécuté avec le code).
Ce que cet algorithme ne sait pas faire
- Le fichier iron.py ne traite pas la saturation de la transferrine : aucun calcul ni affichage de saturation n'existe dans ce module. Seuls la ferritine, les T2*, le LIC et la fraction d'éjection sont suivis.
- Tous les repères sont notés « à valider par un hématologue » dans le code, et valent pour la thalassémie dépendante des transfusions. Pour la forme non dépendante, le code n'invente aucun seuil.
- Le sens compare seulement les deux dernières valeurs, sans tenir compte de l'écart de temps entre elles.
- La ferritine monte aussi avec l'inflammation ; le module ne le distingue pas.
- Si deux valeurs différentes sont saisies le même jour pour un même type, une seule est gardée dans la série.
Code : src/trevor/iron.py (normalize, reading, trend, spark, series, build, add), src/trevor/value_alerts.py (FERRITIN_THAL_WATCH, FERRITIN_THAL_REPORT), src/trevor/conversation/iron_screens.py, src/trevor/dashboard/phone_care_routes.py
Symptômes après une transfusion : niveau d'alerte
Tu coches les symptômes que tu ressens depuis une transfusion. Le serveur donne à la déclaration le niveau du symptôme le plus grave coché : signe d'alerte, à signaler ou noté. Il te dit alors qui appeler, et signale qu'une réaction retardée est possible si le signe et le délai s'y prêtent.
Où tu le vois : Écran « Après ma transfusion » : liste des symptômes, texte de conseil selon le niveau, historique des déclarations. Le niveau est calculé par le serveur et le client ne le choisit pas.
Ce que l’algorithme lit
- Symptômes cochés parmi 17 choix fermés (déclaration)
- Date de début (aujourd'hui par défaut) et intensité (0 à 3) (déclaration)
- Dates de transfusion connues (dossier)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
DELAYED_HINTS | dark_urine, jaundice, fatigue, pain, fever, back_pain | Signes qui évoquent une destruction retardée des globules rouges transfusés. | src/trevor/transfusion.py:62 |
DELAYED_WINDOW_DAYS | (1, 28) | Fenêtre du lendemain à quatre semaines. | src/trevor/transfusion.py:63 |
RECENT_DAYS | 30 | Une déclaration plus ancienne ne remonte plus dans le bandeau du dossier. | src/trevor/transfusion.py:64 |
MAX_NOTE | 280 | Caractères de la note. | src/trevor/transfusion.py:66 |
INTENSITIES | 0, 1, 2, 3 | Inconnue, léger, gênant, très fort. | src/trevor/transfusion.py:68 |
SYMPTOMS | 6 urgent, 8 à signaler, 3 noté | Catalogue et niveaux. | src/trevor/transfusion.py:40-57 |
Ce qui est affiché
Trois niveaux : « signe d'alerte » (urgent), « à signaler », « noté ». Le texte d'urgence dit d'appeler le numéro d'urgence du pays de la personne. La mention de réaction retardée s'ajoute pour urgent et à signaler quand le délai est de 1 à 28 jours avec un signe évocateur.
Un exemple chiffré
Symptômes cochés : fièvre et nausées. Fièvre est « urgent », nausées « noté » : le niveau est « urgent ». Coche seulement « plaques rouges » : « à signaler ». « Peau ou yeux jaunes » 10 jours après la transfusion : réaction retardée possible ; le même signe le jour même (délai 0) : non (exécuté avec le code).
Ce que cet algorithme ne sait pas faire
- Le niveau dépend uniquement de la liste cochée : l'intensité et la note sont enregistrées mais ne changent pas le niveau.
- Le libellé de la fièvre dit « 38 °C ou plus », tandis que le texte du module cite la règle de 38,3 °C du protocole HUG. Le niveau « urgent » vient de la case cochée, pas d'une température mesurée.
- Le catalogue est fermé : un symptôme absent de la liste ne peut être décrit que par « autre symptôme », de niveau « noté ».
- La mention de réaction retardée n'apparaît pas pour le niveau « noté ».
- Le service d'hématologie doit encore relire la liste avant diffusion large, d'après le module.
Code : src/trevor/transfusion.py (SYMPTOMS, tier_of, delayed_possible, linked_transfusion, record_reaction, advice_lines)
Historique des transfusions, intervalle et programme
Trévor retrouve ta dernière transfusion à partir de plusieurs endroits où tu peux l'avoir notée, compte les jours écoulés, calcule l'intervalle moyen entre transfusions et, si tu as un programme régulier, dit si tu es en retard ou à l'heure. Il note aussi le nombre de culots et la durée de chaque séance.
Où tu le vois : Écran « Mon programme transfusionnel » du bot (statut, jours et semaines écoulés, nombre de transfusions, intervalle moyen, retard ou prochaine date), puis questions sur le nombre de culots et la durée. Mêmes données sur la page web et dans l'application (transfusion.payload).
Ce que l’algorithme lit
- Date de la transfusion ou de l'échange (aujourd'hui, ou une date écrite) (déclaration)
- Programme régulier oui ou non, et intervalle en semaines (déclaration)
- Nombre de culots (1 à 30), durée de la séance (1 minute à 24 heures) (déclaration)
- Ligne « Dernière transfusion » de la carte d'urgence (dossier)
Le calcul, étape par étape
Les valeurs utilisées
| Nom dans le code | Valeur | Rôle | Où |
|---|---|---|---|
MAX_BACK_DAYS | 730 | Ancienneté maximale d'une transfusion notée. | src/trevor/transfusion.py:65 |
MAX_UNITS | 30 | Culots maximum par séance. | src/trevor/transfusion.py:67 |
MAX_MINUTES | 1440 | Durée maximale d'une séance. | src/trevor/transfusion.py:111 |
DURATION_CHOICES_HOURS | 1 à 8 heures | Boutons de durée. | src/trevor/conversation/carnet_screens.py:26 |
Seuil de retard | 0,5 semaine | À partir de ce retard, le bot affiche un message de retard. | src/trevor/conversation/wearables_screen.py:644-650 |
Intervalle par défaut | 4 semaines | Utilisé si l'intervalle du programme est vide ou nul dans l'écran du bot. | src/trevor/conversation/wearables_screen.py:644 |
Boutons de culots | 1 à 6, nombre écrit jusqu'à 30 | Choix proposés. | src/trevor/conversation/wearables_screen.py:701-712 |
Ce qui est affiché
Pour chaque transfusion : date, type, culots, durée, source de la date (historique, profil ou carte) et jours depuis la précédente. Pour l'ensemble : date de la dernière, jours et semaines écoulés, nombre, intervalle moyen en semaines, régularité (écart-type des intervalles en jours, calculé mais non affiché dans l'écran du bot). Si tu as un programme : « en retard de X semaines (intervalle prévu Y) » ou « prochaine transfusion le JJ.MM.AAAA dans Z semaines ». Aucun seuil clinique.
Un exemple chiffré
aucun (le calcul d'écart se lit directement dans le code ; aucun jeu de données n'a été exécuté). Lecture du code : programme de 6 semaines, dernière transfusion il y a 45 jours, soit 6,4 semaines ; retard = 6,4 - 6 = 0,4 semaine, sous 0,5, donc le bot affiche la prochaine date.
Ce que cet algorithme ne sait pas faire
- L'écran du bot lit seulement l'historique et la date du profil ; le service transfusion.py lit aussi la carte d'urgence. Une date connue seulement par la carte compte dans l'application et pas dans l'écran du bot.
- Le calcul du fuseau diffère : Genève dans transfusion.py, heure de la personne dans l'écran du bot et dans transfusion_stats.
- L'intervalle moyen mélange transfusions simples et échanges, alors que leurs rythmes cibles sont différents.
- Le programme n'a pas de seuil clinique : le retard est une simple soustraction, sans lien avec ton hémoglobine.
- Une transfusion vieille de plus de deux ans ne peut pas être notée.
- Le calcul du retard (WearableService.transfusion_status) dans le modèle de risque de crise n'a pas été lu ici.
Code : src/trevor/transfusion.py (transfusion_dates, last_transfusion, history, record_transfusion, clean_units, clean_minutes), src/trevor/conversation/wearables_screen.py (screen_transfusion, on_transfusion_units), src/trevor/conversation/carnet_screens.py (screen_transfusion_minutes, on_transfusion_minutes), src/trevor/storage/database.py (transfusion_stats)
