Documentation v1.2.0 : changelog, guide (fonctions du graphique), doc de dev (AGP 9, migrations, panoramique, prévision, auto-cal), README (git, features)

- docs/CHANGELOG.md : entrée v1.2.0 complète
- docs/DEVELOPPEMENT.md : versions épinglées à jour, note Material Expressive
  (API encore internal), migration Room v1→v2, §7.3b (override modèle/prévision/
  auto-cal), §8 (36 tests), §10/§11 (panoramique, multi-séries), §16 (workflow git),
  §18 (disque), historique des sessions
- README : statut v1.2.0, features à jour, section Git (tags de release)
- GUIDE : nouvelle section « Fonctions du graphique » (panoramique, toggles
  modèles, prévision, calibration auto)
This commit is contained in:
Siphonight 2026-09-05 16:58:32 +02:00
parent 8ab97d4ed4
commit e8f87c096d
4 changed files with 210 additions and 62 deletions

View File

@ -9,7 +9,7 @@ sauvegarde JSON. **100 % local, aucun compte, aucun serveur.**
> à titre informatif — ce ne sont pas des mesures. Fie-toi toujours à tes analyses de > à titre informatif — ce ne sont pas des mesures. Fie-toi toujours à tes analyses de
> sang et aux consignes de ton endocrinologue. > sang et aux consignes de ton endocrinologue.
- **Statut** : v1.1.0 — build Android ✅, 30 tests unitaires ✅ (dont régression sur données réelles), intégration montre = notifications ✅ - **Statut** : v1.2.0 — build Android ✅, **36 tests unitaires** ✅ (dont régression sur données réelles), intégration montre = notifications ✅, **repo git avec releases taguées** ✅
- **Journal des versions** : [docs/CHANGELOG.md](docs/CHANGELOG.md) - **Journal des versions** : [docs/CHANGELOG.md](docs/CHANGELOG.md)
- **Guide utilisateur** : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.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) - **Doc de développement** (architecture, maths, décisions, bugs) : [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md)
@ -17,38 +17,59 @@ sauvegarde JSON. **100 % local, aucun compte, aucun serveur.**
## Fonctionnalités ## Fonctionnalités
- **Courbes estimées heure par heure** : E2 (pg/mL) et T (ng/mL), vue 24 h / 7 j / 30 j - **Courbes estimées heure par heure** : E2 (pg/mL) et T (ng/mL), vue 24 h / 7 j / 30 j,
- **Deux modèles PK au choix** (issus de la feuille `Estrogen.ods` de l'autrice) : **panoramique** (glisser pour remonter dans le passé)
**Estrannaise (EstraNase)** et **Transfem Science**, pour les injections EV / EU / EEn - **Deux modèles PK au choix, superposables** (issus de la feuille `Estrogen.ods`) :
**Estrannaise (EstraNase)** et **Transfem Science** pour les injections EV / EU / EEn,
affichés côte à côte avec toggles indépendants
- **Modèle Bateman** paramétrable (temps au pic, demi-vie, biodisponibilité) pour gel, - **Modèle Bateman** paramétrable (temps au pic, demi-vie, biodisponibilité) pour gel,
patch et voie orale patch et voie orale
- **Simulation prévisionnelle** : configurer la **fréquence d'injection** (jours) sur un
traitement → projection des doses à venir sur le graphique (jamais sauvegardées)
- **Log des doses** avec date/heure exacte, dose en mg, **ester par injection** - **Log des doses** avec date/heure exacte, dose en mg, **ester par injection**
(switch EV↔EU↔EEn comme dans le tableur), **éditable** (tap sur une ligne dans Doses) (switch EV↔EU↔EEn comme dans le tableur), **éditable** (tap sur une ligne dans Doses),
**intervalle en jours entre dosages** affiché
- **Analyses de sang** (E2, T, PRL…) tracées sur les courbes comme points de calibration - **Analyses de sang** (E2, T, PRL…) tracées sur les courbes comme points de calibration
- **Calibration** : facteur d'échelle par traitement = médiane(lab ÷ prédiction du modèle), - **Calibration** : facteur d'échelle par traitement = médiane(lab ÷ prédiction du modèle),
calculé automatiquement (« Scale factor » du `.ods`, automatisé) calculé automatiquement (« Scale factor » du `.ods`, automatisé) — ou **calibration
automatique permanente** (option, désactivée par défaut)
- **Estimation T** empirique `T = plancher + (base − plancher) ÷ (1 + k·E2)`, calibrable - **Estimation T** empirique `T = plancher + (base − plancher) ÷ (1 + k·E2)`, calibrable
avec tes résultats T avec tes résultats T (unités ng/mL, ng/dL, ng/L, nmol/L acceptées)
- **Rappels quotidiens** avec actions **« Pris » / « Reporter 1 h »** dans la notification ; - **Rappels quotidiens** avec actions **« Pris » / « Reporter 1 h »** dans la notification ;
les notifications remontent sur la Watch GT 3 (Gadgetbridge ou Huawei Health) les notifications remontent sur la Watch GT 3 (Gadgetbridge ou Huawei Health)
- **Sauvegarde/Restauration JSON** complète (traitements + doses + analyses + réglages T) - **Sauvegarde/Restauration JSON** complète (traitements + doses + analyses + réglages T)
- **FR + EN** (langue par app, indépendante du système) - **FR + EN** (langue par app, indépendante du système)
- UI Jetpack Compose récente (BOM 2026.08, Material You) ; 100 % local, aucun compte
## Démarrage rapide (build depuis les sources) ## Démarrage rapide (build depuis les sources)
Prérequis : JDK 17+ (Java 21 OK), Android SDK (API 34). Le wrapper télécharge Gradle 8.9. Prérequis : JDK 17+ (Java 21 OK), Android SDK (la plateforme 37 sera auto-téléchargée
par AGP si les licences sont signées). Le wrapper télécharge Gradle 9.7.1.
```bash ```bash
git clone <repo> && cd HormoneTrack git clone <repo> && cd HormoneTrack
echo "sdk.dir=/chemin/vers/android-sdk" > local.properties # ou ANDROID_HOME echo "sdk.dir=/chemin/vers/android-sdk" > local.properties # ou ANDROID_HOME
./gradlew assembleDebug # APK : app/build/outputs/apk/debug/app-debug.apk ./gradlew assembleDebug # APK : app/build/outputs/apk/debug/app-debug.apk
./gradlew testDebugUnitTest # 24 tests (moteur PK, profils, backup) ./gradlew testDebugUnitTest # 36 tests (moteur PK, profils, backup, régression)
``` ```
Installation sur un téléphone : mode développeur + Débogage USB, puis Android Studio Installation sur un téléphone : mode développeur + Débogage USB, puis Android Studio
(**Run ▶️**) ou `adb install -r app/build/outputs/apk/debug/app-debug.apk`. (**Run ▶️**) ou `adb install -r app/build/outputs/apk/debug/app-debug.apk`.
Pas de Play Store : l'app est sideloadée. Détails pas-à-pas : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md). Pas de Play Store : l'app est sideloadée. Détails pas-à-pas : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md).
## Git
Dépôt initialisé avec des **releases taguées** (`v1.1.0`, `v1.2.0`, …) :
```bash
git tag # lister les releases
git log --oneline # historique par couches (toolchain / moteur / UI / docs)
git push -u origin main --tags # après ajout d'un remote
```
Chaque commit de release passe `./gradlew testDebugUnitTest` (vert obligatoire) et est
taggué annoté. Voir [docs/DEVELOPPEMENT.md §16](docs/DEVELOPPEMENT.md).
## Les modèles en bref ## Les modèles en bref
Chaque injection contribue `dose_mg × profil(dt)` où `profil` est la réponse normalisée Chaque injection contribue `dose_mg × profil(dt)` où `profil` est la réponse normalisée

