Fix VO2 max extraction from training_status.mostRecentVO2Max.generic
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 6s
Build and push images / build-frontend (push) Successful in 5s

Correct path confirmed from live API response. Store against the actual
measurement date (calendarDate from the VO2 max record) rather than
today, so the carry-forward logic shows the right value from the correct
day. Also store fitnessAge from the fitnessage endpoint alongside it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 22:16:09 +01:00
parent 41a39ec3c7
commit 854d4ed7cb
+7 -23
View File
@@ -325,40 +325,23 @@ def sync_wellness(garmin, user_id: int, since: Optional[datetime], db,
logger.warning("Failed to upsert health_metrics for %s: %s", day_str, exc)
db.rollback()
# Fetch current VO2 max once (slow-changing — only update today's row)
# Fetch VO2 max from training status (slow-changing — store against measurement date)
today_str = date.today().isoformat()
ts_data = _safe(garmin.get_training_status, today_str)
logger.info("training_status raw response: %s", ts_data)
sb_data = _safe(garmin.get_stats_and_body, today_str)
logger.info("stats_and_body raw response: %s", sb_data)
fa_data = _safe(garmin.get_fitnessage_data, today_str)
vo2 = None
# Try training status first
if ts_data and not vo2:
for key in ("vo2MaxPreciseValue", "vo2Max", "latestVO2Max"):
v = ts_data.get(key)
if v and isinstance(v, (int, float)) and float(v) > 0:
vo2 = float(v)
break
if not vo2 and isinstance(ts_data.get("mostRecentVO2Max"), dict):
v = ts_data["mostRecentVO2Max"].get("generic") or ts_data["mostRecentVO2Max"].get("value")
vo2_date = today_str
generic = (ts_data or {}).get("mostRecentVO2Max", {}).get("generic") or {}
v = generic.get("vo2MaxPreciseValue") or generic.get("vo2MaxValue")
if v and float(v) > 0:
vo2 = float(v)
# Try stats_and_body
if sb_data and not vo2:
for key in ("vo2MaxPreciseValue", "vo2Max"):
v = sb_data.get(key)
if v and isinstance(v, (int, float)) and float(v) > 0:
vo2 = float(v)
break
vo2_date = generic.get("calendarDate") or today_str
fa_age = None
if fa_data:
fa_age = fa_data.get("fitnessAge") or fa_data.get("achievableFitnessAge")
logger.info("parsed vo2=%s fitness_age=%s", vo2, fa_age)
if vo2:
try:
fa_row = {"vo2max": vo2}
@@ -370,8 +353,9 @@ def sync_wellness(garmin, user_id: int, since: Optional[datetime], db,
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})
"""), {"user_id": user_id, "day": vo2_date, **fa_row})
db.commit()
logger.info("Stored VO2 max %.1f for %s", vo2, vo2_date)
except Exception as exc:
logger.warning("Failed to upsert VO2 max: %s", exc)
db.rollback()