Added Team table with caching and tests

This commit is contained in:
Cal Corum 2024-10-12 02:08:05 -05:00
parent aa66008577
commit 0deb547257
11 changed files with 208 additions and 20 deletions

View File

@ -7,7 +7,8 @@ from discord.ext import commands
from helpers import PD_PLAYERS_ROLE_NAME from helpers import PD_PLAYERS_ROLE_NAME
from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME
from in_game.gameplay_db import Session, engine, select, Game from in_game.data_cache import get_pd_team
from in_game.gameplay_db import Session, engine, create_db_and_tables, select, Game
def get_games_by_channel(session: Session, channel_id: int): def get_games_by_channel(session: Session, channel_id: int):
@ -17,6 +18,7 @@ def get_games_by_channel(session: Session, channel_id: int):
class Gameplay(commands.Cog): class Gameplay(commands.Cog):
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
create_db_and_tables()
async def cog_command_error(self, ctx, error): async def cog_command_error(self, ctx, error):
await ctx.send(f'{error}\n\nRun !help <command_name> to see the command requirements') await ctx.send(f'{error}\n\nRun !help <command_name> to see the command requirements')
@ -27,13 +29,16 @@ class Gameplay(commands.Cog):
group_new_game = app_commands.Group(name='new-game', description='Start a new baseball game') group_new_game = app_commands.Group(name='new-game', description='Start a new baseball game')
@group_new_game.command(name='mlb-campaign', description='Start a new MLB campaign game against an AI') @group_new_game.command(name='mlb-campaign', description='Start a new MLB campaign game against an AI')
@app_commands.rename( @app_commands.describe(
league='Campaign', sp_card_id='Light gray number to the left of the pitcher\'s name on your depth chart'
away_team_abbrev='Away Team Abbrev',
home_team_abbrev='Home Team Abbrev',
sp_card_id='SP Card ID',
num_innings='Number of Innings'
) )
# @app_commands.rename(
# league='campaign',
# away_team_abbrev='away team abbrev',
# home_team_abbrev='home team abbrev',
# sp_card_id='sp card id',
# num_innings='number of innings'
# )
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME) @app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def new_game_mlb_campaign_command( async def new_game_mlb_campaign_command(
self, interaction: discord.Interaction, self, interaction: discord.Interaction,
@ -49,14 +54,23 @@ class Gameplay(commands.Cog):
content=f'Ope. There is already a game going on in this channel. Please wait for it to complete ' content=f'Ope. There is already a game going on in this channel. Please wait for it to complete '
f'before starting a new one.' f'before starting a new one.'
) )
return
if interaction.channel.category is None or interaction.channel.category.name != PUBLIC_FIELDS_CATEGORY_NAME: if interaction.channel.category is None or interaction.channel.category.name != PUBLIC_FIELDS_CATEGORY_NAME:
await interaction.response.send_message( await interaction.edit_original_response(
f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything ' content=f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything '
f'pops up?' f'pops up?'
) )
return return
away_team = await get_pd_team(away_team_abbrev)
await interaction.edit_original_response(
content=f'This channel is ripe for the picking!'
)
async def setup(bot): async def setup(bot):
await bot.add_cog(Gameplay(bot)) await bot.add_cog(Gameplay(bot))

View File

@ -101,6 +101,7 @@ class Owner(commands.Cog):
!sync * -> copies all global app commands to current guild and syncs !sync * -> copies all global app commands to current guild and syncs
!sync id_1 id_2 -> syncs guilds with id 1 and 2 !sync id_1 id_2 -> syncs guilds with id 1 and 2
""" """
logging.info(f'{ctx.author.name} has initiated a sync from guild ID {ctx.guild.id}')
if not guilds: if not guilds:
if spec == "~": if spec == "~":
fmt = await ctx.bot.tree.sync(guild=ctx.guild) fmt = await ctx.bot.tree.sync(guild=ctx.guild)

View File

