Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/quarterback/viperball. Version of pre-1906 Gridiron Football based off a silly Reddit question viperball.xyz
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630"""Viperball Stats Export
Exports season and dynasty statistics to CSV files for external analysis.
Available exports: export_season_standings_csv(season, filepath) - One row per team: wins, losses, points, all sabermetric averages
export_season_game_log_csv(season, filepath) - One row per completed game: teams, scores, both teams' metrics
export_dynasty_standings_csv(dynasty, filepath) - One row per (year, team): all-time totals and per-season record
export_dynasty_awards_csv(dynasty, filepath) - One row per year: all team and individual award winners
export_injury_history_csv(dynasty, filepath) - One row per injury across all recorded seasons
export_development_history_csv(dynasty, filepath) - One row per notable development event across all offseasons
export_all_american_csv(dynasty, filepath) - One row per All-American selection across all seasons
Usage: from engine.export import export_season_standings_csv export_season_standings_csv(season, "output/season_2026_standings.csv")"""
from __future__ import annotations
import csvimport osfrom typing import TYPE_CHECKING
if TYPE_CHECKING: from engine.season import Season from engine.dynasty import Dynasty
def _ensure_dir(filepath: str): """Create parent directory if it doesn't exist.""" parent = os.path.dirname(filepath) if parent: os.makedirs(parent, exist_ok=True)
# ──────────────────────────────────────────────# SEASON EXPORTS# ──────────────────────────────────────────────
def export_season_standings_csv(season: "Season", filepath: str) -> str: """ Export end-of-season standings to CSV.
Columns: team, conference, wins, losses, games_played, win_pct, points_for, points_against, point_differential, conf_wins, conf_losses, conf_win_pct, team_rating, ppd, conversion_pct, lateral_pct, kick_rating, to_margin, avg_start, offense_style, defense_style """ _ensure_dir(filepath) fieldnames = [ "team", "conference", "wins", "losses", "games_played", "win_pct", "points_for", "points_against", "point_differential", "conf_wins", "conf_losses", "conf_win_pct", "team_rating", "ppd", "conversion_pct", "lateral_pct", "kick_rating", "to_margin", "avg_start", "offense_style", "defense_style", ]
rows = [] for team_name, record in season.standings.items(): rows.append({ "team": team_name, "conference": record.conference, "wins": record.wins, "losses": record.losses, "games_played": record.games_played, "win_pct": round(record.win_percentage, 4), "points_for": round(record.points_for, 1), "points_against": round(record.points_against, 1), "point_differential": round(record.point_differential, 2), "conf_wins": record.conf_wins, "conf_losses": record.conf_losses, "conf_win_pct": round(record.conf_win_percentage, 4), "team_rating": round(record.avg_team_rating, 2), "ppd": round(record.avg_ppd, 2), "conversion_pct": round(record.avg_conversion_pct, 2), "lateral_pct": round(record.avg_lateral_pct, 2), "kick_rating": round(record.avg_kicking, 2), "to_margin": round(record.avg_to_margin, 2), "avg_start": round(record.avg_start_position, 2), "offense_style": record.offense_style, "defense_style": record.defense_style, })
# Sort by win_pct descending rows.sort(key=lambda r: r["win_pct"], reverse=True)
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
def export_season_game_log_csv(season: "Season", filepath: str) -> str: """ Export every completed game in the season to CSV.
Columns: week, home_team, away_team, home_score, away_score, winner, is_conference_game, home_team_rating, home_lateral_pct, home_conv_pct, home_kick_rating, away_team_rating, away_lateral_pct, away_conv_pct, away_kick_rating """ _ensure_dir(filepath) fieldnames = [ "week", "home_team", "away_team", "home_score", "away_score", "winner", "margin", "is_conference_game", "home_team_rating", "home_lateral_pct", "home_avg_start", "home_conv_pct", "home_kick_rating", "away_team_rating", "away_lateral_pct", "away_avg_start", "away_conv_pct", "away_kick_rating", ]
rows = [] for game in season.schedule: if not game.completed: continue hs = game.home_score or 0 as_ = game.away_score or 0 winner = game.home_team if hs > as_ else game.away_team hm = game.home_metrics or {} am = game.away_metrics or {} rows.append({ "week": game.week, "home_team": game.home_team, "away_team": game.away_team, "home_score": round(hs, 1), "away_score": round(as_, 1), "winner": winner, "margin": round(abs(hs - as_), 1), "is_conference_game": int(game.is_conference_game), "home_team_rating": round(hm.get("team_rating", hm.get("opi", 0)), 2), "home_lateral_pct": round(hm.get("lateral_pct", hm.get("chaos_factor", 0)), 2), "home_avg_start": round(hm.get("avg_start", hm.get("territory_rating", 0)), 2), "home_conv_pct": round(hm.get("conversion_pct", hm.get("pressure_index", 0)), 2), "home_kick_rating": round(hm.get("kicking_efficiency", 0), 2), "away_team_rating": round(am.get("team_rating", am.get("opi", 0)), 2), "away_lateral_pct": round(am.get("lateral_pct", am.get("chaos_factor", 0)), 2), "away_avg_start": round(am.get("avg_start", am.get("territory_rating", 0)), 2), "away_conv_pct": round(am.get("conversion_pct", am.get("pressure_index", 0)), 2), "away_kick_rating": round(am.get("kicking_efficiency", 0), 2), })
rows.sort(key=lambda r: r["week"])
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
# ──────────────────────────────────────────────# DYNASTY EXPORTS# ──────────────────────────────────────────────
def export_dynasty_standings_csv(dynasty: "Dynasty", filepath: str) -> str: """ Export full dynasty season-by-season standings to CSV.
Columns: year, team, conference, wins, losses, win_pct, points_for, points_against, team_rating, champion, playoff """ _ensure_dir(filepath) fieldnames = [ "year", "team", "conference", "wins", "losses", "win_pct", "points_for", "points_against", "team_rating", "champion", "playoff", ]
rows = [] for year in sorted(dynasty.seasons.keys()): season = dynasty.seasons[year] for team_name, record in season.standings.items(): history = dynasty.team_histories.get(team_name) yr_record = history.season_records.get(year, {}) if history else {} rows.append({ "year": year, "team": team_name, "conference": record.conference, "wins": record.wins, "losses": record.losses, "win_pct": round(record.win_percentage, 4), "points_for": round(record.points_for, 1), "points_against": round(record.points_against, 1), "team_rating": round(record.avg_team_rating, 2), "champion": int(yr_record.get("champion", False)), "playoff": int(yr_record.get("playoff", False)), })
rows.sort(key=lambda r: (r["year"], -r["win_pct"]))
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
def export_dynasty_awards_csv(dynasty: "Dynasty", filepath: str) -> str: """ Export all team and individual award winners per year to CSV.
Columns: year, award_type, award_name, winner, team, position, overall_rating """ _ensure_dir(filepath) fieldnames = [ "year", "award_type", "award_name", "winner", "team", "position", "overall_rating" ]
rows = []
for year in sorted(dynasty.awards_history.keys()): awards = dynasty.awards_history[year]
# Team-level awards team_awards = [ ("team", "CVL Champion", awards.champion, awards.champion), ("team", "Best Record", awards.best_record, awards.best_record), ("team", "Highest Scoring", awards.highest_scoring, awards.highest_scoring), ("team", "Best Defense", awards.best_defense, awards.best_defense), ("team", "Top Team Rating", awards.highest_opi, awards.highest_opi), ("team", "Best Kicking", awards.best_kicking, awards.best_kicking), ] for award_type, award_name, winner, team in team_awards: rows.append({ "year": year, "award_type": award_type, "award_name": award_name, "winner": winner, "team": team, "position": "", "overall_rating": 0, })
# Individual awards from honors honors = dynasty.honors_history.get(year, {}) for a in honors.get("individual_awards", []): rows.append({ "year": year, "award_type": "individual", "award_name": a.get("award_name", ""), "winner": a.get("player_name", ""), "team": a.get("team_name", ""), "position": a.get("position", ""), "overall_rating": a.get("overall_rating", 0), })
# Team-level honors for award_name, winner in [ ("Coach of the Year", honors.get("coach_of_year", "")), ("Most Improved Program", honors.get("most_improved", "")), ]: if winner: rows.append({ "year": year, "award_type": "team_honor", "award_name": award_name, "winner": winner, "team": winner, "position": "", "overall_rating": 0, })
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
def export_injury_history_csv(dynasty: "Dynasty", filepath: str) -> str: """ Export all recorded injuries across all seasons to CSV.
Columns: year, team, player, position, tier, description, week_injured, weeks_out, season_ending """ _ensure_dir(filepath) fieldnames = [ "year", "team", "player", "position", "tier", "description", "week_injured", "weeks_out", "season_ending", ]
rows = [] for year in sorted(dynasty.injury_history.keys()): report = dynasty.injury_history[year] for team_name, injuries in report.items(): for inj in injuries: rows.append({ "year": year, "team": team_name, "player": inj.get("player_name", ""), "position": inj.get("position", ""), "tier": inj.get("tier", ""), "description": inj.get("description", ""), "week_injured": inj.get("week_injured", 0), "weeks_out": inj.get("weeks_out", 0), "season_ending": int(inj.get("is_season_ending", False)), })
rows.sort(key=lambda r: (r["year"], r["team"]))
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
def export_development_history_csv(dynasty: "Dynasty", filepath: str) -> str: """ Export notable player development events across all offseasons to CSV.
Columns: offseason_year, team, player, event_type, description """ _ensure_dir(filepath) fieldnames = ["offseason_year", "team", "player", "event_type", "description"]
rows = [] for year in sorted(dynasty.development_history.keys()): for ev in dynasty.development_history[year]: rows.append({ "offseason_year": year, "team": ev.get("team", ""), "player": ev.get("player", ""), "event_type": ev.get("event_type", ""), "description": ev.get("description", ""), })
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
def export_all_american_csv(dynasty: "Dynasty", filepath: str) -> str: """ Export All-CVL selections (1st, 2nd, 3rd, HM, Freshman) across all seasons to CSV.
Columns: year, team_level, position_slot, player, year_in_school, team, position, overall_rating """ _ensure_dir(filepath) fieldnames = [ "year", "team_level", "position_slot", "player", "year_in_school", "team", "position", "overall_rating", ]
_tiers = [ ("all_american_first", "1st Team"), ("all_american_second", "2nd Team"), ("all_american_third", "3rd Team"), ("honorable_mention", "Honorable Mention"), ("all_freshman", "All-Freshman"), ]
rows = [] for year in sorted(dynasty.honors_history.keys()): honors = dynasty.honors_history[year] for team_key, level_label in _tiers: team_data = honors.get(team_key) or {} for slot in team_data.get("slots", []): rows.append({ "year": year, "team_level": level_label, "position_slot": slot.get("award_name", ""), "player": slot.get("player_name", ""), "year_in_school": slot.get("year_in_school", ""), "team": slot.get("team_name", ""), "position": slot.get("position", ""), "overall_rating": slot.get("overall_rating", 0), })
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
def export_all_conference_csv(dynasty: "Dynasty", filepath: str) -> str: """ Export All-Conference selections (1st and 2nd team) across all seasons to CSV.
Columns: year, conference, team_level, position_slot, player, year_in_school, team, position, overall_rating """ _ensure_dir(filepath) fieldnames = [ "year", "conference", "team_level", "position_slot", "player", "year_in_school", "team", "position", "overall_rating", ]
rows = [] for year in sorted(dynasty.honors_history.keys()): honors = dynasty.honors_history[year] for conf_name, tiers in (honors.get("all_conference_teams") or {}).items(): # tiers is {"first": {...}, "second": {...}} for tier_key, tier_label in [("first", "1st Team"), ("second", "2nd Team")]: conf_data = tiers.get(tier_key) if isinstance(tiers, dict) else None if not conf_data: continue slots = conf_data.get("slots", []) if isinstance(conf_data, dict) else [] for slot in slots: rows.append({ "year": year, "conference": conf_name, "team_level": tier_label, "position_slot": slot.get("award_name", ""), "player": slot.get("player_name", ""), "year_in_school": slot.get("year_in_school", ""), "team": slot.get("team_name", ""), "position": slot.get("position", ""), "overall_rating": slot.get("overall_rating", 0), })
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
# ──────────────────────────────────────────────# CONVENIENCE: EXPORT EVERYTHING# ──────────────────────────────────────────────
def export_dynasty_full(dynasty: "Dynasty", output_dir: str) -> dict: """ Export all dynasty stats to a directory.
Creates: <output_dir>/standings.csv <output_dir>/awards.csv <output_dir>/injuries.csv <output_dir>/development.csv <output_dir>/all_american.csv <output_dir>/all_conference.csv
Returns dict of {export_name: filepath}. """ os.makedirs(output_dir, exist_ok=True) exported = {}
exported["standings"] = export_dynasty_standings_csv( dynasty, os.path.join(output_dir, "standings.csv") ) exported["awards"] = export_dynasty_awards_csv( dynasty, os.path.join(output_dir, "awards.csv") ) exported["injuries"] = export_injury_history_csv( dynasty, os.path.join(output_dir, "injuries.csv") ) exported["development"] = export_development_history_csv( dynasty, os.path.join(output_dir, "development.csv") ) exported["all_american"] = export_all_american_csv( dynasty, os.path.join(output_dir, "all_american.csv") ) exported["all_conference"] = export_all_conference_csv( dynasty, os.path.join(output_dir, "all_conference.csv") )
return exported
def export_dynasty_classes_csv(dynasty: "Dynasty", season, filepath: str) -> str: """ Export all CVL roster classes (Freshman through Senior) to CSV.
Year-agnostic — omits calendar year so the file can be imported into any WVL dynasty regardless of what year it is in.
Columns: class_year, team, conference, prestige, player, position, overall, potential, development, speed, stamina, agility, power, awareness, hands, kicking, kick_power, kick_accuracy, lateral_skill, tackling, height, weight, hometown, high_school """ _ensure_dir(filepath) fieldnames = [ "class_year", "team", "conference", "prestige", "player", "position", "overall", "potential", "development", "speed", "stamina", "agility", "power", "awareness", "hands", "kicking", "kick_power", "kick_accuracy", "lateral_skill", "tackling", "height", "weight", "hometown", "high_school", ]
from engine.player_card import player_to_card
rows = [] for class_year in ("Freshman", "Sophomore", "Junior", "Senior"): for team_name, team in season.teams.items(): conf = "" prestige = 50 if dynasty: conf = dynasty.get_team_conference(team_name) prestige = dynasty.team_prestige.get(team_name, 50) for player in team.players: py = getattr(player, "year", "") if py == class_year or (class_year == "Senior" and py == "Graduate"): card = player_to_card(player, team_name) rows.append({ "class_year": class_year, "team": team_name, "conference": conf, "prestige": prestige, "player": card.full_name, "position": card.position, "overall": card.overall, "potential": card.potential, "development": card.development, "speed": card.speed, "stamina": card.stamina, "agility": card.agility, "power": card.power, "awareness": card.awareness, "hands": card.hands, "kicking": card.kicking, "kick_power": card.kick_power, "kick_accuracy": card.kick_accuracy, "lateral_skill": card.lateral_skill, "tackling": card.tackling, "height": card.height, "weight": card.weight, "hometown": f"{card.hometown_city}, {card.hometown_state}", "high_school": card.high_school, })
rows.sort(key=lambda r: (-{"Senior": 4, "Junior": 3, "Sophomore": 2, "Freshman": 1}.get(r["class_year"], 0), -r["overall"]))
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
def export_hs_pipeline_csv(pipeline, filepath: str) -> str: """ Export high school recruiting pipeline to CSV.
Columns: grade, rank, name, position, scouted_stars, hometown, high_school, region, height, weight, position_rank, regional_rank, is_alpha, is_sleeper, breakout """ _ensure_dir(filepath) fieldnames = [ "grade", "rank", "name", "position", "scouted_stars", "hometown", "high_school", "region", "height", "weight", "position_rank", "regional_rank", "is_alpha", "is_sleeper", "breakout", ]
rows = [] for grade in ("12th", "11th", "10th", "9th"): for p in pipeline.classes.get(grade, []): rows.append({ "grade": p.grade, "rank": p.national_rank, "name": p.recruit.full_name, "position": p.recruit.position, "scouted_stars": p.scouted_stars, "hometown": p.recruit.hometown, "high_school": p.recruit.high_school, "region": p.recruit.region, "height": p.recruit.height, "weight": p.recruit.weight, "position_rank": p.position_rank, "regional_rank": p.regional_rank, "is_alpha": int(p.is_alpha), "is_sleeper": int(p.is_sleeper), "breakout": int(p.breakout), })
with open(filepath, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows)
return filepath
def export_season_full(season: "Season", output_dir: str) -> dict: """ Export all single-season stats to a directory.
Creates: <output_dir>/season_standings.csv <output_dir>/season_games.csv
Returns dict of {export_name: filepath}. """ os.makedirs(output_dir, exist_ok=True) return { "standings": export_season_standings_csv( season, os.path.join(output_dir, "season_standings.csv") ), "games": export_season_game_log_csv( season, os.path.join(output_dir, "season_games.csv") ), }