Added RosterLinks to remove card_id from setup process
Add SelectStartingPitcher dropdown New .sync function
This commit is contained in:
parent
a4af7652fc
commit
073bd04b4b
145
cogs/gameplay.py
145
cogs/gameplay.py
@ -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:
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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):
|
||||
|
||||
32
tests/gameplay_models/test_rosterlinks_model.py
Normal file
32
tests/gameplay_models/test_rosterlinks_model.py
Normal 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
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user