Oura Ring Gen 4 sensor data, not clinical measurementsN=1 case study, not validated for clinical decisionsHEV diagnosed Mar 18; Day 205 post-ruxolitinibMore
Consumer wearable data can support exploratory review only. The HEV diagnosis, temporally confounded with treatment start, remains a material confounder.

How this was built

Measured from the repository and the pipeline's own outputs, generated with the rest of the site
WHO

Who did what

I have no programming background. Claude Code, Anthropic's command-line agent, wrote the code in this repository. I told it what I needed, gave it the data, and checked what came back. The placebo tests, the statistical audit and the link checker exist because outputs were not trusted on sight.

TaskWho
Describing what to build, in Norwegian, one report at a timeMe
Writing the code: every analysis module, the theme, the pipeline, this pageClaude Code
Supplying the data: my Oura account, the clinical dates in config.pyMe
Checking outputs against the source data and the clinical recordMe
Deciding which statistical methods to run and how to report their limitsBoth, in conversation
Every clinical decisionMy clinicians
NUMBERS

By the numbers

Pipeline scripts
Info
36
run in order by run_all.py
Report pages
Info
34
in the navigation registry
Analysis code
Info
57,893lines
46 Python files in analysis/
Nights of data
Info
263
2026-01-08 to 2026-10-06
Commits
Info
686
since 2026-03-23
Test files
Info
6
run by run_tests.py
PIPELINE

How a number gets onto a page

The same five steps run every morning. A systemd user timer fires it at *-*-* 06:15:00. If any step fails, the previous day's site stays up and I get a desktop notification with the failing step.

  1. Import. The Oura API is read for the whole window and written to a SQLite database. Device imports (blood pressure, glucose, ECG) are preserved across rebuilds.
  2. Analyse. run_all.py runs every script in order. Each writes one HTML page and one JSON file with the numbers it printed.
  3. Audit. statcheck_reports.py re-reads every page, extracts every p-value, effect size and test statistic, and matches it against the JSON. A mismatch stops the deploy. The result is published as Every number, checked.
  4. Check links. check-links.py walks every page and fails on any link that does not resolve to a file.
  5. Publish. wrangler uploads the folder to Cloudflare Pages. Nothing is edited by hand between the database and the page.
VERIFICATION

Does it check itself?

The statistical audit that closed the previous pipeline run checked 36 pages, extracted 691 claims and found 0 mismatches; it passes. The audit of this run is published as <a href="claims.html">Every number, checked</a>. The latest pipeline run passed 36 of 36 scripts in 5 minutes.

LIMITS

Limits

One person. A consumer ring, not a clinical monitor. An observational before-and-after with two co-interventions (hepatitis E resolving, a beta-blocker added). The models are exploratory and the site says so on every page. Data run from January 08, 2026 to October 06, 2026; treatment split at March 16, 2026.

SOURCE

Source

The code is public under the MIT licence at github.com/The-Educational-Equality-Institute/oura-hsct-digital-twin. To run it on your own ring: copy config.example.py to config.py, put an Oura token in .env, then pip install -r requirements-full.txt and python run_all.py.