# 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 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 `<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 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.
