Skip to content

fix(sync): support new Garmin API metric format + backfill script + make targets#203

Merged
gerfru merged 1 commit into
mainfrom
wave-17-sync
Jun 13, 2026
Merged

fix(sync): support new Garmin API metric format + backfill script + make targets#203
gerfru merged 1 commit into
mainfrom
wave-17-sync

Conversation

@gerfru

@gerfru gerfru commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Garmin API format change: activityDetailMetrics changed from per-metric-type groups (with sequenceNumber) to positional value arrays per timepoint (with a separate metricDescriptors lookup). The old mapper silently returned 0 records for all activities using the new format.
  • Elevation fallback removed: directGrade is a % slope value, not elevation in metres — was silently writing wrong data
  • Backfill script: one-shot make backfill-records to retroactively fetch GPS/HR detail for historical activities missing records
  • Makefile fixes: make trigger-sync and make db were reading DB_APP_USER from the wrong env file, causing role "-d" does not exist errors

Changes

File Change
mapper.py _build_metric_index_v2 for new format; auto-detects via metricDescriptors key; removes directGrade elevation fallback
repositories/base.py Abstract get_activities_without_records method
repositories/timescale.py Implementation: SELECT activities WHERE no records exist
backfill_records.py New script: fetches Garmin detail for all activities with 0 records, rate-limited at 0.5 s/call
Makefile backfill-records target; hardcode garmin_app in trigger-sync + db targets
tests/test_mapper.py 5 tests for new positional array format (HR, elevation, pace, GPS, count)
tests/test_repository.py 2 tests for get_activities_without_records

Test plan

  • cd sync-service && .venv/bin/pytest tests/ -v — all tests pass
  • make trigger-sync — no longer errors with role "-d" does not exist
  • make backfill-records — fetches records for outdoor activities missing GPS/HR data
  • Activity page for a cycling/running activity shows HR + elevation + pace charts after backfill

🤖 Generated with Claude Code

…ake targets

Garmin changed activityDetailMetrics from per-metric-type groups (with
sequenceNumber) to positional value arrays per timepoint (with a separate
metricDescriptors lookup). Old mapper silently produced 0 records for all
activities using the new format.

- _build_metric_index_v2: handles new format via metricDescriptors +
  positional array alignment; old _build_metric_index kept for legacy
- map_records: auto-detects format by presence of metricDescriptors key
- elevation fallback removed: directGrade is a % slope, not elevation in m
- get_activities_without_records: new repo method + abstract base declaration;
  returns activities with 0 records for targeted backfill
- backfill_records.py: one-shot script to fetch GPS/HR detail from Garmin for
  all historical activities missing records; rate-limited (0.5 s between calls)
- Makefile: backfill-records target; fix trigger-sync + db targets (DB_APP_USER
  was read from wrong env file, now hardcoded to garmin_app like backfill-battery)
- Tests: 5 new mapper tests for new API format; 2 repository tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@gerfru gerfru merged commit 6e14788 into main Jun 13, 2026
13 checks passed
@gerfru gerfru deleted the wave-17-sync branch June 13, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant