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.

Monitor files imported by older versions have no timestamps on the 24/7 heart rate and intraday step samples; import reports how many such rows remain. Re-import them with grafit import <GARMIN>/Monitor <db_path> --force.

Files that change in place

Garmin devices keep appending to Monitor and Device files after they're first synced, and rewrite state files such as Location/Lctns.fit or Coach/schedule.fit on almost every sync. A file is identified by its path exactly as it was imported, so when the content at that path changes, the next import replaces the previous import instead of adding a second copy:

  • Rows from the previous version dated before anything in the new version (history that rotated off the front of the file) stay in the live tables under the new import.
  • Every other row from the previous version is moved to a _superseded_<table> table in the same database, along with its _import_log entry. Nothing is deleted, and the dashboards never read these tables.
  • If the new version fails to import, the previous one stays live.

Databases built by older versions can hold several imports of the same path. import folds them the same way before it starts and reports it (Superseded: N older import(s) replaced; ...).

Always import a tree through the same path: ./GARMIN and /garmin count as different files.

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

For demo/testing purposes, GraFIT can generate a synthetic dataset for a fictional Los Angeles athlete — a year of activities, sleep, stress, body battery, HRV, SpO2, saved locations etc:

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. Set GRAFIT_DEMO_DAYS=90 to generate less history — a year takes a couple of minutes and about 460 MB of database, 90 days about 115 MB.

Demo route geometry © OpenStreetMap contributors (ODbL), elevations from Open-Meteo (Copernicus DEM GLO-90).

Release files for grafit 1.46.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for grafit 1.46.0
File Size Uploaded
grafit-1.46.0.tar.gz 75.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for grafit 1.46.0
File Interpreter ABI Platform
grafit-1.46.0-py3-none-any.whl Python 3 none any Details

Total release size: 163.8 kB

Release files / grafit-1.46.0.tar.gz

Download URL grafit-1.46.0.tar.gz
Size 75.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b54c32ad1d90bc2d20f02f7407f706e416015be600747d6e391acc40f63e1790
BLAKE2b-256 checksum
How to use checksums
26c150ff01d93f5687d44dbb9d50b12d6a95fb0cbc4b2d936a89ccb13d6214ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","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}

Release files / grafit-1.46.0-py3-none-any.whl

Download URL grafit-1.46.0-py3-none-any.whl
Size 88.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc778ae9a9900a3db74ca795f55c9b63ebadb0da293f5e7fd9e5d0a052492713
BLAKE2b-256 checksum
How to use checksums
9fd7a0af673ec861556e6fb77b538e05aea0257f3b90e5219b28664c97f5071d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","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}

Release history Release notifications | RSS feed

This release

1.46.0 This release

2 release files

1.45.0

2 release files

1.43.0

2 release files

1.42.0

2 release files

1.41.1

2 release files

1.41.0

2 release files

1.40.0

2 release files

1.39.0

2 release files

1.38.0

2 release files

1.37.0

2 release files

1.35.0

2 release files

1.34.0

2 release files

1.33.0

2 release files

1.32.0

2 release files

1.31.0

2 release files

1.30.0

2 release files

1.29.1

2 release files

1.29.0

2 release files

1.28.0

2 release files

1.25.0

2 release files

1.24.0

2 release files

1.23.0

2 release files

1.22.0

2 release files

1.21.0

2 release files

1.20.0

2 release files

1.19.0

2 release files

1.18.0

2 release files

1.17.0

2 release files

1.16.0

2 release files

1.15.0

2 release files

1.14.0

2 release files

1.9.0

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page