fix lap-bests distance matching + show PRs on activity page
This commit is contained in:
@@ -216,20 +216,76 @@ async def get_lap_bests(
|
||||
if not act.named_route_id:
|
||||
return {}
|
||||
|
||||
# Best per lap number across OTHER activities on the same route, so the
|
||||
# comparison is meaningful (excluding this activity from its own benchmark).
|
||||
rows = (await db.execute(
|
||||
select(ActivityLap.lap_number, func.min(ActivityLap.duration_s))
|
||||
# This activity's laps, so we know each lap's distance.
|
||||
this_laps = (await db.execute(
|
||||
select(ActivityLap.lap_number, ActivityLap.distance_m)
|
||||
.where(ActivityLap.activity_id == activity_id)
|
||||
)).all()
|
||||
this_dist = {ln: d for ln, d in this_laps if d}
|
||||
|
||||
# Laps from OTHER activities on the same route, so the comparison excludes
|
||||
# this activity from its own benchmark.
|
||||
other_laps = (await db.execute(
|
||||
select(ActivityLap.lap_number, ActivityLap.distance_m, ActivityLap.duration_s)
|
||||
.join(Activity, Activity.id == ActivityLap.activity_id)
|
||||
.where(
|
||||
Activity.named_route_id == act.named_route_id,
|
||||
Activity.user_id == current_user.id,
|
||||
Activity.id != activity_id,
|
||||
ActivityLap.duration_s.isnot(None),
|
||||
ActivityLap.distance_m.isnot(None),
|
||||
)
|
||||
.group_by(ActivityLap.lap_number)
|
||||
)).all()
|
||||
return {str(lap_number): best for lap_number, best in rows}
|
||||
|
||||
# Best (fastest) time per lap number, comparing only laps that cover roughly
|
||||
# the same distance (±5%). Without this, a short/partial lap that happens to
|
||||
# share a lap number (e.g. a 461 m "lap 5" vs this activity's 1 km lap 5)
|
||||
# would win as "best" and produce a nonsensical benchmark.
|
||||
bests: dict[str, float] = {}
|
||||
for ln, dist, dur in other_laps:
|
||||
target = this_dist.get(ln)
|
||||
if target is None or abs(dist - target) > target * 0.05:
|
||||
continue
|
||||
key = str(ln)
|
||||
if key not in bests or dur < bests[key]:
|
||||
bests[key] = dur
|
||||
return bests
|
||||
|
||||
|
||||
@router.get("/{activity_id}/records")
|
||||
async def get_activity_records(
|
||||
activity_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Current personal records that were set in this activity (e.g. a 1 km PR),
|
||||
so the activity page can flag which records it holds."""
|
||||
act = (await db.execute(
|
||||
select(Activity.id).where(
|
||||
Activity.id == activity_id,
|
||||
Activity.user_id == current_user.id,
|
||||
)
|
||||
)).scalar_one_or_none()
|
||||
if not act:
|
||||
raise HTTPException(status_code=404, detail="Activity not found")
|
||||
|
||||
rows = (await db.execute(
|
||||
select(PersonalRecord)
|
||||
.where(
|
||||
PersonalRecord.user_id == current_user.id,
|
||||
PersonalRecord.activity_id == activity_id,
|
||||
PersonalRecord.is_current_record == True,
|
||||
)
|
||||
.order_by(PersonalRecord.distance_m)
|
||||
)).scalars().all()
|
||||
return [
|
||||
{
|
||||
"distance_label": r.distance_label,
|
||||
"distance_m": r.distance_m,
|
||||
"duration_s": r.duration_s,
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
|
||||
|
||||
@router.get("/{activity_id}/route-leaderboard")
|
||||
|
||||
Reference in New Issue
Block a user