v1.9.2 : fix nuage vide sans traitement stocké ESE (filtre par ester effectif)
Bug remonté : activer Estrannaise dans le graphique puis le nuage n'affichait rien tant qu'aucun traitement n'était STOCKÉ avec le modèle ESE — « la seule méthode trouvée était de mettre un traitement en cours sur le modèle ESE, mais ça ne devrait pas être un prérequis ». Cause : le filtre du nuage testait le pkModel STOCKÉ du traitement, alors que la courbe ESE affichée redessine toutes les doses E2 avec modelOverride = "ESE" (peu importe le modèle stocké) — courbe et nuage n'utilisaient pas la même définition de « quelles doses sont tracées en ESE ». Fix : EstrannaiseCloud.compute couvre toutes les doses E2 à profil injectable dont l'ESTER EFFECTIF (override compris) est couvert par le fit Estrannaise — indépendamment du modèle stocké. L'exclusivité ESE reste portée par le chip (activable seulement si ESE est affiché). L'oral Bateman reste hors nuage (pas d'ester échantillonnable). Les traitements inactifs sont inclus (§6.bis : la courbe ESE les trace aussi). Tests réécrits (237 verts / 207 sans données locales) : « les doses d'un traitement TFS sont couvertes quand ESE est affiché » (épinglé Android + web) ; oral seul → vide (conservé). Validé émulateur §16.ter sur le profil réel de l'utilisatrice (EEn stocké TFS, ESE affiché, nuage visible autour de la courbe, 0 crash). Web miroir v1.9.2. versionCode 40 / versionName 1.9.2.
This commit is contained in:
parent
96a0dbc56a
commit
03fdbf61ed
@ -16,7 +16,7 @@ sauvegarde JSON. **100 % local, aucun compte, aucun serveur.**
|
||||
> à titre informatif — ce ne sont pas des mesures. Fie-toi toujours à tes analyses de
|
||||
> sang et aux consignes de ton endocrinologue.
|
||||
|
||||
- **Statut** : v1.9.1 — build Android ✅, **lint vert** ✅, **235 tests unitaires** ✅ (205 sans les données de test locales ; régressions épinglées sur données réelles **non versionnées**), intégration montre = notifications ✅, **dépôt Gitea privé + releases avec APK** ✅
|
||||
- **Statut** : v1.9.2 — build Android ✅, **lint vert** ✅, **237 tests unitaires** ✅ (207 sans les données de test locales ; régressions épinglées sur données réelles **non versionnées**), intégration montre = notifications ✅, **dépôt Gitea privé + releases avec APK** ✅
|
||||
- **Journal des versions** : [docs/CHANGELOG.md](docs/CHANGELOG.md)
|
||||
- **Guide utilisateur** : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md)
|
||||
- **Doc de développement** (architecture, maths, décisions, bugs) : [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md)
|
||||
@ -123,7 +123,7 @@ par AGP si les licences sont signées). Le wrapper télécharge Gradle 9.7.1.
|
||||
git clone <repo> && cd HormoneTrack
|
||||
echo "sdk.dir=/chemin/vers/android-sdk" > local.properties # ou ANDROID_HOME
|
||||
./gradlew assembleDebug # APK : app/build/outputs/apk/debug/app-debug.apk
|
||||
./gradlew testDebugUnitTest # 235 tests (205 sans les données locales)
|
||||
./gradlew testDebugUnitTest # 237 tests (207 sans les données locales)
|
||||
./gradlew lint # lint vert obligatoire avant release
|
||||
```
|
||||
|
||||
|
||||
@ -17,8 +17,8 @@ android {
|
||||
// tag : v1.3.0→v1.3.2 contenaient tous versionCode 14 / "1.3.0"
|
||||
// (bump jamais commité) → BuildConfig.VERSION_NAME était faux dans
|
||||
// les APK publiés (Paramètres + titre du dialog « Nouveautés »).
|
||||
versionCode = 39
|
||||
versionName = "1.9.1"
|
||||
versionCode = 40
|
||||
versionName = "1.9.2"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
||||
@ -16,13 +16,23 @@ import com.hormonetrack.data.model.TreatmentType
|
||||
* site estrannaise. TFS/WHSAH ne publient pas de posterior → pas de nuage
|
||||
* pour eux (choix confirmé : le nuage reste sur ESE).
|
||||
*
|
||||
* QU'EST-CE QUE LE NUAGE REPRÉSENTE : la superposition des doses des
|
||||
* traitements E2 **à profil ESE** évaluée avec UN échantillon MCMC par
|
||||
* courbe — chaque polyligne du nuage est « ce que serait la courbe si le
|
||||
* vrai paramètre physiologique était celui-là ». Les contributions des
|
||||
* autres traitements (TFS/WHSAH/Bateman) ne sont PAS incluses dans le
|
||||
* nuage : elles n'ont pas d'incertitude publiée (limite documentée — le
|
||||
* nuage montre l'incertitude du modèle Estrannaise, pas le niveau total).
|
||||
* QUELLES DOSES LE NUAGE COUVRE-T-IL (v1.9.2) : **toutes les doses E2 à
|
||||
* profil injectable dont l'ESTER EFFECTIF (override compris) est couvert
|
||||
* par le fit Estrannaise** — c'est exactement l'ensemble des doses que la
|
||||
* courbe ESE trace (`computeCurve(modelOverride = "ESE")`). Le `pkModel`
|
||||
* STOCKÉ du traitement n'importe pas : quand le chip Nuage est actif avec
|
||||
* ESE affiché, la courbe ESE redessine toutes ces doses avec le modèle
|
||||
* Estrannaise, et le nuage les redessine avec les paramètres MCMC de
|
||||
* LEUR ester. (Fix v1.9.2 : l'ancien filtre `pkModel stocké == ESE`
|
||||
* laissait le nuage vide dès qu'aucun traitement n'était stocké en ESE —
|
||||
* remontée : « le nuage ne s'active que autour du tracé émulé, pas autour
|
||||
* du modèle Estrannaise ». L'exclusivité ESE reste portée par le CHIP,
|
||||
* pas par le filtre des doses.)
|
||||
*
|
||||
* Les contributions Bateman (oral/gel — sans ester échantillonnable) ne
|
||||
* sont PAS dans le nuage : le posterior MCMC ne s'applique qu'aux esters
|
||||
* (limite documentée — le nuage montre l'incertitude du modèle
|
||||
* Estrannaise, pas le niveau total).
|
||||
*
|
||||
* DÉTERMINISME : les M échantillons sont **échelonnés** (stride sur les
|
||||
* 313, pas d'aléatoire) — le nuage est identique à chaque affichage et
|
||||
@ -78,34 +88,51 @@ object EstrannaiseCloud {
|
||||
): List<List<LevelPoint>> {
|
||||
if (startMs >= endMs || stepMs <= 0 || nbCurves < 2) return emptyList()
|
||||
|
||||
// ── Porteurs : traitements E2 actifs à profil ESE avec ester couvert ──
|
||||
val carriers = treatments.filter {
|
||||
it.type == TreatmentType.ESTRADIOL && it.isActive &&
|
||||
it.usesProfileModel && it.pkModel == PKModels.ESTRANNAISE &&
|
||||
EstrannaiseModels.hasModel(it.esterType)
|
||||
// ── Doses tracées par la courbe ESE (v1.9.2) ─────────────────────────
|
||||
// Toutes les doses E2 à profil injectable dont l'ESTER EFFECTIF
|
||||
// (override compris) est couvert par le fit Estrannaise — peu importe
|
||||
// le pkModel STOCKÉ du traitement : quand ESE est affiché,
|
||||
// computeCurve redessine toutes ces doses avec modelOverride = "ESE"
|
||||
// et le nuage doit les suivre (fix v1.9.2 : l'ancien filtre
|
||||
// `pkModel stocké == ESE` laissait le nuage vide dès qu'aucun
|
||||
// traitement n'était stocké en ESE — remontée : « le nuage ne
|
||||
// s'active que autour du tracé émulé, pas autour du modèle
|
||||
// Estrannaise ». L'exclusivité ESE reste portée par le CHIP, pas
|
||||
// par le filtre des doses). Les doses des traitements inactifs sont
|
||||
// incluses (§6.bis : l'inactif reste simulé, la courbe ESE les
|
||||
// trace aussi). La couche Bateman (oral/gel sans ester
|
||||
// échantillonnable) est naturellement exclue.
|
||||
val carrierDoses = ArrayList<Pair<DoseLog, Treatment>>()
|
||||
val samplesByEster = HashMap<String, List<EstrannaiseModels.Param>>()
|
||||
for (dose in doseLogs) {
|
||||
if (dose.timestamp > endMs) continue
|
||||
val tr = treatments.firstOrNull { it.id == dose.treatmentId } ?: continue
|
||||
if (tr.type != TreatmentType.ESTRADIOL || !tr.usesProfileModel) continue
|
||||
val ester = PharmacokineticEngine.doseEster(tr, dose)
|
||||
if (!EstrannaiseModels.hasModel(ester)) continue
|
||||
if (!samplesByEster.containsKey(ester)) {
|
||||
// Posterior MCMC de cet ester (chargé au démarrage) — vide si
|
||||
// l'asset n'est pas chargé → pas de nuage (sans posterior,
|
||||
// pas de plage d'imprécision à dessiner)
|
||||
val samples = EstrannaiseModels.mcmcSamples(ester)
|
||||
if (samples.isEmpty()) return emptyList()
|
||||
samplesByEster[ester] = samples
|
||||
}
|
||||
if (carriers.isEmpty()) return emptyList()
|
||||
|
||||
val carrierIds = carriers.map { it.id }.toSet()
|
||||
val carrierDoses = doseLogs
|
||||
.filter { it.treatmentId in carrierIds && it.timestamp <= endMs }
|
||||
.ifEmpty { return emptyList() }
|
||||
carrierDoses.add(dose to tr)
|
||||
}
|
||||
if (carrierDoses.isEmpty()) return emptyList()
|
||||
|
||||
// ── Échantillons échelonnés du posterior (déterministes) ─────────────
|
||||
// Un échantillon m est appliqué À TOUS les esters du nuage (même
|
||||
// index) : le nuage explore le posterior ester par ester, index par
|
||||
// index — reproductible.
|
||||
val samplesByEster = carriers.associate {
|
||||
it.esterType to EstrannaiseModels.mcmcSamples(it.esterType)
|
||||
}
|
||||
if (samplesByEster.values.any { it.isEmpty() }) return emptyList()
|
||||
val maxSamples = samplesByEster.values.minOf { it.size }
|
||||
val stride = (maxSamples.toDouble() / nbCurves.coerceAtMost(maxSamples)).toInt()
|
||||
.coerceAtLeast(1)
|
||||
|
||||
// Grille : de max(start, 1ʳᵉ dose du porteur) à endMs — M=0 avant la
|
||||
// 1ʳᵉ dose (rien à faire varier)
|
||||
val firstDoseMs = carrierDoses.minOf { it.timestamp }
|
||||
val firstDoseMs = carrierDoses.minOf { (dose, _) -> dose.timestamp }
|
||||
val t0 = maxOf(startMs, firstDoseMs)
|
||||
if (t0 > endMs) return emptyList()
|
||||
|
||||
@ -120,17 +147,15 @@ object EstrannaiseCloud {
|
||||
var t = t0
|
||||
while (t <= endMs) {
|
||||
var e2 = 0.0
|
||||
for (dose in carrierDoses) {
|
||||
for ((dose, tr) in carrierDoses) {
|
||||
if (dose.timestamp > t) continue
|
||||
val dtH = (t - dose.timestamp) / 3_600_000.0
|
||||
val tr = carriers.first { it.id == dose.treatmentId }
|
||||
val params = paramsByEster.getValue(tr.esterType)
|
||||
val ester = PharmacokineticEngine.doseEster(tr, dose)
|
||||
val params = paramsByEster.getValue(ester)
|
||||
// Même calibration que la courbe ESE : le nuage ENTOURE
|
||||
// la courbe calibrée (fix v1.9.1)
|
||||
val scale = scalePerEster?.get(
|
||||
PharmacokineticEngine.doseEster(tr, dose)
|
||||
) ?: tr.scaleFactor
|
||||
e2 += EstrannaiseModels.sampleWithParams(tr.esterType, dtH, params) * dose.doseAmount * scale
|
||||
val scale = scalePerEster?.get(ester) ?: tr.scaleFactor
|
||||
e2 += EstrannaiseModels.sampleWithParams(ester, dtH, params) * dose.doseAmount * scale
|
||||
}
|
||||
curve.add(LevelPoint(t, e2, 0.0))
|
||||
t += stepMs
|
||||
|
||||
@ -80,11 +80,24 @@ class EstrannaiseCloudTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non-ESE carriers produce no cloud (feature exclusive to ESE)`() {
|
||||
// TFS : pas de posterior publié → nuage vide
|
||||
val tfs = ese().copy(pkModel = "TFS")
|
||||
assertTrue(EstrannaiseCloud.compute(listOf(tfs), doses(), NOW - 30 * DAY_MS, NOW, 3_600_000L, NOW).isEmpty())
|
||||
// Oral (Bateman) : idem
|
||||
fun `doses of a TFS-stored treatment ARE covered when ESE is displayed (fix v1_9_2)`() {
|
||||
// FIX « le nuage ne s'active que autour du tracé émulé, pas autour du
|
||||
// modèle Estrannaise » : le traitement est STOCKÉ en TFS, mais quand
|
||||
// ESE est affiché, computeCurve(modelOverride = "ESE") redessine ses
|
||||
// doses avec le modèle Estrannaise → le nuage doit les couvrir aussi
|
||||
// (le pkModel stocké n'est pas un prérequis). Le nuage superpose les
|
||||
// doses EEn avec les params MCMC de EEN.
|
||||
val tfsStored = ese().copy(pkModel = "TFS")
|
||||
val cloud = EstrannaiseCloud.compute(listOf(tfsStored), doses(), NOW - 30 * DAY_MS, NOW, 3_600_000L, NOW)
|
||||
assertEquals(32, cloud.size)
|
||||
assertTrue(cloud.all { it.size >= 2 })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oral-only treatment produces no cloud (Bateman is not MCMC-sampled)`() {
|
||||
// L'oral Bateman n'a pas d'ester E2 échantillonnable : le posterior
|
||||
// MCMC ne s'applique pas → pas de nuage (cohérent : rien n'est tracé
|
||||
// « en ESE » pour ces doses)
|
||||
val oral = Treatment(
|
||||
id = 2, name = "CPA", type = TreatmentType.ANTI_ANDROGEN,
|
||||
route = AdministrationRoute.ORAL, doseAmount = 12.5, doseUnit = "mg",
|
||||
@ -93,6 +106,17 @@ class EstrannaiseCloudTest {
|
||||
assertTrue(EstrannaiseCloud.compute(listOf(oral), doses(), NOW - 30 * DAY_MS, NOW, 3_600_000L, NOW).isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unknown ester doses contribute zero (EUCS-free esters covered by ESE)`() {
|
||||
// PEP : pas de fit Estrannaise → ses doses contribuent 0 au nuage
|
||||
// (comme dans la courbe ESE) — le nuage des autres esters reste
|
||||
val pep = ese().copy(esterType = "PEP")
|
||||
val dosesPep = listOf(DoseLog(id = 9, treatmentId = 1, timestamp = NOW - 7 * DAY_MS, doseAmount = 25.0))
|
||||
val cloud = EstrannaiseCloud.compute(listOf(pep), dosesPep, NOW - 30 * DAY_MS, NOW, 3_600_000L, NOW)
|
||||
// PEP n'est pas couvert par le fit ESE → pas d'échantillon → nuage vide
|
||||
assertTrue(cloud.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no doses on the carrier produces no cloud`() {
|
||||
assertTrue(EstrannaiseCloud.compute(
|
||||
|
||||
@ -3,6 +3,31 @@
|
||||
Format : [Keep a Changelog](https://keepachangelog.com/fr-FR/1.1.0/).
|
||||
Versionnage : [SemVer](https://semver.org/). Releases taguées dans git (`v1.4.10`, …).
|
||||
|
||||
## [1.9.2] — 2026-09-19 (versionCode 40)
|
||||
|
||||
### Corrigé — le nuage d'incertitude exigeait un traitement STOCKÉ en ESE
|
||||
|
||||
- **Bug remonté** : activer Estrannaise dans le graphique puis le nuage
|
||||
n'affichait rien tant qu'aucun traitement n'était **stocké** avec le
|
||||
modèle ESE — « la seule méthode trouvée était de mettre un traitement en
|
||||
cours sur le modèle ESE, mais ça ne devrait pas être un prérequis ».
|
||||
- **Cause** : le filtre du nuage testait le `pkModel` **STOCKÉ** du
|
||||
traitement, alors que la courbe ESE affichée redessine toutes les doses
|
||||
E2 avec `modelOverride = "ESE"` (peu importe le modèle stocké) — courbe
|
||||
et nuage n'utilisaient pas la même définition de « quelles doses sont
|
||||
tracées en ESE ».
|
||||
- **Fix** : le nuage couvre **toutes les doses E2 à profil injectable dont
|
||||
l'ester effectif (override compris) est couvert par le fit Estrannaise**
|
||||
— indépendamment du modèle stocké. L'exclusivité ESE reste portée par le
|
||||
chip (activable seulement si ESE est affiché). L'oral Bateman reste hors
|
||||
nuage (pas d'ester échantillonnable). Les traitements inactifs sont
|
||||
inclus (§6.bis : la courbe ESE les trace aussi).
|
||||
- 2 tests réécrits (237 au total / 207 sans les données locales) :
|
||||
« doses d'un traitement TFS couvertes quand ESE est affiché » (le fix,
|
||||
épinglé des deux plateformes) ; oral seul → vide (conservé).
|
||||
- Validé émulateur §16.ter sur le profil réel (EEn stocké TFS, ESE
|
||||
activé) : nuage visible autour de la courbe ESE, 0 crash.
|
||||
|
||||
## [1.9.1] — 2026-09-19 (versionCode 39)
|
||||
|
||||
### Corrigé — le nuage d'incertitude entoure maintenant la courbe Estrannaise calibrée
|
||||
|
||||
@ -63,6 +63,7 @@ Fonctionnalités v1 :
|
||||
|
||||
| Date | Événement |
|
||||
|---|---|
|
||||
| 19 sept. 2026 (session v1.9.2) | **Fix nuage vide sans traitement stocké ESE** (remontée : « il ne s'active que autour du tracé émulé, pas autour du modèle Estrannaise ») : le filtre du nuage testait le pkModel STOCKÉ — or la courbe ESE redessine TOUTES les doses E2 (modelOverride ESE) quel que soit le modèle stocké. FIX : le nuage couvre les doses à ester effectif couvert par ESE (peu importe le stocké) ; l'exclusivité reste portée par le chip ; l'oral Bateman reste hors nuage. 2 tests réécrits (237 verts / 207 sans). Web miroir v1.9.2. |
|
||||
| 19 sept. 2026 (session v1.9.1) | **Fix nuage calibré + suggestion sur l'Accueil + explication +4 pg/mL** : (a) remontée « le nuage ne s'active que autour du tracé, pas autour du modèle Estrannaise » : la courbe ESE est CALIBRÉE (scalePerEster) mais le nuage était BRUT → échelles différentes. FIX : EstrannaiseCloud.compute reçoit le MÊME scalePerEster que la courbe ESE (Android + web miroir) ; test nuage calibré ×2 (Android). (b) Le +4 pg/mL constaté depuis v1.8.2 : l'ARRONDI de la médiane (fix v1.8.2, plus juste que la troncature) a déplacé une échelle auto-calibrée d'un cran — le niveau actuel est le CORRIGÉ, documenté CHANGELOG 1.9.1. (c) Émulateur : nuage visible autour de la courbe ESE calibrée (1363 px rose pâle, seed réel + traitement EV ESE de test), 0 crash. Web v1.9.1 sync (harmonisation de l'arrondi — les niveaux web/android convergent). 235 verts / 205 sans données locales. |
|
||||
| 17 sept. 2026 (session v1.9.0) | **Modèle Estrannaise ANALYTIQUE (abandon de l'ODS) + nuage d'incertitude MCMC (Android + web sync)** (demande) : découverte des sources originelles (github.com/WHSAH/estrannaise.js — forme close 3C + paramètres publiés + posterior MCMC 313/ester). Vérification numérique AVANT débranchement : **RMS 0,00 vs tables ODS** (l'ODS était l'échantillonnage de ces formules). ESE → analytique (dispatch, cutoff 10×t½, t½ LabTiming), PKProfileStore retiré du runtime (asset ODS → src/test/assets, −550 Ko d'APK), ESE couvre 6 esters (+EUCS), TFS = liste explicite (EUCS sans V3C TFS). **Nuage MCMC exclusif ESE** : chip `Nuage` (off, activable si ESE affiché, coupure auto si ESE off), 32 courbes du posterior déterministes, alpha faible sous les courbes, hors échelles/labels/extrema. 11 nouveaux tests (234 verts / 204 sans données locales) + lint + check web (166). Validé émulateur §16.ter : courbe ESE analytique affichée, unités, chips Extend/Cloud, 0 crash. |
|
||||
| 17 sept. 2026 (session v1.8.2) | **Maintenance — audit complet code + doc** : (a) **perf moteur** : cutoffHours pré-calculé par traitement dans e2At/computeCurve (le chemin chaud du tracé labs recalculait ~430 k appels/refresh), Bateman paresseux (inutile pour les traitements à profil), doses pré-groupées par traitement — résultat identique (ordre de sommation préservé, 223 tests verts inchangés) ; (b) **2 fixes de fraîcheur** (#65 accueil : clés produceState sans allTreatments/tConfig — éditer un scaleFactor n'actualisait pas la courbe avant le tick ; #66 graphiques : clé `labResults.size` — éditer la valeur d'un lab ne recalculait pas) ; (c) **nettoyage** : ~9 imports morts, qualifications raccourcies, code mort (Repository/DAO, currentLevel, scheduler2), médiane factorisée (4 copies), SimpleDateFormat hors boucle, AppLog sans recopie, Regex précompilées, formatDose dédupliqué ; (d) **doc** : ~35 corrections (compteurs actuels 223/193 partout, TOC + ancres réparées, inventaire §8 à jour et fusionné, doublons CHANGELOG, §19 backup, fautes). Audit préalable par double exploration lecture-seule (46 fichiers + 4 docs). 223 verts + lint + émulateur smoke. Android seul (web non concerné — re-render complet, aucune release web). |
|
||||
@ -667,13 +668,21 @@ devient à son tour**.
|
||||
l'ODS n'était que l'échantillonnage horaire de ces formules. Épinglé par
|
||||
`EstrannaiseModelsTest` (Android) et `estrannaise-models.test.js` (web).
|
||||
- **Débranchement runtime** : `concentrationOfDose` dispatche ESE →
|
||||
[EstrannaiseModels.sample] (plus de fallback
|
||||
[PKProfileStore.sample]) ; `cutoffHours` ESE → 10 × t½ terminale
|
||||
analytique (plus la longueur de table) ; [LabTiming] t½ ESE analytique ;
|
||||
[PKProfileStore.init] retirée du démarrage. **L'asset `pk_profiles.json`
|
||||
quitte `src/main/assets` (−550 Ko d'APK) pour `src/test/assets/`** :
|
||||
l'ODS survit UNIQUEMENT comme référence des tests de fidélité.
|
||||
[PKProfileStore] est conservé (lecteur pur) pour ces tests.
|
||||
`EstrannaiseModels.sample` (plus de fallback `PKProfileStore.sample`) ;
|
||||
`cutoffHours` ESE → 10 × t½ terminale analytique (plus la longueur de
|
||||
table) ; `LabTiming` t½ ESE analytique ; `PKProfileStore.init` retirée
|
||||
du démarrage. **L'asset `pk_profiles.json` quitte `src/main/assets`
|
||||
(−550 Ko d'APK) pour `src/test/assets/`** : l'ODS survit UNIQUEMENT
|
||||
comme référence des tests de fidélité. `PKProfileStore` est conservé
|
||||
(lecteur pur) pour ces tests.
|
||||
- **Quelles doses le nuage couvre-t-il (v1.9.2)** : toutes les doses E2 à
|
||||
profil injectable dont l'**ester effectif** (override compris) est
|
||||
couvert par le fit Estrannaise — indépendamment du `pkModel` stocké du
|
||||
traitement (fix v1.9.2 : l'ancien filtre `pkModel stocké == ESE` laissait
|
||||
le nuage vide dès qu'aucun traitement n'était stocké en ESE — la courbe
|
||||
ESE affichée trace pourtant toutes ces doses via modelOverride). Les
|
||||
doses des traitements inactifs sont incluses (§6.bis) ; l'oral Bateman
|
||||
reste hors nuage (pas d'ester échantillonnable).
|
||||
- **Couverture étendue** : ESE passe de 3 à **6 esters injectables**
|
||||
(EV/EU/EEn/EC/EB/**EUCS**) — `Esters.choicesForModel` mis à jour (TFS
|
||||
reste 7, WHSAH 6 ; ⚠️ TFS = liste EXPLICITE : EUCS n'a pas de V3C TFS).
|
||||
@ -2292,6 +2301,13 @@ Sur le téléphone de test (à compléter par l'utilisatrice) :
|
||||
recommandé → la carte passe au creux SUIVANT ; enlever la Posologie
|
||||
→ carte remplacée par l'invite « renseigne une Posologie » ;
|
||||
traitement oral seul → aucune carte
|
||||
- [ ] **v1.9.2** : **nuage sans traitement stocké ESE** : avec un
|
||||
traitement E2 injectable STOCKÉ en TFS (ou autre), activer Estrannaise
|
||||
dans le graphique → la courbe ESE apparaît ; activer Nuage (exclusif
|
||||
ESE) → le nuage ENTOURE la courbe ESE tracée sur ces doses (le
|
||||
pkModel stocké n'est pas un prérequis) ; l'oral Bateman seul → pas de
|
||||
nuage ; l'exclusivité ESE reste sur le chip
|
||||
|
||||
- [ ] **v1.8.1** : **stabilisation corrigée + suggestion Accueil** : après
|
||||
changement de dose/ester/intervalle, la carte ne dit plus « stabilisé
|
||||
depuis » l'ancienne période (régime = séquence terminale (ester, dose,
|
||||
|
||||
@ -221,7 +221,9 @@ Pics de référence (dose unique de 5 mg, IM) :
|
||||
- **Nuage d'incertitude (v1.9.0, exclusif ESE)** : chip `Nuage` (actif
|
||||
seulement quand Estrannaise est affiché) → nuage diffus de courbes du
|
||||
posterior MCMC d'Estrannaise montrant la plage d'imprécision du modèle
|
||||
(comme sur le site estrannaise)
|
||||
(comme sur le site estrannaise). v1.9.2 : il couvre toutes les doses E2
|
||||
injectables quand ESE est affiché — aucun traitement n'a besoin d'être
|
||||
stocké en ESE
|
||||
- **Unités sur les axes** (v1.7.0) : l'axe gauche affiche « pg/mL » (E2) et
|
||||
l'axe droit « ng/mL » (T, avec le toggle T) au sommet des colonnes de
|
||||
labels — plus besoin de deviner l'unité des nombres
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user