From 0ace71fbee60ed0ecc32b14f8908652797ca3246 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sun, 19 Feb 2023 21:43:17 -0600 Subject: [PATCH] Initial commit --- db_engine.py | 1811 ++++++++++++ main.py | 7296 ++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 5 + 3 files changed, 9112 insertions(+) create mode 100644 db_engine.py create mode 100644 main.py create mode 100644 requirements.txt diff --git a/db_engine.py b/db_engine.py new file mode 100644 index 0000000..c2a3eaa --- /dev/null +++ b/db_engine.py @@ -0,0 +1,1811 @@ +import copy +import math + +from peewee import * + +from playhouse.shortcuts import model_to_dict + +db = SqliteDatabase( + 'storage/sba_master.db', + pragmas={ + 'journal_mode': 'wal', + 'cache_size': -1 * 64000, + 'synchronous': 0 + } +) + + +""" +Per season updates: + Result: regular_season & post_season - set season length + update_standings - confirm division alignments and records + Standings: recalculate - e_number function, set season length + - wildcard section, set league abbrevs +""" + + +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) + + +def games_back(leader, chaser): + return ((leader.wins - chaser.wins) + (chaser.losses - leader.losses)) / 2 + + +def e_number(leader, chaser): + e_num = 89 - leader.wins - chaser.losses + return e_num if e_num > 0 else 0 + + +class BaseModel(Model): + class Meta: + database = db + + +class Current(BaseModel): + week = IntegerField(default=0) + freeze = BooleanField(default=True) + season = IntegerField() + transcount = IntegerField(default=0) + bstatcount = IntegerField(default=0) + pstatcount = IntegerField(default=0) + bet_week = IntegerField(default=0) + trade_deadline = IntegerField() + pick_trade_start = IntegerField() + pick_trade_end = IntegerField() + playoffs_begin = IntegerField() + injury_count = IntegerField() + + @staticmethod + def latest(): + latest_current = Current.select().order_by(-Current.id).get() + return latest_current + + +class Division(BaseModel): + division_name = CharField() + division_abbrev = CharField() + league_name = CharField(null=True) + league_abbrev = CharField(null=True) + season = IntegerField(default=0) + + def abbrev(self): + 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}' + + def full_name(self): + 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_teams = [team_stan for team_stan in div_query] + div_teams.sort(key=lambda team: win_pct(team), reverse=True) + + # Assign div_gb and e_num + for x in range(len(div_teams)): + # # Used for two playoff teams per divsion + # # Special calculations for the division leader + # if x == 0: + # div_teams[0].div_gb = -games_back(div_teams[0], div_teams[2]) + # div_teams[0].div_e_num = None + # div_teams[0].wc_gb = None + # div_teams[0].wc_e_num = None + # elif x == 1: + # div_teams[1].div_gb = 0 + # div_teams[1].div_e_num = None + # div_teams[1].wc_gb = None + # div_teams[1].wc_e_num = None + # else: + # div_teams[x].div_gb = games_back(div_teams[1], div_teams[x]) + # div_teams[x].div_e_num = e_number(div_teams[1], div_teams[x]) + # Used for one playoff team per division + if x == 0: + div_teams[0].div_gb = -games_back(div_teams[0], div_teams[1]) + div_teams[0].div_e_num = None + div_teams[0].wc_gb = None + div_teams[0].wc_e_num = None + else: + div_teams[x].div_gb = games_back(div_teams[0], div_teams[x]) + div_teams[x].div_e_num = e_number(div_teams[0], div_teams[x]) + + div_teams[x].save() + + @staticmethod + def sort_wildcard(season, league_abbrev): + divisions = Division.select().where(Division.league_abbrev == league_abbrev) + teams_query = Standings.select_season(season).where( + Standings.wc_gb & (Standings.team.division << divisions) + ) + league_teams = [team_stan for team_stan in teams_query] + league_teams.sort(key=lambda team: win_pct(team), reverse=True) + + for x in range(len(league_teams)): + # Special calculations for two wildcard teams + if x == 0: + league_teams[0].wc_gb = -games_back(league_teams[0], league_teams[2]) + league_teams[0].wc_e_num = None + elif x == 1: + league_teams[1].wc_gb = 0 + league_teams[1].wc_e_num = None + else: + league_teams[x].wc_gb = games_back(league_teams[1], league_teams[x]) + league_teams[x].wc_e_num = e_number(league_teams[1], league_teams[x]) + + league_teams[x].save() + + +class Manager(BaseModel): + name = CharField(unique=True) + image = CharField(null=True) + headline = CharField(null=True) + bio = CharField(null=True) + + +class Team(BaseModel): + abbrev = CharField() + sname = CharField() + lname = CharField() + manager_legacy = CharField(null=True) + division_legacy = CharField(null=True) + gmid = IntegerField() + gmid2 = IntegerField(null=True) + manager1 = ForeignKeyField(Manager, null=True) + manager2 = ForeignKeyField(Manager, null=True) + division = ForeignKeyField(Division, null=True) + mascot = CharField(null=True) + stadium = CharField(null=True) + gsheet = CharField(null=True) + thumbnail = CharField(null=True) + color = CharField(null=True) + dice_color = CharField(null=True) + season = IntegerField() + auto_draft = BooleanField() + + @staticmethod + def select_season(num): + return Team.select().where(Team.season == num) + + @staticmethod + def get_by_owner(gmid, season): + team = Team.get_or_none(Team.gmid == gmid, Team.season == season) + if not team: + team = Team.get_or_none(Team.gmid2 == gmid, Team.season == season) + if not team: + return None + return team + + @staticmethod + def get_season(name_or_abbrev, 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) + if not team: + 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) + ) + 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) + ) + 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} + + def get_gms(self): + if self.gmid2: + return [self.gmid, self.gmid2] + else: + return [self.gmid] + + def get_this_week(self): + active_team = Player.select_season(self.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': []} + + combo_pitchers = 0 + for guy in active_team: + 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: + combo_pitchers += 1 + else: + try: + for pos in guy_pos: + active_roster[pos] += 1 + except KeyError: + # This happens for season 1 without player positions listed + pass + + if combo_pitchers > 0: + 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 + combo_pitchers -= delta + + if combo_pitchers > 0: + active_roster['RP'] += combo_pitchers + + 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': []} + + combo_pitchers = 0 + for guy in short_il: + 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: + 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'] + else: + delta = combo_pitchers + short_roster['SP'] += delta + combo_pitchers -= delta + + if combo_pitchers > 0: + short_roster['RP'] += combo_pitchers + + 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': []} + + combo_pitchers = 0 + for guy in long_il: + 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: + 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'] + else: + delta = combo_pitchers + long_roster['SP'] += delta + combo_pitchers -= delta + + if combo_pitchers > 0: + long_roster['RP'] += combo_pitchers + + 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': []} + + combo_pitchers = 0 + for guy in active_team: + 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: + 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) + ) + all_adds = Transaction.select_season(Current.latest().season).where( + (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: + 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 + try: + active_roster['players'].remove(move.player) + except: + 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: + 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) + + if combo_pitchers > 0: + 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 + combo_pitchers -= delta + + if combo_pitchers > 0: + active_roster['RP'] += combo_pitchers + + 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': []} + + combo_pitchers = 0 + for guy in short_il: + 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: + combo_pitchers += 1 + else: + for pos in guy_pos: + short_roster[pos] += 1 + + 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) + ) + all_adds = Transaction.select_season(Current.latest().season).where( + (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: + combo_pitchers -= 1 + else: + for pos in guy_pos: + short_roster[pos] -= 1 + 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) + except: + 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: + 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) + + if combo_pitchers > 0: + 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 + combo_pitchers -= delta + + if combo_pitchers > 0: + short_roster['RP'] += combo_pitchers + + 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': []} + + combo_pitchers = 0 + for guy in long_il: + 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: + combo_pitchers += 1 + else: + for pos in guy_pos: + long_roster[pos] += 1 + + 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) + ) + all_adds = Transaction.select_season(Current.latest().season).where( + (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: + combo_pitchers -= 1 + else: + for pos in guy_pos: + long_roster[pos] -= 1 + 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) + except: + 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: + 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) + + if combo_pitchers > 0: + 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 + combo_pitchers -= delta + + if combo_pitchers > 0: + long_roster['RP'] += combo_pitchers + + 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_home = Result.select(fn.SUM(Result.homescore).alias('runs')).where( + Result.hometeam == self + )[0].runs + runs_scored_away = Result.select(fn.SUM(Result.awayscore).alias('runs')).where( + Result.awayteam == self + )[0].runs + runs_allowed_home = Result.select(fn.SUM(Result.homescore).alias('runs')).where( + Result.awayteam == self + )[0].runs + runs_allowed_away = Result.select(fn.SUM(Result.awayscore).alias('runs')).where( + Result.hometeam == self + )[0].runs + + if not runs_scored_home: + runs_scored_home = 0 + if not runs_scored_away: + runs_scored_away = 0 + if not runs_allowed_home: + runs_allowed_home = 0 + if not runs_allowed_away: + runs_allowed_away = 0 + + runs_scored = runs_scored_home + runs_scored_away + runs_allowed = runs_allowed_home + runs_allowed_away + if runs_allowed == 0: + pythag_win_pct = 0 + else: + 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 = Result.select_season(self.season).where( + (Result.hometeam == self) | (Result.awayteam == self) + ).order_by(-Result.id).limit(8) + + for game in last_games: + if game.homescore > game.awayscore: + if game.hometeam == self: + team_stan.last8_wins += 1 + else: + team_stan.last8_losses += 1 + else: + if game.hometeam == self: + team_stan.last8_losses += 1 + else: + team_stan.last8_wins += 1 + + return team_stan.save() + + +class Result(BaseModel): + week = IntegerField() + game = IntegerField() + awayteam = ForeignKeyField(Team) + hometeam = ForeignKeyField(Team) + awayscore = IntegerField() + homescore = IntegerField() + season = IntegerField() + scorecard_url = CharField(null=True) + + @staticmethod + def regular_season(num): + if num == 1: + return Result.select().where((Result.season == 1) & (Result.week < 21)) + elif num == 2: + return Result.select().where((Result.season == 2) & (Result.week < 19)) + elif num == 3 or num == 4: + return Result.select().where((Result.season == num) & (Result.week < 23)) + else: + return None + + @staticmethod + def post_season(num): + if num == 1: + return Result.select().where((Result.season == 1) & (Result.week >= 21)) + elif num == 2: + return Result.select().where((Result.season == 2) & (Result.week >= 19)) + elif num == 3 or num == 4: + return Result.select().where((Result.season == num) & (Result.week >= 23)) + else: + return None + + @staticmethod + def select_season(num): + return Result.select().where(Result.season == num) + + def update_standings(self): + away_stan = Standings.get_season(self.awayteam) + home_stan = Standings.get_season(self.hometeam) + away_div = Division.get_by_id(self.awayteam.division.id) + home_div = Division.get_by_id(self.hometeam.division.id) + + if self.homescore > self.awayscore: + # - generic w/l & home/away w/l + home_stan.wins += 1 + home_stan.home_wins += 1 + away_stan.losses += 1 + away_stan.away_losses += 1 + + # - update streak wl and num + if home_stan.streak_wl == 'w': + home_stan.streak_num += 1 + else: + home_stan.streak_wl = 'w' + home_stan.streak_num = 1 + + if away_stan.streak_wl == 'l': + away_stan.streak_num += 1 + else: + away_stan.streak_wl = 'l' + away_stan.streak_num = 1 + + # - if 1-run, tally accordingly + if self.homescore == self.awayscore + 1: + home_stan.one_run_wins += 1 + away_stan.one_run_losses += 1 + + # Used for one league with 3 divisions + # - update record v division + # if away_div.division_abbrev == 'BE': + # home_stan.div1_wins += 1 + # elif away_div.division_abbrev == 'DO': + # home_stan.div2_wins += 1 + # else: + # home_stan.div3_wins += 1 + # + # if home_div.division_abbrev == 'BE': + # away_stan.div1_losses += 1 + # elif home_div.division_abbrev == 'DO': + # away_stan.div2_losses += 1 + # else: + # away_stan.div3_losses += 1 + + # Used for two league plus divisions + if away_div.league_abbrev == 'AL': + if away_div.division_abbrev == 'E': + home_stan.div1_wins += 1 + else: + home_stan.div2_wins += 1 + else: + if away_div.division_abbrev == 'E': + home_stan.div3_wins += 1 + else: + home_stan.div4_wins += 1 + + if home_div.league_abbrev == 'AL': + if home_div.division_abbrev == 'E': + away_stan.div1_losses += 1 + else: + away_stan.div2_losses += 1 + else: + if home_div.division_abbrev == 'E': + away_stan.div3_losses += 1 + else: + away_stan.div4_losses += 1 + + # - adjust run_diff + home_stan.run_diff += self.homescore - self.awayscore + away_stan.run_diff -= self.homescore - self.awayscore + else: + # - generic w/l & home/away w/l + home_stan.losses += 1 + home_stan.home_losses += 1 + away_stan.wins += 1 + away_stan.away_wins += 1 + + # - update streak wl and num + if home_stan.streak_wl == 'l': + home_stan.streak_num += 1 + else: + home_stan.streak_wl = 'l' + home_stan.streak_num = 1 + + if away_stan.streak_wl == 'w': + away_stan.streak_num += 1 + else: + away_stan.streak_wl = 'w' + away_stan.streak_num = 1 + + # - if 1-run, tally accordingly + if self.awayscore == self.homescore + 1: + home_stan.one_run_losses += 1 + away_stan.one_run_wins += 1 + + # Used for one league with 3 divisions + # - update record v division + # if away_div.division_abbrev == 'BE': + # home_stan.div1_losses += 1 + # elif away_div.division_abbrev == 'DO': + # home_stan.div2_losses += 1 + # else: + # home_stan.div3_losses += 1 + # + # if home_div.division_abbrev == 'BE': + # away_stan.div1_wins += 1 + # elif home_div.division_abbrev == 'DO': + # away_stan.div2_wins += 1 + # else: + # away_stan.div3_wins += 1 + + # Used for two league plus divisions + if away_div.league_abbrev == 'AL': + if away_div.division_abbrev == 'E': + home_stan.div1_losses += 1 + else: + home_stan.div2_losses += 1 + else: + if away_div.division_abbrev == 'E': + home_stan.div3_losses += 1 + else: + home_stan.div4_losses += 1 + + if home_div.league_abbrev == 'AL': + if home_div.division_abbrev == 'E': + away_stan.div1_wins += 1 + else: + away_stan.div2_wins += 1 + else: + if home_div.division_abbrev == 'E': + away_stan.div3_wins += 1 + else: + away_stan.div4_wins += 1 + + # - adjust run_diff + home_stan.run_diff -= self.awayscore - self.homescore + away_stan.run_diff += self.awayscore - self.homescore + + home_stan.save() + away_stan.save() + + +class Player(BaseModel): + name = CharField() + wara = FloatField() + image = CharField() + image2 = CharField(null=True) + team = ForeignKeyField(Team) + season = IntegerField() + pitcher_injury = IntegerField(null=True) + pos_1 = CharField() + pos_2 = CharField(null=True) + pos_3 = CharField(null=True) + pos_4 = CharField(null=True) + pos_5 = CharField(null=True) + pos_6 = CharField(null=True) + pos_7 = CharField(null=True) + pos_8 = CharField(null=True) + last_game = CharField(null=True) + last_game2 = CharField(null=True) + il_return = CharField(null=True) + demotion_week = IntegerField(null=True) + headshot = CharField(null=True) + vanity_card = CharField(null=True) + strat_code = CharField(null=True) + bbref_id = CharField(null=True) + injury_rating = CharField(null=True) + + @staticmethod + def select_season(num): + return Player.select().where(Player.season == num) + + @staticmethod + def get_season(name, num): + player = None + try: + player = Player.get(fn.Lower(Player.name) == name.lower(), Player.season == num) + except Exception as e: + print(f'**Error** (db_engine player): {e}') + finally: + return player + + def get_positions(self): + """ + Params: None + Return: List of positions (ex ['1b', '3b']) + """ + pos_list = [] + if self.pos_1: + pos_list.append(self.pos_1) + if self.pos_2: + pos_list.append(self.pos_2) + if self.pos_3: + pos_list.append(self.pos_3) + if self.pos_4: + pos_list.append(self.pos_4) + if self.pos_5: + pos_list.append(self.pos_5) + if self.pos_6: + pos_list.append(self.pos_6) + if self.pos_7: + pos_list.append(self.pos_7) + if self.pos_8: + pos_list.append(self.pos_8) + + return pos_list + + +class Schedule(BaseModel): + week = IntegerField() + awayteam = ForeignKeyField(Team) + hometeam = ForeignKeyField(Team) + gamecount = IntegerField() + season = IntegerField() + + @staticmethod + def select_season(season): + return Schedule.select().where(Schedule.season == season) + + +class Transaction(BaseModel): + week = IntegerField() + player = ForeignKeyField(Player) + oldteam = ForeignKeyField(Team) + newteam = ForeignKeyField(Team) + season = IntegerField() + moveid = IntegerField() + cancelled = BooleanField(default=False) + frozen = BooleanField(default=False) + + @staticmethod + def select_season(num): + return Transaction.select().where(Transaction.season == num) + + +class BattingStat(BaseModel): + player = ForeignKeyField(Player) + team = ForeignKeyField(Team) + pos = CharField() + pa = IntegerField() + ab = IntegerField() + run = IntegerField() + hit = IntegerField() + rbi = IntegerField() + double = IntegerField() + triple = IntegerField() + hr = IntegerField() + bb = IntegerField() + so = IntegerField() + hbp = IntegerField() + sac = IntegerField() + ibb = IntegerField() + gidp = IntegerField() + sb = IntegerField() + cs = IntegerField() + bphr = IntegerField() + bpfo = IntegerField() + bp1b = IntegerField() + bplo = IntegerField() + xba = IntegerField() + xbt = IntegerField() + xch = IntegerField() + xhit = IntegerField() + error = IntegerField() + pb = IntegerField() + sbc = IntegerField() + csc = IntegerField() + roba = IntegerField() + robs = IntegerField() + raa = IntegerField() + rto = IntegerField() + week = IntegerField() + game = IntegerField() + season = IntegerField() + + @staticmethod + def combined_season(season): + """ + Params: season, integer (season number), optional + Return: ModelSelect object for season + """ + return BattingStat.select().where(BattingStat.season == season) + + @staticmethod + def regular_season(season): + """ + Params: num, integer (season number) + Return: ModelSelect object for season's regular season + """ + if season == 1: + 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))\ + .order_by(BattingStat.week) + elif season > 2: + return BattingStat.select().where((BattingStat.season == season) & (BattingStat.week < 23))\ + .order_by(BattingStat.week) + else: + return None + + @staticmethod + def post_season(season): + """ + Params: num, integer (season number) + Return: ModelSelect object for season's post season + """ + if season == 1: + return BattingStat.select().where((BattingStat.season == 1) & (BattingStat.week >= 21)) + elif season == 2: + return BattingStat.select().where((BattingStat.season == 2) & (BattingStat.week >= 19)) + elif season > 2: + 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) + + 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, + # '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['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 * .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 + + 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) + + 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, + # '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, + } + + 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 + + return total + + +class PitchingStat(BaseModel): + player = ForeignKeyField(Player) + team = ForeignKeyField(Team) + ip = FloatField() + hit = FloatField() + run = FloatField() + erun = FloatField() + so = FloatField() + bb = FloatField() + hbp = FloatField() + wp = FloatField() + balk = FloatField() + hr = FloatField() + ir = FloatField() + irs = FloatField() + gs = FloatField() + win = FloatField() + loss = FloatField() + hold = FloatField() + sv = FloatField() + bsv = FloatField() + week = IntegerField() + game = IntegerField() + season = IntegerField() + + @staticmethod + def select_season(season): + return PitchingStat.select().where(PitchingStat.season == season) + + @staticmethod + def regular_season(season): + if season == 1: + 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))\ + .order_by(PitchingStat.week) + elif season > 2: + 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))\ + .order_by(PitchingStat.week) + elif season == 2: + 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))\ + .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) + + 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, + } + + if total['ip']: + total['era'] = (total['erun'] * 9) / total['ip'] + + total['whip'] = (total['bb'] + total['hit']) / total['ip'] + + if total['win'] + total['loss'] > 0: + total['wl%'] = total['win'] / (total['win'] + total['loss']) + + return total + + +class Standings(BaseModel): + team = ForeignKeyField(Team) + wins = IntegerField(default=0) + losses = IntegerField(default=0) + run_diff = IntegerField(default=0) + div_gb = FloatField(default=0.0, null=True) + div_e_num = IntegerField(default=0, null=True) + wc_gb = FloatField(default=99.0, null=True) + wc_e_num = IntegerField(default=99, null=True) + home_wins = IntegerField(default=0) + home_losses = IntegerField(default=0) + away_wins = IntegerField(default=0) + away_losses = IntegerField(default=0) + last8_wins = IntegerField(default=0) + last8_losses = IntegerField(default=0) + streak_wl = CharField(default='w') + streak_num = IntegerField(default=0) + one_run_wins = IntegerField(default=0) + one_run_losses = IntegerField(default=0) + pythag_wins = IntegerField(default=0) + pythag_losses = IntegerField(default=0) + div1_wins = IntegerField(default=0) + div1_losses = IntegerField(default=0) + div2_wins = IntegerField(default=0) + div2_losses = IntegerField(default=0) + div3_wins = IntegerField(default=0) + div3_losses = IntegerField(default=0) + div4_wins = IntegerField(default=0) + div4_losses = IntegerField(default=0) + + @staticmethod + def select_season(season): + return Standings.select().join(Team).where(Standings.team.season == season) + + @staticmethod + def get_season(team): + return Standings.get_or_none(Standings.team == team) + + @staticmethod + def recalculate(season, full_wipe=True): + all_teams = Team.select_season(season).where(Team.division) + if full_wipe: + # Wipe existing data + delete_lines = Standings.select_season(season) + for line in delete_lines: + line.delete_instance() + + # Recreate current season Standings objects + create_teams = [Standings(team=team) for team in all_teams] + with db.atomic(): + Standings.bulk_create(create_teams) + + # Iterate through each individual result + for game in Result.select_season(season).where(Result.week <= 22): + # tally win and loss for each standings object + game.update_standings() + + # Set pythag record and iterate through last 8 games for last8 record + for team in all_teams: + team.run_pythag_last8() + + # Pull each division at a time and sort by win pct + for division in Division.select().where(Division.season == season): + division.sort_division(season) + + # Pull each league (filter by not null wc_gb) and sort by win pct + + # # For one league: + # Division.sort_wildcard(season, 'SBa') + + # For two leagues + Division.sort_wildcard(season, 'AL') + Division.sort_wildcard(season, 'NL') + + +class BattingCareer(BaseModel): + name = CharField() + pa = FloatField(default=0) + ab = FloatField(default=0) + run = FloatField(default=0) + hit = FloatField(default=0) + rbi = FloatField(default=0) + double = FloatField(default=0) + triple = FloatField(default=0) + hr = FloatField(default=0) + bb = FloatField(default=0) + so = FloatField(default=0) + hbp = FloatField(default=0) + sac = FloatField(default=0) + ibb = FloatField(default=0) + gidp = FloatField(default=0) + sb = FloatField(default=0) + cs = FloatField(default=0) + bphr = FloatField(default=0) + bpfo = FloatField(default=0) + bp1b = FloatField(default=0) + bplo = FloatField(default=0) + xba = FloatField(default=0) + xbt = FloatField(default=0) + game = FloatField(default=0) + + @staticmethod + def recalculate(): + # Wipe existing data + delete_lines = BattingCareer.select() + for line in delete_lines: + 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) + if not this_career: + this_career = BattingCareer(name=this_season.player.name) + this_career.save() + + this_career.pa += this_season.pa + this_career.ab += this_season.ab + this_career.run += this_season.run + this_career.hit += this_season.hit + this_career.rbi += this_season.rbi + this_career.double += this_season.double + this_career.triple += this_season.triple + this_career.hr += this_season.hr + this_career.bb += this_season.bb + this_career.so += this_season.so + this_career.hbp += this_season.hbp + this_career.sac += this_season.sac + this_career.ibb += this_season.ibb + this_career.gidp += this_season.gidp + this_career.sb += this_season.sb + this_career.cs += this_season.cs + this_career.bphr += this_season.bphr + this_career.bpfo += this_season.bpfo + this_career.bp1b += this_season.bp1b + this_career.bplo += this_season.bplo + this_career.xba += this_season.xba + this_career.xbt += this_season.xbt + this_career.save() + + +class PitchingCareer(BaseModel): + name = CharField() + ip = FloatField(default=0) + hit = FloatField(default=0) + run = FloatField(default=0) + erun = FloatField(default=0) + so = FloatField(default=0) + bb = FloatField(default=0) + hbp = FloatField(default=0) + wp = FloatField(default=0) + balk = FloatField(default=0) + hr = FloatField(default=0) + ir = FloatField(default=0) + irs = FloatField(default=0) + gs = FloatField(default=0) + win = FloatField(default=0) + loss = FloatField(default=0) + hold = FloatField(default=0) + sv = FloatField(default=0) + bsv = FloatField(default=0) + game = FloatField(default=0) + + @staticmethod + def recalculate(): + # Wipe existing data + delete_lines = PitchingCareer.select() + for line in delete_lines: + 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) + if not this_career: + this_career = PitchingCareer(name=this_season.player.name) + this_career.save() + + this_career.ip += this_season.ip + this_career.hit += this_season.hit + this_career.run += this_season.run + this_career.erun += this_season.erun + this_career.so += this_season.so + this_career.bb += this_season.bb + this_career.hbp += this_season.hbp + this_career.wp += this_season.wp + this_career.balk += this_season.balk + this_career.hr += this_season.hr + this_career.ir += this_season.ir + this_career.irs += this_season.irs + this_career.gs += this_season.gs + this_career.win += this_season.win + this_career.loss += this_season.loss + this_career.hold += this_season.hold + this_career.sv += this_season.sv + this_career.bsv += this_season.bsv + this_career.save() + + +class FieldingCareer(BaseModel): + name = CharField() + pos = CharField() + xch = IntegerField(default=0) + xhit = IntegerField(default=0) + error = IntegerField(default=0) + pb = IntegerField(default=0) + sbc = IntegerField(default=0) + csc = IntegerField(default=0) + roba = IntegerField(default=0) + robs = IntegerField(default=0) + raa = IntegerField(default=0) + rto = IntegerField(default=0) + game = IntegerField(default=0) + + @staticmethod + def recalculate(): + # Wipe existing data + delete_lines = FieldingCareer.select() + for line in delete_lines: + line.delete_instance() + + # For each seasonstat, find career or create new and increment + 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 + ) + if not this_career: + this_career = FieldingCareer(name=this_season.player.name, pos=this_season.pos) + this_career.save() + + this_career.xch += this_season.xch + this_career.xhit += this_season.xhit + this_career.error += this_season.error + this_career.pb += this_season.pb + this_career.sbc += this_season.sbc + this_career.csc += this_season.csc + this_career.roba += this_season.roba + this_career.robs += this_season.robs + this_career.raa += this_season.raa + this_career.rto += this_season.rto + this_career.save() + + +class BattingSeason(BaseModel): + player = ForeignKeyField(Player) + season = IntegerField() + season_type = CharField(default='Regular') + career = ForeignKeyField(BattingCareer, null=True) + pa = FloatField(default=0) + ab = FloatField(default=0) + run = FloatField(default=0) + hit = FloatField(default=0) + rbi = FloatField(default=0) + double = FloatField(default=0) + triple = FloatField(default=0) + hr = FloatField(default=0) + bb = FloatField(default=0) + so = FloatField(default=0) + hbp = FloatField(default=0) + sac = FloatField(default=0) + ibb = FloatField(default=0) + gidp = FloatField(default=0) + sb = FloatField(default=0) + cs = FloatField(default=0) + bphr = FloatField(default=0) + bpfo = FloatField(default=0) + bp1b = FloatField(default=0) + bplo = FloatField(default=0) + xba = FloatField(default=0) + xbt = FloatField(default=0) + game = FloatField(default=0) + + @staticmethod + def select_season(season): + return BattingSeason.select().where(BattingSeason.season == season) + + # @staticmethod + # def recalculate(season, manager_id): + # # Wipe existing data + # delete_lines = BattingSeason.select_season(season) + # for line in delete_lines: + # line.delete_instance() + # + # # For each battingstat, find season or create new and increment + # for line in BattingStat.select().where( + # (BattingStat.season == season) & (BattingStat.player.team.manager1 == manager_id) + # ): + # if line.season == 1: + # s_type = 'Regular' if line.week < 21 else 'Post' + # elif line.season == 2: + # s_type = 'Regular' if line.week < 19 else 'Post' + # else: + # s_type = 'Regular' if line.week < 23 else 'Post' + # + # this_season = BattingSeason.get_or_none(player=line.player, season_type=s_type) + # if not this_season: + # this_season = BattingSeason(player=line.player, season_type=s_type, season=line.season) + # this_season.save() + # + # this_season.pa += line.pa + # this_season.ab += line.ab + # this_season.run += line.run + # this_season.hit += line.hit + # this_season.rbi += line.rbi + # this_season.double += line.double + # this_season.triple += line.triple + # this_season.hr += line.hr + # this_season.bb += line.bb + # this_season.so += line.so + # this_season.hbp += line.hbp + # this_season.sac += line.sac + # this_season.ibb += line.ibb + # this_season.gidp += line.gidp + # this_season.sb += line.sb + # this_season.cs += line.cs + # this_season.save() + + def recalculate(self): + self.pa = 0 + self.ab = 0 + self.run = 0 + self.hit = 0 + self.rbi = 0 + self.double = 0 + self.triple = 0 + self.hr = 0 + self.bb = 0 + self.so = 0 + self.hbp = 0 + self.sac = 0 + self.ibb = 0 + self.gidp = 0 + self.sb = 0 + self.cs = 0 + self.bphr = 0 + self.bpfo = 0 + self.bp1b = 0 + self.bplo = 0 + self.xba = 0 + self.xbt = 0 + self.game = 0 + + 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) + for line in all_stats: + self.pa += line.pa + self.ab += line.ab + self.run += line.run + self.hit += line.hit + self.rbi += line.rbi + self.double += line.double + self.triple += line.triple + self.hr += line.hr + self.bb += line.bb + self.so += line.so + self.hbp += line.hbp + self.sac += line.sac + self.ibb += line.ibb + self.gidp += line.gidp + self.sb += line.sb + self.cs += line.cs + self.bphr += line.bphr + self.bpfo += line.bpfo + self.bp1b += line.bp1b + self.bplo += line.bplo + self.xba += line.xba + self.xbt += line.xbt + self.game += 1 + + self.save() + return all_stats.count() + + +class PitchingSeason(BaseModel): + player = ForeignKeyField(Player) + season = IntegerField() + season_type = CharField(default='Regular') + career = ForeignKeyField(PitchingCareer, null=True) + ip = FloatField(default=0) + hit = FloatField(default=0) + run = FloatField(default=0) + erun = FloatField(default=0) + so = FloatField(default=0) + bb = FloatField(default=0) + hbp = FloatField(default=0) + wp = FloatField(default=0) + balk = FloatField(default=0) + hr = FloatField(default=0) + ir = FloatField(default=0) + irs = FloatField(default=0) + gs = FloatField(default=0) + win = FloatField(default=0) + loss = FloatField(default=0) + hold = FloatField(default=0) + sv = FloatField(default=0) + bsv = FloatField(default=0) + game = FloatField(default=0) + + @staticmethod + def select_season(season): + return PitchingSeason.select().where(PitchingSeason.season == season) + + # @staticmethod + # def recalculate(season, manager_id): + # # Wipe existing data + # delete_lines = PitchingSeason.select_season(season) + # for line in delete_lines: + # line.delete_instance() + # + # # For each pitchingstat, find season or create new and increment + # for line in PitchingStat.select().where( + # (PitchingStat.season == season) & (PitchingStat.player.team.manager1 == manager_id) + # ): + # if line.season == 1: + # s_type = 'Regular' if line.week < 21 else 'Post' + # elif line.season == 2: + # s_type = 'Regular' if line.week < 19 else 'Post' + # else: + # s_type = 'Regular' if line.week < 23 else 'Post' + # + # this_season = PitchingSeason.get_or_none(player=line.player, season_type=s_type) + # if not this_season: + # this_season = PitchingSeason(player=line.player, season_type=s_type, season=line.season) + # this_season.save() + # + # this_season.ip += line.ip + # this_season.hit += line.hit + # this_season.run += line.run + # this_season.erun += line.erun + # this_season.so += line.so + # this_season.bb += line.bb + # this_season.hbp += line.hbp + # this_season.wp += line.wp + # this_season.balk += line.balk + # this_season.hr += line.hr + # this_season.gs += line.gs + # this_season.win += line.win + # this_season.loss += line.loss + # this_season.hold += line.hold + # this_season.sv += line.sv + # this_season.bsv += line.bsv + # this_season.game += 1 + # this_season.save() + + def recalculate(self): + self.ip = 0 + self.hit = 0 + self.run = 0 + self.erun = 0 + self.so = 0 + self.bb = 0 + self.hbp = 0 + self.wp = 0 + self.balk = 0 + self.hr = 0 + self.ir = 0 + self.irs = 0 + self.gs = 0 + self.win = 0 + self.loss = 0 + self.hold = 0 + self.sv = 0 + self.bsv = 0 + self.game = 0 + + 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) + for line in all_stats: + self.ip += line.ip + self.hit += line.hit + self.run += line.run + self.erun += line.erun + self.so += line.so + self.bb += line.bb + self.hbp += line.hbp + self.wp += line.wp + self.balk += line.balk + self.hr += line.hr + self.ir += line.ir + self.irs += line.irs + self.gs += line.gs + self.win += line.win + self.loss += line.loss + self.hold += line.hold + self.sv += line.sv + self.bsv += line.bsv + self.game += 1 + + self.save() + return all_stats.count() + + +class FieldingSeason(BaseModel): + player = ForeignKeyField(Player) + season = IntegerField() + season_type = CharField(default='Regular') + pos = CharField() + career = ForeignKeyField(FieldingCareer, null=True) + xch = IntegerField(default=0) + xhit = IntegerField(default=0) + error = IntegerField(default=0) + pb = IntegerField(default=0) + sbc = IntegerField(default=0) + csc = IntegerField(default=0) + roba = IntegerField(default=0) + robs = IntegerField(default=0) + raa = IntegerField(default=0) + rto = IntegerField(default=0) + game = IntegerField(default=0) + + @staticmethod + def select_season(season): + return FieldingSeason.select().where(FieldingSeason.season == season) + + # @staticmethod + # def recalculate(season, manager_id): + # # Wipe existing data + # delete_lines = FieldingSeason.select() + # for line in delete_lines: + # line.delete_instance() + # + # # players = Player.select_season(season).where(Player.team) + # + # # For each battingstat, find season or create new and increment + # for line in BattingStat.select().join(Player).join(Team).where( + # (BattingStat.season == season) & (BattingStat.player.team.manager1 == manager_id) + # ): + # if line.season == 1: + # s_type = 'Regular' if line.week < 21 else 'Post' + # elif line.season == 2: + # s_type = 'Regular' if line.week < 19 else 'Post' + # else: + # s_type = 'Regular' if line.week < 23 else 'Post' + # + # this_season = BattingSeason.get_or_none(player=line.player, season_type=s_type, pos=line.pos) + # if not this_season: + # this_season = BattingSeason(player=line.player, season_type=s_type, pos=line.pos, season=line.season) + # this_season.save() + # + # this_season.xch += line.xch + # this_season.xhit += line.xhit + # this_season.error += line.error + # this_season.pb += line.pb + # this_season.sbc += line.sbc + # this_season.csc += line.csc + # this_season.game += 1 + # this_season.save() + + def recalculate(self): + self.xch = 0 + self.xhit = 0 + self.error = 0 + self.pb = 0 + self.sbc = 0 + self.csc = 0 + self.roba = 0 + self.robs = 0 + self.raa = 0 + self.rto = 0 + self.game = 0 + + if self.season_type == 'Regular': + all_stats = BattingStat.regular_season(self.season).where( + (BattingStat.player == self.player) & (BattingStat.pos == self.pos) + ) + else: + all_stats = BattingStat.post_season(self.season).where( + (BattingStat.player == self.player) & (BattingStat.pos == self.pos) + ) + + for line in all_stats: + self.xch += line.xch + self.xhit += line.xhit + self.error += line.error + self.pb += line.pb + self.sbc += line.sbc + self.csc += line.csc + self.roba += line.roba + self.robs += line.robs + self.raa += line.raa + self.rto += line.rto + self.game += 1 + + self.save() + return all_stats.count() + + +class DraftPick(BaseModel): + overall = IntegerField(null=True) + round = IntegerField() + origowner = ForeignKeyField(Team) + owner = ForeignKeyField(Team) + season = IntegerField() + player = ForeignKeyField(Player, null=True) + + @staticmethod + def select_season(num): + return DraftPick.select().where(DraftPick.season == num) + + @staticmethod + def get_season(team, rd, num): + return DraftPick.get(DraftPick.season == num, DraftPick.origowner == team, DraftPick.round == rd) + + +class DraftData(BaseModel): + currentpick = IntegerField() + timer = BooleanField() + pick_deadline = DateTimeField(null=True) + result_channel = IntegerField(null=True) + ping_channel = IntegerField(null=True) + pick_minutes = IntegerField(null=True) + + +class Award(BaseModel): + name = CharField() + season = IntegerField() + timing = CharField(default="In-Season") + manager1 = ForeignKeyField(Manager, null=True) + manager2 = ForeignKeyField(Manager, null=True) + player = ForeignKeyField(Player, null=True) + team = ForeignKeyField(Team, null=True) + image = CharField(null=True) + + +class DiceRoll(BaseModel): + season = IntegerField(default=Current.latest().season) + week = IntegerField(default=Current.latest().week) + team = ForeignKeyField(Team, null=True) + roller = IntegerField() + dsix = IntegerField(null=True) + twodsix = IntegerField(null=True) + threedsix = IntegerField(null=True) + dtwenty = IntegerField(null=True) + + +class DraftList(BaseModel): + season = IntegerField() + team = ForeignKeyField(Team) + rank = IntegerField() + player = ForeignKeyField(Player) + + +# class Streak(BaseModel): +# player = ForeignKeyField(Player) +# streak_type = CharField() +# start_season = IntegerField() +# start_week = IntegerField() +# start_game = IntegerField() +# end_season = IntegerField() +# end_week = IntegerField() +# end_game = IntegerField() +# game_length = IntegerField() +# active = BooleanField() +# +# def recalculate(self): +# # Pitcher streaks +# if self.streak_type in ['win', 'loss', 'save', 'scoreless']: +# all_stats = PitchingStat.select_season(self.start_season).where( +# (PitchingStat.player == self.player) & (PitchingStat.week >= self.start_week) +# ) +# sorted_stats = sorted(all_stats, key=lambda x: f'{x.season:0>2}-{x.week:0>2}-{x.game:}') +# +# for line in sorted_stats: diff --git a/main.py b/main.py new file mode 100644 index 0000000..3615448 --- /dev/null +++ b/main.py @@ -0,0 +1,7296 @@ +from db_engine import * + +import datetime +import logging +import os + +from typing import Optional, List +from fastapi import FastAPI, HTTPException, Depends, Response +from fastapi.security import OAuth2PasswordBearer +import pydantic + +from playhouse.shortcuts import model_to_dict +from pandas import DataFrame + +# TODO: remove block comments from GET /schedules and /transactions + +date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}' +log_level = logging.INFO if os.environ.get('LOG_LEVEL') == 'INFO' else 'WARN' +logging.basicConfig( + filename=f'logs/{date}.log', + format='%(asctime)s - database - %(levelname)s - %(message)s', + level=log_level +) +SEASON_DEFAULT = 7 + +db.create_tables([Current, Division, Manager, Team, Result, Player, Schedule, Transaction, BattingStat, PitchingStat, + Standings, BattingCareer, PitchingCareer, FieldingCareer, Manager, Award, DiceRoll, DraftList]) +db.close() + +app = FastAPI() + +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") + + +def valid_token(token): + if token == os.environ.get('API_TOKEN'): + return True + else: + return False + + +class PlayerPydantic(pydantic.BaseModel): + name: str + wara: float + image: str + image2: Optional[str] = None + team_id: int + season: int + pitcher_injury: Optional[int] = None + pos_1: str + pos_2: Optional[str] = None + pos_3: Optional[str] = None + pos_4: Optional[str] = None + pos_5: Optional[str] = None + pos_6: Optional[str] = None + pos_7: Optional[str] = None + pos_8: Optional[str] = None + last_game: Optional[str] = None + last_game2: Optional[str] = None + il_return: Optional[str] = None + demotion_week: Optional[int] = None + strat_code: Optional[str] = None + bbref_id: Optional[str] = None + injury_rating: Optional[str] = None + + +class PlayerModel(pydantic.BaseModel): + players: List[PlayerPydantic] + + +class ResultModel(pydantic.BaseModel): + week: int + game: int + away_team_id: int + home_team_id: int + away_score: int + home_score: int + season: int + scorecard_url: Optional[str] = None + + +class BatStat(pydantic.BaseModel): + player_id: int + team_id: int + pos: str + pa: int + ab: int + run: int + hit: int + rbi: int + double: int + triple: int + hr: int + bb: int + so: int + hbp: int + sac: int + ibb: int + gidp: int + sb: int + cs: int + bphr: int + bpfo: int + bp1b: int + bplo: int + xba: int + xbt: int + xch: int + xhit: int + error: int + pb: int + sbc: int + csc: int + roba: int + robs: int + raa: int + rto: int + week: int + game: int + season: int + + +class BattingStatModel(pydantic.BaseModel): + count: int + stats: List[BatStat] + + +class PitStat(pydantic.BaseModel): + player_id: int + team_id: int + ip: float + hit: int + run: int + erun: int + so: int + bb: int + hbp: int + wp: int + balk: int + hr: int + gs: int + win: int + loss: int + hold: int + sv: int + bsv: int + ir: int + irs: int + week: int + game: int + season: int + + +class PitchingStatModel(pydantic.BaseModel): + count: int + stats: List[PitStat] + + +class GameModel(pydantic.BaseModel): + season: int + week: int + away_team_id: int + home_team_id: int + game_num: int + + +class TransactionPydantic(pydantic.BaseModel): + week: int + player_id: int + oldteam_id: int + newteam_id: int + season: int + moveid: str + cancelled: Optional[bool] = False + frozen: Optional[bool] = False + + +class TransModel(pydantic.BaseModel): + count: int + moves: List[TransactionPydantic] + + +class DraftPickPydantic(pydantic.BaseModel): + overall: Optional[int] = None + round: int + origowner_id: int + owner_id: Optional[int] = None + season: int + player_id: Optional[int] = None + + +class DraftPickModel(pydantic.BaseModel): + picks: List[DraftPickPydantic] + + +class ManagerModel(pydantic.BaseModel): + name: str + image: Optional[str] = None + headline: Optional[str] = None + bio: Optional[str] = None + + +class AwardPydantic(pydantic.BaseModel): + name: str + season: int + timing: Optional[str] = "In-Season" + manager1_id: Optional[int] = None + manager2_id: Optional[int] = None + player_id: Optional[int] = None + team_id: Optional[int] = None + image: Optional[str] = None + + +class AwardModel(pydantic.BaseModel): + count: int + awards: List[AwardPydantic] + + +class DiceRollPydantic(pydantic.BaseModel): + season: int + week: int + team_id: int + roller: int + dsix: Optional[int] + twodsix: Optional[int] + threedsix: Optional[int] + dtwenty: Optional[int] + + +class DiceModel(pydantic.BaseModel): + count: int + rolls: List[DiceRollPydantic] + + +class DraftListPydantic(pydantic.BaseModel): + season: int + team_id: int + rank: int + player_id: int + + +class DraftListModel(pydantic.BaseModel): + count: int + draft_list: List[DraftListPydantic] + + +class SchedulePydantic(pydantic.BaseModel): + week: int + away_team_id: int + home_team_id: int + game_count: int + season: int + + +class ScheduleModel(pydantic.BaseModel): + count: int + schedules: List[SchedulePydantic] + + +class TeamModel(pydantic.BaseModel): + abbrev: str + sname: str + lname: str + gmid: Optional[int] = None + gmid2: Optional[int] = None + manager1_id: Optional[int] = None + manager2_id: Optional[int] = None + division_id: Optional[int] = None + stadium: Optional[str] = None + thumbnail: Optional[str] = None + color: Optional[str] = None + dice_color: Optional[str] = None + season: int + + +class CurrentModel(pydantic.BaseModel): + week: Optional[int] = 0 + freeze: Optional[bool] = False + season: int + transcount: Optional[int] = 0 + bstatcount: Optional[int] = 0 + pstatcount: Optional[int] = 0 + bet_week: Optional[int] = 0 + trade_deadline: int + pick_trade_start: int + pick_trade_end: int + playoffs_begin: int + injury_count: Optional[int] = 0 + + +@app.get('/api/v1/current') +async def v1_current(season: Optional[int] = None, return_type: Optional[str] = 'json'): + if season: + current = Current.get_or_none(season=season) + else: + current = Current.latest() + + if return_type == 'csv': + current_list = [ + ['season', 'week', 'trade deadline', 'pick trade start', 'pick trade end', 'playoffs begin', 'injury count'], + [current.season, current.week, current.trade_deadline, current.pick_trade_start, current.pick_trade_end, + current.playoffs_begin, current.injury_count] + ] + return_current = DataFrame(current_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_current, media_type='text/csv') + else: + db.close() + return model_to_dict(current) + + +@app.patch('/api/v1/current') +async def v1_current_patch( + season: Optional[int] = SEASON_DEFAULT, week: Optional[int] = None, freeze: Optional[bool] = None, + transcount: Optional[int] = None, bstatcount: Optional[int] = None, pstatcount: Optional[int] = None, + bet_week: Optional[int] = None, trade_deadline: Optional[int] = None, pick_trade_start: Optional[int] = None, + pick_trade_end: Optional[int] = None, injury_count: Optional[int] = None, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post current') + current = Current.get_or_none(season=season) + if not current: + db.close() + raise HTTPException(status_code=404, detail=f'Current for season {season} not found') + + if week is not None: + current.week = week + if freeze is not None: + current.freeze = freeze + if transcount is not None: + current.transcount = transcount + if bstatcount is not None: + current.bstatcount = bstatcount + if pstatcount is not None: + current.pstatcount = pstatcount + if bet_week is not None: + current.bet_week = bet_week + if trade_deadline is not None: + current.trade_deadline = trade_deadline + if pick_trade_start is not None: + current.pick_trade_start = pick_trade_start + if pick_trade_end is not None: + current.pick_trade_end = pick_trade_end + if injury_count is not None: + current.injury_count = injury_count + + current.save() + db.close() + return model_to_dict(current) + + +@app.post('/api/v1/current') +async def v1_current_post(current: CurrentModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post current') + + this_current = Current( + week=current.week, + freeze=current.freeze, + season=current.season, + transcount=current.transcount, + bstatcount=current.bstatcount, + pstatcount=current.pstatcount, + bet_week=current.bet_week, + trade_deadline=current.trade_deadline, + pick_trade_start=current.pick_trade_start, + pick_trade_end=current.pick_trade_end, + playoffs_begin=current.playoffs_begin, + injury_count=current.injury_count + ) + + saved = this_current.save() + db.close() + if saved == 1: + return model_to_dict(this_current) + else: + raise HTTPException(status_code=418, detail='Well slap my ass and call me a teapot. I could not post that.') + + +@app.get('/api/v1/teams') +async def v1_teams_get( + season: Optional[int] = None, owner_id: Optional[int] = None, manager_id: Optional[int] = None, + active_only: Optional[bool] = False, return_type: Optional[str] = 'json', csv: Optional[bool] = False): + """ + Param: season: int + Param: team_abbrev: string + Param: owner_id: int + """ + if season: + all_teams = Team.select_season(season) + else: + all_teams = Team.select() + + if manager_id: + print(f'query: {all_teams}') + try: + this_manager = Manager.get_by_id(manager_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Manager ID {manager_id} not found') + + all_teams = all_teams.where( + (Team.manager1 == this_manager) | (Team.manager2 == this_manager) + ).order_by(-Team.season) + + if owner_id: + all_teams = all_teams.where((Team.gmid == owner_id) | (Team.gmid2 == owner_id)) + + if active_only: + all_teams = all_teams.where( + ~(Team.abbrev.endswith('IL')) & ~(Team.abbrev.endswith('MiL')) + ) + + if all_teams.count() == 0: + db.close() + logging.error(f'No teams found\n\nseason: {season} / {owner_id}') + raise HTTPException(status_code=404, detail=f'Season {season} not found') + + if return_type == 'csv' or csv: + teams_list = [['abbrev', 'sname', 'lname', 'league', 'division', 'stadium', 'manager1', 'manager2', 'thumbnail', + 'color', 'season', 'dice_color', 'id']] + for line in all_teams: + teams_list.append( + [ + line.abbrev, line.sname, line.lname, line.division.league_abbrev if line.division else '', + line.division.division_name if line.division else '', line.stadium, + line.manager1.name if line.manager1 else '', line.manager2.name if line.manager2 else '', + line.thumbnail, line.color, line.season, line.dice_color, line.id + ] + ) + return_teams = DataFrame(teams_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_teams, media_type='text/csv') + + else: + return_teams = {'count': all_teams.count(), 'teams': []} + for team in all_teams: + return_teams['teams'].append(model_to_dict(team)) + + db.close() + return return_teams + + +@app.get('/api/v1/teams/{id_or_abbrev}') +async def v1_teams_get_one(id_or_abbrev, season: Optional[int] = SEASON_DEFAULT): + try: + this_team = Team.get_by_id(id_or_abbrev) + except Exception as e: + if season: + this_team = Team.get_season(id_or_abbrev, season) + else: + this_team = Team.get_season(id_or_abbrev, Current.latest().season) + + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {id_or_abbrev} not found') + else: + db.close() + return model_to_dict(this_team) + + +@app.get('/api/v1/teams/{id_or_abbrev}/roster/{which}') +async def v1_teams_get_roster(id_or_abbrev, which, sort: Optional[str] = None): + try: + this_team = Team.get_by_id(id_or_abbrev) + except Exception as e: + this_team = Team.get_season(id_or_abbrev, SEASON_DEFAULT) + + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {id_or_abbrev} not found') + + if which != 'current' and which != 'next': + db.close() + raise HTTPException(status_code=404, detail=f'Missing /current or /next') + + if which == 'current': + full_roster = this_team.get_this_week() + else: + full_roster = this_team.get_next_week() + + active_players = copy.deepcopy(full_roster['active']['players']) + sil_players = copy.deepcopy(full_roster['shortil']['players']) + lil_players = copy.deepcopy(full_roster['longil']['players']) + full_roster['active']['players'] = [] + full_roster['shortil']['players'] = [] + full_roster['longil']['players'] = [] + + for player in active_players: + full_roster['active']['players'].append(model_to_dict(player)) + for player in sil_players: + full_roster['shortil']['players'].append(model_to_dict(player)) + for player in lil_players: + full_roster['longil']['players'].append(model_to_dict(player)) + + if sort: + if sort == 'wara-desc': + full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True) + full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True) + full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True) + + db.close() + return full_roster + + +@app.get('/api/v1/teams/{id_or_abbrev}/record/{week_num}') +async def v1_teams_get_record(id_or_abbrev, week_num: int): + try: + this_team = Team.get_by_id(id_or_abbrev) + except Exception as e: + this_team = Team.get_season(id_or_abbrev, Current.latest().season) + + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {id_or_abbrev} not found') + + db.close() + return this_team.get_record(week=week_num) + + +@app.patch('/api/v1/teams/{team_id}') +async def v1_teams_patch( + team_id, manager1_id: Optional[int] = None, manager2_id: Optional[int] = None, gmid: Optional[int] = None, + gmid2: Optional[int] = None, mascot: Optional[str] = None, stadium: Optional[str] = None, + thumbnail: Optional[str] = None, color: Optional[str] = None, abbrev: Optional[str] = None, + sname: Optional[str] = None, lname: Optional[str] = None, dice_color: Optional[str] = None, + division_id: Optional[int] = None, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch teams') + try: + this_team = Team.get_by_id(team_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {team_id}') + + if abbrev is not None: + this_team.abbrev = abbrev + if manager1_id is not None: + if manager1_id == 0: + this_team.manager1 = None + else: + try: + this_manager = Manager.get_by_id(manager1_id) + this_team.manager1 = this_manager + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No manager found with id {manager1_id}') + if manager2_id is not None: + print('in mgr2') + if manager2_id == 0: + print('deleting mgr2') + this_team.manager2 = None + else: + try: + print('trying mgr2') + this_manager = Manager.get_by_id(manager2_id) + this_team.manager2 = this_manager + print('set mgr2') + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No manager found with id {manager2_id}') + if gmid is not None: + this_team.gmid = gmid + if gmid2 is not None: + if gmid2 == 0: + this_team.gmid2 = None + else: + this_team.gmid2 = gmid2 + if mascot is not None: + if mascot == 'False': + this_team.mascot = None + else: + this_team.mascot = mascot + if stadium is not None: + this_team.stadium = stadium + if thumbnail is not None: + this_team.thumbnail = thumbnail + if color is not None: + this_team.color = color + if dice_color is not None: + this_team.dice_color = dice_color + if sname is not None: + this_team.sname = sname + if lname is not None: + this_team.lname = lname + if division_id is not None: + if division_id == 0: + this_team.division = None + else: + try: + this_division = Division.get_by_id(division_id) + this_team.division = this_division + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No division found with id {division_id}') + + if this_team.save(): + db.close() + return model_to_dict(this_team) + else: + db.close() + raise HTTPException(status_code=400, detail='This update did not go through') + + +@app.post('/api/v1/teams') +async def v1_teams_post(team: TeamModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post teams') + + dupe_team = Team.get_or_none(Team.season == team.season, Team.abbrev == team.abbrev) + if dupe_team: + db.close() + raise HTTPException(status_code=400, detail=f'There is already a season {team.season} team using {team.abbrev}') + + manager1 = None + if team.manager1_id: + try: + manager1 = Manager.get_by_id(team.manager1_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Manager with id {team.manager1_id} not found') + + manager2 = None + if team.manager2_id: + try: + manager2 = Manager.get_by_id(team.manager2_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Manager with id {team.manager2_id} not found') + + division = None + if team.division_id: + try: + division = Division.get_by_id(team.division_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Division with id {team.division_id} not found') + + this_team = Team( + season=team.season, + abbrev=team.abbrev, + sname=team.sname, + lname=team.lname, + gmid=team.gmid, + gmid2=team.gmid2, + manager1=manager1, + manager2=manager2, + division=division, + stadium=team.stadium, + thumbnail=team.thumbnail, + color=team.color, + dice_color=team.dice_color + ) + + saved = this_team.save() + db.close() + if saved == 1: + return model_to_dict(this_team) + else: + raise HTTPException(status_code=418, detail='Well slap my ass and call me a teapot; I could not save that team') + + +@app.delete('/api/v1/teams/{team_id}') +async def v1_teams_delete(team_id, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to delete teams') + try: + this_team = Team.get_by_id(team_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {team_id}') + + count = this_team.delete_instance() + db.close() + + if count == 1: + raise HTTPException(status_code=200, detail=f'Team {team_id} has been deleted') + else: + raise HTTPException(status_code=500, detail=f'Team {team_id} was not deleted') + + +@app.get('/api/v1/results') +async def v1_results_get( + season: int, team_abbrev: Optional[str] = None, week: Optional[int] = None, game: Optional[int] = None, + away_abbrev: Optional[str] = None, home_abbrev: Optional[str] = None, return_type: Optional[str] = 'json', + csv: Optional[bool] = False): + """ + Param: team_abbrev: string, may not use with away_abbrev or home_abbrev + Param: week: int + Param: away_abbrev: string, may not use with team_abbrev + Param: home_abbrev: string, may not use with team_abbrev + """ + all_results = Result.select_season(season) + # if all_results.count() == 0: + # db.close() + # raise HTTPException(status_code=404, detail=f'Season {season} not found') + + if week: + all_results = all_results.where(Result.week == week) + + if game: + all_results = all_results.where(Result.game == game) + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_results = all_results.where( + (Result.awayteam == this_team) | (Result.hometeam == this_team) + ) + else: + if away_abbrev: + this_team = Team.get_season(away_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Player {away_abbrev} not found') + + all_results = all_results.where(Result.awayteam == this_team) + + if home_abbrev: + this_team = Team.get_season(home_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Player {home_abbrev} not found') + + all_results = all_results.where(Result.hometeam == this_team) + + if return_type == 'csv' or csv: + helper_list = [['season', 'week', 'game_num', 'away_team', 'away_score', 'home_team', 'home_score']] + for game in all_results: + helper_list.append([game.season, game.week, game.game, game.awayteam.abbrev, game.awayscore, + game.hometeam.abbrev, game.homescore]) + + return_results = DataFrame(helper_list).to_csv(header=False, index=False) + db.close() + return Response(content=return_results, media_type='text/csv') + else: + return_results = {} + for game in all_results: + return_results[f'{game.id}'] = model_to_dict(game) + + db.close() + return return_results + + +@app.post('/api/v1/results') +async def v1_results_post(result: ResultModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post results') + + away_team = Team.get_by_id(result.away_team_id) + if not away_team: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {result.away_team_id}') + + home_team = Team.get_by_id(result.home_team_id) + if not home_team: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {result.home_team_id}') + + new_result = Result( + week=result.week, + game=result.game, + awayteam=away_team, + hometeam=home_team, + awayscore=result.away_score, + homescore=result.home_score, + season=result.season, + scorecard_url=result.scorecard_url + ) + + saved = new_result.save() + if saved != 1: + db.close() + raise HTTPException(status_code=400, detail=f'Unable to create {away_team.abbrev} @ {home_team.abbrev} ' + f'w{result.week}g{result.game}') + + # new_result.update_standings() + + # away_team.run_pythag_last8() + # home_team.run_pythag_last8() + # + # away_team.division.sort_division(new_result.season) + # if away_team.division != home_team.division: + # home_team.division.sort_division(new_result.season) + # + # away_team.division.sort_wildcard(new_result.season, away_team.division.league_abbrev) + # if away_team.division.league_abbrev != home_team.division.league_abbrev: + # home_team.division.sort_wildcard(new_result.season, home_team.division.league_abbrev) + + db.close() + raise HTTPException(status_code=200, detail=f'{away_team.abbrev} @ {home_team.abbrev} ' + f'w{result.week}g{result.game} has been logged') + + +@app.patch('/api/v1/results/{result_id}') +async def v1_results_patch( + result_id, away_score: Optional[int] = None, home_score: Optional[int] = None, + scorecard_url: Optional[str] = None, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch results') + + try: + this_result = Result.get_by_id(result_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Result id {result_id} not found') + + if away_score: + this_result.awayscore = away_score + if home_score: + this_result.homescore = home_score + if scorecard_url: + this_result.scorecard_url = scorecard_url + + this_result.save() + db.close() + return model_to_dict(this_result) + + +@app.delete('/api/v1/results/{result_id}') +async def v1_results_delete(result_id, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch results') + + try: + this_result = Result.get_by_id(result_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Result id {result_id} not found') + + this_result.delete_instance() + db.close() + raise HTTPException(status_code=200, detail=f'Deleted 1 result') + + +@app.get('/api/v1/schedules') +async def v1_schedules_get( + season: Optional[int] = SEASON_DEFAULT, team_abbrev1: Optional[str] = None, team_abbrev2: Optional[str] = None, + away_abbrev: Optional[str] = None, home_abbrev: Optional[str] = None, week_start: Optional[int] = None, + week_end: Optional[int] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False): + """ + Param: team_abbrev1: string, may not use with away_abbrev or home_abbrev + Param: team_abbrev2: string, may not use with away_abbrev or home_abbrev + Param: away_abbrev: string, may not use with team_abbrev1/2 + Param: home_abbrev: string, may not use with team_abbrev1/2 + Param: week_start: int + Param: week_end: int + """ + if not season: + season = SEASON_DEFAULT + schedule = Schedule.select_season(season) + # if schedule.count() == 0: + # db.close() + # raise HTTPException(status_code=404, detail=f'Season {season} not found') + + # Check for team_abbrev or away/home_abbrev + if team_abbrev1 or team_abbrev2: + if team_abbrev1: + this_team = Team.get_season(team_abbrev1, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev1} not found') + + schedule = schedule.where( + (Schedule.hometeam == this_team) | (Schedule.awayteam == this_team) + ) + if team_abbrev2: + this_team = Team.get_season(team_abbrev2, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev2} not found') + + schedule = schedule.where( + (Schedule.hometeam == this_team) | (Schedule.awayteam == this_team) + ) + else: + if away_abbrev: + this_team = Team.get_season(away_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {away_abbrev} not found') + + schedule = schedule.where(Schedule.awayteam == this_team) + + if home_abbrev: + this_team = Team.get_season(home_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {home_abbrev} not found') + + schedule = schedule.where(Schedule.hometeam == this_team) + + # Check for week limits + start = 0 + end = Schedule.select(fn.MAX(Schedule.week)).scalar() + if week_start: + start = week_start + + if week_end: + end = week_end + + if start > end: + logging.error('Start week is after end week - cannot pull schedule') + db.close() + raise HTTPException(status_code=404, + detail=f'Start week {start} is after end week {end} - cannot pull schedule') + + schedule = schedule.where( + (Schedule.week >= start) & (Schedule.week <= end) + ) + logging.info(f'schedule query: {schedule}') + + if return_type == 'csv' or csv: + schedule_list = [['season', 'week', 'away_abbrev', 'home_abbrev', 'game_count']] + for line in schedule: + schedule_list.append( + [ + line.season, line.week, line.awayteam.abbrev, line.hometeam.abbrev, line.gamecount + ] + ) + return_schedule = DataFrame(schedule_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_schedule, media_type='text/csv') + else: + return_schedule = {} + for game in schedule: + return_schedule[f'{game.id}'] = model_to_dict(game) + + db.close() + return return_schedule + + +@app.post('/api/v1/schedules') +async def v1_schedules_post(schedule: ScheduleModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post schedules') + all_scheds = [] + + for x in schedule.schedules: + try: + away_team = Team.get_by_id(x.away_team_id) + if not away_team: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {x.away_team_id}') + + home_team = Team.get_by_id(x.home_team_id) + if not home_team: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {x.home_team_id}') + + this_sched = Schedule( + season=x.season, + week=x.week, + awayteam=away_team, + hometeam=home_team, + gamecount=x.game_count + ) + all_scheds.append(this_sched) + except: + db.close() + raise HTTPException( + status_code=404, + detail=f'Failed to enter Team {x.away_team_id} @ Team {x.home_team_id}' + ) + + with db.atomic(): + Schedule.bulk_create(all_scheds, batch_size=20) + + db.close() + raise HTTPException(status_code=200, detail=f'Added {schedule.count} game{"s" if schedule.count > 1 else ""}') + + +@app.patch('/api/v1/schedules/{schedule_id}') +async def v1_schedules_patch( + schedule_id: int, week: Optional[int] = None, away_team_id: Optional[int] = None, + home_team_id: Optional[int] = None, game_count: Optional[int] = None, season: Optional[int] = None, + token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch schedules') + + this_schedule = None + try: + this_schedule = Schedule.get_by_id(schedule_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No schedule found with id {schedule_id}') + + if week is not None: + this_schedule.week = week + if away_team_id is not None: + try: + away_team = Team.get_by_id(away_team_id) + this_schedule.awayteam = away_team + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {away_team_id}') + if home_team_id is not None: + try: + home_team = Team.get_by_id(home_team_id) + this_schedule.hometeam = home_team + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {home_team_id}') + if game_count is not None: + this_schedule.gamecount = game_count + if season is not None: + this_schedule.season = season + + this_schedule.save() + db.close() + return model_to_dict(this_schedule) + + +@app.delete('/api/v1/schedules/{schedule_id}') +async def v1_schedules_delete(schedule_id: int, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to delete schedules') + + delete_query = Schedule.delete().where(Schedule.id == schedule_id) + + count = delete_query.execute() + db.close() + if count > 0: + raise HTTPException(status_code=200, detail=f'Removed {count} schedules') + else: + raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; ' + f'I did not delete any records') + + +@app.get('/api/v1/transactions') +async def v1_transactions( + season = SEASON_DEFAULT, team_abbrev: Optional[str] = None, week_start: Optional[int] = 0, + week_end: Optional[int] = None, cancelled: Optional[bool] = None, frozen: Optional[bool] = None, + player_name: Optional[str] = None, player_id: Optional[int] = None, move_id: Optional[str] = None, + is_trade: Optional[bool] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False): + """ + Param: season: int + Param: team_abbrev: string + Param: week_start: int + Param: week_end: int + Param: player_id: int, overrides player_name (and season) + Param: player_name: string, overridden by player_id + Param: include_cancelled: bool, default False + Param: include_frozen: bool, default False + """ + if season: + transactions = Transaction.select_season(season) + else: + transactions = Transaction.select() + + # if transactions.count() == 0: + # db.close() + # raise HTTPException(status_code=404, detail=f'Season {season} not found') + + if team_abbrev: + these_teams = Team.select().where( + (Team.abbrev == team_abbrev.upper()) | (Team.abbrev == f'{team_abbrev.upper()}SIL') | + (Team.abbrev == f'{team_abbrev.upper()}MiL') | (Team.abbrev == f'{team_abbrev.upper()}IL') + ) + if these_teams.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + transactions = transactions.where( + (Transaction.newteam << these_teams) | (Transaction.oldteam << these_teams) + ) + + if week_start is not None: + transactions = transactions.where(Transaction.week >= week_start) + + if week_end is not None: + transactions = transactions.where(Transaction.week <= week_end) + + if move_id: + transactions = transactions.where(Transaction.moveid == move_id) + + if player_id or player_name: + if player_id: + try: + this_player = Player.get_by_id(player_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + + transactions = transactions.where(Transaction.player == this_player) + else: + these_players = Player.select().where(fn.Lower(Player.name) == player_name.lower()) + print(f'these_players: {these_players}\nCount: {these_players.count()}') + if these_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Player {player_name} not found') + + transactions = transactions.where(Transaction.player << these_players) + + if cancelled: + transactions = transactions.where(Transaction.cancelled == 1) + else: + transactions = transactions.where(Transaction.cancelled == 0) + + if frozen: + transactions = transactions.where(Transaction.frozen == 1) + else: + transactions = transactions.where(Transaction.frozen == 0) + + if is_trade is not None: + if not is_trade: + db.close() + raise HTTPException(status_code=501, detail='The is_trade parameter only supports True') + + # if is_trade is not None: + # if is_trade: + # old_team = Team.alias() + # new_team = Team.alias() + # transactions = transactions.join(Team, on=Transaction.oldteam).where( + # Transaction.oldteam.gmid != Transaction.newteam.gmid + # ) + # logging.info(transactions) + # + # # # Get IL, MIL and FA teams + # # ilteams = Team.select_season(season).where((fn.Lower(Team.abbrev).endswith('il'))) + # # all_teams = Team.select_season(season).where( + # # ~(Team.abbrev.endswith('IL')) & ~(Team.abbrev.endswith('MiL')) + # # ) + # # fa_team = Team.get(Team.season == season, Team.abbrev == 'FA') + # # + # # # Get transactions where new team << bad_teams + # # transactions = transactions.where( + # # (~(Transaction.oldteam << fa_team) & (Transaction.oldteam << all_teams) & + # # ~(Transaction.newteam << ilteams)) | + # # ((Transaction.oldteam << all_teams) & ~(Transaction.newteam << ilteams)) + # # ) + # # # Get transactions where old team << bad_teams + # else: + # db.close() + # raise HTTPException(status_code=501, detail='The "is_trade" parameter only supports True') + + transactions = transactions.order_by(-Transaction.week, Transaction.moveid) + + if return_type == 'csv' or csv: + trans_list = [['season', 'week', 'player_name', 'old_team_abbrev', 'new_team_abbrev', 'move_id']] + for line in transactions: + if (is_trade and line.oldteam.gmid and line.newteam.gmid and line.oldteam.gmid != line.newteam.gmid and + 'rule5' not in line.moveid) or not is_trade: + trans_list.append( + [ + line.season, line.week, line.player.name, line.oldteam.abbrev, line.newteam.abbrev, line.moveid + ] + ) + return_trans = DataFrame(trans_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_trans, media_type='text/csv') + else: + return_trans = {} + for line in transactions: + if (is_trade and line.oldteam.gmid and line.newteam.gmid and line.oldteam.gmid != line.newteam.gmid and + 'rule5' not in line.moveid) or not is_trade: + return_trans[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_trans + + +@app.post('/api/v1/transactions') +async def v1_transactions_post(moves: TransModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post transactions') + + all_moves = [] + + for x in moves.moves: + try: + old_team = Team.get_by_id(x.oldteam_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Old team with id {x.oldteam_id} not found') + try: + new_team = Team.get_by_id(x.newteam_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Away team with id {x.newteam_id} not found') + try: + this_player = Player.get_by_id(x.player_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {x.player_id} not found') + + this_move = Transaction( + week=x.week, + player=this_player, + oldteam=old_team, + newteam=new_team, + season=x.season, + moveid=x.moveid, + cancelled=x.cancelled, + frozen=x.frozen + ) + + all_moves.append(this_move) + + with db.atomic(): + Transaction.bulk_create(all_moves, batch_size=15) + + db.close() + raise HTTPException(status_code=200, detail=f'{len(all_moves)} of {moves.count} lines have been added') + + +@app.patch('/api/v1/transactions/{move_id}') +async def v1_transactions_patch( + move_id, token: str = Depends(oauth2_scheme), frozen: Optional[bool] = None, cancelled: Optional[bool] = None): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch transactions') + + these_moves = Transaction.select().where(Transaction.moveid == move_id) + if these_moves.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Move ID {move_id} not found') + + if frozen is not None: + for x in these_moves: + x.frozen = frozen + x.save() + if cancelled is not None: + for x in these_moves: + x.cancelled = cancelled + x.save() + + db.close() + raise HTTPException(status_code=200, detail=f'Updated {these_moves.count()} transactions') + + +@app.delete('/api/v1/transactions/{move_id}') +async def v1_transactions_delete( + move_id, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch transactions') + + delete_query = Transaction.delete().where(Transaction.moveid == move_id) + + count = delete_query.execute() + db.close() + if count > 0: + raise HTTPException(status_code=200, detail=f'Removed {count} transactions') + else: + raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; ' + f'I did not delete any records') + + +@app.get('/api/v1/players') +async def v1_players_get( + season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None, + injured: Optional[bool] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False): + """ + Param: season: int + Param: team_abbrev: string + Param: sort: string, [wara-desc] + """ + all_players = Player.select_season(season) + if all_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Season {season} not found') + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_players = all_players.where(Player.team == this_team) + + if sort: + if sort == 'wara-desc': + all_players = all_players.order_by(-Player.wara) + + if injured is not None: + all_players = all_players.where(Player.il_return.is_null(False)) + + if return_type == 'csv' or csv: + player_list = [ + ['name', 'wara', 'image', 'vanity_card', 'team_abbrev', 'pit_inj', 'pos_1', 'pos_2', 'pos_3', 'pos_4', + 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', 'dem_week'] + ] + for line in all_players: + player_list.append( + [ + line.name, line.wara, line.image, line.vanity_card, line.team.abbrev, line.pitcher_injury, + line.pos_1, line.pos_2, line.pos_3, line.pos_4, line.pos_5, line.pos_6, line.pos_7, line.pos_8, + line.last_game, line.last_game2, line.il_return, line.demotion_week + ] + ) + return_players = DataFrame(player_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_players, media_type='text/csv') + else: + return_players = {} + for player in all_players: + return_players[f'{player.name}'] = model_to_dict(player) + + db.close() + return return_players + + +@app.get('/api/v2/players') +async def v2_players_get( + season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None, + injured: Optional[bool] = None, csv: Optional[bool] = False): + """ + Param: season: int + Param: team_abbrev: string + Param: sort: string, [wara-desc] + """ + all_players = Player.select_season(season) + if all_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Season {season} not found') + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_players = all_players.where(Player.team == this_team) + + if sort: + if sort == 'wara-desc': + all_players = all_players.order_by(-Player.wara) + + if injured is not None: + all_players = all_players.where(Player.il_return.is_null(False)) + + if csv: + player_list = [ + ['name', 'wara', 'image', 'vanity_card', 'team_abbrev', 'inj_rat', 'pos_1', 'pos_2', 'pos_3', 'pos_4', + 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', 'dem_week', 'strat_code', + 'bbref_id'] + ] + for line in all_players: + player_list.append( + [ + line.name, line.wara, line.image, line.vanity_card, line.team.abbrev, line.injury_rating, + line.pos_1, line.pos_2, line.pos_3, line.pos_4, line.pos_5, line.pos_6, line.pos_7, line.pos_8, + line.last_game, line.last_game2, line.il_return, line.demotion_week, line.strat_code, line.bbref_id + ] + ) + return_players = DataFrame(player_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_players, media_type='text/csv') + else: + return_players = {} + for player in all_players: + return_players[f'{player.name}'] = model_to_dict(player) + + db.close() + return return_players + + +@app.get('/api/v3/players') +async def v3_players_get( + season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None, + injured: Optional[bool] = None, csv: Optional[bool] = False): + """ + Param: season: int + Param: team_abbrev: string + Param: sort: string, [wara-desc] + """ + all_players = Player.select_season(season) + if all_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Season {season} not found') + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_players = all_players.where(Player.team == this_team) + + if sort: + if sort == 'wara-desc': + all_players = all_players.order_by(-Player.wara) + + if injured is not None: + all_players = all_players.where(Player.il_return.is_null(False)) + + if csv: + player_list = [ + ['name', 'wara', 'image', 'vanity_card', 'team_abbrev', 'inj_rat', 'pos_1', 'pos_2', 'pos_3', 'pos_4', + 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', 'dem_week', 'strat_code', + 'bbref_id'] + ] + for line in all_players: + player_list.append( + [ + line.name, line.wara, line.image, line.vanity_card, line.team.abbrev, line.injury_rating, + line.pos_1, line.pos_2, line.pos_3, line.pos_4, line.pos_5, line.pos_6, line.pos_7, line.pos_8, + line.last_game, line.last_game2, line.il_return, line.demotion_week, line.strat_code, line.bbref_id + ] + ) + return_players = DataFrame(player_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_players, media_type='text/csv') + else: + return_players = {'count': all_players.count(), 'players': []} + for player in all_players: + return_players['players'].append(model_to_dict(player)) + + db.close() + return return_players + + +@app.get('/api/v2/players/{id_or_name}') +async def v2_players_get_one(id_or_name, season: Optional[int] = SEASON_DEFAULT, csv: Optional[bool] = False): + this_player = None + + try: + this_player = Player.get_by_id(id_or_name) + except Exception as e: + this_player = Player.get_season(id_or_name, season) + + if this_player: + if csv: + player_list = [['name', 'wara', 'image', 'image2', 'team_abbrev', 'inj_rat', 'pos_1', 'pos_2', 'pos_3', + 'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', + 'dem_week', 'strat_code', 'bbref_id'], + [this_player.name, this_player.wara, this_player.image, this_player.image2, + this_player.team.abbrev, this_player.injury_rating, this_player.pos_1, this_player.pos_2, + this_player.pos_3, this_player.pos_4, this_player.pos_5, this_player.pos_6, + this_player.pos_7, this_player.pos_8, this_player.last_game, this_player.last_game2, + this_player.il_return, this_player.demotion_week, this_player.strat_code, + this_player.bbref_id] + ] + return_players = DataFrame(player_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_players, media_type='text/csv') + else: + db.close() + return model_to_dict(this_player) + else: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {id_or_name} not found') + + +@app.get('/api/v1/players/{id_or_name}') +async def v1_players_get_one(id_or_name, season: Optional[int] = SEASON_DEFAULT, return_type: Optional[str] = 'json', + csv: Optional[bool] = False): + this_player = None + + try: + this_player = Player.get_by_id(id_or_name) + except Exception as e: + this_player = Player.get_season(id_or_name, season) + + if this_player: + if return_type == 'csv' or csv: + player_list = [['name', 'wara', 'image', 'image2', 'team_abbrev', 'pit_inj', 'pos_1', 'pos_2', 'pos_3', + 'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', + 'dem_week'], + [this_player.name, this_player.wara, this_player.image, this_player.image2, + this_player.team.abbrev, this_player.pitcher_injury, this_player.pos_1, this_player.pos_2, + this_player.pos_3, this_player.pos_4, this_player.pos_5, this_player.pos_6, + this_player.pos_7, this_player.pos_8, this_player.last_game, this_player.last_game2, + this_player.il_return, this_player.demotion_week] + ] + return_players = DataFrame(player_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_players, media_type='text/csv') + else: + db.close() + return model_to_dict(this_player) + else: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {id_or_name} not found') + + +@app.patch('/api/v2/players/{pid}') +async def v1_players_patch( + pid: int, token: str = Depends(oauth2_scheme), name: Optional[str] = None, wara: Optional[float] = None, + image: Optional[str] = None, image2=None, team_id: Optional[int] = None, season: Optional[int] = None, + pitcher_injury=None, pos_1=None, pos_2=None, pos_3=None, pos_4=None, pos_5=None, pos_6=None, pos_7=None, + pos_8=None, last_game=None, last_game2=None, il_return=None, demotion_week=None, headshot=None, + vanity_card=None, strat_code=None, bbref_id=None, injury_rating=None): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch players') + + try: + this_player = Player.get_by_id(pid) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {pid} not found') + + if team_id: + try: + this_team = Team.get_by_id(team_id) + this_player.team = this_team + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Team id {team_id} not found') + if name: + this_player.name = name + if wara: + this_player.wara = wara + if image: + this_player.image = image + if image2 is not None: + if image2.lower() == 'false': + this_player.image2 = None + else: + this_player.image2 = image2 + if season: + this_player.season = season + if pitcher_injury is not None: + if pitcher_injury.lower() == 'false': + this_player.pitcher_injury = None + else: + this_player.pitcher_injury = pitcher_injury + if pos_1: + this_player.pos_1 = pos_1 + if pos_2 is not None: + if pos_2.lower() == 'false': + this_player.pos_2 = None + else: + this_player.pos_2 = pos_2 + if pos_3 is not None: + if pos_3.lower() == 'false': + this_player.pos_3 = None + else: + this_player.pos_3 = pos_3 + if pos_4 is not None: + if pos_4.lower() == 'false': + this_player.pos_4 = None + else: + this_player.pos_4 = pos_4 + if pos_5 is not None: + if pos_5.lower() == 'false': + this_player.pos_5 = None + else: + this_player.pos_5 = pos_5 + if pos_6 is not None: + if pos_6.lower() == 'false': + this_player.pos_6 = None + else: + this_player.pos_6 = pos_6 + if pos_7 is not None: + if pos_7.lower() == 'false': + this_player.pos_7 = None + else: + this_player.pos_7 = pos_7 + if pos_8 is not None: + if pos_8.lower() == 'false': + this_player.pos_8 = None + else: + this_player.pos_8 = pos_8 + if last_game is not None: + if last_game.lower() == 'false': + this_player.last_game = None + else: + this_player.last_game = last_game + if last_game2 is not None: + if last_game2.lower() == 'false': + this_player.last_game2 = None + else: + this_player.last_game2 = last_game2 + if il_return is not None: + if il_return.lower() == 'false': + this_player.il_return = None + else: + this_player.il_return = il_return + if demotion_week is not None: + if demotion_week.lower() == 'false': + this_player.demotion_week = None + else: + this_player.demotion_week = demotion_week + if headshot is not None: + if headshot.lower() == 'false': + this_player.headshot = None + else: + this_player.headshot = headshot + if vanity_card is not None: + if vanity_card.lower() == 'false': + this_player.vanity_card = None + else: + this_player.vanity_card = vanity_card + if strat_code is not None: + if strat_code.lower() == 'false': + this_player.strat_code = None + else: + this_player.strat_code = strat_code + if bbref_id is not None: + if bbref_id.lower() == 'false': + this_player.bbref_id = None + else: + this_player.bbref_id = bbref_id + if injury_rating is not None: + if injury_rating.lower() == 'false': + this_player.injury_rating = None + else: + this_player.injury_rating = injury_rating + + saved = this_player.save() + db.close() + if saved == 1: + return model_to_dict(this_player) + else: + raise HTTPException(status_code=400, detail=f'Unable to update {this_player.name}') + + +@app.post('/api/v2/players') +async def v1_players_post(players: PlayerModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post players') + + new_players = [] + for x in players.players: + logging.info(f'x: {x}') + this_player = Player( + name=x.name, + wara=x.wara, + image=x.image, + image2=x.image2, + team_id=x.team_id, + season=x.season, + pitcher_injury=x.pitcher_injury, + pos_1=x.pos_1, + pos_2=x.pos_2, + pos_3=x.pos_3, + pos_4=x.pos_4, + pos_5=x.pos_5, + pos_6=x.pos_6, + pos_7=x.pos_7, + pos_8=x.pos_8, + last_game=x.last_game, + last_game2=x.last_game2, + il_return=x.il_return, + demotion_week=x.demotion_week, + strat_code=x.strat_code, + bbref_id=x.bbref_id, + injury_rating=x.injury_rating + ) + new_players.append(this_player) + + with db.atomic(): + Player.bulk_create(new_players, batch_size=15) + db.close() + + raise HTTPException(status_code=200, detail=f'{len(new_players)} players have been added') + + +@app.get('/api/v1/battingcareer/{id_or_name}') +async def v1_battingcareer_get(id_or_name, csv: Optional[bool] = False): + this_career = None + try: + this_career = BattingCareer.get_by_id(id_or_name) + except Exception as e: + logging.info(f'Could not find BattingCareer with id {id_or_name}') + + if not this_career: + this_career = BattingCareer.get_or_none(fn.Lower(BattingCareer.name) == id_or_name.lower()) + + if this_career: + if csv: + career_list = [ + ['name', 'pa', 'ab', 'run', 'hit', 'rbi', 'double', 'triple', 'hr', 'bb', 'so', 'hbp', 'sac', 'ibb', + 'gidp', 'sb', 'cs', 'bphr', 'bpfo', 'bp1b', 'bplo', 'games'], + [this_career.name, this_career.pa, this_career.ab, this_career.run, this_career.hit, this_career.rbi, + this_career.double, this_career.triple, this_career.hr, this_career.bb, this_career.so, + this_career.hbp, this_career.sac, this_career.ibb, this_career.gidp, this_career.sb, this_career.cs, + this_career.bphr, this_career.bpfo, this_career.bp1b, this_career.bplo, this_career.games] + ] + return_career = DataFrame(career_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_career, media_type='text/csv') + else: + db.close() + return model_to_dict(this_career) + else: + db.close() + raise HTTPException(status_code=404, detail=f'Career id {id_or_name} not found') + + +@app.get('/api/v1/pitchingcareer/{id_or_name}') +async def v1_pitchingcareer_get(id_or_name, csv: Optional[bool] = False): + this_career = None + try: + this_career = PitchingCareer.get_by_id(id_or_name) + except Exception as e: + logging.info(f'Could not find PitchingCareer with id {id_or_name}') + + if not this_career: + this_career = PitchingCareer.get_or_none(fn.Lower(PitchingCareer.name) == id_or_name.lower()) + + if this_career: + if csv: + career_list = [ + ['name', 'ip', 'hit', 'run', 'erun', 'so', 'bb', 'hbp', 'wp', 'balk', 'hr', 'ir', 'irs', 'gs', 'win', + 'loss', 'hold', 'sv', 'bsv', 'game'], + [this_career.name, this_career.ip, this_career.hit, this_career.run, this_career.erun, this_career.so, + this_career.bb, this_career.hbp, this_career.wp, this_career.balk, this_career.hr, this_career.ir, + this_career.irs, this_career.gs, this_career.win, this_career.loss, this_career.hold, this_career.sv, + this_career.bsv, this_career.game] + ] + return_career = DataFrame(career_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_career, media_type='text/csv') + else: + db.close() + return model_to_dict(this_career) + else: + db.close() + raise HTTPException(status_code=404, detail=f'Career id {id_or_name} not found') + + +@app.get('/api/v1/battingstats/s{season}/{s_type}') +async def v1_battingstats_get( + s_type: str, season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None, + player_name: Optional[str] = None, player_id: Optional[int] = None, week_start: Optional[int] = None, + week_end: Optional[int] = None, game_num: Optional[int] = None, position: Optional[str] = None, + limit: Optional[int] = None, sort: Optional[str] = None, return_type: Optional[str] = 'json', + csv: Optional[bool] = False): + """ + :param season: int + :param s_type: string (regular, post, combined) + :param team_abbrev: string + :param player_name: string + :param player_id: integer, overrides player_name + :param week_start: integer + :param week_end: integer + :param game_num: integer + :param position: string + :param limit: int + :param sort: string, 'oldest' or 'newest' to return first + :return: + """ + if s_type.lower() == 'post': + all_stats = BattingStat.post_season(season) + if all_stats.count() == 0: + db.close() + return {} + elif s_type.lower() == 'combined': + all_stats = BattingStat.combined_season(season) + if all_stats.count() == 0: + db.close() + return {} + else: + all_stats = BattingStat.regular_season(season) + if not all_stats: + db.close() + return {} + + if position: + all_stats = all_stats.where(BattingStat.pos == position.upper()) + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_stats = all_stats.where(BattingStat.team == this_team) + + if player_name or player_id: + if player_id: + try: + this_player = Player.get_by_id(player_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + else: + this_player = Player.get_season(player_name, season) + if not this_player: + db.close() + raise HTTPException(status_code=404, detail=f'Player {player_name} not found') + + all_stats = all_stats.where(BattingStat.player == this_player) + + start = 0 + end = Schedule.select(fn.MAX(Schedule.week)).scalar() + if week_start: + start = week_start + + if week_end: + end = week_end + + if start > end: + db.close() + raise HTTPException(status_code=404, + detail=f'Start week {start} is after end week {end} - cannot pull stats') + + logging.info(f'\n\nweek_start: {week_start} / week_end: {week_end} / game_num: {game_num} / ' + f'start: {start} / end: {end}') + all_stats = all_stats.where( + (BattingStat.week >= start) & (BattingStat.week <= end) + ) + + if game_num: + all_stats = all_stats.where(BattingStat.game == game_num) + + if limit: + all_stats = all_stats.limit(limit) + + if sort: + if sort == 'newest': + all_stats = all_stats.order_by(-BattingStat.week, -BattingStat.game) + + if return_type == 'csv' or csv: + stats_list = [['season', 'week', 'game_num', 'player', 'team', 'pos', 'pa', 'ab', 'run', 'hit', 'rbi', 'double', + 'triple', 'hr', 'bb', 'so', 'hbp', 'sac', 'ibb', 'gidp', 'sb', 'cs', 'xba', 'xbt']] + for line in all_stats: + stats_list.append( + [ + line.season, line.week, line.game, line.player.name, line.team.abbrev, line.pos, line.pa, line.ab, + line.run, line.hit, line.rbi, line.double, line.triple, line.hr, line.bb, line.so, line.hbp, + line.sac, line.ibb, line.gidp, line.sb, line.cs, line.xba, line.xbt + ] + ) + return_stats = DataFrame(stats_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_stats, media_type='text/csv') + else: + return_stats = {} + for line in all_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.post('/api/v1/battingstats') +async def v1_battingstats_post(stats: BattingStatModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post batting stats') + + all_stats = [] + season = None + week = None + teams = [] + game_num = None + + for x in stats.stats: + try: + team = Team.get_by_id(x.team_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {x.team_id}') + try: + this_player = Player.get_by_id(x.player_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {x.player_id} not found') + + # Helpers for query + if team not in teams: + teams.append(team) + if not season: + season = x.season + if not week: + week = x.week + if not game_num: + game_num = x.game + + new_stats = BattingStat( + player=this_player, + team=team, + pos=x.pos, + pa=x.pa, + ab=x.ab, + run=x.run, + hit=x.hit, + rbi=x.rbi, + double=x.double, + triple=x.triple, + hr=x.hr, + bb=x.bb, + so=x.so, + hbp=x.hbp, + sac=x.sac, + ibb=x.ibb, + gidp=x.gidp, + sb=x.sb, + cs=x.cs, + bphr=x.bphr, + bpfo=x.bpfo, + bp1b=x.bp1b, + bplo=x.bplo, + xba=x.xba, + xbt=x.xbt, + xch=x.xch, + xhit=x.xhit, + error=x.error, + pb=x.pb, + sbc=x.sbc, + csc=x.csc, + roba=x.roba, + robs=x.robs, + raa=x.raa, + rto=x.rto, + week=x.week, + game=x.game, + season=x.season, + ) + all_stats.append(new_stats) + + with db.atomic(): + BattingStat.bulk_create(all_stats, batch_size=15) + + this_game_stats = BattingStat.select().where( + (BattingStat.season == season) & (BattingStat.week == week) & + (BattingStat.game == game_num) & ((BattingStat.team == teams[0]) | (BattingStat.team == teams[1])) + ) + + logging.warning(f'I pulled {this_game_stats.count()} batting lines with this query:\n{this_game_stats}') + + # Update season and career stats with freshly submitted for regular season games + if (season > 2) and week <= 22: + s_type = 'Regular' + else: + s_type = 'Post' + + for line in this_game_stats: + this_season = BattingSeason.get_or_none(player=line.player, season=line.player.season, season_type=s_type) + if not this_season: + this_season = BattingSeason(player=line.player, season=line.player.season, season_type=s_type) + this_season.save() + 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() + + fielding_season = FieldingSeason.get_or_none( + player=line.player, season=line.player.season, pos=line.pos, season_type=s_type + ) + if not fielding_season: + fielding_season = FieldingSeason( + player=line.player, season=line.player.season, pos=line.pos, season_type=s_type + ) + fielding_season.save() + fielding_career = FieldingCareer.get_or_none(name=line.player.name, pos=line.pos) + if not fielding_career: + fielding_career = FieldingCareer(name=line.player.name, pos=line.pos) + fielding_career.save() + + this_season.pa += line.pa + this_season.ab += line.ab + this_season.run += line.run + this_season.hit += line.hit + this_season.rbi += line.rbi + this_season.double += line.double + this_season.triple += line.triple + this_season.hr += line.hr + this_season.bb += line.bb + this_season.so += line.so + this_season.hbp += line.hbp + this_season.sac += line.sac + this_season.ibb += line.ibb + this_season.gidp += line.gidp + this_season.sb += line.sb + this_season.cs += line.cs + this_season.bphr += line.bphr + this_season.bpfo += line.bpfo + this_season.bp1b += line.bp1b + this_season.bplo += line.bplo + this_season.xba += line.xba + this_season.xbt += line.xbt + this_season.game += 1 + fielding_season.xch += line.xch + fielding_season.xhit += line.xhit + fielding_season.error += line.error + fielding_season.pb += line.pb + fielding_season.sbc += line.sbc + fielding_season.csc += line.csc + fielding_season.roba += line.roba + fielding_season.robs += line.robs + fielding_season.raa += line.raa + fielding_season.rto += line.rto + fielding_season.game += 1 + + if s_type == 'Regular': + this_career.pa += line.pa + this_career.ab += line.ab + this_career.run += line.run + this_career.hit += line.hit + this_career.rbi += line.rbi + this_career.double += line.double + this_career.triple += line.triple + this_career.hr += line.hr + this_career.bb += line.bb + this_career.so += line.so + this_career.hbp += line.hbp + this_career.sac += line.sac + this_career.ibb += line.ibb + this_career.gidp += line.gidp + this_career.sb += line.sb + this_career.cs += line.cs + this_career.bphr += line.bphr + this_career.bpfo += line.bpfo + this_career.bp1b += line.bp1b + this_career.bplo += line.bplo + this_career.xba += line.xba + this_career.xbt += line.xbt + this_career.game += 1 + fielding_career.xch += line.xch + fielding_career.xhit += line.xhit + fielding_career.error += line.error + fielding_career.pb += line.pb + fielding_career.sbc += line.sbc + fielding_career.csc += line.csc + fielding_career.roba += line.roba + fielding_career.robs += line.robs + fielding_career.raa += line.raa + fielding_career.rto += line.rto + fielding_career.game += 1 + + this_season.save() + this_career.save() + fielding_season.save() + fielding_career.save() + + db.close() + raise HTTPException(status_code=200, detail=f'{len(all_stats)} of {stats.count} lines have been added') + + +@app.delete('/api/v1/battingstats') +async def v1_battingstats_delete(game: GameModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to delete batting stats') + + # Get teams + try: + away_team = Team.get_by_id(game.away_team_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Away team with id {game.away_team_id} not found') + try: + home_team = Team.get_by_id(game.home_team_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Away team with id {game.home_team_id} not found') + + # Check for matchup + this_matchup = Schedule.select_season(Current.latest().season).where( + (Schedule.awayteam == away_team) & (Schedule.hometeam == home_team) & (Schedule.week == game.week) + ) + if this_matchup.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No game found between {away_team.abbrev} and {home_team.abbrev} ' + f'in week {game.week}') + + # Check for game batting stats + this_game_stats = BattingStat.select().where( + (BattingStat.season == game.season) & (BattingStat.week == game.week) & (BattingStat.game == game.game_num) & + ((BattingStat.team == away_team) | (BattingStat.team == home_team)) & (BattingStat.pa > 0) + ) + if this_game_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No stats found for {away_team.abbrev} at {home_team.abbrev} in ' + f'that matchup') + + # # Remove stats from season lines + # for line in this_game_stats: + # this_season = BattingSeason.get_or_none(player=line.player, season=line.player.season) + # if not this_season: + # db.close() + # raise HTTPException(status_code=404, detail=f'Could not find existing batting season stats ' + # f'for {line.player.name} (ID: {line.player.id})') + # 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() + # + # this_season.pa -= line.pa + # # this_career.pa -= line.pa + # this_season.ab -= line.ab + # # this_career.ab -= line.ab + # this_season.run -= line.run + # # this_career.run -= line.run + # this_season.hit -= line.hit + # # this_career.hit -= line.hit + # this_season.rbi -= line.rbi + # # this_career.rbi -= line.rbi + # this_season.double -= line.double + # # this_career.double -= line.double + # this_season.triple -= line.triple + # # this_career.triple -= line.triple + # this_season.hr -= line.hr + # # this_career.hr -= line.hr + # this_season.bb -= line.bb + # # this_career.bb -= line.bb + # this_season.so -= line.so + # # this_career.so -= line.so + # this_season.hbp -= line.hbp + # # this_career.hbp -= line.hbp + # this_season.sac -= line.sac + # # this_career.sac -= line.sac + # this_season.ibb -= line.ibb + # # this_career.ibb -= line.ibb + # this_season.gidp -= line.gidp + # # this_career.gidp -= line.gidp + # this_season.sb -= line.sb + # # this_career.sb -= line.sb + # this_season.cs -= line.cs + # # this_career.cs -= line.cs + # this_season.game -= 1 + # # this_career.game -= 1 + # + # this_season.save() + # # this_career.save() + # + # this_career.recalculate() + # + # # Check for game fielding stats + # this_game_stats = BattingStat.select().where( + # (BattingStat.season == game.season) & (BattingStat.week == game.week) & (BattingStat.game == game.game_num) & + # ((BattingStat.team == away_team) | (BattingStat.team == home_team)) & (BattingStat.xch + BattingStat.sbc > 0) + # ) + # if this_game_stats.count() == 0: + # db.close() + # raise HTTPException(status_code=404, detail=f'No stats found for {away_team.abbrev} at {home_team.abbrev} in ' + # f'that matchup') + # + # # Remove stats from season lines + # for line in this_game_stats: + # this_season = FieldingSeason.get_or_none(player=line.player, season=line.player.season) + # if not this_season: + # db.close() + # raise HTTPException(status_code=404, detail=f'Could not find existing batting season stats ' + # f'for {line.player.name} (ID: {line.player.id})') + # this_career = FieldingCareer.get_or_none(FieldingCareer.name == this_season.player.name, + # FieldingCareer.pos == line.pos) + # if not this_career: + # this_career = FieldingCareer(name=this_season.player.name, pos=this_season.pos) + # this_career.save() + # + # this_season.xch -= line.xch + # # this_career.xch -= line.xch + # this_season.xhit -= line.xhit + # # this_career.xhit -= line.xhit + # this_season.error -= line.error + # # this_career.error -= line.error + # this_season.pb -= line.pb + # # this_career.pb -= line.pb + # this_season.sbc -= line.sbc + # # this_career.sbc -= line.sbc + # this_season.csc -= line.csc + # # this_career.csc -= line.csc + # this_season.game -= 1 + # # this_career.game -= 1 + # + # this_season.save() + # # this_career.save() + # + # this_career.recalculate() + + # Delete the stats + delete_query = BattingStat.delete().where( + (BattingStat.season == game.season) & (BattingStat.week == game.week) & + ((BattingStat.team == away_team) | (BattingStat.team == home_team)) + ) + if game.game_num: + delete_query = delete_query.where(BattingStat.game == game.game_num) + + count = delete_query.execute() + db.close() + if count > 0: + raise HTTPException(status_code=200, detail=f'Removed {count} batting stat lines') + else: + raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; ' + f'I did not delete any records') + + +@app.get('/api/v1/pitchingstats/s{season}/{s_type}') +async def v1_pitchingstat_get( + s_type: str, season: int, team_abbrev: Optional[str] = None, + player_name: Optional[str] = None, player_id: Optional[int] = None, week_start: Optional[int] = None, + week_end: Optional[int] = None, game_num: Optional[int] = None, limit: Optional[int] = None, + sort: Optional[str] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False): + """ + :param season: int + :param s_type: string (regular, post, combined) + :param team_abbrev: string + :param player_name: string + :param player_id: integer, overrides player_name + :param week_start: integer + :param week_end: integer + :param limit: int + :param sort: string, 'newest' or 'oldest' to get those first + :return: + """ + if s_type.lower() == 'post': + all_stats = PitchingStat.post_season(season) + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No postseason stats found for season {season}') + elif s_type.lower() == 'combined': + all_stats = PitchingStat.combined_season(season) + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No stats found for season {season}') + else: + all_stats = PitchingStat.regular_season(season) + if not all_stats: + db.close() + return {} + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_stats = all_stats.where(PitchingStat.team == this_team) + + if player_name or player_id: + if player_id: + try: + this_player = Player.get_by_id(player_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + else: + this_player = Player.get_season(player_name, season) + if not this_player: + db.close() + raise HTTPException(status_code=404, detail=f'Player {player_name} not found') + + all_stats = all_stats.where(PitchingStat.player == this_player) + + start = 0 + end = Schedule.select(fn.MAX(Schedule.week)).scalar() + if week_start: + start = week_start + + if week_end: + end = week_end + + if start > end: + db.close() + raise HTTPException(status_code=404, + detail=f'Start week {start} is after end week {end} - cannot pull stats') + + all_stats = all_stats.where( + (PitchingStat.week >= start) & (PitchingStat.week <= end) + ) + + if game_num: + all_stats = all_stats.where(PitchingStat.game == game_num) + + if limit: + all_stats = all_stats.limit(limit) + + if sort: + if sort == 'newest': + all_stats = all_stats.order_by(-PitchingStat.week, -PitchingStat.game) + + if return_type == 'csv' or csv: + stats_list = [['pitcher', 'team', 'ip', 'h', 'r', 'er', 'so', 'bb', 'hbp', 'wp', 'bk', 'hr', + 'gs', 'w', 'l', 'hd', 'sv', 'bs', 'week', 'game', 'code']] + for line in all_stats: + stats_list.append( + [ + line.player.name, line.team.abbrev, line.ip, line.hit, line.run, line.erun, line.so, line.bb, + line.hbp, line.wp, line.balk, line.hr, line.gs, line.win, line.loss, line.hold, line.sv, line.bsv, + line.week, line.game, f'{line.week}.{line.game}' + ] + ) + return_stats = DataFrame(stats_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_stats, media_type='text/csv') + + else: + return_stats = {} + for line in all_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.post('/api/v1/pitchingstats') +async def v1_pitchingstats_post(stats: PitchingStatModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post pitching stats') + + all_stats = [] + season = None + week = None + teams = [] + game_num = None + + for x in stats.stats: + team = Team.get_by_id(x.team_id) + if not team: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {x.team_id}') + + player = Player.get_by_id(x.player_id) + if not player: + db.close() + raise HTTPException(status_code=404, detail=f'No player found with id {x.player_id}') + + # Helpers for query + if team not in teams: + teams.append(team) + if not season: + season = x.season + if not week: + week = x.week + if not game_num: + game_num = x.game + + new_stats = PitchingStat( + player=player, + team=team, + ip=x.ip, + hit=x.hit, + run=x.run, + erun=x.erun, + so=x.so, + bb=x.bb, + hbp=x.hbp, + wp=x.wp, + balk=x.balk, + hr=x.hr, + gs=x.gs, + win=x.win, + loss=x.loss, + hold=x.hold, + sv=x.sv, + bsv=x.bsv, + ir=x.ir, + irs=x.irs, + week=x.week, + game=x.game, + season=x.season + ) + all_stats.append(new_stats) + + with db.atomic(): + PitchingStat.bulk_create(all_stats, batch_size=20) + + this_game_stats = PitchingStat.select().where( + (PitchingStat.season == season) & (PitchingStat.week == week) & (PitchingStat.game == game_num) & + ((PitchingStat.team == teams[0]) | (PitchingStat.team == teams[1])) + ) + + # Update season stats with freshly submitted for regular season games + if (season > 2) and week <= 22: + s_type = 'Regular' + else: + s_type = 'Post' + + for line in this_game_stats: + this_season = PitchingSeason.get_or_none( + player=line.player, season=line.season, season_type=s_type + ) + if not this_season: + this_season = PitchingSeason(player=line.player, season=line.season, season_type=s_type) + this_season.save() + + this_career = PitchingCareer.get_or_none(name=line.player.name) + if not this_career: + this_career = PitchingCareer(name=line.player.name) + this_career.save() + + this_season.ip += line.ip + this_season.hit += line.hit + this_season.run += line.run + this_season.erun += line.erun + this_season.so += line.so + this_season.bb += line.bb + this_season.hbp += line.hbp + this_season.wp += line.wp + this_season.balk += line.balk + this_season.hr += line.hr + this_season.ir += line.ir + this_season.irs += line.irs + this_season.gs += line.gs + this_season.win += line.win + this_season.loss += line.loss + this_season.hold += line.hold + this_season.sv += line.sv + this_season.bsv += line.bsv + this_season.game += 1 + + if s_type == 'Regular': + this_career.ip += line.ip + this_career.hit += line.hit + this_career.run += line.run + this_career.erun += line.erun + this_career.so += line.so + this_career.bb += line.bb + this_career.hbp += line.hbp + this_career.wp += line.wp + this_career.balk += line.balk + this_career.hr += line.hr + this_career.ir += line.ir + this_career.irs += line.irs + this_career.gs += line.gs + this_career.win += line.win + this_career.loss += line.loss + this_career.hold += line.hold + this_career.sv += line.sv + this_career.bsv += line.bsv + this_career.game += 1 + + this_season.save() + this_career.save() + + db.close() + raise HTTPException(status_code=200, detail=f'{len(all_stats)} of {stats.count} lines have been added') + + +@app.delete('/api/v1/pitchingstats') +async def v1_pitchingstats_delete(game: GameModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to delete pitching stats') + + # Get teams + try: + away_team = Team.get_by_id(game.away_team_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Away team with id {game.away_team_id} not found') + try: + home_team = Team.get_by_id(game.home_team_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Away team with id {game.home_team_id} not found') + + # Check for matchup + this_matchup = Schedule.select_season(Current.latest().season).where( + (Schedule.awayteam == away_team) & (Schedule.hometeam == home_team) & (Schedule.week == game.week) + ) + if this_matchup.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No game found between {away_team.abbrev} and {home_team.abbrev} ' + f'in week {game.week}') + + # # Check for game stats + # this_game_stats = PitchingStat.select().where( + # (PitchingStat.season == game.season) & (PitchingStat.week == game.week) & + # ((PitchingStat.team == away_team) | (PitchingStat.team == home_team)) + # ) + # if game.game_num: + # this_game_stats = this_game_stats.where(PitchingStat.game == game.game_num) + # if this_game_stats.count() == 0: + # db.close() + # raise HTTPException(status_code=404, detail=f'No stats found for {away_team.abbrev} at {home_team.abbrev} in ' + # f'that matchup') + # + # # Remove stats from season lines + # for line in this_game_stats: + # this_season = PitchingSeason.get_or_none(player=line.player, season=line.player.season) + # if not this_season: + # db.close() + # raise HTTPException(status_code=404, detail=f'Could not find existing pitching season stats ' + # f'for {line.player.name} (ID: {line.player.id})') + # 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() + # + # # this_career.ip -= line.ip + # this_season.ip -= line.ip + # # this_career.hit -= line.hit + # this_season.hit -= line.hit + # # this_career.run -= line.run + # this_season.run -= line.run + # # this_career.erun -= line.erun + # this_season.erun -= line.erun + # # this_career.so -= line.so + # this_season.so -= line.so + # # this_career.bb -= line.bb + # this_season.bb -= line.bb + # # this_career.hbp -= line.hbp + # this_season.hbp -= line.hbp + # # this_career.wp -= line.wp + # this_season.wp -= line.wp + # # this_career.balk -= line.balk + # this_season.balk -= line.balk + # # this_career.hr -= line.hr + # this_season.hr -= line.hr + # # this_career.gs -= line.gs + # this_season.gs -= line.gs + # # this_career.win -= line.win + # this_season.win -= line.win + # # this_career.loss -= line.loss + # this_season.loss -= line.loss + # # this_career.hold -= line.hold + # this_season.hold -= line.hold + # this_season.sv -= line.sv + # # this_career.sv -= line.sv + # this_season.bsv -= line.bsv + # # this_career.bsv -= line.bsv + # this_season.game -= 1 + # # this_career.game -= 1 + # + # this_season.save() + # # this_career.save() + # + # this_career.recalculate() + + # Delete the stats + delete_query = PitchingStat.delete().where( + (PitchingStat.season == game.season) & (PitchingStat.week == game.week) & + ((PitchingStat.team == away_team) | (PitchingStat.team == home_team)) + ) + if game.game_num: + delete_query = delete_query.where(PitchingStat.game == game.game_num) + + count = delete_query.execute() + db.close() + if count > 0: + raise HTTPException(status_code=200, detail=f'Removed {count} pitching stat lines') + else: + raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; ' + f'I did not delete any records') + + +@app.get('/api/v1/fieldingstats/s{season}/{s_type}') +async def v1_fieldingstats_get( + s_type: str, season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None, + player_name: Optional[str] = None, player_id: Optional[int] = None, week_start: Optional[int] = None, + week_end: Optional[int] = None, position: Optional[str] = None, return_type: Optional[str] = 'json', + csv: Optional[bool] = False): + """ + :param season: int + :param s_type: string (regular, post, combined) + :param team_abbrev: string + :param player_name: string + :param player_id: integer, overrides player_name + :param week_start: integer + :param week_end: integer + :param position: string + :return: + """ + if s_type.lower() == 'post': + all_stats = BattingStat.post_season(season) + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No postseason stats found for season {season}') + elif s_type.lower() == 'combined': + all_stats = BattingStat.combined_season(season) + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No stats found for season {season}') + else: + all_stats = BattingStat.regular_season(season) + if not all_stats: + db.close() + return {} + + all_stats = all_stats.where( + (BattingStat.xch > 0) | (BattingStat.pb > 0) | (BattingStat.sbc > 0) + ) + + if position: + all_stats = all_stats.where(BattingStat.pos == position.upper()) + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_stats = all_stats.where(BattingStat.team == this_team) + + if player_name or player_id: + if player_id: + try: + this_player = Player.get_by_id(player_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + else: + this_player = Player.get_season(player_name, season) + if not this_player: + db.close() + raise HTTPException(status_code=404, detail=f'Player {player_name} not found') + + all_stats = all_stats.where(BattingStat.player == this_player) + + start = 0 + end = Schedule.select(fn.MAX(Schedule.week)).scalar() + if week_start: + start = week_start + + if week_end: + end = week_end + + if start > end: + db.close() + raise HTTPException(status_code=404, + detail=f'Start week {start} is after end week {end} - cannot pull stats') + + all_stats = all_stats.where( + (BattingStat.week >= start) & (BattingStat.week <= end) + ) + + if return_type == 'csv' or csv: + stats_list = [['season', 'week', 'game_num', 'player', 'team', 'pos', 'xch', 'xhit', 'error', + 'pb', 'sbc', 'csc', 'roba', 'robs', 'raa', 'rto']] + for line in all_stats: + stats_list.append( + [ + line.season, line.week, line.game, line.player.name, line.team.abbrev, line.pos, line.xch, + line.xhit, line.error, line.pb, line.sbc, line.csc, line.roba, line.robs, line.raa, line.rto + ] + ) + return_stats = DataFrame(stats_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_stats, media_type='text/csv') + else: + return_stats = {} + for line in all_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.post('/api/v1/battingseasons/recalculate') +async def v1_battingseasons_recalculate( + season: Optional[int] = None, team_id: Optional[int] = None, player_id: Optional[int] = None, + token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats') + + all_players = BattingSeason.select() + + if season: + all_players = all_players.where(BattingSeason.season == season) + if team_id: + try: + this_team = Team.get_by_id(team_id) + all_players = all_players.join(Player).where(BattingSeason.player.team == this_team) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team id {team_id} not found') + if player_id: + try: + this_player = Player.get_by_id(player_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + + all_players = all_players.where(BattingSeason.player == this_player) + if all_players.count() == 0: + if BattingStat.select().where(BattingStat.player == this_player).count() > 0: + this_season = BattingSeason(player=this_player, season=this_player.season) + this_season.save() + else: + db.close() + raise HTTPException(status_code=404, detail=f'No batting stats found for player id {player_id}') + + for line in all_players: + count = line.recalculate() + + db.close() + raise HTTPException(status_code=200, detail=f'Recalculated {all_players.count()} batting seasons') + + +@app.post('/api/v1/pitchingseasons/recalculate') +async def v1_pitchingseasons_recalculate( + season: Optional[int] = None, team_id: Optional[int] = None, player_id: Optional[int] = None, + token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats') + + all_players = PitchingSeason.select() + + if season: + all_players = all_players.where(PitchingSeason.season == season) + if team_id: + try: + this_team = Team.get_by_id(team_id) + all_players = all_players.join(Player).where(PitchingSeason.player.team == this_team) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team id {team_id} not found') + if player_id: + try: + this_player = Player.get_by_id(player_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + + all_players = all_players.where(PitchingSeason.player == this_player) + if all_players.count() == 0: + if PitchingStat.select().where(PitchingStat.player == this_player).count() > 0: + this_season = PitchingSeason(player=this_player, season=this_player.season) + this_season.save() + else: + db.close() + raise HTTPException(status_code=404, detail=f'No pitching stats found for player id {player_id}') + + for line in all_players: + count = line.recalculate() + + db.close() + raise HTTPException(status_code=200, detail=f'Recalculated {all_players.count()} pitching seasons') + + +@app.post('/api/v1/fieldingseasons/recalculate') +async def v1_fieldingseasons_recalculate( + season: Optional[int] = None, team_id: Optional[int] = None, player_id: Optional[int] = None, + token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats') + + all_players = FieldingSeason.select() + + if season: + all_players = all_players.where(FieldingSeason.season == season) + if team_id: + try: + this_team = Team.get_by_id(team_id) + all_players = all_players.join(Player).where(FieldingSeason.player.team == this_team) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team id {team_id} not found') + if player_id: + try: + this_player = Player.get_by_id(player_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + + all_players = all_players.where(FieldingSeason.player == this_player) + if all_players.count() == 0: + search_query = BattingStat.select().where( + (BattingStat.player == this_player) & (BattingStat.xch + BattingStat.sbc > 0) + ) + if search_query.count() > 0: + pos_list = [] + for x in search_query: + pos_list.append(x.pos) + for x in pos_list: + this_season = FieldingSeason(player=this_player, season=this_player.season, pos=x) + this_season.save() + else: + db.close() + raise HTTPException(status_code=404, detail=f'No fielding stats found for player id {player_id}') + + for line in all_players: + count = line.recalculate() + logging.info(count) + + db.close() + raise HTTPException(status_code=200, detail=f'Recalculated {all_players.count()} fielding seasons') + + +# @app.post('/api/v1/seasonstats/recalculate') +# async def v1_seasonstats_recalculate( +# which: str, season_start: int, season_end: int, mgr_start: int, mgr_end: int, +# token: str = Depends(oauth2_scheme)): +# if not valid_token(token): +# db.close() +# raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats') +# +# if mgr_start > mgr_end: +# db.close() +# raise HTTPException(status_code=404, detail=f'mgr_start may not be greater than mgr_end') +# if season_start > season_end: +# db.close() +# raise HTTPException(status_code=404, detail=f'season_start may not be greater than season_end') +# if which not in ['batting', 'fielding', 'pitching']: +# db.close() +# raise HTTPException(status_code=404, detail=f'Invalid which value') +# +# x = season_start +# while x <= season_end: +# y = mgr_start +# while y <= mgr_end: +# if which == 'batting': +# BattingSeason.recalculate(x, y) +# elif which == 'pitching': +# PitchingSeason.recalculate(x, y) +# elif which == 'fielding': +# FieldingSeason.recalculate(x, y) +# +# db.close() +# raise HTTPException(status_code=200, detail='Season stats have been recalculated') + + +@app.post('/api/v1/careerstats/recalculate') +async def v1_careerstats_recalculate(which: str, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats') + + if which == 'all': + BattingCareer.recalculate() + PitchingCareer.recalculate() + FieldingCareer.recalculate() + elif which == 'batting': + BattingCareer.recalculate() + elif which == 'pitching': + PitchingCareer.recalculate() + elif which == 'fielding': + FieldingCareer.recalculate() + + db.close() + raise HTTPException(status_code=200, detail='Career stats have been recalculated') + + +@app.get('/api/v1/standings') +async def v1_standings( + season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, return_type: Optional[str] = 'json', + league_abbrev: Optional[str] = None, division_abbrev: Optional[str] = None, csv: Optional[bool] = False): + """ + :param season: int + :param team_abbrev: string, overrides league/division_abbrev + :param league_abbrev: string + :param division_abbrev: string + """ + standings = Standings.select_season(season) + + if standings.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for season {season}') + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + db.close() + return model_to_dict(Standings.get_or_none(Standings.team == this_team)) + + if league_abbrev: + these_divisions = Division.select().where(fn.Lower(Division.league_abbrev) == league_abbrev.lower()) + if these_divisions.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for league {league_abbrev}') + + standings = standings.where(Standings.team.division << these_divisions) + + if division_abbrev: + this_division = Division.select().where(fn.Lower(Division.division_abbrev) == division_abbrev.lower()) + if not this_division: + db.close() + raise HTTPException(status_code=404, detail=f'No output for division {division_abbrev}') + + standings = standings.where(Standings.team.division << this_division) + + 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) + + div_teams = [team_stan for team_stan in standings] + div_teams.sort(key=lambda team: win_pct(team), reverse=True) + + if return_type == 'csv' or csv: + standings_list = [ + ['season', 'team', 'league', 'division', 'wins', 'losses', 'run diff', 'div gb', 'div enum', 'wc gb', + 'wc enum', 'home wins', 'home losses', 'away wins', 'away losses', 'last 8 wins', 'last 8 losses', + 'streak wl', 'streak num', 'one run wins', 'one run losses', 'pythag_wins', 'pythag losses', + 'wins v bote', 'losses v bote', 'wins v dc', 'losses v dc', 'wins v nlw', 'losses v nlw'] + ] + for line in div_teams: + standings_list.append( + [line.team.season, line.team.abbrev, line.team.division.league_abbrev, line.team.division.division_name, + line.wins, line.losses, line.run_diff, line.div_gb, line.div_e_num, line.wc_gb, line.wc_e_num, + line.home_wins, line.home_losses, line.away_wins, line.away_losses, line.last8_wins, line.last8_losses, + line.streak_wl, line.streak_num, line.one_run_wins, line.one_run_losses, line.pythag_wins, + line.pythag_losses, line.div1_wins, line.div1_losses, line.div2_wins, line.div2_losses, line.div3_wins, + line.div3_losses] + ) + return_stats = DataFrame(standings_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_stats, media_type='text/csv') + else: + return_standings = {} + for line in div_teams: + return_standings[f'{line.team.abbrev}'] = model_to_dict(line) + + db.close() + return return_standings + + +@app.patch('/api/v1/standings/{stan_id}') +async def v1_standings_patch( + stan_id, wins: Optional[int] = None, losses: Optional[int] = None, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch standings') + + try: + this_stan = Standings.get_by_id(stan_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {stan_id}') + + if wins: + this_stan.wins = wins + if losses: + this_stan.losses = losses + + this_stan.save() + db.close() + + return model_to_dict(this_stan) + + +@app.post('/api/v1/standings/s{season}/recalculate') +async def v1_standings_recalculate(season: int, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to calculate stats') + + code = Standings.recalculate(season) + db.close() + if code == 69: + HTTPException(status_code=500, detail=f'Error recreating Standings rows') + raise HTTPException(status_code=200, detail=f'Just recalculated standings for season {season}') + + +@app.get('/api/v1/battingseasons') +async def v1_battingseasons_get( + season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None, + season_type: Optional[str] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False): + """ + :param season: int + :param player_id: int, overrides other args + :param player_name: int, overrides team_abbrev + :param team_abbrev: str, lowest priority arg + :param sort: str, pa-desc; wara-desc + """ + all_stats = BattingSeason.select().where(BattingSeason.pa > 0) + + if season: + all_stats = all_stats.where(BattingSeason.season == season) + + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for season {season}') + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_players = Player.select_season(season).where(Player.team == this_team) + all_stats = all_stats.where(BattingSeason.player << all_players) + + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for team {team_abbrev}') + + if season_type: + all_stats = all_stats.where(fn.Lower(BattingSeason.season_type) == season_type.lower()) + + if sort: + if sort == 'pa-desc': + all_stats = all_stats.order_by(-BattingSeason.pa) + elif sort == 'wara-desc': + all_stats = all_stats.order_by(-BattingSeason.player.wara) + + if return_type == 'csv' or csv: + stat_list = [ + ['season', 'season_type', 'player', 'pa', 'ab', 'run', 'hit', 'rbi', 'double', 'triple', + 'hr', 'bb', 'so', 'hbp', 'sac', 'ibb', 'gidp', 'sb', 'cs', 'bphr', 'bpfo', 'bp1b', 'bplo', + 'xba', 'xbt'] + ] + for line in all_stats: + stat_list.append( + [line.player.season, line.season_type, line.player.name, line.pa, line.ab, line.run, line.hit, + line.rbi, line.double, line.triple, line.hr, line.bb, line.so, line.hbp, line.sac, line.ibb, line.gidp, + line.sb, line.cs, line.bphr, line.bpfo, line.bp1b, line.bplo, line.xba, line.xbt] + ) + return_stats = DataFrame(stat_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_stats, media_type='text/csv') + else: + return_stats = {} + for line in all_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.get('/api/v1/battingseasons/{id_or_name}') +async def v1_battingseasons_get_player(id_or_name): + try: + this_player = Player.get_by_id(id_or_name) + except Exception as e: + this_player = None + + if this_player: + these_stats = BattingSeason.get_or_none(BattingSeason.player == this_player) + if these_stats: + db.close() + return model_to_dict(these_stats) + else: + db.close() + return {} + else: + these_players = Player.select().where(fn.Lower(Player.name) == id_or_name.lower()) + if these_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Player {id_or_name} not found') + + these_stats = BattingSeason.select().where(BattingSeason.player << these_players) + return_stats = {} + for line in these_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.get('/api/v1/pitchingseasons') +async def v1_pitchingseasons_get( + season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None, + season_type: Optional[str] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False): + """ + :param season: int + :param team_abbrev: str, lowest priority arg + :param sort: str, ip-desc; wara-desc + """ + all_stats = PitchingSeason.select_season(season) + + if season: + all_stats = all_stats.where(PitchingSeason.season == season) + + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for season {season}') + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_players = Player.select_season(season).where(Player.team == this_team) + all_stats = all_stats.where(PitchingSeason.player << all_players) + + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for team {team_abbrev}') + + if season_type: + all_stats = all_stats.where(fn.Lower(PitchingSeason.season_type) == season_type.lower()) + + if sort: + logging.warning('In sort!') + if sort == 'ip-desc': + logging.warning('Matched ip-desc') + all_stats = all_stats.order_by(-PitchingSeason.ip) + logging.warning(all_stats) + elif sort == 'wara-desc': + all_stats = all_stats.order_by(-PitchingSeason.player.wara) + + if return_type == 'csv' or csv: + stat_list = [ + ['season', 'season_type', 'player', 'ip', 'hit', 'run', 'erun', 'so', 'bb', 'hbp', + 'wp', 'balk', 'hr', 'ir', 'irs', 'gs', 'win', 'loss', 'hold', 'sv', 'bsv', 'games'] + ] + for line in all_stats: + stat_list.append( + [line.player.season, line.season_type, line.player.name, line.ip, line.hit, line.run, line.erun, + line.so, line.bb, line.hbp, line.wp, line.balk, line.hr, line.ir, line.irs, line.gs, line.win, + line.loss, line.hold, line.sv, line.bsv, line.game] + ) + return_stats = DataFrame(stat_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_stats, media_type='text/csv') + else: + return_stats = {} + for line in all_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.get('/api/v1/pitchingseasons/{id_or_name}') +async def v1_pitchingseasons_get_player(id_or_name): + try: + this_player = Player.get_by_id(id_or_name) + except Exception as e: + this_player = None + + if this_player: + these_stats = PitchingSeason.get_or_none(PitchingSeason.player == this_player) + if these_stats: + db.close() + return model_to_dict(these_stats) + else: + db.close() + return {} + else: + these_players = Player.select().where(fn.Lower(Player.name) == id_or_name.lower()) + if these_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Player {id_or_name} not found') + + these_stats = PitchingSeason.select().where(PitchingSeason.player << these_players) + return_stats = {} + for line in these_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.get('/api/v1/fieldingseasons') +async def v1_fieldingseasons_get( + season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None, position: Optional[str] = None, + season_type: Optional[str] = None, sort: Optional[str] = None, return_type: Optional[str] = 'json', + csv: Optional[bool] = False): + """ + :param season: int + :param player_id: int, overrides other args + :param player_name: int, overrides team_abbrev + :param team_abbrev: str, lowest priority arg + :param position: str + :param sort: str, pa-desc; wara-desc + """ + all_stats = FieldingSeason.select_season(season).where((FieldingSeason.xch + FieldingSeason.sbc) > 0) + + if season: + all_stats = all_stats.where(FieldingSeason.season == season) + + if position: + all_stats = all_stats.where(FieldingSeason.pos == position.upper()) + + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for season {season}') + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + all_players = Player.select_season(season).where(Player.team == this_team) + all_stats = all_stats.where(FieldingSeason.player << all_players) + + if all_stats.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for team {team_abbrev}') + + if season_type: + all_stats = all_stats.where(fn.Lower(FieldingSeason.season_type) == season_type.lower()) + + if sort: + if sort == 'xch-desc': + all_stats = all_stats.order_by(-(FieldingSeason.xch + FieldingSeason.sbc)) + elif sort == 'wara-desc': + all_stats = all_stats.order_by(-FieldingSeason.player.wara) + + if return_type == 'csv' or csv: + stat_list = [ + ['season', 'season_type', 'player', 'pos', 'xch', 'xhit', 'error', 'pb', 'sbc', 'csc', 'roba', 'robs', + 'raa', 'rto'] + ] + for line in all_stats: + stat_list.append( + [line.player.season, line.season_type, line.player.name, line.pos, line.xch, line.xhit, line.error, + line.pb, line.sbc, line.csc, line.roba, line.robs, line.raa, line.rto] + ) + return_stats = DataFrame(stat_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_stats, media_type='text/csv') + else: + return_stats = {} + for line in all_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.get('/api/v1/fieldingseasons/{id_or_name}') +async def v1_fieldingseasons_get_player(id_or_name): + try: + this_player = Player.get_by_id(id_or_name) + except Exception as e: + this_player = None + + if this_player: + these_stats = FieldingSeason.get_or_none(FieldingSeason.player == this_player) + if these_stats: + db.close() + return model_to_dict(these_stats) + else: + db.close() + return {} + else: + these_players = Player.select().where(fn.Lower(Player.name) == id_or_name.lower()) + if these_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Player {id_or_name} not found') + + these_stats = FieldingSeason.select().where(FieldingSeason.player << these_players) + return_stats = {} + for line in these_stats: + return_stats[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_stats + + +@app.get('/api/v1/draftpicks') +async def v1_draftpicks_get( + season: int = SEASON_DEFAULT, owner_team_abbrev: Optional[str] = None, + orig_team_abbrev: Optional[str] = None, pick_round_start: Optional[int] = None, + pick_round_end: Optional[int] = None, traded: Optional[bool] = None, overall: Optional[int] = None, + team_season: Optional[int] = None, overall_start: Optional[int] = None, overall_end: Optional[int] = None, + return_type = 'json', csv: Optional[bool] = False): + """ + :param season: int + :param owner_team_abbrev: string + :param orig_team_abbrev: string + :param pick_round_start: int + :param pick_round_end: int + :param traded: bool + :param overall: int + """ + all_picks = DraftPick.select_season(season) + if all_picks.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No picks found for season {season}') + + if owner_team_abbrev: + if not team_season: + team_season = season + this_team = Team.get_season(owner_team_abbrev, team_season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {owner_team_abbrev} not found') + + all_picks = all_picks.where(DraftPick.owner == this_team) + if all_picks.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for team {owner_team_abbrev}') + + if orig_team_abbrev: + if not team_season: + team_season = season + this_team = Team.get_season(orig_team_abbrev, team_season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {orig_team_abbrev} not found') + + all_picks = all_picks.where(DraftPick.origowner == this_team) + if all_picks.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for team {orig_team_abbrev}') + + if pick_round_start and pick_round_end: + if pick_round_start > pick_round_end: + db.close() + raise HTTPException(status_code=400, detail=f'Cannot search for pick with end round before start round') + + if pick_round_start: + all_picks = all_picks.where(DraftPick.round >= pick_round_start) + if all_picks.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No picks found beginning in round {pick_round_start}') + + if pick_round_end: + all_picks = all_picks.where(DraftPick.round <= pick_round_end) + if all_picks.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No picks found ending with round {pick_round_end}') + + if traded: + all_picks = all_picks.where(DraftPick.origowner != DraftPick.owner) + + if overall is not None: + all_picks = all_picks.where(DraftPick.overall == overall) + + if overall_start: + all_picks = all_picks.where(DraftPick.overall >= overall_start) + + if overall_end: + all_picks = all_picks.where(DraftPick.overall <= overall_end) + + logging.info('Trying to spit out draft picks') + if return_type == 'csv' or csv: + logging.info('Working on csv') + pick_list = [ + ['season', 'overall', 'round', 'orig_owner', 'owner', 'player', 'swar'] + ] + logging.info(f'built the pick_list / len(all_picks): {len(all_picks)}') + for line in all_picks: + logging.info(f'line: {line}') + pick_list.append( + [line.season, line.overall, line.round, line.origowner.abbrev, line.owner.abbrev, + f'{line.player.name if line.player else ""}', f'{line.player.wara if line.player else ""}'] + ) + logging.info('filled the pick_list') + return_picks = DataFrame(pick_list).to_csv(header=False, index=False) + + db.close() + return Response(content=return_picks, media_type='text/csv') + else: + return_picks = {} + for line in all_picks: + return_picks[f'{line.id}'] = model_to_dict(line) + + db.close() + return return_picks + + +@app.post('/api/v1/draftpicks') +async def v1_draftpicks_post(picks: DraftPickModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post draft picks') + all_picks = [] + + for x in picks.picks: + this_origowner = None + if x.origowner_id: + try: + this_origowner = Team.get_by_id(x.origowner_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'No owner found with id {x.origowner_id}') + + this_owner = None + if x.owner_id: + try: + this_owner = Team.get_by_id(x.owner_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'No owner found with id {x.owner_id}') + else: + this_owner = this_origowner + + this_player = None + if x.player_id: + try: + this_player = Player.get_by_id(x.player_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'No player found with id {x.player_id}') + + this_pick = DraftPick( + season=x.season, + overall=x.overall, + round=x.round, + origowner=this_origowner, + owner=this_owner, + player=this_player + ) + + all_picks.append(this_pick) + + with db.atomic(): + DraftPick.bulk_create(all_picks, batch_size=15) + db.close() + + raise HTTPException(status_code=200, detail=f'Created {len(all_picks)} draft picks') + + +# player_id is not checked for [int] to enable False as a clear parameter +@app.patch('/api/v1/draftpicks/{pick_id}') +async def v1_draftpicks_patch( + pick_id: int, overall: Optional[int] = None, round: Optional[int] = None, orig_owner_id: Optional[int] = None, + owner_id: Optional[int] = None, season: Optional[int] = None, player_id=None, + token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch draft picks') + + try: + this_pick = DraftPick.get_by_id(pick_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {pick_id}') + + if overall: + this_pick.overall = overall + if season: + this_pick.season = season + if round: + this_pick.round = round + if orig_owner_id: + try: + orig_owner = Team.get_by_id(orig_owner_id) + this_pick.origowner = orig_owner + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {orig_owner_id}') + if owner_id: + try: + new_owner = Team.get_by_id(owner_id) + this_pick.owner = new_owner + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {owner_id}') + if player_id is not None: + if player_id.lower() == 'false': + this_pick.player = None + else: + try: + this_player = Player.get_by_id(player_id) + this_pick.player = this_player + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No player found with id {player_id}') + + this_pick.save() + db.close() + return model_to_dict(this_pick) + + +@app.get('/api/v1/draftpicks/{pick_id}') +async def v1_draftpicks_get_one(pick_id): + try: + this_pick = DraftPick.get_or_none(DraftPick.id == pick_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Draft pick id {pick_id} not found') + + db.close() + return model_to_dict(this_pick) + + +@app.get('/api/v1/draftdata') +async def v1_draftdata(): + draft_data = DraftData.get_or_none() + db.close() + + if not draft_data: + db.close() + raise HTTPException(status_code=404, detail=f'No draft data found') + else: + db.close() + return model_to_dict(draft_data) + + +@app.patch('/api/v1/draftdata') +async def v1_draftdata_patch( + currentpick: Optional[int] = None, timer: Optional[bool] = None, + pick_deadline: Optional[datetime.datetime] = None, result_channel: Optional[int] = None, + ping_channel: Optional[int] = None, pick_minutes: Optional[int] = None, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch draftdata') + + draft_data = DraftData.get_or_none() + + if not draft_data: + db.close() + raise HTTPException(status_code=404, detail=f'No draft data found') + + if currentpick is not None: + draft_data.currentpick = currentpick + if timer is not None: + draft_data.timer = timer + if pick_deadline is not None: + draft_data.pick_deadline = pick_deadline + if result_channel is not None: + draft_data.result_channel = result_channel + if ping_channel is not None: + draft_data.ping_channel = ping_channel + if pick_minutes is not None: + draft_data.pick_minutes = pick_minutes + + saved = draft_data.save() + db.close() + + if saved == 1: + return model_to_dict(draft_data) + else: + raise HTTPException(status_code=500, detail='Updating draft data failed') + + +@app.get('/api/v1/leaders') +async def v1_leaders( + group: str = 'batting', season=SEASON_DEFAULT, team_abbrev: Optional[str] = None, + league_abbrev: Optional[str] = None, division_abbrev: Optional[str] = None): + current = Current.get_or_none(season=season) + all_leaders = None + if season == 1: + min_ip = 68 + min_pa = 204 + min_ch_high = 34 + min_ch_mid = 17 + min_ch_low = 8 + min_ch_pit = 4 + elif season == 2: + min_ip = 76 + min_pa = 228 + min_ch_high = 38 + min_ch_mid = 19 + min_ch_low = 9 + min_ch_pit = 4 + elif int(season) < SEASON_DEFAULT: + min_ip = 88 + min_pa = 264 + min_ch_high = 44 + min_ch_mid = 22 + min_ch_low = 11 + min_ch_pit = 5 + else: + min_ip = ((current.week - 1) * 4) if current.week > 5 else current.week * 2 + min_pa = ((current.week - 1) * 12) if current.week > 5 else current.week * 6 + min_ch_high = ((current.week - 1) * 2) if current.week > 5 else current.week + min_ch_mid = (min_ch_high / 2) if (min_ch_high / 2) > 1 else 1 + min_ch_low = (min_ch_high / 4) if (min_ch_high / 3) > 1 else 1 + min_ch_pit = (min_ch_high / 8) if (min_ch_high / 6) > 1 else 1 + + if group == 'pitching': + all_leaders = { + 'Starter Wins': {'abbrev': 'W', 'leaders': []}, + 'Wins': {'abbrev': 'W', 'leaders': []}, + 'Earned Run Average': {'abbrev': 'ERA', 'leaders': []}, + 'Strikeouts': {'abbrev': 'K', 'leaders': []}, + 'Innings Pitched': {'abbrev': 'IP', 'leaders': []}, + 'Strikeouts per Nine': {'abbrev': 'K/9', 'leaders': []}, + 'Walks + Hits / IP': {'abbrev': 'WHIP', 'leaders': []}, + 'Saves': {'abbrev': 'SV', 'leaders': []}, + 'Walks Allowed': {'abbrev': 'BB', 'leaders': []}, + 'Runs Allowed': {'abbrev': 'R', 'leaders': []}, + 'Earned Runs Allowed': {'abbrev': 'ER', 'leaders': []}, + 'Blown Saves': {'abbrev': 'BSV', 'leaders': []}, + 'Hit By Pitches Allowed': {'abbrev': 'HBP', 'leaders': []}, + 'Hits Allowed': {'abbrev': 'H', 'leaders': []}, + 'Home Runs Allowed': {'abbrev': 'HR', 'leaders': []}, + 'Wild Pitches': {'abbrev': 'WP', 'leaders': []}, + 'Holds': {'abbrev': 'HD', 'leaders': []}, + 'Walks Per Nine': {'abbrev': 'BB/9', 'leaders': []}, + 'Strikeouts Per Walk': {'abbrev': 'K/BB', 'leaders': []}, + 'Losses': {'abbrev': 'L', 'leaders': []}, + 'Inherited Runners': {'abbrev': 'IR', 'leaders': []}, + 'Inherited Runners Scored': {'abbrev': 'IRS', 'leaders': []}, + 'Runners Stranded %': {'abbrev': 'RS%', 'leaders': []}, + } + + top_swin = (PitchingStat + .select(PitchingStat.player, fn.COUNT(PitchingStat.win).alias('swin')) + .where(PitchingStat.season == season, PitchingStat.gs == 1, PitchingStat.win == 1) + .order_by(-fn.COUNT(PitchingStat.win), PitchingStat.player) + .group_by(PitchingStat.player) + .limit(10)) + top_win = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.win) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.win, PitchingSeason.player) + .limit(10)) + top_ip = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.ip) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.ip, PitchingSeason.player) + .limit(10)) + top_so = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.so) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.so, PitchingSeason.player) + .limit(10)) + top_era = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.erun, PitchingSeason.ip) + .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip) + .order_by(((PitchingSeason.erun * 9) / PitchingSeason.ip), PitchingSeason.player) + .limit(10)) + top_kpn = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.so, PitchingSeason.ip) + .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip) + .order_by(-((PitchingSeason.so * 9) / PitchingSeason.ip), PitchingSeason.player) + .limit(10)) + top_whip = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.hit, PitchingSeason.ip) + .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip) + .order_by(+((PitchingSeason.bb + PitchingSeason.hit) / PitchingSeason.ip), PitchingSeason.player) + .limit(10)) + top_sv = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.sv) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.sv, PitchingSeason.player) + .limit(10)) + top_bb = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bb) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.bb, PitchingSeason.player) + .limit(10)) + top_run = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.run) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.run, PitchingSeason.player) + .limit(10)) + top_erun = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.erun) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.erun, PitchingSeason.player) + .limit(10)) + top_bsv = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bsv) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.bsv, PitchingSeason.player) + .limit(10)) + top_hbp = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.hbp) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.hbp, PitchingSeason.player) + .limit(10)) + top_hit = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.hit) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.hit, PitchingSeason.player) + .limit(10)) + top_hr = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.hr) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.hr, PitchingSeason.player) + .limit(10)) + top_wp = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.wp) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.wp, PitchingSeason.player) + .limit(10)) + top_hd = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.hold) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.hold, PitchingSeason.player) + .limit(10)) + top_bpn = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.ip) + .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip) + .order_by(((PitchingSeason.bb * 9) / PitchingSeason.ip), PitchingSeason.player) + .limit(10)) + top_kpw = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.so) + .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip) + .order_by(-(PitchingSeason.so / PitchingSeason.bb), PitchingSeason.player) + .limit(10)) + top_los = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.loss) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.loss, PitchingSeason.player) + .limit(10)) + top_ir = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.ir) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.ir, PitchingSeason.player) + .limit(10)) + top_irs = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.irs) + .where(PitchingSeason.season == season) + .order_by(-PitchingSeason.irs, PitchingSeason.player) + .limit(10)) + top_irp = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.ir, PitchingSeason.irs) + .where(PitchingSeason.season == season, PitchingSeason.ir >= current.week) + .order_by(-((PitchingSeason.ir - PitchingSeason.irs) / PitchingSeason.ir), PitchingSeason.player) + .limit(10)) + + db.close() + + for x in top_swin: + all_leaders['Starter Wins']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.swin)} + ) + for x in top_win: + all_leaders['Wins']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.win)} + ) + for x in top_ip: + all_leaders['Innings Pitched']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{x.ip:.1f}'} + ) + for x in top_so: + all_leaders['Strikeouts']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.so)} + ) + for x in top_era: + all_leaders['Earned Run Average']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.erun * 9) / x.ip):.2f}'} + ) + for x in top_kpn: + all_leaders['Strikeouts per Nine']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.so * 9) / x.ip):.2f}'} + ) + for x in top_whip: + all_leaders['Walks + Hits / IP']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.bb + x.hit) / x.ip):.2f}'} + ) + for x in top_sv: + all_leaders['Saves']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.sv)} + ) + for x in top_bb: + all_leaders['Walks Allowed']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bb)} + ) + for x in top_run: + all_leaders['Runs Allowed']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.run)} + ) + for x in top_erun: + all_leaders['Earned Runs Allowed']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.erun)} + ) + for x in top_bsv: + all_leaders['Blown Saves']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bsv)} + ) + for x in top_hbp: + all_leaders['Hit By Pitches Allowed']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hbp)} + ) + for x in top_hit: + all_leaders['Hits Allowed']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hit)} + ) + for x in top_hr: + all_leaders['Home Runs Allowed']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hr)} + ) + for x in top_wp: + all_leaders['Wild Pitches']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.wp)} + ) + for x in top_hd: + all_leaders['Holds']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hold)} + ) + for x in top_bpn: + all_leaders['Walks Per Nine']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.bb * 9) / x.ip):.2f}'} + ) + for x in top_kpw: + all_leaders['Strikeouts Per Walk']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{(x.so / x.bb):.2f}'} + ) + for x in top_los: + all_leaders['Losses']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.loss)} + ) + for x in top_ir: + all_leaders['Inherited Runners']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.ir)} + ) + for x in top_irs: + all_leaders['Inherited Runners Scored']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.irs)} + ) + for x in top_irp: + all_leaders['Runners Stranded %']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.ir - x.irs) / x.ir) * 100:.1f}'} + ) + elif group == 'fielding': + all_leaders = { + 'Fielding Chances': {'abbrev': 'X-Ch', 'leaders': []}, + 'Weighted Fielding % (RF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (CF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (LF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (SS)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (3B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (2B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (1B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (C)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (P)': {'abbrev': 'wF%', 'leaders': []}, + 'Errors': {'abbrev': 'E', 'leaders': []}, + 'Hits Allowed': {'abbrev': 'X-Hit', 'leaders': []}, + 'Passed Balls': {'abbrev': 'PB', 'leaders': []}, + 'Steal Attempts Against': {'abbrev': 'SBa', 'leaders': []}, + 'Runners Thrown Out (C)': {'abbrev': 'CSc', 'leaders': []}, + 'Caught Stealing % (Catchers)': {'abbrev': 'CS%', 'leaders': []}, + } + + top_xch = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.sbc) + .where(FieldingSeason.season == season) + .order_by(-(FieldingSeason.xch + FieldingSeason.sbc), FieldingSeason.player) + .limit(10)) + raw_ss = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'SS') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_high))) + raw_cf = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'CF') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_mid))) + raw_2b = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == '2B') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_high))) + raw_3b = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == '3B') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_mid))) + raw_1b = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == '1B') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_mid))) + raw_lf = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'LF') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_low))) + raw_rf = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'RF') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_low))) + raw_ca = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'C') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_mid))) + raw_pi = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'P') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_pit))) + top_err = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.error) + .where(FieldingSeason.season == season) + .order_by(-FieldingSeason.error, FieldingSeason.player) + .limit(10)) + top_hit = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xhit) + .where(FieldingSeason.season == season) + .order_by(-FieldingSeason.xhit, FieldingSeason.player) + .limit(10)) + top_sbc = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.sbc) + .where((FieldingSeason.season == season) & (FieldingSeason.sbc > 0)) + .order_by(-FieldingSeason.sbc, FieldingSeason.player) + .limit(10)) + top_csc = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.csc) + .where((FieldingSeason.season == season) & (FieldingSeason.csc > 0)) + .order_by(-FieldingSeason.csc, FieldingSeason.player) + .limit(10)) + top_csp = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.sbc, FieldingSeason.csc) + .where((FieldingSeason.season == season) & (FieldingSeason.sbc >= min_ch_low)) + .order_by(-(FieldingSeason.csc / FieldingSeason.sbc), FieldingSeason.player) + .limit(10)) + top_pb = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.pb) + .where((FieldingSeason.season == season) & (FieldingSeason.pb > 0)) + .order_by(-FieldingSeason.pb, FieldingSeason.player) + .limit(10)) + # Removed for season 5 + # top_roba = (FieldingSeason + # .select(FieldingSeason.player, FieldingSeason.roba) + # .where((FieldingSeason.season == season) & (FieldingSeason.roba > 0)) + # .order_by(-FieldingSeason.roba, FieldingSeason.player) + # .limit(10)) + # top_robs = (FieldingSeason + # .select(FieldingSeason.player, FieldingSeason.robs) + # .where((FieldingSeason.season == season) & (FieldingSeason.robs > 0)) + # .order_by(-FieldingSeason.robs, FieldingSeason.player) + # .limit(10)) + # top_raa = (FieldingSeason + # .select(FieldingSeason.player, FieldingSeason.raa) + # .where((FieldingSeason.season == season) & (FieldingSeason.raa > 0)) + # .order_by(-FieldingSeason.raa, FieldingSeason.player) + # .limit(10)) + # top_rto = (FieldingSeason + # .select(FieldingSeason.player, FieldingSeason.rto) + # .where((FieldingSeason.season == season) & (FieldingSeason.rto > 0)) + # .order_by(-FieldingSeason.rto, FieldingSeason.player) + # .limit(10)) + + db.close() + + for x in top_xch: + all_leaders['Fielding Chances']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.xch + x.sbc} + ) + for x in top_err: + all_leaders['Errors']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.error} + ) + for x in top_sbc: + all_leaders['Steal Attempts Against']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.sbc} + ) + for x in top_csc: + all_leaders['Runners Thrown Out (C)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.csc} + ) + for x in top_csp: + all_leaders['Caught Stealing % (Catchers)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{x.csc * 100 / x.sbc:.1f}'} + ) + for x in top_hit: + all_leaders['Hits Allowed']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.xhit} + ) + for x in top_pb: + all_leaders['Passed Balls']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.pb} + ) + # for x in top_roba: + # all_leaders['Rob Attempts']['leaders'].append( + # {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.roba} + # ) + # for x in top_robs: + # all_leaders['Rob Successes']['leaders'].append( + # {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.robs} + # ) + # for x in top_raa: + # all_leaders['Runner Advance Attempts']['leaders'].append( + # {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.raa} + # ) + # for x in top_rto: + # all_leaders['Runners Thrown Out (OF)']['leaders'].append( + # {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.rto} + # ) + + for x in raw_ss: + all_leaders['Weighted Fielding % (SS)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_cf: + all_leaders['Weighted Fielding % (CF)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_2b: + all_leaders['Weighted Fielding % (2B)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_3b: + all_leaders['Weighted Fielding % (3B)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_1b: + all_leaders['Weighted Fielding % (1B)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_lf: + all_leaders['Weighted Fielding % (LF)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_rf: + all_leaders['Weighted Fielding % (RF)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_ca: + all_leaders['Weighted Fielding % (C)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_pi: + all_leaders['Weighted Fielding % (P)']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for stat in all_leaders: + if 'Weighted Fielding' in stat: + all_leaders[stat]['leaders'] = sorted( + all_leaders[stat]['leaders'], + key=lambda leader: leader['stat'], + reverse=True + ) + all_leaders[stat]['leaders'] = all_leaders[stat]['leaders'][:10] + else: + # return batting leaders + all_leaders = { + 'Home Runs': {'abbrev': 'HR', 'leaders': []}, + 'Batting Average': {'abbrev': 'AVG', 'leaders': []}, + 'On Base Percentage': {'abbrev': 'OBP', 'leaders': []}, + 'Slugging Percentage': {'abbrev': 'SLG', 'leaders': []}, + 'On Base Plus Slugging': {'abbrev': 'OPS', 'leaders': []}, + 'Weighted On Base Avg': {'abbrev': 'wOBA', 'leaders': []}, + 'Avg on Balls in Play': {'abbrev': 'BABIP', 'leaders': []}, + 'Runs Batted In': {'abbrev': 'RBI', 'leaders': []}, + 'Doubles': {'abbrev': '2B', 'leaders': []}, + 'Triples': {'abbrev': '3B', 'leaders': []}, + 'Hits': {'abbrev': 'H', 'leaders': []}, + 'Plate Appearances': {'abbrev': 'PA', 'leaders': []}, + 'Ground Into Double Play': {'abbrev': 'GIDP', 'leaders': []}, + 'Walks': {'abbrev': 'BB', 'leaders': []}, + 'Strikeouts': {'abbrev': 'K', 'leaders': []}, + 'Hit By Pitch': {'abbrev': 'HBP', 'leaders': []}, + 'Intentional Walks': {'abbrev': 'IBB', 'leaders': []}, + 'Stolen Bases': {'abbrev': 'SB', 'leaders': []}, + 'Caught Stealing': {'abbrev': 'CS', 'leaders': []}, + 'Runs Scored': {'abbrev': 'R', 'leaders': []}, + 'At Bats': {'abbrev': 'AB', 'leaders': []}, + 'No-Doubt Home Runs': {'abbrev': 'NDHR', 'leaders': []}, + 'Ballpark Home Runs': {'abbrev': 'BPHR', 'leaders': []}, + 'Ballpark Flyouts': {'abbrev': 'BPFO', 'leaders': []}, + 'Ballpark Singles': {'abbrev': 'BP1B', 'leaders': []}, + 'Ballpark Lineouts': {'abbrev': 'BPLO', 'leaders': []}, + } + + top_avg = (BattingSeason + .select() + .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season)) + .order_by(-(BattingSeason.hit / BattingSeason.ab), BattingSeason.player) + .limit(10)) + top_hr = (BattingSeason + .select(BattingSeason.player, BattingSeason.hr) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.hr, BattingSeason.player) + .limit(10)) + top_obp = (BattingSeason + .select() + .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season)) + .order_by( + -((BattingSeason.hit + BattingSeason.bb + BattingSeason.hbp + BattingSeason.ibb) / BattingSeason.pa), + BattingSeason.player) + .limit(10)) + top_slg = (BattingSeason + .select() + .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season)) + .order_by(-(((BattingSeason.hr * 4) + (BattingSeason.triple * 3) + (BattingSeason.double * 2) + ( + BattingSeason.hit - BattingSeason.hr - BattingSeason.triple - BattingSeason.double)) / BattingSeason.ab)) + .limit(10)) + top_ops = (BattingSeason + .select() + .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season)) + .order_by(-((BattingSeason.ab * ( + BattingSeason.hit + BattingSeason.bb + BattingSeason.ibb + BattingSeason.hbp) + ( + (BattingSeason.hr * 4) + (BattingSeason.triple * 3) + ( + BattingSeason.double * 2) + ( + BattingSeason.hit - BattingSeason.double - BattingSeason.triple - BattingSeason.hr)) * ( + BattingSeason.ab + BattingSeason.bb + BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp)) / + (BattingSeason.ab * ( + BattingSeason.ab + BattingSeason.bb + BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp)))) + .limit(10)) + top_wob = (BattingSeason + .select() + .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season)) + .order_by(-(((BattingSeason.bb * .69) + (BattingSeason.hbp * .72) + (BattingSeason.hr * 2.1) + ( + BattingSeason.triple * 1.62) + (BattingSeason.double * 1.27) + (( + BattingSeason.hit - BattingSeason.double - BattingSeason.triple - BattingSeason.hr) * .89)) / + ( + BattingSeason.ab + BattingSeason.bb - BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp))) + .limit(10)) + top_bab = (BattingSeason + .select() + .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season)) + .order_by(-((BattingSeason.hit - BattingSeason.hr) / + (BattingSeason.ab - BattingSeason.so - BattingSeason.hr + BattingSeason.sac))) + .limit(10)) + top_rbi = (BattingSeason + .select(BattingSeason.player, BattingSeason.rbi) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.rbi, BattingSeason.player) + .limit(10)) + top_dbl = (BattingSeason + .select(BattingSeason.player, BattingSeason.double) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.double, BattingSeason.player) + .limit(10)) + top_trp = (BattingSeason + .select(BattingSeason.player, BattingSeason.triple) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.triple, BattingSeason.player) + .limit(10)) + top_hit = (BattingSeason + .select(BattingSeason.player, BattingSeason.hit) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.hit, BattingSeason.player) + .limit(10)) + top_pa = (BattingSeason + .select(BattingSeason.player, BattingSeason.pa) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.pa, BattingSeason.player) + .limit(10)) + top_gdp = (BattingSeason + .select(BattingSeason.player, BattingSeason.gidp) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.gidp, BattingSeason.player) + .limit(10)) + top_bb = (BattingSeason + .select(BattingSeason.player, BattingSeason.bb) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.bb, BattingSeason.player) + .limit(10)) + top_so = (BattingSeason + .select(BattingSeason.player, BattingSeason.so) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.so, BattingSeason.player) + .limit(10)) + top_hbp = (BattingSeason + .select(BattingSeason.player, BattingSeason.hbp) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.hbp, BattingSeason.player) + .limit(10)) + top_ibb = (BattingSeason + .select(BattingSeason.player, BattingSeason.ibb) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.ibb, BattingSeason.player) + .limit(10)) + top_sb = (BattingSeason + .select(BattingSeason.player, BattingSeason.sb) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.sb, BattingSeason.player) + .limit(10)) + top_cs = (BattingSeason + .select(BattingSeason.player, BattingSeason.cs) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.cs, BattingSeason.player) + .limit(10)) + top_run = (BattingSeason + .select(BattingSeason.player, BattingSeason.run) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.run, BattingSeason.player) + .limit(10)) + top_ab = (BattingSeason + .select(BattingSeason.player, BattingSeason.ab) + .where(BattingSeason.season == season) + .order_by(-BattingSeason.ab, BattingSeason.player) + .limit(10)) + top_bphr = (BattingSeason + .select(BattingSeason.player, BattingSeason.bphr) + .where((BattingSeason.season == season) & (BattingSeason.bphr > 0)) + .order_by(-BattingSeason.bphr, BattingSeason.player) + .limit(10)) + top_bpfo = (BattingSeason + .select(BattingSeason.player, BattingSeason.bpfo) + .where((BattingSeason.season == season) & (BattingSeason.bpfo > 0)) + .order_by(-BattingSeason.bpfo, BattingSeason.player) + .limit(10)) + top_bp1b = (BattingSeason + .select(BattingSeason.player, BattingSeason.bp1b) + .where((BattingSeason.season == season) & (BattingSeason.bp1b > 0)) + .order_by(-BattingSeason.bp1b, BattingSeason.player) + .limit(10)) + top_bplo = (BattingSeason + .select(BattingSeason.player, BattingSeason.bplo) + .where((BattingSeason.season == season) & (BattingSeason.bplo > 0)) + .order_by(-BattingSeason.bplo, BattingSeason.player) + .limit(10)) + # Removed for season 5 + # top_xba = (BattingSeason + # .select(BattingSeason.player, BattingSeason.xba) + # .where((BattingSeason.season == season) & (BattingSeason.xba > 0)) + # .order_by(-BattingSeason.xba, BattingSeason.player) + # .limit(10)) + # top_xbt = (BattingSeason + # .select(BattingSeason.player, BattingSeason.xbt) + # .where((BattingSeason.season == season) & (BattingSeason.xbt > 0)) + # .order_by(-BattingSeason.xbt, BattingSeason.player) + # .limit(10)) + top_ndhr = (BattingSeason + .select(BattingSeason.player, BattingSeason.bphr, BattingSeason.hr) + .where((BattingSeason.season == season) & (BattingSeason.hr > 0)) + .order_by(-(BattingSeason.hr - BattingSeason.bphr), BattingSeason.player) + .limit(10)) + db.close() + + for x in top_avg: + all_leaders['Batting Average']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{x.hit / x.ab:.3f}'} + ) + for x in top_hr: + all_leaders['Home Runs']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hr)} + ) + for x in top_obp: + all_leaders['On Base Percentage']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.hit + x.bb + x.hbp + x.ibb) / x.pa:.3f}'} + ) + for x in top_slg: + all_leaders['Slugging Percentage']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.hr - x.triple - x.double)) / x.ab:.3f}'} + ) + for x in top_ops: + all_leaders['On Base Plus Slugging']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(x.ab * (x.hit + x.bb + x.ibb + x.hbp) + ((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.double - x.triple - x.hr)) * (x.ab + x.bb + x.ibb + x.sac + x.hbp)) / (x.ab * (x.ab + x.bb + x.ibb + x.sac + x.hbp)):.3f}'} + ) + for x in top_wob: + all_leaders['Weighted On Base Avg']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{(((x.bb * .69) + (x.hbp * .72) + (x.hr * 2.1) + (x.triple * 1.62) + (x.double * 1.27) + ((x.hit - x.double - x.triple - x.hr) * .89)) / (x.ab + x.bb - x.ibb + x.sac + x.hbp)):.3f}'} + ) + for x in top_bab: + all_leaders['Avg on Balls in Play']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, + 'stat': f'{((x.hit - x.hr) / (x.ab - x.so - x.hr + x.sac)):.3f}'} + ) + for x in top_rbi: + all_leaders['Runs Batted In']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.rbi)} + ) + for x in top_dbl: + all_leaders['Doubles']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.double)} + ) + for x in top_trp: + all_leaders['Triples']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.triple)} + ) + for x in top_hit: + all_leaders['Hits']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hit)} + ) + for x in top_pa: + all_leaders['Plate Appearances']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.pa)} + ) + for x in top_gdp: + all_leaders['Ground Into Double Play']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.gidp)} + ) + for x in top_bb: + all_leaders['Walks']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bb)} + ) + for x in top_so: + all_leaders['Strikeouts']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.so)} + ) + for x in top_hbp: + all_leaders['Hit By Pitch']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hbp)} + ) + for x in top_ibb: + all_leaders['Intentional Walks']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.ibb)} + ) + for x in top_sb: + all_leaders['Stolen Bases']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.sb)} + ) + for x in top_cs: + all_leaders['Caught Stealing']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.cs)} + ) + for x in top_run: + all_leaders['Runs Scored']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.run)} + ) + for x in top_ab: + all_leaders['At Bats']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.ab)} + ) + for x in top_bphr: + all_leaders['Ballpark Home Runs']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bphr)} + ) + for x in top_bpfo: + all_leaders['Ballpark Flyouts']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bpfo)} + ) + for x in top_bp1b: + all_leaders['Ballpark Singles']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bp1b)} + ) + for x in top_bplo: + all_leaders['Ballpark Lineouts']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bplo)} + ) + # for x in top_xba: + # all_leaders['Extra Base Attempts']['leaders'].append( + # {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.xba)} + # ) + # for x in top_xbt: + # all_leaders['Extra Bases Taken']['leaders'].append( + # {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.xbt)} + # ) + for x in top_ndhr: + all_leaders['No-Doubt Home Runs']['leaders'].append( + {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{x.hr - x.bphr:.0f}'} + ) + + db.close() + return all_leaders + + +@app.get('/api/v1/single-season-leaders') +async def v1_single_season_leaders_get( + group: str = 'batting', team_abbrev: Optional[str] = None, + league_abbrev: Optional[str] = None, division_abbrev: Optional[str] = None): + all_leaders = None + min_ip = 76 + min_pa = 228 + min_ch_high = 38 + min_ch_mid = 19 + min_ch_low = 9 + min_ch_pit = 5 + weeks = 20 + + if group == 'pitching': + all_leaders = { + # 'Starter Wins': {'abbrev': 'W', 'leaders': []}, + 'Wins': {'abbrev': 'W', 'leaders': []}, + 'Earned Run Average': {'abbrev': 'ERA', 'leaders': []}, + 'Strikeouts': {'abbrev': 'K', 'leaders': []}, + 'Innings Pitched': {'abbrev': 'IP', 'leaders': []}, + 'Strikeouts per Nine': {'abbrev': 'K/9', 'leaders': []}, + 'Walks + Hits / IP': {'abbrev': 'WHIP', 'leaders': []}, + 'Saves': {'abbrev': 'SV', 'leaders': []}, + 'Walks Allowed': {'abbrev': 'BB', 'leaders': []}, + 'Runs Allowed': {'abbrev': 'R', 'leaders': []}, + 'Earned Runs Allowed': {'abbrev': 'ER', 'leaders': []}, + 'Blown Saves': {'abbrev': 'BSV', 'leaders': []}, + 'Hit By Pitches Allowed': {'abbrev': 'HBP', 'leaders': []}, + 'Hits Allowed': {'abbrev': 'H', 'leaders': []}, + 'Home Runs Allowed': {'abbrev': 'HR', 'leaders': []}, + 'Wild Pitches': {'abbrev': 'WP', 'leaders': []}, + 'Holds': {'abbrev': 'HD', 'leaders': []}, + 'Walks Per Nine': {'abbrev': 'BB/9', 'leaders': []}, + 'Strikeouts Per Walk': {'abbrev': 'K/BB', 'leaders': []}, + 'Losses': {'abbrev': 'L', 'leaders': []}, + 'Inherited Runners': {'abbrev': 'IR', 'leaders': []}, + 'Inherited Runners Scored': {'abbrev': 'IRS', 'leaders': []}, + 'Runners Stranded %': {'abbrev': 'RS%', 'leaders': []}, + } + + # top_swin = (PitchingStat + # .select(PitchingStat.player, fn.COUNT(PitchingStat.win).alias('swin')) + # .where(PitchingStat.gs == 1, PitchingStat.win == 1) + # .order_by(-fn.COUNT(PitchingStat.win), PitchingStat.player) + # .group_by(PitchingStat.player) + # .limit(10)) + top_win = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.win) + .order_by(-PitchingSeason.win, PitchingSeason.player) + .limit(10)) + top_ip = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.ip) + .order_by(-PitchingSeason.ip, PitchingSeason.player) + .limit(10)) + top_so = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.so) + .order_by(-PitchingSeason.so, PitchingSeason.player) + .limit(10)) + top_era = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.erun, PitchingSeason.ip) + .where(PitchingSeason.ip >= min_ip) + .order_by(((PitchingSeason.erun * 9) / PitchingSeason.ip), PitchingSeason.player) + .limit(10)) + top_kpn = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.so, PitchingSeason.ip) + .where(PitchingSeason.ip >= min_ip) + .order_by(-((PitchingSeason.so * 9) / PitchingSeason.ip), PitchingSeason.player) + .limit(10)) + top_whip = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.hit, PitchingSeason.ip) + .where(PitchingSeason.ip >= min_ip) + .order_by(+((PitchingSeason.bb + PitchingSeason.hit) / PitchingSeason.ip), PitchingSeason.player) + .limit(10)) + top_sv = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.sv) + .order_by(-PitchingSeason.sv, PitchingSeason.player) + .limit(10)) + top_bb = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bb) + .order_by(-PitchingSeason.bb, PitchingSeason.player) + .limit(10)) + top_run = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.run) + .order_by(-PitchingSeason.run, PitchingSeason.player) + .limit(10)) + top_erun = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.erun) + .order_by(-PitchingSeason.erun, PitchingSeason.player) + .limit(10)) + top_bsv = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bsv) + .order_by(-PitchingSeason.bsv, PitchingSeason.player) + .limit(10)) + top_hbp = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.hbp) + .order_by(-PitchingSeason.hbp, PitchingSeason.player) + .limit(10)) + top_hit = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.hit) + .order_by(-PitchingSeason.hit, PitchingSeason.player) + .limit(10)) + top_hr = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.hr) + .order_by(-PitchingSeason.hr, PitchingSeason.player) + .limit(10)) + top_wp = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.wp) + .order_by(-PitchingSeason.wp, PitchingSeason.player) + .limit(10)) + top_hd = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.hold) + .order_by(-PitchingSeason.hold, PitchingSeason.player) + .limit(10)) + top_bpn = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.ip) + .where(PitchingSeason.ip >= min_ip) + .order_by(((PitchingSeason.bb * 9) / PitchingSeason.ip), PitchingSeason.player) + .limit(10)) + top_kpw = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.so) + .where(PitchingSeason.ip >= min_ip) + .order_by(-(PitchingSeason.so / PitchingSeason.bb), PitchingSeason.player) + .limit(10)) + top_los = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.loss) + .order_by(-PitchingSeason.loss, PitchingSeason.player) + .limit(10)) + top_ir = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.ir) + .order_by(-PitchingSeason.ir, PitchingSeason.player) + .limit(10)) + top_irs = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.irs) + .order_by(-PitchingSeason.irs, PitchingSeason.player) + .limit(10)) + top_irp = (PitchingSeason + .select(PitchingSeason.player, PitchingSeason.ir, PitchingSeason.irs) + .where(PitchingSeason.ir >= weeks) + .order_by(-((PitchingSeason.ir - PitchingSeason.irs) / PitchingSeason.ir), PitchingSeason.player) + .limit(10)) + + db.close() + + # for x in top_swin: + # all_leaders['Starter Wins']['leaders'].append( + # {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.swin)} + # ) + for x in top_win: + all_leaders['Wins']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.win)} + ) + for x in top_ip: + all_leaders['Innings Pitched']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{x.ip:.1f}'} + ) + for x in top_so: + all_leaders['Strikeouts']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.so)} + ) + for x in top_era: + all_leaders['Earned Run Average']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{((x.erun * 9) / x.ip):.2f}'} + ) + for x in top_kpn: + all_leaders['Strikeouts per Nine']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{((x.so * 9) / x.ip):.2f}'} + ) + for x in top_whip: + all_leaders['Walks + Hits / IP']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{((x.bb + x.hit) / x.ip):.2f}'} + ) + for x in top_sv: + all_leaders['Saves']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.sv)} + ) + for x in top_bb: + all_leaders['Walks Allowed']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.bb)} + ) + for x in top_run: + all_leaders['Runs Allowed']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.run)} + ) + for x in top_erun: + all_leaders['Earned Runs Allowed']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.erun)} + ) + for x in top_bsv: + all_leaders['Blown Saves']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.bsv)} + ) + for x in top_hbp: + all_leaders['Hit By Pitches Allowed']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hbp)} + ) + for x in top_hit: + all_leaders['Hits Allowed']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hit)} + ) + for x in top_hr: + all_leaders['Home Runs Allowed']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hr)} + ) + for x in top_wp: + all_leaders['Wild Pitches']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.wp)} + ) + for x in top_hd: + all_leaders['Holds']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.hold)} + ) + for x in top_bpn: + all_leaders['Walks Per Nine']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{((x.bb * 9) / x.ip):.2f}'} + ) + for x in top_kpw: + all_leaders['Strikeouts Per Walk']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.so / x.bb):.2f}'} + ) + for x in top_los: + all_leaders['Losses']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.loss)} + ) + for x in top_ir: + all_leaders['Inherited Runners']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.ir)} + ) + for x in top_irs: + all_leaders['Inherited Runners Scored']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.irs)} + ) + for x in top_irp: + all_leaders['Runners Stranded %']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{((x.ir - x.irs) / x.ir) * 100:.1f}'} + ) + elif group == 'fielding': + all_leaders = { + 'Fielding Chances': {'abbrev': 'X-Ch', 'leaders': []}, + 'Weighted Fielding % (RF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (CF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (LF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (SS)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (3B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (2B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (1B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (C)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (P)': {'abbrev': 'wF%', 'leaders': []}, + 'Errors': {'abbrev': 'E', 'leaders': []}, + 'Hits Allowed': {'abbrev': 'X-Hit', 'leaders': []}, + 'Passed Balls': {'abbrev': 'PB', 'leaders': []}, + 'Steal Attempts Against': {'abbrev': 'SBa', 'leaders': []}, + 'Runners Thrown Out (C)': {'abbrev': 'CSc', 'leaders': []}, + 'Caught Stealing % (Catchers)': {'abbrev': 'CS%', 'leaders': []}, + # 'Rob Attempts': {'abbrev': 'RobA', 'leaders': []}, + # 'Rob Successes': {'abbrev': 'RobS', 'leaders': []}, + # 'Runner Advance Attempts': {'abbrev': 'RAA', 'leaders': []}, + # 'Runners Thrown Out (OF)': {'abbrev': 'RTO', 'leaders': []}, + } + + top_xch = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.sbc) + .order_by(-(FieldingSeason.xch + FieldingSeason.sbc), FieldingSeason.player) + .limit(10)) + raw_ss = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'SS') & (FieldingSeason.xch >= min_ch_high))) + raw_cf = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'CF') & (FieldingSeason.xch >= min_ch_mid))) + raw_2b = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == '2B') & (FieldingSeason.xch >= min_ch_high))) + raw_3b = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == '3B') & (FieldingSeason.xch >= min_ch_mid))) + raw_1b = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == '1B') & (FieldingSeason.xch >= min_ch_mid))) + raw_lf = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'LF') & (FieldingSeason.xch >= min_ch_low))) + raw_rf = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'RF') & (FieldingSeason.xch >= min_ch_low))) + raw_ca = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'C') & (FieldingSeason.xch >= min_ch_mid))) + raw_pi = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error) + .where( + (FieldingSeason.pos == 'P') & (FieldingSeason.xch >= min_ch_pit))) + top_err = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.error) + .order_by(-FieldingSeason.error, FieldingSeason.player) + .limit(10)) + top_hit = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.xhit) + .order_by(-FieldingSeason.xhit, FieldingSeason.player) + .limit(10)) + top_sbc = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.sbc) + .where((FieldingSeason.sbc > 0)) + .order_by(-FieldingSeason.sbc, FieldingSeason.player) + .limit(10)) + top_csc = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.csc) + .where((FieldingSeason.csc > 0)) + .order_by(-FieldingSeason.csc, FieldingSeason.player) + .limit(10)) + top_csp = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.sbc, FieldingSeason.csc) + .where((FieldingSeason.sbc >= min_ch_mid)) + .order_by(-(FieldingSeason.csc / FieldingSeason.sbc), FieldingSeason.player) + .limit(10)) + top_pb = (FieldingSeason + .select(FieldingSeason.player, FieldingSeason.pb) + .where((FieldingSeason.pb > 0)) + .order_by(-FieldingSeason.pb, FieldingSeason.player) + .limit(10)) + # Removed for season 5 + # top_roba = (FieldingSeason + # .select(FieldingSeason.player, FieldingSeason.roba) + # .where((FieldingSeason.roba > 0)) + # .order_by(-FieldingSeason.roba, FieldingSeason.player) + # .limit(10)) + # top_robs = (FieldingSeason + # .select(FieldingSeason.player, FieldingSeason.robs) + # .where((FieldingSeason.robs > 0)) + # .order_by(-FieldingSeason.robs, FieldingSeason.player) + # .limit(10)) + # top_raa = (FieldingSeason + # .select(FieldingSeason.player, FieldingSeason.raa) + # .where((FieldingSeason.raa > 0)) + # .order_by(-FieldingSeason.raa, FieldingSeason.player) + # .limit(10)) + # top_rto = (FieldingSeason + # .select(FieldingSeason.player, FieldingSeason.rto) + # .where((FieldingSeason.rto > 0)) + # .order_by(-FieldingSeason.rto, FieldingSeason.player) + # .limit(10)) + + db.close() + + for x in top_xch: + all_leaders['Fielding Chances']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': x.xch + x.sbc} + ) + for x in top_err: + all_leaders['Errors']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.error} + ) + for x in top_sbc: + all_leaders['Steal Attempts Against']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.sbc} + ) + for x in top_csc: + all_leaders['Runners Thrown Out (C)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.csc} + ) + for x in top_csp: + all_leaders['Caught Stealing % (Catchers)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{x.csc * 100 / x.sbc:.1f}'} + ) + for x in top_hit: + all_leaders['Hits Allowed']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.xhit} + ) + for x in top_pb: + all_leaders['Passed Balls']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.pb} + ) + # for x in top_roba: + # all_leaders['Rob Attempts']['leaders'].append( + # {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.roba} + # ) + # for x in top_robs: + # all_leaders['Rob Successes']['leaders'].append( + # {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.robs} + # ) + # for x in top_raa: + # all_leaders['Runner Advance Attempts']['leaders'].append( + # {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.raa} + # ) + # for x in top_rto: + # all_leaders['Runners Thrown Out (OF)']['leaders'].append( + # {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.rto} + # ) + + for x in raw_ss: + all_leaders['Weighted Fielding % (SS)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_cf: + all_leaders['Weighted Fielding % (CF)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_2b: + all_leaders['Weighted Fielding % (2B)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_3b: + all_leaders['Weighted Fielding % (3B)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_1b: + all_leaders['Weighted Fielding % (1B)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_lf: + all_leaders['Weighted Fielding % (LF)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_rf: + all_leaders['Weighted Fielding % (RF)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_ca: + all_leaders['Weighted Fielding % (C)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_pi: + all_leaders['Weighted Fielding % (P)']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for stat in all_leaders: + if 'Weighted Fielding' in stat: + all_leaders[stat]['leaders'] = sorted( + all_leaders[stat]['leaders'], + key=lambda leader: leader['stat'], + reverse=True + ) + all_leaders[stat]['leaders'] = all_leaders[stat]['leaders'][:10] + else: + # return batting leaders + all_leaders = { + 'Home Runs': {'abbrev': 'HR', 'leaders': []}, + 'Batting Average': {'abbrev': 'AVG', 'leaders': []}, + 'On Base Percentage': {'abbrev': 'OBP', 'leaders': []}, + 'Slugging Percentage': {'abbrev': 'SLG', 'leaders': []}, + 'On Base Plus Slugging': {'abbrev': 'OPS', 'leaders': []}, + 'Weighted On Base Avg': {'abbrev': 'wOBA', 'leaders': []}, + 'Avg on Balls in Play': {'abbrev': 'BABIP', 'leaders': []}, + 'Runs Batted In': {'abbrev': 'RBI', 'leaders': []}, + 'Doubles': {'abbrev': '2B', 'leaders': []}, + 'Triples': {'abbrev': '3B', 'leaders': []}, + 'Hits': {'abbrev': 'H', 'leaders': []}, + 'Plate Appearances': {'abbrev': 'PA', 'leaders': []}, + 'Ground Into Double Play': {'abbrev': 'GIDP', 'leaders': []}, + 'Walks': {'abbrev': 'BB', 'leaders': []}, + 'Strikeouts': {'abbrev': 'K', 'leaders': []}, + 'Hit By Pitch': {'abbrev': 'HBP', 'leaders': []}, + 'Intentional Walks': {'abbrev': 'IBB', 'leaders': []}, + 'Stolen Bases': {'abbrev': 'SB', 'leaders': []}, + 'Caught Stealing': {'abbrev': 'CS', 'leaders': []}, + 'Runs Scored': {'abbrev': 'R', 'leaders': []}, + 'At Bats': {'abbrev': 'AB', 'leaders': []}, + 'No-Doubt Home Runs': {'abbrev': 'NDHR', 'leaders': []}, + 'Ballpark Home Runs': {'abbrev': 'BPHR', 'leaders': []}, + 'Ballpark Flyouts': {'abbrev': 'BPFO', 'leaders': []}, + 'Ballpark Singles': {'abbrev': 'BP1B', 'leaders': []}, + 'Ballpark Lineouts': {'abbrev': 'BPLO', 'leaders': []}, + # 'Extra Base Attempts': {'abbrev': 'XBA', 'leaders': []}, + # 'Extra Bases Taken': {'abbrev': 'XBT', 'leaders': []}, + } + + top_avg = (BattingSeason + .select() + .where((BattingSeason.pa >= min_pa)) + .order_by(-(BattingSeason.hit / BattingSeason.ab), BattingSeason.player) + .limit(10)) + top_hr = (BattingSeason + .select(BattingSeason.player, BattingSeason.hr) + .order_by(-BattingSeason.hr, BattingSeason.player) + .limit(10)) + top_obp = (BattingSeason + .select() + .where(BattingSeason.pa >= min_pa) + .order_by( + -((BattingSeason.hit + BattingSeason.bb + BattingSeason.hbp + BattingSeason.ibb) / BattingSeason.pa), + BattingSeason.player) + .limit(10)) + top_slg = (BattingSeason + .select() + .where(BattingSeason.pa >= min_pa) + .order_by(-(((BattingSeason.hr * 4) + (BattingSeason.triple * 3) + (BattingSeason.double * 2) + ( + BattingSeason.hit - BattingSeason.hr - BattingSeason.triple - BattingSeason.double)) / BattingSeason.ab)) + .limit(10)) + top_ops = (BattingSeason + .select() + .where(BattingSeason.pa >= min_pa) + .order_by(-((BattingSeason.ab * ( + BattingSeason.hit + BattingSeason.bb + BattingSeason.ibb + BattingSeason.hbp) + ( + (BattingSeason.hr * 4) + (BattingSeason.triple * 3) + ( + BattingSeason.double * 2) + ( + BattingSeason.hit - BattingSeason.double - BattingSeason.triple - BattingSeason.hr)) * ( + BattingSeason.ab + BattingSeason.bb + BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp)) / + (BattingSeason.ab * ( + BattingSeason.ab + BattingSeason.bb + BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp)))) + .limit(10)) + top_wob = (BattingSeason + .select() + .where(BattingSeason.pa >= min_pa) + .order_by(-(((BattingSeason.bb * .69) + (BattingSeason.hbp * .72) + (BattingSeason.hr * 2.1) + ( + BattingSeason.triple * 1.62) + (BattingSeason.double * 1.27) + (( + BattingSeason.hit - BattingSeason.double - BattingSeason.triple - BattingSeason.hr) * .89)) / + ( + BattingSeason.ab + BattingSeason.bb - BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp))) + .limit(10)) + top_bab = (BattingSeason + .select() + .where(BattingSeason.pa >= min_pa) + .order_by(-((BattingSeason.hit - BattingSeason.hr) / + (BattingSeason.ab - BattingSeason.so - BattingSeason.hr + BattingSeason.sac))) + .limit(10)) + top_rbi = (BattingSeason + .select(BattingSeason.player, BattingSeason.rbi) + .order_by(-BattingSeason.rbi, BattingSeason.player) + .limit(10)) + top_dbl = (BattingSeason + .select(BattingSeason.player, BattingSeason.double) + .order_by(-BattingSeason.double, BattingSeason.player) + .limit(10)) + top_trp = (BattingSeason + .select(BattingSeason.player, BattingSeason.triple) + .order_by(-BattingSeason.triple, BattingSeason.player) + .limit(10)) + top_hit = (BattingSeason + .select(BattingSeason.player, BattingSeason.hit) + .order_by(-BattingSeason.hit, BattingSeason.player) + .limit(10)) + top_pa = (BattingSeason + .select(BattingSeason.player, BattingSeason.pa) + .order_by(-BattingSeason.pa, BattingSeason.player) + .limit(10)) + top_gdp = (BattingSeason + .select(BattingSeason.player, BattingSeason.gidp) + .order_by(-BattingSeason.gidp, BattingSeason.player) + .limit(10)) + top_bb = (BattingSeason + .select(BattingSeason.player, BattingSeason.bb) + .order_by(-BattingSeason.bb, BattingSeason.player) + .limit(10)) + top_so = (BattingSeason + .select(BattingSeason.player, BattingSeason.so) + .order_by(-BattingSeason.so, BattingSeason.player) + .limit(10)) + top_hbp = (BattingSeason + .select(BattingSeason.player, BattingSeason.hbp) + .order_by(-BattingSeason.hbp, BattingSeason.player) + .limit(10)) + top_ibb = (BattingSeason + .select(BattingSeason.player, BattingSeason.ibb) + .order_by(-BattingSeason.ibb, BattingSeason.player) + .limit(10)) + top_sb = (BattingSeason + .select(BattingSeason.player, BattingSeason.sb) + .order_by(-BattingSeason.sb, BattingSeason.player) + .limit(10)) + top_cs = (BattingSeason + .select(BattingSeason.player, BattingSeason.cs) + .order_by(-BattingSeason.cs, BattingSeason.player) + .limit(10)) + top_run = (BattingSeason + .select(BattingSeason.player, BattingSeason.run) + .order_by(-BattingSeason.run, BattingSeason.player) + .limit(10)) + top_ab = (BattingSeason + .select(BattingSeason.player, BattingSeason.ab) + .order_by(-BattingSeason.ab, BattingSeason.player) + .limit(10)) + top_bphr = (BattingSeason + .select(BattingSeason.player, BattingSeason.bphr) + .where(BattingSeason.bphr > 0) + .order_by(-BattingSeason.bphr, BattingSeason.player) + .limit(10)) + top_bpfo = (BattingSeason + .select(BattingSeason.player, BattingSeason.bpfo) + .where(BattingSeason.bpfo > 0) + .order_by(-BattingSeason.bpfo, BattingSeason.player) + .limit(10)) + top_bp1b = (BattingSeason + .select(BattingSeason.player, BattingSeason.bp1b) + .where(BattingSeason.bp1b > 0) + .order_by(-BattingSeason.bp1b, BattingSeason.player) + .limit(10)) + top_bplo = (BattingSeason + .select(BattingSeason.player, BattingSeason.bplo) + .where(BattingSeason.bplo > 0) + .order_by(-BattingSeason.bplo, BattingSeason.player) + .limit(10)) + # top_xba = (BattingSeason + # .select(BattingSeason.player, BattingSeason.xba) + # .where(BattingSeason.xba > 0) + # .order_by(-BattingSeason.xba, BattingSeason.player) + # .limit(10)) + # top_xbt = (BattingSeason + # .select(BattingSeason.player, BattingSeason.xbt) + # .where(BattingSeason.xbt > 0) + # .order_by(-BattingSeason.xbt, BattingSeason.player) + # .limit(10)) + top_ndhr = (BattingSeason + .select(BattingSeason.player, BattingSeason.bphr, BattingSeason.hr) + .where(BattingSeason.hr > 0) + .order_by(-(BattingSeason.hr - BattingSeason.bphr), BattingSeason.player) + .limit(10)) + db.close() + + for x in top_avg: + all_leaders['Batting Average']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{x.hit / x.ab:.3f}'} + ) + for x in top_hr: + all_leaders['Home Runs']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hr)} + ) + for x in top_obp: + all_leaders['On Base Percentage']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.hit + x.bb + x.hbp + x.ibb) / x.pa:.3f}'} + ) + for x in top_slg: + all_leaders['Slugging Percentage']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.hr - x.triple - x.double)) / x.ab:.3f}'} + ) + for x in top_ops: + all_leaders['On Base Plus Slugging']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(x.ab * (x.hit + x.bb + x.ibb + x.hbp) + ((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.double - x.triple - x.hr)) * (x.ab + x.bb + x.ibb + x.sac + x.hbp)) / (x.ab * (x.ab + x.bb + x.ibb + x.sac + x.hbp)):.3f}'} + ) + for x in top_wob: + all_leaders['Weighted On Base Avg']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{(((x.bb * .69) + (x.hbp * .72) + (x.hr * 2.1) + (x.triple * 1.62) + (x.double * 1.27) + ((x.hit - x.double - x.triple - x.hr) * .89)) / (x.ab + x.bb - x.ibb + x.sac + x.hbp)):.3f}'} + ) + for x in top_bab: + all_leaders['Avg on Balls in Play']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{((x.hit - x.hr) / (x.ab - x.so - x.hr + x.sac)):.3f}'} + ) + for x in top_rbi: + all_leaders['Runs Batted In']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.rbi)} + ) + for x in top_dbl: + all_leaders['Doubles']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.double)} + ) + for x in top_trp: + all_leaders['Triples']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.triple)} + ) + for x in top_hit: + all_leaders['Hits']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hit)} + ) + for x in top_pa: + all_leaders['Plate Appearances']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.pa)} + ) + for x in top_gdp: + all_leaders['Ground Into Double Play']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.gidp)} + ) + for x in top_bb: + all_leaders['Walks']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.bb)} + ) + for x in top_so: + all_leaders['Strikeouts']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.so)} + ) + for x in top_hbp: + all_leaders['Hit By Pitch']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hbp)} + ) + for x in top_ibb: + all_leaders['Intentional Walks']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.ibb)} + ) + for x in top_sb: + all_leaders['Stolen Bases']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.sb)} + ) + for x in top_cs: + all_leaders['Caught Stealing']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.cs)} + ) + for x in top_run: + all_leaders['Runs Scored']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.run)} + ) + for x in top_ab: + all_leaders['At Bats']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.ab)} + ) + for x in top_bphr: + all_leaders['Ballpark Home Runs']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.bphr)} + ) + for x in top_bpfo: + all_leaders['Ballpark Flyouts']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.bpfo)} + ) + for x in top_bp1b: + all_leaders['Ballpark Singles']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.bp1b)} + ) + for x in top_bplo: + all_leaders['Ballpark Lineouts']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': round(x.bplo)} + ) + # for x in top_xba: + # all_leaders['Extra Base Attempts']['leaders'].append( + # {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.xba)} + # ) + # for x in top_xbt: + # all_leaders['Extra Bases Taken']['leaders'].append( + # {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.xbt)} + # ) + for x in top_ndhr: + all_leaders['No-Doubt Home Runs']['leaders'].append( + {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, + 'stat': f'{x.hr - x.bphr:.0f}'} + ) + + db.close() + return all_leaders + + +@app.get('/api/v1/career-leaders') +async def v1_career_leaders_get(group: str = 'batting'): + min_ip = 300 + min_pa = 1000 + min_ch_high = 200 + min_ch_mid = 80 + min_ch_low = 40 + all_leaders = None + + if group == 'pitching': + all_leaders = { + # 'Starter Wins': {'abbrev': 'W', 'leaders': []}, + 'Wins': {'abbrev': 'W', 'leaders': []}, + 'Earned Run Average': {'abbrev': 'ERA', 'leaders': []}, + 'Strikeouts': {'abbrev': 'K', 'leaders': []}, + 'Innings Pitched': {'abbrev': 'IP', 'leaders': []}, + 'Strikeouts per Nine': {'abbrev': 'K/9', 'leaders': []}, + 'Walks + Hits / IP': {'abbrev': 'WHIP', 'leaders': []}, + 'Saves': {'abbrev': 'SV', 'leaders': []}, + 'Walks Allowed': {'abbrev': 'BB', 'leaders': []}, + 'Runs Allowed': {'abbrev': 'R', 'leaders': []}, + 'Earned Runs Allowed': {'abbrev': 'ER', 'leaders': []}, + 'Blown Saves': {'abbrev': 'BSV', 'leaders': []}, + 'Hit By Pitches Allowed': {'abbrev': 'HBP', 'leaders': []}, + 'Hits Allowed': {'abbrev': 'H', 'leaders': []}, + 'Home Runs Allowed': {'abbrev': 'HR', 'leaders': []}, + 'Wild Pitches': {'abbrev': 'WP', 'leaders': []}, + 'Holds': {'abbrev': 'HD', 'leaders': []}, + 'Walks Per Nine': {'abbrev': 'BB/9', 'leaders': []}, + 'Strikeouts Per Walk': {'abbrev': 'K/BB', 'leaders': []}, + 'Losses': {'abbrev': 'L', 'leaders': []}, + } + + # top_swin = (PitchingStat + # .select(PitchingStat.player, fn.COUNT(PitchingStat.win).alias('swin')) + # .where(PitchingStat.gs == 1, PitchingStat.win == 1) + # .order_by(-fn.COUNT(PitchingStat.win), PitchingStat.player) + # .group_by(PitchingStat.player) + # .limit(10)) + top_win = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.win) + .order_by(-PitchingCareer.win, PitchingCareer.name) + .limit(10)) + top_ip = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.ip) + .order_by(-PitchingCareer.ip, PitchingCareer.name) + .limit(10)) + top_so = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.so) + .order_by(-PitchingCareer.so, PitchingCareer.name) + .limit(10)) + top_era = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.erun, PitchingCareer.ip) + .where(PitchingCareer.ip >= min_ip) + .order_by(((PitchingCareer.erun * 9) / PitchingCareer.ip), PitchingCareer.name) + .limit(10)) + top_kpn = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.so, PitchingCareer.ip) + .where(PitchingCareer.ip >= min_ip) + .order_by(-((PitchingCareer.so * 9) / PitchingCareer.ip), PitchingCareer.name) + .limit(10)) + top_whip = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.bb, PitchingCareer.hit, PitchingCareer.ip) + .where(PitchingCareer.ip >= min_ip) + .order_by(+((PitchingCareer.bb + PitchingCareer.hit) / PitchingCareer.ip), PitchingCareer.name) + .limit(10)) + top_sv = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.sv) + .order_by(-PitchingCareer.sv, PitchingCareer.name) + .limit(10)) + top_bb = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.bb) + .order_by(-PitchingCareer.bb, PitchingCareer.name) + .limit(10)) + top_run = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.run) + .order_by(-PitchingCareer.run, PitchingCareer.name) + .limit(10)) + top_erun = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.erun) + .order_by(-PitchingCareer.erun, PitchingCareer.name) + .limit(10)) + top_bsv = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.bsv) + .order_by(-PitchingCareer.bsv, PitchingCareer.name) + .limit(10)) + top_hbp = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.hbp) + .order_by(-PitchingCareer.hbp, PitchingCareer.name) + .limit(10)) + top_hit = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.hit) + .order_by(-PitchingCareer.hit, PitchingCareer.name) + .limit(10)) + top_hr = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.hr) + .order_by(-PitchingCareer.hr, PitchingCareer.name) + .limit(10)) + top_wp = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.wp) + .order_by(-PitchingCareer.wp, PitchingCareer.name) + .limit(10)) + top_hd = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.hold) + .order_by(-PitchingCareer.hold, PitchingCareer.name) + .limit(10)) + top_bpn = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.bb, PitchingCareer.ip) + .where(PitchingCareer.ip >= min_ip) + .order_by(((PitchingCareer.bb * 9) / PitchingCareer.ip), PitchingCareer.name) + .limit(10)) + top_kpw = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.bb, PitchingCareer.so) + .where(PitchingCareer.ip >= min_ip) + .order_by(-(PitchingCareer.so / PitchingCareer.bb), PitchingCareer.name) + .limit(10)) + top_los = (PitchingCareer + .select(PitchingCareer.name, PitchingCareer.loss) + .order_by(-PitchingCareer.loss, PitchingCareer.name) + .limit(10)) + + db.close() + + # for x in top_swin: + # all_leaders['Starter Wins']['leaders'].append( + # {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.swin)} + # ) + for x in top_win: + all_leaders['Wins']['leaders'].append( + {'player': x.name, 'stat': round(x.win)} + ) + for x in top_ip: + all_leaders['Innings Pitched']['leaders'].append( + {'player': x.name, 'stat': f'{x.ip:.1f}'} + ) + for x in top_so: + all_leaders['Strikeouts']['leaders'].append( + {'player': x.name, 'stat': round(x.so)} + ) + for x in top_era: + all_leaders['Earned Run Average']['leaders'].append( + {'player': x.name, 'stat': f'{((x.erun * 9) / x.ip):.2f}'} + ) + for x in top_kpn: + all_leaders['Strikeouts per Nine']['leaders'].append( + {'player': x.name, 'stat': f'{((x.so * 9) / x.ip):.2f}'} + ) + for x in top_whip: + all_leaders['Walks + Hits / IP']['leaders'].append( + {'player': x.name, 'stat': f'{((x.bb + x.hit) / x.ip):.2f}'} + ) + for x in top_sv: + all_leaders['Saves']['leaders'].append( + {'player': x.name, 'stat': round(x.sv)} + ) + for x in top_bb: + all_leaders['Walks Allowed']['leaders'].append( + {'player': x.name, 'stat': round(x.bb)} + ) + for x in top_run: + all_leaders['Runs Allowed']['leaders'].append( + {'player': x.name, 'stat': round(x.run)} + ) + for x in top_erun: + all_leaders['Earned Runs Allowed']['leaders'].append( + {'player': x.name, 'stat': round(x.erun)} + ) + for x in top_bsv: + all_leaders['Blown Saves']['leaders'].append( + {'player': x.name, 'stat': round(x.bsv)} + ) + for x in top_hbp: + all_leaders['Hit By Pitches Allowed']['leaders'].append( + {'player': x.name, 'stat': round(x.hbp)} + ) + for x in top_hit: + all_leaders['Hits Allowed']['leaders'].append( + {'player': x.name, 'stat': round(x.hit)} + ) + for x in top_hr: + all_leaders['Home Runs Allowed']['leaders'].append( + {'player': x.name, 'stat': round(x.hr)} + ) + for x in top_wp: + all_leaders['Wild Pitches']['leaders'].append( + {'player': x.name, 'stat': round(x.wp)} + ) + for x in top_hd: + all_leaders['Holds']['leaders'].append( + {'player': x.name, 'stat': round(x.hold)} + ) + for x in top_bpn: + all_leaders['Walks Per Nine']['leaders'].append( + {'player': x.name, 'stat': f'{((x.bb * 9) / x.ip):.2f}'} + ) + for x in top_kpw: + all_leaders['Strikeouts Per Walk']['leaders'].append( + {'player': x.name, 'stat': f'{(x.so / x.bb):.2f}'} + ) + for x in top_los: + all_leaders['Losses']['leaders'].append( + {'player': x.name, 'stat': round(x.loss)} + ) + elif group == 'fielding': + all_leaders = { + 'Fielding Chances': {'abbrev': 'X-Ch', 'leaders': []}, + 'Weighted Fielding % (RF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (CF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (LF)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (SS)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (3B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (2B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (1B)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (C)': {'abbrev': 'wF%', 'leaders': []}, + 'Weighted Fielding % (P)': {'abbrev': 'wF%', 'leaders': []}, + 'Errors': {'abbrev': 'E', 'leaders': []}, + 'Hits Allowed': {'abbrev': 'X-Hit', 'leaders': []}, + 'Passed Balls': {'abbrev': 'PB', 'leaders': []}, + 'Steal Attempts Against': {'abbrev': 'SBa', 'leaders': []}, + 'Runners Thrown Out': {'abbrev': 'CSc', 'leaders': []}, + 'Caught Stealing % (Catchers)': {'abbrev': 'CS%', 'leaders': []}, + } + + top_xch = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.sbc) + .order_by(-(FieldingCareer.xch + FieldingCareer.sbc), FieldingCareer.name) + .limit(10)) + raw_ss = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == 'SS') & (FieldingCareer.xch >= min_ch_high))) + raw_cf = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == 'CF') & (FieldingCareer.xch >= min_ch_mid))) + raw_2b = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == '2B') & (FieldingCareer.xch >= min_ch_high))) + raw_3b = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == '3B') & (FieldingCareer.xch >= min_ch_mid))) + raw_1b = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == '1B') & (FieldingCareer.xch >= min_ch_mid))) + raw_lf = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == 'LF') & (FieldingCareer.xch >= min_ch_low))) + raw_rf = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == 'RF') & (FieldingCareer.xch >= min_ch_low))) + raw_ca = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == 'C') & (FieldingCareer.xch >= min_ch_mid))) + raw_pi = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error) + .where( + (FieldingCareer.pos == 'P') & (FieldingCareer.xch >= (min_ch_low / 3)))) + top_err = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.error) + .order_by(-FieldingCareer.error, FieldingCareer.name) + .limit(10)) + top_hit = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.xhit) + .order_by(-FieldingCareer.xhit, FieldingCareer.name) + .limit(10)) + top_sbc = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.sbc) + .order_by(-FieldingCareer.sbc, FieldingCareer.name) + .limit(10)) + top_csc = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.csc) + .order_by(-FieldingCareer.csc, FieldingCareer.name) + .limit(10)) + top_csp = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.sbc, FieldingCareer.csc) + .where(FieldingCareer.sbc >= min_ch_mid) + .order_by(-((FieldingCareer.csc * 100) / FieldingCareer.sbc), FieldingCareer.name) + .limit(10)) + top_pb = (FieldingCareer + .select(FieldingCareer.name, FieldingCareer.pb) + .where(FieldingCareer.pb > 0) + .order_by(-FieldingCareer.pb, FieldingCareer.name) + .limit(10)) + + db.close() + + for x in top_xch: + all_leaders['Fielding Chances']['leaders'].append( + {'player': x.name, 'stat': x.xch + x.sbc} + ) + for x in top_err: + all_leaders['Errors']['leaders'].append( + {'player': x.name, 'stat': x.error} + ) + for x in top_sbc: + all_leaders['Steal Attempts Against']['leaders'].append( + {'player': x.name, 'stat': x.sbc} + ) + for x in top_csc: + all_leaders['Runners Thrown Out']['leaders'].append( + {'player': x.name, 'stat': x.csc} + ) + for x in top_csp: + all_leaders['Caught Stealing % (Catchers)']['leaders'].append( + {'player': x.name, 'stat': f'{x.csc * 100 / x.sbc:.1f}'} + ) + for x in top_hit: + all_leaders['Hits Allowed']['leaders'].append( + {'player': x.name, 'stat': x.xhit} + ) + for x in top_pb: + all_leaders['Passed Balls']['leaders'].append( + {'player': x.name, 'stat': x.pb} + ) + + for x in raw_ss: + all_leaders['Weighted Fielding % (SS)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_cf: + all_leaders['Weighted Fielding % (CF)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_2b: + all_leaders['Weighted Fielding % (2B)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_3b: + all_leaders['Weighted Fielding % (3B)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_1b: + all_leaders['Weighted Fielding % (1B)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_lf: + all_leaders['Weighted Fielding % (LF)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_rf: + all_leaders['Weighted Fielding % (RF)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_ca: + all_leaders['Weighted Fielding % (C)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for x in raw_pi: + all_leaders['Weighted Fielding % (P)']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'} + ) + for stat in all_leaders: + if 'Weighted Fielding' in stat: + all_leaders[stat]['leaders'] = sorted( + all_leaders[stat]['leaders'], + key=lambda leader: leader['stat'], + reverse=True + ) + all_leaders[stat]['leaders'] = all_leaders[stat]['leaders'][:10] + else: + # return batting leaders + all_leaders = { + 'Home Runs': {'abbrev': 'HR', 'leaders': []}, + 'Batting Average': {'abbrev': 'AVG', 'leaders': []}, + 'On Base Percentage': {'abbrev': 'OBP', 'leaders': []}, + 'Slugging Percentage': {'abbrev': 'SLG', 'leaders': []}, + 'Runs Batted In': {'abbrev': 'RBI', 'leaders': []}, + 'Doubles': {'abbrev': '2B', 'leaders': []}, + 'Triples': {'abbrev': '3B', 'leaders': []}, + 'Hits': {'abbrev': 'H', 'leaders': []}, + 'Plate Appearances': {'abbrev': 'PA', 'leaders': []}, + 'Ground Into Double Play': {'abbrev': 'GIDP', 'leaders': []}, + 'Walks': {'abbrev': 'BB', 'leaders': []}, + 'Strikeouts': {'abbrev': 'K', 'leaders': []}, + 'Hit By Pitch': {'abbrev': 'HBP', 'leaders': []}, + 'Intentional Walks': {'abbrev': 'IBB', 'leaders': []}, + 'Stolen Bases': {'abbrev': 'SB', 'leaders': []}, + 'Caught Stealing': {'abbrev': 'CS', 'leaders': []}, + 'Runs Scored': {'abbrev': 'R', 'leaders': []}, + 'At Bats': {'abbrev': 'AB', 'leaders': []}, + } + + top_avg = (BattingCareer + .select() + .where(BattingCareer.pa >= min_pa) + .order_by(-(BattingCareer.hit / BattingCareer.ab), BattingCareer.name) + .limit(10)) + top_hr = (BattingCareer + .select(BattingCareer.name, BattingCareer.hr) + .order_by(-BattingCareer.hr, BattingCareer.name) + .limit(10)) + top_obp = (BattingCareer + .select() + .where(BattingCareer.pa >= min_pa) + .order_by( + -((BattingCareer.hit + BattingCareer.bb + BattingCareer.hbp + BattingCareer.ibb) / BattingCareer.pa), + BattingCareer.name) + .limit(10)) + top_slg = (BattingCareer + .select() + .where(BattingCareer.pa >= min_pa) + .order_by(-(((BattingCareer.hr * 4) + (BattingCareer.triple * 3) + (BattingCareer.double * 2) + ( + BattingCareer.hit - BattingCareer.hr - BattingCareer.triple - BattingCareer.double)) / BattingCareer.ab)) + .limit(10)) + top_rbi = (BattingCareer + .select(BattingCareer.name, BattingCareer.rbi) + .order_by(-BattingCareer.rbi, BattingCareer.name) + .limit(10)) + top_dbl = (BattingCareer + .select(BattingCareer.name, BattingCareer.double) + .order_by(-BattingCareer.double, BattingCareer.name) + .limit(10)) + top_trp = (BattingCareer + .select(BattingCareer.name, BattingCareer.triple) + .order_by(-BattingCareer.triple, BattingCareer.name) + .limit(10)) + top_hit = (BattingCareer + .select(BattingCareer.name, BattingCareer.hit) + .order_by(-BattingCareer.hit, BattingCareer.name) + .limit(10)) + top_pa = (BattingCareer + .select(BattingCareer.name, BattingCareer.pa) + .order_by(-BattingCareer.pa, BattingCareer.name) + .limit(10)) + top_gdp = (BattingCareer + .select(BattingCareer.name, BattingCareer.gidp) + .order_by(-BattingCareer.gidp, BattingCareer.name) + .limit(10)) + top_bb = (BattingCareer + .select(BattingCareer.name, BattingCareer.bb) + .order_by(-BattingCareer.bb, BattingCareer.name) + .limit(10)) + top_so = (BattingCareer + .select(BattingCareer.name, BattingCareer.so) + .order_by(-BattingCareer.so, BattingCareer.name) + .limit(10)) + top_hbp = (BattingCareer + .select(BattingCareer.name, BattingCareer.hbp) + .order_by(-BattingCareer.hbp, BattingCareer.name) + .limit(10)) + top_ibb = (BattingCareer + .select(BattingCareer.name, BattingCareer.ibb) + .order_by(-BattingCareer.ibb, BattingCareer.name) + .limit(10)) + top_sb = (BattingCareer + .select(BattingCareer.name, BattingCareer.sb) + .order_by(-BattingCareer.sb, BattingCareer.name) + .limit(10)) + top_cs = (BattingCareer + .select(BattingCareer.name, BattingCareer.cs) + .order_by(-BattingCareer.cs, BattingCareer.name) + .limit(10)) + top_run = (BattingCareer + .select(BattingCareer.name, BattingCareer.run) + .order_by(-BattingCareer.run, BattingCareer.name) + .limit(10)) + top_ab = (BattingCareer + .select(BattingCareer.name, BattingCareer.ab) + .order_by(-BattingCareer.ab, BattingCareer.name) + .limit(10)) + db.close() + + for x in top_avg: + all_leaders['Batting Average']['leaders'].append( + {'player': x.name, 'stat': f'{x.hit / x.ab:.3f}'} + ) + for x in top_hr: + all_leaders['Home Runs']['leaders'].append( + {'player': x.name, 'stat': round(x.hr)} + ) + for x in top_obp: + all_leaders['On Base Percentage']['leaders'].append( + {'player': x.name, + 'stat': f'{(x.hit + x.bb + x.hbp + x.ibb) / x.pa:.3f}'} + ) + for x in top_slg: + all_leaders['Slugging Percentage']['leaders'].append( + {'player': x.name, + 'stat': f'{((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.hr - x.triple - x.double)) / x.ab:.3f}'} + ) + for x in top_rbi: + all_leaders['Runs Batted In']['leaders'].append( + {'player': x.name, 'stat': round(x.rbi)} + ) + for x in top_dbl: + all_leaders['Doubles']['leaders'].append( + {'player': x.name, 'stat': round(x.double)} + ) + for x in top_trp: + all_leaders['Triples']['leaders'].append( + {'player': x.name, 'stat': round(x.triple)} + ) + for x in top_hit: + all_leaders['Hits']['leaders'].append( + {'player': x.name, 'stat': round(x.hit)} + ) + for x in top_pa: + all_leaders['Plate Appearances']['leaders'].append( + {'player': x.name, 'stat': round(x.pa)} + ) + for x in top_gdp: + all_leaders['Ground Into Double Play']['leaders'].append( + {'player': x.name, 'stat': round(x.gidp)} + ) + for x in top_bb: + all_leaders['Walks']['leaders'].append( + {'player': x.name, 'stat': round(x.bb)} + ) + for x in top_so: + all_leaders['Strikeouts']['leaders'].append( + {'player': x.name, 'stat': round(x.so)} + ) + for x in top_hbp: + all_leaders['Hit By Pitch']['leaders'].append( + {'player': x.name, 'stat': round(x.hbp)} + ) + for x in top_ibb: + all_leaders['Intentional Walks']['leaders'].append( + {'player': x.name, 'stat': round(x.ibb)} + ) + for x in top_sb: + all_leaders['Stolen Bases']['leaders'].append( + {'player': x.name, 'stat': round(x.sb)} + ) + for x in top_cs: + all_leaders['Caught Stealing']['leaders'].append( + {'player': x.name, 'stat': round(x.cs)} + ) + for x in top_run: + all_leaders['Runs Scored']['leaders'].append( + {'player': x.name, 'stat': round(x.run)} + ) + for x in top_ab: + all_leaders['At Bats']['leaders'].append( + {'player': x.name, 'stat': round(x.ab)} + ) + + db.close() + return all_leaders + + +@app.get('/api/v1/leaders/{stat}') # Not implemented +async def v1_leaders_stat( + stat: str, season: int = Current.get().season, team_abbrev: Optional[str] = None, + league_abbrev: Optional[str] = None, division_abbrev: Optional[str] = None): + db.close() + raise HTTPException(501, detail='Function not implemented, yet') + + +@app.get('/api/v1/single-game-leaders') +async def v1_single_game_leaders_get( + group: str = 'batting', season: Optional[int] = None, team_abbrev: Optional[str] = None): + all_leaders = None + + if group == 'pitching': + all_leaders = { + 'Perfect Games': {'abbrev': 'PG', 'leaders': []}, + 'Complete Games': {'abbrev': 'PG', 'leaders': []}, + # 'Innings Pitched': {'abbrev': 'IP', 'leaders': []}, + 'Strikeouts': {'abbrev': 'K', 'leaders': []}, + 'Scoreless Innings': {'abbrev': 'SI', 'leaders': []}, + 'Zero Baserunners': {'abbrev': 'ZB', 'leaders': []}, + 'Earned Runs Allowed': {'abbrev': 'ER', 'leaders': []}, + 'Hit By Pitches Allowed': {'abbrev': 'HBP', 'leaders': []}, + 'Hits Allowed': {'abbrev': 'H', 'leaders': []}, + 'Home Runs Allowed': {'abbrev': 'HR', 'leaders': []}, + 'Wild Pitches': {'abbrev': 'WP', 'leaders': []}, + } + all_stats = PitchingStat.select().join(Team).where( + (PitchingStat.team.abbrev != 'SRS') & (PitchingStat.ip <= 10) + ) + if season: + all_stats = all_stats.where(PitchingStat.season == season) + if team_abbrev and season: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + raise HTTPException(404, f'Team {team_abbrev} not found in season {season}') + all_stats = all_stats.where(PitchingStat.team == this_team) + + # top_ip = (all_stats + # .order_by(-PitchingStat.ip, PitchingStat.season, PitchingStat.player) + # .limit(10)) + top_so = (all_stats + .order_by(-PitchingStat.so, PitchingStat.season, PitchingStat.player) + .limit(10)) + top_si = (all_stats.where(PitchingStat.run == 0) + .order_by(-PitchingStat.ip, PitchingStat.season, PitchingStat.player) + .limit(10)) + top_zb = (all_stats + .where((PitchingStat.hit == 0) & (PitchingStat.bb == 0) & (PitchingStat.hbp == 0)) + .order_by(-PitchingStat.ip, PitchingStat.season, PitchingStat.player) + .limit(10)) + top_erun = (all_stats + .order_by(-PitchingStat.erun, PitchingStat.season, PitchingStat.player) + .limit(10)) + top_hbp = (all_stats + .order_by(-PitchingStat.hbp, PitchingStat.season, PitchingStat.player) + .limit(10)) + top_hit = (all_stats + .order_by(-PitchingStat.hit, PitchingStat.season, PitchingStat.player) + .limit(10)) + top_hr = (all_stats + .order_by(-PitchingStat.hr, PitchingStat.season, PitchingStat.player) + .limit(10)) + top_wp = (all_stats + .order_by(-PitchingStat.wp, PitchingStat.season, PitchingStat.player) + .limit(10)) + + db.close() + return all_leaders + + # for x in top_ip: + # this_result = Result.select().where( + # (Result.season == x.season) & (Result.week == x.week) & + # ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + # ).limit(1) + # + # opponent = None + # home_team = None + # away_team = None + # if this_result.count() > 0: + # # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # # f'This Team: {x.player.team}') + # home_team = this_result[0].hometeam + # away_team = this_result[0].awayteam + # if this_result[0].hometeam == x.player.team: + # opponent = this_result[0].awayteam + # else: + # opponent = this_result[0].hometeam + # # logging.info(f'Opponent: {opponent}') + # + # all_leaders['Innings Pitched']['leaders'].append( + # { + # 'player': x.player.name, + # 'team': x.team.abbrev, + # 'stat': f'{x.ip:.1f}', + # 'season': x.player.season, + # 'week': x.week, + # 'game_num': x.game, + # 'opponent': opponent.abbrev if opponent else '???', + # 'home_team': home_team.abbrev if home_team else '???', + # 'away_team': away_team.abbrev if away_team else '???' + # } + # ) + for x in top_so: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Strikeouts']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': round(x.so), + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_si: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Scoreless Innings']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': f'{x.ip:.1f}', + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_zb: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Zero Baserunners']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': f'{x.ip:.1f}', + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_erun: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Earned Runs Allowed']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': round(x.erun), + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_hbp: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Hit By Pitches Allowed']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': round(x.hbp), + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_hit: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Hits Allowed']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': round(x.hit), + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_hr: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Home Runs Allowed']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': round(x.hr), + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_wp: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Wild Pitches']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': round(x.wp), + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + + elif group == 'fielding': + all_leaders = { + 'Fielding Chances': {'abbrev': 'xch', 'leaders': []}, + 'Errors': {'abbrev': 'E', 'leaders': []}, + 'Hits Allowed': {'abbrev': 'H', 'leaders': []}, + 'Passed Balls': {'abbrev': 'PB', 'leaders': []}, + 'Steal Attempts Against': {'abbrev': 'SBa', 'leaders': []}, + 'Runners Thrown Out (C)': {'abbrev': 'CSc', 'leaders': []}, + # 'Rob Attempts': {'abbrev': 'ROBa', 'leaders': []}, + # 'Rob Successes': {'abbrev': 'ROBs', 'leaders': []}, + } + all_stats = BattingStat.select().join(Team).where(BattingStat.team.abbrev != 'SRS') + if season: + all_stats = all_stats.where(BattingStat.season == season) + if team_abbrev and season: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + raise HTTPException(404, f'Team {team_abbrev} not found in season {season}') + all_stats = all_stats.where(BattingStat.team == this_team) + + top_xch = (all_stats + .where(BattingStat.xch > 1) + .order_by(-BattingStat.xch, BattingStat.season, BattingStat.week) + .limit(10)) + top_err = (all_stats + .where(BattingStat.error > 1) + .order_by(-BattingStat.error, BattingStat.season, BattingStat.week) + .limit(10)) + top_hit = (all_stats + .where(BattingStat.hit > 1) + .order_by(-BattingStat.xhit, BattingStat.season, BattingStat.week) + .limit(10)) + top_sbc = (all_stats + .where(BattingStat.sbc > 1) + .order_by(-BattingStat.sbc, BattingStat.season, BattingStat.week) + .limit(10)) + top_csc = (all_stats + .where(BattingStat.csc > 1) + .order_by(-BattingStat.csc, BattingStat.season, BattingStat.week) + .limit(10)) + top_pb = (all_stats + .where(BattingStat.pb > 1) + .order_by(-BattingStat.pb, BattingStat.season, BattingStat.week) + .limit(10)) + # top_roba = (all_stats + # .where(BattingStat.roba > 1) + # .order_by(-BattingStat.roba, BattingStat.season, BattingStat.week) + # .limit(10)) + # top_robs = (all_stats + # .where(BattingStat.robs > 1) + # .order_by(-BattingStat.robs, BattingStat.season, BattingStat.week) + # .limit(10)) + + db.close() + + for x in top_xch: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Fielding Chances']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': x.xch, + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_err: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Errors']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': x.error, + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_sbc: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Steal Attempts Against']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': x.sbc, + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_csc: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Runners Thrown Out (C)']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': x.csc, + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_hit: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Hits Allowed']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': x.xhit, + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + for x in top_pb: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Passed Balls']['leaders'].append( + { + 'player': x.player.name, + 'team': x.team.abbrev, + 'stat': x.pb, + 'season': x.player.season, + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???' + } + ) + # for x in top_roba: + # this_result = Result.select().where( + # (Result.season == x.season) & (Result.week == x.week) & + # ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + # ).limit(1) + # + # opponent = None + # home_team = None + # away_team = None + # if this_result.count() > 0: + # # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # # f'This Team: {x.player.team}') + # home_team = this_result[0].hometeam + # away_team = this_result[0].awayteam + # if this_result[0].hometeam == x.player.team: + # opponent = this_result[0].awayteam + # else: + # opponent = this_result[0].hometeam + # # logging.info(f'Opponent: {opponent}') + # + # all_leaders['Rob Attempts']['leaders'].append( + # { + # 'player': x.player.name, + # 'team': x.team.abbrev, + # 'stat': x.roba, + # 'season': x.player.season, + # 'week': x.week, + # 'game_num': x.game, + # 'opponent': opponent.abbrev if opponent else '???', + # 'home_team': home_team.abbrev if home_team else '???', + # 'away_team': away_team.abbrev if away_team else '???' + # } + # ) + # for x in top_robs: + # this_result = Result.select().where( + # (Result.season == x.season) & (Result.week == x.week) & + # ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + # ).limit(1) + # + # opponent = None + # home_team = None + # away_team = None + # if this_result.count() > 0: + # # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # # f'This Team: {x.player.team}') + # home_team = this_result[0].hometeam + # away_team = this_result[0].awayteam + # if this_result[0].hometeam == x.player.team: + # opponent = this_result[0].awayteam + # else: + # opponent = this_result[0].hometeam + # # logging.info(f'Opponent: {opponent}') + # + # all_leaders['Rob Successes']['leaders'].append( + # { + # 'player': x.player.name, + # 'team': x.team.abbrev, + # 'stat': x.robs, + # 'season': x.player.season, + # 'week': x.week, + # 'game_num': x.game, + # 'opponent': opponent.abbrev if opponent else '???', + # 'home_team': home_team.abbrev if home_team else '???', + # 'away_team': away_team.abbrev if away_team else '???' + # } + # ) + + else: + all_leaders = { + 'Home Runs': {'abbrev': 'HR', 'leaders': []}, + 'Hits': {'abbrev': 'H', 'leaders': []}, + 'RBI': {'abbrev': 'RBI', 'leaders': []}, + 'Hit for Cycle': {'abbrev': 'H', 'leaders': []}, + 'Stolen Bases': {'abbrev': 'SB', 'leaders': []}, + 'Doubles': {'abbrev': '2B', 'leaders': []}, + 'Triples': {'abbrev': '3B', 'leaders': []}, + 'Walks': {'abbrev': 'BB', 'leaders': []}, + # 'Extra Bases Taken': {'abbrev': 'XBT', 'leaders': []}, + 'Strikeouts': {'abbrev': 'K', 'leaders': []}, + 'Hit by Pitch': {'abbrev': 'HBP', 'leaders': []}, + } + all_stats = BattingStat.select().join(Team).where(BattingStat.team.abbrev != 'SRS') + if season: + all_stats = all_stats.where(BattingStat.season == season) + if team_abbrev and season: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + raise HTTPException(404, f'Team {team_abbrev} not found in season {season}') + all_stats = all_stats.where(BattingStat.team == this_team) + + top_hr = (all_stats.where(BattingStat.hr > 1).order_by(-BattingStat.hr, BattingStat.season).limit(10)) + logging.info(f'top_hr: {top_hr}') + top_hit = (all_stats.where(BattingStat.hit > 2).order_by( + -BattingStat.hit, BattingStat.season, BattingStat.week + ).limit(10)) + top_rbi = (all_stats.where(BattingStat.rbi > 3).order_by( + -BattingStat.rbi, BattingStat.season, BattingStat.week + ).limit(10)) + top_sb = (all_stats.where(BattingStat.sb > 1).order_by( + -BattingStat.sb, BattingStat.season, BattingStat.week + ).limit(10)) + top_dbl = (all_stats.where(BattingStat.double > 1).order_by( + -BattingStat.double, BattingStat.season, BattingStat.week + ).limit(10)) + top_tri = (all_stats.where(BattingStat.triple > 1).order_by( + -BattingStat.triple, BattingStat.season, BattingStat.week + ).limit(10)) + top_bb = (all_stats.where(BattingStat.bb > 2).order_by( + -BattingStat.bb, BattingStat.season, BattingStat.week + ).limit(10)) + # top_xbt = (all_stats.where(BattingStat.xbt > 1).order_by( + # -BattingStat.xbt, BattingStat.season, BattingStat.week + # ).limit(10)) + top_k = (all_stats.where(BattingStat.so > 2).order_by( + -BattingStat.so, BattingStat.season, BattingStat.week + ).limit(10)) + top_hbp = (all_stats.where(BattingStat.hbp > 1).order_by( + -BattingStat.hbp, BattingStat.season, BattingStat.week + ).limit(10)) + top_ccl = (all_stats.where( + (BattingStat.hr > 0) & (BattingStat.triple > 0) & (BattingStat.double > 0) & + ((BattingStat.hit - BattingStat.double - BattingStat.triple - BattingStat.hr) > 0) + ).order_by(-BattingStat.hit, BattingStat.season, BattingStat.week).limit(10)) + + for x in top_hr: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Home Runs']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.hr}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + for x in top_hit: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Hits']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.hit}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + for x in top_rbi: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['RBI']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.rbi}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + for x in top_sb: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Stolen Bases']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.sb}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + for x in top_dbl: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Doubles']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.double}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + for x in top_tri: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Triples']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.triple}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + for x in top_bb: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Walks']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.bb}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + # for x in top_xbt: + # this_result = Result.select().where( + # (Result.season == x.season) & (Result.week == x.week) & + # ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + # ).limit(1) + # + # opponent = None + # home_team = None + # away_team = None + # if this_result.count() > 0: + # # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # # f'This Team: {x.player.team}') + # home_team = this_result[0].hometeam + # away_team = this_result[0].awayteam + # if this_result[0].hometeam == x.player.team: + # opponent = this_result[0].awayteam + # else: + # opponent = this_result[0].hometeam + # # logging.info(f'Opponent: {opponent}') + # + # all_leaders['Extra Bases Taken']['leaders'].append( + # { + # 'player': x.player.name, + # 'season': x.player.season, + # 'team': x.team.abbrev, + # 'stat': f'{x.xbt}', + # 'week': x.week, + # 'game_num': x.game, + # 'opponent': opponent.abbrev if opponent else '???', + # 'home_team': home_team.abbrev if home_team else '???', + # 'away_team': away_team.abbrev if away_team else '???', + # } + # ) + for x in top_k: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Strikeouts']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.so}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + for x in top_hbp: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Hit by Pitch']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.hbp}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + for x in top_ccl: + this_result = Result.select().where( + (Result.season == x.season) & (Result.week == x.week) & + ((Result.awayteam == x.team) | (Result.hometeam == x.team)) + ).limit(1) + + opponent = None + home_team = None + away_team = None + if this_result.count() > 0: + # logging.info(f'Query: {this_result} / Result: {this_result[0]}') + # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / ' + # f'This Team: {x.player.team}') + home_team = this_result[0].hometeam + away_team = this_result[0].awayteam + if this_result[0].hometeam == x.player.team: + opponent = this_result[0].awayteam + else: + opponent = this_result[0].hometeam + # logging.info(f'Opponent: {opponent}') + + all_leaders['Hit for Cycle']['leaders'].append( + { + 'player': x.player.name, + 'season': x.player.season, + 'team': x.team.abbrev, + 'stat': f'{x.hit}', + 'week': x.week, + 'game_num': x.game, + 'opponent': opponent.abbrev if opponent else '???', + 'home_team': home_team.abbrev if home_team else '???', + 'away_team': away_team.abbrev if away_team else '???', + } + ) + + db.close() + return all_leaders + + +@app.get('/api/v1/teamstats') +async def v1_teamstats( + group: str = 'batting', season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None): + team_stats = {} + if team_abbrev: + all_teams = Team.select_season(season).where(Team.abbrev == team_abbrev) + else: + all_teams = Team.select_season(season).where(Team.division.is_null(False)).order_by(Team.sname) + + if group == 'batting': + for team in all_teams: + if team.abbrev[:2] != 'FA': + team_stats[team.abbrev] = { + 'team': model_to_dict(team), + 'stats': BattingStat.team_season(team, season) + } + elif group == 'pitching': + for team in all_teams: + if team.abbrev[:2] != 'FA': + team_stats[team.abbrev] = { + 'team': model_to_dict(team), + 'stats': PitchingStat.team_season(team, season) + } + else: + for team in all_teams: + if team.abbrev[:2] != 'FA': + team_stats[team.abbrev] = { + 'team': model_to_dict(team), + 'stats': BattingStat.team_fielding_season(team, season) + } + + db.close() + return team_stats + + +@app.get('/api/v1/managers') +async def v1_managers_get(): + all_managers = Manager.select() + + return_managers = {} + for x in all_managers: + return_managers[f'{x.id}'] = model_to_dict(x) + + db.close() + return return_managers + + +@app.get('/api/v1/managers/{name_or_id}') +async def v1_managers_get_one(name_or_id): + try: + this_manager = Manager.get_by_id(name_or_id) + except: + this_manager = Manager.get_or_none(fn.Lower(Manager.name) == name_or_id.lower()) + + if not this_manager: + db.close() + raise HTTPException(status_code=404, detail=f'Manager {name_or_id} not found') + + db.close() + return model_to_dict(this_manager) + + +@app.patch('/api/v1/managers/{name_or_id}') +async def v1_managers_patch( + name_or_id, image: Optional[str] = None, headline: Optional[str] = None, bio: Optional[str] = None, + token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch managers') + + try: + this_manager = Manager.get_by_id(name_or_id) + except: + this_manager = Manager.get_or_none(fn.Lower(Manager.name) == name_or_id.lower()) + + if not this_manager: + db.close() + raise HTTPException(status_code=404, detail=f'Manager {name_or_id} not found') + + if image: + this_manager.image = image + + if headline: + this_manager.headline = headline + + if bio: + this_manager.bio = bio + + saved = this_manager.save() + db.close() + + if saved == 1: + return model_to_dict(this_manager) + else: + raise HTTPException(status_code=500, detail='Manager update has failed') + + +@app.post('/api/v1/managers') +async def v1_managers_post(manager: ManagerModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post managers') + + this_manager = Manager(name=manager.name) + this_manager.save() + + if manager.image: + this_manager.image = manager.image + if manager.headline: + this_manager.headline = manager.headline + if manager.bio: + this_manager.bio = manager.bio + + this_manager.save() + db.close() + + return model_to_dict(this_manager) + + +@app.get('/api/v1/awards') +async def v1_awards_get( + name: Optional[str] = None, season: Optional[int] = None, timing: Optional[str] = None, + manager_id: Optional[int] = None, player_id: Optional[int] = None, team_id: Optional[int] = None, + player_name: Optional[str] = None): + all_awards = Award.select() + + if name: + all_awards = all_awards.where(Award.name == name) + if season: + all_awards = all_awards.where(Award.season == season) + if timing: + all_awards = all_awards.where(Award.timing == timing) + if manager_id: + try: + this_manager = Manager.get_by_id(manager_id) + all_awards = all_awards.where( + ((Award.manager1 == this_manager) | (Award.manager2 == this_manager)) + ) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Manager id {manager_id} not found') + if player_id or player_name: + if player_id: + try: + this_player = Player.get_by_id(player_id) + all_awards = all_awards.where(Award.player == this_player) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + else: + these_players = Player.select().where(fn.Lower(Player.name) == player_name.lower()) + if these_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Player {player_name} not found') + + all_awards = all_awards.where(Award.player << these_players) + if team_id: + try: + this_team = Team.get_by_id(team_id) + all_awards = all_awards.where(Award.team == this_team) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team id {team_id} not found') + + return_awards = {} + for x in all_awards: + return_awards[f'{x.id}'] = model_to_dict(x) + + db.close() + return return_awards + + +@app.get('/api/v1/awards/{award_id}') +async def v1_awards_get_one(award_id): + try: + this_award = Award.get_by_id(award_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Award {award_id} not found') + + db.close() + return model_to_dict(this_award) + + +@app.patch('/api/v1/awards/{award_id}') +async def v1_awards_patch( + award_id, name: Optional[str] = None, season: Optional[int] = None, timing: Optional[str] = None, + image: Optional[str] = None, manager1_id: Optional[int] = None, manager2_id: Optional[int] = None, + player_id: Optional[int] = None, team_id: Optional[int] = None, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to patch awards') + + try: + this_award = Award.get_by_id(award_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Award {award_id} not found') + + if name: + this_award.name = name + if image: + this_award.image = image + if timing: + this_award.timing = timing + if season: + this_award.season = season + if manager1_id: + try: + this_manager = Manager.get_by_id(manager1_id) + this_award.manager1 = this_manager + except: + db.close() + raise HTTPException(status_code=404, detail=f'Manager id {manager1_id} not found') + if manager2_id: + try: + this_manager = Manager.get_by_id(manager2_id) + this_award.manager2 = this_manager + except: + db.close() + raise HTTPException(status_code=404, detail=f'Manager id {manager2_id} not found') + if player_id: + try: + this_player = Player.get_by_id(player_id) + this_award.player = this_player + except: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + if team_id: + try: + this_team = Team.get_by_id(team_id) + this_award.team = this_team + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team id {team_id} not found') + + saved = this_award.save() + db.close() + + if saved == 1: + return model_to_dict(this_award) + else: + raise HTTPException(status_code=500, detail='Award update has failed') + + +@app.post('/api/v1/awards') +async def v1_awards_post(awards: AwardModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post awards') + + all_awards = [] + + for award in awards.awards: + this_award = Award( + name=award.name, + season=award.season + ) + + if award.image: + this_award.image = award.image + if award.timing: + this_award.timing = award.timing + if award.manager1_id: + try: + this_manager = Manager.get_by_id(award.manager1_id) + this_award.manager1 = this_manager + except: + db.close() + raise HTTPException(status_code=404, detail=f'Manager id {award.manager1_id} not found') + if award.manager2_id: + try: + this_manager = Manager.get_by_id(award.manager2_id) + this_award.manager2 = this_manager + except: + db.close() + raise HTTPException(status_code=404, detail=f'Manager id {award.manager2_id} not found') + if award.player_id: + try: + this_player = Player.get_by_id(award.player_id) + this_award.player = this_player + except: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {award.player_id} not found') + if award.team_id: + try: + this_team = Team.get_by_id(award.team_id) + this_award.team = this_team + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team id {award.team_id} not found') + + all_awards.append(this_award) + + with db.atomic(): + Award.bulk_create(all_awards, batch_size=15) + db.close() + + raise HTTPException(status_code=200, detail=f'Created {len(all_awards)} awards') + + +@app.delete('/api/v1/awards/{id}') +async def v1_awards_delete(id): + try: + this_award = Award.get_by_id(id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Award {id} not found') + + count = this_award.delete_instance() + db.close() + if count == 1: + raise HTTPException(status_code=200, detail=f'Award {id} has been deleted') + else: + raise HTTPException(status_code=500, detail=f'Award was not deleted') + + +@app.get('/api/v1/dice') +async def v1_dice_get( + season: Optional[int] = None, week_start: Optional[int] = None, week_end: Optional[int] = None, + team_id: Optional[int] = None, roller: Optional[int] = None): + all_dice = DiceRoll.select() + + if season: + all_dice = all_dice.where(DiceRoll.season == season) + + if (week_start and not week_end) or (week_end and not week_start): + if week_start: + all_dice = all_dice.where(DiceRoll.week >= week_start) + else: + all_dice = all_dice.where(DiceRoll.week <= week_start) + if week_start and week_end: + if week_end >= week_start: + all_dice = all_dice.where((DiceRoll.week >= week_start) & (DiceRoll.week <= week_end)) + else: + db.close() + raise HTTPException(status_code=400, detail='Week end must be greater than or equal to week start') + if team_id: + try: + this_team = Team.get_by_id(team_id) + all_dice = all_dice.where(DiceRoll.team == this_team) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team ID {team_id} not found') + if roller: + all_dice = all_dice.where(DiceRoll.roller == roller) + + return_dice = {} + for x in all_dice: + return_dice[f'{x.id}'] = model_to_dict(x) + + db.close() + return return_dice + + +@app.post('/api/v1/dice') +async def v1_dice_post(dice: DiceModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post dice rolls') + all_dice = [] + + for x in dice.rolls: + try: + this_team = Team.get_by_id(x.team_id) + this_die = DiceRoll( + season=x.season, + week=x.week, + team=this_team, + roller=x.roller, + dsix=x.dsix, + twodsix=x.twodsix, + threedsix=x.threedsix, + dtwenty=x.dtwenty + ) + all_dice.append(this_die) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team ID {x.team_id} not found') + + with db.atomic(): + DiceRoll.bulk_create(all_dice, batch_size=20) + + db.close() + raise HTTPException(status_code=200, detail=f'Posted {len(all_dice)} dice rolls') + + +@app.delete('/api/v1/dice') +async def v1_dice_delete( + season: int, week_start: Optional[int] = None, week_end: Optional[int] = None, + team_id: Optional[int] = None, roller: Optional[int] = None): + delete_query = DiceRoll.delete().where(DiceRoll.season == season) + + if (week_start and not week_end) or (week_end and not week_start): + if week_start: + delete_query = delete_query.where(DiceRoll.week >= week_start) + else: + delete_query = delete_query.where(DiceRoll.week <= week_start) + if week_start and week_end: + if week_end >= week_start: + delete_query = delete_query.where((DiceRoll.week >= week_start) & (DiceRoll.week <= week_end)) + else: + db.close() + raise HTTPException(status_code=400, detail='Week end must be greater than or equal to week start') + if team_id: + try: + this_team = Team.get_by_id(team_id) + delete_query = delete_query.where(DiceRoll.team == this_team) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team ID {team_id} not found') + if roller: + delete_query = delete_query.where(DiceRoll.roller == roller) + + print(f'delete_query: {delete_query}') + + count = delete_query.execute() + db.close() + if count > 0: + raise HTTPException(status_code=200, detail=f'Removed {count} batting stat lines') + else: + raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; ' + f'I did not delete any records') + + +@app.post('/api/v1/draft-list') +async def v1_draft_list_post(draft_list: DraftListModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post dice rolls') + all_draft_list = [] + + try: + this_team = Team.get_by_id(draft_list.draft_list[0].team_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Team ID {draft_list.draft_list[0].team_id} not found') + + old_list = DraftList.delete().where( + DraftList.team == this_team + ) + old_list.execute() + + for x in draft_list.draft_list: + try: + this_player = Player.get_by_id(x.player_id) + except: + db.close() + raise HTTPException(status_code=404, detail=f'Player ID {x.player_id} not found') + + this_list = DraftList( + season=x.season, + team=this_team, + rank=x.rank, + player=this_player + ) + all_draft_list.append(this_list) + + with db.atomic(): + DraftList.bulk_create(all_draft_list, batch_size=20) + + db.close() + raise HTTPException(status_code=200, detail=f'Posted {len(all_draft_list)} dice rolls') + + +@app.get('/api/v1/draft-list/{team_id}') +async def v1_draft_list_get( + team_id, return_type: Optional[str] = 'json', csv: Optional[bool] = False, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'Bad Token: {token}') + db.close() + raise HTTPException(status_code=401, detail='You are not authorized to post dice rolls') + + this_team = Team.get_by_id(team_id) + team_list = DraftList.select().where(DraftList.team == this_team) + + if return_type == 'csv' or csv: + return_list = [['season', 'team', 'rank', 'player']] + for x in team_list: + return_list.append([x.season, x.team.abbrev, x.rank, x.player.name]) + else: + return_list = {} + for x in team_list: + return_list[f'{x.id}'] = model_to_dict(x) + + db.close() + return return_list diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1e90bfe --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.61.1 +uvicorn==0.12.2 +peewee==3.13.3 +python-multipart +pandas