Add divisions, update standings

This commit is contained in:
Cal Corum 2023-08-05 00:28:00 -05:00
parent 474448556e
commit 505d0a1a8a
7 changed files with 632 additions and 31 deletions

View File

@ -69,7 +69,7 @@ def games_back(leader, chaser):
def e_number(leader, chaser): def e_number(leader, chaser):
e_num = 89 - leader.wins - chaser.losses e_num = 73 - leader.wins - chaser.losses
return e_num if e_num > 0 else 0 return e_num if e_num > 0 else 0
@ -1219,9 +1219,11 @@ class Standings(BaseModel):
all_teams = Team.select_season(season).where(Team.division) all_teams = Team.select_season(season).where(Team.division)
if full_wipe: if full_wipe:
# Wipe existing data # Wipe existing data
delete_lines = Standings.select_season(season) s_teams = Team.select().where(Team.season == season)
for line in delete_lines: Standings.delete().where(Standings.team << s_teams).execute()
line.delete_instance() # delete_lines = Standings.select_season(season)
# for line in delete_lines:
# line.delete_instance()
# Recreate current season Standings objects # Recreate current season Standings objects
create_teams = [Standings(team=team) for team in all_teams] create_teams = [Standings(team=team) for team in all_teams]
@ -1229,7 +1231,9 @@ class Standings(BaseModel):
Standings.bulk_create(create_teams) Standings.bulk_create(create_teams)
# Iterate through each individual result # Iterate through each individual result
for game in Result.select_season(season).where(Result.week <= 22): # for game in Result.select_season(season).where(Result.week <= 22):
for game in StratGame.select().where(
(StratGame.season == season) & (StratGame.week <= 18) & (StratGame.game_num.is_null(False))):
# tally win and loss for each standings object # tally win and loss for each standings object
game.update_standings() game.update_standings()
@ -1244,11 +1248,11 @@ class Standings(BaseModel):
# Pull each league (filter by not null wc_gb) and sort by win pct # Pull each league (filter by not null wc_gb) and sort by win pct
# # For one league: # # For one league:
# Division.sort_wildcard(season, 'SBa') Division.sort_wildcard(season, 'SBa')
# For two leagues # For two leagues
Division.sort_wildcard(season, 'AL') # Division.sort_wildcard(season, 'AL')
Division.sort_wildcard(season, 'NL') # Division.sort_wildcard(season, 'NL')
class BattingCareer(BaseModel): class BattingCareer(BaseModel):
@ -1849,6 +1853,190 @@ class StratGame(BaseModel):
away_manager = ForeignKeyField(Manager, null=True) away_manager = ForeignKeyField(Manager, null=True)
home_manager = ForeignKeyField(Manager, null=True) home_manager = ForeignKeyField(Manager, null=True)
def update_standings(self):
away_stan = Standings.get_season(self.away_team)
home_stan = Standings.get_season(self.home_team)
away_div = Division.get_by_id(self.away_team.division.id)
home_div = Division.get_by_id(self.home_team.division.id)
if self.home_score > self.away_score:
# - 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.home_score == self.away_score + 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 one league with 3 divisions
# - update record v division
if away_div.division_abbrev == 'FD':
home_stan.div1_wins += 1
elif away_div.division_abbrev == 'NLW':
home_stan.div2_wins += 1
elif away_div.division_abbrev == 'IWGP':
home_stan.div3_wins += 1
else:
home_stan.div4_wins += 1
if home_div.division_abbrev == 'FD':
away_stan.div1_losses += 1
elif home_div.division_abbrev == 'NLW':
away_stan.div2_losses += 1
elif away_div.division_abbrev == 'IWGP':
away_stan.div3_losses += 1
else:
away_stan.div4_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.home_score - self.away_score
away_stan.run_diff -= self.home_score - self.away_score
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.away_score == self.home_score + 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 one league with 4 divisions
# - update record v division
if away_div.division_abbrev == 'FD':
home_stan.div1_losses += 1
elif away_div.division_abbrev == 'NLW':
home_stan.div2_losses += 1
elif away_div.division_abbrev == 'IWGP':
home_stan.div3_losses += 1
else:
home_stan.div4_losses += 1
if home_div.division_abbrev == 'FD':
away_stan.div1_wins += 1
elif home_div.division_abbrev == 'NLW':
away_stan.div2_wins += 1
elif away_div.division_abbrev == 'IWGP':
away_stan.div3_wins += 1
else:
away_stan.div4_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.away_score - self.home_score
away_stan.run_diff += self.away_score - self.home_score
home_stan.save()
away_stan.save()
class StratPlay(BaseModel): class StratPlay(BaseModel):
game = ForeignKeyField(StratGame) game = ForeignKeyField(StratGame)
@ -1877,6 +2065,7 @@ class StratPlay(BaseModel):
pa = IntegerField(default=0) pa = IntegerField(default=0)
ab = IntegerField(default=0) ab = IntegerField(default=0)
e_run = IntegerField(default=0)
run = IntegerField(default=0) run = IntegerField(default=0)
hit = IntegerField(default=0) hit = IntegerField(default=0)
rbi = IntegerField(default=0) rbi = IntegerField(default=0)

