# 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 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 . 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 24–72 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 1–5 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 `_.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 30–60 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.