145 lines
4.0 KiB
Python
Executable File
145 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
import argparse
|
||
import os
|
||
import sqlite3
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import requests
|
||
|
||
|
||
def find_project_root():
|
||
direnv_file = os.environ.get("DIRENV_FILE")
|
||
if direnv_file:
|
||
return Path(direnv_file).parent.resolve()
|
||
|
||
direnv_dir = os.environ.get("DIRENV_DIR")
|
||
if direnv_dir:
|
||
return Path(direnv_dir).resolve()
|
||
|
||
cwd = Path.cwd().resolve()
|
||
for parent in [cwd] + list(cwd.parents):
|
||
if (parent / "flake.nix").exists():
|
||
return parent
|
||
|
||
raise RuntimeError(
|
||
"Cannot find project root. Set DIRENV_FILE or DIRENV_DIR, "
|
||
"or run from within the project tree."
|
||
)
|
||
|
||
|
||
def fetch_json(api_root, path):
|
||
url = f"{api_root}{path}"
|
||
resp = requests.get(url, timeout=30)
|
||
resp.raise_for_status()
|
||
return resp.json()
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(
|
||
description="Populate a local SQLite DB from production data"
|
||
)
|
||
parser.add_argument(
|
||
"--url",
|
||
default=os.environ.get("PHENIX_URL", "https://ballephenix.com"),
|
||
help="Source API root (default: https://ballephenix.com)",
|
||
)
|
||
parser.add_argument(
|
||
"--db",
|
||
default=None,
|
||
help="Path to SQLite DB (default: <project-root>/phenix.db)",
|
||
)
|
||
parser.add_argument(
|
||
"--force",
|
||
action="store_true",
|
||
help="Clear existing games before inserting",
|
||
)
|
||
args = parser.parse_args()
|
||
|
||
api_root = args.url.rstrip("/")
|
||
|
||
project_root = find_project_root()
|
||
db_path = Path(args.db) if args.db else project_root / "phenix.db"
|
||
|
||
years = fetch_json(api_root, "/api/available-years.json")
|
||
print(f"Years on source: {len(years)} ({min(years)}–{max(years)})")
|
||
|
||
all_games = []
|
||
for year in years:
|
||
games = fetch_json(api_root, f"/api/results/{year}.json")
|
||
all_games.extend(games)
|
||
print(f"Total games fetched: {len(all_games)}")
|
||
|
||
conn = sqlite3.connect(str(db_path))
|
||
cursor = conn.cursor()
|
||
|
||
cursor.execute("""
|
||
CREATE TABLE IF NOT EXISTS games (
|
||
id TEXT PRIMARY KEY,
|
||
date TEXT NOT NULL,
|
||
time TEXT NOT NULL,
|
||
home TEXT NOT NULL,
|
||
away TEXT NOT NULL,
|
||
year INTEGER NOT NULL,
|
||
home_score INTEGER,
|
||
away_score INTEGER,
|
||
practice INTEGER NOT NULL DEFAULT 0,
|
||
rain INTEGER NOT NULL DEFAULT 0,
|
||
UNIQUE(date, time)
|
||
)
|
||
""")
|
||
|
||
if args.force:
|
||
cursor.execute("DELETE FROM games")
|
||
print("Cleared existing games")
|
||
|
||
inserted = 0
|
||
skipped = 0
|
||
errors = 0
|
||
|
||
for g in all_games:
|
||
try:
|
||
home_score = int(g["homeScore"]) if g.get("homeScore") is not None else None
|
||
away_score = int(g["awayScore"]) if g.get("awayScore") is not None else None
|
||
except (ValueError, TypeError):
|
||
home_score = None
|
||
away_score = None
|
||
|
||
try:
|
||
cursor.execute(
|
||
"""
|
||
INSERT OR IGNORE INTO games
|
||
(id, date, time, home, away, year, home_score, away_score, practice, rain)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||
""",
|
||
(
|
||
g.get("id", ""),
|
||
g["date"],
|
||
g.get("time", ""),
|
||
g.get("home", ""),
|
||
g.get("away", ""),
|
||
g.get("year", 0),
|
||
home_score,
|
||
away_score,
|
||
1 if g.get("practice") else 0,
|
||
1 if g.get("rain") else 0,
|
||
),
|
||
)
|
||
if cursor.rowcount > 0:
|
||
inserted += 1
|
||
else:
|
||
skipped += 1
|
||
except sqlite3.Error as e:
|
||
print(f"DB error on {g.get('date')} {g.get('time')}: {e}", file=sys.stderr)
|
||
errors += 1
|
||
|
||
conn.commit()
|
||
conn.close()
|
||
|
||
print(f"Inserted: {inserted}, skipped: {skipped}, errors: {errors}")
|
||
print(f"Database: {db_path}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|