From c451e02c525aa3d225304cb677dedac73d460a73 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Thu, 26 Mar 2026 23:15:07 -0500 Subject: [PATCH] fix: remove hardcoded fallback password from PostgreSQL connection Raise RuntimeError on startup if POSTGRES_PASSWORD env var is not set, instead of silently falling back to a known password in source code. Closes #C2 from postgres migration review. Co-Authored-By: Claude Opus 4.6 (1M context) --- app/db_engine.py | 1280 ++++++++++++++++++++++++++++------------------ 1 file changed, 788 insertions(+), 492 deletions(-) diff --git a/app/db_engine.py b/app/db_engine.py index c1979cf..7cc23e3 100644 --- a/app/db_engine.py +++ b/app/db_engine.py @@ -11,35 +11,38 @@ from peewee import ModelSelect from playhouse.shortcuts import model_to_dict # Database configuration - supports both SQLite and PostgreSQL -DATABASE_TYPE = os.environ.get('DATABASE_TYPE', 'sqlite') +DATABASE_TYPE = os.environ.get("DATABASE_TYPE", "sqlite") -if DATABASE_TYPE.lower() == 'postgresql': +if DATABASE_TYPE.lower() == "postgresql": from playhouse.pool import PooledPostgresqlDatabase + + _postgres_password = os.environ.get("POSTGRES_PASSWORD") + if _postgres_password is None: + raise RuntimeError( + "POSTGRES_PASSWORD environment variable is not set. " + "This variable is required when DATABASE_TYPE=postgresql." + ) db = PooledPostgresqlDatabase( - os.environ.get('POSTGRES_DB', 'sba_master'), - user=os.environ.get('POSTGRES_USER', 'sba_admin'), - password=os.environ.get('POSTGRES_PASSWORD', 'sba_dev_password_2024'), - host=os.environ.get('POSTGRES_HOST', 'sba_postgres'), - port=int(os.environ.get('POSTGRES_PORT', '5432')), + os.environ.get("POSTGRES_DB", "sba_master"), + user=os.environ.get("POSTGRES_USER", "sba_admin"), + password=_postgres_password, + host=os.environ.get("POSTGRES_HOST", "sba_postgres"), + port=int(os.environ.get("POSTGRES_PORT", "5432")), max_connections=20, stale_timeout=300, # 5 minutes timeout=0, autoconnect=True, - autorollback=True # Automatically rollback failed transactions + autorollback=True, # Automatically rollback failed transactions ) else: # Default SQLite configuration db = SqliteDatabase( - 'storage/sba_master.db', - pragmas={ - 'journal_mode': 'wal', - 'cache_size': -1 * 64000, - 'synchronous': 0 - } + "storage/sba_master.db", + pragmas={"journal_mode": "wal", "cache_size": -1 * 64000, "synchronous": 0}, ) -date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}' -logger = logging.getLogger('discord_app') +date = f"{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}" +logger = logging.getLogger("discord_app") """ @@ -51,7 +54,6 @@ Per season updates: """ - def model_csv_headers(this_obj, exclude=None) -> List: data = model_to_dict(this_obj, recurse=False, exclude=exclude) return [x for x in data.keys()] @@ -64,7 +66,7 @@ def model_to_csv(this_obj, exclude=None) -> List: def query_to_csv(all_items: ModelSelect, exclude=None): if all_items.count() == 0: - data_list = [['No data found']] + data_list = [["No data found"]] else: data_list = [model_csv_headers(all_items[0], exclude=exclude)] for x in all_items: @@ -75,29 +77,29 @@ def query_to_csv(all_items: ModelSelect, exclude=None): def complex_data_to_csv(complex_data: List): if len(complex_data) == 0: - data_list = [['No data found']] + data_list = [["No data found"]] else: data_list = [[x for x in complex_data[0].keys()]] for line in complex_data: - logger.info(f'line: {line}') + logger.info(f"line: {line}") this_row = [] for key in line: - logger.info(f'key: {key}') + logger.info(f"key: {key}") if line[key] is None: - this_row.append('') + this_row.append("") elif isinstance(line[key], dict): - if 'name' in line[key]: - this_row.append(line[key]['name']) - elif 'abbrev' in line[key]: - this_row.append(line[key]['abbrev']) + if "name" in line[key]: + this_row.append(line[key]["name"]) + elif "abbrev" in line[key]: + this_row.append(line[key]["abbrev"]) else: - this_row.append(line[key]['id']) + this_row.append(line[key]["id"]) elif isinstance(line[key], int) and line[key] > 100000000: this_row.append(f"'{line[key]}") - elif isinstance(line[key], str) and ',' in line[key]: + elif isinstance(line[key], str) and "," in line[key]: this_row.append(line[key].replace(",", "-_-")) else: @@ -108,37 +110,38 @@ def complex_data_to_csv(complex_data: List): return DataFrame(data_list).to_csv(header=False, index=False) -def per_season_weeks(season: int, s_type: Literal['regular', 'post', 'total']): +def per_season_weeks(season: int, s_type: Literal["regular", "post", "total"]): if season == 1: - if s_type == 'regular': - return {'start': 1, 'end': 20} - elif s_type == 'post': - return {'start': 21, 'end': 22} + if s_type == "regular": + return {"start": 1, "end": 20} + elif s_type == "post": + return {"start": 21, "end": 22} else: - return {'start': 1, 'end': 22} + return {"start": 1, "end": 22} elif season in [3, 4, 5, 6, 7]: - if s_type == 'regular': - return {'start': 1, 'end': 22} - elif s_type == 'post': - return {'start': 23, 'end': 25} + if s_type == "regular": + return {"start": 1, "end": 22} + elif s_type == "post": + return {"start": 23, "end": 25} else: - return {'start': 1, 'end': 25} + return {"start": 1, "end": 25} # Season 2, 8, and beyond else: - if s_type == 'regular': - return {'start': 1, 'end': 18} - elif s_type == 'post': - return {'start': 19, 'end': 21} + if s_type == "regular": + return {"start": 1, "end": 18} + elif s_type == "post": + return {"start": 19, "end": 21} else: - return {'start': 1, 'end': 21} + return {"start": 1, "end": 21} def win_pct(this_team_stan): if this_team_stan.wins + this_team_stan.losses == 0: return 0 else: - return (this_team_stan.wins / (this_team_stan.wins + this_team_stan.losses)) + \ - (this_team_stan.run_diff * .000001) + return (this_team_stan.wins / (this_team_stan.wins + this_team_stan.losses)) + ( + this_team_stan.run_diff * 0.000001 + ) def games_back(leader, chaser): @@ -183,19 +186,21 @@ class Division(BaseModel): season = IntegerField(default=0) def abbrev(self): - league_short = self.league_abbrev + ' ' if self.league_abbrev else '' - return f'{league_short}{self.division_abbrev}' + league_short = self.league_abbrev + " " if self.league_abbrev else "" + return f"{league_short}{self.division_abbrev}" def short_name(self): - league_short = self.league_abbrev + ' ' if self.league_abbrev else '' - return f'{league_short}{self.division_name}' + league_short = self.league_abbrev + " " if self.league_abbrev else "" + return f"{league_short}{self.division_name}" def full_name(self): - league_long = self.league_name + ' ' if self.league_name else '' - return f'{league_long}{self.division_name}' + league_long = self.league_name + " " if self.league_name else "" + return f"{league_long}{self.division_name}" def sort_division(self, season): - div_query = Standings.select_season(season).where(Standings.team.division == self) + div_query = Standings.select_season(season).where( + Standings.team.division == self + ) div_teams = [team_stan for team_stan in div_query] div_teams.sort(key=lambda team: win_pct(team), reverse=True) @@ -297,27 +302,40 @@ class Team(BaseModel): @staticmethod def get_season(name_or_abbrev, season): - team = Team.get_or_none(fn.Upper(Team.abbrev) == name_or_abbrev.upper(), Team.season == season) + team = Team.get_or_none( + fn.Upper(Team.abbrev) == name_or_abbrev.upper(), Team.season == season + ) if not team: - team = Team.get_or_none(fn.Lower(Team.sname) == name_or_abbrev.lower(), Team.season == season) + team = Team.get_or_none( + fn.Lower(Team.sname) == name_or_abbrev.lower(), Team.season == season + ) if not team: - team = Team.get_or_none(fn.Lower(Team.lname) == name_or_abbrev.lower(), Team.season == season) + team = Team.get_or_none( + fn.Lower(Team.lname) == name_or_abbrev.lower(), + Team.season == season, + ) return team def get_record(self, week): wins = Result.select_season(Current.latest().season).where( - (((Result.hometeam == self) & (Result.homescore > Result.awayscore)) | - ((Result.awayteam == self) & (Result.awayscore > Result.homescore))) & (Result.week <= week) + ( + ((Result.hometeam == self) & (Result.homescore > Result.awayscore)) + | ((Result.awayteam == self) & (Result.awayscore > Result.homescore)) + ) + & (Result.week <= week) ) losses = Result.select_season(Current.latest().season).where( - (((Result.awayteam == self) & (Result.homescore > Result.awayscore)) | - ((Result.hometeam == self) & (Result.awayscore > Result.homescore))) & (Result.week <= week) + ( + ((Result.awayteam == self) & (Result.homescore > Result.awayscore)) + | ((Result.hometeam == self) & (Result.awayscore > Result.homescore)) + ) + & (Result.week <= week) ) if wins.count() + losses.count() > 0: pct = wins.count() / (wins.count() + losses.count()) else: pct = 0 - return {'w': wins.count(), 'l': losses.count(), 'pct': pct} + return {"w": wins.count(), "l": losses.count(), "pct": pct} def get_gms(self): if self.gmid2: @@ -326,17 +344,35 @@ class Team(BaseModel): return [self.gmid] def get_this_week(self): - active_team = Player.select_season(self.season).where(Player.team == self).order_by(Player.wara) + active_team = ( + Player.select_season(self.season) + .where(Player.team == self) + .order_by(Player.wara) + ) - active_roster = {'C': 0, '1B': 0, '2B': 0, '3B': 0, 'SS': 0, 'LF': 0, 'CF': 0, 'RF': 0, 'DH': 0, - 'SP': 0, 'RP': 0, 'CP': 0, 'WARa': 0, 'players': []} + active_roster = { + "C": 0, + "1B": 0, + "2B": 0, + "3B": 0, + "SS": 0, + "LF": 0, + "CF": 0, + "RF": 0, + "DH": 0, + "SP": 0, + "RP": 0, + "CP": 0, + "WARa": 0, + "players": [], + } combo_pitchers = 0 for guy in active_team: - active_roster['WARa'] += guy.wara - active_roster['players'].append(guy) + active_roster["WARa"] += guy.wara + active_roster["players"].append(guy) guy_pos = guy.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: try: @@ -347,276 +383,374 @@ class Team(BaseModel): pass if combo_pitchers > 0: - if active_roster['SP'] < 5: - if 5 - active_roster['SP'] <= combo_pitchers: - delta = 5 - active_roster['SP'] + if active_roster["SP"] < 5: + if 5 - active_roster["SP"] <= combo_pitchers: + delta = 5 - active_roster["SP"] else: delta = combo_pitchers - active_roster['SP'] += delta + active_roster["SP"] += delta combo_pitchers -= delta if combo_pitchers > 0: - active_roster['RP'] += combo_pitchers + active_roster["RP"] += combo_pitchers - short_il = Player.select_season(self.season).join(Team).where(Player.team.abbrev == f'{self.abbrev}IL') + short_il = ( + Player.select_season(self.season) + .join(Team) + .where(Player.team.abbrev == f"{self.abbrev}IL") + ) - short_roster = {'C': 0, '1B': 0, '2B': 0, '3B': 0, 'SS': 0, 'LF': 0, 'CF': 0, 'RF': 0, 'DH': 0, - 'SP': 0, 'RP': 0, 'CP': 0, 'WARa': 0, 'players': []} + short_roster = { + "C": 0, + "1B": 0, + "2B": 0, + "3B": 0, + "SS": 0, + "LF": 0, + "CF": 0, + "RF": 0, + "DH": 0, + "SP": 0, + "RP": 0, + "CP": 0, + "WARa": 0, + "players": [], + } combo_pitchers = 0 for guy in short_il: - short_roster['WARa'] += guy.wara - short_roster['players'].append(guy) + short_roster["WARa"] += guy.wara + short_roster["players"].append(guy) guy_pos = guy.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: for pos in guy_pos: short_roster[pos] += 1 if combo_pitchers > 0: - if short_roster['SP'] < 5: - if 5 - short_roster['SP'] <= combo_pitchers: - delta = 5 - short_roster['SP'] + if short_roster["SP"] < 5: + if 5 - short_roster["SP"] <= combo_pitchers: + delta = 5 - short_roster["SP"] else: delta = combo_pitchers - short_roster['SP'] += delta + short_roster["SP"] += delta combo_pitchers -= delta if combo_pitchers > 0: - short_roster['RP'] += combo_pitchers + short_roster["RP"] += combo_pitchers - long_il = Player.select_season(self.season).join(Team).where(Player.team.abbrev == f'{self.abbrev}MiL') + long_il = ( + Player.select_season(self.season) + .join(Team) + .where(Player.team.abbrev == f"{self.abbrev}MiL") + ) - long_roster = {'C': 0, '1B': 0, '2B': 0, '3B': 0, 'SS': 0, 'LF': 0, 'CF': 0, 'RF': 0, 'DH': 0, - 'SP': 0, 'RP': 0, 'CP': 0, 'WARa': 0, 'players': []} + long_roster = { + "C": 0, + "1B": 0, + "2B": 0, + "3B": 0, + "SS": 0, + "LF": 0, + "CF": 0, + "RF": 0, + "DH": 0, + "SP": 0, + "RP": 0, + "CP": 0, + "WARa": 0, + "players": [], + } combo_pitchers = 0 for guy in long_il: - long_roster['WARa'] += guy.wara - long_roster['players'].append(guy) + long_roster["WARa"] += guy.wara + long_roster["players"].append(guy) guy_pos = guy.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: for pos in guy_pos: long_roster[pos] += 1 if combo_pitchers > 0: - if long_roster['SP'] < 5: - if 5 - long_roster['SP'] <= combo_pitchers: - delta = 5 - long_roster['SP'] + if long_roster["SP"] < 5: + if 5 - long_roster["SP"] <= combo_pitchers: + delta = 5 - long_roster["SP"] else: delta = combo_pitchers - long_roster['SP'] += delta + long_roster["SP"] += delta combo_pitchers -= delta if combo_pitchers > 0: - long_roster['RP'] += combo_pitchers + long_roster["RP"] += combo_pitchers - return {'active': active_roster, 'shortil': short_roster, 'longil': long_roster} + return {"active": active_roster, "shortil": short_roster, "longil": long_roster} def get_next_week(self): current = Current.latest() active_team = Player.select_season(current.season).where(Player.team == self) - active_roster = {'C': 0, '1B': 0, '2B': 0, '3B': 0, 'SS': 0, 'LF': 0, 'CF': 0, 'RF': 0, 'DH': 0, - 'SP': 0, 'RP': 0, 'CP': 0, 'WARa': 0, 'players': []} + active_roster = { + "C": 0, + "1B": 0, + "2B": 0, + "3B": 0, + "SS": 0, + "LF": 0, + "CF": 0, + "RF": 0, + "DH": 0, + "SP": 0, + "RP": 0, + "CP": 0, + "WARa": 0, + "players": [], + } combo_pitchers = 0 for guy in active_team: - active_roster['WARa'] += guy.wara - active_roster['players'].append(guy) + active_roster["WARa"] += guy.wara + active_roster["players"].append(guy) guy_pos = guy.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: for pos in guy_pos: active_roster[pos] += 1 all_drops = Transaction.select_season(Current.latest().season).where( - (Transaction.oldteam == self) & (Transaction.week == current.week + 1) & (Transaction.cancelled == 0) + (Transaction.oldteam == self) + & (Transaction.week == current.week + 1) + & (Transaction.cancelled == 0) ) all_adds = Transaction.select_season(Current.latest().season).where( - (Transaction.newteam == self) & (Transaction.week == current.week + 1) & (Transaction.cancelled == 0) + (Transaction.newteam == self) + & (Transaction.week == current.week + 1) + & (Transaction.cancelled == 0) ) for move in all_drops: guy_pos = move.player.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers -= 1 else: for pos in guy_pos: active_roster[pos] -= 1 # print(f'dropping {move.player.name} id ({move.player.get_id()}) for {move.player.wara} WARa') - active_roster['WARa'] -= move.player.wara + active_roster["WARa"] -= move.player.wara try: - active_roster['players'].remove(move.player) + active_roster["players"].remove(move.player) except Exception: - print(f'I could not drop {move.player.name}') + print(f"I could not drop {move.player.name}") for move in all_adds: guy_pos = move.player.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: for pos in guy_pos: active_roster[pos] += 1 # print(f'adding {move.player.name} id ({move.player.get_id()}) for {move.player.wara} WARa') - active_roster['WARa'] += move.player.wara - active_roster['players'].append(move.player) + active_roster["WARa"] += move.player.wara + active_roster["players"].append(move.player) if combo_pitchers > 0: - if active_roster['SP'] < 5: - if 5 - active_roster['SP'] <= combo_pitchers: - delta = 5 - active_roster['SP'] + if active_roster["SP"] < 5: + if 5 - active_roster["SP"] <= combo_pitchers: + delta = 5 - active_roster["SP"] else: delta = combo_pitchers - active_roster['SP'] += delta + active_roster["SP"] += delta combo_pitchers -= delta if combo_pitchers > 0: - active_roster['RP'] += combo_pitchers + active_roster["RP"] += combo_pitchers - short_il = Player.select_season(current.season).join(Team).where(Player.team.abbrev == f'{self.abbrev}SIL') + short_il = ( + Player.select_season(current.season) + .join(Team) + .where(Player.team.abbrev == f"{self.abbrev}SIL") + ) - short_roster = {'C': 0, '1B': 0, '2B': 0, '3B': 0, 'SS': 0, 'LF': 0, 'CF': 0, 'RF': 0, 'DH': 0, - 'SP': 0, 'RP': 0, 'CP': 0, 'WARa': 0, 'players': []} + short_roster = { + "C": 0, + "1B": 0, + "2B": 0, + "3B": 0, + "SS": 0, + "LF": 0, + "CF": 0, + "RF": 0, + "DH": 0, + "SP": 0, + "RP": 0, + "CP": 0, + "WARa": 0, + "players": [], + } combo_pitchers = 0 for guy in short_il: - short_roster['WARa'] += guy.wara - short_roster['players'].append(guy) + short_roster["WARa"] += guy.wara + short_roster["players"].append(guy) guy_pos = guy.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: for pos in guy_pos: short_roster[pos] += 1 - sil_team = Team.get_season(f'{self.abbrev}SIL', current.season) + sil_team = Team.get_season(f"{self.abbrev}SIL", current.season) all_drops = Transaction.select_season(Current.latest().season).where( - (Transaction.oldteam == sil_team) & (Transaction.week == current.week + 1) & (Transaction.cancelled == 0) + (Transaction.oldteam == sil_team) + & (Transaction.week == current.week + 1) + & (Transaction.cancelled == 0) ) all_adds = Transaction.select_season(Current.latest().season).where( - (Transaction.newteam == sil_team) & (Transaction.week == current.week + 1) & (Transaction.cancelled == 0) + (Transaction.newteam == sil_team) + & (Transaction.week == current.week + 1) + & (Transaction.cancelled == 0) ) for move in all_drops: guy_pos = move.player.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers -= 1 else: for pos in guy_pos: short_roster[pos] -= 1 - short_roster['WARa'] -= move.player.wara + short_roster["WARa"] -= move.player.wara # print(f'SIL dropping {move.player.name} id ({move.player.get_id()}) for {move.player.wara} WARa') try: - short_roster['players'].remove(move.player) + short_roster["players"].remove(move.player) except Exception: - print(f'I could not drop {move.player.name}') + print(f"I could not drop {move.player.name}") for move in all_adds: guy_pos = move.player.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: for pos in guy_pos: short_roster[pos] += 1 # print(f'SIL adding {move.player.name} id ({move.player.get_id()}) for {move.player.wara} WARa') - short_roster['WARa'] += move.player.wara - short_roster['players'].append(move.player) + short_roster["WARa"] += move.player.wara + short_roster["players"].append(move.player) if combo_pitchers > 0: - if short_roster['SP'] < 5: - if 5 - short_roster['SP'] <= combo_pitchers: - delta = 5 - short_roster['SP'] + if short_roster["SP"] < 5: + if 5 - short_roster["SP"] <= combo_pitchers: + delta = 5 - short_roster["SP"] else: delta = combo_pitchers - short_roster['SP'] += delta + short_roster["SP"] += delta combo_pitchers -= delta if combo_pitchers > 0: - short_roster['RP'] += combo_pitchers + short_roster["RP"] += combo_pitchers - long_il = Player.select_season(current.season).join(Team).where(Player.team.abbrev == f'{self.abbrev}MiL') + long_il = ( + Player.select_season(current.season) + .join(Team) + .where(Player.team.abbrev == f"{self.abbrev}MiL") + ) - long_roster = {'C': 0, '1B': 0, '2B': 0, '3B': 0, 'SS': 0, 'LF': 0, 'CF': 0, 'RF': 0, 'DH': 0, - 'SP': 0, 'RP': 0, 'CP': 0, 'WARa': 0, 'players': []} + long_roster = { + "C": 0, + "1B": 0, + "2B": 0, + "3B": 0, + "SS": 0, + "LF": 0, + "CF": 0, + "RF": 0, + "DH": 0, + "SP": 0, + "RP": 0, + "CP": 0, + "WARa": 0, + "players": [], + } combo_pitchers = 0 for guy in long_il: - long_roster['WARa'] += guy.wara - long_roster['players'].append(guy) + long_roster["WARa"] += guy.wara + long_roster["players"].append(guy) guy_pos = guy.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: for pos in guy_pos: long_roster[pos] += 1 - lil_team = Team.get_season(f'{self.abbrev}LIL', current.season) + lil_team = Team.get_season(f"{self.abbrev}LIL", current.season) all_drops = Transaction.select_season(Current.latest().season).where( - (Transaction.oldteam == lil_team) & (Transaction.week == current.week + 1) & (Transaction.cancelled == 0) + (Transaction.oldteam == lil_team) + & (Transaction.week == current.week + 1) + & (Transaction.cancelled == 0) ) all_adds = Transaction.select_season(Current.latest().season).where( - (Transaction.newteam == lil_team) & (Transaction.week == current.week + 1) & (Transaction.cancelled == 0) + (Transaction.newteam == lil_team) + & (Transaction.week == current.week + 1) + & (Transaction.cancelled == 0) ) for move in all_drops: guy_pos = move.player.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers -= 1 else: for pos in guy_pos: long_roster[pos] -= 1 - long_roster['WARa'] -= move.player.wara + long_roster["WARa"] -= move.player.wara # print(f'LIL dropping {move.player.name} id ({move.player.get_id()}) for {move.player.wara} WARa') try: - long_roster['players'].remove(move.player) + long_roster["players"].remove(move.player) except Exception: - print(f'I could not drop {move.player.name}') + print(f"I could not drop {move.player.name}") for move in all_adds: guy_pos = move.player.get_positions() - if 'SP' in guy_pos and 'RP' in guy_pos: + if "SP" in guy_pos and "RP" in guy_pos: combo_pitchers += 1 else: for pos in guy_pos: long_roster[pos] += 1 # print(f'LIL adding {move.player.name} id ({move.player.get_id()}) for {move.player.wara} WARa') - long_roster['WARa'] += move.player.wara - long_roster['players'].append(move.player) + long_roster["WARa"] += move.player.wara + long_roster["players"].append(move.player) if combo_pitchers > 0: - if long_roster['SP'] < 5: - if 5 - long_roster['SP'] <= combo_pitchers: - delta = 5 - long_roster['SP'] + if long_roster["SP"] < 5: + if 5 - long_roster["SP"] <= combo_pitchers: + delta = 5 - long_roster["SP"] else: delta = combo_pitchers - long_roster['SP'] += delta + long_roster["SP"] += delta combo_pitchers -= delta if combo_pitchers > 0: - long_roster['RP'] += combo_pitchers + long_roster["RP"] += combo_pitchers - return {'active': active_roster, 'shortil': short_roster, 'longil': long_roster} + return {"active": active_roster, "shortil": short_roster, "longil": long_roster} def run_pythag_last8(self): team_stan = Standings.get_or_none(Standings.team == self) runs_scored, runs_allowed = 0, 0 away_games = StratGame.select( - fn.SUM(StratGame.away_score).alias('r_scored'), - fn.SUM(StratGame.home_score).alias('r_allowed') + fn.SUM(StratGame.away_score).alias("r_scored"), + fn.SUM(StratGame.home_score).alias("r_allowed"), ).where((StratGame.away_team == self) & StratGame.game_num.is_null(False)) if away_games.count() > 0: runs_scored += away_games[0].r_scored runs_allowed += away_games[0].r_allowed home_games = StratGame.select( - fn.SUM(StratGame.home_score).alias('r_scored'), - fn.SUM(StratGame.away_score).alias('r_allowed') + fn.SUM(StratGame.home_score).alias("r_scored"), + fn.SUM(StratGame.away_score).alias("r_allowed"), ).where((StratGame.home_team == self) & StratGame.game_num.is_null(False)) if home_games.count() > 0: runs_scored += home_games[0].r_scored @@ -627,17 +761,23 @@ class Team(BaseModel): elif runs_scored == 0: pythag_win_pct = 0 else: - pythag_win_pct = runs_scored ** 1.83 / ((runs_scored ** 1.83) + (runs_allowed ** 1.83)) + pythag_win_pct = runs_scored**1.83 / ( + (runs_scored**1.83) + (runs_allowed**1.83) + ) games_played = team_stan.wins + team_stan.losses team_stan.pythag_wins = round(games_played * pythag_win_pct) team_stan.pythag_losses = games_played - team_stan.pythag_wins - last_games = StratGame.select().where( - ((StratGame.home_team == self) | (StratGame.away_team == self)) & (StratGame.game_num.is_null(False)) - ).order_by( - -StratGame.season, -StratGame.week, -StratGame.game_num - ).limit(8) + last_games = ( + StratGame.select() + .where( + ((StratGame.home_team == self) | (StratGame.away_team == self)) + & (StratGame.game_num.is_null(False)) + ) + .order_by(-StratGame.season, -StratGame.week, -StratGame.game_num) + .limit(8) + ) for game in last_games: if game.home_score > game.away_score: @@ -879,9 +1019,11 @@ class Player(BaseModel): def get_season(name, num): player = None try: - player = Player.get(fn.Lower(Player.name) == name.lower(), Player.season == num) + player = Player.get( + fn.Lower(Player.name) == name.lower(), Player.season == num + ) except Exception as e: - print(f'**Error** (db_engine player): {e}') + print(f"**Error** (db_engine player): {e}") finally: return player @@ -993,14 +1135,23 @@ class BattingStat(BaseModel): Return: ModelSelect object for season's regular season """ if season == 1: - return BattingStat.select().where((BattingStat.season == 1) & (BattingStat.week < 21))\ + return ( + BattingStat.select() + .where((BattingStat.season == 1) & (BattingStat.week < 21)) .order_by(BattingStat.week) + ) elif season == 2: - return BattingStat.select().where((BattingStat.season == 2) & (BattingStat.week < 19))\ + return ( + BattingStat.select() + .where((BattingStat.season == 2) & (BattingStat.week < 19)) .order_by(BattingStat.week) + ) elif season > 2: - return BattingStat.select().where((BattingStat.season == season) & (BattingStat.week < 23))\ + return ( + BattingStat.select() + .where((BattingStat.season == season) & (BattingStat.week < 23)) .order_by(BattingStat.week) + ) else: return None @@ -1011,137 +1162,176 @@ class BattingStat(BaseModel): Return: ModelSelect object for season's post season """ if season == 1: - return BattingStat.select().where((BattingStat.season == 1) & (BattingStat.week >= 21)) + return BattingStat.select().where( + (BattingStat.season == 1) & (BattingStat.week >= 21) + ) elif season == 2: - return BattingStat.select().where((BattingStat.season == 2) & (BattingStat.week >= 19)) + return BattingStat.select().where( + (BattingStat.season == 2) & (BattingStat.week >= 19) + ) elif season > 2: - return BattingStat.select().where((BattingStat.season == season) & (BattingStat.week >= 23)) + return BattingStat.select().where( + (BattingStat.season == season) & (BattingStat.week >= 23) + ) else: return None @staticmethod def team_season(team, season): - b_stats = BattingStat.regular_season(season).join(Player).select( - fn.SUM(BattingStat.pa).alias('pas'), - fn.SUM(BattingStat.ab).alias('abs'), - fn.SUM(BattingStat.run).alias('runs'), - fn.SUM(BattingStat.hit).alias('hits'), - fn.SUM(BattingStat.rbi).alias('rbis'), - fn.SUM(BattingStat.double).alias('doubles'), - fn.SUM(BattingStat.triple).alias('triples'), - fn.SUM(BattingStat.hr).alias('hrs'), - fn.SUM(BattingStat.bb).alias('bbs'), - fn.SUM(BattingStat.so).alias('sos'), - fn.SUM(BattingStat.hbp).alias('hbps'), - fn.SUM(BattingStat.sac).alias('sacs'), - fn.SUM(BattingStat.ibb).alias('ibbs'), - fn.SUM(BattingStat.gidp).alias('gidps'), - fn.SUM(BattingStat.sb).alias('sbs'), - fn.SUM(BattingStat.cs).alias('css'), - fn.SUM(BattingStat.bphr).alias('bphr'), - fn.SUM(BattingStat.bpfo).alias('bpfo'), - fn.SUM(BattingStat.bp1b).alias('bp1b'), - fn.SUM(BattingStat.bplo).alias('bplo'), - # fn.SUM(BattingStat.xba).alias('xba'), - # fn.SUM(BattingStat.xbt).alias('xbt'), - fn.COUNT(BattingStat.game).alias('games'), - ).where(BattingStat.team == team) + b_stats = ( + BattingStat.regular_season(season) + .join(Player) + .select( + fn.SUM(BattingStat.pa).alias("pas"), + fn.SUM(BattingStat.ab).alias("abs"), + fn.SUM(BattingStat.run).alias("runs"), + fn.SUM(BattingStat.hit).alias("hits"), + fn.SUM(BattingStat.rbi).alias("rbis"), + fn.SUM(BattingStat.double).alias("doubles"), + fn.SUM(BattingStat.triple).alias("triples"), + fn.SUM(BattingStat.hr).alias("hrs"), + fn.SUM(BattingStat.bb).alias("bbs"), + fn.SUM(BattingStat.so).alias("sos"), + fn.SUM(BattingStat.hbp).alias("hbps"), + fn.SUM(BattingStat.sac).alias("sacs"), + fn.SUM(BattingStat.ibb).alias("ibbs"), + fn.SUM(BattingStat.gidp).alias("gidps"), + fn.SUM(BattingStat.sb).alias("sbs"), + fn.SUM(BattingStat.cs).alias("css"), + fn.SUM(BattingStat.bphr).alias("bphr"), + fn.SUM(BattingStat.bpfo).alias("bpfo"), + fn.SUM(BattingStat.bp1b).alias("bp1b"), + fn.SUM(BattingStat.bplo).alias("bplo"), + # fn.SUM(BattingStat.xba).alias('xba'), + # fn.SUM(BattingStat.xbt).alias('xbt'), + fn.COUNT(BattingStat.game).alias("games"), + ) + .where(BattingStat.team == team) + ) total = { - 'game': b_stats[0].games if b_stats[0].games else 0, - 'pa': b_stats[0].pas if b_stats[0].pas else 0, - 'ab': b_stats[0].abs if b_stats[0].abs else 0, - 'run': b_stats[0].runs if b_stats[0].runs else 0, - 'hit': b_stats[0].hits if b_stats[0].hits else 0, - 'rbi': b_stats[0].rbis if b_stats[0].rbis else 0, - 'double': b_stats[0].doubles if b_stats[0].doubles else 0, - 'triple': b_stats[0].triples if b_stats[0].triples else 0, - 'hr': b_stats[0].hrs if b_stats[0].hrs else 0, - 'bb': b_stats[0].bbs if b_stats[0].bbs else 0, - 'so': b_stats[0].sos if b_stats[0].sos else 0, - 'hbp': b_stats[0].hbps if b_stats[0].hbps else 0, - 'sac': b_stats[0].sacs if b_stats[0].sacs else 0, - 'ibb': b_stats[0].ibbs if b_stats[0].ibbs else 0, - 'gidp': b_stats[0].gidps if b_stats[0].gidps else 0, - 'sb': b_stats[0].sbs if b_stats[0].sbs else 0, - 'cs': b_stats[0].css if b_stats[0].css else 0, - 'ba': 0, - 'obp': 0, - 'slg': 0, - 'woba': 0, - 'kpct': 0, - 'bphr': b_stats[0].bphr if b_stats[0].bphr else 0, - 'bpfo': b_stats[0].bpfo if b_stats[0].bpfo else 0, - 'bp1b': b_stats[0].bp1b if b_stats[0].bp1b else 0, - 'bplo': b_stats[0].bplo if b_stats[0].bplo else 0, + "game": b_stats[0].games if b_stats[0].games else 0, + "pa": b_stats[0].pas if b_stats[0].pas else 0, + "ab": b_stats[0].abs if b_stats[0].abs else 0, + "run": b_stats[0].runs if b_stats[0].runs else 0, + "hit": b_stats[0].hits if b_stats[0].hits else 0, + "rbi": b_stats[0].rbis if b_stats[0].rbis else 0, + "double": b_stats[0].doubles if b_stats[0].doubles else 0, + "triple": b_stats[0].triples if b_stats[0].triples else 0, + "hr": b_stats[0].hrs if b_stats[0].hrs else 0, + "bb": b_stats[0].bbs if b_stats[0].bbs else 0, + "so": b_stats[0].sos if b_stats[0].sos else 0, + "hbp": b_stats[0].hbps if b_stats[0].hbps else 0, + "sac": b_stats[0].sacs if b_stats[0].sacs else 0, + "ibb": b_stats[0].ibbs if b_stats[0].ibbs else 0, + "gidp": b_stats[0].gidps if b_stats[0].gidps else 0, + "sb": b_stats[0].sbs if b_stats[0].sbs else 0, + "cs": b_stats[0].css if b_stats[0].css else 0, + "ba": 0, + "obp": 0, + "slg": 0, + "woba": 0, + "kpct": 0, + "bphr": b_stats[0].bphr if b_stats[0].bphr else 0, + "bpfo": b_stats[0].bpfo if b_stats[0].bpfo else 0, + "bp1b": b_stats[0].bp1b if b_stats[0].bp1b else 0, + "bplo": b_stats[0].bplo if b_stats[0].bplo else 0, # 'xba': b_stats[0].xba if b_stats[0].xba else 0, # 'xbt': b_stats[0].xbt if b_stats[0].xbt else 0, } if b_stats[0].abs: - total['ba'] = b_stats[0].hits / b_stats[0].abs + total["ba"] = b_stats[0].hits / b_stats[0].abs - total['obp'] = ( - (b_stats[0].bbs + b_stats[0].hits + b_stats[0].hbps + b_stats[0].ibbs) / b_stats[0].pas + total["obp"] = ( + b_stats[0].bbs + b_stats[0].hits + b_stats[0].hbps + b_stats[0].ibbs + ) / b_stats[0].pas + + total["slg"] = ( + (b_stats[0].hrs * 4) + + (b_stats[0].triples * 3) + + (b_stats[0].doubles * 2) + + ( + b_stats[0].hits + - b_stats[0].hrs + - b_stats[0].triples + - b_stats[0].doubles + ) + ) / b_stats[0].abs + + total["woba"] = ( + (b_stats[0].bbs * 0.69) + + (b_stats[0].hbps * 0.722) + + (b_stats[0].doubles * 1.271) + + (b_stats[0].triples * 1.616) + + (b_stats[0].hrs * 2.101) + + ( + ( + b_stats[0].hits + - b_stats[0].hrs + - b_stats[0].triples + - b_stats[0].doubles + ) + * 0.888 + ) + ) / (b_stats[0].pas - b_stats[0].ibbs) + + total["kpct"] = (total["so"] * 100) / total["ab"] + + total_innings = ( + PitchingStat.regular_season(season) + .join(Player) + .select( + fn.SUM(PitchingStat.ip).alias("ips"), + ) + .where(PitchingStat.player.team == team) ) - - total['slg'] = ( - ((b_stats[0].hrs * 4) + (b_stats[0].triples * 3) + (b_stats[0].doubles * 2) + - (b_stats[0].hits - b_stats[0].hrs - b_stats[0].triples - b_stats[0].doubles)) / b_stats[0].abs - ) - - total['woba'] = ( - ((b_stats[0].bbs * .69) + (b_stats[0].hbps * .722) + (b_stats[0].doubles * 1.271) + - (b_stats[0].triples * 1.616) + (b_stats[0].hrs * 2.101) + - ((b_stats[0].hits - b_stats[0].hrs - b_stats[0].triples - b_stats[0].doubles) * .888)) / - (b_stats[0].pas - b_stats[0].ibbs) - ) - - total['kpct'] = (total['so'] * 100) / total['ab'] - - total_innings = PitchingStat.regular_season(season).join(Player).select( - fn.SUM(PitchingStat.ip).alias('ips'), - ).where(PitchingStat.player.team == team) - total['rper9'] = (total['run'] * 9) / total_innings[0].ips + total["rper9"] = (total["run"] * 9) / total_innings[0].ips return total @staticmethod def team_fielding_season(team, season): - f_stats = BattingStat.regular_season(season).select( - fn.SUM(BattingStat.xch).alias('xchs'), - fn.SUM(BattingStat.xhit).alias('xhits'), - fn.SUM(BattingStat.error).alias('errors'), - # fn.SUM(BattingStat.roba).alias('roba'), - # fn.SUM(BattingStat.robs).alias('robs'), - # fn.SUM(BattingStat.raa).alias('raa'), - # fn.SUM(BattingStat.rto).alias('rto'), - fn.SUM(BattingStat.pb).alias('pbs'), - fn.SUM(BattingStat.sbc).alias('sbas'), - fn.SUM(BattingStat.csc).alias('cscs'), - fn.COUNT(BattingStat.game).alias('games'), - ).where(BattingStat.team == team) + f_stats = ( + BattingStat.regular_season(season) + .select( + fn.SUM(BattingStat.xch).alias("xchs"), + fn.SUM(BattingStat.xhit).alias("xhits"), + fn.SUM(BattingStat.error).alias("errors"), + # fn.SUM(BattingStat.roba).alias('roba'), + # fn.SUM(BattingStat.robs).alias('robs'), + # fn.SUM(BattingStat.raa).alias('raa'), + # fn.SUM(BattingStat.rto).alias('rto'), + fn.SUM(BattingStat.pb).alias("pbs"), + fn.SUM(BattingStat.sbc).alias("sbas"), + fn.SUM(BattingStat.csc).alias("cscs"), + fn.COUNT(BattingStat.game).alias("games"), + ) + .where(BattingStat.team == team) + ) total = { - 'game': f_stats[0].games if f_stats[0].games else 0, - 'xch': f_stats[0].xchs if f_stats[0].xchs else 0, - 'xhit': f_stats[0].xhits if f_stats[0].xhits else 0, - 'error': f_stats[0].errors if f_stats[0].errors else 0, + "game": f_stats[0].games if f_stats[0].games else 0, + "xch": f_stats[0].xchs if f_stats[0].xchs else 0, + "xhit": f_stats[0].xhits if f_stats[0].xhits else 0, + "error": f_stats[0].errors if f_stats[0].errors else 0, # 'roba': f_stats[0].roba if f_stats[0].roba else 0, # 'robs': f_stats[0].robs if f_stats[0].robs else 0, # 'raa': f_stats[0].raa if f_stats[0].raa else 0, # 'rto': f_stats[0].rto if f_stats[0].rto else 0, - 'pb': f_stats[0].pbs if f_stats[0].pbs else 0, - 'sbc': f_stats[0].sbas if f_stats[0].sbas else 0, - 'csc': f_stats[0].cscs if f_stats[0].cscs else 0, - 'wfpct': 0, - 'cspct': 0, + "pb": f_stats[0].pbs if f_stats[0].pbs else 0, + "sbc": f_stats[0].sbas if f_stats[0].sbas else 0, + "csc": f_stats[0].cscs if f_stats[0].cscs else 0, + "wfpct": 0, + "cspct": 0, } - if total['xch'] > 0: - total['wfpct'] = (total['xch'] - (total['error'] * .5) - (total['xhit'] * .75)) / (total['xch']) - if total['sbc'] > 0: - total['cspct'] = (total['csc'] / total['sbc']) * 100 + if total["xch"] > 0: + total["wfpct"] = ( + total["xch"] - (total["error"] * 0.5) - (total["xhit"] * 0.75) + ) / (total["xch"]) + if total["sbc"] > 0: + total["cspct"] = (total["csc"] / total["sbc"]) * 100 return total @@ -1178,87 +1368,109 @@ class PitchingStat(BaseModel): @staticmethod def regular_season(season): if season == 1: - return PitchingStat.select().where((PitchingStat.season == 1) & (PitchingStat.week < 21))\ + return ( + PitchingStat.select() + .where((PitchingStat.season == 1) & (PitchingStat.week < 21)) .order_by(PitchingStat.week) + ) elif season == 2: - return PitchingStat.select().where((PitchingStat.season == 2) & (PitchingStat.week < 19))\ + return ( + PitchingStat.select() + .where((PitchingStat.season == 2) & (PitchingStat.week < 19)) .order_by(PitchingStat.week) + ) elif season > 2: - return PitchingStat.select().where((PitchingStat.season == season) & (PitchingStat.week < 23))\ + return ( + PitchingStat.select() + .where((PitchingStat.season == season) & (PitchingStat.week < 23)) .order_by(PitchingStat.week) + ) else: return None @staticmethod def post_season(season): if season == 1: - return PitchingStat.select().where((PitchingStat.season == 1) & (PitchingStat.week >= 21))\ + return ( + PitchingStat.select() + .where((PitchingStat.season == 1) & (PitchingStat.week >= 21)) .order_by(PitchingStat.week) + ) elif season == 2: - return PitchingStat.select().where((PitchingStat.season == 2) & (PitchingStat.week >= 19))\ + return ( + PitchingStat.select() + .where((PitchingStat.season == 2) & (PitchingStat.week >= 19)) .order_by(PitchingStat.week) + ) elif season > 2: - return PitchingStat.select().where((PitchingStat.season == season) & (PitchingStat.week >= 23))\ + return ( + PitchingStat.select() + .where((PitchingStat.season == season) & (PitchingStat.week >= 23)) .order_by(PitchingStat.week) + ) else: return None @staticmethod def team_season(team, season): - p_stats = PitchingStat.regular_season(season).select( - fn.SUM(PitchingStat.ip).alias('ips'), - fn.SUM(PitchingStat.hit).alias('hits'), - fn.SUM(PitchingStat.run).alias('runs'), - fn.SUM(PitchingStat.erun).alias('eruns'), - fn.SUM(PitchingStat.so).alias('sos'), - fn.SUM(PitchingStat.bb).alias('bbs'), - fn.SUM(PitchingStat.hbp).alias('hbps'), - fn.SUM(PitchingStat.wp).alias('wps'), - fn.SUM(PitchingStat.ir).alias('ir'), - fn.SUM(PitchingStat.irs).alias('irs'), - fn.SUM(PitchingStat.balk).alias('balks'), - fn.SUM(PitchingStat.hr).alias('hrs'), - fn.COUNT(PitchingStat.game).alias('games'), - fn.SUM(PitchingStat.gs).alias('gss'), - fn.SUM(PitchingStat.win).alias('wins'), - fn.SUM(PitchingStat.loss).alias('losses'), - fn.SUM(PitchingStat.hold).alias('holds'), - fn.SUM(PitchingStat.sv).alias('saves'), - fn.SUM(PitchingStat.bsv).alias('bsaves'), - ).where(PitchingStat.team == team) + p_stats = ( + PitchingStat.regular_season(season) + .select( + fn.SUM(PitchingStat.ip).alias("ips"), + fn.SUM(PitchingStat.hit).alias("hits"), + fn.SUM(PitchingStat.run).alias("runs"), + fn.SUM(PitchingStat.erun).alias("eruns"), + fn.SUM(PitchingStat.so).alias("sos"), + fn.SUM(PitchingStat.bb).alias("bbs"), + fn.SUM(PitchingStat.hbp).alias("hbps"), + fn.SUM(PitchingStat.wp).alias("wps"), + fn.SUM(PitchingStat.ir).alias("ir"), + fn.SUM(PitchingStat.irs).alias("irs"), + fn.SUM(PitchingStat.balk).alias("balks"), + fn.SUM(PitchingStat.hr).alias("hrs"), + fn.COUNT(PitchingStat.game).alias("games"), + fn.SUM(PitchingStat.gs).alias("gss"), + fn.SUM(PitchingStat.win).alias("wins"), + fn.SUM(PitchingStat.loss).alias("losses"), + fn.SUM(PitchingStat.hold).alias("holds"), + fn.SUM(PitchingStat.sv).alias("saves"), + fn.SUM(PitchingStat.bsv).alias("bsaves"), + ) + .where(PitchingStat.team == team) + ) total = { - 'ip': p_stats[0].ips if p_stats[0].ips else 0, - 'hit': int(p_stats[0].hits) if p_stats[0].hits else 0, - 'run': int(p_stats[0].runs) if p_stats[0].runs else 0, - 'erun': int(p_stats[0].eruns) if p_stats[0].eruns else 0, - 'so': int(p_stats[0].sos) if p_stats[0].sos else 0, - 'bb': int(p_stats[0].bbs) if p_stats[0].bbs else 0, - 'hbp': int(p_stats[0].hbps) if p_stats[0].hbps else 0, - 'wp': int(p_stats[0].wps) if p_stats[0].wps else 0, - 'balk': int(p_stats[0].balks) if p_stats[0].balks else 0, - 'hr': int(p_stats[0].hrs) if p_stats[0].hrs else 0, - 'game': int(p_stats[0].games) if p_stats[0].games else 0, - 'gs': int(p_stats[0].gss) if p_stats[0].gss else 0, - 'win': int(p_stats[0].wins) if p_stats[0].wins else 0, - 'loss': int(p_stats[0].losses) if p_stats[0].losses else 0, - 'hold': int(p_stats[0].holds) if p_stats[0].holds else 0, - 'sv': int(p_stats[0].saves) if p_stats[0].saves else 0, - 'bsv': int(p_stats[0].bsaves) if p_stats[0].bsaves else 0, - 'wl%': 0, - 'era': 0, - 'whip': 0, - 'ir': int(p_stats[0].ir) if p_stats[0].ir else 0, - 'irs': int(p_stats[0].irs) if p_stats[0].irs else 0, + "ip": p_stats[0].ips if p_stats[0].ips else 0, + "hit": int(p_stats[0].hits) if p_stats[0].hits else 0, + "run": int(p_stats[0].runs) if p_stats[0].runs else 0, + "erun": int(p_stats[0].eruns) if p_stats[0].eruns else 0, + "so": int(p_stats[0].sos) if p_stats[0].sos else 0, + "bb": int(p_stats[0].bbs) if p_stats[0].bbs else 0, + "hbp": int(p_stats[0].hbps) if p_stats[0].hbps else 0, + "wp": int(p_stats[0].wps) if p_stats[0].wps else 0, + "balk": int(p_stats[0].balks) if p_stats[0].balks else 0, + "hr": int(p_stats[0].hrs) if p_stats[0].hrs else 0, + "game": int(p_stats[0].games) if p_stats[0].games else 0, + "gs": int(p_stats[0].gss) if p_stats[0].gss else 0, + "win": int(p_stats[0].wins) if p_stats[0].wins else 0, + "loss": int(p_stats[0].losses) if p_stats[0].losses else 0, + "hold": int(p_stats[0].holds) if p_stats[0].holds else 0, + "sv": int(p_stats[0].saves) if p_stats[0].saves else 0, + "bsv": int(p_stats[0].bsaves) if p_stats[0].bsaves else 0, + "wl%": 0, + "era": 0, + "whip": 0, + "ir": int(p_stats[0].ir) if p_stats[0].ir else 0, + "irs": int(p_stats[0].irs) if p_stats[0].irs else 0, } - if total['ip']: - total['era'] = (total['erun'] * 9) / total['ip'] + if total["ip"]: + total["era"] = (total["erun"] * 9) / total["ip"] - total['whip'] = (total['bb'] + total['hit']) / total['ip'] + total["whip"] = (total["bb"] + total["hit"]) / total["ip"] - if total['win'] + total['loss'] > 0: - total['wl%'] = total['win'] / (total['win'] + total['loss']) + if total["win"] + total["loss"] > 0: + total["wl%"] = total["win"] / (total["win"] + total["loss"]) return total @@ -1278,7 +1490,7 @@ class Standings(BaseModel): away_losses = IntegerField(default=0) last8_wins = IntegerField(default=0) last8_losses = IntegerField(default=0) - streak_wl = CharField(default='w') + streak_wl = CharField(default="w") streak_num = IntegerField(default=0) one_run_wins = IntegerField(default=0) one_run_losses = IntegerField(default=0) @@ -1319,9 +1531,15 @@ class Standings(BaseModel): # Iterate through each individual result # for game in Result.select_season(season).where(Result.week <= 22): - for game in StratGame.select().where( - (StratGame.season == season) & (StratGame.week <= 18) & (StratGame.game_num.is_null(False)) - ).order_by(StratGame.week, StratGame.game_num): + for game in ( + StratGame.select() + .where( + (StratGame.season == season) + & (StratGame.week <= 18) + & (StratGame.game_num.is_null(False)) + ) + .order_by(StratGame.week, StratGame.game_num) + ): # tally win and loss for each standings object game.update_standings() @@ -1336,7 +1554,7 @@ class Standings(BaseModel): # Pull each league (filter by not null wc_gb) and sort by win pct # # For one league: - Division.sort_wildcard(season, 'SBa') + Division.sort_wildcard(season, "SBa") # For two leagues # Division.sort_wildcard(season, 'AL') @@ -1377,8 +1595,12 @@ class BattingCareer(BaseModel): line.delete_instance() # For each seasonstat, find career or create new and increment - for this_season in BattingSeason.select().where(BattingSeason.season_type == 'Regular'): - this_career = BattingCareer.get_or_none(BattingCareer.name == this_season.player.name) + for this_season in BattingSeason.select().where( + BattingSeason.season_type == "Regular" + ): + this_career = BattingCareer.get_or_none( + BattingCareer.name == this_season.player.name + ) if not this_career: this_career = BattingCareer(name=this_season.player.name) this_career.save() @@ -1438,8 +1660,12 @@ class PitchingCareer(BaseModel): line.delete_instance() # For each seasonstat, find career or create new and increment - for this_season in PitchingSeason.select().where(PitchingSeason.season_type == 'Regular'): - this_career = PitchingCareer.get_or_none(PitchingCareer.name == this_season.player.name) + for this_season in PitchingSeason.select().where( + PitchingSeason.season_type == "Regular" + ): + this_career = PitchingCareer.get_or_none( + PitchingCareer.name == this_season.player.name + ) if not this_career: this_career = PitchingCareer(name=this_season.player.name) this_career.save() @@ -1488,12 +1714,17 @@ class FieldingCareer(BaseModel): line.delete_instance() # For each seasonstat, find career or create new and increment - for this_season in FieldingSeason.select().where(FieldingSeason.season_type == 'Regular'): + for this_season in FieldingSeason.select().where( + FieldingSeason.season_type == "Regular" + ): this_career = FieldingCareer.get_or_none( - FieldingCareer.name == this_season.player.name, FieldingCareer.pos == this_season.pos + FieldingCareer.name == this_season.player.name, + FieldingCareer.pos == this_season.pos, ) if not this_career: - this_career = FieldingCareer(name=this_season.player.name, pos=this_season.pos) + this_career = FieldingCareer( + name=this_season.player.name, pos=this_season.pos + ) this_career.save() this_career.xch += this_season.xch @@ -1512,7 +1743,7 @@ class FieldingCareer(BaseModel): class BattingSeason(BaseModel): player = ForeignKeyField(Player) season = IntegerField() - season_type = CharField(default='Regular') + season_type = CharField(default="Regular") career = ForeignKeyField(BattingCareer, null=True) pa = FloatField(default=0) ab = FloatField(default=0) @@ -1608,10 +1839,14 @@ class BattingSeason(BaseModel): self.xbt = 0 self.game = 0 - if self.season_type == 'Regular': - all_stats = BattingStat.regular_season(self.season).where(BattingStat.player == self.player) + if self.season_type == "Regular": + all_stats = BattingStat.regular_season(self.season).where( + BattingStat.player == self.player + ) else: - all_stats = BattingStat.post_season(self.season).where(BattingStat.player == self.player) + all_stats = BattingStat.post_season(self.season).where( + BattingStat.player == self.player + ) for line in all_stats: self.pa += line.pa self.ab += line.ab @@ -1644,7 +1879,7 @@ class BattingSeason(BaseModel): class PitchingSeason(BaseModel): player = ForeignKeyField(Player) season = IntegerField() - season_type = CharField(default='Regular') + season_type = CharField(default="Regular") career = ForeignKeyField(PitchingCareer, null=True) ip = FloatField(default=0) hit = FloatField(default=0) @@ -1733,10 +1968,14 @@ class PitchingSeason(BaseModel): self.bsv = 0 self.game = 0 - if self.season_type == 'Regular': - all_stats = PitchingStat.regular_season(self.season).where(PitchingStat.player == self.player) + if self.season_type == "Regular": + all_stats = PitchingStat.regular_season(self.season).where( + PitchingStat.player == self.player + ) else: - all_stats = PitchingStat.post_season(self.season).where(PitchingStat.player == self.player) + all_stats = PitchingStat.post_season(self.season).where( + PitchingStat.player == self.player + ) for line in all_stats: self.ip += line.ip self.hit += line.hit @@ -1765,7 +2004,7 @@ class PitchingSeason(BaseModel): class FieldingSeason(BaseModel): player = ForeignKeyField(Player) season = IntegerField() - season_type = CharField(default='Regular') + season_type = CharField(default="Regular") pos = CharField() career = ForeignKeyField(FieldingCareer, null=True) xch = IntegerField(default=0) @@ -1831,7 +2070,7 @@ class FieldingSeason(BaseModel): self.rto = 0 self.game = 0 - if self.season_type == 'Regular': + if self.season_type == "Regular": all_stats = BattingStat.regular_season(self.season).where( (BattingStat.player == self.player) & (BattingStat.pos == self.pos) ) @@ -1871,7 +2110,9 @@ class DraftPick(BaseModel): @staticmethod def get_season(team, rd, num): - return DraftPick.get(DraftPick.season == num, DraftPick.origowner == team, DraftPick.round == rd) + return DraftPick.get( + DraftPick.season == num, DraftPick.origowner == team, DraftPick.round == rd + ) class DraftData(BaseModel): @@ -1879,7 +2120,7 @@ class DraftData(BaseModel): timer = BooleanField() pick_deadline = DateTimeField(null=True) result_channel = CharField(max_length=20, null=True) # Discord channel ID as string - ping_channel = CharField(max_length=20, null=True) # Discord channel ID as string + ping_channel = CharField(max_length=20, null=True) # Discord channel ID as string pick_minutes = IntegerField(null=True) @@ -1896,7 +2137,7 @@ class Award(BaseModel): class DiceRoll(BaseModel): season = IntegerField(default=12) # Will be updated to current season when needed - week = IntegerField(default=1) # Will be updated to current week when needed + week = IntegerField(default=1) # Will be updated to current week when needed team = ForeignKeyField(Team, null=True) roller = CharField(max_length=20) dsix = IntegerField(null=True) @@ -1933,7 +2174,7 @@ class StratGame(BaseModel): season = IntegerField() week = IntegerField() game_num = IntegerField(null=True) - season_type = CharField(default='regular') + season_type = CharField(default="regular") away_team = ForeignKeyField(Team) home_team = ForeignKeyField(Team) away_score = IntegerField(null=True) @@ -1957,16 +2198,16 @@ class StratGame(BaseModel): away_stan.away_losses += 1 # - update streak wl and num - if home_stan.streak_wl == 'w': + if home_stan.streak_wl == "w": home_stan.streak_num += 1 else: - home_stan.streak_wl = 'w' + home_stan.streak_wl = "w" home_stan.streak_num = 1 - if away_stan.streak_wl == 'l': + if away_stan.streak_wl == "l": away_stan.streak_num += 1 else: - away_stan.streak_wl = 'l' + away_stan.streak_wl = "l" away_stan.streak_num = 1 # - if 1-run, tally accordingly @@ -1992,22 +2233,22 @@ class StratGame(BaseModel): # Used for one league with 4 divisions # - update record v division (check opponent's division) - if away_div.division_abbrev == 'TC': + if away_div.division_abbrev == "TC": home_stan.div1_wins += 1 - elif away_div.division_abbrev == 'ETSOS': + elif away_div.division_abbrev == "ETSOS": home_stan.div2_wins += 1 - elif away_div.division_abbrev == 'APL': + elif away_div.division_abbrev == "APL": home_stan.div3_wins += 1 - elif away_div.division_abbrev == 'BBC': + elif away_div.division_abbrev == "BBC": home_stan.div4_wins += 1 - if home_div.division_abbrev == 'TC': + if home_div.division_abbrev == "TC": away_stan.div1_losses += 1 - elif home_div.division_abbrev == 'ETSOS': + elif home_div.division_abbrev == "ETSOS": away_stan.div2_losses += 1 - elif home_div.division_abbrev == 'APL': + elif home_div.division_abbrev == "APL": away_stan.div3_losses += 1 - elif home_div.division_abbrev == 'BBC': + elif home_div.division_abbrev == "BBC": away_stan.div4_losses += 1 # Used for two league plus divisions @@ -2045,16 +2286,16 @@ class StratGame(BaseModel): away_stan.away_wins += 1 # - update streak wl and num - if home_stan.streak_wl == 'l': + if home_stan.streak_wl == "l": home_stan.streak_num += 1 else: - home_stan.streak_wl = 'l' + home_stan.streak_wl = "l" home_stan.streak_num = 1 - if away_stan.streak_wl == 'w': + if away_stan.streak_wl == "w": away_stan.streak_num += 1 else: - away_stan.streak_wl = 'w' + away_stan.streak_wl = "w" away_stan.streak_num = 1 # - if 1-run, tally accordingly @@ -2080,22 +2321,22 @@ class StratGame(BaseModel): # Used for one league with 4 divisions # - update record v division (check opponent's division) - if away_div.division_abbrev == 'TC': + if away_div.division_abbrev == "TC": home_stan.div1_losses += 1 - elif away_div.division_abbrev == 'ETSOS': + elif away_div.division_abbrev == "ETSOS": home_stan.div2_losses += 1 - elif away_div.division_abbrev == 'APL': + elif away_div.division_abbrev == "APL": home_stan.div3_losses += 1 - elif away_div.division_abbrev == 'BBC': + elif away_div.division_abbrev == "BBC": home_stan.div4_losses += 1 - if home_div.division_abbrev == 'TC': + if home_div.division_abbrev == "TC": away_stan.div1_wins += 1 - elif home_div.division_abbrev == 'ETSOS': + elif home_div.division_abbrev == "ETSOS": away_stan.div2_wins += 1 - elif home_div.division_abbrev == 'APL': + elif home_div.division_abbrev == "APL": away_stan.div3_wins += 1 - elif home_div.division_abbrev == 'BBC': + elif home_div.division_abbrev == "BBC": away_stan.div4_wins += 1 # Used for two league plus divisions @@ -2224,6 +2465,7 @@ class Decision(BaseModel): class CustomCommandCreator(BaseModel): """Model for custom command creators.""" + discord_id = CharField(max_length=20, unique=True) # Discord snowflake ID as string username = CharField(max_length=32) display_name = CharField(max_length=32, null=True) @@ -2232,126 +2474,147 @@ class CustomCommandCreator(BaseModel): active_commands = IntegerField(default=0) class Meta: - table_name = 'custom_command_creators' + table_name = "custom_command_creators" class CustomCommand(BaseModel): """Model for custom commands created by users.""" + name = CharField(max_length=32, unique=True) content = TextField() - creator = ForeignKeyField(CustomCommandCreator, backref='commands') - + creator = ForeignKeyField(CustomCommandCreator, backref="commands") + # Timestamps created_at = DateTimeField() updated_at = DateTimeField(null=True) last_used = DateTimeField(null=True) - + # Usage tracking use_count = IntegerField(default=0) warning_sent = BooleanField(default=False) - + # Metadata is_active = BooleanField(default=True) tags = TextField(null=True) # JSON string for tags list class Meta: - table_name = 'custom_commands' + table_name = "custom_commands" @staticmethod def get_by_name(name: str): """Get a custom command by name (case-insensitive).""" return CustomCommand.get_or_none(fn.Lower(CustomCommand.name) == name.lower()) - + @staticmethod def search_by_name(partial_name: str, limit: int = 25): """Search commands by partial name match.""" - return (CustomCommand - .select() - .where((CustomCommand.is_active == True) & - (fn.Lower(CustomCommand.name).contains(partial_name.lower()))) - .order_by(CustomCommand.name) - .limit(limit)) - + return ( + CustomCommand.select() + .where( + (CustomCommand.is_active == True) + & (fn.Lower(CustomCommand.name).contains(partial_name.lower())) + ) + .order_by(CustomCommand.name) + .limit(limit) + ) + @staticmethod def get_popular(limit: int = 10): """Get most popular commands by usage.""" - return (CustomCommand - .select() - .where(CustomCommand.is_active == True) - .order_by(CustomCommand.use_count.desc()) - .limit(limit)) - + return ( + CustomCommand.select() + .where(CustomCommand.is_active == True) + .order_by(CustomCommand.use_count.desc()) + .limit(limit) + ) + @staticmethod def get_by_creator(creator_id: int, limit: int = 25, offset: int = 0): """Get commands by creator ID.""" - return (CustomCommand - .select() - .where((CustomCommand.creator == creator_id) & (CustomCommand.is_active == True)) - .order_by(CustomCommand.name) - .limit(limit) - .offset(offset)) - + return ( + CustomCommand.select() + .where( + (CustomCommand.creator == creator_id) + & (CustomCommand.is_active == True) + ) + .order_by(CustomCommand.name) + .limit(limit) + .offset(offset) + ) + @staticmethod def get_unused_commands(days: int = 60): """Get commands that haven't been used in specified days.""" from datetime import datetime, timedelta + cutoff_date = datetime.now() - timedelta(days=days) - return (CustomCommand - .select() - .where((CustomCommand.is_active == True) & - ((CustomCommand.last_used.is_null()) | - (CustomCommand.last_used < cutoff_date)))) - + return CustomCommand.select().where( + (CustomCommand.is_active == True) + & ( + (CustomCommand.last_used.is_null()) + | (CustomCommand.last_used < cutoff_date) + ) + ) + @staticmethod def get_commands_needing_warning(): """Get commands that need deletion warning (60+ days unused, no warning sent).""" from datetime import datetime, timedelta + cutoff_date = datetime.now() - timedelta(days=60) - return (CustomCommand - .select() - .where((CustomCommand.is_active == True) & - (CustomCommand.warning_sent == False) & - ((CustomCommand.last_used.is_null()) | - (CustomCommand.last_used < cutoff_date)))) - + return CustomCommand.select().where( + (CustomCommand.is_active == True) + & (CustomCommand.warning_sent == False) + & ( + (CustomCommand.last_used.is_null()) + | (CustomCommand.last_used < cutoff_date) + ) + ) + @staticmethod def get_commands_eligible_for_deletion(): """Get commands eligible for deletion (90+ days unused).""" from datetime import datetime, timedelta + cutoff_date = datetime.now() - timedelta(days=90) - return (CustomCommand - .select() - .where((CustomCommand.is_active == True) & - ((CustomCommand.last_used.is_null()) | - (CustomCommand.last_used < cutoff_date)))) - + return CustomCommand.select().where( + (CustomCommand.is_active == True) + & ( + (CustomCommand.last_used.is_null()) + | (CustomCommand.last_used < cutoff_date) + ) + ) + def execute(self): """Execute the command and update usage statistics.""" from datetime import datetime + self.last_used = datetime.now() self.use_count += 1 self.warning_sent = False # Reset warning on use self.save() - + def mark_warning_sent(self): """Mark that a deletion warning has been sent.""" self.warning_sent = True self.save() - + def get_tags_list(self): """Parse tags JSON string into a list.""" if not self.tags: return [] try: import json + return json.loads(self.tags) except Exception: return [] - + def set_tags_list(self, tags_list): """Set tags from a list to JSON string.""" if tags_list: import json + self.tags = json.dumps(tags_list) else: self.tags = None @@ -2359,6 +2622,7 @@ class CustomCommand(BaseModel): class HelpCommand(BaseModel): """Model for admin-created help topics.""" + name = CharField(max_length=32, unique=True) title = CharField(max_length=200) content = TextField() @@ -2368,7 +2632,9 @@ class HelpCommand(BaseModel): created_by_discord_id = CharField(max_length=20) # Discord snowflake ID as string created_at = DateTimeField() updated_at = DateTimeField(null=True) - last_modified_by = CharField(max_length=20, null=True) # Discord snowflake ID as string + last_modified_by = CharField( + max_length=20, null=True + ) # Discord snowflake ID as string # Status and metrics is_active = BooleanField(default=True) @@ -2376,7 +2642,7 @@ class HelpCommand(BaseModel): display_order = IntegerField(default=0) class Meta: - table_name = 'help_commands' + table_name = "help_commands" @staticmethod def get_by_name(name: str, include_inactive: bool = False): @@ -2389,17 +2655,22 @@ class HelpCommand(BaseModel): @staticmethod def search_by_name(partial_name: str, limit: int = 25): """Search help topics by partial name match.""" - return (HelpCommand - .select() - .where((HelpCommand.is_active == True) & - (fn.Lower(HelpCommand.name).contains(partial_name.lower()))) - .order_by(HelpCommand.display_order, HelpCommand.name) - .limit(limit)) + return ( + HelpCommand.select() + .where( + (HelpCommand.is_active == True) + & (fn.Lower(HelpCommand.name).contains(partial_name.lower())) + ) + .order_by(HelpCommand.display_order, HelpCommand.name) + .limit(limit) + ) @staticmethod def get_by_category(category: str, include_inactive: bool = False): """Get help commands by category.""" - query = HelpCommand.select().where(fn.Lower(HelpCommand.category) == category.lower()) + query = HelpCommand.select().where( + fn.Lower(HelpCommand.category) == category.lower() + ) if not include_inactive: query = query.where(HelpCommand.is_active == True) return query.order_by(HelpCommand.display_order, HelpCommand.name) @@ -2407,19 +2678,21 @@ class HelpCommand(BaseModel): @staticmethod def get_all_active(): """Get all active help topics.""" - return (HelpCommand - .select() - .where(HelpCommand.is_active == True) - .order_by(HelpCommand.display_order, HelpCommand.name)) + return ( + HelpCommand.select() + .where(HelpCommand.is_active == True) + .order_by(HelpCommand.display_order, HelpCommand.name) + ) @staticmethod def get_most_viewed(limit: int = 10): """Get most viewed help topics.""" - return (HelpCommand - .select() - .where(HelpCommand.is_active == True) - .order_by(HelpCommand.view_count.desc()) - .limit(limit)) + return ( + HelpCommand.select() + .where(HelpCommand.is_active == True) + .order_by(HelpCommand.view_count.desc()) + .limit(limit) + ) def increment_view_count(self): """Increment view count for this help topic.""" @@ -2469,30 +2742,35 @@ class SeasonBattingStatsView(BaseModel): hbp = IntegerField() sac = IntegerField() ibb = IntegerField() - + class Meta: - table_name = 'season_batting_stats_view' + table_name = "season_batting_stats_view" primary_key = False - + @staticmethod def get_by_season(season): - return SeasonBattingStatsView.select().where(SeasonBattingStatsView.season == season) - + return SeasonBattingStatsView.select().where( + SeasonBattingStatsView.season == season + ) + @staticmethod def get_team_stats(season, team_id): - return (SeasonBattingStatsView.select() - .where(SeasonBattingStatsView.season == season, - SeasonBattingStatsView.player_team_id == team_id)) - + return SeasonBattingStatsView.select().where( + SeasonBattingStatsView.season == season, + SeasonBattingStatsView.player_team_id == team_id, + ) + @staticmethod - def get_top_hitters(season, stat='avg', limit=10, desc=True): + def get_top_hitters(season, stat="avg", limit=10, desc=True): """Get top hitters by specified stat (avg, hr, rbi, ops, etc.)""" stat_field = getattr(SeasonBattingStatsView, stat, SeasonBattingStatsView.avg) order_field = stat_field.desc() if desc else stat_field.asc() - return (SeasonBattingStatsView.select() - .where(SeasonBattingStatsView.season == season) - .order_by(order_field) - .limit(limit)) + return ( + SeasonBattingStatsView.select() + .where(SeasonBattingStatsView.season == season) + .order_by(order_field) + .limit(limit) + ) class SeasonPitchingStats(BaseModel): @@ -2558,24 +2836,33 @@ class SeasonPitchingStats(BaseModel): lob_2outs = FloatField() rbipercent = FloatField() re24 = FloatField() - + class Meta: - table_name = 'seasonpitchingstats' - primary_key = CompositeKey('player', 'season') - + table_name = "seasonpitchingstats" + primary_key = CompositeKey("player", "season") + @staticmethod def get_team_stats(season, team_id): - return (SeasonPitchingStats.select() - .where(SeasonPitchingStats.season == season, - SeasonPitchingStats.player_team_id == team_id)) - + return SeasonPitchingStats.select().where( + SeasonPitchingStats.season == season, + SeasonPitchingStats.player_team_id == team_id, + ) + @staticmethod - def get_top_pitchers(season: Optional[int] = None, stat: str = 'era', limit: Optional[int] = 200, - desc: bool = False, team_id: Optional[int] = None, player_id: Optional[int] = None, - sbaplayer_id: Optional[int] = None, min_outs: Optional[int] = None, offset: int = 0): + def get_top_pitchers( + season: Optional[int] = None, + stat: str = "era", + limit: Optional[int] = 200, + desc: bool = False, + team_id: Optional[int] = None, + player_id: Optional[int] = None, + sbaplayer_id: Optional[int] = None, + min_outs: Optional[int] = None, + offset: int = 0, + ): """ Get top pitchers by specified stat with optional filtering. - + Args: season: Season to filter by (None for all seasons) stat: Stat field to sort by (default: era) @@ -2589,9 +2876,9 @@ class SeasonPitchingStats(BaseModel): """ stat_field = getattr(SeasonPitchingStats, stat, SeasonPitchingStats.era) order_field = stat_field.desc() if desc else stat_field.asc() - + query = SeasonPitchingStats.select().order_by(order_field) - + # Apply filters if season is not None: query = query.where(SeasonPitchingStats.season == season) @@ -2603,13 +2890,13 @@ class SeasonPitchingStats(BaseModel): query = query.where(SeasonPitchingStats.sbaplayer_id == sbaplayer_id) if min_outs is not None: query = query.where(SeasonPitchingStats.outs >= min_outs) - + # Apply pagination if offset > 0: query = query.offset(offset) if limit is not None and limit > 0: query = query.limit(limit) - + return query @@ -2649,28 +2936,37 @@ class SeasonBattingStats(BaseModel): ops = FloatField() woba = FloatField() k_pct = FloatField() - + # Running stats sb = IntegerField() cs = IntegerField() - + class Meta: - table_name = 'seasonbattingstats' - primary_key = CompositeKey('player', 'season') - + table_name = "seasonbattingstats" + primary_key = CompositeKey("player", "season") + @staticmethod def get_team_stats(season, team_id): - return (SeasonBattingStats.select() - .where(SeasonBattingStats.season == season, - SeasonBattingStats.player_team_id == team_id)) - + return SeasonBattingStats.select().where( + SeasonBattingStats.season == season, + SeasonBattingStats.player_team_id == team_id, + ) + @staticmethod - def get_top_hitters(season: Optional[int] = None, stat: str = 'woba', limit: Optional[int] = 200, - desc: bool = True, team_id: Optional[int] = None, player_id: Optional[int] = None, - sbaplayer_id: Optional[int] = None, min_pa: Optional[int] = None, offset: int = 0): + def get_top_hitters( + season: Optional[int] = None, + stat: str = "woba", + limit: Optional[int] = 200, + desc: bool = True, + team_id: Optional[int] = None, + player_id: Optional[int] = None, + sbaplayer_id: Optional[int] = None, + min_pa: Optional[int] = None, + offset: int = 0, + ): """ Get top hitters by specified stat with optional filtering. - + Args: season: Season to filter by (None for all seasons) stat: Stat field to sort by (default: woba) @@ -2683,9 +2979,9 @@ class SeasonBattingStats(BaseModel): """ stat_field = getattr(SeasonBattingStats, stat, SeasonBattingStats.woba) order_field = stat_field.desc() if desc else stat_field.asc() - + query = SeasonBattingStats.select().order_by(order_field) - + # Apply filters if season is not None: query = query.where(SeasonBattingStats.season == season) @@ -2697,13 +2993,13 @@ class SeasonBattingStats(BaseModel): query = query.where(SeasonBattingStats.sbaplayer_id == sbaplayer_id) if min_pa is not None: query = query.where(SeasonBattingStats.pa >= min_pa) - + # Apply pagination if offset > 0: query = query.offset(offset) if limit is not None and limit > 0: query = query.limit(limit) - + return query -- 2.25.1