View File

@ -1,7 +1,49 @@
# Changelog — HormoneTrack # Changelog — HormoneTrack
Format : [Keep a Changelog](https://keepachangelog.com/fr-FR/1.1.0/). Format : [Keep a Changelog](https://keepachangelog.com/fr-FR/1.1.0/).
Versionnage : [SemVer](https://semver.org/). Versionnage : [SemVer](https://semver.org/). Releases taguées dans git (`v1.2.0`, …).
## [1.2.0] — 2026-09-05 (versionCode 3)
### Ajouté
- **Panoramique du graphique** : fais glisser le graphique vers la droite pour
remonter dans le passé (fenêtre glissante sur tout l'historique) ; bouton
« Revenir à maintenant ».
- **Les deux modèles PK superposables** : Estrannaise (bleu) et Transfem Science
(turquoise) tracées simultanément sur le même graphique, avec toggles
indépendants (override de modèle dans le moteur, indépendant du modèle stocké
par traitement).
- **Simulation prévisionnelle** : nouvelle section « Fréquence d'injection » dans
l'éditeur de traitement (intervalle en jours) → chip « Prévision » du graphique
qui projette les doses à venir (2× l'intervalle, borné 7–30 j) à partir de la
dernière injection réelle ; les doses projetées ne sont jamais sauvegardées et
conservent l'ester override de la dernière injection.
- **Calibration automatique (optionnelle)** : dans les Paramètres, option
« Calibration automatique » — ajuste à la volée (affichage seul) les facteurs
d'échelle et le modèle T depuis les labs ; désactivée par défaut, les valeurs
stockées ne sont jamais modifiées.
- **Intervalle entre dosages** : l'écran Doses affiche Δ (jours) entre chaque dose
et la précédente du même traitement (colonne « Interval (d) » du `.ods`).
- 6 nouveaux tests (`V120FeaturesTest`) → **36 tests**.
### Modifié
- **Montée des toolchains** : Gradle 9.7.1, **AGP 9.4.0** (Kotlin intégré — plus de
plugin `org.jetbrains.kotlin.android`, `kotlinOptions` supprimé), Kotlin 2.3.21,
KSP 2.3.11, **Compose BOM 2026.08.00** (Compose 1.12), Room 2.8.4,
Navigation 2.10.0, Activity 1.13.0, AppCompat 1.8.0, DataStore 1.2.1, Gson 2.14.0,
compileSdk/targetSdk **37**.
- Note Material You / Expressive : la BOM récente apporte composants et comportements
Material You ; `MaterialExpressiveTheme` reste **internal** dans la ligne material3
pinnée (vérifié par compilation) → `MaterialTheme` standard conservé, à basculer
dès que l'API devient publique (commenté dans `ui/theme/Theme.kt`).
### Corrigé
- **Horloge du TimePicker décentrée** (partait vers la gauche dans les dialogs de
saisie) : centrage explicite (Box + Center) dans `DateTimeField` et l'éditeur de
traitement.
- Migration Room **v1 → v2** propre (colonne `forecastIntervalDays`) avec
suppression du `fallbackToDestructiveMigration()` — les données réelles sont
protégées : une migration manquante plante bruyamment au lieu d'effacer.
## [1.1.0] — 2026-09-05 (versionCode 2) ## [1.1.0] — 2026-09-05 (versionCode 2)
@ -17,8 +59,8 @@ Versionnage : [SemVer](https://semver.org/).
33 et 44 ng/dL). L'axe T du chart mélangeait alors des ng/dL avec des estimations en 33 et 44 ng/dL). L'axe T du chart mélangeait alors des ng/dL avec des estimations en
ng/mL (erreur ×100, courbe T invisible) et la calibration T aurait été faussée d'un ng/mL (erreur ×100, courbe T invisible) et la calibration T aurait été faussée d'un
facteur 100. Fix : `PharmacokineticEngine.convertTToNgMl()` (ng/dL → ×0,01 ; facteur 100. Fix : `PharmacokineticEngine.convertTToNgMl()` (ng/dL → ×0,01 ;
ng/L → ×0,001 ; nmol/L → ×0,2884 ; ng/mL inchangé), appliqué à l'affichage ng/L → ×0,001 ; nmol/L → ×0,2884 ; ng/mL inchangé), appliqué à la calibration **et
(à intégrer dans le rendu du chart) et à la calibration. au rendu du chart**.
### Ajouté ### Ajouté
- **Édition des doses** (le bug rapporté : « on ne peut pas modifier les entrées - **Édition des doses** (le bug rapporté : « on ne peut pas modifier les entrées

View File

@ -62,32 +62,48 @@ Fonctionnalités v1 :
| 5 sept. 2026 | L'utilisatrice mentionne un travail d'un assistant tiers « **Mimo V2.5** » : **aucune trace trouvée** (fichiers identiques à la session 1, timestamps identiques). Reprise depuis l'état existant. Bugs trouvés au passage : settings.gradle, BootReceiver, cancel PendingIntent. | | 5 sept. 2026 | L'utilisatrice mentionne un travail d'un assistant tiers « **Mimo V2.5** » : **aucune trace trouvée** (fichiers identiques à la session 1, timestamps identiques). Reprise depuis l'état existant. Bugs trouvés au passage : settings.gradle, BootReceiver, cancel PendingIntent. |
| 5 sept. 2026 (session build) | Redesign données (ester/pkModel/scaleFactor), réécriture moteur PK sur tables ODS, modèle T + calibration, rappels complets, UI 6 écrans, chart Canvas, backup JSON, i18n, wrapper Gradle, guide. **Installation SDK Android (brew) + premier build.** | | 5 sept. 2026 (session build) | Redesign données (ester/pkModel/scaleFactor), réécriture moteur PK sur tables ODS, modèle T + calibration, rappels complets, UI 6 écrans, chart Canvas, backup JSON, i18n, wrapper Gradle, guide. **Installation SDK Android (brew) + premier build.** |
| 5 sept. 2026 (session tests/docs) | Correction de toutes les erreurs de compilation (dont 3 vrais bugs logiciels trouvés par les tests), **24 tests unitaires verts**, APK debug généré (18 MB), documentation complète (README + docs/), préparation repo git. | | 5 sept. 2026 (session tests/docs) | Correction de toutes les erreurs de compilation (dont 3 vrais bugs logiciels trouvés par les tests), **24 tests unitaires verts**, APK debug généré (18 MB), documentation complète (README + docs/), préparation repo git. |
| 5 sept. 2026 (session v1.1.0) | Bugs remontés par l'utilisatrice : courbes vides (casse EEn) + pas d'édition des doses → corrigés ; régression épinglée sur ses données réelles ; APK v1.1.0. |
| 5 sept. 2026 (session v1.2.0) | **Repo git initialisé** (commits par couche + tags de release) ; montée toolchain AGP 9.4/Gradle 9.7.1/Kotlin 2.3.21/BOM 2026.08.00/compileSdk 37 ; panoramique du chart, superposition des deux modèles, prévision par « Fréquence », calibration automatique optionnelle, intervalles entre doses, TimePicker centré ; **36 tests verts**, APK v1.2.0 (23 MB). |
Leçon importante de la session build : **les erreurs de compilation et les bugs sémantiques Leçon importante de la session build : **les erreurs de compilation et les bugs sémantiques
(bisection inversée, plancher d'affichage des profils) n'ont été détectés qu'en construisant (bisection inversée, plancher d'affichage des profils) n'ont été détectés qu'en construisant
et en testant** — aucun build n'avait été lancé avant la session 3. et en testant** — aucun build n'avait été lancé avant la session 3.
## 3. Stack & versions (épinglées) ## 3. Stack & versions (épinglées — à jour v1.2.0)
| Composant | Version | Où | | Composant | Version | Où |
|---|---|---| |---|---|---|
| Gradle | 8.9 (wrapper jar v8.9.0) | `gradle/wrapper/` | | Gradle | **9.7.1** (wrapper) | `gradle/wrapper/gradle-wrapper.properties` |
| AGP | 8.5.2 | `build.gradle.kts` racine | | AGP | **9.4.0** — **Kotlin intégré** : ne PAS appliquer `org.jetbrains.kotlin.android` ; `kotlinOptions` supprimé (cible JVM via `compileOptions`, 17) | `build.gradle.kts` racine |
| Kotlin | 2.0.0 + plugin compose 2.0.0 | idem | | Kotlin | 2.3.21 (plugin compose 2.3.21) | idem |
| KSP | 2.0.0-1.0.21 | idem | | KSP | 2.3.11 (versionnage indépendant depuis KSP2) | idem |
| Compose BOM | 2024.06.00 | `app/build.gradle.kts` | | Compose BOM | **2026.08.00** (Compose 1.12 ; material3 pinné par le BOM) | `app/build.gradle.kts` |
| Room | 2.6.1 (KSP) | idem | | Room | 2.8.4 (KSP) — **DB v2 + MIGRATION_1_2**, `fallbackToDestructiveMigration` retiré | idem + `AppDatabase.kt` |
| Navigation Compose | 2.7.7 | idem | | Navigation Compose | 2.10.0 | idem |
| AppCompat | 1.7.0 (langue par app) | idem | | AppCompat | 1.8.0 (langue par app) | idem |
| DataStore Preferences | 1.1.1 | idem | | DataStore Preferences | 1.2.1 | idem |
| Gson | 2.11.0 | idem | | Gson | 2.14.0 | idem |
| JUnit | 4.13.2 (testImplementation) | idem | | JUnit | 4.13.2 (testImplementation) | idem |
| WorkManager | 2.9.1 (**déclaré, non utilisé — supprimable**) | idem | | WorkManager | 2.11.2 (déclaré, non utilisé — supprimable) | idem |
| compileSdk/targetSdk | 34 ; minSdk 26 ; Java target 17 | app | | compileSdk / targetSdk | **37** / 37 ; minSdk 26 ; Java target 17 | app |
Kotlin 2.0 → compose compiler via `org.jetbrains.kotlin.plugin.compose`. Room convertit Notes importantes (v1.2.0) :
- **AGP 9** : Kotlin est intégré à AGP — appliquer `org.jetbrains.kotlin.android` est une
erreur ; le plugin `org.jetbrains.kotlin.plugin.compose` reste appliqué normalement.
- **compileSdk 37** : la plateforme `platforms;android-37` n'était pas dans sdkmanager
(API 37 en preview à la date du build) mais **AGP l'a auto-téléchargée** (licences
signées) — le build passe.
- **Compose 1.12 (BOM 2026.08.00) exige compileSdk ≥ 37 et AGP ≥ 9.1** ; le BOM
2026.06.01 est le dernier compatible compileSdk 36.
- **Material Expressive** : `MaterialExpressiveTheme` / `ExperimentalMaterial3ExpressiveApi`
sont encore **internal** dans la ligne material3 pinnée par ce BOM (erreur de
compilation vérifiée — javap montre `public` JVM mais la visibilité **Kotlin** est
internal). `MaterialTheme` standard conservé ; basculer dès que l'API devient
publique (NOTE dans `ui/theme/Theme.kt`).
- Kotlin 2.0 → compose compiler via `org.jetbrains.kotlin.plugin.compose`. Room convertit
les enums ↔ String automatiquement. **Ne pas monter Kotlin/AGP/Gradle sans vérifier la les enums ↔ String automatiquement. **Ne pas monter Kotlin/AGP/Gradle sans vérifier la
matrice de compatibilité.** matrice de compatibilité** (les versions sont récupérées via maven-metadata.xml de
dl.google.com / repo1.maven.org, pas devinées).
## 4. Environnement de build (cette machine) ## 4. Environnement de build (cette machine)
@ -129,8 +145,9 @@ Points clés :
## 6. Modèle de données (Room) ## 6. Modèle de données (Room)
DB `hormonetrack.db`, version 1, **`fallbackToDestructiveMigration()`** (⚠️ à retirer DB `hormonetrack.db`, **version 2**, migrations explicites (⚠️ plus de
avant toute migration réelle — sinon perte de données silencieuse). `fallbackToDestructiveMigration` — retiré en v1.2.0 car l'utilisatrice a des données
réelles ; toute évolution de schéma = `Migration(x, y)` + ALTER TABLE).
### `Treatment` (treatments) ### `Treatment` (treatments)
- base : `id`, `name`, `type` (ESTRADIOL/ANTI_ANDROGEN/PROGESTOGEN/OTHER), `route` - base : `id`, `name`, `type` (ESTRADIOL/ANTI_ANDROGEN/PROGESTOGEN/OTHER), `route`
@ -140,6 +157,8 @@ avant toute migration réelle — sinon perte de données silencieuse).
("ESE"/"TFS" — objets `PKModels`) ("ESE"/"TFS" — objets `PKModels`)
- PK Bateman : `absorptionHours` (Tmax), `eliminationHalfLifeHours`, `bioavailabilityFraction` - PK Bateman : `absorptionHours` (Tmax), `eliminationHalfLifeHours`, `bioavailabilityFraction`
- Calibration : `scaleFactor` (défaut 1.0) - Calibration : `scaleFactor` (défaut 1.0)
- **Prévision (v1.2)** : `forecastIntervalDays: Double?` (jours ; null = pas de
simulation à venir) — colonne ajoutée par la **migration Room v1→v2**
- Rappel : `reminderHour/Minute/Enabled` - Rappel : `reminderHour/Minute/Enabled`
- Helpers : `isInjection` (IM/SC), `usesProfileModel` (injection **et** ester ≠ NONE) - Helpers : `isInjection` (IM/SC), `usesProfileModel` (injection **et** ester ≠ NONE)
@ -205,6 +224,22 @@ de toutes les doses E2, chacune multipliée par le `scaleFactor` de son traiteme
Coupure par dose : `cutoffHours` = longueur de table (8001 h) pour les profils, Coupure par dose : `cutoffHours` = longueur de table (8001 h) pour les profils,
`30 × t½` pour Bateman. `30 × t½` pour Bateman.
### 7.3b Override de modèle + prévision + auto-calibration (v1.2)
- **`modelOverride`** : paramètre optionnel de `concentrationOfDose` / `e2At` /
`computeCurve` qui force ESE ou TFS pour les traitements par profil — le graphique
dessine les deux modèles côte à côte depuis le même traitement (Bateman non concerné :
les deux séries y sont identiques).
- **`generateForecastDoses(treatment, doseLogs, toMs, nowMs)`** : projette les doses à
venir = dernière dose réelle + k × `forecastIntervalDays` jusqu'à `toMs`, strictement
après `nowMs` ; dose = standard du traitement, ester = override de la dernière
injection. Jamais persistées : uniquement passées à `computeCurve` par le ChartScreen
quand le chip « Prévision » est actif.
- **`autoCalibrated(treatments, doseLogs, labs, tConfig)`** : option « Calibration
automatique » — renvoie des **copies** de traitements avec les scale factors
recalculés (médiane lab ÷ prédiction) + TConfig recalibré. Les valeurs stockées ne
bougent jamais ; HomeScreen et ChartScreen branchent dessus quand l'option est active.
### 7.4 Bateman (gel/patch/oral) ### 7.4 Bateman (gel/patch/oral)
`C(dt) = (F·D·ka/(ka−ke))·(e^(−ke·dt) − e^(−ka·dt))` ; cas dégénéré ka≈ke : `C(dt) = (F·D·ka/(ka−ke))·(e^(−ke·dt) − e^(−ka·dt))` ; cas dégénéré ka≈ke :
@ -220,6 +255,11 @@ pas la T) — modèle d'inhibition simple, étiqueté « estimation » partout.
Calibration : `k_i = ((base−floor)/(T_lab − floor) − 1)/E2_est(t_lab)`, garde Calibration : `k_i = ((base−floor)/(T_lab − floor) − 1)/E2_est(t_lab)`, garde
k ∈ (1e-4, 10), **médiane** (plante k=0.25 → recalibre 0.25 ±15 %, testé). k ∈ (1e-4, 10), **médiane** (plante k=0.25 → recalibre 0.25 ±15 %, testé).
**Unités** : les labs T peuvent être saisis en ng/mL, ng/dL, ng/L ou nmol/L —
`convertTToNgMl(value, unit)` normalise (ng/dL ÷100, ng/L ÷1000, nmol/L ×0,2884) ;
appliqué à la calibration ET au rendu du chart (sinon l'axe T est faux d'un facteur 100,
bug réel remonté par l'utilisatrice : labs 33/44 ng/dL).
### 7.6 Calibration E2 (par traitement) ### 7.6 Calibration E2 (par traitement)
`computeScaleFactor(treatment, allDoseLogs, e2Labs)` : `computeScaleFactor(treatment, allDoseLogs, e2Labs)` :
@ -237,7 +277,7 @@ Type de retour : `LevelPoint(timestamp, e2, t)`.
## 8. Tests unitaires ## 8. Tests unitaires
**30 tests JVM, tous verts** (`./gradlew testDebugUnitTest`). Dépendance : JUnit 4.13.2. **36 tests JVM, tous verts** (`./gradlew testDebugUnitTest`). Dépendance : JUnit 4.13.2.
Emplacement : `app/src/test/java/com/hormonetrack/`. Répertoire de travail d'exécution = Emplacement : `app/src/test/java/com/hormonetrack/`. Répertoire de travail d'exécution =
`app/` → l'asset est lu via `src/main/assets/pk_profiles.json` (fallback `app/src/…`). `app/` → l'asset est lu via `src/main/assets/pk_profiles.json` (fallback `app/src/…`).
@ -259,6 +299,11 @@ Emplacement : `app/src/test/java/com/hormonetrack/`. Répertoire de travail d'ex
labs antérieurs à la 1ʳᵉ dose ignorés par la calibration SF, conversion ng/dL→ng/mL, labs antérieurs à la 1ʳᵉ dose ignorés par la calibration SF, conversion ng/dL→ng/mL,
calibration T avec labs en ng/dL. **En cas de nouveau bug remonté par l'utilisatrice : calibration T avec labs en ng/dL. **En cas de nouveau bug remonté par l'utilisatrice :
exporter le JSON, l'épingler ici, reproduire, corriger.** exporter le JSON, l'épingler ici, reproduire, corriger.**
- **`V120FeaturesTest`** (6) : v1.2.0 — doses prévisionnelles (rythme 7 j depuis la
dernière dose réelle, liste exacte J+4/J+11/J+18/J+25 ; vide sans intervalle ou sans
doses ; ester override projeté), override de modèle (ESE ≠ TFS à 45 h pour EV ;
sans override = modèle du traitement), auto-calibration (SF 0,72 recalculé depuis un
lab planté, T recalibré, **originaux non modifiés** ; inchangée sans lab utilisable).
**Ce que les tests ont déjà attrapé** : bisection inversée de `computeKa` (présente depuis **Ce que les tests ont déjà attrapé** : bisection inversée de `computeKa` (présente depuis
la session 1 !), plancher 0,01 des queues de profils, mapping silencieux du modèle inconnu. la session 1 !), plancher 0,01 des queues de profils, mapping silencieux du modèle inconnu.
@ -294,38 +339,57 @@ Sur la montre : remontée par Gadgetbridge **ou** Huawei Health (cf §17).
`settings`, `treatment_edit/{id}` (-1 = nouveau) ; barre masquée sur ces 2 routes `settings`, `treatment_edit/{id}` (-1 = nouveau) ; barre masquée sur ces 2 routes
- `HomeScreen` : bandeau gradient (TransSky→TransPink, discret), carte **niveau actuel** - `HomeScreen` : bandeau gradient (TransSky→TransPink, discret), carte **niveau actuel**
(E2 ≈ X pg/mL, T ≈ Y ng/mL, delta vs 6 h), carte prochaine dose, chips de log rapide (E2 ≈ X pg/mL, T ≈ Y ng/mL, delta vs 6 h), carte prochaine dose, chips de log rapide
(+ FAB), mini-chart 24 h, disclaimer ; rafraîchissement `tick` 60 s (+ FAB), mini-chart 24 h (multi-séries via `ChartSeries`), disclaimer ; données
- `ChartScreen` : 24 h/7 j/30 j, toggles T + labs, chart 320 dp + légende auto-calibrées si l'option est active ; rafraîchissement `tick` 60 s
- `DosesScreen` : LazyColumn par jour (desc), suppression avec confirmation, FAB → - `ChartScreen` (v1.2, le plus riche) : plages 24 h/7 j/30 j ; **panoramique**
`LogDoseDialog` (traitement, dose, DateTimeField, notes) (`detectHorizontalDragGestures` — tirer vers la droite remonte dans le passé,
`panHours` borné à [0, âge de la 1ʳᵉ dose + plage], bouton « Revenir à maintenant ») ;
**toggles indépendants Estrannaise/TFS** → deux `computeCurve` avec `modelOverride`
superposées (E2 ESE bleu plein, E2 TFS turquoise, T ESE rose plein, T TFS rose
pointillé) ; **chip Prévision** (doses projetées via `generateForecastDoses`, horizon
= 2× le plus grand intervalle configuré, borné 7–30 j) ; **auto-calibration** branchée
sur les Paramètres ; légende dynamique ; labs T normalisés en ng/mL
- `DosesScreen` : LazyColumn par jour (desc), **Δ jours depuis la dose précédente du même
traitement** (`intervalsByDoseId`, colonne « Interval (d) » du `.ods`), suppression
avec confirmation, FAB → `DoseDialog` (création), **tap sur la ligne → édition**
- `LabsScreen` : groupée par marqueur, FAB → `LabDialog` (E2/T/PRL, unité suggérée) - `LabsScreen` : groupée par marqueur, FAB → `LabDialog` (E2/T/PRL, unité suggérée)
- `TreatmentsScreen` : cartes (nom, route, dose, chips ester·modèle / Tmax / ×scale / ⏰, - `TreatmentsScreen` : cartes (nom, route, dose, chips ester·modèle / Tmax / ×scale / ⏰,
badge inactif), FAB → éditeur badge inactif), FAB → éditeur
- `TreatmentEditorScreen` : 12 presets (`PKPresets`, cf `nameRes`) pré-remplissent tout ; - `TreatmentEditorScreen` : 12 presets (`PKPresets`, cf `nameRes`) pré-remplissent tout ;
champs conditionnels (ester+modèle si injection, Bateman sinon) ; carte Calibration champs conditionnels (ester+modèle si injection, Bateman sinon) ; carte Calibration
(scaleFactor + « Calibrer avec les analyses ») ; carte Rappel (switch + TimePicker + (scaleFactor + « Calibrer avec les analyses ») ; **section « Fréquence » (v1.2 :
avertissement alarmes exactes) ; switch actif ; save → insert/update + schedule/cancel ; switch « Simuler les doses à venir » + intervalle en jours)** ; carte Rappel (switch +
delete avec confirmation ; `createdAt` préservé à l'édition TimePicker **centré** + avertissement alarmes exactes) ; switch actif ; save →
insert/update + schedule/cancel ; delete avec confirmation ; `createdAt` préservé
- `SettingsScreen` : langue (Système/Français/English, chips reflétant l'état) ; réglages - `SettingsScreen` : langue (Système/Français/English, chips reflétant l'état) ; réglages
T + calibration ; statut alarmes exactes ; Export/Import JSON ; à propos + crédits T + calibration manuelle ; **option Calibration automatique (switch, désactivée par
- Composants : `CurveChart`, `DateTimeField` (DatePicker+TimePicker Material3, LocalDateTime), défaut)** ; statut alarmes exactes + bouton d'octroi ; Export/Import JSON ; à propos
`LogDoseDialog`, `LabDialog`, `formatDose()` (top-level réutilisée) - Composants : `CurveChart` (§11), `DateTimeField` (DatePicker+TimePicker Material3,
LocalDateTime, **horloge centrée**), `DoseDialog` (create/edit + override d'ester),
`LabDialog`, `formatDose()` (top-level, dans `DoseDialog.kt`)
- Thème M3 custom (`ui/theme/Color.kt` : bleu #4F5BD5, rose #D6589E, labs orange, bandeau - Thème M3 custom (`ui/theme/Color.kt` : bleu #4F5BD5, rose #D6589E, labs orange, bandeau
TransSky/TransPink), dynamic color désactivé TransSky/TransPink), dynamic color désactivé ; `MaterialExpressiveTheme` encore
**internal** dans la ligne material3 pinnée (cf §3) → `MaterialTheme` standard
- ⚠️ `Card(onClick=…)` et `ExposedDropdownMenuBox` = **API expérimentales M3** → `@OptIn` - ⚠️ `Card(onClick=…)` et `ExposedDropdownMenuBox` = **API expérimentales M3** → `@OptIn`
requis sur chaque composable qui les utilise requis sur chaque composable qui les utilise
## 11. Graphiques (CurveChart) ## 11. Graphiques (CurveChart)
Canvas pur (aucune lib). Dual axe : E2 gauche (pg/mL), T droite (ng/mL, pointillés rose). Canvas pur (aucune lib), **multi-séries** (v1.2) : `ChartSeries(points, e2Style, tStyle?)`
Échelle « nice » (`niceCeil` : 1/2/2.5/5/10 × 10ⁿ). Grille 4 lignes ; labels Y gauche/droite ; — le ChartScreen superpose les courbes Estrannaise **et** Transfem Science depuis le
X : pas 6 h/24 h/5 j selon plage (`SimpleDateFormat` HH'h' / dd/MM). Labs : cercles (E2) et même traitement (`modelOverride`), styles plein/pointillé par série. Dual axe : E2
carrés (T) orange + valeur. Ligne verticale « maintenant ». gauche (pg/mL), T droite (ng/mL). Échelle « nice » (`niceCeil` : 1/2/2.5/5/10 × 10ⁿ)
partagée entre toutes les séries. Grille 4 lignes ; labels Y gauche/droite ; X : pas
6 h/24 h/5 j selon plage (`SimpleDateFormat` HH'h' / dd/MM). Labs : cercles (E2) et
carrés (T) orange + valeur, **T convertie en ng/mL** (`convertTToNgMl`) au rendu.
Ligne verticale « maintenant ».
Pièges : Pièges :
- `DrawScope` implémente `Density` → `X.dp.toPx()` direct ; ne PAS écrire de helper custom - `DrawScope` implémente `Density` → `X.dp.toPx()` direct ; ne PAS écrire de helper custom
- Tout label passe par `drawContext.canvas.nativeCanvas` + `android.graphics.Paint` - Tout label passe par `drawContext.canvas.nativeCanvas` + `android.graphics.Paint`
- Mélange Double/Float interdit (`1 - i / 4f` et pas `/4.0`) - Mélange Double/Float interdit (`1 - i / 4f` et pas `/4.0`)
- Le panoramique est géré **par le parent** (ChartScreen change `startMs/endMs`), pas par
le Canvas — le chart reste un composant purement déclaratif
## 12. i18n FR/EN ## 12. i18n FR/EN
@ -435,21 +499,27 @@ Le script de la session 1 a été exécuté inline (non archivé) — le refaire
structure ci-dessus. **Toute restructuration du JSON impose de mettre à jour structure ci-dessus. **Toute restructuration du JSON impose de mettre à jour
`PKProfileStore.initWithJson`.** `PKProfileStore.initWithJson`.**
## 16. Workflow build / test / install ## 16. Workflow build / test / git
```bash ```bash
cd ~/projects/HormoneTrack cd ~/projects/HormoneTrack
./gradlew assembleDebug testDebugUnitTest # build + 24 tests ./gradlew assembleDebug testDebugUnitTest # build + 36 tests
./gradlew lint # linters Android (à configurer) ./gradlew lint # linters Android (à configurer)
adb install -r app/build/outputs/apk/debug/app-debug.apk adb install -r app/build/outputs/apk/debug/app-debug.apk
``` ```
**Git (initialisé le 2026-09-05, branche `main`)** :
- Historique = commits logiques par couche (toolchain / moteur / UI / docs) ;
- **Chaque release = tag annoté** (`v1.1.0`, `v1.2.0`, …) :
`git tag -a vX.Y.Z -m "…" && git tag` pour lister ;
- `local.properties`, `build/`, `.gradle/`, `.idea/` sont ignorés (`.gitignore`) ;
- Avant chaque commit de release : `./gradlew testDebugUnitTest` doit être vert ;
- Prochaine étape repo : ajouter un remote et `git push -u origin main --tags`.
- Téléphone : mode développeur + Débogage USB (détails : GUIDE_INSTALLATION.md) - Téléphone : mode développeur + Débogage USB (détails : GUIDE_INSTALLATION.md)
- À ma charge (assistant) : build + tests JVM ✓ ; émulateur possible sur demande - À ma charge (assistant) : build + tests JVM ✓ ; émulateur possible sur demande ;
(~2–3 Go + image système) ; **les tests humains sur vrai téléphone restent la référence** **les tests humains sur vrai téléphone restent la référence**
(notifs → montre, UX de saisie, pickers) (notifs → montre, UX de saisie, pickers, panoramique du chart)
- Recommandé avant chaque commit : `./gradlew testDebugUnitTest` (les tests du moteur
attrapent les régressions mathématiques)
## 17. Montre : Gadgetbridge & options ## 17. Montre : Gadgetbridge & options
@ -467,17 +537,18 @@ Doc dédiée : [MONTRE-GADGETBRIDGE.md](MONTRE-GADGETBRIDGE.md). Synthèse :
## 18. Espace disque & coûts ## 18. Espace disque & coûts
Mesuré le 5 sept. 2026 (Mac, 228 Go, **33 Go libres**) : Mesuré le 5 sept. 2026 (Mac, 228 Go, **33 Go libres** au départ) :
| Élément | Taille | | Élément | Taille |
|---|---| |---|---|
| SDK Android (cmdline-tools + platform 34 + build-tools + platform-tools) | 524 MB | | SDK Android (cmdline-tools + platforms 34/36/**37** + build-tools 34/36/37 + platform-tools) | ≈ 700 MB |
| Cache Gradle (~/.gradle) | 1,5 GB | | Cache Gradle (~/.gradle, plusieurs distributions 8.9→9.7.1 + deps AGP 9/Compose 1.12) | ≈ 3–4 GB |
| Projet (sources + build outputs) | 69 MB | | Projet (sources + build outputs) | ≈ 100 MB |
| **Total outillage actuel** | **≈ 2,1 GB** | | **Total outillage actuel** | **≈ 4–5 GB** |
Marges : émulateur + image système ≈ +2–3 GB ; DevEco Studio (Phase 2) ≈ +10 GB → Marges : émulateur + image système ≈ +2–3 GB ; DevEco Studio (Phase 2) ≈ +10 GB →
tout rentre très largement. Aucune contrainte disque prévue. tout rentre très largement. Note : **AGP télécharge automatiquement les plateformes
manquantes** (licences signées) — c'est comme ça que android-37 est arrivé.
## 19. Limites connues ## 19. Limites connues
@ -526,4 +597,4 @@ Sur le téléphone de test (à compléter par l'utilisatrice) :
- [ ] Tester l'installation d'une watchface `.hwt` via Gadgetbridge (pour la Phase 2) - [ ] Tester l'installation d'une watchface `.hwt` via Gadgetbridge (pour la Phase 2)
--- ---
*Doc mise à jour le 5 sept. 2026 — build OK, 24/24 tests verts, APK debug 18 MB.* *Doc mise à jour le 5 sept. 2026 (v1.2.0) — build OK, 36/36 tests verts, APK debug 23 MB, repo git avec tags.*

View File

@ -91,8 +91,22 @@ Les profils viennent de ta feuille `Estrogen.ods` (table « Models ») :
- **Interpolation** linéaire entre les heures ; au-delà de 8000 h, extrapolation avec la pente terminale - **Interpolation** linéaire entre les heures ; au-delà de 8000 h, extrapolation avec la pente terminale
- **Calibration** : facteur d'échelle par traitement (médiane des ratios lab/prédiction) - **Calibration** : facteur d'échelle par traitement (médiane des ratios lab/prédiction)
- **Courbe T** : dérivée de l'E2 estimé (modèle empirique, calibrable) — indicative seulement - **Courbe T** : dérivée de l'E2 estimé (modèle empirique, calibrable) — indicative seulement
- Unités T acceptées : ng/mL, **ng/dL**, ng/L, nmol/L (conversion automatique)
## 8. Dépannage ## 8. Fonctions du graphique (v1.2)
- **Panoramique** : fais glisser le graphique **vers la droite** pour remonter dans le
passé (toute ta fenêtre d'historique) ; bouton « Revenir à maintenant » pour revenir
- **Toggles Estrannaise / Transfem Science** : les deux courbes peuvent être affichées
simultanément (Estrannaise = bleu, Transfem Science = turquoise) pour comparer
- **Prévision** : configure la **Fréquence d'injection** (jours) dans un traitement
(section « Fréquence ») puis active le chip « Prévision » → les doses à venir sont
simulées et dessinées après la ligne « maintenant » (jamais sauvegardées)
- **Calibration automatique** (Paramètres, désactivée par défaut) : quand activée, les
facteurs d'échelle et le modèle T sont ajustés en continu depuis tes labs — pour
l'affichage seulement, tes réglages stockés ne changent pas
## 9. Dépannage
| Problème | Solution | | Problème | Solution |
|----------|----------| |----------|----------|
@ -102,7 +116,7 @@ Les profils viennent de ta feuille `Estrogen.ods` (table « Models ») :
| Rappels en retard | Paramètres → « Accorder les alarmes exactes » + désactive l'optimisation de batterie pour l'app | | Rappels en retard | Paramètres → « Accorder les alarmes exactes » + désactive l'optimisation de batterie pour l'app |
| Import JSON échoué | Le fichier doit venir d'un export de l'app même version (IDs conservés) | | Import JSON échoué | Le fichier doit venir d'un export de l'app même version (IDs conservés) |
## 9. Données & vie privée ## 10. Données & vie privée
- **Tout est local** : base Room sur le téléphone, aucun serveur, aucun compte - **Tout est local** : base Room sur le téléphone, aucun serveur, aucun compte
- Sauvegarde = fichier JSON que tu choisis où stocker (Owncloud, etc.) - Sauvegarde = fichier JSON que tu choisis où stocker (Owncloud, etc.)