Fix upload auto-refresh, health data refresh, and HR zone recalculation
- UploadPage now polls task status every 2s and invalidates activity, health-summary, and health-metrics queries on completion so new activities and health data appear without a hard refresh - Garmin and Strava export endpoints now return a task_id for polling - Updating max HR in Profile triggers a background Celery task to recalculate hr_zones for all existing activities; profile page shows a confirmation note when this is queued - Add CLAUDE.md with repo architecture and dev commands Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,7 @@ async def update_profile(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
old_max_hr = current_user.max_heart_rate
|
||||
if body.max_heart_rate is not None:
|
||||
if not (100 <= body.max_heart_rate <= 250):
|
||||
raise HTTPException(400, "Max HR must be 100–250")
|
||||
@@ -74,6 +75,11 @@ async def update_profile(
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(current_user)
|
||||
|
||||
if body.max_heart_rate is not None and body.max_heart_rate != old_max_hr:
|
||||
from app.workers.tasks import recalculate_hr_zones_for_user
|
||||
recalculate_hr_zones_for_user.delay(current_user.id, body.max_heart_rate)
|
||||
|
||||
return {**{c.name: getattr(current_user, c.name)
|
||||
for c in User.__table__.columns},
|
||||
"estimated_max_hr": _estimated_max_hr(current_user)}
|
||||
|
||||
@@ -82,7 +82,7 @@ async def upload_garmin_export(
|
||||
return {
|
||||
"status": "queued",
|
||||
"activity_tasks": len(task_ids),
|
||||
"health_task": health_task.id,
|
||||
"task_id": health_task.id,
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ async def upload_strava_export(
|
||||
return {
|
||||
"status": "queued",
|
||||
"activity_tasks": len(task_ids),
|
||||
"task_id": task_ids[-1] if task_ids else None,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -439,4 +439,29 @@ def process_garmin_health_zip(zip_path: str, user_id: int):
|
||||
"spo2": data.get("avgSpo2"),
|
||||
})
|
||||
|
||||
db.commit()
|
||||
db.commit()
|
||||
|
||||
|
||||
@celery_app.task(name="recalculate_hr_zones_for_user")
|
||||
def recalculate_hr_zones_for_user(user_id: int, new_max_hr: float):
|
||||
"""Recalculate hr_zones for all of a user's activities using a new max HR."""
|
||||
from app.services.fit_parser import calculate_hr_zones
|
||||
from app.core.database import SyncSessionLocal
|
||||
from app.models.user import Activity, ActivityDataPoint
|
||||
from sqlalchemy import select
|
||||
|
||||
with SyncSessionLocal() as db:
|
||||
activities = db.execute(
|
||||
select(Activity).where(Activity.user_id == user_id)
|
||||
).scalars().all()
|
||||
|
||||
for activity in activities:
|
||||
data_points = db.execute(
|
||||
select(ActivityDataPoint).where(ActivityDataPoint.activity_id == activity.id)
|
||||
).scalars().all()
|
||||
points_dicts = [{"heart_rate": dp.heart_rate} for dp in data_points]
|
||||
new_zones = calculate_hr_zones(points_dicts, new_max_hr)
|
||||
if new_zones:
|
||||
activity.hr_zones = new_zones
|
||||
|
||||
db.commit()
|
||||
|
||||
Reference in New Issue
Block a user