Added RosterLinks to remove card_id from setup process

Add SelectStartingPitcher dropdown
New .sync function
This commit is contained in:
Cal Corum 2024-11-23 19:53:48 -06:00
parent a4af7652fc
commit 073bd04b4b
10 changed files with 371 additions and 135 deletions

View File

@ -3,23 +3,27 @@ from typing import Literal
import discord
from discord import app_commands
from discord import SelectOption
from discord.app_commands import Choice
from discord.ext import commands, tasks
import pygsheets
from sqlmodel import or_
from api_calls import db_get
from command_logic.logic_gameplay import advance_runners, bunts, chaos, complete_game, doubles, flyballs, get_lineups_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, hit_by_pitch, homeruns, is_game_over, manual_end_game, popouts, show_defense_cards, singles, strikeouts, triples, undo_play, update_game_settings, walks
from command_logic.logic_gameplay import advance_runners, bunts, chaos, complete_game, doubles, flyballs, get_full_roster_from_sheets, get_lineups_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, hit_by_pitch, homeruns, is_game_over, manual_end_game, popouts, read_lineup, show_defense_cards, singles, starting_pitcher_dropdown_view, strikeouts, triples, undo_play, update_game_settings, walks
from dice import ab_roll
from exceptions import GameNotFoundException, TeamNotFoundException, PlayNotFoundException, GameException, log_exception
from exceptions import GameNotFoundException, GoogleSheetsException, TeamNotFoundException, PlayNotFoundException, GameException, log_exception
from helpers import DEFENSE_LITERAL, PD_PLAYERS_ROLE_NAME, get_channel, team_role, user_has_role, random_gif, random_from_list
# from in_game import ai_manager
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.gameplay_models import Lineup, Play, Session, engine, player_description, select, Game
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 in_game.gameplay_queries import get_and_cache_position, get_available_pitchers, 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, ScorebugButtons, ask_confirm
from utilities.dropdown import DropdownView, SelectStartingPitcher
CLASSIC_EMBED = True
@ -122,19 +126,22 @@ class Gameplay(commands.Cog):
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')
@app_commands.describe(
sp_card_id='Light gray number to the left of the pitcher\'s name on your depth chart'
@app_commands.choices(
league=[
Choice(value='minor-league', name='Minor League'),
Choice(value='flashback', name='Flashback'),
Choice(value='major-league', name='Major League'),
Choice(value='hall-of-fame', name='Hall of Fame')
],
roster=[
Choice(value='1', name='Primary'),
Choice(value='2', name='Secondary'),
Choice(value='3', name='Ranked')
]
)
@app_commands.choices(league=[
Choice(value='minor-league', name='Minor League'),
Choice(value='flashback', name='Flashback'),
Choice(value='major-league', name='Major League'),
Choice(value='hall-of-fame', name='Hall of Fame')
])
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def new_game_mlb_campaign_command(
self, interaction: discord.Interaction, league: Choice[str], away_team_abbrev: str, home_team_abbrev: str, sp_card_id: int
):
self, interaction: discord.Interaction, league: Choice[str], away_team_abbrev: str, home_team_abbrev: str, roster: Choice[str]):
await interaction.response.defer()
with Session(engine) as session:
@ -212,6 +219,8 @@ class Gameplay(commands.Cog):
this_game = Game(
away_team_id=away_team.id,
home_team_id=home_team.id,
away_roster_id=69 if away_team.is_ai else int(roster.value),
home_roster_id=69 if home_team.is_ai else int(roster.value),
channel_id=interaction.channel_id,
season=current['season'],
week=week_num,
@ -223,44 +232,9 @@ class Gameplay(commands.Cog):
game_info_log = f'{league.name} game between {away_team.description} and {home_team.description} / first message: {this_game.first_message}'
logger.info(game_info_log)
# Get Human SP card
human_sp_card = await get_card_or_none(session, card_id=sp_card_id)
if human_sp_card is None:
await interaction.channel.send(
f'Uh oh. I can\'t find a card with ID {sp_card_id}. Will you double check that before we get started?'
)
return
if human_sp_card.team_id != human_team.id:
logger.error(f'Card_id {sp_card_id} does not belong to {human_team.abbrev} in Game {this_game.id}')
await interaction.channel.send(
f'Uh oh. Card ID {sp_card_id} is {human_sp_card.player.name} and belongs to {human_sp_card.team.sname}. Will you double check that before we get started?'
)
return
await get_and_cache_position(session, human_sp_card, 'P')
legal_data = await legal_check([sp_card_id], difficulty_name=league.value)
if not legal_data['legal']:
await interaction.edit_original_response(
content=f'It looks like this is a Ranked Legal game and {human_sp_card.player.name_with_desc} is not legal in {league.name} games. You can start a new game once you pick a new SP.'
)
return
human_sp_lineup = Lineup(
team_id=human_team.id,
player_id=human_sp_card.player.id,
card_id=sp_card_id,
position='P',
batting_order=10,
is_fatigued=False,
game=this_game
)
# session.add(human_sp_lineup)
# Get AI SP
await interaction.edit_original_response(
content=f'{ai_team.gmname} is looking for a SP to counter {human_sp_card.player.name}...'
content=f'{ai_team.gmname} is looking for a Starting Pitcher...'
)
ai_sp_lineup = await get_starting_pitcher(
session,
@ -282,9 +256,17 @@ class Gameplay(commands.Cog):
team=ai_team,
game=this_game,
league_name=league.value,
sp_name=human_sp_card.player.name
sp_name=ai_sp_lineup.player.name
)
# Check for last game settings
g_query = session.exec(select(Game).where(or_(Game.home_team == human_team, Game.away_team == human_team)).order_by(Game.id.desc()).limit(1)).all()
if len(g_query) > 0:
last_game = g_query[0]
this_game.auto_roll = last_game.auto_roll
this_game.roll_buttons = last_game.roll_buttons
# Commit game and lineups
session.add(this_game)
session.commit()
@ -306,6 +288,13 @@ class Gameplay(commands.Cog):
value=this_game.team_lineup(session, ai_team)
)
# Get pitchers from rosterlinks
done = await get_full_roster_from_sheets(session, interaction, self.sheets, this_game, human_team, roster.value)
logger.info(f'done: {done}')
if done:
sp_view = starting_pitcher_dropdown_view(session, this_game, human_team)
await interaction.channel.send(content=f'### {human_team.lname} Starting Pitcher', view=sp_view)
await final_message.edit(
content=f'{away_role.mention} @ {home_role.mention} is set!\n\n'
f'Go ahead and set lineups with the `/read-lineup` command!',
@ -347,22 +336,16 @@ class Gameplay(commands.Cog):
@app_commands.command(name='read-lineup', description='Import a saved lineup for this channel\'s PD game.')
@app_commands.describe(
roster='Which roster to pull from your sheet?',
lineup='Which handedness lineup are you using?'
)
@app_commands.choices(
roster=[
Choice(value='1', name='Primary'),
Choice(value='2', name='Secondary'),
Choice(value='3', name='Ranked')
],
lineup=[
Choice(value='1', name='v Right'),
Choice(value='2', name='v Left')
]
)
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def read_lineup_command(self, interaction: discord.Interaction, roster: Choice[str], lineup: Choice[str]):
async def read_lineup_command(self, interaction: discord.Interaction, lineup: Choice[str]):
await interaction.response.defer()
with Session(engine) as session:
@ -373,48 +356,23 @@ class Gameplay(commands.Cog):
)
return
lineup_team = this_game.away_team if this_game.ai_team == 'home' else this_game.home_team
if interaction.user.id != lineup_team.gmid:
this_team = this_game.away_team if this_game.ai_team == 'home' else this_game.home_team
if interaction.user.id != this_team.gmid:
logger.info(f'{interaction.user.name} tried to run a command in Game {this_game.id} when they aren\'t a GM in the game.')
await interaction.edit_original_response(content='Bruh. Only GMs of the active teams can pull lineups.')
return
existing_lineups = get_game_lineups(
session=session,
this_play = await read_lineup(
session,
interaction,
this_game=this_game,
specific_team=lineup_team,
is_active=True
lineup_team=this_team,
sheets_auth=self.sheets,
lineup=lineup,
league_name=this_game.game_type
)
if len(existing_lineups) > 1:
await interaction.edit_original_response(
f'It looks like the {lineup_team.sname} already have a lineup. Run `/substitution` to make changes.'
)
return
await interaction.edit_original_response(content='Okay, let\'s put this lineup card together...')
if this_game.away_team == lineup_team:
this_game.away_roster_id = int(roster.value)
else:
this_game.home_roster_id = int(roster.value)
session.add(this_game)
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:
session.add(batter)
session.commit()
for batter in human_lineups:
if batter.position != 'DH':
await get_and_cache_position(session, batter.card, batter.position)
this_play = this_game.initialize_play(session)
await self.post_play(session, interaction, this_play)
if this_play is not None:
await self.post_play(session, interaction, this_play)
@app_commands.command(name='gamestate', description='Post the current game state')
async def gamestate_command(self, interaction: discord.Interaction, include_lineups: bool = False):
@ -585,7 +543,6 @@ class Gameplay(commands.Cog):
await self.complete_and_post_play(session, interaction, this_play)
@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']):
with Session(engine) as session:

View File

@ -92,47 +92,46 @@ class Owner(commands.Cog):
@commands.is_owner()
async def sync(self, ctx: Context, guilds: Greedy[Object], spec: Optional[Literal['~', "*", '!']] = None) -> None:
"""
!sync -> global sync
!sync ~ -> sync current guild
!sync * -> copies all global app commands to current guild and syncs
!sync id_1 id_2 -> syncs guilds with id 1 and 2
!sync
This takes all global commands within the CommandTree and sends them to Discord. (see CommandTree for more info.)
!sync ~
This will sync all guild commands for the current contexts guild.
!sync *
This command copies all global commands to the current guild (within the CommandTree) and syncs.
!sync ^
This command will remove all guild commands from the CommandTree and syncs, which effectively removes all commands from the guild.
!sync 123 456 789
This command will sync the 3 guild ids we passed: 123, 456 and 789. Only their guilds and guild-bound commands.
"""
logger.info(f'{ctx.author.name} has initiated a sync from guild ID {ctx.guild.id}')
if not guilds:
if spec == "~":
fmt = await ctx.bot.tree.sync(guild=ctx.guild)
synced = await ctx.bot.tree.sync(guild=ctx.guild)
elif spec == "*":
ctx.bot.tree.copy_global_to(guild=ctx.guild)
fmt = await ctx.bot.tree.sync(guild=ctx.guild)
elif spec == '!':
fmt = await ctx.bot.tree.sync()
await ctx.send(f'Synced {len(fmt)} commands globally.')
if len(fmt) > 0:
ctx.bot.tree.copy_global_to(guild=ctx.guild)
fmt = await ctx.bot.tree.sync(guild=ctx.guild)
await ctx.send(f'Synced global commands to this guild.')
ctx.bot.tree.clear_commands(guild=ctx.guild)
await ctx.send(f'Cleared all local commands.')
synced = await ctx.bot.tree.sync(guild=ctx.guild)
elif spec == "^":
ctx.bot.tree.clear_commands(guild=ctx.guild)
await ctx.bot.tree.sync(guild=ctx.guild)
synced = []
else:
fmt = await ctx.bot.tree.sync()
synced = await ctx.bot.tree.sync()
await ctx.send(
f"Synced {len(fmt)} commands {'globally' if spec is None else 'to the current guild.'}"
f"Synced {len(synced)} commands {'globally' if spec is None else 'to the current guild.'}"
)
return
fmt = 0
ret = 0
for guild in guilds:
try:
await ctx.bot.tree.sync(guild=guild)
except discord.HTTPException:
pass
else:
fmt += 1
ret += 1
await ctx.send(f"Synced the tree to {fmt}/{len(guilds)} guilds.")
await ctx.send(f"Synced the tree to {ret}/{len(guilds)}.")
async def setup(bot):

View File

@ -2,6 +2,8 @@
import asyncio
import logging
import discord
from discord import SelectOption
from discord.app_commands import Choice
import pandas as pd
from sqlmodel import Session, select, func
from sqlalchemy import delete
@ -11,10 +13,10 @@ from api_calls import db_delete, db_get, db_post
from exceptions import *
from helpers import DEFENSE_LITERAL, SBA_COLOR, get_channel
from in_game.game_helpers import legal_check
from in_game.gameplay_models import BattingCard, Game, Lineup, PositionRating, Team, Play
from in_game.gameplay_queries import get_and_cache_position, get_card_or_none, get_channel_game_or_none, get_db_ready_decisions, get_db_ready_plays, get_last_team_play, get_one_lineup, get_player_id_from_dict, get_player_name_from_dict, get_player_or_none, get_sorted_lineups, get_team_or_none, get_players_last_pa, post_game_rewards
from in_game.gameplay_models import BattingCard, Game, Lineup, PositionRating, RosterLink, Team, Play
from in_game.gameplay_queries import get_and_cache_position, get_available_pitchers, get_card_or_none, get_channel_game_or_none, get_db_ready_decisions, get_db_ready_plays, get_game_lineups, get_last_team_play, get_one_lineup, get_player_id_from_dict, get_player_name_from_dict, get_player_or_none, get_sorted_lineups, get_team_or_none, get_players_last_pa, post_game_rewards
from utilities.buttons import ButtonOptions, Confirm, ask_confirm
from utilities.dropdown import DropdownView, SelectViewDefense
from utilities.dropdown import DropdownView, SelectStartingPitcher, SelectViewDefense
from utilities.embeds import image_embed
from utilities.pages import Pagination
@ -226,6 +228,63 @@ async def get_scorebug_embed(session: Session, this_game: Game, full_length: boo
return embed
def starting_pitcher_dropdown_view(session: Session, this_game: Game, human_team: Team):
pitchers = get_available_pitchers(session, this_game, human_team, sort='starter-desc')
logger.info(f'sorted pitchers: {pitchers}')
sp_selection = SelectStartingPitcher(
this_game=this_game,
this_team=human_team,
session=session,
league_name=this_game.game_type,
options=[SelectOption(label=f'{x.player.name_with_desc} (S{x.pitcherscouting.pitchingcard.starter_rating}/R{x.pitcherscouting.pitchingcard.relief_rating})', value=x.id) for x in pitchers],
placeholder='Select your starting pitcher'
)
return DropdownView(dropdown_objects=[sp_selection])
async def read_lineup(session: Session, interaction: discord.Interaction, this_game: Game, lineup_team: Team, sheets_auth, lineup: Choice[str], league_name: str):
"""
Commits lineups and rosterlinks
"""
existing_lineups = get_game_lineups(
session=session,
this_game=this_game,
specific_team=lineup_team,
is_active=True
)
if len(existing_lineups) > 1:
await interaction.edit_original_response(
f'It looks like the {lineup_team.sname} already have a lineup. Run `/substitution` to make changes.'
)
return
await interaction.edit_original_response(content='Okay, let\'s put this lineup card together...')
session.add(this_game)
human_lineups = await get_lineups_from_sheets(session, sheets_auth, this_game, this_team=lineup_team, lineup_num=lineup.name, roster_num=this_game.away_roster_id if this_game.is_ai else this_game.home_roster_id)
await interaction.edit_original_response(content='Heard from sheets, pulling in scouting data...')
legal_data = await legal_check([lineup.card.id for lineup in human_lineups], difficulty_name=league_name)
if not legal_data['legal']:
await interaction.edit_original_response(
content=f'It looks like this is a Ranked Legal game and {legal_data["error_string"]} is not legal in {league_name} games. You can start a new game once you update this lineup.'
)
return None
for batter in human_lineups:
session.add(batter)
session.commit()
for batter in human_lineups:
if batter.position != 'DH':
await get_and_cache_position(session, batter.card, batter.position)
return this_game.initialize_play(session)
def get_obc(on_first = None, on_second = None, on_third = None) -> int:
if on_third is not None:
if on_second is not None:
@ -415,7 +474,7 @@ def complete_play(session:Session, this_play: Play):
async def get_lineups_from_sheets(session: Session, sheets, this_game: Game, this_team: Team, lineup_num: int, roster_num: int) -> list[Lineup]:
logger.debug(f'sheets: {sheets}')
logger.debug(f'get_lineups_from_sheets - sheets: {sheets}')
this_sheet = sheets.open_by_key(this_team.gsheet)
logger.debug(f'this_sheet: {this_sheet}')
@ -446,7 +505,7 @@ async def get_lineups_from_sheets(session: Session, sheets, this_game: Game, thi
logger.debug(f'lineup_cells: {lineup_cells}')
except ValueError as e:
logger.error(f'Could not pull roster for {this_team.abbrev}: {e}')
raise ValueError(f'Uh oh. Looks like your roster might not be saved. I am reading blanks when I try to get the card IDs')
log_exception(GoogleSheetsException, f'Uh oh. Looks like your lineup might not be saved. I am reading blanks when I try to get the card IDs')
all_lineups = []
all_pos = []
@ -491,10 +550,60 @@ async def get_lineups_from_sheets(session: Session, sheets, this_game: Game, thi
return all_lineups
async def get_full_roster_from_sheets(session: Session, interaction: discord.Interaction, sheets, this_game: Game, this_team: Team, roster_num: int) -> list[RosterLink]:
"""
Commits roster links
"""
logger.debug(f'get_full_roster_from_sheets - sheets: {sheets}')
this_sheet = sheets.open_by_key(this_team.gsheet)
this_sheet = sheets.open_by_key(this_team.gsheet)
logger.debug(f'this_sheet: {this_sheet}')
r_sheet = this_sheet.worksheet_by_title('My Rosters')
logger.debug(f'r_sheet: {r_sheet}')
if roster_num == 1:
l_range = 'B3:B28'
elif roster_num == 2:
l_range = 'B29:B54'
else:
l_range = 'B55:B80'
roster_message = await interaction.channel.send(content='I\'m diving into Sheets - wish me luck.')
logger.info(f'l_range: {l_range}')
raw_cells = r_sheet.range(l_range)
logger.info(f'raw_cells: {raw_cells}')
await roster_message.edit(content='Got your roster, now to find these cards in your collection...')
try:
card_ids = [row[0].value for row in raw_cells]
logger.info(f'card_ids: {card_ids}')
except ValueError as e:
logger.error(f'Could not pull roster for {this_team.abbrev}: {e}')
log_exception(GoogleSheetsException, f'Uh oh. Looks like your roster might not be saved. I am reading blanks when I try to get the card IDs')
for x in card_ids:
this_card = await get_card_or_none(session, card_id=x)
session.add(RosterLink(
game=this_game,
card=this_card,
team=this_team
))
session.commit()
await roster_message.edit(content='Your roster is logged and scouting data is available.')
return session.exec(select(RosterLink).where(RosterLink.game == this_game, RosterLink.team == this_team)).all()
async def checks_log_interaction(session: Session, interaction: discord.Interaction, command_name: str) -> tuple[Game, Team, Play]:
"""
Commits this_play
"""
logger.info(f'log interaction checks for {interaction.user.name} in channel {interaction.channel.name}')
await interaction.response.defer(thinking=True)
this_game = get_channel_game_or_none(session, interaction.channel_id)
if this_game is None:

View File

@ -31,6 +31,10 @@ class CardLegalityException(GameException):
pass
class CardNotFoundException(GameException):
pass
class GameNotFoundException(GameException):
pass
@ -65,3 +69,7 @@ class MultipleHumanTeamsException(GameException):
class NoHumanTeamsException(GameException):
pass
class GoogleSheetsException(GameException):
pass

View File

@ -47,6 +47,16 @@ class GameCardsetLink(SQLModel, table=True):
cardset: 'Cardset' = Relationship(back_populates='game_links')
class RosterLink(SQLModel, table=True):
game_id: int | None = Field(default=None, foreign_key='game.id', primary_key=True)
card_id: int | None = Field(default=None, foreign_key='card.id', primary_key=True)
team_id: int = Field(index=True, foreign_key='team.id')
game: 'Game' = Relationship(back_populates='roster_links')
card: 'Card' = Relationship()
team: 'Team' = Relationship()
class TeamBase(SQLModel):
id: int = Field(primary_key=True)
abbrev: str = Field(index=True)
@ -110,6 +120,7 @@ class Game(SQLModel, table=True):
auto_roll: bool | None = Field(default=False)
cardset_links: list[GameCardsetLink] = Relationship(back_populates='game', cascade_delete=True)
roster_links: list[RosterLink] = Relationship(back_populates='game', cascade_delete=True)
away_team: Team = Relationship(
# back_populates='away_games',
# sa_relationship_kwargs={

View File

@ -6,7 +6,8 @@ from typing import Literal
import pydantic
from sqlalchemy import func
from api_calls import db_get, db_post
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 sqlmodel import col
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, RosterLink, Session, Team, TeamBase, select, or_, Game, Play
from exceptions import DatabaseError, PositionNotFoundException, log_exception, PlayNotFoundException
@ -797,3 +798,36 @@ async def post_game_rewards(session: Session, winning_team: Team, losing_team: T
await db_post(f'teams/{losing_team.id}/money/{loss_reward["money"]}')
return win_string, loss_string
def get_available_subs(session: Session, this_game: Game, this_team: Team) -> list[Card]:
team_lineups = session.exec(select(Lineup).where(Lineup.game == this_game, Lineup.team == this_team)).all()
used_card_ids = [x.card.id for x in team_lineups]
all_roster_links = session.exec(select(RosterLink).where(RosterLink.game == this_game, RosterLink.team == this_team)).all()
return [x.card for x in all_roster_links if x.card_id not in used_card_ids]
def get_available_pitchers(session: Session, this_game: Game, this_team: Team, sort: Literal['starter-desc', 'closer-desc'] = 'closer-desc') -> list[Card]:
logger.info(f'getting available pitchers for team {this_team.id} in game {this_game.id}')
all_subs = get_available_subs(session, this_game, this_team)
logger.info(f'all_subs: {all_subs}')
pitchers = [x for x in all_subs if x.pitcherscouting is not None]
logger.info(f'pitchers: {pitchers}')
def sort_by_pow(this_card: Card):
s_pow = this_card.pitcherscouting.pitchingcard.starter_rating
r_pow = this_card.pitcherscouting.pitchingcard.relief_rating
c_pow = this_card.pitcherscouting.pitchingcard.closer_rating
if sort == 'starter-desc':
r_val = (s_pow * 3) + r_pow
else:
r_val = (c_pow * 10) - (r_pow * 5) - (s_pow * 3)
return r_val
pitchers.sort(key=sort_by_pow, reverse=True)
return pitchers

View File

@ -1,10 +1,10 @@
import datetime
import pytest
from sqlmodel import Session, SQLModel, create_engine
from sqlmodel import Session, SQLModel, create_engine, select
from sqlmodel.pool import StaticPool
from typing import Literal
from in_game.gameplay_models import BatterScouting, BattingCard, BattingRatings, Card, Cardset, Game, GameCardsetLink, Lineup, ManagerAi, PitcherScouting, PitchingCard, PitchingRatings, Play, Team, Player
from in_game.gameplay_models import BatterScouting, BattingCard, BattingRatings, Card, Cardset, Game, GameCardsetLink, Lineup, ManagerAi, PitcherScouting, PitchingCard, PitchingRatings, Play, RosterLink, Team, Player
@pytest.fixture(name='session')
@ -231,6 +231,30 @@ def session_fixture():
session.commit()
g1_t1_cards = session.exec(select(Card).where(Card.team_id == 31)).all()
g1_t2_cards = session.exec(select(Card).where(Card.team_id == 400)).all()
g2_t1_cards = session.exec(select(Card).where(Card.team_id == 69)).all()
g2_t2_cards = session.exec(select(Card).where(Card.team_id == 420)).all()
for card in [*g1_t1_cards, *g1_t2_cards]:
session.add(RosterLink(
game_id=1,
card_id=card.id,
team_id=card.team_id
))
for card in [*g2_t1_cards, *g2_t2_cards]:
session.add(RosterLink(
game_id=3,
card_id=card.id,
team_id=card.team_id
))
# session.add(RosterLink(
# game_id=3,
# card_id=12,
# team_id=420
# ))
session.commit()
all_ai = ManagerAi.create_ai(session)
yield session

View File

@ -109,19 +109,19 @@ def test_delete_game(session: Session):
assert len(bad_links) == 0
async def test_get_scorebug(session: Session):
game_1 = session.get(Game, 1)
# scorebug = game_1.get_scorebug_embed(session)
scorebug = await get_scorebug_embed(session, game_1)
# async def test_get_scorebug(session: Session):
# game_1 = session.get(Game, 1)
# # scorebug = game_1.get_scorebug_embed(session)
# scorebug = await get_scorebug_embed(session, game_1)
assert scorebug.title == 'CornBelters @ Black Bears - Minor League'
assert scorebug.color.value == int('a6ce39', 16)
# assert scorebug.title == 'CornBelters @ Black Bears - Minor League'
# assert scorebug.color.value == int('a6ce39', 16)
game_3 = session.get(Game, 3)
# scorebug = game_3.get_scorebug_embed(session)
scorebug = await get_scorebug_embed(session, game_3)
# game_3 = session.get(Game, 3)
# # scorebug = game_3.get_scorebug_embed(session)
# scorebug = await get_scorebug_embed(session, game_3)
assert '0 Outs' in scorebug.fields[0].value
# assert '0 Outs' in scorebug.fields[0].value
def test_sum_function(session: Session):

View File

@ -0,0 +1,32 @@
import pytest
from sqlmodel import Session, select, func
from in_game.gameplay_models import Game, RosterLink
from in_game.gameplay_queries import get_available_subs
from tests.factory import session_fixture
def test_get_rosterlinks(session: Session):
game_1 = session.get(Game, 1)
g1_links = session.exec(select(RosterLink).where(RosterLink.game == game_1)).all()
assert len(g1_links) == 20
home_team = game_1.home_team
home_roster = session.exec(select(RosterLink).where(RosterLink.game == game_1, RosterLink.team == home_team)).all()
assert len(home_roster) == 10
def test_get_available_subs(session: Session):
this_game = session.get(Game, 3)
home_team = this_game.home_team
home_roster = session.exec(select(RosterLink).where(RosterLink.game == this_game, RosterLink.team == home_team)).all()
assert len(home_roster) == 11
cards = get_available_subs(session, this_game, this_game.home_team)
assert len(cards) == 1

View File

@ -1,9 +1,15 @@
from typing import List
import discord
import logging
from discord import SelectOption
from discord.utils import MISSING
from sqlmodel import Session
from in_game.gameplay_models import Lineup, Play
from exceptions import CardNotFoundException, log_exception
from in_game.game_helpers import legal_check
from in_game.gameplay_models import Game, Lineup, Play, Team
from in_game.gameplay_queries import get_and_cache_position, get_card_or_none
logger = logging.getLogger('discord_app')
@ -84,3 +90,59 @@ class SelectViewDefense(discord.ui.Select):
)
await interaction.response.edit_message(content=None, embed=self.embed, view=new_view)
class SelectStartingPitcher(discord.ui.Select):
def __init__(self, this_game: Game, this_team: Team, session: Session, league_name: str, custom_id: str = MISSING, placeholder: str | None = None, options: List[SelectOption] = ...) -> None:
logger.info(f'Inside SelectStartingPitcher init function')
self.game = this_game
self.team = this_team
self.session = session
self.league_name = league_name
super().__init__(custom_id=custom_id, placeholder=placeholder, options=options)
async def callback(self, interaction: discord.Interaction):
logger.info(f'SelectStartingPitcher - selection: {self.values[0]}')
# Get Human SP card
human_sp_card = await get_card_or_none(self.session, card_id=self.values[0])
if human_sp_card is None:
log_exception(CardNotFoundException, f'Card ID {self.values[0]} not found')
if human_sp_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_sp_card.player.name} and belongs to {human_sp_card.team.sname}. Will you double check that before we get started?'
)
return
await get_and_cache_position(self.session, human_sp_card, 'P')
legal_data = await legal_check([self.values[0]], difficulty_name=self.league_name)
if not legal_data['legal']:
await interaction.edit_original_response(
content=f'It looks like this is a Ranked Legal game and {human_sp_card.player.name_with_desc} is not legal in {self.league_name} games. You can start a new game once you pick a new SP.'
)
return
human_sp_lineup = Lineup(
team_id=self.team.id,
player_id=human_sp_card.player.id,
card_id=self.values[0],
position='P',
batting_order=10,
is_fatigued=False,
game=self.game
)
self.session.add(human_sp_lineup)
self.session.commit()
await interaction.response.edit_message(
content=f'The {self.team.lname} are starting {human_sp_card.player.name_with_desc}',
view=None
)