Add sync progress bar; change auto-sync to every 30 minutes
Build and push images / validate (push) Successful in 2s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 16s

Backend:
- Change beat schedule from 3600s (hourly) to 1800s (30 minutes)
- Emit intermediate last_sync_status DB commits at each phase of
  sync_garmin_connect_user ("Connecting to Garmin...", "Syncing activities...",
  "Syncing wellness data...") so the frontend can reflect live progress.
  Snapshot config fields upfront to avoid reading expired ORM attrs after commits.

Frontend (ProfilePage):
- Replace blind 3-second timeout with 2s polling loop that reads the live
  last_sync_status from /garmin-sync/config after triggering a sync.
- Wait until an in-progress status is observed before declaring completion,
  avoiding a false-finish on the previous terminal status.
- Show an animated progress bar that advances through the sync phases with
  the current status text below it. Safety timeout stops polling after 10 min.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 10:36:15 +01:00
parent a9b3da858d
commit a28ce0e009
2 changed files with 69 additions and 11 deletions
+26 -9
View File
@@ -23,9 +23,9 @@ celery_app.conf.update(
task_track_started=True,
worker_prefetch_multiplier=1,
beat_schedule={
"sync-garmin-connect-hourly": {
"sync-garmin-connect": {
"task": "sync_all_garmin_connect",
"schedule": 3600.0, # every hour
"schedule": 1800.0, # every 30 minutes
},
},
)
@@ -481,8 +481,20 @@ def sync_garmin_connect_user(user_id: int):
if not cfg or not cfg.sync_enabled:
return {"status": "skipped"}
# Snapshot config values before any intermediate commits (commits expire ORM attrs)
email = cfg.email
password_enc = cfg.password_enc
token_store = cfg.token_store
last_sync_at = cfg.last_sync_at
sync_acts = cfg.sync_activities
sync_well = cfg.sync_wellness
lookback = cfg.sync_lookback_days if cfg.sync_lookback_days is not None else 30
cfg.last_sync_status = "Connecting to Garmin..."
db.commit()
try:
garmin, new_token = authenticate_garmin(cfg.email, cfg.password_enc, cfg.token_store)
garmin, new_token = authenticate_garmin(email, password_enc, token_store)
except Exception as exc:
cfg.last_sync_at = datetime.now(timezone.utc)
cfg.last_sync_status = f"Auth error: {exc}"
@@ -491,25 +503,30 @@ def sync_garmin_connect_user(user_id: int):
if new_token:
cfg.token_store = new_token
db.commit()
activities_queued = 0
wellness_days = 0
errors = []
if cfg.sync_activities:
if sync_acts:
cfg.last_sync_status = "Syncing activities..."
db.commit()
try:
activities_queued = sync_activities(
garmin, user_id, cfg.last_sync_at, db, settings.file_store_path,
lookback_days=cfg.sync_lookback_days if cfg.sync_lookback_days is not None else 30,
garmin, user_id, last_sync_at, db, settings.file_store_path,
lookback_days=lookback,
)
except Exception as exc:
errors.append(f"activities: {exc}")
if cfg.sync_wellness:
if sync_well:
cfg.last_sync_status = "Syncing wellness data..."
db.commit()
try:
wellness_days = sync_wellness(
garmin, user_id, cfg.last_sync_at, db,
lookback_days=cfg.sync_lookback_days if cfg.sync_lookback_days is not None else 90,
garmin, user_id, last_sync_at, db,
lookback_days=lookback,
)
except Exception as exc:
errors.append(f"wellness: {exc}")