lets get this party starteddddd (#1287)

This commit is contained in:
NotSoToothless
2026-05-30 08:45:32 -07:00
committed by GitHub
parent bd3871ef20
commit 7edc0202f4
8 changed files with 892 additions and 2 deletions
+222 -1
View File
@@ -19,7 +19,7 @@ import logging
import os
import time
from pathlib import Path
from typing import Any, Dict
from typing import Any, Dict, List, Optional
import aiosqlite
@@ -396,3 +396,224 @@ async def insert_player_games(game: Dict[str, Any]) -> None:
rows,
)
await conn.commit()
# ---------------------------------------------------------------------------
# Team resolve / lookup (python twin of the Rust backend's find_team)
# ---------------------------------------------------------------------------
async def resolve_team(name_or_id: str) -> Optional[Dict[str, Any]]:
"""Resolve a team by numeric id, or by tag/short/long name (case-insensitive).
Returns ``{team_id, long_name, short_name, tag_name}`` or None.
Tag matches rank above short, then long name.
"""
text = (name_or_id or "").strip()
if not text:
return None
try:
as_id: Optional[int] = int(text)
except ValueError:
as_id = None
async with aiosqlite.connect(TSS_TEAMS_DB_PATH) as conn:
conn.row_factory = aiosqlite.Row
async with conn.execute(
"""
SELECT team_id, long_name, short_name, tag_name
FROM teams_data
WHERE team_id = ?1
OR long_name = ?2 COLLATE NOCASE
OR short_name = ?2 COLLATE NOCASE
OR tag_name = ?2 COLLATE NOCASE
ORDER BY CASE
WHEN tag_name = ?2 COLLATE NOCASE THEN 0
WHEN short_name = ?2 COLLATE NOCASE THEN 1
WHEN long_name = ?2 COLLATE NOCASE THEN 2
ELSE 3
END
LIMIT 1
""",
(as_id, text),
) as cur:
row = await cur.fetchone()
return dict(row) if row else None
async def resolve_team_id_for_tag(tag: str) -> Optional[int]:
"""Return the team_id whose tag_name matches ``tag`` (case-insensitive)."""
tag = (tag or "").strip()
if not tag:
return None
async with aiosqlite.connect(TSS_TEAMS_DB_PATH) as conn:
async with conn.execute(
"SELECT team_id FROM teams_data WHERE tag_name = ? COLLATE NOCASE LIMIT 1",
(tag,),
) as cur:
row = await cur.fetchone()
return int(row[0]) if row else None
async def search_teams(query: str, limit: int = 25) -> List[Dict[str, Any]]:
"""Autocomplete-friendly team search. Empty query → top teams by rating."""
q = (query or "").strip()
async with aiosqlite.connect(TSS_TEAMS_DB_PATH) as conn:
conn.row_factory = aiosqlite.Row
if not q:
async with conn.execute(
"""
SELECT team_id, long_name, tag_name FROM teams_data
ORDER BY clanrating DESC NULLS LAST, members DESC
LIMIT ?
""",
(limit,),
) as cur:
rows = await cur.fetchall()
else:
like = f"%{q}%"
async with conn.execute(
"""
SELECT team_id, long_name, tag_name FROM teams_data
WHERE long_name LIKE ?1 COLLATE NOCASE
OR tag_name LIKE ?1 COLLATE NOCASE
ORDER BY CASE
WHEN tag_name = ?2 COLLATE NOCASE THEN 0
WHEN long_name = ?2 COLLATE NOCASE THEN 1
ELSE 2
END
LIMIT ?3
""",
(like, q, limit),
) as cur:
rows = await cur.fetchall()
return [dict(r) for r in rows]
# ---------------------------------------------------------------------------
# Player resolve / aggregate (derived from player_games_hist)
# ---------------------------------------------------------------------------
async def search_players(query: str, limit: int = 25) -> List[Dict[str, Any]]:
"""Autocomplete-friendly player search by nick. Returns [{uid, nick}]."""
q = (query or "").strip()
if len(q) < 2:
return []
async with aiosqlite.connect(TSS_BATTLES_DB_PATH) as conn:
conn.row_factory = aiosqlite.Row
await conn.create_function("ulower", 1, str.lower)
async with conn.execute(
"""
SELECT nick, UID FROM (
SELECT nick, UID, MAX(endtime_unix) AS last_seen
FROM player_games_hist
WHERE nick LIKE ? COLLATE NOCASE
GROUP BY UID
ORDER BY
CASE WHEN ulower(nick) = ulower(?) THEN 0
WHEN ulower(nick) LIKE ulower(?) THEN 1
ELSE 2 END,
last_seen DESC
LIMIT ?
)
""",
(f"{q}%", q, f"{q}%", limit),
) as cur:
rows = await cur.fetchall()
return [{"uid": r["UID"], "nick": r["nick"]} for r in rows]
async def resolve_players(name: str) -> List[Dict[str, Any]]:
"""Resolve a nick to candidates (exact match first, else substring).
Returns ``[{uid, nick}]`` grouped by UID.
"""
name = (name or "").strip()
if not name:
return []
async with aiosqlite.connect(TSS_BATTLES_DB_PATH) as conn:
conn.row_factory = aiosqlite.Row
async with conn.execute(
"SELECT UID, MIN(nick) AS nick FROM player_games_hist "
"WHERE nick = ? COLLATE NOCASE GROUP BY UID ORDER BY nick LIMIT 25",
(name,),
) as cur:
rows = list(await cur.fetchall())
if not rows:
async with conn.execute(
"SELECT UID, MIN(nick) AS nick FROM player_games_hist "
"WHERE nick LIKE ? COLLATE NOCASE GROUP BY UID ORDER BY nick LIMIT 25",
(f"%{name}%",),
) as cur:
rows = list(await cur.fetchall())
return [{"uid": r["UID"], "nick": r["nick"]} for r in rows]
async def latest_nick_for_uid(uid: str) -> str:
"""Best-effort latest nick for a UID; falls back to the UID string."""
async with aiosqlite.connect(TSS_BATTLES_DB_PATH) as conn:
async with conn.execute(
"SELECT nick FROM player_games_hist WHERE UID = ? ORDER BY endtime_unix DESC LIMIT 1",
(uid,),
) as cur:
row = await cur.fetchone()
return row[0] if row else str(uid)
async def player_career(uid: str) -> Optional[Dict[str, Any]]:
"""Aggregate career stats for a UID.
Player totals (kills/deaths/...) are Spectra per-player totals duplicated
across that player's vehicle rows, so we collapse to one value per session
(MAX) before summing — see the module docstring.
"""
async with aiosqlite.connect(TSS_BATTLES_DB_PATH) as conn:
conn.row_factory = aiosqlite.Row
async with conn.execute(
"""
SELECT
COUNT(*) AS battles,
SUM(CASE WHEN UPPER(victor_bool) = 'WIN' THEN 1 ELSE 0 END) AS wins,
SUM(CASE WHEN UPPER(victor_bool) = 'LOSS' THEN 1 ELSE 0 END) AS losses,
SUM(gk) AS ground_kills, SUM(ak) AS air_kills,
SUM(asi) AS assists, SUM(cap) AS captures, SUM(de) AS deaths
FROM (
SELECT session_id,
MAX(victor_bool) AS victor_bool,
MAX(ground_kills) AS gk, MAX(air_kills) AS ak,
MAX(assists) AS asi, MAX(captures) AS cap, MAX(deaths) AS de
FROM player_games_hist
WHERE UID = ?
GROUP BY session_id
)
""",
(uid,),
) as cur:
row = await cur.fetchone()
if not row or not row["battles"]:
return None
career = dict(row)
career["nick"] = await latest_nick_for_uid(uid)
career["uid"] = str(uid)
return career
async def player_teams(uid: str) -> List[Dict[str, Any]]:
"""Teams a UID has appeared with, most recent first."""
async with aiosqlite.connect(TSS_BATTLES_DB_PATH) as conn:
conn.row_factory = aiosqlite.Row
async with conn.execute(
"""
SELECT team_tag,
MAX(team_name) AS team_name,
team_id,
COUNT(DISTINCT session_id) AS games,
MAX(endtime_unix) AS last_seen
FROM player_games_hist
WHERE UID = ?
GROUP BY team_tag
ORDER BY last_seen DESC
""",
(uid,),
) as cur:
rows = await cur.fetchall()
return [dict(r) for r in rows]