HormoneTrack/README.en.md

298 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.10.0 — Android build ✅, **lint clean** ✅, **259 unit tests** ✅
(225 without the local test data; 6 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**
- **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
- **Inactive treatments**: an archived treatment disappears from input and
reminders, but **its whole history stays simulated and calibrated** — useful
for a valerate → enanthate transition
- **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
- **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 # 259 tests (225 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)).