113 lines
3.7 KiB
Python
113 lines
3.7 KiB
Python
"""Backfill tss_tournaments.db from tournament ids already in match_summary.
|
|
|
|
Usage:
|
|
python TSSBOT/scripts/backfill_tournaments.py [--dry-run] [--limit N] [--sleep SECONDS]
|
|
"""
|
|
import argparse
|
|
import asyncio
|
|
import pathlib
|
|
import sqlite3
|
|
import sys
|
|
import time
|
|
from typing import List, Optional, Tuple
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
sys.path.insert(0, str(ROOT.parent / "SHARED"))
|
|
|
|
# Match start_bot.py/tss_ws.py: standalone scripts need to load TSSBOT/.env
|
|
# before importing storage, because storage resolves STORAGE_VOL_PATH at import.
|
|
try:
|
|
from dotenv import load_dotenv
|
|
load_dotenv(dotenv_path=ROOT / ".env")
|
|
except Exception:
|
|
pass
|
|
|
|
from BOT.storage import TSS_BATTLES_DB_PATH # noqa: E402
|
|
from BOT.tss_tournaments import ( # noqa: E402
|
|
TSS_TOURNAMENTS_DB_PATH,
|
|
build_scan_sync,
|
|
init_tss_tournaments_db,
|
|
store_scan,
|
|
)
|
|
|
|
|
|
async def tournament_ids(limit: Optional[int]) -> List[Tuple[int, Optional[str]]]:
|
|
sql = """
|
|
SELECT tournament_id, NULLIF(MAX(tournament_name), '') AS tournament_name
|
|
FROM match_summary
|
|
WHERE tournament_id IS NOT NULL AND tournament_id > 0
|
|
GROUP BY tournament_id
|
|
ORDER BY MAX(endtime_unix) DESC
|
|
"""
|
|
if limit:
|
|
sql += " LIMIT ?"
|
|
params = (limit,)
|
|
else:
|
|
params = ()
|
|
|
|
with sqlite3.connect(TSS_BATTLES_DB_PATH) as conn:
|
|
rows = conn.execute(sql, params).fetchall()
|
|
return [(int(row[0]), row[1]) for row in rows]
|
|
|
|
|
|
def scanned_tournament_ids() -> set[int]:
|
|
if not TSS_TOURNAMENTS_DB_PATH.exists():
|
|
return set()
|
|
with sqlite3.connect(TSS_TOURNAMENTS_DB_PATH) as conn:
|
|
try:
|
|
rows = conn.execute(
|
|
"SELECT tournament_id FROM tournaments WHERE scanned_unix IS NOT NULL"
|
|
).fetchall()
|
|
except sqlite3.OperationalError:
|
|
return set()
|
|
return {int(row[0]) for row in rows}
|
|
|
|
|
|
async def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--dry-run", action="store_true")
|
|
parser.add_argument("--limit", type=int, default=None)
|
|
parser.add_argument("--sleep", type=float, default=1.0)
|
|
parser.add_argument("--battle-workers", type=int, default=8)
|
|
parser.add_argument("--rescan", action="store_true", help="rescan tournaments already present in tss_tournaments.db")
|
|
args = parser.parse_args()
|
|
|
|
rows = await tournament_ids(args.limit)
|
|
print(f"Found {len(rows)} tournament ids in {TSS_BATTLES_DB_PATH}")
|
|
await init_tss_tournaments_db()
|
|
if not args.rescan:
|
|
done = scanned_tournament_ids()
|
|
if done:
|
|
before = len(rows)
|
|
rows = [(tid, name) for tid, name in rows if tid not in done]
|
|
print(f"Skipping {before - len(rows)} already-scanned tournaments ({len(rows)} remaining)")
|
|
if args.dry_run:
|
|
for tid, name in rows:
|
|
print(f" {tid}: {name or 'Tournament ' + str(tid)}")
|
|
return
|
|
|
|
for index, (tid, name) in enumerate(rows, start=1):
|
|
started = time.monotonic()
|
|
print(f"[{index}/{len(rows)}] scanning tournament {tid}", flush=True)
|
|
scan = build_scan_sync(
|
|
tid,
|
|
fallback_name=name,
|
|
battle_workers=args.battle_workers,
|
|
progress=lambda msg, tid=tid: print(f" {tid}: {msg}", flush=True),
|
|
)
|
|
await store_scan(scan)
|
|
elapsed = time.monotonic() - started
|
|
print(
|
|
f" stored {tid}: {scan['match_count']} matches, "
|
|
f"{len(scan['battles'])} battles, {len(scan['standings'])} standings "
|
|
f"({scan['status']}) in {elapsed:.1f}s",
|
|
flush=True,
|
|
)
|
|
if args.sleep and index < len(rows):
|
|
await asyncio.sleep(args.sleep)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|