View File

@ -8,7 +8,7 @@ from fastapi import Depends, FastAPI, Request
from .routers_v3 import current, players, results, schedules, standings, teams, transactions, battingstats, \ from .routers_v3 import current, players, results, schedules, standings, teams, transactions, battingstats, \
pitchingstats, fieldingstats, draftpicks, draftlist, managers, awards, draftdata, keepers, stratgame, stratplay, \ pitchingstats, fieldingstats, draftpicks, draftlist, managers, awards, draftdata, keepers, stratgame, stratplay, \
injuries, decisions injuries, decisions, divisions
date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}' 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' log_level = logging.INFO if os.environ.get('LOG_LEVEL') == 'INFO' else 'WARN'
@ -46,6 +46,7 @@ app.include_router(stratgame.router)
app.include_router(stratplay.router) app.include_router(stratplay.router)
app.include_router(injuries.router) app.include_router(injuries.router)
app.include_router(decisions.router) app.include_router(decisions.router)
app.include_router(divisions.router)
logging.info(f'Loaded all routers.') logging.info(f'Loaded all routers.')

View File

@ -44,12 +44,13 @@ class DecisionList(pydantic.BaseModel):
@router.get('') @router.get('')
async def get_decisions( async def get_decisions(
season: list = Query(default=None), week: list = Query(default=None), game_num: list = Query(default=None), season: list = Query(default=None), week: list = Query(default=None), game_num: list = Query(default=None),
season_type: Literal['regular', 'post', 'all', None] = None, team_id: list = Query(default=None), s_type: Literal['regular', 'post', 'all', None] = None, team_id: list = Query(default=None),
week_start: Optional[int] = None, week_end: Optional[int] = None, win: Optional[int] = None, week_start: Optional[int] = None, week_end: Optional[int] = None, win: Optional[int] = None,
loss: Optional[int] = None, hold: Optional[int] = None, save: Optional[int] = None, loss: Optional[int] = None, hold: Optional[int] = None, save: Optional[int] = None,
b_save: Optional[int] = None, irunners: list = Query(default=None), irunners_scored: list = Query(default=None), b_save: Optional[int] = None, irunners: list = Query(default=None), irunners_scored: list = Query(default=None),
game_id: list = Query(default=None), short_output: Optional[bool] = False): game_id: list = Query(default=None), player_id: list = Query(default=None),
all_dec = Decision.select() limit: Optional[int] = None, short_output: Optional[bool] = False):
all_dec = Decision.select().order_by(-Decision.season, -Decision.week, -Decision.game_num)
if season is not None: if season is not None:
all_dec = all_dec.where(Decision.season << season) all_dec = all_dec.where(Decision.season << season)
@ -59,12 +60,17 @@ async def get_decisions(
all_dec = all_dec.where(Decision.game_num << game_num) all_dec = all_dec.where(Decision.game_num << game_num)
if game_id is not None: if game_id is not None:
all_dec = all_dec.where(Decision.game_id << game_id) all_dec = all_dec.where(Decision.game_id << game_id)
if season_type is not None: if player_id is not None:
all_games = StratGame.select().where(StratGame.season_type == season_type) all_dec = all_dec.where(Decision.pitcher << player_id)
all_dec = all_dec.where(Decision.game << all_games) if team_id is not None:
if season_type is not None:
all_players = Player.select().where(Player.team_id << team_id) all_players = Player.select().where(Player.team_id << team_id)
all_dec = all_dec.where(Decision.pitcher << all_players) all_dec = all_dec.where(Decision.pitcher << all_players)
if s_type is not None:
all_games = StratGame.select().where(StratGame.season_type == s_type)
all_dec = all_dec.where(Decision.game << all_games)
# if team_id is not None:
# all_players = Player.select().where(Player.team_id << team_id)
# all_dec = all_dec.where(Decision.pitcher << all_players)
if week_start is not None: if week_start is not None:
all_dec = all_dec.where(Decision.week >= week_start) all_dec = all_dec.where(Decision.week >= week_start)
if week_end is not None: if week_end is not None:
@ -84,6 +90,11 @@ async def get_decisions(
if irunners_scored is not None: if irunners_scored is not None:
all_dec = all_dec.where(Decision.irunners_scored << irunners_scored) all_dec = all_dec.where(Decision.irunners_scored << irunners_scored)
if limit is not None:
if limit < 1:
limit = 1
all_dec = all_dec.limit(limit)
return_dec = { return_dec = {
'count': all_dec.count(), 'count': all_dec.count(),
'decisions': [model_to_dict(x, recurse=not short_output) for x in all_dec] 'decisions': [model_to_dict(x, recurse=not short_output) for x in all_dec]

131
app/routers_v3/divisions.py Normal file
View File

@ -0,0 +1,131 @@
from fastapi import APIRouter, Depends, HTTPException, Query, Response
from typing import List, Optional
import logging
import pydantic
from ..db_engine import db, Division, Team, model_to_dict, chunked, fn
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v3/divisions',
tags=['divisions']
)
class DivisionModel(pydantic.BaseModel):
division_name: str
division_abbrev: str
league_name: Optional[str]
league_abbrev: Optional[str]
season: int = 0
@router.get('')
async def get_divisions(
season: int, div_name: Optional[str] = None, div_abbrev: Optional[str] = None, lg_name: Optional[str] = None,
lg_abbrev: Optional[str] = None):
all_divisions = Division.select().where(Division.season == season)
if div_name is not None:
all_divisions = all_divisions.where(Division.division_name == div_name)
if div_abbrev is not None:
all_divisions = all_divisions.where(Division.division_abbrev == div_abbrev)
if lg_name is not None:
all_divisions = all_divisions.where(Division.league_name == lg_name)
if lg_abbrev is not None:
all_divisions = all_divisions.where(Division.league_abbrev == lg_abbrev)
return_div = {
'count': all_divisions.count(),
'divisions': [model_to_dict(x) for x in all_divisions]
}
db.close()
return return_div
@router.get('/{division_id}')
async def get_one_division(division_id: int):
this_div = Division.get_or_none(Division.id == division_id)
if this_div is None:
db.close()
raise HTTPException(status_code=404, detail=f'Division ID {division_id} not found')
r_div = model_to_dict(this_div)
db.close()
return r_div
@router.patch('/{division_id}')
async def patch_division(
division_id: int, div_name: Optional[str] = None, div_abbrev: Optional[str] = None,
lg_name: Optional[str] = None, lg_abbrev: Optional[str] = None, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'patch_division - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
this_div = Division.get_or_none(Division.id == division_id)
if this_div is None:
db.close()
raise HTTPException(status_code=404, detail=f'Division ID {division_id} not found')
if div_name is not None:
this_div.division_name = div_name
if div_abbrev is not None:
this_div.division_abbrev = div_abbrev
if lg_name is not None:
this_div.league_name = lg_name
if lg_abbrev is not None:
this_div.league_abbrev = lg_abbrev
if this_div.save() == 1:
r_division = model_to_dict(this_div)
db.close()
return r_division
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch division {division_id}')
@router.post('')
async def post_division(new_division: DivisionModel, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'post_division - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
this_division = Division(**new_division.dict())
if this_division.save() == 1:
r_division = model_to_dict(this_division)
db.close()
return r_division
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to post division')
@router.delete('/{division_id}')
async def delete_division(division_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'delete_division - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
this_div = Division.get_or_none(Division.id == division_id)
if this_div is None:
db.close()
raise HTTPException(status_code=404, detail=f'Division ID {division_id} not found')
count = this_div.delete_instance()
db.close()
if count == 1:
return f'Division {division_id} has been deleted'
else:
raise HTTPException(status_code=500, detail=f'Division {division_id} could not be deleted')

View File

@ -20,7 +20,7 @@ router = APIRouter(
@router.get('') @router.get('')
async def get_standings( async def get_standings(
season: int, team_abbrev: list = Query(default=None), league_abbrev: Optional[str] = None, season: int, team_id: list = Query(default=None), league_abbrev: Optional[str] = None,
division_abbrev: Optional[str] = None, short_output: Optional[bool] = False): division_abbrev: Optional[str] = None, short_output: Optional[bool] = False):
standings = Standings.select_season(season) standings = Standings.select_season(season)
@ -28,8 +28,8 @@ async def get_standings(
# db.close() # db.close()
# raise HTTPException(status_code=404, detail=f'No output for season {season}') # raise HTTPException(status_code=404, detail=f'No output for season {season}')
if team_abbrev is not None: if team_id is not None:
t_query = Team.select().where(fn.Lower(Team.abbrev) << [x.lower() for x in team_abbrev]) t_query = Team.select().where(Team.id << team_id)
standings = standings.where(Standings.team << t_query) standings = standings.where(Standings.team << t_query)
if league_abbrev is not None: if league_abbrev is not None:

View File

@ -44,7 +44,7 @@ async def get_games(
team1_id: list = Query(default=None), team2_id: list = Query(default=None), played: Optional[bool] = None, team1_id: list = Query(default=None), team2_id: list = Query(default=None), played: Optional[bool] = None,
away_manager_id: list = Query(default=None), home_manager_id: list = Query(default=None), away_manager_id: list = Query(default=None), home_manager_id: list = Query(default=None),
manager1_id: list = Query(default=None), manager2_id: list = Query(default=None), manager1_id: list = Query(default=None), manager2_id: list = Query(default=None),
short_output: Optional[bool] = False): division_id: Optional[int] = None, short_output: Optional[bool] = False):
all_games = StratGame.select() all_games = StratGame.select()
if season is not None: if season is not None:
@ -71,6 +71,11 @@ async def get_games(
all_games = all_games.where( all_games = all_games.where(
(StratGame.away_team_id << team2_id) | (StratGame.home_team_id << team2_id) (StratGame.away_team_id << team2_id) | (StratGame.home_team_id << team2_id)
) )
if division_id is not None:
all_teams = Team.select().where(Team.division == division_id)
all_games = all_games.where(
(StratGame.away_team << all_teams) | (StratGame.home_team << all_teams)
)
if away_manager_id is not None: if away_manager_id is not None:
all_games = all_games.where(StratGame.away_manager_id << away_manager_id) all_games = all_games.where(StratGame.away_manager_id << away_manager_id)
if home_manager_id is not None: if home_manager_id is not None:

View File

@ -4,7 +4,7 @@ import copy
import logging import logging
from pydantic import BaseModel, validator from pydantic import BaseModel, validator
from ..db_engine import db, StratPlay, StratGame, Team, Player, model_to_dict, chunked, fn from ..db_engine import db, StratPlay, StratGame, Team, Player, Decision, model_to_dict, chunked, fn, SQL
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
logging.basicConfig( logging.basicConfig(
@ -47,6 +47,7 @@ class PlayModel(BaseModel):
pa: int = 0 pa: int = 0
ab: int = 0 ab: int = 0
e_run: int = 0
run: int = 0 run: int = 0
hit: int = 0 hit: int = 0
rbi: int = 0 rbi: int = 0
@ -124,6 +125,7 @@ async def get_plays(
offense_team_id: list = Query(default=None), defense_team_id: list = Query(default=None), offense_team_id: list = Query(default=None), defense_team_id: list = Query(default=None),
double: Optional[int] = None, triple: Optional[int] = None, homerun: Optional[int] = None, double: Optional[int] = None, triple: Optional[int] = None, homerun: Optional[int] = None,
sb: Optional[int] = None, cs: Optional[int] = None, manager_id: list = Query(default=None), sb: Optional[int] = None, cs: Optional[int] = None, manager_id: list = Query(default=None),
run: Optional[int] = None, e_run: Optional[int] = None, rbi: list = Query(default=None),
outs: list = Query(default=None), wild_pitch: Optional[int] = None, is_final_out: Optional[bool] = None, outs: list = Query(default=None), wild_pitch: Optional[int] = None, is_final_out: Optional[bool] = None,
is_go_ahead: Optional[bool] = None, is_tied: Optional[bool] = None, is_new_inning: Optional[bool] = None, is_go_ahead: Optional[bool] = None, is_tied: Optional[bool] = None, is_new_inning: Optional[bool] = None,
short_output: Optional[bool] = False, sort: Optional[str] = None, limit: Optional[int] = 200): short_output: Optional[bool] = False, sort: Optional[str] = None, limit: Optional[int] = 200):
@ -174,6 +176,12 @@ async def get_plays(
all_plays = all_plays.where(StratPlay.cs == cs) all_plays = all_plays.where(StratPlay.cs == cs)
if wild_pitch is not None: if wild_pitch is not None:
all_plays = all_plays.where(StratPlay.wild_pitch == wild_pitch) all_plays = all_plays.where(StratPlay.wild_pitch == wild_pitch)
if run is not None:
all_plays = all_plays.where(StratPlay.run == run)
if e_run is not None:
all_plays = all_plays.where(StratPlay.e_run == e_run)
if rbi is not None:
all_plays = all_plays.where(StratPlay.rbi << rbi)
if outs is not None: if outs is not None:
all_plays = all_plays.where(StratPlay.outs << outs) all_plays = all_plays.where(StratPlay.outs << outs)
if manager_id is not None: if manager_id is not None:
@ -211,14 +219,17 @@ async def get_plays(
@router.get('/batting') @router.get('/batting')
async def get_totalplays( async def get_batting_totals(
season: list = Query(default=None), s_type: Literal['regular', 'post', 'total', None] = None, season: list = Query(default=None), week: list = Query(default=None),
s_type: Literal['regular', 'post', 'total', None] = None, position: list = Query(default=None),
player_id: list = Query(default=None), group_by: Literal['team', 'player', 'playerteam'] = 'player', player_id: list = Query(default=None), group_by: Literal['team', 'player', 'playerteam'] = 'player',
min_pa: Optional[int] = 1, team_id: list = Query(default=None), manager_id: list = Query(default=None), min_pa: Optional[int] = 1, team_id: list = Query(default=None), manager_id: list = Query(default=None),
sort: Optional[str] = None, limit: Optional[int] = None, short_output: Optional[bool] = False): sort: Optional[str] = None, limit: Optional[int] = None, short_output: Optional[bool] = False):
season_games = StratGame.select() season_games = StratGame.select()
if season is not None: if season is not None:
season_games = season_games.where(StratGame.season << season) season_games = season_games.where(StratGame.season << season)
if week is not None:
season_games = season_games.where(StratGame.week << week)
if manager_id is not None: if manager_id is not None:
season_games = season_games.where( season_games = season_games.where(
(StratGame.away_manager_id << manager_id) | (StratGame.home_manager_id << manager_id) (StratGame.away_manager_id << manager_id) | (StratGame.home_manager_id << manager_id)
@ -266,6 +277,8 @@ async def get_totalplays(
bat_plays = bat_plays.where(StratPlay.batter_team << all_teams) bat_plays = bat_plays.where(StratPlay.batter_team << all_teams)
run_plays = run_plays.where(StratPlay.runner_team << all_teams) run_plays = run_plays.where(StratPlay.runner_team << all_teams)
def_plays = def_plays.where(StratPlay.defender_team << all_teams) def_plays = def_plays.where(StratPlay.defender_team << all_teams)
if position is not None:
bat_plays = bat_plays.where(StratPlay.batter_pos << position)
if group_by is not None: if group_by is not None:
if group_by == 'player': if group_by == 'player':
@ -289,10 +302,14 @@ async def get_totalplays(
bat_plays = bat_plays.order_by(StratPlay.batter_team) bat_plays = bat_plays.order_by(StratPlay.batter_team)
run_plays = run_plays.order_by(StratPlay.runner_team) run_plays = run_plays.order_by(StratPlay.runner_team)
def_plays = def_plays.order_by(StratPlay.defender_team) def_plays = def_plays.order_by(StratPlay.defender_team)
# elif sort == 'wpa-desc': elif sort == 'wpa-desc':
# bat_plays = bat_plays.order_by(-StratPlay.sum_wpa) bat_plays = bat_plays.order_by(SQL('sum_wpa').desc())
# elif sort == 'wpa-asc': elif sort == 'wpa-asc':
# bat_plays = bat_plays.order_by(StratPlay.sum_wpa) bat_plays = bat_plays.order_by(SQL('sum_wpa').asc())
elif sort == 'pa-desc':
bat_plays = bat_plays.order_by(SQL('sum_pa').desc())
elif sort == 'pa-asc':
bat_plays = bat_plays.order_by(SQL('sum_pa').asc())
if limit is not None: if limit is not None:
if limit < 1: if limit < 1:
limit = 1 limit = 1
@ -300,7 +317,6 @@ async def get_totalplays(
logging.info(f'bat_plays query: {bat_plays}') logging.info(f'bat_plays query: {bat_plays}')
logging.info(f'run_plays query: {run_plays}') logging.info(f'run_plays query: {run_plays}')
logging.info(f'def_plays query: {def_plays}')
return_stats = { return_stats = {
'count': bat_plays.count(), 'count': bat_plays.count(),
@ -318,6 +334,7 @@ async def get_totalplays(
sum_cs = 0 sum_cs = 0
sum_wpa = 0 sum_wpa = 0
tot_ab = x.sum_ab if x.sum_ab > 0 else 1
return_stats['stats'].append({ return_stats['stats'].append({
'player': x.batter_id if short_output else model_to_dict(x.batter, recurse=False), 'player': x.batter_id if short_output else model_to_dict(x.batter, recurse=False),
'team': x.batter_team_id if short_output else model_to_dict(x.batter_team, recurse=False), 'team': x.batter_team_id if short_output else model_to_dict(x.batter_team, recurse=False),
@ -342,10 +359,10 @@ async def get_totalplays(
'bp1b': x.sum_bp1b, 'bp1b': x.sum_bp1b,
'bplo': x.sum_bplo, 'bplo': x.sum_bplo,
'wpa': x.sum_wpa + sum_wpa, 'wpa': x.sum_wpa + sum_wpa,
'avg': x.sum_hit / x.sum_ab, 'avg': x.sum_hit / tot_ab,
'obp': (x.sum_hit + x.sum_bb + x.sum_hbp + x.sum_ibb) / x.sum_pa, 'obp': (x.sum_hit + x.sum_bb + x.sum_hbp + x.sum_ibb) / x.sum_pa,
'slg': (x.sum_hr * 4 + x.sum_triple * 3 + x.sum_double * 2 + 'slg': (x.sum_hr * 4 + x.sum_triple * 3 + x.sum_double * 2 +
(x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr)) / x.sum_ab, (x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr)) / tot_ab,
'woba': (.69 * x.sum_bb + .72 * x.sum_hbp + .89 * (x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr) + 'woba': (.69 * x.sum_bb + .72 * x.sum_hbp + .89 * (x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr) +
1.27 * x.sum_double + 1.62 * x.sum_triple + 2.1 * x.sum_hr) / (x.sum_pa - x.sum_ibb) 1.27 * x.sum_double + 1.62 * x.sum_triple + 2.1 * x.sum_hr) / (x.sum_pa - x.sum_ibb)
}) })
@ -358,6 +375,254 @@ async def get_totalplays(
return return_stats return return_stats
@router.get('/pitching')
async def get_pitching_totals(
season: list = Query(default=None), week: list = Query(default=None),
s_type: Literal['regular', 'post', 'total', None] = None,
player_id: list = Query(default=None), group_by: Literal['team', 'player', 'playerteam'] = 'player',
min_pa: Optional[int] = 1, team_id: list = Query(default=None), manager_id: list = Query(default=None),
sort: Optional[str] = None, limit: Optional[int] = None, short_output: Optional[bool] = False):
season_games = StratGame.select()
if season is not None:
season_games = season_games.where(StratGame.season << season)
if week is not None:
season_games = season_games.where(StratGame.week << week)
if manager_id is not None:
season_games = season_games.where(
(StratGame.away_manager_id << manager_id) | (StratGame.home_manager_id << manager_id)
)
pit_plays = (
StratPlay
.select(StratPlay.pitcher, StratPlay.pitcher_team, fn.SUM(StratPlay.pa).alias('sum_pa'),
fn.SUM(StratPlay.ab).alias('sum_ab'), fn.SUM(StratPlay.run).alias('sum_run'),
fn.SUM(StratPlay.hit).alias('sum_hit'),
fn.SUM(StratPlay.double).alias('sum_double'), fn.SUM(StratPlay.triple).alias('sum_triple'),
fn.SUM(StratPlay.homerun).alias('sum_hr'), fn.SUM(StratPlay.bb).alias('sum_bb'),
fn.SUM(StratPlay.so).alias('sum_so'), fn.SUM(StratPlay.wpa).alias('sum_wpa'),
fn.SUM(StratPlay.hbp).alias('sum_hbp'), fn.SUM(StratPlay.sac).alias('sum_sac'),
fn.SUM(StratPlay.ibb).alias('sum_ibb'), fn.SUM(StratPlay.gidp).alias('sum_gidp'),
fn.SUM(StratPlay.sb).alias('sum_sb'), fn.SUM(StratPlay.cs).alias('sum_cs'),
fn.SUM(StratPlay.bphr).alias('sum_bphr'), fn.SUM(StratPlay.bpfo).alias('sum_bpfo'),
fn.SUM(StratPlay.bp1b).alias('sum_bp1b'), fn.SUM(StratPlay.bplo).alias('sum_bplo'),
fn.SUM(StratPlay.wild_pitch).alias('sum_wp'), fn.SUM(StratPlay.balk).alias('sum_balk'),
fn.SUM(StratPlay.outs).alias('sum_outs'), fn.SUM(StratPlay.e_run).alias('sum_erun'))
.where((StratPlay.game << season_games) & (StratPlay.pitcher.is_null(False)))
.having(fn.SUM(StratPlay.pa) >= min_pa)
)
all_dec = (
Decision
.select(Decision.pitcher, fn.SUM(Decision.win).alias('sum_win'), fn.SUM(Decision.loss).alias('sum_loss'),
fn.SUM(Decision.hold).alias('sum_hold'), fn.SUM(Decision.is_save).alias('sum_save'),
fn.SUM(Decision.b_save).alias('sum_b_save'), fn.SUM(Decision.irunners).alias('sum_irunners'),
fn.SUM(Decision.irunners_scored).alias('sum_irun_scored'),
fn.SUM(Decision.is_start).alias('sum_gs'), fn.COUNT(Decision.game).alias('sum_game'))
.where(Decision.game << season_games)
)
if player_id is not None:
all_players = Player.select().where(Player.id << player_id)
pit_plays = pit_plays.where(StratPlay.pitcher << all_players)
if team_id is not None:
all_teams = Team.select().where(Team.id << team_id)
pit_plays = pit_plays.where(StratPlay.pitcher_team << all_teams)
if group_by is not None:
if group_by == 'player':
pit_plays = pit_plays.group_by(StratPlay.pitcher)
elif group_by == 'team':
pit_plays = pit_plays.group_by(StratPlay.pitcher_team)
elif group_by == 'playerteam':
pit_plays = pit_plays.group_by(StratPlay.pitcher, StratPlay.pitcher_team)
if sort is not None:
if sort == 'player':
pit_plays = pit_plays.order_by(StratPlay.pitcher)
elif sort == 'team':
pit_plays = pit_plays.order_by(StratPlay.pitcher_team)
elif sort == 'wpa-desc':
pit_plays = pit_plays.order_by(SQL('sum_wpa').asc()) # functions seem reversed since pitcher plays negative
elif sort == 'wpa-asc':
pit_plays = pit_plays.order_by(SQL('sum_wpa').desc())
elif sort == 'ip-desc':
pit_plays = pit_plays.order_by(SQL('sum_outs').desc())
elif sort == 'ip-asc':
pit_plays = pit_plays.order_by(SQL('sum_outs').asc())
elif sort == 'game-desc':
pit_plays = pit_plays.order_by(SQL('sum_game').desc())
elif sort == 'game-asc':
pit_plays = pit_plays.order_by(SQL('sum_game').asc())
if limit is not None:
if limit < 1:
limit = 1
pit_plays = pit_plays.limit(limit)
return_stats = {
'count': pit_plays.count(),
'stats': []
}
for x in pit_plays:
this_dec = all_dec.where(Decision.pitcher == x.pitcher)
tot_outs = x.sum_outs if x.sum_outs > 0 else 1
return_stats['stats'].append({
'player': x.pitcher_id if short_output else model_to_dict(x.pitcher, recurse=False),
'team': x.pitcher_team_id if short_output else model_to_dict(x.pitcher_team, recurse=False),
'tbf': x.sum_pa,
'outs': x.sum_outs,
'games': this_dec[0].sum_game,
'gs': this_dec[0].sum_gs,
'win': this_dec[0].sum_win,
'loss': this_dec[0].sum_loss,
'hold': this_dec[0].sum_hold,
'save': this_dec[0].sum_save,
'bsave': this_dec[0].sum_b_save,
'ir': this_dec[0].sum_irunners,
'ir_sc': this_dec[0].sum_irun_scored,
'ab': x.sum_ab,
'run': x.sum_run,
'e_run': x.sum_erun,
'hits': x.sum_hit,
'double': x.sum_double,
'triple': x.sum_triple,
'hr': x.sum_hr,
'bb': x.sum_bb,
'so': x.sum_so,
'hbp': x.sum_hbp,
'sac': x.sum_sac,
'ibb': x.sum_ibb,
'gidp': x.sum_gidp,
'sb': x.sum_sb,
'cs': x.sum_cs,
'bphr': x.sum_bphr,
'bpfo': x.sum_bpfo,
'bp1b': x.sum_bp1b,
'bplo': x.sum_bplo,
'wp': x.sum_wp,
'balk': x.sum_balk,
'wpa': x.sum_wpa * -1,
'era': (x.sum_erun * 27) / tot_outs,
'whip': ((x.sum_bb + x.sum_hit + x.sum_ibb) * 3) / tot_outs,
'avg': x.sum_hit / x.sum_ab,
'obp': (x.sum_hit + x.sum_bb + x.sum_hbp + x.sum_ibb) / x.sum_pa,
'slg': (x.sum_hr * 4 + x.sum_triple * 3 + x.sum_double * 2 +
(x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr)) / x.sum_ab,
'woba': (.69 * x.sum_bb + .72 * x.sum_hbp + .89 * (x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr) +
1.27 * x.sum_double + 1.62 * x.sum_triple + 2.1 * x.sum_hr) / (x.sum_pa - x.sum_ibb)
})
db.close()
return return_stats
@router.get('/fielding')
async def get_fielding_totals(
season: list = Query(default=None), week: list = Query(default=None),
s_type: Literal['regular', 'post', 'total', None] = None, position: list = Query(default=None),
player_id: list = Query(default=None), group_by: Literal['team', 'player', 'playerteam'] = 'player',
min_ch: Optional[int] = 1, team_id: list = Query(default=None), manager_id: list = Query(default=None),
sort: Optional[str] = None, limit: Optional[int] = None, short_output: Optional[bool] = False):
season_games = StratGame.select()
if season is not None:
season_games = season_games.where(StratGame.season << season)
if week is not None:
season_games = season_games.where(StratGame.week << week)
if manager_id is not None:
season_games = season_games.where(
(StratGame.away_manager_id << manager_id) | (StratGame.home_manager_id << manager_id)
)
def_plays = (
StratPlay
.select(StratPlay.defender, StratPlay.defender_team, fn.SUM(StratPlay.error).alias('sum_error'),
fn.SUM(StratPlay.hit).alias('sum_hit'), fn.SUM(StratPlay.pa).alias('sum_chances'),
fn.SUM(StratPlay.wpa).alias('sum_wpa'))
.where((StratPlay.game << season_games) & (StratPlay.defender.is_null(False)))
.having(fn.SUM(StratPlay.pa) >= min_ch)
)
cat_plays = (
StratPlay
.select(StratPlay.catcher, StratPlay.catcher_team, fn.SUM(StratPlay.sb).alias('sum_sb'),
fn.SUM(StratPlay.cs).alias('sum_cs'), fn.SUM(StratPlay.wpa).alias('sum_wpa'),
fn.SUM(StratPlay.passed_ball).alias('sum_pb'))
.where((StratPlay.game << season_games) & (StratPlay.catcher.is_null(False)))
)
if player_id is not None:
all_players = Player.select().where(Player.id << player_id)
def_plays = def_plays.where(StratPlay.defender << all_players)
cat_plays = cat_plays.where(StratPlay.catcher << all_players)
if team_id is not None:
all_teams = Team.select().where(Team.id << team_id)
def_plays = def_plays.where(StratPlay.defender_team << all_teams)
cat_plays = cat_plays.where(StratPlay.catcher_team << all_teams)
if position is not None:
def_plays = def_plays.where(StratPlay.check_pos << position)
if group_by is not None:
if group_by == 'player':
def_plays = def_plays.group_by(StratPlay.defender)
cat_plays = cat_plays.group_by(StratPlay.catcher)
elif group_by == 'team':
def_plays = def_plays.group_by(StratPlay.defender_team)
cat_plays = cat_plays.group_by(StratPlay.catcher_team)
elif group_by == 'playerteam':
def_plays = def_plays.group_by(StratPlay.defender, StratPlay.defender_team)
cat_plays = cat_plays.group_by(StratPlay.catcher, StratPlay.catcher_team)
if sort is not None:
if sort == 'player':
def_plays = def_plays.order_by(StratPlay.defender)
elif sort == 'team':
def_plays = def_plays.order_by(StratPlay.defender_team)
elif sort == 'wpa-desc':
def_plays = def_plays.order_by(SQL('sum_wpa').asc())
elif sort == 'wpa-asc':
def_plays = def_plays.order_by(SQL('sum_wpa').desc())
elif sort == 'ch-desc':
def_plays = def_plays.order_by(SQL('sum_chances').desc())
elif sort == 'ch-asc':
def_plays = def_plays.order_by(SQL('sum_chances').asc())
if limit is not None:
if limit < 1:
limit = 1
def_plays = def_plays.limit(limit)
logging.info(f'def_plays query: {def_plays}')
return_stats = {
'count': def_plays.count(),
'stats': []
}
for x in def_plays:
this_cat = cat_plays.where(StratPlay.catcher == x.defender)
if this_cat.count() > 0:
sum_sb = this_cat[0].sum_sb
sum_cs = this_cat[0].sum_cs
sum_wpa = this_cat[0].sum_wpa
sum_pb = this_cat[0].sum_pb
else:
sum_sb = 0
sum_cs = 0
sum_wpa = 0
sum_pb = 0
return_stats['stats'].append({
'player': x.defender_id if short_output else model_to_dict(x.defender, recurse=False),
'team': x.defender_team_id if short_output else model_to_dict(x.defender_team, recurse=False),
'pos': 'TOT' if position is None or len(position) > 1 else position[0],
'x-ch': x.sum_chances,
'hit': x.sum_hit,
'error': x.sum_error,
'sb-ch': sum_sb + sum_cs,
'sb': sum_sb,
'cs': sum_cs,
'pb': sum_pb,
'wpa': (x.sum_wpa + sum_wpa) * -1,
'wf%': (x.sum_chances - (x.sum_error * .5) - (x.sum_hit * .75)) / x.sum_chances,
'cs%': sum_cs / (sum_sb + sum_cs) if (sum_sb + sum_cs) > 0 else ''
})
db.close()
return return_stats
@router.get('/{play_id}') @router.get('/{play_id}')
async def get_one_play(play_id: int): async def get_one_play(play_id: int):
if StratPlay.get_or_none(StratPlay.id == play_id) is None: if StratPlay.get_or_none(StratPlay.id == play_id) is None:
@ -413,7 +678,6 @@ async def post_plays(p_list: PlayList, token: str = Depends(oauth2_scheme)):
if this_play.pa == 0: if this_play.pa == 0:
this_play.batter_final = None this_play.batter_final = None
new_plays.append(this_play.dict()) new_plays.append(this_play.dict())
with db.atomic(): with db.atomic():