Fix activity dedup crash: MultipleResultsFound on same-day same-sport activities
Build and push images / validate (push) Successful in 18s
Build and push images / build-backend (push) Successful in 31s
Build and push images / build-worker (push) Successful in 30s
Build and push images / build-frontend (push) Successful in 30s

The previous query used >= on start_time with no upper bound, so it matched
ALL activities of that sport type starting after the given minute on that day —
crashing with MultipleResultsFound whenever two such activities existed.

Fix: bound the window to ±60s from start_time and use .scalars().first()
so the query returns at most one Activity rather than raising on duplicates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 12:11:55 +01:00
parent f5d91cf8ae
commit 2d94f99356
+5 -4
View File
@@ -82,15 +82,16 @@ def process_activity_file(self, file_path: str, user_id: int, source_type: str,
with SyncSessionLocal() as db: with SyncSessionLocal() as db:
start_time = datetime.fromisoformat(parsed["start_time"]) start_time = datetime.fromisoformat(parsed["start_time"])
# Deduplicate by (user_id, sport_type, start_time date + within 60s) # Deduplicate: same user + sport_type + start_time within ±60s
from datetime import timedelta
existing = db.execute( existing = db.execute(
select(Activity).where( select(Activity).where(
Activity.user_id == user_id, Activity.user_id == user_id,
Activity.sport_type == parsed["sport_type"], Activity.sport_type == parsed["sport_type"],
func.date(Activity.start_time) == start_time.date(), Activity.start_time >= start_time - timedelta(seconds=60),
Activity.start_time >= start_time.replace(second=0, microsecond=0), Activity.start_time <= start_time + timedelta(seconds=60),
) )
).scalar_one_or_none() ).scalars().first()
if existing: if existing:
# Stamp garmin_activity_id if this came from a Garmin Connect sync # Stamp garmin_activity_id if this came from a Garmin Connect sync