- predictedE2 : valeur brute au creux × facteur du ester actif — fournie par Home/Labs quand l'auto-calibration est ON (sinon null : la carte n'affiche rien, une valeur brute serait trompeuse). Miroir web. - Régime POOLÉ (pooledRegimeDoses) : les doses de tous les traitements partageant (ester effectif, mg) forment UNE séquence — le re-parenting d'historique (« 6d-old », v1.12.0) ne fait plus redémarrer la stabilisation. Garde-fous : ester ≠ jamais poolé (régression n°3) ; dose ≠ exclue (le 8 mg casse via le trou). Régression n°7 data-driven sur le nouvel export (backup-v1.12.0.json). - Cible de creux E2 (opt-in) : DataStore + carte settings (hint croisé « distinct des seuils d'alerte — jamais de notification ») + coloration carte reco (primaire/hors cible tertiary) + backup rétrocompatible (un backup ancien n'efface pas la cible locale). Système à 3 niveaux documenté §7.11. Miroir web complet. 284 tests JVM (250 sans données locales) + 14 UI + lint verts ; web 199 tests + E2E verts (check.sh). Validé émulateur sur données réelles : « Expected at this trough: ≈ 208 pg/mL — outside your target », régime poolé = stable depuis le 12 août, 0 crash.
306 lines
18 KiB
Markdown
306 lines
18 KiB
Markdown
# HormoneTrack
|
||
|
||
> **Language**: [Français](README.md) · **English** (this file)
|
||
|
||
> **🤖 AI-assisted development**: this project was designed and coded with an
|
||
> AI assistant. The human contribution was **essential**: continuous feedback,
|
||
> user reports (testing on a real phone, bug reports with real data exports),
|
||
> improvement suggestions and validation of every release. The session-by-session
|
||
> detail is documented in
|
||
> [docs/DEVELOPPEMENT.md §2](docs/DEVELOPPEMENT.md) (French).
|
||
|
||
Hormone therapy (HRT) tracker on Android, with **hour-by-hour** estimated
|
||
curves of estradiol (E2) and testosterone (T), calibration against blood
|
||
tests, reminders mirrored on a smartwatch (Huawei Watch GT 3 via Gadgetbridge
|
||
or Huawei Health) and JSON backup. **100% local, no account, no server.**
|
||
|
||
> **⚠️ Medical disclaimer**: the curves are **pharmacokinetic estimates**
|
||
> for informational purposes — they are not measurements. Always rely on your
|
||
> blood tests and on your endocrinologist's guidance.
|
||
|
||
- **Status**: v1.13.0 — Android build ✅, **lint clean** ✅, **284 unit tests** ✅
|
||
(250 without the local test data; 7 regressions pinned on real data
|
||
**not committed to the repo**), **14 Compose UI tests** ✅ (emulator),
|
||
smartwatch integration = notifications ✅, **private Gitea repo + releases
|
||
with APK** ✅
|
||
- **Changelog**: [docs/CHANGELOG.md](docs/CHANGELOG.md) (French)
|
||
- **User guide**: [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md) (French)
|
||
- **Development docs** (architecture, math, decisions, bugs): [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md) (French)
|
||
- **Watch / Gadgetbridge**: [docs/MONTRE-GADGETBRIDGE.md](docs/MONTRE-GADGETBRIDGE.md) (French)
|
||
|
||
## Features
|
||
|
||
- **Hour-by-hour estimated curves**: E2 (pg/mL) and T (ng/mL), 24 h / 7 d / 30 d views,
|
||
**dose markers** (forecast and actual), **configurable chart timezone** (v1.4.5),
|
||
**pan** (drag to scroll back in time), **zoom** (pinch or − / + buttons,
|
||
6 h → 300 d, adaptive sampling), displayable **peaks & troughs** with their
|
||
**estimated values** (▲▼ triangles at local extrema, toggleable)
|
||
- **Two PK models, superimposable**: **Estrannaise (EstraNase)** (hourly tables
|
||
from the `.ods` spreadsheet) and **Transfem Science** — since v1.4.0 the TFS
|
||
model is the **official 3-compartment meta-analysis** (exact closed form,
|
||
parameters from the [TFS simulator](https://transfemscience.org/misc/injectable-e2-simulator/),
|
||
[article](https://transfemscience.org/articles/injectable-e2-meta-analysis/)),
|
||
covering **7 esters** (EV, EU, EEn, EB, EC oil, EC suspension, PEP) — WHSAH
|
||
covers 6 (no PEP). Displayed side by side with independent toggles (3 colors) —
|
||
**pre-checked according to your treatments' models** (v1.4.7) and **each model
|
||
calibrated separately** by your labs (v1.4.8 — scale factors adapt to each profile)
|
||
- **"Lab track" curve (v1.5.0, extended in v1.6.0)**: the curve anchored on your
|
||
blood tests (`Lab track` chip, off by default) — the PK model's shape between
|
||
labs, with the amplitude rescaled to pass EXACTLY through every E2 lab value;
|
||
log-linear transition from one lab to the next, keeps out-of-action-window labs
|
||
(#61); **`Extend` chip (v1.6.0)**: the curve extends beyond the last lab
|
||
(model × last lab ratio, horizon = model extinction, estimated part drawn
|
||
faded + dedicated legend) — the curve is a COMPARISON reference, never an
|
||
estimate of action (it never feeds the home screen or the alerts)
|
||
- **Analytical Estrannaise model (v1.9.0)**: the ESE model now uses the 3C
|
||
closed form published by estrannaise.js (fidelity to the old tables pinned at
|
||
RMS 0) — **MCMC uncertainty cloud** (`Cloud` chip, ESE-exclusive: 32 posterior
|
||
curves showing the imprecision range, like the estrannaise website); **6
|
||
injectable esters** (incl. EUCS, exclusive); the historical ODS is no longer
|
||
used at runtime (fidelity = tests only); analytical terminal t½
|
||
(blood draw recommendation)
|
||
- **Configurable Bateman model** (time to peak, half-life, bioavailability) for
|
||
gel, patch and oral routes
|
||
- **Forecast simulation**: set a **Dosage interval** (days) on a treatment →
|
||
upcoming doses are projected on the chart (never saved); **enabling forecast
|
||
does not move the chart start** but EXTENDS the window to the next dose
|
||
(v1.4.4), and the projection is browsed by dragging left, up to **1 year**
|
||
- **Inactive treatments**: an archived treatment disappears from input and
|
||
reminders, but **its whole history stays simulated and calibrated** — useful
|
||
for a valerate → enanthate transition; **grouped at the very bottom** of the
|
||
Treatments page under a dedicated header, dimmed cards (v1.11.0);
|
||
**no future projection anymore** (v1.12.0 — its slots disappear from the
|
||
Forecast chart)
|
||
- **Dose log** with exact date/time, dose in mg, **per-injection ester**
|
||
(EV↔EU↔EEn switch like in the spreadsheet), **editable** (tap a row in Doses),
|
||
**interval in days between doses** displayed; **route icon per dose row**
|
||
(oral / IM-SC injection / gel / patch, v1.12.0) and **same-day blood test
|
||
markers** (🧪↑ before the dose, 🧪↓ after)
|
||
- **Blood tests**: **E2 + T in a single entry** (each optional), displayed
|
||
**side by side** when they share the same date/time, editable (tap → pick the
|
||
entry); T units: ng/mL, ng/dL, ng/L, nmol/L
|
||
- **Suggested next blood draw (v1.8.0, extended v1.8.1 to the Labs and Home pages)**:
|
||
the Labs page recommends the **estimated trough just before the next injection**
|
||
(the most comparable moment), **at the first trough where your regimen is
|
||
stabilized** (~5 half-lives after your last change — dose, ester or interval) —
|
||
derived from the curves and the Dosage interval; requires an active injectable
|
||
E2 with a Dosage (a prompt suggests setting one otherwise); estimates, never
|
||
medical advice;
|
||
**v1.13.0**: the card shows the **expected E2 at that trough** (when
|
||
auto-calibration is ON) and compares it to your personal **trough target**
|
||
(opt-in — distinct from the alert thresholds, never notifies)
|
||
- **Calibration**: per-treatment scale factor = median(lab ÷ model prediction),
|
||
computed automatically (the `.ods` "Scale factor", automated) — or **permanent
|
||
automatic calibration** (option, off by default) which calibrates **each ester
|
||
with the labs of its own period** (valerate labs → valerate doses, enanthate
|
||
labs → enanthate doses) and recalibrates the T model
|
||
- **Empirical T estimation** `T = floor + (base − floor) ÷ (1 + k·E2)`, with
|
||
**k calibrated per ester period** (T suppression differs valerate vs enanthate),
|
||
against the already-calibrated E2 — accepted T units: ng/mL, ng/dL, ng/L, nmol/L
|
||
- **Daily reminders** with **"Taken" / "Snooze 1 h"** actions right in the
|
||
notification; notifications are mirrored to the GT 3 watch (Gadgetbridge or
|
||
Huawei Health); **reminders follow the Dosage interval** (v1.4.0): a treatment
|
||
injected every 7 days only rings on injection day, not every day; **a dose
|
||
already logged on the day skips the notification** (v1.10.0 — the next
|
||
reminder resumes at the following slot)
|
||
- **Configurable alert thresholds** (v1.4.2): E2 (pg/mL) and T (ng/mL) high/low
|
||
limits in Settings → warning card on the home screen + **notification every
|
||
15 min even with the app closed** (WorkManager, anti-spam, dedicated channel) —
|
||
evaluated on the **estimated** level, opt-in
|
||
- **Full JSON backup**: treatments + doses + labs + T settings
|
||
**+ parameters (language, auto-calibration, alert thresholds)** (v1.4.2)
|
||
- **Daily automatic backup (v1.7.0, opt-in)**: IN ADDITION to the manual export —
|
||
every day, a full JSON backup (same format, directly importable) is written to
|
||
**the folder you choose** (e.g. a synchronized Owncloud folder). No storage
|
||
permission (SAF, folder picked once, persistent revocable permission),
|
||
configurable retention (1–30 copies, default 7 — timestamped file per run,
|
||
never overwritten; manual exports and foreign files **never touched**), last
|
||
run status in Settings, first backup immediately on activation
|
||
- **What's new on every update**: automatic changelog dialog (dismissed =
|
||
won't show again before the next version)
|
||
- **Time on HRT** displayed at the top of the Doses page (since the first dose);
|
||
**next dose in days** on the home screen when it is more than 24 h away (v1.4.1)
|
||
- **Exportable diagnostic logs** (Settings) — useful for support
|
||
- **Calendar events**: dose reminders can create a recurring (posology) event in
|
||
a "HormoneTrack" calendar on your phone
|
||
- **Full JSON backup/restore** (treatments + doses + labs + T settings + parameters, v1.4.2)
|
||
- **FR + EN** (per-app language, independent of the system)
|
||
- Jetpack Compose UI (BOM 2026.08, Material You); 100% local, no account
|
||
|
||
## Quick start (building from source)
|
||
|
||
Prerequisites: JDK 17+ (Java 21 OK), Android SDK (platform 37 will be
|
||
auto-downloaded by AGP if the licenses are signed). The wrapper downloads
|
||
Gradle 9.7.1.
|
||
|
||
```bash
|
||
git clone <repo> && cd HormoneTrack
|
||
echo "sdk.dir=/path/to/android-sdk" > local.properties # or ANDROID_HOME
|
||
./gradlew assembleDebug # APK: app/build/outputs/apk/debug/app-debug.apk
|
||
./gradlew testDebugUnitTest # 284 tests (250 without local test data)
|
||
./gradlew connectedDebugAndroidTest # 14 UI tests (emulator/device required)
|
||
./gradlew lint # clean lint required before a release
|
||
```
|
||
|
||
Installing on a phone: developer mode + USB debugging, then Android Studio
|
||
(**Run ▶️**) or `adb install -r app/build/outputs/apk/debug/app-debug.apk`.
|
||
No Play Store: the app is sideloaded. Step-by-step details (French):
|
||
[docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md).
|
||
|
||
## Git
|
||
|
||
Repositories: **gitea.cloudyfy.fr** and **gitea.farewell.dev** (mirror) —
|
||
`Siphonight/HormoneTrack` on both (private), with **tagged releases**
|
||
(`v1.1.0` → `v1.10.0`) and **two APKs per release** (since v1.2.5)
|
||
(downloadable without building, cf
|
||
[docs/DEVELOPPEMENT.md §16.bis](docs/DEVELOPPEMENT.md)):
|
||
`-release.apk` (**recommended**, R8-optimized, 2.7 MB) and `-debug.apk` (20 MB):
|
||
|
||
```bash
|
||
git tag # list releases
|
||
git log --oneline # layer-by-layer history (toolchain / engine / UI / docs)
|
||
git push -u origin main --tags
|
||
```
|
||
|
||
**Web port**: since v1.4.10, the browser version lives in its own repository
|
||
`Siphonight/HormoneTrack-web` (same instances) — **aligned versions**
|
||
(web v1.4.10 = port of Android v1.4.10), synced releases (checklist §16 step
|
||
4.bis), separate changelogs.
|
||
|
||
Every release commit passes `./gradlew testDebugUnitTest` (green required) and
|
||
is an annotated tag. See [docs/DEVELOPPEMENT.md §16](docs/DEVELOPPEMENT.md).
|
||
|
||
**Release signing**: the release APK is signed with the debug key as long as
|
||
no `keystore.properties` exists at the repo root; once created via
|
||
`scripts/make-release-keystore.sh` (both files gitignored), `assembleRelease`
|
||
signs with that keystore — phone migration documented in
|
||
[docs/DEVELOPPEMENT.md §16.quater](docs/DEVELOPPEMENT.md).
|
||
|
||
## The models in short
|
||
|
||
Each injection contributes `dose_mg × profile(dt)` where `profile` is the
|
||
normalized response (pg/mL per mg); contributions superimpose. **Three
|
||
superimposable models**:
|
||
|
||
- **Estrannaise** = hourly tables from the `.ods` (8001 h), EV/EU/EEn
|
||
- **Transfem Science** = 3-compartment meta-analysis (V3C, closed form,
|
||
[article](https://transfemscience.org/articles/injectable-e2-meta-analysis/)),
|
||
7 esters (EV, EU, EEn, EB, EC, EC suspension, PEP)
|
||
- **WHSAH** (v1.4.6) = "license-free" fit of the [WHSAH Collective via
|
||
Mona](https://github.com/mona-hrt/mona) — same mathematical family but
|
||
independent parameters with explicit bioavailability F < 1: faster rise
|
||
at D+1 (EEn ~70 pg/mL at D+1 for 5 mg vs ~22 for TFS) and longer decay
|
||
(EEn t½ 7.3 d vs 4.5 d). 6 esters (no PEP)
|
||
|
||
Reference peaks (pg/mL per mg):
|
||
|
||
| Profile | Model | Peak (pg/mL/mg) | Tmax | Terminal t½ |
|
||
|----------|------------------|-----------------|--------|-------------|
|
||
| EV | Estrannaise | 61.1 | ~45 h | — |
|
||
| EU | Estrannaise | 3.4 | ~55 h (long plateau) | — |
|
||
| EEn | Estrannaise | 31.4 | ~152 h | — |
|
||
| EV | Transfem Science | 59.0 | ~51 h | 3.0 d |
|
||
| EU | Transfem Science | 10.1 | ~198 h | — |
|
||
| EEn | Transfem Science | 32.0 | ~156 h | 4.5 d |
|
||
| EB | Transfem Science | 194.2 | ~16 h | 1.2 d |
|
||
| EC (oil) | Transfem Science | 31.1 | ~103 h | 6.7 d |
|
||
| EC (susp.) | Transfem Science | 48.2 | ~29 h | 5.1 d |
|
||
| PEP | Transfem Science | 1.03 (~6.5× dose) | ~18 d | 28.4 d |
|
||
| EV | WHSAH | 73.5 | ~41 h | 3.1 d |
|
||
| EEn | WHSAH | 37.6 | ~120 h | 7.3 d |
|
||
| EB | WHSAH | 260.1 | ~12 h | 1.3 d |
|
||
| EC (oil) | WHSAH | 25.0 | ~81 h | 7.9 d |
|
||
| EC (susp.) | WHSAH | 53.5 | ~16 h | 7.1 d |
|
||
| EU | WHSAH | 4.9 | ~67 h | 31.7 d |
|
||
|
||
**Calibration (v1.4.8)**: the scale factor applies per ESTER and per
|
||
injection PERIOD (median of lab ÷ prediction ratios, like the original
|
||
spreadsheet's "Scale factor" column) — and per MODEL: every displayed curve
|
||
(Estrannaise / Transfem Science / WHSAH) is calibrated with ITS OWN model's
|
||
prediction → they all stick to your labs, whatever their shape.
|
||
Auto-calibration is optional (off by default).
|
||
|
||
## Privacy
|
||
|
||
- **The app is 100% local**: Room database on the phone, no server, no
|
||
telemetry; Gadgetbridge-compatible (FOSS) with no Huawei Health dependency
|
||
- **The repository contains no health data**: code and docs are generic; the
|
||
regression tests that use real exports load their data from
|
||
`local-test-data/` (**gitignored**, outside the repo — and the history was
|
||
cleaned before the first push, cf
|
||
[docs/DEVELOPPEMENT.md §8.bis](docs/DEVELOPPEMENT.md))
|
||
- Backups = JSON files you store wherever you want (Owncloud, etc.)
|
||
- **The daily auto-backup (v1.7.0) only writes to the folder YOU picked
|
||
yourself** (system SAF picker, revocable permission) — nothing ever leaves
|
||
the phone, no server
|
||
- `allowBackup=false` (sensitive data); biometric lock planned for Phase 2
|
||
- The repository is **private**: release APKs download while logged in;
|
||
making the repo public would make the APKs downloadable without an account
|
||
(no data risk, see above)
|
||
|
||
## Repository structure
|
||
|
||
```
|
||
HormoneTrack/
|
||
├── README.md ← this file's French twin
|
||
├── docs/
|
||
│ ├── GUIDE_INSTALLATION.md user guide (phone + watch, French)
|
||
│ ├── DEVELOPPEMENT.md full dev docs (architecture, math, bugs, tests, French)
|
||
│ ├── CHANGELOG.md detailed version history (French)
|
||
│ └── MONTRE-GADGETBRIDGE.md Huawei GT 3 watch: options + limits (French)
|
||
├── scripts/
|
||
│ ├── gitea-release.py publishes a release body (CHANGELOG + APK)
|
||
│ ├── publish-release.py publishes a tag's 2 APKs (verified by download)
|
||
│ ├── seed-emulator.py injects a JSON backup into an emulator DB
|
||
│ └── make-release-keystore.sh generates the release keystore + properties (v1.9.8)
|
||
├── local-test-data/ ← gitignored: real backups for regression
|
||
│ tests (NEVER in the repo, cf §8.bis)
|
||
├── keystore.properties ← gitignored: release signing (optional, §16.quater)
|
||
├── build.gradle.kts root Gradle config (AGP/Kotlin/KSP pinned)
|
||
├── settings.gradle.kts
|
||
├── gradle.properties
|
||
├── gradle/wrapper/ Gradle 9.7.1 wrapper (jar + properties)
|
||
├── gradlew / gradlew.bat
|
||
└── app/
|
||
├── build.gradle.kts dependencies (Compose, Room, DataStore, Gson…)
|
||
├── proguard-rules.pro
|
||
└── src/
|
||
├── main/
|
||
│ ├── AndroidManifest.xml
|
||
│ ├── assets/pk_profiles.json ← hourly tables (Estrannaise/TFS)
|
||
│ ├── java/com/hormonetrack/
|
||
│ │ ├── data/ (Room: models, DAOs, repository, backup)
|
||
│ │ ├── pk/ (PK engine + profiles: Estrannaise/TFS/WHSAH, alerts, export)
|
||
│ │ ├── reminder/ (exact alarms, notifs + actions, boot, alert worker)
|
||
│ │ ├── settings/ (DataStore: TConfig, language)
|
||
│ │ ├── ui/ (Compose: screens, components, theme)
|
||
│ │ │ └── screens/settings/ ← Settings cards (v1.9.8 split)
|
||
│ │ ├── HormoneTrackApp.kt
|
||
│ │ └── MainActivity.kt
|
||
│ └── res/ (FR/EN strings, theme, icons)
|
||
├── test/java/com/hormonetrack/ ← JVM unit tests
|
||
│ ├── pk/ (engine, ESE/TFS/WHSAH profiles, per-model
|
||
│ │ calibration, reminders, data-driven regressions)
|
||
│ ├── data/backup/ (Gson round-trip + parameters)
|
||
│ ├── ui/ (chart window, source guard)
|
||
│ ├── util/ (export file names, durations)
|
||
│ └── reminder/ + settings/ (calendar RRULE, changelog)
|
||
└── androidTest/java/com/hormonetrack/ ← Compose UI tests (v1.9.8)
|
||
└── uitest/ (navigation, changelog dialog, settings)
|
||
```
|
||
|
||
## Roadmap
|
||
|
||
- [x] v1: E2/T curves, dose log, labs + calibration, reminders, JSON backup, FR/EN
|
||
- [x] Compose UI tests (13) + dedicated release signing infrastructure (v1.9.8)
|
||
- [ ] Biometric lock, widget, CSV export
|
||
- [ ] Watch Phase 2: custom watchface and/or Lite Wearable mini-app (see
|
||
[docs/MONTRE-GADGETBRIDGE.md](docs/MONTRE-GADGETBRIDGE.md))
|
||
|
||
## License
|
||
|
||
**GPL-3.0** — see [LICENSE](LICENSE). Consistent with the Gadgetbridge
|
||
ecosystem. The PK models belong to their respective authors
|
||
([Estrannaise](https://estrannaise.github.io/),
|
||
[Transfem Science](https://transfemscience.org)).
|