/read-lineup logic is complete
This commit is contained in:
parent
428afe048e
commit
46860fb3c0
@ -1,20 +1,20 @@
|
||||
import asyncio
|
||||
import enum
|
||||
import logging
|
||||
from typing import Literal
|
||||
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.app_commands import Choice
|
||||
from discord.ext import commands
|
||||
from discord.ext import commands, tasks
|
||||
import pygsheets
|
||||
|
||||
from api_calls import db_get
|
||||
from command_logic.gameplay import get_lineups_from_sheets
|
||||
from helpers import PD_PLAYERS_ROLE_NAME, team_role, user_has_role, random_gif, random_from_list
|
||||
|
||||
from in_game import ai_manager
|
||||
from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME, legal_check
|
||||
from in_game.gameplay_models import Lineup, Session, engine, get_card_or_none, player_description, select, Game, get_team_or_none
|
||||
from in_game.gameplay_queries import get_channel_game_or_none, get_active_games_by_team
|
||||
from in_game.gameplay_models import Lineup, Session, engine, player_description, select, Game
|
||||
from in_game.gameplay_queries import get_channel_game_or_none, get_active_games_by_team, get_game_lineups, get_team_or_none, get_card_or_none
|
||||
|
||||
from utilities.buttons import Confirm
|
||||
|
||||
@ -22,6 +22,19 @@ from utilities.buttons import Confirm
|
||||
class Gameplay(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.sheets = None
|
||||
|
||||
self.get_sheets.start()
|
||||
|
||||
@tasks.loop(count=1)
|
||||
async def get_sheets(self):
|
||||
logging.info(f'Getting sheets')
|
||||
self.sheets = pygsheets.authorize(service_file='storage/paper-dynasty-service-creds.json', retries=1)
|
||||
|
||||
@get_sheets.before_loop
|
||||
async def before_get_sheets(self):
|
||||
logging.info(f'Waiting to get sheets')
|
||||
await self.bot.wait_until_ready()
|
||||
|
||||
async def cog_command_error(self, ctx, error):
|
||||
await ctx.send(f'{error}\n\nRun !help <command_name> to see the command requirements')
|
||||
@ -36,10 +49,10 @@ class Gameplay(commands.Cog):
|
||||
sp_card_id='Light gray number to the left of the pitcher\'s name on your depth chart'
|
||||
)
|
||||
@app_commands.choices(league=[
|
||||
Choice(name='minor-league', value='Minor League'),
|
||||
Choice(name='flashback', value='Flashback'),
|
||||
Choice(name='major-league', value='Major League'),
|
||||
Choice(name='hall-of-fame', value='Hall of Fame')
|
||||
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(
|
||||
@ -58,8 +71,7 @@ class Gameplay(commands.Cog):
|
||||
|
||||
if interaction.channel.category is None or interaction.channel.category.name != PUBLIC_FIELDS_CATEGORY_NAME:
|
||||
await interaction.edit_original_response(
|
||||
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?'
|
||||
content=f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything pops up?'
|
||||
)
|
||||
return
|
||||
|
||||
@ -101,19 +113,19 @@ class Gameplay(commands.Cog):
|
||||
def role_error(required_role: str, league_name: str, lower_league: str):
|
||||
return f'Ope. Looks like you haven\'t received the **{required_role}** role, yet!\n\nTo play **{league_name}** games, you need to defeat all 30 MLB teams in the {lower_league} campaign. You can see your progress with `/record`.\n\nIf you have completed the {lower_league} campaign, go ping Cal to get your new role!'
|
||||
|
||||
if league.name == 'flashback':
|
||||
if league.value == 'flashback':
|
||||
if not user_has_role(interaction.user, 'PD - Major League'):
|
||||
await interaction.edit_original_response(
|
||||
content=role_error('PD - Major League', league_name='Flashback', lower_league='Minor League')
|
||||
)
|
||||
return
|
||||
elif league.name == 'major-league':
|
||||
elif league.value == 'major-league':
|
||||
if not user_has_role(interaction.user, 'PD - Major League'):
|
||||
await interaction.edit_original_response(
|
||||
content=role_error('PD - Major League', league_name='Major League', lower_league='Minor League')
|
||||
)
|
||||
return
|
||||
elif league.name == 'hall-of-fame':
|
||||
elif league.value == 'hall-of-fame':
|
||||
if not user_has_role(interaction.user, 'PD - Hall of Fame'):
|
||||
await interaction.edit_original_response(
|
||||
content=role_error('PD - Hall of Fame', league_name='Hall of Fame', lower_league='Major League')
|
||||
@ -128,10 +140,10 @@ class Gameplay(commands.Cog):
|
||||
week_num=week_num,
|
||||
first_message=None if interaction.message is None else interaction.message.channel.id,
|
||||
ai_team='away' if away_team.is_ai else 'home',
|
||||
game_type=league.name
|
||||
game_type=league.value
|
||||
)
|
||||
|
||||
game_info_log = f'{league.value} game between {away_team.description} and {home_team.description} / first message: {this_game.first_message}'
|
||||
game_info_log = f'{league.name} game between {away_team.description} and {home_team.description} / first message: {this_game.first_message}'
|
||||
logging.info(game_info_log)
|
||||
|
||||
# Get Human SP card
|
||||
@ -149,10 +161,10 @@ class Gameplay(commands.Cog):
|
||||
)
|
||||
return
|
||||
|
||||
legal_data = await legal_check([sp_card_id], difficulty_name=league.name)
|
||||
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 {player_description(player=human_sp_card.player)} is not legal in {league.value} games. You can start a new game once you pick a new SP.'
|
||||
content=f'It looks like this is a Ranked Legal game and {player_description(player=human_sp_card.player)} is not legal in {league.name} games. You can start a new game once you pick a new SP.'
|
||||
)
|
||||
return
|
||||
|
||||
@ -176,7 +188,7 @@ class Gameplay(commands.Cog):
|
||||
ai_team,
|
||||
this_game,
|
||||
True if home_team.is_ai else False,
|
||||
league.name
|
||||
league.value
|
||||
)
|
||||
await interaction.edit_original_response(
|
||||
content=f'The {ai_team.sname} are starting **{player_description(player=ai_sp_lineup.player)}**:\n\n{ai_sp_lineup.player.p_card_url}'
|
||||
@ -190,7 +202,7 @@ class Gameplay(commands.Cog):
|
||||
session,
|
||||
team=ai_team,
|
||||
game=this_game,
|
||||
league_name=league.name,
|
||||
league_name=league.value,
|
||||
sp_name=human_sp_card.player.name
|
||||
)
|
||||
|
||||
@ -248,17 +260,17 @@ class Gameplay(commands.Cog):
|
||||
)
|
||||
@app_commands.choices(
|
||||
roster=[
|
||||
Choice(name=1, value='Primary'),
|
||||
Choice(name=2, value='Secondary'),
|
||||
Choice(name=3, value='Ranked')
|
||||
Choice(value='1', name='Primary'),
|
||||
Choice(value='2', name='Secondary'),
|
||||
Choice(value='3', name='Ranked')
|
||||
],
|
||||
lineup=[
|
||||
Choice(name=1, value='v Right'),
|
||||
Choice(name=2, value='v Left')
|
||||
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[int], lineup: Choice[int]):
|
||||
async def read_lineup_command(self, interaction: discord.Interaction, roster: Choice[str], lineup: Choice[str]):
|
||||
await interaction.response.defer()
|
||||
|
||||
with Session(engine) as session:
|
||||
@ -269,6 +281,41 @@ 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:
|
||||
logging.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_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...')
|
||||
|
||||
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))
|
||||
|
||||
for batter in human_lineups:
|
||||
session.add(batter)
|
||||
|
||||
session.commit()
|
||||
|
||||
await interaction.edit_original_response(content=f'Lineups are read and logged!')
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
|
||||
@ -294,7 +294,7 @@ def get_record_embed(team: dict, results: dict, league: str):
|
||||
class Players(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.sheets = pygsheets.authorize(service_file='storage/paper-dynasty-service-creds.json', retries=1)
|
||||
# self.sheets = pygsheets.authorize(service_file='storage/paper-dynasty-service-creds.json', retries=1)
|
||||
self.player_list = []
|
||||
self.cardset_list = []
|
||||
self.freeze = False
|
||||
|
||||
87
command_logic/gameplay.py
Normal file
87
command_logic/gameplay.py
Normal file
@ -0,0 +1,87 @@
|
||||
|
||||
import logging
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from in_game.game_helpers import legal_check, CardLegalityException
|
||||
from in_game.gameplay_models import Game, Lineup, Team
|
||||
from in_game.gameplay_queries import get_card_or_none
|
||||
|
||||
|
||||
async def get_lineups_from_sheets(session: Session, sheets, this_game: Game, this_team: Team, lineup_num: int, roster_num: int) -> list[Lineup]:
|
||||
logging.debug(f'sheets: {sheets}')
|
||||
|
||||
this_sheet = sheets.open_by_key(this_team.gsheet)
|
||||
logging.debug(f'this_sheet: {this_sheet}')
|
||||
|
||||
r_sheet = this_sheet.worksheet_by_title('My Rosters')
|
||||
logging.debug(f'r_sheet: {r_sheet}')
|
||||
|
||||
if lineup_num == 1:
|
||||
row_start = 9
|
||||
row_end = 17
|
||||
else:
|
||||
row_start = 18
|
||||
row_end = 26
|
||||
|
||||
if roster_num == 1:
|
||||
l_range = f'H{row_start}:I{row_end}'
|
||||
elif roster_num == 2:
|
||||
l_range = f'J{row_start}:K{row_end}'
|
||||
else:
|
||||
l_range = f'L{row_start}:M{row_end}'
|
||||
|
||||
logging.debug(f'l_range: {l_range}')
|
||||
raw_cells = r_sheet.range(l_range)
|
||||
logging.debug(f'raw_cells: {raw_cells}')
|
||||
|
||||
try:
|
||||
lineup_cells = [(row[0].value, int(row[1].value)) for row in raw_cells]
|
||||
logging.debug(f'lineup_cells: {lineup_cells}')
|
||||
except ValueError as e:
|
||||
logging.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')
|
||||
|
||||
all_lineups = []
|
||||
all_pos = []
|
||||
card_ids = []
|
||||
for index, row in enumerate(lineup_cells):
|
||||
if '' in row:
|
||||
break
|
||||
|
||||
if row[0].upper() not in all_pos:
|
||||
all_pos.append(row[0].upper())
|
||||
else:
|
||||
raise SyntaxError(f'You have more than one {row[0].upper()} in this lineup. Please update and set the lineup again.')
|
||||
|
||||
this_card = await get_card_or_none(session, card_id=int(row[1]))
|
||||
if this_card is None:
|
||||
raise LookupError(
|
||||
f'Your {row[0].upper()} has a Card ID of {int(row[1])} and I cannot find that card. Did you sell it by chance? Or maybe you sold a duplicate and the bot sold the one you were using?'
|
||||
)
|
||||
if this_card.team_id != this_team.id:
|
||||
raise SyntaxError(f'Easy there, champ. Looks like card ID {row[1]} belongs to the {this_card.team.lname}. Try again with only cards you own.')
|
||||
card_id = row[1]
|
||||
card_ids.append(str(card_id))
|
||||
|
||||
this_lineup = Lineup(
|
||||
position=row[0].upper(),
|
||||
batting_order=index + 1,
|
||||
game=this_game,
|
||||
team=this_team,
|
||||
player=this_card.player,
|
||||
card=this_card
|
||||
)
|
||||
all_lineups.append(this_lineup)
|
||||
|
||||
legal_data = await legal_check([card_ids], difficulty_name=this_game.game_type)
|
||||
logging.debug(f'legal_data: {legal_data}')
|
||||
if not legal_data['legal']:
|
||||
raise CardLegalityException(f'The following cards appear to be illegal for this game mode:\n{legal_data["error_string"]}')
|
||||
|
||||
if len(all_lineups) != 9:
|
||||
raise Exception(f'I was only able to pull in {len(all_lineups)} batters from Sheets. Please check your saved lineup and try again.')
|
||||
|
||||
return all_lineups
|
||||
|
||||
|
||||
|
||||
@ -13,7 +13,8 @@ from typing import Optional, Literal
|
||||
|
||||
from in_game import data_cache
|
||||
import in_game.gameplay_models as iggm
|
||||
from in_game.gameplay_models import Session, Game, Team, get_or_create_ai_card, get_player_id_from_dict, get_player_or_none
|
||||
from in_game.gameplay_models import Session, Game, Team
|
||||
from in_game.gameplay_queries import get_or_create_ai_card, get_player_id_from_dict, get_player_or_none
|
||||
|
||||
db = SqliteDatabase(
|
||||
'storage/ai-database.db',
|
||||
@ -352,7 +353,7 @@ async def get_starting_pitcher(
|
||||
f'teams/{this_team.id}/sp/{league_name}?sp_rank={sp_rank}{this_game.cardset_param_string}'
|
||||
)
|
||||
this_player = await get_player_or_none(session, get_player_id_from_dict(sp_query))
|
||||
sp_card = await iggm.get_or_create_ai_card(session, this_player, this_team)
|
||||
sp_card = await get_or_create_ai_card(session, this_player, this_team)
|
||||
|
||||
return iggm.Lineup(
|
||||
team=this_team,
|
||||
|
||||
@ -14,6 +14,10 @@ from typing import Literal, Optional
|
||||
PUBLIC_FIELDS_CATEGORY_NAME = 'Public Fields'
|
||||
|
||||
|
||||
class CardLegalityException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def single_onestar(this_play: StratPlay, comp_play: bool = True):
|
||||
patch_play(this_play.id, locked=True)
|
||||
advance_runners(this_play.id, num_bases=1)
|
||||
|
||||
@ -50,12 +50,15 @@ class TeamBase(SQLModel):
|
||||
class Team(TeamBase, table=True):
|
||||
cards: list['Card'] = Relationship(back_populates='team', cascade_delete=True)
|
||||
lineups: list['Lineup'] = Relationship(back_populates='team', cascade_delete=True)
|
||||
# away_games: list['Game'] = Relationship(back_populates='away_team')
|
||||
# home_games: list['Game'] = Relationship(back_populates='home_team')
|
||||
|
||||
|
||||
|
||||
class Game(SQLModel, table=True):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
away_team_id: int
|
||||
home_team_id: int
|
||||
away_team_id: int = Field(foreign_key='team.id')
|
||||
home_team_id: int = Field(foreign_key='team.id')
|
||||
channel_id: int = Field(index=True)
|
||||
season: int
|
||||
active: bool | None = Field(default=True)
|
||||
@ -71,6 +74,24 @@ class Game(SQLModel, table=True):
|
||||
game_type: str | None = Field(default=None)
|
||||
|
||||
cardset_links: list[GameCardsetLink] = Relationship(back_populates='game', cascade_delete=True)
|
||||
away_team: Team = Relationship(
|
||||
# back_populates='away_games',
|
||||
# sa_relationship_kwargs={
|
||||
# 'primaryjoin': 'Game.away_team_id==Team.id',
|
||||
# 'foreign_keys': '[Game.away_team.id]',
|
||||
# 'lazy': 'joined'
|
||||
# }
|
||||
sa_relationship_kwargs=dict(foreign_keys="[Game.away_team_id]")
|
||||
)
|
||||
home_team: Team = Relationship(
|
||||
# back_populates='home_games',
|
||||
# sa_relationship_kwargs={
|
||||
# 'primaryjoin': 'Game.home_team_id==Team.id',
|
||||
# 'foreign_keys': '[Game.home_team.id]',
|
||||
# 'lazy': 'joined'
|
||||
# }
|
||||
sa_relationship_kwargs=dict(foreign_keys="[Game.home_team_id]")
|
||||
)
|
||||
lineups: list['Lineup'] = Relationship(back_populates='game', cascade_delete=True)
|
||||
|
||||
@property
|
||||
|
||||
@ -3,7 +3,7 @@ import logging
|
||||
|
||||
from sqlalchemy import func
|
||||
from api_calls import db_get, db_post
|
||||
from in_game.gameplay_models import CACHE_LIMIT, Card, CardBase, Player, PlayerBase, Session, Team, TeamBase, select, or_, Game
|
||||
from in_game.gameplay_models import CACHE_LIMIT, Card, CardBase, Lineup, Player, PlayerBase, Session, Team, TeamBase, select, or_, Game
|
||||
|
||||
|
||||
def get_games_by_channel(session: Session, channel_id: int) -> list[Game]:
|
||||
@ -15,7 +15,7 @@ def get_channel_game_or_none(session: Session, channel_id: int) -> Game | None:
|
||||
if len(all_games) > 1:
|
||||
err = 'Too many games found in get_channel_game_or_none'
|
||||
logging.error(f'cogs.gameplay - get_channel_game_or_none - channel_id: {channel_id} / {err}')
|
||||
raise LookupError(err)
|
||||
raise Exception(err)
|
||||
elif len(all_games) == 0:
|
||||
return None
|
||||
return all_games[0]
|
||||
@ -232,3 +232,15 @@ async def get_card_or_none(session: Session, card_id: int, skip_cache: bool = Fa
|
||||
return cache_card(c_query)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_game_lineups(session: Session, this_game: Game, specific_team: Team = None, is_active: bool = None) -> list[Lineup]:
|
||||
st = select(Lineup).where(Lineup.game == this_game)
|
||||
|
||||
if specific_team is not None:
|
||||
st = st.where(Lineup.team == specific_team)
|
||||
if is_active is not None:
|
||||
st = st.where(Lineup.active == is_active)
|
||||
|
||||
return session.exec(st).all()
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import datetime
|
||||
from sqlmodel import Session
|
||||
|
||||
from in_game.gameplay_models import CACHE_LIMIT, Card, Player, Team, select, get_card_or_none, get_or_create_ai_card
|
||||
from in_game.gameplay_models import CACHE_LIMIT, Card, Player, Team, select, get_card_or_none
|
||||
from in_game.gameplay_queries import get_or_create_ai_card
|
||||
from factory import session_fixture
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from in_game.gameplay_models import Lineup
|
||||
from in_game.gameplay_models import Game, Lineup
|
||||
from in_game.gameplay_queries import get_game_lineups
|
||||
from factory import session_fixture
|
||||
|
||||
|
||||
@ -18,6 +19,31 @@ def test_create_lineup(session: Session):
|
||||
assert lineup_id_21.position == 'C'
|
||||
|
||||
|
||||
def test_get_game_lineups(session: Session):
|
||||
this_game = session.get(Game, 1)
|
||||
all_lineups = get_game_lineups(session, this_game=this_game)
|
||||
away_lineups = get_game_lineups(session, this_game=this_game, specific_team=this_game.away_team)
|
||||
|
||||
assert len(all_lineups) == 20
|
||||
assert len(away_lineups) == 10
|
||||
|
||||
for count in range(5):
|
||||
away_lineups[count].active = False
|
||||
session.add(away_lineups[count])
|
||||
|
||||
session.commit()
|
||||
|
||||
all_lineups = get_game_lineups(session, this_game=this_game)
|
||||
away_lineups = get_game_lineups(session, this_game=this_game, specific_team=this_game.away_team)
|
||||
active_away_lineups = get_game_lineups(session, this_game=this_game, specific_team=this_game.away_team, is_active=True)
|
||||
inactive_home_lineups = get_game_lineups(session, this_game=this_game, specific_team=this_game.home_team, is_active=False)
|
||||
|
||||
assert len(all_lineups) == 20
|
||||
assert len(away_lineups) == 10
|
||||
assert len(active_away_lineups) == 5
|
||||
assert len(inactive_home_lineups) == 0
|
||||
|
||||
|
||||
# def test_lineup_substitution(session: Session, new_games_with_lineups: list[Game]):
|
||||
# game_1 = new_games_with_lineups[0]
|
||||
# game_2 = new_games_with_lineups[1]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user