feat(cli): migrate build_season_schedule and compute_ratings to typer CLI

- add typer-based CLI to build_season_schedule.py for structured option handling
- refactor compute_ratings.py to remove argparse and support typer CLI
- improve typing and option descriptions in compute_ratings.py main function
- add .gitignore entry for __pycache__
- add requirements.txt with dependencies for the project
This commit is contained in:
2025-08-29 16:14:50 -05:00
parent 5cecc6e280
commit c541c3fc51
4 changed files with 76 additions and 69 deletions

View File

@@ -30,6 +30,7 @@ from urllib.parse import urlencode
import requests
from bs4 import BeautifulSoup
from dateutil import parser as dtp
import typer
# ----------------- logging -----------------
logging.basicConfig(
@@ -264,16 +265,14 @@ def fetch_game_time(game_id: str, session: requests.Session) -> Optional[str]:
return None
# ----------------- build & merge -----------------
def main():
ap = argparse.ArgumentParser(description="Build a deduped season schedule with IDs, winners/losers, runs, and times.")
ap.add_argument("--subseason", required=True, help="Subseason ID, e.g. 942425")
ap.add_argument("--teams", required=True, help="Path to teams.json (array with team_id, team_slug, instance_id, teamName)")
ap.add_argument("--out", default="season_schedule.csv", help="Output CSV path")
ap.add_argument("--fetch-time", action="store_true", help="Fetch game time from /game/show/<id>")
ap.add_argument("--sleep", type=float, default=0.35, help="Delay between requests (seconds)")
args = ap.parse_args()
by_instance, by_slug, by_norm = load_teams(args.teams)
def main(
subseason: str = typer.Option(..., help="Subseason ID, e.g. 942425"),
teams: str = typer.Option(..., help="Path to teams.json (array with team_id, team_slug, instance_id, teamName)"),
out: str = typer.Option("season_schedule.csv", help="Output CSV path"),
fetch_time: bool = typer.Option(False, help="Fetch game time from /game/show/<id>"),
sleep: float = typer.Option(0.35, help="Delay between requests (seconds)")
):
by_instance, by_slug, by_norm = load_teams(teams)
instance_ids = sorted(by_instance.keys())
session = requests.Session()
@@ -283,8 +282,8 @@ def main():
raw: List[dict] = []
for i, iid in enumerate(instance_ids, 1):
logging.info(f"[{i}/{len(instance_ids)}] Fetching schedule for instance {iid}")
raw.extend(parse_printable(iid, args.subseason, session=session))
time.sleep(args.sleep) # be polite
raw.extend(parse_printable(iid, subseason, session=session))
time.sleep(sleep) # be polite
def rec_from_instance(iid: str) -> Optional[TeamRec]:
return by_instance.get(iid)
@@ -407,7 +406,7 @@ def main():
# -------- NEW: fetch game start time from game page --------
time_local = ""
if args.fetch_time and game_id:
if fetch_time and game_id:
if game_id in time_cache:
tval = time_cache[game_id]
else:
@@ -415,8 +414,7 @@ def main():
tval = fetch_game_time(game_id, session=session)
time_cache[game_id] = tval
if tval is None:
# small backoff to be nice if many misses
time.sleep(min(args.sleep * 2, 1.0))
time.sleep(min(sleep * 2, 1.0))
if tval:
time_local = tval
@@ -452,13 +450,13 @@ def main():
"loser_slug","loser_instance","loser_id",
"location","status","game_id","source_urls",
]
with open(args.out, "w", newline="", encoding="utf-8") as f:
with open(out, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=fieldnames)
w.writeheader()
for r in out_rows:
w.writerow(r)
logging.info(f"Wrote {len(out_rows)} games → {args.out}")
logging.info(f"Wrote {len(out_rows)} games → {out}")
if __name__ == "__main__":
main()
typer.run(main)