fix: personal records only count watch-recorded FIT activities; exclude phone/Strava GPX/TCX imports (bad GPS teleporting in old activities)
Build and push images / validate (push) Successful in 3s
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 5s

This commit is contained in:
2026-06-21 19:51:32 +01:00
parent e2870e86a8
commit 156ce60aa0
+16 -2
View File
@@ -530,6 +530,13 @@ def detect_route(activity_id: int, user_id: int):
return {"status": "no_match"}
# Personal records are only computed from device-recorded FIT files (Garmin and
# other GPS watches/head units). Phone- and Strava-sourced GPX/TCX imports —
# especially older ones — suffer GPS "teleporting" that fabricates impossibly
# fast splits, so they're excluded from PRs entirely.
PR_ELIGIBLE_SOURCE_TYPES = {"fit"}
@celery_app.task(name="compute_personal_records")
def compute_personal_records(activity_id: int, user_id: int, parsed: dict):
"""Calculate personal records for standard distances from this activity."""
@@ -539,6 +546,10 @@ def compute_personal_records(activity_id: int, user_id: int, parsed: dict):
from sqlalchemy import select
from datetime import datetime, timezone
# Only FIT (watch-recorded) activities are trusted for PRs — see note above.
if parsed.get("source_type") not in PR_ELIGIBLE_SOURCE_TYPES:
return {"status": "skipped_non_watch", "activity_id": activity_id}
data_points = parsed.get("data_points", [])
total_dist = parsed.get("distance_m", 0) or 0
sport = parsed.get("sport_type", "running")
@@ -1188,8 +1199,9 @@ def backfill_indoor_distances(user_id: int):
@celery_app.task(name="recompute_personal_records_all")
def recompute_personal_records_all(user_id: int):
"""Wipe and rebuild all personal records from stored activity data, excluding
indoor (no-GPS) runs whose distance is unreliable. Fixes records polluted by
treadmill over-measurement and any duplicate current-record rows."""
indoor (no-GPS) runs and non-watch (GPX/TCX) imports whose distance is
unreliable. Fixes records polluted by treadmill over-measurement, GPS
teleporting in old phone/Strava imports, and any duplicate current-record rows."""
from app.services.route_matcher import compute_best_splits, STANDARD_DISTANCES
from app.core.database import SyncSessionLocal
from app.models.user import Activity, ActivityDataPoint, PersonalRecord
@@ -1209,6 +1221,8 @@ def recompute_personal_records_all(user_id: int):
best = {} # (sport, dist_m) -> {dur, aid, at, label}
for a in acts:
if a.source_type not in PR_ELIGIBLE_SOURCE_TYPES:
continue # only watch-recorded FIT files count toward PRs
if a.sport_type == "running" and a.polyline is None:
continue # indoor/treadmill — unreliable distance
rows = db.execute(