Update api logging
New Position exception Pull scouting data with lineups More bunt types String validation on gameplay models AI Defensive alignment
This commit is contained in:
parent
bfd72ae0f5
commit
3d333dabc3
21
api_calls.py
21
api_calls.py
@ -39,14 +39,23 @@ def get_req_url(endpoint: str, api_ver: int = 2, object_id: int = None, params:
|
|||||||
|
|
||||||
|
|
||||||
def log_return_value(log_string: str):
|
def log_return_value(log_string: str):
|
||||||
if master_debug:
|
start = 0
|
||||||
logger.info(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}\n')
|
end = 3000
|
||||||
else:
|
while end < 300000:
|
||||||
logger.debug(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}\n')
|
line = log_string[start:end]
|
||||||
|
if len(line) == 0:
|
||||||
|
return
|
||||||
|
logger.info(f'{"\n\nreturn: " if start == 0 else ""}{log_string[start:end]}')
|
||||||
|
start += 3000
|
||||||
|
end += 3000
|
||||||
|
logger.warning('[ S N I P P E D ]')
|
||||||
|
# if master_debug:
|
||||||
|
# logger.info(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}\n')
|
||||||
|
# else:
|
||||||
|
# logger.debug(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}\n')
|
||||||
|
|
||||||
|
|
||||||
async def db_get(endpoint: str, api_ver: int = 2, object_id: int = None, params: list = None, none_okay: bool = True,
|
async def db_get(endpoint: str, api_ver: int = 2, object_id: int = None, params: list = None, none_okay: bool = True, timeout: int = 3):
|
||||||
timeout: int = 3):
|
|
||||||
req_url = get_req_url(endpoint, api_ver=api_ver, object_id=object_id, params=params)
|
req_url = get_req_url(endpoint, api_ver=api_ver, object_id=object_id, params=params)
|
||||||
log_string = f'db_get - get: {endpoint} id: {object_id} params: {params}'
|
log_string = f'db_get - get: {endpoint} id: {object_id} params: {params}'
|
||||||
logger.info(log_string) if master_debug else logger.debug(log_string)
|
logger.info(log_string) if master_debug else logger.debug(log_string)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ from helpers import DEFENSE_LITERAL, PD_PLAYERS_ROLE_NAME, get_channel, team_rol
|
|||||||
from in_game.ai_manager import get_starting_pitcher, get_starting_lineup
|
from in_game.ai_manager import get_starting_pitcher, get_starting_lineup
|
||||||
from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME, legal_check
|
from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME, legal_check
|
||||||
from in_game.gameplay_models import Lineup, Play, Session, engine, player_description, select, Game
|
from in_game.gameplay_models import Lineup, Play, Session, engine, player_description, select, Game
|
||||||
from in_game.gameplay_queries import get_channel_game_or_none, get_active_games_by_team, get_game_lineups, get_team_or_none, get_card_or_none
|
from in_game.gameplay_queries import get_and_cache_position, get_channel_game_or_none, get_active_games_by_team, get_game_lineups, get_team_or_none, get_card_or_none
|
||||||
|
|
||||||
from utilities.buttons import Confirm, ask_confirm
|
from utilities.buttons import Confirm, ask_confirm
|
||||||
|
|
||||||
@ -211,6 +211,8 @@ class Gameplay(commands.Cog):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
await get_and_cache_position(session, human_sp_card, 'P')
|
||||||
|
|
||||||
legal_data = await legal_check([sp_card_id], difficulty_name=league.value)
|
legal_data = await legal_check([sp_card_id], difficulty_name=league.value)
|
||||||
if not legal_data['legal']:
|
if not legal_data['legal']:
|
||||||
await interaction.edit_original_response(
|
await interaction.edit_original_response(
|
||||||
@ -259,6 +261,12 @@ class Gameplay(commands.Cog):
|
|||||||
# Commit game and lineups
|
# Commit game and lineups
|
||||||
session.add(this_game)
|
session.add(this_game)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
await final_message.edit(content=f'The {ai_team.sname} lineup is in, pulling in scouting data...')
|
||||||
|
for batter in batter_lineups:
|
||||||
|
if batter.position != 'DH':
|
||||||
|
await get_and_cache_position(session, batter.card, batter.position)
|
||||||
|
|
||||||
# session.refresh(this_game)
|
# session.refresh(this_game)
|
||||||
|
|
||||||
away_role = await team_role(interaction, away_team)
|
away_role = await team_role(interaction, away_team)
|
||||||
@ -366,11 +374,17 @@ class Gameplay(commands.Cog):
|
|||||||
|
|
||||||
human_lineups = await get_lineups_from_sheets(session, self.sheets, this_game, this_team=lineup_team, lineup_num=lineup.name, roster_num=int(roster.value))
|
human_lineups = await get_lineups_from_sheets(session, self.sheets, this_game, this_team=lineup_team, lineup_num=lineup.name, roster_num=int(roster.value))
|
||||||
|
|
||||||
|
await interaction.edit_original_response(content='Heard from sheets, pulling in scouting data...')
|
||||||
|
|
||||||
for batter in human_lineups:
|
for batter in human_lineups:
|
||||||
session.add(batter)
|
session.add(batter)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
for batter in human_lineups:
|
||||||
|
if batter.position != 'DH':
|
||||||
|
await get_and_cache_position(session, batter.card, batter.position)
|
||||||
|
|
||||||
await interaction.edit_original_response(content=None, embed=this_game.get_scorebug_embed(session))
|
await interaction.edit_original_response(content=None, embed=this_game.get_scorebug_embed(session))
|
||||||
|
|
||||||
@app_commands.command(name='gamestate', description='Post the current game state')
|
@app_commands.command(name='gamestate', description='Post the current game state')
|
||||||
@ -397,8 +411,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log flyball')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log flyball')
|
||||||
|
|
||||||
this_play = await flyballs(session, interaction, this_play, flyball_type)
|
|
||||||
logger.info(f'log flyball {flyball_type} - this_play: {this_play}')
|
logger.info(f'log flyball {flyball_type} - this_play: {this_play}')
|
||||||
|
this_play = await flyballs(session, interaction, this_play, flyball_type)
|
||||||
|
|
||||||
await self.complete_and_post_play(
|
await self.complete_and_post_play(
|
||||||
session,
|
session,
|
||||||
@ -413,8 +427,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log single')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log single')
|
||||||
|
|
||||||
this_play = await singles(session, interaction, this_play, single_type)
|
|
||||||
logger.info(f'log single {single_type} - this_play: {this_play}')
|
logger.info(f'log single {single_type} - this_play: {this_play}')
|
||||||
|
this_play = await singles(session, interaction, this_play, single_type)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play, buffer_message='Double logged' if ((this_play.on_first or this_play.on_second) and single_type == 'uncapped') else None)
|
await self.complete_and_post_play(session, interaction, this_play, buffer_message='Double logged' if ((this_play.on_first or this_play.on_second) and single_type == 'uncapped') else None)
|
||||||
|
|
||||||
@ -437,8 +451,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log double')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log double')
|
||||||
|
|
||||||
this_play = await doubles(session, interaction, this_play, double_type)
|
|
||||||
logger.info(f'log double {double_type} - this_play: {this_play}')
|
logger.info(f'log double {double_type} - this_play: {this_play}')
|
||||||
|
this_play = await doubles(session, interaction, this_play, double_type)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play, buffer_message='Double logged' if (this_play.on_first and double_type == 'uncapped') else None)
|
await self.complete_and_post_play(session, interaction, this_play, buffer_message='Double logged' if (this_play.on_first and double_type == 'uncapped') else None)
|
||||||
|
|
||||||
@ -447,8 +461,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log triple')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log triple')
|
||||||
|
|
||||||
this_play = await triples(session, interaction, this_play)
|
|
||||||
logger.info(f'log triple - this_play: {this_play}')
|
logger.info(f'log triple - this_play: {this_play}')
|
||||||
|
this_play = await triples(session, interaction, this_play)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play)
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
@ -457,8 +471,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log homerun')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log homerun')
|
||||||
|
|
||||||
this_play = await homeruns(session, interaction, this_play, homerun_type)
|
|
||||||
logger.info(f'log homerun {homerun_type} - this_play: {this_play}')
|
logger.info(f'log homerun {homerun_type} - this_play: {this_play}')
|
||||||
|
this_play = await homeruns(session, interaction, this_play, homerun_type)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play)
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
@ -467,8 +481,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log walk')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log walk')
|
||||||
|
|
||||||
this_play = await walks(session, interaction, this_play, walk_type)
|
|
||||||
logger.info(f'log walk {walk_type} - this_play: {this_play}')
|
logger.info(f'log walk {walk_type} - this_play: {this_play}')
|
||||||
|
this_play = await walks(session, interaction, this_play, walk_type)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play)
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
@ -477,8 +491,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log strikeout')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log strikeout')
|
||||||
|
|
||||||
this_play = await strikeouts(session, interaction, this_play)
|
|
||||||
logger.info(f'log strikeout - this_play: {this_play}')
|
logger.info(f'log strikeout - this_play: {this_play}')
|
||||||
|
this_play = await strikeouts(session, interaction, this_play)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play)
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
@ -487,8 +501,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log popout')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log popout')
|
||||||
|
|
||||||
this_play = await popouts(session, interaction, this_play)
|
|
||||||
logger.info(f'log popout - this_play: {this_play}')
|
logger.info(f'log popout - this_play: {this_play}')
|
||||||
|
this_play = await popouts(session, interaction, this_play)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play)
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
@ -497,18 +511,29 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log hit-by-pitch')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log hit-by-pitch')
|
||||||
|
|
||||||
this_play = await hit_by_pitch(session, interaction, this_play)
|
|
||||||
logger.info(f'log hit-by-pitch - this_play: {this_play}')
|
logger.info(f'log hit-by-pitch - this_play: {this_play}')
|
||||||
|
this_play = await hit_by_pitch(session, interaction, this_play)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play)
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
@group_log.command(name='bunt', description='Hit by pitch: batter to first; runners advance if forced')
|
@group_log.command(name='bunt', description='Bunts: sacrifice, bad, popout, double-play, defense')
|
||||||
async def log_sac_bunt(self, interaction: discord.Interaction, bunt_type: Literal['sacrifice', 'bad', 'popout', 'double-play', 'defense']):
|
async def log_sac_bunt(self, interaction: discord.Interaction, bunt_type: Literal['sacrifice', 'bad', 'popout', 'double-play', 'defense']):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log bunt')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log bunt')
|
||||||
|
|
||||||
this_play = await bunts(session, interaction, this_play, bunt_type)
|
if this_play.on_base_code == 0:
|
||||||
|
await interaction.edit_original_response(
|
||||||
|
content=f'You cannot bunt when the bases are empty.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
elif this_play.starting_outs == 2:
|
||||||
|
await interaction.edit_original_response(
|
||||||
|
content=f'You cannot bunt with two outs.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
logger.info(f'log bunt - this_play: {this_play}')
|
logger.info(f'log bunt - this_play: {this_play}')
|
||||||
|
this_play = await bunts(session, interaction, this_play, bunt_type)
|
||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play)
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
@ -517,8 +542,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log undo-play')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log undo-play')
|
||||||
|
|
||||||
this_play = undo_play(session, this_play)
|
|
||||||
logger.info(f'log undo-play - this_play: {this_play}')
|
logger.info(f'log undo-play - this_play: {this_play}')
|
||||||
|
this_play = undo_play(session, this_play)
|
||||||
|
|
||||||
await self.post_play(session, interaction, this_play)
|
await self.post_play(session, interaction, this_play)
|
||||||
|
|
||||||
@ -528,8 +553,8 @@ class Gameplay(commands.Cog):
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='show-card defense')
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='show-card defense')
|
||||||
|
|
||||||
await show_defense_cards(session, interaction, this_play, position)
|
|
||||||
logger.info(f'show-card defense - position: {position}')
|
logger.info(f'show-card defense - position: {position}')
|
||||||
|
await show_defense_cards(session, interaction, this_play, position)
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
async def setup(bot):
|
||||||
|
|||||||
@ -21,6 +21,16 @@ from utilities.pages import Pagination
|
|||||||
|
|
||||||
logger = logging.getLogger('discord_app')
|
logger = logging.getLogger('discord_app')
|
||||||
WPA_DF = pd.read_csv(f'storage/wpa_data.csv').set_index('index')
|
WPA_DF = pd.read_csv(f'storage/wpa_data.csv').set_index('index')
|
||||||
|
TO_BASE = {
|
||||||
|
2: 'to second',
|
||||||
|
3: 'to third',
|
||||||
|
4: 'home'
|
||||||
|
}
|
||||||
|
AT_BASE = {
|
||||||
|
2: 'at second',
|
||||||
|
3: 'at third',
|
||||||
|
4: 'at home'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_obc(on_first = None, on_second = None, on_third = None) -> int:
|
def get_obc(on_first = None, on_second = None, on_third = None) -> int:
|
||||||
@ -112,7 +122,7 @@ def complete_play(session:Session, this_play: Play):
|
|||||||
switch_sides = True
|
switch_sides = True
|
||||||
obc = 0
|
obc = 0
|
||||||
nso = 0
|
nso = 0
|
||||||
nih = 'bot' if this_play.inning_half.lower() == 'top' else 'top'
|
nih = 'bot' if this_play.inning_half == 'top' else 'top'
|
||||||
away_score = this_play.away_score
|
away_score = this_play.away_score
|
||||||
home_score = this_play.home_score
|
home_score = this_play.home_score
|
||||||
|
|
||||||
@ -652,16 +662,6 @@ async def check_uncapped_advance(session: Session, interaction: discord.Interact
|
|||||||
outfielder = await show_outfield_cards(session, interaction, this_play)
|
outfielder = await show_outfield_cards(session, interaction, this_play)
|
||||||
logger.info(f'throw from {outfielder.player.name_with_desc}')
|
logger.info(f'throw from {outfielder.player.name_with_desc}')
|
||||||
def_team = this_play.pitcher.team
|
def_team = this_play.pitcher.team
|
||||||
TO_BASE = {
|
|
||||||
2: 'to second',
|
|
||||||
3: 'to third',
|
|
||||||
4: 'home'
|
|
||||||
}
|
|
||||||
AT_BASE = {
|
|
||||||
2: 'at second',
|
|
||||||
3: 'at third',
|
|
||||||
4: 'at home'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Either there is no AI team or the AI is pitching
|
# Either there is no AI team or the AI is pitching
|
||||||
if not this_game.ai_team or not this_play.ai_is_batting:
|
if not this_game.ai_team or not this_play.ai_is_batting:
|
||||||
@ -1072,11 +1072,37 @@ async def hit_by_pitch(session: Session, interaction: discord.Interaction, this_
|
|||||||
async def bunts(session: Session, interaction: discord.Interaction, this_play: Play, bunt_type: Literal['sacrifice', 'bad', 'popout', 'double-play', 'defense']):
|
async def bunts(session: Session, interaction: discord.Interaction, this_play: Play, bunt_type: Literal['sacrifice', 'bad', 'popout', 'double-play', 'defense']):
|
||||||
this_play.ab = 1 if bunt_type != 'sacrifice' else 0
|
this_play.ab = 1 if bunt_type != 'sacrifice' else 0
|
||||||
this_play.sac = 1 if bunt_type != 'sacrifice' else 0
|
this_play.sac = 1 if bunt_type != 'sacrifice' else 0
|
||||||
|
this_play.outs = 1
|
||||||
|
|
||||||
if bunt_type == 'sacrifice':
|
if bunt_type == 'sacrifice':
|
||||||
this_play = advance_runners(session, this_play, num_bases=1)
|
this_play = advance_runners(session, this_play, num_bases=1)
|
||||||
elif bunt_type == 'popout':
|
elif bunt_type == 'popout':
|
||||||
this_play = advance_runners(session, this_play, num_bases=0)
|
this_play = advance_runners(session, this_play, num_bases=0)
|
||||||
|
elif bunt_type == 'bad':
|
||||||
|
this_play = advance_runners(session, this_play, num_bases=1)
|
||||||
|
this_play.batter_final = 1
|
||||||
|
|
||||||
|
if this_play.on_third is not None:
|
||||||
|
this_play.on_third_final = None
|
||||||
|
|
||||||
|
elif this_play.on_second is not None:
|
||||||
|
this_play.on_second_final = None
|
||||||
|
|
||||||
|
elif this_play.on_first is not None:
|
||||||
|
this_play.on_first_final = None
|
||||||
|
elif bunt_type == 'double-play':
|
||||||
|
this_play = advance_runners(session, this_play, num_bases=0)
|
||||||
|
this_play.outs = 2 if this_play.starting_outs < 2 else 1
|
||||||
|
|
||||||
|
if this_play.on_third is not None:
|
||||||
|
this_play.on_third_final = None
|
||||||
|
|
||||||
|
elif this_play.on_second is not None:
|
||||||
|
this_play.on_second_final = None
|
||||||
|
|
||||||
|
elif this_play.on_first is not None:
|
||||||
|
this_play.on_first_final = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
log_exception(KeyError, f'Bunt type {bunt_type} is not yet implemented')
|
log_exception(KeyError, f'Bunt type {bunt_type} is not yet implemented')
|
||||||
|
|
||||||
@ -1255,6 +1281,36 @@ async def get_game_summary_embed(session: Session, interaction: discord.Interact
|
|||||||
inline=False
|
inline=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.info(f'getting pooper string')
|
||||||
|
poop_string = ''
|
||||||
|
if 'pooper' in game_summary and game_summary['pooper'] is not None:
|
||||||
|
if isinstance(game_summary['pooper'], dict):
|
||||||
|
all_poop = [game_summary['pooper']]
|
||||||
|
elif isinstance(game_summary['pooper'], list):
|
||||||
|
all_poop = game_summary['pooper']
|
||||||
|
|
||||||
|
for line in all_poop:
|
||||||
|
poop_line = f'{player_name} - '
|
||||||
|
player_name = f'{get_player_name_from_dict(tp['player'])}'
|
||||||
|
|
||||||
|
if 'hr' in line:
|
||||||
|
poop_line += f'{line["hit"]}-{line["ab"]}'
|
||||||
|
else:
|
||||||
|
poop_line += f'{line["ip"]} IP, {line["run"]} R'
|
||||||
|
if tp['run'] != line['e_run']:
|
||||||
|
poop_line += f' ({line["e_run"]} ER)'
|
||||||
|
poop_line += f', {line["hit"]} H, {line["so"]} K'
|
||||||
|
poop_line += f', {tp["re24"]:.2f} re24\n'
|
||||||
|
poop_string += poop_line
|
||||||
|
|
||||||
|
if len(poop_string) > 0:
|
||||||
|
game_embed.add_field(
|
||||||
|
'Pooper of the Game',
|
||||||
|
value=poop_string,
|
||||||
|
inline=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
pit_string = f'Win: {game_summary["pitchers"]["win"]["p_name"]}\nLoss: {game_summary["pitchers"]["loss"]["p_name"]}\n'
|
pit_string = f'Win: {game_summary["pitchers"]["win"]["p_name"]}\nLoss: {game_summary["pitchers"]["loss"]["p_name"]}\n'
|
||||||
|
|
||||||
hold_string = None
|
hold_string = None
|
||||||
|
|||||||
@ -49,3 +49,6 @@ class PlayInitException(GameException):
|
|||||||
|
|
||||||
class DatabaseError(GameException):
|
class DatabaseError(GameException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class PositionNotFoundException(GameException):
|
||||||
|
pass
|
||||||
|
|||||||
@ -12,8 +12,7 @@ from peewee import *
|
|||||||
from typing import Optional, Literal
|
from typing import Optional, Literal
|
||||||
|
|
||||||
from in_game import data_cache
|
from in_game import data_cache
|
||||||
import in_game.gameplay_models as iggm
|
from in_game.gameplay_models import Play, Session, Game, Team, Lineup
|
||||||
from in_game.gameplay_models import Play, Session, Game, Team
|
|
||||||
from in_game.gameplay_queries import get_or_create_ai_card, get_player_id_from_dict, get_player_or_none
|
from in_game.gameplay_queries import get_or_create_ai_card, get_player_id_from_dict, get_player_or_none
|
||||||
|
|
||||||
db = SqliteDatabase(
|
db = SqliteDatabase(
|
||||||
@ -42,28 +41,28 @@ class BaseModel(Model):
|
|||||||
database = db
|
database = db
|
||||||
|
|
||||||
|
|
||||||
class Lineup(BaseModel):
|
# class Lineup(BaseModel):
|
||||||
team_id = IntegerField()
|
# team_id = IntegerField()
|
||||||
game_level = CharField() # 'minor', 'major', 'hof'
|
# game_level = CharField() # 'minor', 'major', 'hof'
|
||||||
vs_hand = CharField() # 'left', 'right'
|
# vs_hand = CharField() # 'left', 'right'
|
||||||
bat_one_card = IntegerField()
|
# bat_one_card = IntegerField()
|
||||||
bat_one_pos = CharField()
|
# bat_one_pos = CharField()
|
||||||
bat_two_card = IntegerField()
|
# bat_two_card = IntegerField()
|
||||||
bat_two_pos = CharField()
|
# bat_two_pos = CharField()
|
||||||
bat_three_card = IntegerField()
|
# bat_three_card = IntegerField()
|
||||||
bat_three_pos = CharField()
|
# bat_three_pos = CharField()
|
||||||
bat_four_card = IntegerField()
|
# bat_four_card = IntegerField()
|
||||||
bat_four_pos = CharField()
|
# bat_four_pos = CharField()
|
||||||
bat_five_card = IntegerField()
|
# bat_five_card = IntegerField()
|
||||||
bat_five_pos = CharField()
|
# bat_five_pos = CharField()
|
||||||
bat_six_card = IntegerField()
|
# bat_six_card = IntegerField()
|
||||||
bat_six_pos = CharField()
|
# bat_six_pos = CharField()
|
||||||
bat_seven_card = IntegerField()
|
# bat_seven_card = IntegerField()
|
||||||
bat_seven_pos = CharField()
|
# bat_seven_pos = CharField()
|
||||||
bat_eight_card = IntegerField()
|
# bat_eight_card = IntegerField()
|
||||||
bat_eight_pos = CharField()
|
# bat_eight_pos = CharField()
|
||||||
bat_nine_card = IntegerField()
|
# bat_nine_card = IntegerField()
|
||||||
bat_nine_pos = CharField()
|
# bat_nine_pos = CharField()
|
||||||
|
|
||||||
|
|
||||||
class Rotation(BaseModel):
|
class Rotation(BaseModel):
|
||||||
@ -300,7 +299,7 @@ async def get_starting_lineup(session: Session, team: Team, game: Game, league_n
|
|||||||
this_player = await get_player_or_none(session, get_player_id_from_dict(y[1]['player']))
|
this_player = await get_player_or_none(session, get_player_id_from_dict(y[1]['player']))
|
||||||
this_card = await get_or_create_ai_card(session, player=this_player, team=team)
|
this_card = await get_or_create_ai_card(session, player=this_player, team=team)
|
||||||
|
|
||||||
lineups.append(iggm.Lineup(
|
lineups.append(Lineup(
|
||||||
position=y[0],
|
position=y[0],
|
||||||
batting_order=i,
|
batting_order=i,
|
||||||
game=game,
|
game=game,
|
||||||
@ -316,7 +315,7 @@ async def get_starting_lineup(session: Session, team: Team, game: Game, league_n
|
|||||||
|
|
||||||
|
|
||||||
async def get_starting_pitcher(
|
async def get_starting_pitcher(
|
||||||
session: Session, this_team: Team, this_game: Game, is_home: bool, league_name: str) -> iggm.Lineup:
|
session: Session, this_team: Team, this_game: Game, is_home: bool, league_name: str) -> Lineup:
|
||||||
d_100 = random.randint(1, 100)
|
d_100 = random.randint(1, 100)
|
||||||
if is_home:
|
if is_home:
|
||||||
if d_100 <= 30:
|
if d_100 <= 30:
|
||||||
@ -347,7 +346,7 @@ async def get_starting_pitcher(
|
|||||||
this_player = await get_player_or_none(session, get_player_id_from_dict(sp_query))
|
this_player = await get_player_or_none(session, get_player_id_from_dict(sp_query))
|
||||||
sp_card = await get_or_create_ai_card(session, this_player, this_team)
|
sp_card = await get_or_create_ai_card(session, this_player, this_team)
|
||||||
|
|
||||||
return iggm.Lineup(
|
return Lineup(
|
||||||
team=this_team,
|
team=this_team,
|
||||||
player=this_player,
|
player=this_player,
|
||||||
card=sp_card,
|
card=sp_card,
|
||||||
|
|||||||
@ -5,12 +5,12 @@ from typing import Literal
|
|||||||
import discord
|
import discord
|
||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
from sqlmodel import Session, SQLModel, create_engine, select, or_, Field, Relationship
|
from pydantic import field_validator
|
||||||
|
from sqlmodel import Session, SQLModel, UniqueConstraint, create_engine, select, or_, Field, Relationship, text
|
||||||
from sqlalchemy import func, desc
|
from sqlalchemy import func, desc
|
||||||
|
|
||||||
from api_calls import db_get, db_post
|
|
||||||
from exceptions import *
|
from exceptions import *
|
||||||
from in_game.managerai_responses import JumpResponse, TagResponse, ThrowResponse, UncappedRunResponse
|
from in_game.managerai_responses import DefenseResponse, JumpResponse, TagResponse, ThrowResponse, UncappedRunResponse
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('discord_app')
|
logger = logging.getLogger('discord_app')
|
||||||
@ -65,7 +65,7 @@ class TeamBase(SQLModel):
|
|||||||
ranking: int
|
ranking: int
|
||||||
has_guide: bool
|
has_guide: bool
|
||||||
is_ai: bool
|
is_ai: bool
|
||||||
created: datetime.datetime | None = Field(default=datetime.datetime.now())
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self) -> str:
|
def description(self) -> str:
|
||||||
@ -129,6 +129,10 @@ class Game(SQLModel, table=True):
|
|||||||
lineups: list['Lineup'] = Relationship(back_populates='game', cascade_delete=True)
|
lineups: list['Lineup'] = Relationship(back_populates='game', cascade_delete=True)
|
||||||
plays: list['Play'] = Relationship(back_populates='game', cascade_delete=True)
|
plays: list['Play'] = Relationship(back_populates='game', cascade_delete=True)
|
||||||
|
|
||||||
|
@field_validator('ai_team', 'game_type')
|
||||||
|
def lowercase_strings(cls, value: str) -> str:
|
||||||
|
return value.lower()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cardset_param_string(self) -> str:
|
def cardset_param_string(self) -> str:
|
||||||
pri_cardsets = ''
|
pri_cardsets = ''
|
||||||
@ -363,7 +367,7 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
this_resp = JumpResponse()
|
this_resp = JumpResponse()
|
||||||
this_play = this_game.current_play_or_none(session)
|
this_play = this_game.current_play_or_none(session)
|
||||||
if this_play is None:
|
if this_play is None:
|
||||||
raise KeyError(f'No game found while checking for jump')
|
raise GameException(f'No game found while checking for jump')
|
||||||
|
|
||||||
num_outs = this_play.starting_outs
|
num_outs = this_play.starting_outs
|
||||||
run_diff = this_play.away_score - this_play.home_score
|
run_diff = this_play.away_score - this_play.home_score
|
||||||
@ -418,7 +422,7 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
this_resp = TagResponse()
|
this_resp = TagResponse()
|
||||||
this_play = this_game.current_play_or_none(session)
|
this_play = this_game.current_play_or_none(session)
|
||||||
if this_play is None:
|
if this_play is None:
|
||||||
raise KeyError(f'No game found while checking tag_from_second')
|
raise GameException(f'No game found while checking tag_from_second')
|
||||||
|
|
||||||
ai_rd = this_play.ai_run_diff()
|
ai_rd = this_play.ai_run_diff()
|
||||||
aggression_mod = abs(self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5)
|
aggression_mod = abs(self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5)
|
||||||
@ -442,7 +446,7 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
this_resp = ThrowResponse()
|
this_resp = ThrowResponse()
|
||||||
this_play = this_game.current_play_or_none(session)
|
this_play = this_game.current_play_or_none(session)
|
||||||
if this_play is None:
|
if this_play is None:
|
||||||
raise KeyError(f'No game found while checking throw_at_uncapped')
|
raise GameException(f'No game found while checking throw_at_uncapped')
|
||||||
|
|
||||||
ai_rd = this_play.ai_run_diff()
|
ai_rd = this_play.ai_run_diff()
|
||||||
aggression = self.ahead_aggression if ai_rd > 0 else self.behind_aggression
|
aggression = self.ahead_aggression if ai_rd > 0 else self.behind_aggression
|
||||||
@ -481,7 +485,7 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
this_resp = UncappedRunResponse()
|
this_resp = UncappedRunResponse()
|
||||||
this_play = this_game.current_play_or_none(session)
|
this_play = this_game.current_play_or_none(session)
|
||||||
if this_play is None:
|
if this_play is None:
|
||||||
raise KeyError(f'No game found while checking uncapped_advance_lead')
|
raise GameException(f'No game found while checking uncapped_advance_lead')
|
||||||
|
|
||||||
ai_rd = this_play.ai_run_diff()
|
ai_rd = this_play.ai_run_diff()
|
||||||
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
||||||
@ -525,6 +529,47 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
|
|
||||||
return this_resp
|
return this_resp
|
||||||
|
|
||||||
|
def defense_alignment(self, session: Session, this_game: Game) -> DefenseResponse:
|
||||||
|
this_resp = DefenseResponse()
|
||||||
|
this_play = this_game.current_play_or_none(session)
|
||||||
|
if this_play is None:
|
||||||
|
raise GameException(f'No game found while checking uncapped_advance_lead')
|
||||||
|
|
||||||
|
ai_rd = this_play.ai_run_diff()
|
||||||
|
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
||||||
|
|
||||||
|
if self.starting_outs == 2 and self.on_base_code > 0:
|
||||||
|
if self.on_base_code == 1:
|
||||||
|
this_resp.hold_first = True
|
||||||
|
elif self.on_base_code == 2:
|
||||||
|
this_resp.hold_second = True
|
||||||
|
elif self.on_base_code in [4, 5, 7]:
|
||||||
|
this_resp.hold_first = True
|
||||||
|
this_resp.hold_second = True
|
||||||
|
# elif self.on_base_code == 5:
|
||||||
|
# ai_note += f'- hold the runner on first\n'
|
||||||
|
elif self.on_base_code == 6:
|
||||||
|
ai_note += f'- hold {self.on_second.player.name} on 2nd\n'
|
||||||
|
elif self.on_base_code in [1, 5]:
|
||||||
|
runner = self.on_first.player
|
||||||
|
if self.on_first.card.batterscouting.battingcard.steal_auto:
|
||||||
|
ai_note += f'- hold {runner.name} on 1st\n'
|
||||||
|
elif self.on_base_code in [2, 4]:
|
||||||
|
if self.on_second.card.batterscouting.battingcard.steal_low + max(self.pitcher.card.pitcherscouting.pitchingcard.hold, 5) >= 14:
|
||||||
|
ai_note += f'- hold {self.on_second.player.name} on 2nd\n'
|
||||||
|
|
||||||
|
# Defensive Alignment
|
||||||
|
if self.on_third and self.starting_outs < 2:
|
||||||
|
if self.could_walkoff:
|
||||||
|
ai_note += f'- play the outfield and infield in'
|
||||||
|
elif abs(self.away_score - self.home_score) <= 3:
|
||||||
|
ai_note += f'- play the whole infield in\n'
|
||||||
|
else:
|
||||||
|
ai_note += f'- play the corners in\n'
|
||||||
|
|
||||||
|
if len(ai_note) == 0 and self.on_base_code > 0:
|
||||||
|
ai_note += f'- play straight up\n'
|
||||||
|
|
||||||
|
|
||||||
class CardsetBase(SQLModel):
|
class CardsetBase(SQLModel):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
@ -564,7 +609,14 @@ class PlayerBase(SQLModel):
|
|||||||
bbref_id: str | None = Field(default=None)
|
bbref_id: str | None = Field(default=None)
|
||||||
fangr_id: str | None = Field(default=None)
|
fangr_id: str | None = Field(default=None)
|
||||||
mlbplayer_id: int | None = Field(default=None)
|
mlbplayer_id: int | None = Field(default=None)
|
||||||
created: datetime.datetime | None = Field(default=datetime.datetime.now())
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
|
@field_validator('pos_1', 'pos_2', 'pos_3', 'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8')
|
||||||
|
def uppercase_strings(cls, value: str) -> str:
|
||||||
|
if value is not None:
|
||||||
|
return value.upper()
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def p_card_url(self):
|
def p_card_url(self):
|
||||||
@ -597,6 +649,7 @@ class Player(PlayerBase, table=True):
|
|||||||
cardset: Cardset = Relationship(back_populates='players')
|
cardset: Cardset = Relationship(back_populates='players')
|
||||||
cards: list['Card'] = Relationship(back_populates='player', cascade_delete=True)
|
cards: list['Card'] = Relationship(back_populates='player', cascade_delete=True)
|
||||||
lineups: list['Lineup'] = Relationship(back_populates='player', cascade_delete=True)
|
lineups: list['Lineup'] = Relationship(back_populates='player', cascade_delete=True)
|
||||||
|
positions: list['PositionRating'] = Relationship(back_populates='player', cascade_delete=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name_with_desc(self):
|
def name_with_desc(self):
|
||||||
@ -620,18 +673,202 @@ def player_description(player: Player = None, player_dict: dict = None) -> str:
|
|||||||
return r_val
|
return r_val
|
||||||
|
|
||||||
|
|
||||||
|
class BattingCardBase(SQLModel):
|
||||||
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
variant: int | None = Field(default=0)
|
||||||
|
steal_low: int = Field(default=0, ge=0, le=20)
|
||||||
|
steal_high: int = Field(default=0, ge=0, le=20)
|
||||||
|
steal_auto: bool = Field(default=False)
|
||||||
|
steal_jump: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||||
|
bunting: str = Field(default='C')
|
||||||
|
hit_and_run: str = Field(default='C')
|
||||||
|
running: int = Field(default=10, ge=1, le=20)
|
||||||
|
offense_col: int = Field(ge=1, le=3)
|
||||||
|
hand: str
|
||||||
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
# created: datetime.datetime | None = Field(sa_column_kwargs={"server_default": text("CURRENT_TIMESTAMP"),})
|
||||||
|
|
||||||
|
@field_validator('hand')
|
||||||
|
def lowercase_hand(cls, value: str) -> str:
|
||||||
|
return value.lower()
|
||||||
|
|
||||||
|
|
||||||
|
class BattingCard(BattingCardBase, table=True):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BattingRatingsBase(SQLModel):
|
||||||
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
homerun: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
bp_homerun: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
triple: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
double_three: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
double_two: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
double_pull: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
single_two: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
single_one: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
single_center: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
bp_single: float = Field(default=0.0, ge=0.0, le=10.0)
|
||||||
|
hbp: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
walk: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
strikeout: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
lineout: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
popout: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
flyout_a: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
flyout_bq: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
flyout_lf_b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
flyout_rf_b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
groundout_a: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
groundout_b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
groundout_c: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
avg: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||||
|
obp: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||||
|
slg: float = Field(default=0.0, ge=0.0, le=4.0)
|
||||||
|
pull_rate: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||||
|
center_rate: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||||
|
slap_rate: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||||
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class BattingRatings(BattingRatingsBase, table=True):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BatterScoutingBase(SQLModel):
|
||||||
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
battingcard_id: int | None = Field(default=None, foreign_key='battingcard.id')
|
||||||
|
ratings_vl_id: int | None = Field(default=None, foreign_key='battingratings.id')
|
||||||
|
ratings_vr_id: int | None = Field(default=None, foreign_key='battingratings.id')
|
||||||
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class BatterScouting(BatterScoutingBase, table=True):
|
||||||
|
battingcard: BattingCard = Relationship() #back_populates='batterscouting')
|
||||||
|
ratings_vl: BattingRatings = Relationship(
|
||||||
|
sa_relationship_kwargs=dict(foreign_keys="[BatterScouting.ratings_vl_id]")
|
||||||
|
)
|
||||||
|
ratings_vr: BattingRatings = Relationship(
|
||||||
|
sa_relationship_kwargs=dict(foreign_keys="[BatterScouting.ratings_vr_id]")
|
||||||
|
)
|
||||||
|
cards: list['Card'] = Relationship(back_populates='batterscouting', cascade_delete=True)
|
||||||
|
|
||||||
|
|
||||||
|
class PitchingCardBase(SQLModel):
|
||||||
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
variant: int | None = Field(default=0)
|
||||||
|
balk: int = Field(default=0, ge=0, le=20)
|
||||||
|
wild_pitch: int = Field(default=0, ge=0, le=20)
|
||||||
|
hold: int = Field(default=0, ge=-9, le=9)
|
||||||
|
starter_rating: int = Field(default=1, ge=1, le=10)
|
||||||
|
relief_rating: int = Field(default=1, ge=1, le=10)
|
||||||
|
closer_rating: int | None = Field(default=None, ge=0, le=9)
|
||||||
|
offense_col: int = Field(ge=1, le=3)
|
||||||
|
batting: str = Field(default='#1WR-C')
|
||||||
|
hand: str
|
||||||
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
|
@field_validator('hand')
|
||||||
|
def lowercase_hand(cls, value: str) -> str:
|
||||||
|
return value.lower()
|
||||||
|
|
||||||
|
|
||||||
|
class PitchingCard(PitchingCardBase, table=True):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PitchingRatingsBase(SQLModel):
|
||||||
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
homerun: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
bp_homerun: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
triple: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
double_three: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
double_two: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
double_cf: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
single_two: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
single_one: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
single_center: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
bp_single: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
hbp: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
walk: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
strikeout: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
flyout_lf_b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
flyout_cf_b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
flyout_rf_b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
groundout_a: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
groundout_b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_p: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_c: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_1b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_2b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_3b: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_ss: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_lf: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_cf: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
xcheck_rf: float = Field(default=0.0, ge=0.0, le=108.0)
|
||||||
|
avg: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||||
|
obp: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||||
|
slg: float = Field(default=0.0, ge=0.0, le=4.0)
|
||||||
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class PitchingRatings(PitchingRatingsBase, table=True):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PitcherScoutingBase(SQLModel):
|
||||||
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
pitchingcard_id: int | None = Field(default=None, foreign_key='pitchingcard.id',)
|
||||||
|
ratings_vl_id: int | None = Field(default=None, foreign_key='pitchingratings.id')
|
||||||
|
ratings_vr_id: int | None = Field(default=None, foreign_key='pitchingratings.id')
|
||||||
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class PitcherScouting(PitcherScoutingBase, table=True):
|
||||||
|
pitchingcard: PitchingCard = Relationship()
|
||||||
|
ratings_vl: PitchingRatings = Relationship(
|
||||||
|
sa_relationship_kwargs=dict(foreign_keys="[PitcherScouting.ratings_vl_id]")
|
||||||
|
)
|
||||||
|
ratings_vr: PitchingRatings = Relationship(
|
||||||
|
sa_relationship_kwargs=dict(foreign_keys="[PitcherScouting.ratings_vr_id]")
|
||||||
|
)
|
||||||
|
cards: list['Card'] = Relationship(back_populates='pitcherscouting', cascade_delete=True)
|
||||||
|
|
||||||
|
|
||||||
class CardBase(SQLModel):
|
class CardBase(SQLModel):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
player_id: int = Field(foreign_key='player.id', index=True, ondelete='CASCADE')
|
player_id: int = Field(foreign_key='player.id', index=True, ondelete='CASCADE')
|
||||||
team_id: int = Field(foreign_key='team.id', index=True, ondelete='CASCADE')
|
team_id: int = Field(foreign_key='team.id', index=True, ondelete='CASCADE')
|
||||||
|
batterscouting_id: int | None = Field(default=None, foreign_key='batterscouting.id', ondelete='CASCADE')
|
||||||
|
pitcherscouting_id: int | None = Field(default=None, foreign_key='pitcherscouting.id', ondelete='CASCADE')
|
||||||
variant: int | None = Field(default=0)
|
variant: int | None = Field(default=0)
|
||||||
created: datetime.datetime | None = Field(default=datetime.datetime.now())
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
class Card(CardBase, table=True):
|
class Card(CardBase, table=True):
|
||||||
player: Player = Relationship(back_populates='cards')
|
player: Player = Relationship(back_populates='cards')
|
||||||
team: Team = Relationship(back_populates='cards',)
|
team: Team = Relationship(back_populates='cards')
|
||||||
lineups: list['Lineup'] = Relationship(back_populates='card', cascade_delete=True)
|
lineups: list['Lineup'] = Relationship(back_populates='card', cascade_delete=True)
|
||||||
|
batterscouting: BatterScouting = Relationship(back_populates='cards')
|
||||||
|
pitcherscouting: PitcherScouting = Relationship(back_populates='cards')
|
||||||
|
|
||||||
|
|
||||||
|
class PositionRatingBase(SQLModel):
|
||||||
|
__table_args__ = (UniqueConstraint("player_id", "variant", "position"),)
|
||||||
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
player_id: int = Field(foreign_key='player.id', index=True, ondelete='CASCADE')
|
||||||
|
variant: int = Field(default=0, index=True)
|
||||||
|
position: str = Field(index=True, include=['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF'])
|
||||||
|
innings: int = Field(default=0)
|
||||||
|
range: int = Field(default=5)
|
||||||
|
error: int = Field(default=0)
|
||||||
|
arm: int | None = Field(default=None)
|
||||||
|
pb: int | None = Field(default=None)
|
||||||
|
overthrow: int | None = Field(default=None)
|
||||||
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class PositionRating(PositionRatingBase, table=True):
|
||||||
|
player: Player = Relationship(back_populates='positions')
|
||||||
|
|
||||||
|
|
||||||
class Lineup(SQLModel, table=True):
|
class Lineup(SQLModel, table=True):
|
||||||
@ -655,6 +892,10 @@ class Lineup(SQLModel, table=True):
|
|||||||
card_id: int = Field(foreign_key='card.id', index=True, ondelete='CASCADE')
|
card_id: int = Field(foreign_key='card.id', index=True, ondelete='CASCADE')
|
||||||
card: Card = Relationship(back_populates='lineups')
|
card: Card = Relationship(back_populates='lineups')
|
||||||
|
|
||||||
|
@field_validator('position')
|
||||||
|
def uppercase_strings(cls, value: str) -> str:
|
||||||
|
return value.upper()
|
||||||
|
|
||||||
# TODO: add function to return string value of game stats
|
# TODO: add function to return string value of game stats
|
||||||
|
|
||||||
|
|
||||||
@ -725,6 +966,14 @@ class PlayBase(SQLModel):
|
|||||||
is_new_inning: bool = Field(default=False)
|
is_new_inning: bool = Field(default=False)
|
||||||
managerai_id: int | None = Field(default=None, foreign_key='managerai.id')
|
managerai_id: int | None = Field(default=None, foreign_key='managerai.id')
|
||||||
|
|
||||||
|
@field_validator('inning_half')
|
||||||
|
def lowercase_strings(cls, value: str) -> str:
|
||||||
|
return value.lower()
|
||||||
|
|
||||||
|
@field_validator('check_pos', 'batter_pos')
|
||||||
|
def uppercase_strings(cls, value: str) -> str:
|
||||||
|
return value.upper()
|
||||||
|
|
||||||
def ai_run_diff(self):
|
def ai_run_diff(self):
|
||||||
if self.game.ai_team == 'away':
|
if self.game.ai_team == 'away':
|
||||||
return self.away_score - self.home_score
|
return self.away_score - self.home_score
|
||||||
@ -798,26 +1047,36 @@ class Play(PlayBase, table=True):
|
|||||||
ai_note = ''
|
ai_note = ''
|
||||||
# Holding Baserunners
|
# Holding Baserunners
|
||||||
if self.starting_outs == 2 and self.on_base_code > 0:
|
if self.starting_outs == 2 and self.on_base_code > 0:
|
||||||
if self.on_base_code in [1, 2]:
|
if self.on_base_code == 1:
|
||||||
ai_note += f'- hold the runner\n'
|
ai_note += f'- hold {self.on_first.player.name}\n'
|
||||||
elif self.on_base_code in [4, 7]:
|
elif self.on_base_code == 2:
|
||||||
ai_note += f'- hold the runners\n'
|
ai_note += f'- hold {self.on_second.player.name}\n'
|
||||||
elif self.on_base_code == 5:
|
elif self.on_base_code in [4, 5, 7]:
|
||||||
ai_note += f'- hold the runner on first\n'
|
ai_note += f'- hold {self.on_first.player.name} on first\n'
|
||||||
|
# elif self.on_base_code == 5:
|
||||||
|
# ai_note += f'- hold the runner on first\n'
|
||||||
elif self.on_base_code == 6:
|
elif self.on_base_code == 6:
|
||||||
ai_note += f'- hold the runner on second\n'
|
ai_note += f'- hold {self.on_second.player.name} on 2nd\n'
|
||||||
elif self.on_base_code in [1, 5]:
|
elif self.on_base_code in [1, 5]:
|
||||||
ai_note += f'- hold the runner on 1st if they have ***** auto-jump\n'
|
runner = self.on_first.player
|
||||||
|
if self.on_first.card.batterscouting.battingcard.steal_auto:
|
||||||
|
ai_note += f'- hold {runner.name} on 1st\n'
|
||||||
elif self.on_base_code in [2, 4]:
|
elif self.on_base_code in [2, 4]:
|
||||||
ai_note += f'- hold the runner on 2nd if safe range is 14+\n'
|
if self.on_second.card.batterscouting.battingcard.steal_low + max(self.pitcher.card.pitcherscouting.pitchingcard.hold, 5) >= 14:
|
||||||
|
ai_note += f'- hold {self.on_second.player.name} on 2nd\n'
|
||||||
|
|
||||||
# Defensive Alignment
|
# Defensive Alignment
|
||||||
if self.on_third and self.starting_outs < 2:
|
if self.on_third and self.starting_outs < 2:
|
||||||
if abs(self.away_score - self.home_score) <= 3:
|
if self.could_walkoff:
|
||||||
|
ai_note += f'- play the outfield and infield in'
|
||||||
|
elif abs(self.away_score - self.home_score) <= 3:
|
||||||
ai_note += f'- play the whole infield in\n'
|
ai_note += f'- play the whole infield in\n'
|
||||||
else:
|
else:
|
||||||
ai_note += f'- play the corners in\n'
|
ai_note += f'- play the corners in\n'
|
||||||
|
|
||||||
|
if len(ai_note) == 0 and self.on_base_code > 0:
|
||||||
|
ai_note += f'- play straight up\n'
|
||||||
|
|
||||||
return ai_note
|
return ai_note
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -844,11 +1103,16 @@ class Play(PlayBase, table=True):
|
|||||||
if self.game.ai_team is None:
|
if self.game.ai_team is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if (self.game.ai_team.lower() == 'away' and self.inning_half.lower() == 'top') or (self.game.ai_team.lower() == 'home' and self.inning_half.lower() == 'bot'):
|
if (self.game.ai_team == 'away' and self.inning_half == 'top') or (self.game.ai_team == 'home' and self.inning_half == 'bot'):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def could_walkoff(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
BEGIN DEVELOPMENT HELPERS
|
BEGIN DEVELOPMENT HELPERS
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
import pydantic
|
import pydantic
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from api_calls import db_get, db_post
|
from api_calls import db_get, db_post
|
||||||
from in_game.gameplay_models import CACHE_LIMIT, Card, CardBase, Lineup, Player, PlayerBase, Session, Team, TeamBase, select, or_, Game, Play
|
from in_game.gameplay_models import CACHE_LIMIT, BatterScouting, BatterScoutingBase, BattingCard, BattingCardBase, BattingRatings, BattingRatingsBase, Card, CardBase, Lineup, PitcherScouting, PitchingCard, PitchingCardBase, PitchingRatings, PitchingRatingsBase, Player, PlayerBase, PositionRating, PositionRatingBase, Session, Team, TeamBase, select, or_, Game, Play
|
||||||
from exceptions import DatabaseError, log_exception, PlayNotFoundException
|
from exceptions import DatabaseError, PositionNotFoundException, log_exception, PlayNotFoundException
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('discord_app')
|
logger = logging.getLogger('discord_app')
|
||||||
@ -147,6 +148,134 @@ async def get_player_or_none(session: Session, player_id: int, skip_cache: bool
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def get_batter_scouting_or_none(session: Session, card: Card, skip_cache: bool = False) -> BatterScouting | None:
|
||||||
|
logger.info(f'Getting batting scouting for card ID #{card.id}: {card.player.name_with_desc}')
|
||||||
|
if not skip_cache and card.batterscouting is not None:
|
||||||
|
this_scouting = session.get(BatterScouting, card.batterscouting.id)
|
||||||
|
|
||||||
|
if this_scouting is not None:
|
||||||
|
logger.info(f'we found a cached scouting: {this_scouting} / created {this_scouting.created}')
|
||||||
|
tdelta = datetime.datetime.now() - this_scouting.created
|
||||||
|
logger.debug(f'tdelta: {tdelta}')
|
||||||
|
if tdelta.total_seconds() < CACHE_LIMIT:
|
||||||
|
return this_scouting
|
||||||
|
else:
|
||||||
|
session.delete(this_scouting)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
def cache_scouting(batting_card: dict, ratings_vr: dict, ratings_vl: dict) -> BatterScouting:
|
||||||
|
valid_bc = BattingCardBase.model_validate(batting_card, from_attributes=True)
|
||||||
|
db_bc = BattingCard.model_validate(valid_bc)
|
||||||
|
|
||||||
|
valid_vl = BattingRatingsBase.model_validate(ratings_vl, from_attributes=True)
|
||||||
|
db_vl = BattingRatings.model_validate(valid_vl)
|
||||||
|
|
||||||
|
valid_vr = BattingRatingsBase.model_validate(ratings_vr, from_attributes=True)
|
||||||
|
db_vr = BattingRatings.model_validate(valid_vr)
|
||||||
|
|
||||||
|
db_scouting = BatterScouting(
|
||||||
|
battingcard=db_bc,
|
||||||
|
ratings_vl=db_vl,
|
||||||
|
ratings_vr=db_vr
|
||||||
|
)
|
||||||
|
|
||||||
|
session.add(db_scouting)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(db_scouting)
|
||||||
|
return db_scouting
|
||||||
|
|
||||||
|
s_query = await db_get(f'battingcardratings/player/{card.player.id}', none_okay=False)
|
||||||
|
if s_query['count'] == 2:
|
||||||
|
return cache_scouting(
|
||||||
|
batting_card=s_query['ratings'][0]['battingcard'],
|
||||||
|
ratings_vr=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'R' else s_query['ratings'][1],
|
||||||
|
ratings_vl=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'L' else s_query['ratings'][1]
|
||||||
|
)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def get_pitcher_scouting_or_none(session: Session, card: Card, skip_cache: bool = False) -> PitcherScouting | None:
|
||||||
|
logger.info(f'Getting pitching scouting for card ID #{card.id}: {card.player.name_with_desc}')
|
||||||
|
if not skip_cache and card.pitcherscouting is not None:
|
||||||
|
this_scouting = session.get(PitcherScouting, card.pitcherscouting.id)
|
||||||
|
|
||||||
|
if this_scouting is not None:
|
||||||
|
logger.info(f'we found a cached scouting: {this_scouting} / created {this_scouting.created}')
|
||||||
|
tdelta = datetime.datetime.now() - this_scouting.created
|
||||||
|
logger.debug(f'tdelta: {tdelta}')
|
||||||
|
if tdelta.total_seconds() < CACHE_LIMIT:
|
||||||
|
return this_scouting
|
||||||
|
else:
|
||||||
|
session.delete(this_scouting)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
def cache_scouting(pitching_card: dict, ratings_vr: dict, ratings_vl: dict) -> PitcherScouting:
|
||||||
|
valid_bc = PitchingCardBase.model_validate(pitching_card, from_attributes=True)
|
||||||
|
db_bc = PitchingCard.model_validate(valid_bc)
|
||||||
|
|
||||||
|
valid_vl = PitchingRatingsBase.model_validate(ratings_vl, from_attributes=True)
|
||||||
|
db_vl = PitchingRatings.model_validate(valid_vl)
|
||||||
|
|
||||||
|
valid_vr = PitchingRatingsBase.model_validate(ratings_vr, from_attributes=True)
|
||||||
|
db_vr = PitchingRatings.model_validate(valid_vr)
|
||||||
|
|
||||||
|
db_scouting = PitcherScouting(
|
||||||
|
pitchingcard=db_bc,
|
||||||
|
ratings_vl=db_vl,
|
||||||
|
ratings_vr=db_vr
|
||||||
|
)
|
||||||
|
|
||||||
|
session.add(db_scouting)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(db_scouting)
|
||||||
|
return db_scouting
|
||||||
|
|
||||||
|
s_query = await db_get(f'pitchingcardratings/player/{card.player.id}', none_okay=False)
|
||||||
|
if s_query['count'] == 2:
|
||||||
|
scouting = cache_scouting(
|
||||||
|
pitching_card=s_query['ratings'][0]['pitchingcard'],
|
||||||
|
ratings_vr=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'R' else s_query['ratings'][1],
|
||||||
|
ratings_vl=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'L' else s_query['ratings'][1]
|
||||||
|
)
|
||||||
|
pos_rating = await get_and_cache_position(session, card, 'P')
|
||||||
|
return scouting
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# async def get_position_rating_or_none(session: Session, card: Card, position: str, skip_cache: bool = False) -> PositionRating | None:
|
||||||
|
# logger.info(f'Getting position rating for card ID')
|
||||||
|
# if not skip_cache:
|
||||||
|
# ratings = session.exec(select(PositionRating).where(PositionRating.player == card.player, PositionRating.variant == card.variant, PositionRating.position == position).limit(1)).all()
|
||||||
|
|
||||||
|
# """Test all of this; rebuild DB"""
|
||||||
|
|
||||||
|
# if len(ratings) > 0:
|
||||||
|
# logger.info(f'we found a cached position: {ratings[0]} / created {ratings[0].created}')
|
||||||
|
# tdelta = datetime.datetime.now() - ratings[0].created
|
||||||
|
# logger.debug(f'tdelta: {tdelta}')
|
||||||
|
# if tdelta.total_seconds() < CACHE_LIMIT:
|
||||||
|
# return ratings[0]
|
||||||
|
# else:
|
||||||
|
# session.delete(ratings[0])
|
||||||
|
# session.commit()
|
||||||
|
|
||||||
|
# def cache_rating(json_data: dict) -> PositionRating:
|
||||||
|
# valid_position = PositionRatingBase.model_validate(json_data, from_attributes=True)
|
||||||
|
# db_position = PositionRating.model_validate(valid_position)
|
||||||
|
# session.add(db_position)
|
||||||
|
# session.commit()
|
||||||
|
# session.refresh(db_position)
|
||||||
|
# return db_position
|
||||||
|
|
||||||
|
# p_query = await db_get('cardpositions', params=[('player_id', card.player.id)])
|
||||||
|
# if p_query['count'] > 0:
|
||||||
|
# return cache_rating(p_query['positions'][0])
|
||||||
|
|
||||||
|
# return None
|
||||||
|
|
||||||
|
|
||||||
def get_player_id_from_dict(json_data: dict) -> int:
|
def get_player_id_from_dict(json_data: dict) -> int:
|
||||||
logger.info(f'Getting player from dict {json_data}')
|
logger.info(f'Getting player from dict {json_data}')
|
||||||
if 'player_id' in json_data:
|
if 'player_id' in json_data:
|
||||||
@ -165,6 +294,60 @@ def get_player_name_from_dict(json_data: dict) -> str:
|
|||||||
log_exception(KeyError, 'Player name could not be extracted from json data')
|
log_exception(KeyError, 'Player name could not be extracted from json data')
|
||||||
|
|
||||||
|
|
||||||
|
async def shared_get_scouting(session: Session, this_card: Card, which: Literal['batter', 'pitcher']):
|
||||||
|
if which == 'batter':
|
||||||
|
logger.info(f'Pulling batter scouting for {this_card.player.name_with_desc}')
|
||||||
|
this_scouting = await get_batter_scouting_or_none(session, this_card)
|
||||||
|
else:
|
||||||
|
logger.info(f'Pulling pitcher scouting for {this_card.player.name_with_desc}')
|
||||||
|
this_scouting = await get_pitcher_scouting_or_none(session, this_card)
|
||||||
|
|
||||||
|
logger.info(f'this_scouting: {this_scouting}')
|
||||||
|
return this_scouting
|
||||||
|
|
||||||
|
|
||||||
|
async def get_and_cache_position(session: Session, this_card: Card, position: Literal['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF'], skip_cache: bool = False):
|
||||||
|
logger.info(f'Pulling position rating for {this_card.player.name_with_desc} at {position}')
|
||||||
|
if not skip_cache:
|
||||||
|
this_pos = session.exec(select(PositionRating).where(PositionRating.player_id == this_card.player.id, PositionRating.position == position, PositionRating.variant == this_card.variant)).all()
|
||||||
|
logger.info(f'Ratings found: {len(this_pos)}')
|
||||||
|
|
||||||
|
if len(this_pos) > 0:
|
||||||
|
logger.info(f'we found a cached position rating: {this_pos[0]} / created: {this_pos[0].created}')
|
||||||
|
tdelta = datetime.datetime.now() - this_pos[0].created
|
||||||
|
logger.debug(f'tdelta: {tdelta}')
|
||||||
|
if tdelta.total_seconds() < CACHE_LIMIT:
|
||||||
|
return this_pos[0]
|
||||||
|
else:
|
||||||
|
session.delete(this_pos[0])
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
def cache_pos(json_data: dict) -> PositionRating:
|
||||||
|
if 'id' in json_data:
|
||||||
|
del json_data['id']
|
||||||
|
valid_pos = PositionRatingBase.model_validate(json_data, from_attributes=True)
|
||||||
|
db_pos = PositionRating.model_validate(valid_pos)
|
||||||
|
session.add(db_pos)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(db_pos)
|
||||||
|
return db_pos
|
||||||
|
|
||||||
|
p_query = await db_get('cardpositions', params=[('player_id', this_card.player.id), ('position', position)])
|
||||||
|
if p_query['count'] > 0:
|
||||||
|
json_data = p_query['positions'][0]
|
||||||
|
json_data['player_id'] = get_player_id_from_dict(json_data['player'])
|
||||||
|
this_pos = cache_pos(json_data)
|
||||||
|
|
||||||
|
session.add(this_pos)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(this_pos)
|
||||||
|
|
||||||
|
return this_card
|
||||||
|
|
||||||
|
log_exception(PositionNotFoundException, f'{position} ratings not found for {this_card.player.name_with_desc}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def get_or_create_ai_card(session: Session, player: Player, team: Team, skip_cache: bool = False, dev_mode: bool = False) -> Card:
|
async def get_or_create_ai_card(session: Session, player: Player, team: Team, skip_cache: bool = False, dev_mode: bool = False) -> Card:
|
||||||
logger.info(f'Getting or creating card for {player.name_with_desc} on the {team.sname}')
|
logger.info(f'Getting or creating card for {player.name_with_desc} on the {team.sname}')
|
||||||
if not team.is_ai:
|
if not team.is_ai:
|
||||||
@ -205,6 +388,15 @@ async def get_or_create_ai_card(session: Session, player: Player, team: Team, sk
|
|||||||
|
|
||||||
this_card = await pull_card(player, team)
|
this_card = await pull_card(player, team)
|
||||||
if this_card is not None:
|
if this_card is not None:
|
||||||
|
if player.pos_1 not in ['SP', 'RP']:
|
||||||
|
this_card.batterscouting = await shared_get_scouting(session, this_card, 'batter')
|
||||||
|
else:
|
||||||
|
this_card.pitcherscouting = await shared_get_scouting(session, this_card, 'pitcher')
|
||||||
|
|
||||||
|
session.add(this_card)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(this_card)
|
||||||
|
|
||||||
return this_card
|
return this_card
|
||||||
|
|
||||||
logger.info(f'gameplay_models - get_or_create_ai_card: creating {player.description} {player.name} card for {team.abbrev}')
|
logger.info(f'gameplay_models - get_or_create_ai_card: creating {player.description} {player.name} card for {team.abbrev}')
|
||||||
@ -225,6 +417,15 @@ async def get_or_create_ai_card(session: Session, player: Player, team: Team, sk
|
|||||||
|
|
||||||
this_card = await pull_card(player, team)
|
this_card = await pull_card(player, team)
|
||||||
if this_card is not None:
|
if this_card is not None:
|
||||||
|
if player.pos_1 not in ['SP', 'RP']:
|
||||||
|
this_card.batterscouting = await shared_get_scouting(session, this_card, 'batter')
|
||||||
|
else:
|
||||||
|
this_card.pitcherscouting = await shared_get_scouting(session, this_card, 'pitcher')
|
||||||
|
|
||||||
|
session.add(this_card)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(this_card)
|
||||||
|
|
||||||
return this_card
|
return this_card
|
||||||
|
|
||||||
err = f'Could not create {player.name} card for {team.abbrev}'
|
err = f'Could not create {player.name} card for {team.abbrev}'
|
||||||
@ -268,7 +469,19 @@ async def get_card_or_none(session: Session, card_id: int, skip_cache: bool = Fa
|
|||||||
if this_team is None:
|
if this_team is None:
|
||||||
raise LookupError(f'Team ID {c_query["team_id"]} not found during card check')
|
raise LookupError(f'Team ID {c_query["team_id"]} not found during card check')
|
||||||
|
|
||||||
return cache_card(c_query)
|
logger.info(f'Caching card ID {card_id} now')
|
||||||
|
this_card = cache_card(c_query)
|
||||||
|
|
||||||
|
if this_player.pos_1 not in ['SP', 'RP']:
|
||||||
|
this_card.batterscouting = await shared_get_scouting(session, this_card, 'batter')
|
||||||
|
else:
|
||||||
|
this_card.pitcherscouting = await shared_get_scouting(session, this_card, 'pitcher')
|
||||||
|
|
||||||
|
session.add(this_card)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(this_card)
|
||||||
|
|
||||||
|
return this_card
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@ -27,3 +27,11 @@ class ThrowResponse(pydantic.BaseModel):
|
|||||||
trail_max_safe: int = 10
|
trail_max_safe: int = 10
|
||||||
trail_max_safe_delta: int = -6
|
trail_max_safe_delta: int = -6
|
||||||
|
|
||||||
|
|
||||||
|
class DefenseResponse(pydantic.BaseModel):
|
||||||
|
hold_first: bool = False
|
||||||
|
hold_second: bool = False
|
||||||
|
hold_third: bool = False
|
||||||
|
outfield_in: bool = False
|
||||||
|
infield_in: bool = False
|
||||||
|
corners_in: bool = False
|
||||||
|
|||||||
@ -20,7 +20,7 @@ def get_result(pitcher: data_cache.PitchingWrapper, batter: data_cache.BattingWr
|
|||||||
|
|
||||||
if which == 'pitcher':
|
if which == 'pitcher':
|
||||||
logger.info(f'in_game.simulations - get_result - grabbing pitcher card chances')
|
logger.info(f'in_game.simulations - get_result - grabbing pitcher card chances')
|
||||||
ch_data = pitcher.ratings_vl if bat_hand.upper() == 'L' else pitcher.ratings_vr
|
ch_data = pitcher.ratings_vl if bat_hand == 'L' else pitcher.ratings_vr
|
||||||
logger.info(f'ch_data: {ch_data}')
|
logger.info(f'ch_data: {ch_data}')
|
||||||
# for field in fields(ch_data):
|
# for field in fields(ch_data):
|
||||||
# if field.name not in unused_fields:
|
# if field.name not in unused_fields:
|
||||||
@ -28,7 +28,7 @@ def get_result(pitcher: data_cache.PitchingWrapper, batter: data_cache.BattingWr
|
|||||||
# ch_probs.append(getattr(data_cache.PitchingRatings, field.name))
|
# ch_probs.append(getattr(data_cache.PitchingRatings, field.name))
|
||||||
else:
|
else:
|
||||||
logger.info(f'in_game.simulations - get_result - grabbing batter card chances')
|
logger.info(f'in_game.simulations - get_result - grabbing batter card chances')
|
||||||
ch_data = batter.ratings_vl if pit_hand.upper() == 'L' else batter.ratings_vr
|
ch_data = batter.ratings_vl if pit_hand == 'L' else batter.ratings_vr
|
||||||
logger.info(f'ch_data: {ch_data}')
|
logger.info(f'ch_data: {ch_data}')
|
||||||
# for field in fields(ch_data):
|
# for field in fields(ch_data):
|
||||||
# if field.name not in unused_fields:
|
# if field.name not in unused_fields:
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from sqlmodel import Session, SQLModel, create_engine
|
|||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
|
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
from in_game.gameplay_models import Card, Cardset, Game, GameCardsetLink, Lineup, ManagerAi, Play, Team, Player
|
from in_game.gameplay_models import BatterScouting, BattingCard, BattingRatings, Card, Cardset, Game, GameCardsetLink, Lineup, ManagerAi, PitcherScouting, PitchingCard, PitchingRatings, Play, Team, Player
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name='session')
|
@pytest.fixture(name='session')
|
||||||
@ -70,6 +70,12 @@ def session_fixture():
|
|||||||
|
|
||||||
all_players = []
|
all_players = []
|
||||||
all_cards = []
|
all_cards = []
|
||||||
|
all_batscouting = []
|
||||||
|
all_pitscouting = []
|
||||||
|
all_pitratings = []
|
||||||
|
all_batratings = []
|
||||||
|
all_batcards = []
|
||||||
|
all_pitcards = []
|
||||||
pos_list = ['C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'DH', 'P']
|
pos_list = ['C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'DH', 'P']
|
||||||
for x in range(40):
|
for x in range(40):
|
||||||
if x < 10:
|
if x < 10:
|
||||||
@ -98,9 +104,57 @@ def session_fixture():
|
|||||||
pos_1=pos_list[(x % 10)],
|
pos_1=pos_list[(x % 10)],
|
||||||
description="Live" if x % 2 == 1 else "2024"
|
description="Live" if x % 2 == 1 else "2024"
|
||||||
))
|
))
|
||||||
|
# Is Batter
|
||||||
|
if x % 10 == 9:
|
||||||
|
all_batcards.append(BattingCard(
|
||||||
|
id=x+1,
|
||||||
|
steal_high=10 + (x % 10),
|
||||||
|
hand='R'
|
||||||
|
))
|
||||||
|
all_batratings.append(BattingRatings(
|
||||||
|
id=x+1,
|
||||||
|
homerun=x % 10
|
||||||
|
))
|
||||||
|
all_batratings.append(BattingRatings(
|
||||||
|
id=x+1001,
|
||||||
|
homerun=x % 10
|
||||||
|
))
|
||||||
|
all_batscouting.append(BatterScouting(
|
||||||
|
id=x+1,
|
||||||
|
battingcard_id=x+1,
|
||||||
|
ratings_vr_id=x+1,
|
||||||
|
ratings_vl_id=x+1001,
|
||||||
|
))
|
||||||
all_cards.append(Card(
|
all_cards.append(Card(
|
||||||
player_id=x+1,
|
player_id=x+1,
|
||||||
team_id=team_id
|
team_id=team_id,
|
||||||
|
batterscouting_id=x+1
|
||||||
|
))
|
||||||
|
# Is Pitcher
|
||||||
|
else:
|
||||||
|
all_pitcards.append(PitchingCard(
|
||||||
|
id=x+1,
|
||||||
|
wild_pitch=x / 10,
|
||||||
|
hand='R'
|
||||||
|
))
|
||||||
|
all_pitratings.append(PitchingRatings(
|
||||||
|
id=x+1,
|
||||||
|
homerun=x % 10
|
||||||
|
))
|
||||||
|
all_pitratings.append(PitchingRatings(
|
||||||
|
id=x+1001,
|
||||||
|
homerun=x % 10
|
||||||
|
))
|
||||||
|
all_pitscouting.append(PitcherScouting(
|
||||||
|
id=x+1,
|
||||||
|
pitchingcard_id=x+1,
|
||||||
|
ratings_vr_id=x+1,
|
||||||
|
ratings_vl_id=x+1001
|
||||||
|
))
|
||||||
|
all_cards.append(Card(
|
||||||
|
player_id=x+1,
|
||||||
|
team_id=team_id,
|
||||||
|
pitcherscouting_id=x+1
|
||||||
))
|
))
|
||||||
|
|
||||||
all_players.append(Player(
|
all_players.append(Player(
|
||||||
|
|||||||
226
tests/gameplay_models/test_batterscouting_model.py
Normal file
226
tests/gameplay_models/test_batterscouting_model.py
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
import pytest
|
||||||
|
from sqlmodel import Session, select, func
|
||||||
|
|
||||||
|
from in_game.gameplay_models import Card
|
||||||
|
from in_game.gameplay_queries import get_batter_scouting_or_none, get_card_or_none, get_pitcher_scouting_or_none
|
||||||
|
from tests.factory import session_fixture
|
||||||
|
|
||||||
|
sample_ratings_query = {
|
||||||
|
"count": 2,
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"id": 7673,
|
||||||
|
"battingcard": {
|
||||||
|
"id": 3837,
|
||||||
|
"player": {
|
||||||
|
"player_id": 395,
|
||||||
|
"p_name": "Cedric Mullins",
|
||||||
|
"cost": 256,
|
||||||
|
"image": "https://pd.manticorum.com/api/v2/players/395/battingcard?d=2023-11-19",
|
||||||
|
"image2": None,
|
||||||
|
"mlbclub": "Baltimore Orioles",
|
||||||
|
"franchise": "Baltimore Orioles",
|
||||||
|
"cardset": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "2021 Season",
|
||||||
|
"description": "Cards based on the full 2021 season",
|
||||||
|
"event": None,
|
||||||
|
"for_purchase": True,
|
||||||
|
"total_cards": 791,
|
||||||
|
"in_packs": True,
|
||||||
|
"ranked_legal": False
|
||||||
|
},
|
||||||
|
"set_num": 395,
|
||||||
|
"rarity": {
|
||||||
|
"id": 2,
|
||||||
|
"value": 3,
|
||||||
|
"name": "All-Star",
|
||||||
|
"color": "FFD700"
|
||||||
|
},
|
||||||
|
"pos_1": "CF",
|
||||||
|
"pos_2": None,
|
||||||
|
"pos_3": None,
|
||||||
|
"pos_4": None,
|
||||||
|
"pos_5": None,
|
||||||
|
"pos_6": None,
|
||||||
|
"pos_7": None,
|
||||||
|
"pos_8": None,
|
||||||
|
"headshot": "https://www.baseball-reference.com/req/202206291/images/headshots/2/24bf4355_mlbam.jpg",
|
||||||
|
"vanity_card": None,
|
||||||
|
"strat_code": "17929",
|
||||||
|
"bbref_id": "mullice01",
|
||||||
|
"fangr_id": None,
|
||||||
|
"description": "2021",
|
||||||
|
"quantity": 999,
|
||||||
|
"mlbplayer": {
|
||||||
|
"id": 396,
|
||||||
|
"first_name": "Cedric",
|
||||||
|
"last_name": "Mullins",
|
||||||
|
"key_fangraphs": 17929,
|
||||||
|
"key_bbref": "mullice01",
|
||||||
|
"key_retro": "mullc002",
|
||||||
|
"key_mlbam": 656775,
|
||||||
|
"offense_col": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"variant": 0,
|
||||||
|
"steal_low": 9,
|
||||||
|
"steal_high": 12,
|
||||||
|
"steal_auto": True,
|
||||||
|
"steal_jump": 0.2222222222222222,
|
||||||
|
"bunting": "C",
|
||||||
|
"hit_and_run": "B",
|
||||||
|
"running": 13,
|
||||||
|
"offense_col": 1,
|
||||||
|
"hand": "L"
|
||||||
|
},
|
||||||
|
"vs_hand": "L",
|
||||||
|
"pull_rate": 0.43888889,
|
||||||
|
"center_rate": 0.32777778,
|
||||||
|
"slap_rate": 0.23333333,
|
||||||
|
"homerun": 2.25,
|
||||||
|
"bp_homerun": 5.0,
|
||||||
|
"triple": 0.0,
|
||||||
|
"double_three": 0.0,
|
||||||
|
"double_two": 2.4,
|
||||||
|
"double_pull": 7.2,
|
||||||
|
"single_two": 4.6,
|
||||||
|
"single_one": 3.5,
|
||||||
|
"single_center": 5.85,
|
||||||
|
"bp_single": 5.0,
|
||||||
|
"hbp": 2.0,
|
||||||
|
"walk": 9.0,
|
||||||
|
"strikeout": 23.0,
|
||||||
|
"lineout": 3.0,
|
||||||
|
"popout": 6.0,
|
||||||
|
"flyout_a": 0.0,
|
||||||
|
"flyout_bq": 0.15,
|
||||||
|
"flyout_lf_b": 2.8,
|
||||||
|
"flyout_rf_b": 3.75,
|
||||||
|
"groundout_a": 0.0,
|
||||||
|
"groundout_b": 9.0,
|
||||||
|
"groundout_c": 13.5,
|
||||||
|
"avg": 0.2851851851851852,
|
||||||
|
"obp": 0.387037037037037,
|
||||||
|
"slg": 0.5060185185185185
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7674,
|
||||||
|
"battingcard": {
|
||||||
|
"id": 3837,
|
||||||
|
"player": {
|
||||||
|
"player_id": 395,
|
||||||
|
"p_name": "Cedric Mullins",
|
||||||
|
"cost": 256,
|
||||||
|
"image": "https://pd.manticorum.com/api/v2/players/395/battingcard?d=2023-11-19",
|
||||||
|
"image2": None,
|
||||||
|
"mlbclub": "Baltimore Orioles",
|
||||||
|
"franchise": "Baltimore Orioles",
|
||||||
|
"cardset": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "2021 Season",
|
||||||
|
"description": "Cards based on the full 2021 season",
|
||||||
|
"event": None,
|
||||||
|
"for_purchase": True,
|
||||||
|
"total_cards": 791,
|
||||||
|
"in_packs": True,
|
||||||
|
"ranked_legal": False
|
||||||
|
},
|
||||||
|
"set_num": 395,
|
||||||
|
"rarity": {
|
||||||
|
"id": 2,
|
||||||
|
"value": 3,
|
||||||
|
"name": "All-Star",
|
||||||
|
"color": "FFD700"
|
||||||
|
},
|
||||||
|
"pos_1": "CF",
|
||||||
|
"pos_2": None,
|
||||||
|
"pos_3": None,
|
||||||
|
"pos_4": None,
|
||||||
|
"pos_5": None,
|
||||||
|
"pos_6": None,
|
||||||
|
"pos_7": None,
|
||||||
|
"pos_8": None,
|
||||||
|
"headshot": "https://www.baseball-reference.com/req/202206291/images/headshots/2/24bf4355_mlbam.jpg",
|
||||||
|
"vanity_card": None,
|
||||||
|
"strat_code": "17929",
|
||||||
|
"bbref_id": "mullice01",
|
||||||
|
"fangr_id": None,
|
||||||
|
"description": "2021",
|
||||||
|
"quantity": 999,
|
||||||
|
"mlbplayer": {
|
||||||
|
"id": 396,
|
||||||
|
"first_name": "Cedric",
|
||||||
|
"last_name": "Mullins",
|
||||||
|
"key_fangraphs": 17929,
|
||||||
|
"key_bbref": "mullice01",
|
||||||
|
"key_retro": "mullc002",
|
||||||
|
"key_mlbam": 656775,
|
||||||
|
"offense_col": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"variant": 0,
|
||||||
|
"steal_low": 9,
|
||||||
|
"steal_high": 12,
|
||||||
|
"steal_auto": True,
|
||||||
|
"steal_jump": 0.2222222222222222,
|
||||||
|
"bunting": "C",
|
||||||
|
"hit_and_run": "B",
|
||||||
|
"running": 13,
|
||||||
|
"offense_col": 1,
|
||||||
|
"hand": "L"
|
||||||
|
},
|
||||||
|
"vs_hand": "R",
|
||||||
|
"pull_rate": 0.43377483,
|
||||||
|
"center_rate": 0.32119205,
|
||||||
|
"slap_rate": 0.24503311,
|
||||||
|
"homerun": 2.0,
|
||||||
|
"bp_homerun": 7.0,
|
||||||
|
"triple": 0.0,
|
||||||
|
"double_three": 0.0,
|
||||||
|
"double_two": 2.7,
|
||||||
|
"double_pull": 7.95,
|
||||||
|
"single_two": 3.3,
|
||||||
|
"single_one": 0.0,
|
||||||
|
"single_center": 8.6,
|
||||||
|
"bp_single": 5.0,
|
||||||
|
"hbp": 1.0,
|
||||||
|
"walk": 12.9,
|
||||||
|
"strikeout": 11.1,
|
||||||
|
"lineout": 8.0,
|
||||||
|
"popout": 11.0,
|
||||||
|
"flyout_a": 0.0,
|
||||||
|
"flyout_bq": 0.4,
|
||||||
|
"flyout_lf_b": 4.05,
|
||||||
|
"flyout_rf_b": 6.0,
|
||||||
|
"groundout_a": 0.0,
|
||||||
|
"groundout_b": 3.0,
|
||||||
|
"groundout_c": 14.0,
|
||||||
|
"avg": 0.2828703703703704,
|
||||||
|
"obp": 0.4115740740740741,
|
||||||
|
"slg": 0.5342592592592592
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_create_scouting(session: Session):
|
||||||
|
this_card = await get_card_or_none(session, card_id=1405)
|
||||||
|
|
||||||
|
assert this_card.player.id == 395
|
||||||
|
assert this_card.team.id == 31
|
||||||
|
assert this_card.batterscouting.battingcard_id == sample_ratings_query["ratings"][0]['battingcard']['id']
|
||||||
|
|
||||||
|
# this_scouting = await get_batter_scouting_or_none(session, this_card)
|
||||||
|
|
||||||
|
# assert this_scouting.battingcard_id == sample_ratings_query["ratings"][0]['battingcard']['id']
|
||||||
|
|
||||||
|
# this_card = await get_card_or_none(session, card_id=1406)
|
||||||
|
|
||||||
|
# assert this_card.player.id == 161
|
||||||
|
# assert this_card.team.id == 31
|
||||||
|
|
||||||
|
# this_scouting = await get_pitcher_scouting_or_none(session, this_card)
|
||||||
|
|
||||||
|
# assert this_scouting.pitchingcard_id == 4294
|
||||||
|
|
||||||
@ -167,3 +167,9 @@ async def test_walks(session: Session):
|
|||||||
assert this_play.on_base_code == 1
|
assert this_play.on_base_code == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_one(session: Session):
|
||||||
|
play_1 = session.exec(select(Play).where(Play.id == 1)).one()
|
||||||
|
|
||||||
|
assert play_1.play_num == 1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user