Files
TSSBOT/web/api/routes_tournaments.py
T
NotSoToothless 24335a2677 Auto merge dev → main (#1353)
* feat(gateway): hashed key store with grant + hot reload

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(gateway): channel registry + aiohttp app (keyed auth, whoami, per-channel ws/proxy)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(gateway): manage_keys CLI (add/list/revoke)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(gateway): retire srebot_external, run relay-gateway under PM2

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(gateway): point ecosystem + README at relay-gateway

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss): replay outbox producer for relay gateway

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss): forward processed games to relay outbox

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): db helpers, app skeleton, info endpoint, fixtures

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): player, games, history, search endpoints

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): live, match, scoreboard, matches-search, maps

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): filter-required leaderboards (players/vehicles/stats)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): tournament list/detail/standings/matches

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: wire tss upstream through gateway + tssbot-api PM2 app

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 03:38:20 -07:00

77 lines
2.8 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, HTTPException
from web.api import db
router = APIRouter()
def _nullify(row: dict, keys: tuple[str, ...]) -> dict:
for k in keys:
if row.get(k) == "":
row[k] = None
return row
@router.get("/api/tss/tournaments")
async def tournaments(status: str | None = None, game_mode: str | None = None,
cluster: str | None = None, limit: int = 100) -> dict:
clauses: list[str] = []
params: list = []
if status:
clauses.append("status = ?"); params.append(status)
if game_mode:
clauses.append("game_mode = ?"); params.append(game_mode)
if cluster:
clauses.append("cluster = ?"); params.append(cluster)
where = ("WHERE " + " AND ".join(clauses)) if clauses else ""
params.append(max(1, min(limit, 500)))
rows = await db.query(db.tournaments_path(),
f"SELECT * FROM tournaments {where} ORDER BY date_end DESC LIMIT ?", tuple(params))
return {"total": len(rows), "tournaments": rows}
async def _matches(tid: int) -> list[dict]:
rows = await db.query(db.tournaments_path(),
"SELECT * FROM tournament_matches WHERE tournament_id = ? ORDER BY round, position",
(tid,))
battles = await db.query(db.tournaments_path(),
"SELECT match_id, type_bracket, session_hex FROM tournament_battles WHERE tournament_id = ?",
(tid,))
by_match: dict[tuple, list[str]] = {}
for b in battles:
by_match.setdefault((b["match_id"], b["type_bracket"]), []).append(b["session_hex"])
out = []
for m in rows:
_nullify(m, ("round", "position"))
m["session_ids"] = by_match.get((m["match_id"], m["type_bracket"]), [])
out.append(m)
return out
@router.get("/api/tss/tournament/{tid}")
async def tournament(tid: int) -> dict:
meta = await db.query_one(db.tournaments_path(),
"SELECT * FROM tournaments WHERE tournament_id = ?", (tid,))
if not meta:
raise HTTPException(status_code=404, detail=f"tournament {tid} not found")
standings = await db.query(db.tournaments_path(),
"SELECT * FROM tournament_standings WHERE tournament_id = ? ORDER BY group_index, rank",
(tid,))
return {"tournament": meta, "standings": standings, "matches": await _matches(tid)}
@router.get("/api/tss/tournament/{tid}/standings")
async def standings(tid: int) -> dict:
rows = await db.query(db.tournaments_path(),
"SELECT * FROM tournament_standings WHERE tournament_id = ? ORDER BY group_index, rank",
(tid,))
return {"tournament_id": tid, "standings": rows}
@router.get("/api/tss/tournament/{tid}/matches")
async def tournament_matches(tid: int) -> dict:
rows = await _matches(tid)
return {"tournament_id": tid, "total": len(rows), "matches": rows}