Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Running Tracker

A personal running performance dashboard built entirely with Claude Code.

Tracks training over time, syncs with Garmin Connect, and predicts race finish times — built to target a sub-85-minute half-marathon.

Stack


What it does

  • Garmin sync — pulls running activities from Garmin Connect via python-garminconnect; tokens cached after first login
  • Activity import — drag-and-drop FIT, GPX, or Garmin's CSV export (Activities.csv) as an alternative to live sync
  • Dashboard — pace trends, weekly mileage, personal bests
  • Race goal tracker — predicts finish time using the Riegel formula based on recent runs
  • 5-week training plan — auto-generated from your race date: Base → Build → Peak → Taper → Race week, with knee stretch reminders and checkboxes
  • Training load — weekly volume chart with spike warnings (>10% week-on-week flags injury risk)
  • Pace calculator — per-km split breakdown for any target time

Stack

Layer Tech
Frontend React + TypeScript + Vite + Tailwind + Recharts
Backend FastAPI + SQLite (SQLModel)
Garmin python-garminconnect (unofficial API)

Setup

Backend

cd backend
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
cp .env.example .env   # add your Garmin credentials

Frontend

cd frontend
npm install

Run

./start.sh   # starts both servers; Ctrl+C to stop

Open http://localhost:5173.


Garmin credentials

Add your Garmin Connect email and password to backend/.env (gitignored):

GARMIN_EMAIL=you@example.com
GARMIN_PASSWORD=yourpassword

OAuth tokens are cached in backend/.garmin_tokens/ after the first successful login — also gitignored.


Built with Claude Code — key steps

This app was built in a single Claude Code session. Here's a condensed version of the process:

1. Requirements via AskUserQuestion

Claude used its interactive question tool to clarify app type (web), data source (Garmin API), and desired features before writing a single line of code.

2. Full-stack scaffold in one pass

Claude generated the entire project structure in parallel — FastAPI backend, React frontend, all components — without switching back and forth.

3. Fixes along the way

Blank page on first load The tsconfig.json was missing. Without it Vite's TypeScript compilation silently failed, leaving a blank screen. Claude spotted it from the browser console error.

Object.keys(null) crash When no runs exist yet, the backend returns a stats object without a personal_bests key. The frontend crashed trying to call Object.keys() on it. One-line null-guard fix.

Garmin 429 rate limit python-garminconnect hits Garmin's SSO on every call by default. Garmin rate-limits this aggressively. Fix: cache OAuth tokens to disk on first login (client.garth.dump(TOKEN_STORE)) and reuse them on subsequent syncs — SSO is only hit once.

Token store FileNotFoundError The library expected the token directory to already exist before writing to it. Fix: os.makedirs(TOKEN_STORE, exist_ok=True) before login, plus a guard to only pass tokenstore to login() when the file already exists.

Silent token cache failure garth.dump() was wrapped in a bare except: pass, meaning a failed save went unnoticed and every future sync would hit SSO again. Removed the silent catch so failures surface immediately.

CSV import as Garmin sync fallback Garmin's SSO rate-limits aggressively — after repeated 429 errors with no resolution, a CSV/FIT/GPX file import was added. Garmin Connect lets you export all activities as a single Activities.csv instantly, bypassing the API entirely.

TypeScript tuple type error type DayTemplate = [WorkoutType, number?, string?, string, string, boolean?] — TypeScript does not allow required elements after optional ones in a tuple. Fixed by using number | undefined instead of number? for mid-tuple elements.


Notes

  • The Garmin integration uses an unofficial API — it may break if Garmin changes their auth flow.
  • First Garmin sync can be slow (~200 activities). Subsequent syncs skip already-imported runs.
  • As a reliable alternative, export Activities.csv from Garmin Connect and use the Import button.
  • Race time prediction uses the Riegel formula: T2 = T1 × (D2/D1)^1.06
  • The 5-week training plan is generated from your race date. Update the goal if the plan dates look off.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages