Fetch full VO2 max history via maxmet/daily range query
Instead of storing only the most recent measurement, query the maxmet endpoint with the full sync window (start_date to today) to populate one row per measurement date. Falls back to training_status most-recent value if the range query returns nothing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -325,27 +325,39 @@ 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 VO2 max from training status (slow-changing — store against measurement date)
|
# Fetch historical VO2 max across the full sync window via maxmet/daily range query
|
||||||
today_str = date.today().isoformat()
|
today_str = date.today().isoformat()
|
||||||
ts_data = _safe(garmin.get_training_status, today_str)
|
|
||||||
fa_data = _safe(garmin.get_fitnessage_data, today_str)
|
fa_data = _safe(garmin.get_fitnessage_data, today_str)
|
||||||
|
|
||||||
vo2 = None
|
|
||||||
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)
|
|
||||||
vo2_date = generic.get("calendarDate") or today_str
|
|
||||||
|
|
||||||
fa_age = None
|
fa_age = None
|
||||||
if fa_data:
|
if fa_data:
|
||||||
fa_age = fa_data.get("fitnessAge") or fa_data.get("achievableFitnessAge")
|
fa_age = fa_data.get("fitnessAge") or fa_data.get("achievableFitnessAge")
|
||||||
|
|
||||||
if vo2:
|
|
||||||
try:
|
try:
|
||||||
fa_row = {"vo2max": vo2}
|
mm_history = garmin.connectapi(
|
||||||
if fa_age:
|
f"/metrics-service/metrics/maxmet/daily/{start_date.isoformat()}/{today_str}"
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.debug("maxmet history fetch failed: %s", exc)
|
||||||
|
mm_history = []
|
||||||
|
|
||||||
|
# Fall back to most-recent from training status if history is empty
|
||||||
|
if not mm_history:
|
||||||
|
ts_data = _safe(garmin.get_training_status, 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:
|
||||||
|
mm_history = [{"calendarDate": generic.get("calendarDate") or today_str,
|
||||||
|
"vo2MaxPreciseValue": float(v)}]
|
||||||
|
|
||||||
|
stored = 0
|
||||||
|
for entry in (mm_history or []):
|
||||||
|
v = entry.get("vo2MaxPreciseValue") or entry.get("vo2MaxValue")
|
||||||
|
if not v or float(v) <= 0:
|
||||||
|
continue
|
||||||
|
entry_date = entry.get("calendarDate") or today_str
|
||||||
|
try:
|
||||||
|
fa_row = {"vo2max": float(v)}
|
||||||
|
if fa_age and entry_date == today_str:
|
||||||
fa_row["fitness_age"] = int(fa_age)
|
fa_row["fitness_age"] = int(fa_age)
|
||||||
fa_cols = list(fa_row.keys())
|
fa_cols = list(fa_row.keys())
|
||||||
db.execute(text(f"""
|
db.execute(text(f"""
|
||||||
@@ -353,13 +365,16 @@ def sync_wellness(garmin, user_id: int, since: Optional[datetime], db,
|
|||||||
VALUES (:user_id, :day, {", ".join(f":{c}" for c in fa_cols)})
|
VALUES (:user_id, :day, {", ".join(f":{c}" for c in fa_cols)})
|
||||||
ON CONFLICT (user_id, date) DO UPDATE SET
|
ON CONFLICT (user_id, date) DO UPDATE SET
|
||||||
{", ".join(f"{c} = EXCLUDED.{c}" for c in fa_cols)}
|
{", ".join(f"{c} = EXCLUDED.{c}" for c in fa_cols)}
|
||||||
"""), {"user_id": user_id, "day": vo2_date, **fa_row})
|
"""), {"user_id": user_id, "day": entry_date, **fa_row})
|
||||||
db.commit()
|
db.commit()
|
||||||
logger.info("Stored VO2 max %.1f for %s", vo2, vo2_date)
|
stored += 1
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("Failed to upsert VO2 max: %s", exc)
|
logger.warning("Failed to upsert VO2 max for %s: %s", entry_date, exc)
|
||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
|
if stored:
|
||||||
|
logger.info("Stored %d VO2 max data points", stored)
|
||||||
|
|
||||||
return processed
|
return processed
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user