Fix Garmin stats sync, add route merge/map/links, fix PR constraint
Garmin health: fix display_name=None when using stored OAuth tokens.
authenticate_garmin() now calls login(tokenstore=...) instead of
garth.loads() directly, so display_name is populated and get_user_summary
works. Also add avg_hr_day / max_hr_day from stats response.
Routes: add merge endpoint (POST /{id}/merge/{source}), delete endpoint.
Routes page: polyline SVG mini-map on each route card, merge UI with
confirmation, activity rows are now Links to the activity detail page.
Personal records: replace all-columns unique constraint with a partial
index (unique on current records only) to stop UniqueViolation crashes
when parallel workers deactivate the same PR.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -46,15 +46,17 @@ def authenticate_garmin(email: str, password_enc: str, token_store: Optional[str
|
||||
"""
|
||||
import garminconnect
|
||||
|
||||
# Try stored OAuth token first (garth auto-refreshes access token on use)
|
||||
# Try stored OAuth token first.
|
||||
# Must call login(tokenstore=...) rather than garth.loads() directly so that
|
||||
# garmin.display_name is populated — it's required by get_user_summary() and
|
||||
# several other endpoints. Without it every stats call silently returns None.
|
||||
if token_store:
|
||||
try:
|
||||
garmin = garminconnect.Garmin(
|
||||
email=email, password=decrypt_password(password_enc)
|
||||
)
|
||||
garmin.garth.loads(token_store)
|
||||
garmin.get_full_name() # lightweight request; triggers refresh if needed
|
||||
return garmin, None # tokens still valid
|
||||
garmin.login(tokenstore=token_store)
|
||||
return garmin, None
|
||||
except Exception as exc:
|
||||
logger.info("Garmin token invalid (%s), re-authenticating", exc)
|
||||
|
||||
@@ -234,6 +236,8 @@ def _parse_day(stats, sleep_data, hrv_data) -> dict:
|
||||
|
||||
if stats:
|
||||
_set(row, "resting_hr", stats.get("restingHeartRate"))
|
||||
_set(row, "avg_hr_day", stats.get("averageHeartRate"))
|
||||
_set(row, "max_hr_day", stats.get("maxHeartRate"))
|
||||
_set(row, "steps", stats.get("totalSteps"))
|
||||
_set(row, "floors_climbed", stats.get("floorsAscended"))
|
||||
_set(row, "avg_stress", stats.get("averageStressLevel"))
|
||||
|
||||
Reference in New Issue
Block a user