Update cache refresh logic to replace vs delete
This commit is contained in:
parent
557404301a
commit
f8aad38739
@ -3,6 +3,22 @@ from typing import Literal
|
||||
|
||||
logger = logging.getLogger('discord_app')
|
||||
|
||||
|
||||
def log_errors(func):
|
||||
"""
|
||||
This wrapper function will force all exceptions to be logged with executiona and stack info.
|
||||
"""
|
||||
|
||||
def wrap(*args, **kwargs):
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
log_exception(e)
|
||||
logger.info(func.__name__)
|
||||
return result
|
||||
|
||||
return wrap
|
||||
|
||||
def log_exception(e: Exception, msg: str = '', level: Literal['debug', 'error', 'info', 'warn'] = 'error'):
|
||||
if level == 'debug':
|
||||
logger.debug(msg, exc_info=True, stack_info=True)
|
||||
|
||||
@ -2438,7 +2438,7 @@ async def give_packs(team: dict, num_packs: int, pack_type: dict = None) -> dict
|
||||
|
||||
def get_sheets(bot):
|
||||
try:
|
||||
return bot.get_cog('Players').sheets
|
||||
return bot.get_cog('Gameplay').sheets
|
||||
except Exception as e:
|
||||
logger.error(f'Could not grab sheets auth: {e}')
|
||||
raise ConnectionError(f'Bot has not authenticated with discord; please try again in 1 minute.')
|
||||
|
||||
@ -19,7 +19,8 @@ logger = logging.getLogger('discord_app')
|
||||
# sqlite_url = 'sqlite:///storage/gameplay.db'
|
||||
# connect_args = {"check_same_thread": False}
|
||||
# engine = create_engine(sqlite_url, echo=False, connect_args=connect_args)
|
||||
engine = create_engine(f'postgresql://{os.getenv('DB_USERNAME')}:{os.getenv('DB_PASSWORD')}@{os.getenv('DB_URL')}/{os.getenv('DB_NAME')}')
|
||||
postgres_url = f'postgresql://{os.getenv('DB_USERNAME')}:{os.getenv('DB_PASSWORD')}@{os.getenv('DB_URL')}/{os.getenv('DB_NAME')}'
|
||||
engine = create_engine(postgres_url)
|
||||
CACHE_LIMIT = 1209600 # in seconds
|
||||
SBA_COLOR = 'a6ce39'
|
||||
SBA_LOGO = 'https://sombaseball.ddns.net/static/images/sba-logo.png'
|
||||
|
||||
@ -8,7 +8,7 @@ from sqlalchemy import func
|
||||
from api_calls import db_get, db_post
|
||||
from sqlmodel import col
|
||||
from in_game.gameplay_models import CACHE_LIMIT, BatterScouting, BatterScoutingBase, BattingCard, BattingCardBase, BattingRatings, BattingRatingsBase, Card, CardBase, Cardset, CardsetBase, GameCardsetLink, Lineup, PitcherScouting, PitchingCard, PitchingCardBase, PitchingRatings, PitchingRatingsBase, Player, PlayerBase, PositionRating, PositionRatingBase, RosterLink, Session, Team, TeamBase, select, or_, Game, Play
|
||||
from exceptions import DatabaseError, PositionNotFoundException, log_exception, PlayNotFoundException
|
||||
from exceptions import DatabaseError, PositionNotFoundException, log_errors, log_exception, PlayNotFoundException
|
||||
|
||||
|
||||
logger = logging.getLogger('discord_app')
|
||||
@ -93,14 +93,14 @@ async def get_team_or_none(
|
||||
this_team = session.exec(select(Team).where(func.lower(Team.abbrev) == team_abbrev.lower())).one_or_none()
|
||||
|
||||
if this_team is not None:
|
||||
logger.debug(f'we found a team: {this_team} / created: {this_team.created}')
|
||||
logger.info(f'we found a team: {this_team} / created: {this_team.created}')
|
||||
tdelta = datetime.datetime.now() - this_team.created
|
||||
logger.debug(f'tdelta: {tdelta}')
|
||||
logger.info(f'tdelta: {tdelta}')
|
||||
if tdelta.total_seconds() < CACHE_LIMIT:
|
||||
return this_team
|
||||
else:
|
||||
session.delete(this_team)
|
||||
session.commit()
|
||||
# else:
|
||||
# session.delete(this_team)
|
||||
# session.commit()
|
||||
|
||||
def cache_team(json_data: dict) -> Team:
|
||||
logger.info(f'gameplay_queries - cache_team - writing a team to cache: {json_data}')
|
||||
@ -108,6 +108,24 @@ async def get_team_or_none(
|
||||
logger.info(f'gameplay_queries - cache_team - valid_team: {valid_team}')
|
||||
db_team = Team.model_validate(valid_team)
|
||||
logger.info(f'gameplay_queries - cache_team - db_team: {db_team}')
|
||||
logger.info(f'Checking for existing team ID: {db_team.id}')
|
||||
|
||||
try:
|
||||
this_team = session.exec(select(Team).where(Team.id == db_team.id)).one()
|
||||
logger.info(f'Found team: {this_team}\nUpdating with db team: {db_team}')
|
||||
|
||||
for key, value in db_team.model_dump(exclude_unset=True).items():
|
||||
logger.info(f'Setting key ({key}) to value ({value})')
|
||||
setattr(this_team, key, value)
|
||||
|
||||
logger.info(f'Set this_team to db_team')
|
||||
session.add(this_team)
|
||||
session.commit()
|
||||
logger.info(f'Refreshing this_team')
|
||||
session.refresh(this_team)
|
||||
return this_team
|
||||
except:
|
||||
logger.info(f'Team not found, adding to db')
|
||||
session.add(db_team)
|
||||
session.commit()
|
||||
session.refresh(db_team)
|
||||
@ -193,15 +211,31 @@ async def get_player_or_none(session: Session, player_id: int, skip_cache: bool
|
||||
if tdelta.total_seconds() < CACHE_LIMIT:
|
||||
logger.info(f'returning this player')
|
||||
return this_player
|
||||
else:
|
||||
logger.warning('Deleting old player record')
|
||||
session.delete(this_player)
|
||||
session.commit()
|
||||
# else:
|
||||
# logger.warning('Deleting old player record')
|
||||
# session.delete(this_player)
|
||||
# session.commit()
|
||||
|
||||
def cache_player(json_data: dict) -> Player:
|
||||
logger.info(f'gameplay_models - get_player_or_none - cache_player - caching player data: {json_data}')
|
||||
valid_player = PlayerBase.model_validate(json_data, from_attributes=True)
|
||||
db_player = Player.model_validate(valid_player)
|
||||
|
||||
try:
|
||||
this_player = session.get(Player, player_id)
|
||||
logger.info(f'Found player: {this_player}\nUpdating with db_player: {db_player}')
|
||||
|
||||
for key, value in db_player.model_dump(exclude_unset=True).items():
|
||||
logger.info(f'Setting key ({key}) to value ({value})')
|
||||
setattr(this_player, key, value)
|
||||
|
||||
logger.info(f'Set this_player to db_player')
|
||||
session.add(this_player)
|
||||
session.commit()
|
||||
logger.info(f'Refreshing this_player')
|
||||
session.refresh(this_player)
|
||||
return this_player
|
||||
except:
|
||||
session.add(db_player)
|
||||
session.commit()
|
||||
session.refresh(db_player)
|
||||
@ -426,10 +460,10 @@ async def get_or_create_ai_card(session: Session, player: Player, team: Team, sk
|
||||
if tdelta.total_seconds() < CACHE_LIMIT and (this_card.pitcherscouting is not None or this_card.batterscouting is not None):
|
||||
logger.info(f'returning this_card')
|
||||
return this_card
|
||||
else:
|
||||
logger.info(f'deleting card record')
|
||||
session.delete(this_card)
|
||||
session.commit()
|
||||
# else:
|
||||
# logger.info(f'deleting card record')
|
||||
# session.delete(this_card)
|
||||
# session.commit()
|
||||
|
||||
async def pull_card(p: Player, t: Team):
|
||||
c_query = await db_get('cards', params=[('team_id', t.id), ('player_id', p.id)])
|
||||
@ -440,6 +474,25 @@ async def get_or_create_ai_card(session: Session, player: Player, team: Team, sk
|
||||
json_data['player_id'] = get_player_id_from_dict(json_data['player'])
|
||||
valid_card = CardBase.model_validate(c_query['cards'][0], from_attributes=True)
|
||||
db_card = Card.model_validate(valid_card)
|
||||
logger.info(f'gameplay_queries - cache_team - db_card: {db_card}')
|
||||
logger.info(f'Checking for existing team ID: {db_card.id}')
|
||||
|
||||
try:
|
||||
this_card = session.exec(select(Card).where(Card.id == db_card.id)).one()
|
||||
logger.info(f'Found card: {this_card}\nUpdating with db card: {db_card}')
|
||||
|
||||
for key, value in db_card.model_dump(exclude_unset=True).items():
|
||||
logger.info(f'Setting key ({key}) to value ({value})')
|
||||
setattr(this_card, key, value)
|
||||
|
||||
logger.info(f'Set this_card to db_card')
|
||||
session.add(this_card)
|
||||
session.commit()
|
||||
logger.info(f'Refreshing this_card')
|
||||
session.refresh(this_card)
|
||||
return this_card
|
||||
except:
|
||||
logger.info(f'Card not found, adding to db')
|
||||
session.add(db_card)
|
||||
session.commit()
|
||||
session.refresh(db_card)
|
||||
@ -494,6 +547,7 @@ async def get_or_create_ai_card(session: Session, player: Player, team: Team, sk
|
||||
raise LookupError(err)
|
||||
|
||||
|
||||
@log_errors
|
||||
async def get_card_or_none(session: Session, card_id: int, skip_cache: bool = False) -> Card | None:
|
||||
logger.info(f'Getting card {card_id} / skip_cache: {skip_cache}')
|
||||
if not skip_cache:
|
||||
@ -506,24 +560,44 @@ async def get_card_or_none(session: Session, card_id: int, skip_cache: bool = Fa
|
||||
if tdelta.total_seconds() < CACHE_LIMIT and (this_card.pitcherscouting is not None or this_card.batterscouting is not None):
|
||||
logger.info(f'returning this_card')
|
||||
return this_card
|
||||
else:
|
||||
logger.info(f'deleting this_card')
|
||||
try:
|
||||
session.delete(this_card.batterscouting)
|
||||
except Exception as e:
|
||||
logger.error(f'Could not delete batter scouting: {e}')
|
||||
# else:
|
||||
# logger.info(f'deleting this_card')
|
||||
# try:
|
||||
# session.delete(this_card.batterscouting)
|
||||
# except Exception as e:
|
||||
# logger.error(f'Could not delete batter scouting: {e}')
|
||||
|
||||
try:
|
||||
session.delete(this_card.pitcherscouting)
|
||||
except Exception as e:
|
||||
logger.error(f'Could not delete batter scouting: {e}')
|
||||
# try:
|
||||
# session.delete(this_card.pitcherscouting)
|
||||
# except Exception as e:
|
||||
# logger.error(f'Could not delete batter scouting: {e}')
|
||||
|
||||
session.delete(this_card)
|
||||
session.commit()
|
||||
# session.delete(this_card)
|
||||
# session.commit()
|
||||
|
||||
def cache_card(json_data: dict) -> Card:
|
||||
valid_card = CardBase.model_validate(json_data, from_attributes=True)
|
||||
db_card = Card.model_validate(valid_card)
|
||||
logger.info(f'gameplay_queries - cache_team - db_card: {db_card}')
|
||||
logger.info(f'Checking for existing team ID: {db_card.id}')
|
||||
|
||||
try:
|
||||
this_card = session.exec(select(Card).where(Card.id == db_card.id)).one()
|
||||
logger.info(f'Found card: {this_card}\nUpdating with db card: {db_card}')
|
||||
|
||||
# this_team = db_team
|
||||
for key, value in db_card.model_dump(exclude_unset=True).items():
|
||||
logger.info(f'Setting key ({key}) to value ({value})')
|
||||
setattr(this_card, key, value)
|
||||
|
||||
logger.info(f'Set this_card to db_card')
|
||||
session.add(this_card)
|
||||
session.commit()
|
||||
logger.info(f'Refreshing this_card')
|
||||
session.refresh(this_card)
|
||||
return this_card
|
||||
except:
|
||||
logger.info(f'Card not found, adding to db')
|
||||
session.add(db_card)
|
||||
session.commit()
|
||||
session.refresh(db_card)
|
||||
@ -545,15 +619,22 @@ async def get_card_or_none(session: Session, card_id: int, skip_cache: bool = Fa
|
||||
logger.info(f'Caching card ID {card_id} now')
|
||||
this_card = cache_card(c_query)
|
||||
|
||||
logger.info(f'Card is cached, checking for scouting')
|
||||
all_pos = [x for x in [this_player.pos_1, this_player.pos_2, this_player.pos_3, this_player.pos_3, this_player.pos_4, this_player.pos_5, this_player.pos_6, this_player.pos_7, this_player.pos_8] if x is not None]
|
||||
logger.info(f'All positions: {all_pos}')
|
||||
if 'SP' in all_pos or 'RP' in all_pos:
|
||||
logger.info(f'Pulling pitcher scouting')
|
||||
this_card.pitcherscouting = await shared_get_scouting(session, this_card, 'pitcher')
|
||||
if any(item in all_pos for item in ['DH', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF']):
|
||||
logger.info(f'Pulling batter scouting')
|
||||
this_card.batterscouting = await shared_get_scouting(session, this_card, 'batter')
|
||||
|
||||
logger.info(f'Updating this_card')
|
||||
session.add(this_card)
|
||||
session.commit()
|
||||
logger.info(f'Refreshing this_card')
|
||||
session.refresh(this_card)
|
||||
logger.info(f'this_card: {this_card}')
|
||||
|
||||
return this_card
|
||||
|
||||
|
||||
@ -5,13 +5,14 @@ from sqlalchemy import pool
|
||||
|
||||
from alembic import context
|
||||
|
||||
from in_game.gameplay_models import sqlite_url, Game
|
||||
from in_game.gameplay_models import sqlite_url, postgres_url
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
config.set_main_option('sqlalchemy.url', sqlite_url)
|
||||
# config.set_main_option('sqlalchemy.url', sqlite_url)
|
||||
config.set_main_option('sqlalchemy.url', postgres_url)
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
|
||||
@ -9,7 +9,7 @@ from discord.utils import MISSING
|
||||
from sqlmodel import Session
|
||||
|
||||
from api_calls import db_delete, db_get, db_post
|
||||
from exceptions import CardNotFoundException, LegalityCheckNotRequired, PlayNotFoundException, log_exception
|
||||
from exceptions import CardNotFoundException, LegalityCheckNotRequired, PlayNotFoundException, PositionNotFoundException, log_exception, log_errors
|
||||
from helpers import get_card_embeds, random_insult
|
||||
from in_game.game_helpers import legal_check
|
||||
from in_game.gameplay_models import Game, Lineup, Play, Team
|
||||
@ -262,14 +262,15 @@ class SelectReliefPitcher(discord.ui.Select):
|
||||
log_exception(e, 'Couldn\'t clean up after selecting rp')
|
||||
|
||||
|
||||
|
||||
@log_errors
|
||||
class SelectSubPosition(discord.ui.Select):
|
||||
def __init__(self, session: Session, this_lineup: Lineup, custom_id = ..., placeholder = None, options: List[SelectOption] = ..., responders: list[discord.User] = None):
|
||||
def __init__(self, session: Session, this_lineup: Lineup, custom_id = MISSING, placeholder = None, options: List[SelectOption] = ..., responders: list[discord.User] = None):
|
||||
self.session = session
|
||||
self.this_lineup = this_lineup
|
||||
self.responders = responders
|
||||
super().__init__(custom_id=custom_id, placeholder=placeholder, min_values=1, max_values=1, options=options, disabled=False)
|
||||
|
||||
@log_errors
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
if self.responders is not None and interaction.user not in self.responders:
|
||||
await interaction.response.send_message(
|
||||
@ -310,6 +311,7 @@ class SelectBatterSub(discord.ui.Select):
|
||||
self.responders = responders
|
||||
super().__init__(custom_id=custom_id, placeholder=placeholder, min_values=1, max_values=1, options=options)
|
||||
|
||||
@log_errors
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
if self.responders is not None and interaction.user not in self.responders:
|
||||
await interaction.response.send_message(
|
||||
@ -322,16 +324,20 @@ class SelectBatterSub(discord.ui.Select):
|
||||
logger.info(f'Setting batter sub to Card ID: {self.values[0]}')
|
||||
|
||||
# Get Human batter card
|
||||
logger.info(f'Looking up card substitution')
|
||||
human_batter_card = await get_card_or_none(self.session, card_id=self.values[0])
|
||||
if human_batter_card is None:
|
||||
log_exception(CardNotFoundException, f'Card ID {self.values[0]} not found')
|
||||
logger.info(f'human_batter_card: {human_batter_card}')
|
||||
|
||||
logger.info(f'Checking team ownership of batter card')
|
||||
if human_batter_card.team_id != self.team.id:
|
||||
logger.error(f'Card_id {self.values[0]} does not belong to {self.team.abbrev} in Game {self.game.id}')
|
||||
await interaction.channel.send(
|
||||
f'Uh oh. Card ID {self.values[0]} is {human_batter_card.player.name} and belongs to {human_batter_card.team.sname}. Will you double check that before we get started?'
|
||||
)
|
||||
return
|
||||
logger.info(f'Ownership is good')
|
||||
|
||||
# legal_data = await legal_check([self.values[0]], difficulty_name=self.league_name)
|
||||
# if not legal_data['legal']:
|
||||
@ -340,6 +346,7 @@ class SelectBatterSub(discord.ui.Select):
|
||||
# )
|
||||
# return
|
||||
|
||||
logger.info(f'Pulling current play to log subs')
|
||||
this_play = self.game.current_play_or_none(self.session)
|
||||
if this_play is None:
|
||||
log_exception(PlayNotFoundException, 'Play not found during substitution')
|
||||
@ -360,10 +367,56 @@ class SelectBatterSub(discord.ui.Select):
|
||||
)
|
||||
|
||||
if same_position:
|
||||
logger.info(f'same_position is True')
|
||||
position = last_lineup.position
|
||||
pos_text = ''
|
||||
view = None
|
||||
else:
|
||||
logger.info(f'same_position is False')
|
||||
position = 'PH'
|
||||
pos_text = 'What position will they play?'
|
||||
|
||||
logger.info(f'Deactivating last_lineup')
|
||||
last_lineup.active = False
|
||||
self.session.add(last_lineup)
|
||||
logger.info(f'Set {last_lineup.card.player.name_with_desc} as inactive')
|
||||
|
||||
logger.info(f'new position: {position}')
|
||||
if position not in ['DH', 'PR', 'PH']:
|
||||
logger.info(f'go get position rating')
|
||||
try:
|
||||
pos_rating = await get_position(self.session, human_batter_card, position)
|
||||
except PositionNotFoundException as e:
|
||||
await interaction.edit_original_response(
|
||||
content=f'Uh oh, I cannot find {position} ratings for {human_batter_card.player.name_with_desc}. Please go double-check this sub and run again.',
|
||||
view=None
|
||||
)
|
||||
return
|
||||
|
||||
logger.info(f'Creating new lineup record')
|
||||
human_bat_lineup = Lineup(
|
||||
team=self.team,
|
||||
player=human_batter_card.player,
|
||||
card=human_batter_card,
|
||||
position=position,
|
||||
batting_order=self.batting_order,
|
||||
game=self.game,
|
||||
after_play=max(this_play.play_num - 1, 0),
|
||||
replacing_id=last_lineup.id
|
||||
)
|
||||
logger.info(f'adding lineup to session: {human_bat_lineup}')
|
||||
self.session.add(human_bat_lineup)
|
||||
# self.session.commit()
|
||||
|
||||
logger.info(f'Inserted {human_bat_lineup.card.player.name_with_desc} in the {self.batting_order} spot')
|
||||
this_play.batter = human_bat_lineup
|
||||
this_play.batter_pos = position
|
||||
|
||||
logger.info(f'Adding play to session: {this_play}')
|
||||
self.session.add(this_play)
|
||||
self.session.commit()
|
||||
|
||||
if not same_position:
|
||||
pos_dict_list = {
|
||||
'Pinch Hitter': 'PH',
|
||||
'Catcher': 'C',
|
||||
@ -378,48 +431,35 @@ class SelectBatterSub(discord.ui.Select):
|
||||
'Pitcher': 'P'
|
||||
}
|
||||
|
||||
position = 'PH'
|
||||
pos_text = 'What position will they play?'
|
||||
options=[SelectSubPosition(label=f'{x}', value=pos_dict_list[x], default=x=='Pinch Hitter', responders=[interaction.user]) for x in pos_dict_list]
|
||||
|
||||
view = DropdownView(dropdown_objects=options)
|
||||
|
||||
last_lineup.active = False
|
||||
self.session.add(last_lineup)
|
||||
logger.info(f'Set {last_lineup.card.player.name_with_desc} as inactive')
|
||||
|
||||
if position not in ['DH', 'PR', 'PH']:
|
||||
pos_rating = await get_position(self.session, human_batter_card, position)
|
||||
|
||||
human_bat_lineup = Lineup(
|
||||
team=self.team,
|
||||
player=human_batter_card.player,
|
||||
card=human_batter_card,
|
||||
position=position,
|
||||
batting_order=self.batting_order,
|
||||
game=self.game,
|
||||
after_play=max(this_play.play_num - 1, 0),
|
||||
replacing_id=last_lineup.id
|
||||
logger.info(f'Prepping sub position')
|
||||
pos_text = f'What position will {human_batter_card.player.name} play?'
|
||||
options=[
|
||||
SelectOption(
|
||||
label=f'{pos_name}',
|
||||
value=pos_dict_list[pos_name],
|
||||
default=pos_name=='Pinch Hitter'
|
||||
)
|
||||
for pos_name in pos_dict_list.keys()
|
||||
]
|
||||
logger.info(f'options: {options}')
|
||||
view = SelectSubPosition(
|
||||
self.session,
|
||||
this_lineup=human_bat_lineup,
|
||||
placeholder='Select Position',
|
||||
options=options,
|
||||
responders=[interaction.user]
|
||||
)
|
||||
logger.info(f'new lineup: {human_bat_lineup}')
|
||||
self.session.add(human_bat_lineup)
|
||||
# self.session.commit()
|
||||
|
||||
try:
|
||||
logger.info(f'Inserted {human_bat_lineup.card.player.name_with_desc} in the {self.batting_order} spot')
|
||||
this_play.batter = human_bat_lineup
|
||||
this_play.batter_pos = position
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True, stack_info=True)
|
||||
|
||||
self.session.add(this_play)
|
||||
|
||||
self.session.commit()
|
||||
logger.info(f'view: {view}')
|
||||
logger.info(f'SelectSubPosition view is ready')
|
||||
|
||||
logger.info(f'Posting final sub message')
|
||||
this_content = f'{human_batter_card.player.name_with_desc} has entered in the {self.batting_order} spot. {pos_text}'
|
||||
await interaction.edit_original_response(
|
||||
content=f'{human_batter_card.player.name_with_desc} has entered in the {self.batting_order} spot. {pos_text}',
|
||||
content=this_content,
|
||||
view=view
|
||||
)
|
||||
await interaction.channel.send(content='If you have additional subs to make, run `/substitution` again or run `/gamestate` to see the current plate appearance.')
|
||||
|
||||
|
||||
class SelectPokemonEvolution(discord.ui.Select):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user