Personal, local-first, config-driven analytics & visualization platform for Garmin fitness data.
Start with world-class swimming intelligence — length-by-length timelines, SWOLF frontiers, set reconstruction, real coaching advice. Extend to running, cycling, strength — without touching core code.
Built for athletes who want truth, not marketing dashboards.
Garmin gives you raw data. Strava and Connect give you pretty graphs. You want insight:
- What did I actually do in that set?
- Why is my SWOLF stuck?
- What should I change tomorrow?
- Is this week's training working?
GarminLens turns every FIT file and export ZIP into a dense, actionable intelligence system — fully offline, fully private, fully yours.
- Config is king — every sport rule, metric, heuristic, color, and advice lever lives in
config/. Coresrc/code is 100% generic. - Local-first & private — your data never leaves your machine (DuckDB flat file)
- Keyboard-first UX — ⌘K command palette, ⌘U unit toggle, J/K navigation, compare mode
- No black boxes — every number shows provenance via tooltips
| Layer | Technologies |
|---|---|
| Backend | Python 3.12+, DuckDB, Polars, Pydantic v2, FastAPI, Loguru |
| Frontend | Next.js 15 (App Router), TypeScript, Tailwind, shadcn/ui, Radix UI, Recharts |
| Monorepo | Turborepo |
- Python 3.12+
- Node.js 20+ / npm 10+
- macOS, Linux, or WSL2
cd src/backend
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtcd src/frontend
npm installAll commands run from src/backend/ with the virtual environment activated.
# Ingest a Garmin export ZIP or FIT file
python -m garminlens.cli ingest --source ~/Downloads/export.zip
# Validate DB integrity without ingesting
python -m garminlens.cli validate
# Summarize the latest session
python -m garminlens.cli summarize --latest
# Diff two sessions
python -m garminlens.cli diff <activity_id_a> <activity_id_b>
# Export metrics to JSON/CSV
python -m garminlens.cli export --format json --output out.json
# Recompute all derived metrics
python -m garminlens.cli recompute --all
# Start the API server
python -m garminlens.cli serve
# → http://127.0.0.1:8000Start the backend server with python -m garminlens.cli serve, then:
| Method | Route | Description |
|---|---|---|
| GET | /activities |
List all activities |
| GET | /activities/{id} |
Activity detail |
| GET | /activities/{id}/lengths |
All lengths for an activity |
| GET | /activities/{id}/sets |
Reconstructed sets |
| GET | /activities/{id}/metrics |
Computed session metrics |
| POST | /ingest |
Ingest a FIT file |
| GET | /analytics/trends |
Rolling trend series |
| GET | /analytics/prs |
Personal records |
| GET | /analytics/streak |
Training streak |
| GET | /analytics/compare |
Metric diff between two activities |
| GET | /analytics/quality |
Quality flag aggregation |
| GET | /coaching/{id} |
Coaching critique for an activity |
| GET | /coaching/notifications/all |
Recent coaching notifications |
| GET | /experiments |
Experiment definitions |
| GET | /experiments/{id}/weeks |
Weekly experiment rows |
| GET | /config/ui-tokens |
UI design tokens |
| GET | /config/defaults |
App defaults |
| GET | /config/metrics |
Metric catalog |
Swagger UI: http://127.0.0.1:8000/docs
cd src/frontend
npm run dev
# → http://localhost:3000The Next.js dev server proxies /api/* to http://127.0.0.1:8000 automatically (see next.config.ts).
Start the backend server first.
garminlens/
├── config/ # ALL domain logic lives here (metrics, heuristics, UI tokens…)
│ ├── activities.yaml # Supported activity types
│ ├── defaults.yaml # App-wide defaults
│ ├── analytics.yaml # Trend/consistency/frontier config
│ ├── quality_flags.yaml # Quality flag severity mapping (high/medium/low)
│ ├── metrics/ # Metric formula YAML files (common.yaml, swim.yaml, run.yaml…)
│ ├── heuristics/ # Quality checks, set reconstruction, coaching levers
│ ├── pool.yaml # Known pool lengths + device quirks
│ └── ui/ # Tokens (stroke colors, activity type icons), themes, labels
├── data/ # DuckDB at data/garminlens.db; raw_vault/ for FIT files
├── docs/ # Architecture, data model, all module docs
├── src/
│ ├── backend/ # Python package: garminlens.*
│ │ └── garminlens/
│ │ ├── api/ # FastAPI routes + schemas
│ │ ├── analytics/ # Trends, PRs, experiments, coaching
│ │ ├── config/ # Config loader + Pydantic models
│ │ ├── db/ # DuckDB connection + migrations
│ │ ├── ingestion/ # Parser, normalizer, storage pipeline
│ │ ├── metrics/ # Formula engine + registry
│ │ └── reconstruction/ # Set segmenter, labeler, repeat detector
│ └── frontend/ # Next.js 15 App Router
│ └── src/
│ ├── app/ # Pages and layouts
│ ├── components/ # UI components (dashboard/, drawer/, charts/, ui/)
│ └── lib/ # api.ts, store.ts, types.ts, utils.ts
└── turbo.json # Turborepo config
No domain logic belongs in src/. Everything that differs between sports or preferences lives in config/:
- Metric formulas →
config/metrics/*.yaml - Set reconstruction rules →
config/heuristics/set_reconstruction.yaml - Coaching levers →
config/heuristics/coaching_levers.yaml - UI colors, activity type icons, sparkline definitions →
config/ui/tokens.yaml - Quality check thresholds →
config/heuristics/quality_checks.yaml - Quality flag severity →
config/quality_flags.yaml
To add a new sport (e.g. cycling), add its entry to config/activities.yaml and metric definitions to config/metrics/bike.yaml. No Python or TypeScript changes needed.
Full technical documentation lives in docs/:
ARCHITECTURE.md— system design and component boundariesDATA_MODEL.md— DuckDB schema and entity relationshipsDATA_FLOW.md— end-to-end data pipelineINGESTION.md— FIT file parsing and storageQUALITY_AND_NORMALIZATION.md— quality gates and normalization rulesMETRICS.md— metric formula engineRECONSTRUCTION.md— set/interval detectionANALYTICS.md— trends, PRs, consistency, experimentsCOACHING.md— coaching lever systemEXPERIMENTS_AND_COMPARISON.md— weekly progress and session diffVISUALIZATIONS.md— frontend component catalogEXTENDING_INTEGRATIONS.md— adding sports, export, integrationsCONFIGURATION.md— full config schema referenceTECHNICAL_STANDARDS.md— code style and patterns