Moved AI lineup creation to db

This commit is contained in:
Cal Corum 2023-10-21 15:31:36 -05:00
parent b40409e844
commit 0a59d3e98d
5 changed files with 389 additions and 8 deletions

View File

@ -26,6 +26,27 @@ logging.basicConfig(
level=log_level
)
CARDSETS = {
'ranked': {
'primary': [9, 10, 13] # 2023, 23 Promos, 2018
},
'minor-league': {
'primary': [13, 8], # 2018, Mario
'secondary': [9, 3] # 2023, 2022
},
'major-league': {
'primary': [13, 9, 8, 6], # 2018, 2023, Mario, 2013
'secondary': [3, 12] # 2022, 2008
},
'hall-of-fame': {
'primary': [1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
},
'tens': {
'primary': [11, 7, 6, 12], # 2016, 2012, 2013, 2008, Mario
'secondary': [13, 5] # 2018, 2019
}
}
def model_csv_headers(this_obj, exclude=None) -> List:
data = model_to_dict(this_obj, recurse=False, exclude=exclude)
@ -695,6 +716,94 @@ CardPosition.add_index(pos_index)
db.create_tables([BattingCard, BattingCardRatings, PitchingCard, PitchingCardRatings, CardPosition])
class StratGame(BaseModel):
season = IntegerField()
game_type = CharField()
away_team = ForeignKeyField(Team)
home_team = ForeignKeyField(Team)
week = IntegerField(default=1)
away_score = IntegerField(default=0)
home_score = IntegerField(default=0)
away_team_value = IntegerField(null=True)
home_team_value = IntegerField(null=True)
away_team_ranking = IntegerField(null=True)
home_team_ranking = IntegerField(null=True)
ranked = BooleanField(default=False)
short_game = BooleanField(default=False)
class StratPlay(BaseModel):
game = ForeignKeyField(StratGame)
play_num = IntegerField()
batter = ForeignKeyField(Player, null=True)
batter_team = ForeignKeyField(Team, null=True)
pitcher = ForeignKeyField(Player)
pitcher_team = ForeignKeyField(Team)
on_base_code = CharField()
inning_half = CharField()
inning_num = IntegerField()
batting_order = IntegerField()
starting_outs = IntegerField()
away_score = IntegerField()
home_score = IntegerField()
batter_pos = CharField(null=True)
# These <base>_final fields track the base this runner advances to post-play (None) if out
on_first = ForeignKeyField(Player, null=True)
on_first_final = IntegerField(null=True)
on_second = ForeignKeyField(Player, null=True)
on_second_final = IntegerField(null=True)
on_third = ForeignKeyField(Player, null=True)
on_third_final = IntegerField(null=True)
batter_final = IntegerField(null=True)
pa = IntegerField(default=0)
ab = IntegerField(default=0)
e_run = IntegerField(default=0)
run = IntegerField(default=0)
hit = IntegerField(default=0)
rbi = IntegerField(default=0)
double = IntegerField(default=0)
triple = IntegerField(default=0)
homerun = IntegerField(default=0)
bb = IntegerField(default=0)
so = IntegerField(default=0)
hbp = IntegerField(default=0)
sac = IntegerField(default=0)
ibb = IntegerField(default=0)
gidp = IntegerField(default=0)
bphr = IntegerField(default=0)
bpfo = IntegerField(default=0)
bp1b = IntegerField(default=0)
bplo = IntegerField(default=0)
sb = IntegerField(default=0)
cs = IntegerField(default=0)
outs = IntegerField(default=0)
wpa = FloatField(default=0)
# These <position> fields are only required if the play is an x-check or baserunning play
catcher = ForeignKeyField(Player, null=True)
catcher_team = ForeignKeyField(Team, null=True)
defender = ForeignKeyField(Player, null=True)
defender_team = ForeignKeyField(Team, null=True)
runner = ForeignKeyField(Player, null=True)
runner_team = ForeignKeyField(Team, null=True)
check_pos = CharField(null=True)
error = IntegerField(default=0)
wild_pitch = IntegerField(default=0)
passed_ball = IntegerField(default=0)
pick_off = IntegerField(default=0)
balk = IntegerField(default=0)
is_go_ahead = BooleanField(default=False)
is_tied = BooleanField(default=False)
is_new_inning = BooleanField(default=False)
db.create_tables([StratGame, StratPlay])
db.close()
# scout_db = SqliteDatabase(

View File

@ -125,8 +125,7 @@ async def get_card_ratings(
for x in return_vals:
x.update(x['battingcard'])
x['player_id'] = x['battingcard']['player']['player_id']
del x['battingcard']
del x['player']
del x['battingcard'], x['player']
db.close()
return Response(content=pd.DataFrame(return_vals).to_csv(index=False), media_type='text/csv')

View File

@ -4,7 +4,7 @@ import logging
import pydantic
from pandas import DataFrame
from ..db_engine import db, Card, model_to_dict, Team, Player, Pack, Paperdex
from ..db_engine import db, Card, model_to_dict, Team, Player, Pack, Paperdex, CARDSETS
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
logging.basicConfig(
@ -218,15 +218,18 @@ async def v1_cards_legal_check(
status_code=401,
detail='Unauthorized'
)
if rarity_name not in ['ranked']:
if rarity_name not in CARDSETS.keys():
return f'Rarity name {rarity_name} not a valid check'
bad_cards = []
all_cards = Card.select().where(Card.id << card_id)
for x in all_cards:
if x.player.cardset_id not in [3, 4, 9, 10]:
bad_cards.append(x.player.description)
if x.player.cardset_id not in CARDSETS[rarity_name]:
if x.player.p_name in x.player.description:
bad_cards.append(x.player.description)
else:
bad_cards.append(f'{x.player.description} {x.player.p_name}')
return {'count': len(bad_cards), 'bad_cards': bad_cards}

View File

@ -1,4 +1,5 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Response
from pandas import DataFrame
from typing import Optional
import logging
import pydantic

View File

@ -8,7 +8,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import db, Team, model_to_dict, fn, Pack, Card, Player, Paperdex, Notification, PackType, \
Rarity, Current, query_to_csv, complex_data_to_csv
Rarity, Current, query_to_csv, complex_data_to_csv, CARDSETS, CardPosition, BattingCardRatings, BattingCard, \
PitchingCard, PitchingCardRatings
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, int_timestamp
logging.basicConfig(
@ -153,6 +154,274 @@ async def get_one_team(team_id, csv: Optional[bool] = False):
return return_val
@router.get('/{team_id}/lineup/{difficulty_name}')
async def get_team_lineup(team_id: int, difficulty_name: str, pitcher_name: str, d_rank: int = 5, o_rank: int = 5):
"""
d_rank: int - 10: best overall, 9: prioritize range, 8: prioritize error
"""
this_team = Team.get_or_none(Team.id == team_id)
if this_team is None:
db.close()
raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')
if difficulty_name not in CARDSETS.keys():
db.close()
raise HTTPException(status_code=400, detail=f'Difficulty name {difficulty_name} not a valid check')
# all_players = Player.select().where(
# (fn.Lower(Player.p_name) != pitcher_name.lower()) & (Player.mlbclub == this_team.lname)
# )
all_players = Player.select().where(Player.mlbclub == this_team.lname)
legal_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['primary'])
if 'secondary' in CARDSETS[difficulty_name]:
backup_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['secondary'])
else:
backup_players = None
logging.info(f'legal_players: {legal_players.count()}')
if backup_players is not None:
logging.info(f'backup_players: {backup_players.count()}')
player_names = []
starting_nine = {
'C': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
'1B': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
'2B': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
'3B': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
'SS': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
'LF': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
'CF': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
'RF': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
'DH': {'player': None, 'vl': None, 'vr': None, 'ops': 0}
}
def get_bratings(player_id):
this_bcard = BattingCard.get_or_none(BattingCard.player_id == player_id)
vl_ratings = BattingCardRatings.get_or_none(
BattingCardRatings.battingcard == this_bcard, BattingCardRatings.vs_hand == 'L'
)
vl_ops = vl_ratings.obp + vl_ratings.slg
vr_ratings = BattingCardRatings.get_or_none(
BattingCardRatings.battingcard == this_bcard, BattingCardRatings.vs_hand == 'R'
)
vr_ops = vr_ratings.obp + vr_ratings.slg
return model_to_dict(vl_ratings), model_to_dict(vr_ratings), (vl_ops + vr_ops + min(vl_ops, vr_ops)) / 3
for position in starting_nine.keys():
if position == 'DH':
# all_bcards = BattingCard.select().where(BattingCard.player << legal_players)
# all_batters = BattingCardRatings.select().where(
# BattingCardRatings.battingcard << all_bcards
# ).order_by(BattingCardRatings.obp + BattingCardRatings.sl)
#
# for x in all_batters:
# if x.battingcard.player.p_name not in player_names:
# starting_nine['DH'] = x.battingcard.player
# break
logging.info(f'Searching for a DH!')
dh_query = legal_players.order_by(Player.cost.desc())
for x in dh_query:
logging.info(f'checking {x.p_name} for {position}')
if x.p_name not in player_names and 'P' not in x.pos_1:
logging.info(f'adding!')
starting_nine['DH']['player'] = model_to_dict(x)
try:
vl, vr, total_ops = get_bratings(x.player_id)
except AttributeError as e:
logging.info(f'Could not find batting lines')
else:
starting_nine['DH']['vl'] = vl
starting_nine['DH']['vr'] = vr
starting_nine['DH']['ops'] = total_ops
player_names.append(x.p_name)
break
if starting_nine['DH']['player'] is None:
dh_query = backup_players.order_by(Player.cost.desc())
for x in dh_query:
logging.info(f'checking {x.p_name} for {position}')
if x.p_name not in player_names:
logging.info(f'adding!')
starting_nine['DH']['player'] = model_to_dict(x)
try:
vl, vr, total_ops = get_bratings(x.player_id)
except AttributeError as e:
logging.info(f'Could not find batting lines')
else:
vl, vr, total_ops = get_bratings(x.player_id)
starting_nine['DH']['vl'] = vl
starting_nine['DH']['vr'] = vr
starting_nine['DH']['ops'] = total_ops
player_names.append(x.p_name)
break
else:
pos_group = CardPosition.select().where(
(CardPosition.position == position) & (CardPosition.player << legal_players)
)
backup_group = CardPosition.select().where(
(CardPosition.position == position) & (CardPosition.player << backup_players)
)
if difficulty_name == 'minor-league':
pos_group = pos_group.order_by(CardPosition.innings.desc())
elif d_rank == 10:
pos_group = pos_group.order_by((CardPosition.range * 5) + CardPosition.error)
elif d_rank == 9:
pos_group = pos_group.order_by(CardPosition.range)
elif d_rank == 8:
pos_group = pos_group.order_by(CardPosition.error.desc())
logging.info(f'pos_group: {pos_group}\n{starting_nine}\n{player_names}\n\n')
if difficulty_name == 'minor-league':
for x in pos_group:
logging.info(f'checking {x.player.p_name} for {position}')
if x.player.p_name not in player_names and x.player.p_name.lower() != pitcher_name:
logging.info(f'adding!')
starting_nine[position]['player'] = model_to_dict(x.player)
vl, vr, total_ops = get_bratings(x.player.player_id)
starting_nine[position]['vl'] = vl
starting_nine[position]['vr'] = vr
starting_nine[position]['ops'] = total_ops
player_names.append(x.player.p_name)
break
if starting_nine[position]['player'] is None:
for x in backup_group:
logging.info(f'checking {x.player.p_name} for {position}')
if x.player.p_name not in player_names and x.player.p_name.lower() != pitcher_name:
logging.info(f'adding!')
starting_nine[position]['player'] = model_to_dict(x.player)
vl, vr, total_ops = get_bratings(x.player.player_id)
starting_nine[position]['vl'] = vl
starting_nine[position]['vr'] = vr
starting_nine[position]['ops'] = total_ops
player_names.append(x.player.p_name)
break
# all_bcards = BattingCard.select().where(BattingCard.player << starting_nine.values())
# all_ratings = BattingCardRatings.select().where(BattingCardRatings.battingcard << all_bcards)
#
# vl_query = all_ratings.where(BattingCardRatings.vs_hand == 'L')
# vr_query = all_ratings.where(BattingCardRatings.vs_hand == 'R')
#
# vl_vals = [model_to_dict(x) for x in vl_query]
# for x in vl_vals:
# x.update(x['battingcard'])
# x['player_id'] = x['battingcard']['player']['player_id']
# x['player_name'] = x['battingcard']['player']['p_name']
# x['rarity'] = x['battingcard']['player']['rarity']['name']
# x['cardset_id'] = x['battingcard']['player']['cardset']['id']
# x['cardset_name'] = x['battingcard']['player']['cardset']['name']
# del x['player']
#
# vr_vals = [model_to_dict(x) for x in vr_query]
# for x in vr_vals:
# x['player_id'] = x['battingcard']['player']['player_id']
# del x['battingcard']
#
# vl = pd.DataFrame(vl_vals)
# vr = pd.DataFrame(vr_vals)
# db.close()
#
# output = pd.merge(vl, vr, on='player_id', suffixes=('_vl', '_vr'))
#
# def get_total_ops(df_data):
# ops_vl = df_data['obp_vL'] + df_data['slg_vL']
# ops_vr = df_data['obp_vR'] + df_data['slg_vR']
# return (ops_vr + ops_vl + min(ops_vl, ops_vr)) / 3
# output['total_OPS'] = output.apply(get_total_ops, axis=1)
# output = output.sort_values(by=['total_OPS'], ascending=False)
sorted_nine = sorted(starting_nine.items(), key=lambda item: item[1]['ops'], reverse=True)
return {
'json': dict(sorted_nine),
'array': sorted_nine
}
@router.get('/{team_id}/sp/{difficulty_name}')
async def get_team_sp(team_id: int, difficulty_name: str, sp_rank: int):
logging.info(f'get_team_sp - team_id: {team_id} / difficulty_name: {difficulty_name} / sp_rank: {sp_rank}')
this_team = Team.get_or_none(Team.id == team_id)
if this_team is None:
db.close()
raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')
if difficulty_name not in CARDSETS.keys():
db.close()
raise HTTPException(status_code=400, detail=f'Difficulty name {difficulty_name} not a valid check')
all_players = Player.select().where(Player.mlbclub == this_team.lname)
legal_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['primary'])
if 'secondary' in CARDSETS[difficulty_name]:
backup_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['secondary'])
else:
backup_players = None
def sort_starters(starter_query) -> DataFrame | None:
all_s = [model_to_dict(x, recurse=False) for x in starter_query]
if len(all_s) == 0:
logging.error(f'Empty starter_query: {starter_query}')
return None
starter_df = pd.DataFrame(all_s).set_index('player', drop=False)
logging.debug(f'starter_df: {starter_df}')
def get_total_ops(df_data):
vlval = PitchingCardRatings.get_or_none(
PitchingCardRatings.pitchingcard_id == df_data['id'], PitchingCardRatings.vs_hand == 'L')
vrval = PitchingCardRatings.get_or_none(
PitchingCardRatings.pitchingcard_id == df_data['id'], PitchingCardRatings.vs_hand == 'R')
ops_vl = vlval.obp + vlval.slg
ops_vr = vrval.obp + vrval.slg
return (ops_vr + ops_vl + min(ops_vl, ops_vr)) / 3
starter_df['total_ops'] = starter_df.apply(get_total_ops, axis=1)
return starter_df.sort_values(by='total_ops')
# Find SP in primary cardsets
s_query = PitchingCard.select().join(Player).where(
(PitchingCard.player << legal_players) & (PitchingCard.starter_rating >= 4)
)
all_starters = sort_starters(s_query)
logging.debug(f'sorted: {all_starters}')
if all_starters is not None and len(all_starters.index) >= sp_rank:
this_player_id = all_starters.iloc[sp_rank - 1].player
this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False)
db.close()
return this_player
if all_starters is not None and len(all_starters.index) > 0:
this_player_id = all_starters.iloc[len(all_starters.index) - 1].player
this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False)
db.close()
return this_player
# Include backup cardsets
s_query = PitchingCard.select().where(
(PitchingCard.player << backup_players) & (PitchingCard.starter_rating >= 4)
)
all_starters = sort_starters(s_query)
logging.debug(f'sorted: {all_starters}')
if all_starters is not None and len(all_starters.index) >= sp_rank:
this_player_id = all_starters.iloc[sp_rank - 1].player
this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False)
db.close()
return this_player
if all_starters is not None and len(all_starters.index) > 0:
this_player_id = all_starters.iloc[len(all_starters.index) - 1].player
this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False)
db.close()
return this_player
raise HTTPException(status_code=500, detail=f'No SP #{sp_rank} found for Team {team_id}')
@router.get('/{team_id}/buy/players')
async def team_buy_players(team_id: int, ids: str, ts: str):
try: