""" Beregn split-tider og totalresultat fra passeringsloggen. """ from datetime import datetime from typing import Optional import aiosqlite async def get_results(db: aiosqlite.Connection, race_id: Optional[str] = None) -> list[dict]: """ Hent totalresultat for alle utøvere som har passert start og mål. Returnerer sortert liste med split-tider. """ race_filter = "AND p.race_id = ?" if race_id else "" params = [race_id] if race_id else [] async with db.execute(f""" SELECT p.profile_id, p.bib_number, a.name, a.club, p.station, p.timestamp_utc FROM passages p LEFT JOIN athletes a ON a.profile_id = p.profile_id WHERE p.needs_review = 0 AND p.profile_id IS NOT NULL {race_filter} ORDER BY p.profile_id, p.timestamp_utc """, params) as cur: rows = await cur.fetchall() # Grupper per utøver athletes: dict[str, dict] = {} for row in rows: pid = row["profile_id"] if pid not in athletes: athletes[pid] = { "profile_id": pid, "bib_number": row["bib_number"], "name": row["name"], "club": row["club"], "passages": [], } athletes[pid]["passages"].append({ "station": row["station"], "timestamp_utc": row["timestamp_utc"], }) results = [] for pid, data in athletes.items(): passages = data["passages"] if not passages: continue # Finn start og mål start_p = next((p for p in passages if p["station"] == "start"), None) finish_p = next((p for p in passages if p["station"] == "finish"), None) start_time = _parse_ts(start_p["timestamp_utc"]) if start_p else None finish_time = _parse_ts(finish_p["timestamp_utc"]) if finish_p else None total_seconds = None if start_time and finish_time: total_seconds = (finish_time - start_time).total_seconds() # Split-tider mellom alle stasjoner splits = [] prev_ts = start_time prev_station = "start" for p in passages: if p["station"] == "start": continue ts = _parse_ts(p["timestamp_utc"]) split_s = (ts - prev_ts).total_seconds() if prev_ts else None splits.append({ "from": prev_station, "to": p["station"], "split_seconds": split_s, "split_formatted": _fmt_seconds(split_s), "timestamp_utc": p["timestamp_utc"], }) prev_ts = ts prev_station = p["station"] results.append({ "profile_id": pid, "bib_number": data["bib_number"], "name": data["name"] or f"Ukjent ({data['bib_number']})", "club": data["club"], "start_time": start_p["timestamp_utc"] if start_p else None, "finish_time": finish_p["timestamp_utc"] if finish_p else None, "total_seconds": total_seconds, "total_formatted": _fmt_seconds(total_seconds), "splits": splits, "dnf": finish_time is None and start_time is not None, "dns": start_time is None, }) # Sorter: fullført (etter tid), DNF, DNS results.sort(key=_sort_key) return results def _parse_ts(ts_str: str) -> Optional[datetime]: if not ts_str: return None try: return datetime.fromisoformat(ts_str) except ValueError: return None def _fmt_seconds(seconds: Optional[float]) -> Optional[str]: if seconds is None: return None seconds = int(seconds) h = seconds // 3600 m = (seconds % 3600) // 60 s = seconds % 60 if h: return f"{h}:{m:02d}:{s:02d}" return f"{m}:{s:02d}" def _sort_key(r: dict): if r["dns"]: return (3, 0) if r["dnf"]: return (2, 0) return (1, r["total_seconds"] or float("inf"))