Skip to main content

GraFIT

Loads Garmin FIT files into a SQLite database and visualizes them in Grafana.

GraFIT parses .fit files (activities, sleep, GPS, etc.) using python-fitparse, writes them to SQLite using a schema generated from the FIT profile, and ships with Grafana provisioning (datasource + dashboards) so you can browse the data immediately.

Install

uv tool install grafit

Or run without installing:

uvx grafit <command> [options]

Requires Python 3.11+.

Usage

uv run grafit <command> [options]

Commands

  • init <db_path> — create and initialize the SQLite database.
  • import <path> <db_path> — import a FIT file or directory of FIT files into the database.
    • --force reimport files already present in the database.
    • --no-recursive do not recurse into subdirectories.
    • --fetch-weather look up historical hourly weather (temperature, apparent temperature, humidity) for each session's start coordinates and date from the Open-Meteo archive. Failures are logged and skipped — the FIT import itself never fails because of weather.
  • backfill-weather <db_path> — fetch weather for sessions already imported without --fetch-weather. Safe to re-run: only sessions missing weather are queried; rate-limited or failed sessions are picked up on the next run.
    • --force re-fetch weather even for sessions that already have it.
  • stats <db_path> — show row counts per table.
  • purge <db_path> --file-hash <sha256> — remove all rows imported from a given file.
  • generate-schema <output> — write the full DDL SQL to a file.
  • install-provisioning <dest> — copy the bundled Grafana provisioning (datasource + dashboards) to a directory.
    • --force overwrite the destination if it exists.
  • demo-data <dest> — generate a synthetic Garmin FIT tree (see Demo data).
    • --days days of history (default 365), --end-date YYYY-MM-DD last day (default yesterday), --seed random seed, --force write into a non-empty directory.
  • demo-weather <db_path> — fill weather_hourly with modelled Los Angeles weather for demo data, without calling Open-Meteo.

Global flag: -v / --verbose for debug logging.

Upgrading

The schema is generated from the FIT profile, so upgrading GraFIT (or its python-fitparse dependency) can add new fields. On the next import/init, missing columns are added to existing tables automatically (you'll see a Schema: added N new column(s) notice). New columns are NULL for already-imported rows — to backfill history, re-import the affected files with --force or rebuild the database from your FIT source.

Grafana

A docker-compose.yml is provided to run Grafana with the SQLite datasource plugin against ./fit_data.db and the bundled dashboards mounted from ./grafana-provisioning.

Note: the bundled compose file enables anonymous admin access and disables the login form. It is intended for local use only — do not expose it to a network.

Drop your FIT files into ./GARMIN and bring it up:

docker compose up

A grafit-init container installs the bundled provisioning, runs grafit init, and runs grafit import ./GARMIN against shared volumes; Grafana waits for it to finish before starting. Re-running docker compose up reuses the database — already-imported files are skipped by hash.

Grafana is then available at http://localhost:3000.

Demo data

Don't have a Garmin dump handy (or want to see the dashboards without publishing your own data)? GraFIT can generate a synthetic one for a fictional Los Angeles athlete — a year of activities on real LA routes, sleep, stress, body battery, HRV, SpO2, saved locations and the odd plane spotted overhead:

docker compose -f docker-compose.demo.yml up

That generates the FIT files into their own volume, imports them into a separate database and starts Grafana on http://localhost:3000 with everything populated. It uses its own volumes, so it never touches the data from docker-compose.yml. Set GRAFIT_DEMO_DAYS=90 to generate less history — a year takes a couple of minutes and about 300 MB of database, 90 days about 77 MB.

Or generate the files yourself and import them like any other FIT directory:

uvx grafit demo-data ./demo-garmin --days 90
uvx grafit import ./demo-garmin demo.db
uvx grafit demo-weather demo.db

The data is deterministic: the same --seed, --end-date and --days always produce byte-identical files. --end-date defaults to yesterday, so pin it if you want a dataset that doesn't move when you regenerate it on another day. Weather is modelled offline from Los Angeles climate normals (demo-weather) rather than fetched, so no network is needed and it works for any date range.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

grafit-1.37.0.tar.gz (64.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

grafit-1.37.0-py3-none-any.whl (78.2 kB view details)

Uploaded Python 3

File details

Details for the file grafit-1.37.0.tar.gz.

File metadata

  • Download URL: grafit-1.37.0.tar.gz
  • Upload date:
  • Size: 64.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"10","id":"buster","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for grafit-1.37.0.tar.gz
Algorithm Hash digest
SHA256 db1659f9ff0425b9337986de91ff5693a2e0b0b7d5f8bb4a4667f478e5b8b954
MD5 88807bfaf741334b7222ebb53b2279ce
BLAKE2b-256 fc6cf41cffadca8d002da6e412604ef4646d872da96b12338c8bf82ed17910e5

See more details on using hashes here.

File details

Details for the file grafit-1.37.0-py3-none-any.whl.

File metadata

  • Download URL: grafit-1.37.0-py3-none-any.whl
  • Upload date:
  • Size: 78.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"10","id":"buster","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for grafit-1.37.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cb18cae921d53087ae5ac551b04b8daf9d241c5c890ee1f9257c120429361740
MD5 a3d7d05924860019864b46b88de4893c
BLAKE2b-256 5203f64e82900052f605edbb3d5a6fa17d227dce9d3f002443760c50277701d2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page