lets get this party starteddddd (#1287)
This commit is contained in:
+222
-1
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user