@ -9,7 +9,7 @@ import os
AUTH_TOKEN = {'Authorization': f'Bearer {os.environ.get("API_TOKEN")}'} AUTH_TOKEN = {'Authorization': f'Bearer {os.environ.get("API_TOKEN")}'}
DB_URL = 'https://pd.manticorum.com/api' DB_URL = 'https://pd.manticorum.com/api'
master_debug = True master_debug = True
alt_database = False alt_database = 'dev'
PLAYER_CACHE = {} PLAYER_CACHE = {}
if alt_database == 'dev': if alt_database == 'dev':

View File

@ -16,7 +16,7 @@ from db_calls import db_get
from in_game.data_cache import get_pd_player, CardPosition, BattingCard, get_pd_team from in_game.data_cache import get_pd_player, CardPosition, BattingCard, get_pd_team
db = SqliteDatabase( db = SqliteDatabase(
'storage/gameplay.db', 'storage/gameplay-legacy.db',
pragmas={ pragmas={
'journal_mode': 'wal', 'journal_mode': 'wal',
'cache_size': -1 * 64000, 'cache_size': -1 * 64000,

View File

@ -4,10 +4,10 @@ import random
import discord import discord
# from db_calls_gameplay import StratGame, StratPlay, StratLineup, StratManagerAi, patch_play, advance_runners, \ from db_calls_gameplay import StratGame, StratPlay, StratLineup, StratManagerAi, patch_play, advance_runners, \
# complete_play, get_team_lineups, get_or_create_bullpen, get_player, get_sheets, make_sub, get_one_lineup, \ complete_play, get_team_lineups, get_or_create_bullpen, get_player, get_sheets, make_sub, get_one_lineup, \
# advance_one_runner, get_one_lineup, ai_batting, get_manager advance_one_runner, get_one_lineup, ai_batting, get_manager
# from db_calls import db_get, db_post from db_calls import db_get, db_post
from helpers import Pagination, get_team_embed, image_embed, Confirm from helpers import Pagination, get_team_embed, image_embed, Confirm
from typing import Literal, Optional from typing import Literal, Optional

View File

@ -1,8 +1,12 @@
import datetime import datetime
import logging
from sqlmodel import Session, SQLModel, create_engine, select, Field, Relationship from sqlmodel import Session, SQLModel, create_engine, select, Field, Relationship
from db_calls import db_get
from helpers import PD_SEASON
sqlite_url = 'sqlite:///./storage/gameplay.db'
sqlite_url = 'sqlite:///storage/gameplay.db'
connect_args = {"check_same_thread": False} connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, echo=False, connect_args=connect_args) engine = create_engine(sqlite_url, echo=False, connect_args=connect_args)
@ -64,6 +68,81 @@ class Lineup(SQLModel, table=True):
game_id: int = Field(foreign_key='game.id', index=True) game_id: int = Field(foreign_key='game.id', index=True)
game: Game = Relationship(back_populates='lineups') game: Game = Relationship(back_populates='lineups')
class Team(SQLModel, table=True):
id: int = Field(primary_key=True)
abbrev: str = Field(index=True)
sname: str
lname: str
gmid: int = Field(index=True)
gmname: str
gsheet: str
wallet: int
team_value: int
collection_value: int
logo: str | None = Field(default=None)
color: str
season: int
career: int
ranking: int
has_guide: bool
is_ai: bool
created: datetime.datetime | None = Field(default=datetime.datetime.now())
async def get_team(
session: Session, team_id: int | None = None, gm_id: int | None = None, team_abbrev: str | None = None, skip_cache: bool = False) -> Team:
if team_id is None and gm_id is None and team_abbrev is None:
err = 'One of "team_id", "gm_id", or "team_abbrev" must be included in search'
logging.error(f'gameplay_db - get_team - {err}')
raise TypeError(err)
if not skip_cache:
if team_id is not None:
this_team = session.get(Team, team_id)
else:
if gm_id is not None:
statement = select(Team).where(Team.gmid == gm_id)
else:
statement = select(Team).where(Team.abbrev.lower() == team_abbrev.lower())
this_team = session.exec(statement).one_or_none
if this_team is not None:
tdelta = datetime.datetime.now() - this_team.created
if tdelta.total_seconds() < 1209600:
return this_team
else:
session.delete(this_team)
session.commit()
def cache_team(json_data: dict) -> Team:
db_team = Team.model_validate(t_query)
session.add(db_team)
session.commit()
session.refresh(db_team)
return db_team
if team_id is not None:
t_query = await db_get('teams', object_id=team_id, params=[('inc_packs', False)])
if t_query is not None:
return cache_team(t_query)
elif gm_id is not None:
t_query = await db_get('teams', params=[('season', PD_SEASON), ('gm_id', gm_id)])
if t_query['count'] != 0:
for team in [x for x in t_query['teams'] if 'gauntlet' not in x.abbrev.lower()]:
return cache_team(team)
elif team_abbrev is not None:
t_query = await db_get('teams', params=[('season', PD_SEASON), ('abbrev', team_abbrev)])
if t_query['count'] != 0:
for team in [x for x in t_query['teams'] if 'gauntlet' not in x.abbrev.lower()]:
return cache_team(team)
err = 'Team not found'
logging.error(f'gameplay_db - get_team - {err}')
raise LookupError(err)

View File

@ -1,6 +1,7 @@
import discord import discord
import datetime import datetime
import logging import logging
# import logging.handlers
import asyncio import asyncio
import os import os
@ -18,10 +19,25 @@ else:
date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}' date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}'
logging.basicConfig( logging.basicConfig(
filename=f'logs/discord/{date}.log', filename=f'logs/{date}.log',
format='%(asctime)s - %(levelname)s - %(message)s', format='%(asctime)s - %(levelname)s - %(message)s',
level=log_level level=log_level
) )
# logging.getLogger('discord.http').setLevel(logging.INFO)
# logger = logging.getLogger('discord')
# logger.setLevel(log_level)
# handler = logging.handlers.RotatingFileHandler(
# filename='discord.log',
# encoding='utf-8',
# maxBytes=32 * 1024 * 1024, # 32 MiB
# backupCount=5, # Rotate through 5 files
# )
# dt_fmt = '%Y-%m-%d %H:%M:%S'
# formatter = logging.Formatter('[{asctime}] [{levelname:<8}] {name}: {message}', dt_fmt, style='{')
# handler.setFormatter(formatter)
# logger.addHandler(handler)
COGS = [ COGS = [
'cogs.owner', 'cogs.owner',

2
pytest.ini Normal file
View File

@ -0,0 +1,2 @@
[pytest]
asyncio_mode = auto

View File

@ -1,9 +1,10 @@
import datetime
import pytest import pytest
from sqlmodel import Session, SQLModel, create_engine 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_db import Game, Lineup from in_game.gameplay_db import Game, Lineup, Team
@pytest.fixture(name='session') @pytest.fixture(name='session')
@ -41,4 +42,21 @@ def new_games_with_lineups_fixture():
for (order, pos) in [(1, 'C'), (2, '1B'), (3, '2B'), (4, '3B'), (5, 'SS'), (6, 'LF'), (7, 'CF'), (8, 'RF'), (9, 'DH'), (10, 'P')]: for (order, pos) in [(1, 'C'), (2, '1B'), (3, '2B'), (4, '3B'), (5, 'SS'), (6, 'LF'), (7, 'CF'), (8, 'RF'), (9, 'DH'), (10, 'P')]:
all_lineups.append(Lineup(team_id=team_id, card_id=order, player_id=100+order, position=pos, batting_order=order, game=game_2)) all_lineups.append(Lineup(team_id=team_id, card_id=order, player_id=100+order, position=pos, batting_order=order, game=game_2))
return [game_1, game_2] return [game_1, game_2]
@pytest.fixture(name='new_teams')
def new_teams_fixture():
team_1 = Team(
id=31, abbrev='NCB', sname='CornBelters', lname='Normal CornBelters', gmid=1234, gmname='Cal', gsheet='asdf1234', wallet=6969, team_value=69420, collection_value=169420, color='006900', season=7, event=False, career=1234, ranking=1337, has_guide=True, is_ai=False
)
team_2 = Team(
id=400, abbrev='WV', sname='Black Bears', lname='West Virginia Black Bears', gmid=5678, gmname='Evil Cal', gsheet='https://i.postimg.cc/HjDc8bBF/blackbears-transparent.png', wallet=350, team_value=420, collection_value=169, color='6699FF', season=7, event=False, career=2, ranking=969, has_guide=False, is_ai=False
)
incomplete_team = Team(
id=446, abbrev='CLS', sname='Macho Men', lname='Columbus Macho Men', gmid=181818, gmname='Mason Socc', gsheet='asdf1234', wallet=6969, team_value=69420, collection_value=169420, color='https://i.postimg.cc/8kLZCYXh/S10CLS.png', season=7, event=False, career=0
)
old_cache_team = Team(
id=3, abbrev='BAL', sname='Orioles', lname='Baltimore Orioles', gmid=181818, gmname='Brandon Hyde', gsheet='asdf1234', wallet=6969, team_value=69420, collection_value=169420, color='https://i.postimg.cc/8kLZCYXh/S10CLS.png', season=7, event=False, career=0, ranking=500, has_guide=False, is_ai=False, created=datetime.datetime.today() - datetime.timedelta(days=60))
return [team_1, team_2, incomplete_team, old_cache_team]

View File

@ -1,10 +1,13 @@
from sqlmodel import Session, select from sqlmodel import Session, select
from in_game.gameplay_db import Game, Lineup from in_game.gameplay_db import Game, Lineup
from factory import session_fixture, new_games_with_lineups_fixture from factory import session_fixture, new_games_with_lineups_fixture, new_games_fixture
def test_create_lineup(session: Session, new_games_with_lineups: list[Game]): def test_create_lineup(session: Session, new_games_with_lineups: list[Game]):
"""
This test fails when run with the entire suite
"""
game_1 = new_games_with_lineups[0] game_1 = new_games_with_lineups[0]
game_2 = new_games_with_lineups[1] game_2 = new_games_with_lineups[1]

View File

@ -0,0 +1,55 @@
import datetime
from sqlmodel import Session, select
from sqlite3 import IntegrityError
from in_game.gameplay_db import Team, get_team
from factory import session_fixture, new_teams_fixture, pytest
def test_create_team(session: Session, new_teams: list[Team]):
team_1 = new_teams[0]
team_2 = new_teams[1]
session.add(team_1)
session.add(team_2)
session.commit()
assert team_1.abbrev == 'NCB'
assert team_1.id == 31
assert team_2.abbrev == 'WV'
assert team_2.id == 400
def test_create_incomplete_team(session: Session, new_teams: list[Team]):
team_1 = new_teams[2]
session.add(team_1)
with pytest.raises(Exception) as exc_info:
session.commit()
assert str(exc_info) == "<ExceptionInfo IntegrityError('(sqlite3.IntegrityError) NOT NULL constraint failed: team.ranking') tblen=24>"
async def test_team_cache(session: Session, new_teams: list[Team]):
team_1 = new_teams[0]
team_2 = new_teams[1]
team_3 = new_teams[3]
session.add(team_1)
session.add(team_2)
session.add(team_3)
session.commit()
new_team_1 = await get_team(session, team_id=team_1.id)
new_team_2 = await get_team(session, team_id=team_2.id)
new_team_3 = await get_team(session, team_id=team_3.id)
assert team_1.created == new_team_1.created
assert team_2.created == new_team_2.created
assert (datetime.datetime.now() - team_3.created).total_seconds() > 1209600
assert (datetime.datetime.now() - new_team_3.created).total_seconds() < 1209600