Switch VO2 max source to get_max_metrics (maxmet/daily endpoint)
fitnessage endpoint contains fitness age only, not VO2 max. The maxmet endpoint (/metrics-service/metrics/maxmet/daily) is the correct source. Keep debug logging temporarily to confirm key names from live API. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -325,32 +325,57 @@ 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 and fitness age once (slow-changing — only update today's row)
|
||||
# Fetch current VO2 max once via maxmet endpoint (slow-changing — only update today's row)
|
||||
today_str = date.today().isoformat()
|
||||
mm_data = _safe(garmin.get_max_metrics, today_str)
|
||||
logger.info("maxmet raw response: %s", mm_data)
|
||||
fa_data = _safe(garmin.get_fitnessage_data, today_str)
|
||||
logger.info("fitnessage raw response: %s", fa_data)
|
||||
|
||||
vo2 = None
|
||||
if mm_data:
|
||||
# maxmet returns a list of metric dicts or a wrapper
|
||||
items = mm_data if isinstance(mm_data, list) else mm_data.get("allMetrics", {}).get("metricsMap", {})
|
||||
if isinstance(items, list):
|
||||
for item in items:
|
||||
for key in ("vo2MaxPreciseValue", "vo2Max", "generic"):
|
||||
v = item.get(key)
|
||||
if v and isinstance(v, (int, float)) and float(v) > 0:
|
||||
vo2 = float(v)
|
||||
break
|
||||
if vo2:
|
||||
break
|
||||
elif isinstance(items, dict):
|
||||
for key in ("VO2_MAX", "vo2Max", "vo2MaxPreciseValue"):
|
||||
entry = items.get(key)
|
||||
if entry:
|
||||
v = entry[0].get("value") if isinstance(entry, list) else entry.get("value")
|
||||
if v and float(v) > 0:
|
||||
vo2 = float(v)
|
||||
break
|
||||
|
||||
fa_age = None
|
||||
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")
|
||||
logger.info("fitnessage parsed: vo2=%s fa=%s", vo2, fa)
|
||||
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()
|
||||
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}
|
||||
if fa_age:
|
||||
fa_row["fitness_age"] = int(fa_age)
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user