First increment of the clinical reporting feature (see docs/requirements-reporting.md and docs/design-reporting.md): moves the existing live view to pages/1_Live.py, adds a password gate since the dashboard URL will be shared with the diabetes team, adds fetch_entries_range/fetch_treatments_range for multi-day report queries, and adds reporting.py with the core clinical calculations (GMI, %CV, 5-band time-in-range, hypo/hyper event detection per the Battelino et al. 2019 persistence rule). No report pages wired up yet — that's next. Co-Authored-By: Claude Sonnet 5 <[email protected]>
11 KiB
Requirements: oref0-autotune profile tuning
Status: draft — for review and editing Owner: willemjan Related: requirements-reporting.md (separate feature, same Nightscout data source)
1. Purpose & background
AndroidAPS (AAPS) only gained built-in Autotune in version 3.4, and even that
version tunes only a single ISF/carb-ratio value plus the basal schedule from
on-device history — it doesn't use Nightscout at all, and the user is still on
AAPS 3.3 regardless. Previously, the user ran a hosted tool (inspired by
MarkMpn/AutotuneWeb, now archived/discontinued) that analyzed Nightscout data
with oref0-autotune and proposed a new pump profile, which was then manually
entered into AAPS — a process the user describes as "a lot of trial and error."
This feature builds a guided, human-in-the-loop workflow: trigger an oref0-autotune analysis from a dashboard, review the proposed profile against the current one with the underlying estimations visible, selectively accept/adjust values, and stage the result in Nightscout for manual activation.
The user has two LXC containers on Proxmox from earlier experimentation —
autotune (VMID 102, 192.168.2.13) and oref0 (VMID 107, 192.168.2.14) —
currently stopped, of unknown/half-remembered working state.
2. Scope
In scope:
- Booting and inspecting the two existing LXCs to determine (or establish) a working oref0-autotune setup.
- A small HTTP microservice on that LXC that runs oref0-autotune against the user's Nightscout data on demand.
- A separate Streamlit app/service (own deployment, own CI/CD pipeline) that triggers the analysis, displays old-vs-new profile comparisons with estimation detail, lets the user selectively adjust proposed values, and stages the result in Nightscout.
Out of scope (this iteration):
- Automatically activating the new profile in AndroidAPS (creating a
"Profile Switch"treatment) — the dashboard stages the profile only; the user activates it manually. This may become an opt-in "phase 2" once trust is established (see §8). - Any change to AAPS settings or on-device configuration.
- Continuous/scheduled autotune runs — this is a manually triggered, on-demand analysis, not a nightly automated loop.
3. Architecture decisions (made during spec review)
- Execution: an always-on HTTP microservice on whichever LXC has (or gets) a working oref0-autotune setup, rather than SSH-triggering a one-off Docker run or bundling autotune into the dashboard's own container. The dashboard calls it over HTTP and polls for job completion.
- Profile push safety: stage-only. The service writes a new named
profile into the Nightscout profile store but does not create a
"Profile Switch"treatment — final activation stays a deliberate manual step in AAPS or the Nightscout UI. This matters because AndroidAPS applies profiles via Profile Switch treatments (gated behind an explicit "accept profile switch from Nightscout" AAPS setting), not by simply readingprofile.json— so "stage-only" is a real, meaningful safety boundary, not just a formality. - Deployment: a separate app/service from
glucose-dashboard, with its own repo-or-directory, Dockerfile, and Gitea CI/CD pipeline — isolating write-capable, dosing-adjacent code from the read-only dashboard.
4. Functional requirements
FR1 — Infra investigation (first task, needs explicit go-ahead)
Boot LXC 102 (autotune) and LXC 107 (oref0) and inspect what's installed
(Docker containers, cron jobs, scripts, config, any README/notes left behind)
to determine which one — if either — has a working oref0-autotune setup.
Document findings (what's there, what's missing, what needs rebuilding).
Acceptance: a clear decision on which LXC hosts the microservice, and what (if anything) needs to be installed/fixed there before FR2 can start.
FR2 — Autotune HTTP microservice
A small HTTP API (exact framework TBD during design — e.g. Flask/FastAPI wrapping a Docker-based oref0-autotune image) exposing at minimum:
POST /analyze— body: Nightscout URL/token, date range, (optionally) the starting profile to tune from. Returns a job ID.GET /analyze/{job_id}— status (pending/running/done/failed) and, once done, the recommended profile JSON plus the estimation/recommendation detail (per-hour deviations, the autotune log).
Acceptance: a manual curl against the running service, pointed at the
user's real Nightscout instance, returns a plausible recommended profile within
a reasonable time (minutes, not seconds — oref0-autotune is not instant).
FR3 — Trigger analysis from the dashboard
A new Streamlit app page where the user picks a date range (and confirms which current profile to tune from, fetched live from Nightscout), starts the analysis, and sees progress while the microservice job runs (via polling).
Acceptance: starting an analysis with bad/unreachable Nightscout credentials fails with a clear error rather than hanging silently.
FR4 — Old-vs-new profile comparison view
Side-by-side display of the current and proposed profile: hourly basal rates, ISF schedule, carb ratio schedule, DIA — plus the underlying estimation detail (per-hour deviations, confidence notes) surfaced from the autotune log, so the user can judge why a change is being proposed, not just see the new number.
Acceptance: every changed value is visually distinguishable from unchanged values; the estimation detail for a given time slot is viewable alongside that slot's proposed change.
FR5 — Selective adjustment
Per time-slot controls (accept/reject each proposed basal/ISF/CR change individually) plus editable numeric fields so the user can fine-tune any accepted value before staging — matching the user's explicit request to "select parts of the proposed profile and still adjust the values a bit."
Acceptance: rejecting a slot keeps the current value unchanged in the staged profile; editing an accepted value's number is reflected in what actually gets sent to Nightscout.
FR6 — Stage to Nightscout
Write the resulting merged profile (accepted + manually adjusted values) as a
new named profile in the Nightscout profile store (e.g. "Autotune YYYY-MM-DD") via a new POST to /api/v1/profile, without creating a Profile
Switch treatment. After staging, show clear instructions for manual activation
in AAPS (or the Nightscout UI).
Acceptance: the new profile appears in Nightscout's profile list/editor with exactly the merged values; the previously active profile is untouched and still active until the user manually switches.
FR7 — Audit / history
Keep a record of past analysis runs and what was accepted vs. rejected vs. manually adjusted, for traceability (useful both for the user's own reference and potentially for the hospital team). Could reuse Nightscout's own profile history (each staged profile is already a new dated document) rather than a separate store — decide during design.
FR8 — Safety guardrails
- Surface oref0-autotune's own
autosens_min/autosens_maxbounds so the user can see the algorithm's own safety rails, not just the raw numbers. - Flag any accepted value that represents an unusually large jump from the current profile.
- Require an explicit confirmation step (e.g. a review summary + confirmation checkbox) before the FR6 write happens — no silent/implicit staging.
5. Non-functional requirements
- NFR1 — Isolation: separate repository-or-directory, Dockerfile, and Gitea
CI/CD pipeline from
glucose-dashboard, modeled on the existing pipeline (test/lint → build+Trivy+push → deploy) but with its own secrets. - NFR2 — Nightscout token scope: the existing dashboard token is documented
read-only by convention (see
glucose-dashboard/README.md). This feature needs a separate, write-scoped Nightscout token (Nightscout supports role-based tokens — e.g. acareportal-level role) rather than upgrading the shared read-only token, so the read-only reporting dashboard's blast radius doesn't change. - NFR3 — Test safety: automated tests must never call a real Nightscout
instance or the real autotune microservice — mock both, following the
existing
monkeypatch-on-requestspattern fromglucose-dashboard's test suite. - NFR4 — CI/CD gating: given this pipeline can end in a write to a real
medical data store, consider (during design) adding a
pull_requesttrigger for the test/lint job so changes are validated before merge, not only after — the currentglucose-dashboardpipeline only triggers onpushtomain.
6. Data requirements
GET /api/v1/profile.json— read the current active profile store (net-new capability; nothing innightscout_client.pyhandles profiles today).POST /api/v1/profile.json— write the new named profile (net-new).- The autotune microservice's own job API (FR2).
- Read access to entries/treatments over the analysis date range (can reuse the reporting feature's date-range fetch capability once built, or the existing rolling-window fetch for shorter tuning windows).
7. Testing & acceptance criteria
- Unit tests for the profile merge/validation logic (accept/reject/adjust producing the correct final profile document) — pure functions, no I/O, fully testable without any live service.
- Unit tests for the new Nightscout profile read/write client functions, using
the existing
FakeResponse/monkeypatch.setattr(requests, ...)pattern. - Integration-style tests against the autotune microservice using a mocked HTTP layer (never the real LXC service in CI).
- A UAT checklist (produced during the design phase) exercising the full flow end-to-end against the user's real Nightscout instance and the real microservice: trigger → review → adjust → stage → manually verify the new profile appears correctly in Nightscout, with no unintended Profile Switch.
8. Open questions for the user's edit pass
- Which LXC (102 or 107) ends up hosting the service, once FR1 is done?
- Which autotune Docker image to standardize on —
p5nbTgip0r/autotune-docker,houthacker42/nightscout-autotune, or building directly from theoref0npm package? (Design-phase decision, informed by FR1's findings.) - Should automatic Profile Switch activation become an opt-in "phase 2" later, once you've built trust in the recommendations — or should manual activation remain permanent policy regardless of trust?
- FR7 (audit/history) — is reusing Nightscout's own profile-document history sufficient, or do you want a dedicated log outside Nightscout?
- Confirm the separate write-scoped Nightscout token is something you're comfortable creating/managing (NFR2) — this is a manual one-time setup step in the Nightscout admin UI, not something the app can do for you.