from __future__ import annotations from fastapi import APIRouter, HTTPException from web.api import db router = APIRouter() def _window(start_date, end_date, tournament_id): """Return (where_sql, params) or raise 400 if no filter given.""" clauses: list[str] = [] params: list = [] if tournament_id is not None: clauses.append("p.session_id IN (SELECT session_id FROM match_summary WHERE tournament_id = ?)") params.append(tournament_id) if start_date is not None: clauses.append("p.endtime_unix >= ?"); params.append(start_date) if end_date is not None: clauses.append("p.endtime_unix <= ?"); params.append(end_date) if not clauses: raise HTTPException(status_code=400, detail={ "error": "a date window (start_date/end_date) or tournament_id is required", "code": "FILTER_REQUIRED"}) return "WHERE " + " AND ".join(clauses), params @router.get("/api/tss/leaderboard/players") async def leaderboard_players(start_date: int | None = None, end_date: int | None = None, tournament_id: int | None = None, limit: int = 100) -> dict: where, params = _window(start_date, end_date, tournament_id) params.append(max(1, min(limit, 500))) rows = await db.query(db.battles_path(), f""" SELECT p.UID uid, MAX(p.nick) nick, COUNT(*) battles, SUM(CASE WHEN p.victor_bool='Win' THEN 1 ELSE 0 END) wins, SUM(p.air_kills+p.ground_kills) kills, SUM(p.deaths) deaths, SUM(p.score) score FROM player_games_hist p {where} GROUP BY p.UID ORDER BY score DESC LIMIT ?""", tuple(params)) for r in rows: r["uid"] = str(r["uid"]) return {"players": rows} @router.get("/api/tss/leaderboard/vehicles") async def leaderboard_vehicles(start_date: int | None = None, end_date: int | None = None, tournament_id: int | None = None, limit: int = 100) -> dict: where, params = _window(start_date, end_date, tournament_id) params.append(max(1, min(limit, 500))) rows = await db.query(db.battles_path(), f""" SELECT p.vehicle_internal, MAX(p.vehicle) vehicle, COUNT(*) battles, SUM(p.air_kills+p.ground_kills) kills, SUM(p.deaths) deaths, SUM(p.score) score FROM player_games_hist p {where} GROUP BY p.vehicle_internal ORDER BY battles DESC LIMIT ?""", tuple(params)) return {"vehicles": rows} @router.get("/api/tss/leaderboard/stats") async def leaderboard_stats(start_date: int | None = None, end_date: int | None = None, tournament_id: int | None = None) -> dict: where, params = _window(start_date, end_date, tournament_id) totals = await db.query_one(db.battles_path(), f""" SELECT COUNT(*) battles, COUNT(DISTINCT p.UID) players, SUM(p.air_kills+p.ground_kills) kills, SUM(p.score) score FROM player_games_hist p {where}""", tuple(params)) top = await db.query(db.battles_path(), f""" SELECT p.vehicle_internal, MAX(p.vehicle) vehicle, COUNT(*) battles FROM player_games_hist p {where} GROUP BY p.vehicle_internal ORDER BY battles DESC LIMIT 10""", tuple(params)) return {"totals": totals or {}, "top_vehicles": top}