Fix VO2 max extraction — use fitnessage API not daily stats
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 52s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 6s

get_stats() does not include VO2 max. Switch to get_fitnessage_data()
which hits /fitnessage-service/fitnessage and returns the current VO2
max estimate and fitness age. Called once per sync (today only) since
VO2 max is a slow-changing metric; the frontend carry-forward shows it
on older days. Remove the incorrect stats.get() attempt from _parse_day.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 20:53:55 +01:00
parent 13ed824f01
commit 8fd7f984d9
+25 -3
View File
@@ -325,6 +325,31 @@ def sync_wellness(garmin, user_id: int, since: Optional[datetime], db,
logger.warning("Failed to upsert health_metrics for %s: %s", day_str, exc) logger.warning("Failed to upsert health_metrics for %s: %s", day_str, exc)
db.rollback() db.rollback()
# Fetch current VO2 max and fitness age once (slow-changing — only update today's row)
today_str = date.today().isoformat()
fa_data = _safe(garmin.get_fitnessage_data, today_str)
if fa_data:
vo2 = (fa_data.get("vo2Max")
or fa_data.get("vo2MaxPreciseValue")
or fa_data.get("biometricProfile", {}).get("vo2Max"))
fa = fa_data.get("chronologicalAge") or fa_data.get("fitnessAge")
if vo2 and float(vo2) > 0:
try:
fa_row = {"vo2max": float(vo2)}
if fa:
fa_row["fitness_age"] = int(fa)
fa_cols = list(fa_row.keys())
db.execute(text(f"""
INSERT INTO health_metrics (user_id, date, {", ".join(fa_cols)})
VALUES (:user_id, :day, {", ".join(f":{c}" for c in fa_cols)})
ON CONFLICT (user_id, date) DO UPDATE SET
{", ".join(f"{c} = EXCLUDED.{c}" for c in fa_cols)}
"""), {"user_id": user_id, "day": today_str, **fa_row})
db.commit()
except Exception as exc:
logger.warning("Failed to upsert VO2 max: %s", exc)
db.rollback()
return processed return processed
@@ -469,9 +494,6 @@ def _parse_day(stats, sleep_data, hrv_data) -> dict:
_set(row, "active_calories", active) _set(row, "active_calories", active)
if active and bmr: if active and bmr:
_set(row, "total_calories", float(active) + float(bmr)) _set(row, "total_calories", float(active) + float(bmr))
vo2 = stats.get("vo2MaxPreciseValue") or stats.get("vo2Max")
if vo2 and float(vo2) > 0:
_set(row, "vo2max", float(vo2))
if sleep_data: if sleep_data:
dto = sleep_data.get("dailySleepDTO") or sleep_data dto = sleep_data.get("dailySleepDTO") or sleep_data