Add trend-range gating, vehicle filter, sync cancel, moving time, and UI fixes
- Grey out trend ranges beyond available health history - Reject implausibly fast (vehicle) activities on upload with feedback - Add cancel button + cooperative cancellation for Garmin sync - Show daily steps prominently on the dashboard - Clear errors for malformed/empty upload ZIPs - Snap-target dot when drawing a segment on the map - Time-axis fallback for stationary/HIIT HR timelines; hide map when no GPS - Parse and display moving time (timer) vs elapsed; backfill task - Restyle SegmentsPanel like RouteLeaderboard; Laps/Routes/Segments on one row Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -115,14 +115,21 @@ async def upload_garmin_export(
|
||||
extract_dir = dest_dir / f"garmin_{dest.stem}"
|
||||
|
||||
task_ids = []
|
||||
with zipfile.ZipFile(dest) as zf:
|
||||
extracted = _safe_extract(zf, extract_dir)
|
||||
try:
|
||||
with zipfile.ZipFile(dest) as zf:
|
||||
extracted = _safe_extract(zf, extract_dir)
|
||||
except zipfile.BadZipFile:
|
||||
dest.unlink(missing_ok=True)
|
||||
raise HTTPException(status_code=400, detail="Uploaded file is not a valid ZIP archive")
|
||||
|
||||
has_health = False
|
||||
for path in extracted:
|
||||
suffix = path.suffix.lower()
|
||||
if suffix == ".fit":
|
||||
task = process_activity_file.delay(str(path), current_user.id, "fit")
|
||||
task_ids.append(task.id)
|
||||
elif suffix == ".json":
|
||||
has_health = True # Garmin wellness data is exported as JSON files
|
||||
elif suffix == ".zip":
|
||||
# Garmin exports nest activity FIT files inside sub-zips
|
||||
# (e.g. DI-Connect-Uploaded-Files/UploadedFiles_*_Part*.zip)
|
||||
@@ -137,6 +144,12 @@ async def upload_garmin_export(
|
||||
task = process_activity_file.delay(str(np), current_user.id, "fit")
|
||||
task_ids.append(task.id)
|
||||
|
||||
if not task_ids and not has_health:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="No fitness data found in this archive — make sure you uploaded your full Garmin Connect export ZIP",
|
||||
)
|
||||
|
||||
# Queue health/wellness data extraction
|
||||
health_task = process_garmin_health_zip.delay(str(dest), current_user.id)
|
||||
|
||||
@@ -163,8 +176,12 @@ async def upload_strava_export(
|
||||
extract_dir = dest_dir / f"strava_{dest.stem}"
|
||||
|
||||
task_ids = []
|
||||
with zipfile.ZipFile(dest) as zf:
|
||||
extracted = _safe_extract(zf, extract_dir)
|
||||
try:
|
||||
with zipfile.ZipFile(dest) as zf:
|
||||
extracted = _safe_extract(zf, extract_dir)
|
||||
except zipfile.BadZipFile:
|
||||
dest.unlink(missing_ok=True)
|
||||
raise HTTPException(status_code=400, detail="Uploaded file is not a valid ZIP archive")
|
||||
|
||||
for path in extracted:
|
||||
suffix = path.suffix.lower()
|
||||
@@ -172,6 +189,12 @@ async def upload_strava_export(
|
||||
task = process_activity_file.delay(str(path), current_user.id, suffix[1:])
|
||||
task_ids.append(task.id)
|
||||
|
||||
if not task_ids:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="No activity files (.fit or .gpx) found in this Strava archive",
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "queued",
|
||||
"activity_tasks": len(task_ids),
|
||||
|
||||
Reference in New Issue
Block a user