This commit is contained in:
2026-06-12 05:48:30 -04:00
parent 9d91ac8ebc
commit 64a5ab4b7f
37 changed files with 4530 additions and 407 deletions

204
QUICKSTART.md Normal file
View File

@@ -0,0 +1,204 @@
# QUICKSTART
You're ~5 minutes from a running dashboard. Two ways in: **web** (recommended, almost zero terminal) or **CLI** (for scripting).
---
## What you'll need
- **A Mac or Linux machine** with a terminal. Windows works under WSL but isn't tested.
- **[uv](https://github.com/astral-sh/uv)** — Python's package manager. Install:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
- **A Garmin export** — see *Getting your Garmin data* below. You can skip this for now and use the manual logger.
That's it. No accounts, no cloud, no Python knowledge required.
---
## Web app (recommended)
### 1. Get the code & install deps
```bash
git clone <repo-url> openrun
cd openrun
uv sync
```
`uv sync` creates a `.venv/` and installs everything. ~30 seconds on first run.
### 2. Launch the app
```bash
uv run openrun-web
```
Your browser opens at `http://localhost:8501`. (If it doesn't, copy that URL manually.)
### 3. Run the setup wizard
The first time you load the app, a yellow banner says **"Looks like a fresh install"** with a **Start setup →** button. Click it.
The wizard has five steps, each ~15 seconds:
1. **Intro** — read it, click *Start*.
2. **Your profile** — name, max HR, lactate-threshold HR, resting HR. If you don't know your max HR, leave the default at 200 and we'll auto-derive zones. You can always come back.
3. **HR zones** — auto-filled from your max HR. If you've already configured custom zones in Garmin Connect, paste those bpm boundaries here instead.
4. **Race calendar** — optional. Add one row per upcoming race (label + ISO date). Skip-able.
5. **Get your data** — upload a Garmin export zip, or skip and start logging by hand.
When the wizard finishes, you land on the **Dashboard**.
### 4. Look around
- **📊 Dashboard** — CTL/ATL/TSB tiles, PMC chart, weekly volume, polarized split, recent activities.
- **📋 Activities** — filter by type/date/distance/name. Click any row to drill into splits, time-in-zone, decoupling.
- **📈 Race plan** — edit your weekly plan in the table; the projected PMC redraws automatically. Race-day TSB is shown for each race in your calendar.
- **📝 Manual log** — quick form to log strength/hike/unrecorded-run sessions. These feed the PMC.
- **🛌 Recovery** — sleep stages, HRV + RHR trends, training→next-morning HRV correlation.
- **🫀 Efficiency** — m/beat with rolling median, distance-bucket × year heatmap.
- **🔄 Sync** — DB status + a place to upload more exports later.
That's the whole app.
### 5. Stop / restart
```
Ctrl-C in the terminal that's running it.
uv run openrun-web # restart any time
```
Your data is in `data/garmin.db`. Back that file up periodically.
---
## Getting your Garmin data
The web app's Sync page accepts a Garmin Connect **data export zip**. You request one from Garmin:
1. Sign in at <https://www.garmin.com/account/datamanagement/exportdata>.
2. Click **Request Data Export**. Pick the categories you want — `Activities`, `Wellness`, and `Connect Settings` are the useful ones.
3. Garmin emails a download link within 2472 hours.
4. Download the zip (several hundred MB to a few GB depending on your history).
5. Drag-and-drop it into the **Sync** page, click **Ingest**.
You'll see a progress log; takes 15 minutes depending on how many years of data.
### Two zip flavours
Garmin confusingly has two export formats:
- **Garmin Connect data export** (`connect.zip`) — what you get from the link above. Filenames are clean `<activity_id>_<name>.fit`. The wizard / Sync page handles this directly.
- **Garmin Takeout dump** — a separate, UUID-named folder you can request via Google's data tools (less common). FITs use upload-IDs in filenames so they need an extra by-content linking step. If you have one of these, see the **CLI** section below for the 4-step sequence.
### Live sync (skip on first install)
You can also pull incrementally from Garmin Connect's live API instead of waiting on the email export. Currently this is **CLI-only** because Garmin's MFA flow needs interactive prompts:
```bash
uv run openrun-auth # email + password + MFA prompt; saves OAuth to .secrets/
uv run openrun-sync # incremental top-up (last 14 days by default)
uv run openrun-sync --full # 365-day backfill
```
Browser-driven live sync is on the roadmap.
---
## CLI quickstart (power-user path)
If you'd rather skip the web app entirely:
```bash
uv sync
uv run openrun-init # creates data/garmin.db, prints status
uv run openrun-ingest path/to/export/zip
uv run openrun-link-fit path/to/export/ # only if it's a Takeout dump
uv run openrun-time-in-zone # precompute the TIZ cache
```
Then open one of the notebooks under `examples/notebooks/`, pick the project's `.venv` as the kernel, and run-all.
### Manual activity import via CSV
```bash
echo 'activity_date,activity_type,distance_km,duration_min,training_load,notes,external_id
2026-05-19,strength,,45,30,upper body,
2026-05-18,hike,8.0,120,40,morning hike,' > workouts.csv
uv run openrun-import-manual workouts.csv
```
`external_id` makes re-imports idempotent (upsert on conflict). Leave blank if you don't care about dedupe.
---
## Troubleshooting
### "Port 8501 already in use"
Another Streamlit is running. Stop it (`Ctrl-C` in the terminal that started it) or pick another port:
```bash
uv run openrun-web -- --server.port 8800
```
### "FIT file not at recorded path"
You moved your export folder after linking. Run:
```bash
uv run openrun-link-fit /new/path/to/export --relink
```
This rewrites every stored FIT path to the new location by basename match.
### Dashboard is empty
Either you haven't ingested any data yet (use the Sync page or `openrun-ingest`), or the activities are filtered out by the default `running` / `trail_running` types — check the **Activities** page with the type filter cleared.
### Garmin live sync fails with 429
The endpoint rate-limits aggressively. Wait 3060 minutes, switch networks, or use the Path A export instead.
### TSB looks way off
Most likely your training_load column is missing on a chunk of activities (Garmin only reports it for activities recorded by a compatible device). Check on the **Activities** page; the dashboard's PMC only counts rows where `training_load IS NOT NULL`.
---
## What next?
Once you have data in:
- **Race-plan page** — set up your goal race in `openrun.toml` (or via the wizard), then iterate on the weekly km / long-run km until projected race-day TSB lands in **+10 to +25**.
- **Activity-detail page** — pick a recent race or hard workout, scroll to the **Pa:Hr decoupling** section, and look for where the bar chart crosses Friel's 10 % "unsustainable" line. That's where the wheels came off.
- **Recovery page** — after a few weeks of data, check whether the Pearson r between yesterday's training load and tonight's HRV is meaningful for you. Often it's not, which is itself useful information.
- **Manual log** — every strength session you log lifts CTL the next day. The Dashboard updates automatically.
---
## Where things live
| What | Where |
|---|---|
| Your config | `./openrun.toml` (edit by hand any time) |
| Your data | `./data/garmin.db` (single SQLite file — back this up) |
| Garmin auth tokens | `./.secrets/` (gitignored) |
| Logs / errors | Wherever you ran `openrun-web` — stderr in that terminal |
| Notebooks (reference) | `./examples/notebooks/` |
---
## Resetting
Want to start over?
```bash
rm -rf data/ openrun.toml .secrets/
uv run openrun-web # the wizard reappears
```
Nothing on this list touches anything outside the project directory.