diff --git a/commands/admin/management.py b/commands/admin/management.py index 3ca63b0..fcc9b1b 100644 --- a/commands/admin/management.py +++ b/commands/admin/management.py @@ -13,7 +13,6 @@ from discord import app_commands from utils.logging import get_contextual_logger from utils.decorators import logged_command from views.embeds import EmbedColors, EmbedTemplate -from constants import SBA_CURRENT_SEASON class AdminCommands(commands.Cog): @@ -71,7 +70,7 @@ class AdminCommands(commands.Cog): name="Bot Information", value=f"**Latency:** {round(self.bot.latency * 1000)}ms\n" f"**Version:** Discord.py {discord.__version__}\n" - f"**Current Season:** {SBA_CURRENT_SEASON}", + f"**Current Season:** {get_config().sba_current_season}", inline=True ) @@ -389,4 +388,5 @@ class AdminCommands(commands.Cog): async def setup(bot: commands.Bot): """Load the admin commands cog.""" +from config import get_config await bot.add_cog(AdminCommands(bot)) \ No newline at end of file diff --git a/commands/examples/enhanced_player.py b/commands/examples/enhanced_player.py index b8946ce..cf836b9 100644 --- a/commands/examples/enhanced_player.py +++ b/commands/examples/enhanced_player.py @@ -11,7 +11,6 @@ from discord.ext import commands from services.player_service import player_service from models.player import Player -from constants import SBA_CURRENT_SEASON from utils.logging import get_contextual_logger from utils.decorators import logged_command from exceptions import BotException @@ -71,7 +70,7 @@ class EnhancedPlayerCommands(commands.Cog): return # Use current season if not specified - search_season = season or SBA_CURRENT_SEASON + search_season = season or get_config().sba_current_season # Search for players players = await player_service.get_players_by_name(name, search_season) @@ -330,6 +329,7 @@ class EnhancedPlayerCommands(commands.Cog): @logged_command("/player-search-modal") async def player_search_modal(self, interaction: discord.Interaction): """Demonstrate modal-based player search.""" +from config import get_config modal = PlayerSearchModal() await interaction.response.send_modal(modal) @@ -342,7 +342,7 @@ class EnhancedPlayerCommands(commands.Cog): # Perform search based on criteria players = await player_service.get_players_by_name( search_criteria['name'], - search_criteria['season'] or SBA_CURRENT_SEASON + search_criteria['season'] or get_config().sba_current_season ) if players: @@ -351,13 +351,13 @@ class EnhancedPlayerCommands(commands.Cog): await self._show_player_details( interaction, players[0], - search_criteria['season'] or SBA_CURRENT_SEASON + search_criteria['season'] or get_config().sba_current_season ) else: await self._show_player_selection( interaction, players, - search_criteria['season'] or SBA_CURRENT_SEASON + search_criteria['season'] or get_config().sba_current_season ) else: embed = EmbedTemplate.warning( diff --git a/commands/examples/migration_example.py b/commands/examples/migration_example.py index 25ab1df..28e8d81 100644 --- a/commands/examples/migration_example.py +++ b/commands/examples/migration_example.py @@ -11,7 +11,6 @@ from discord.ext import commands from services.team_service import team_service from models.team import Team -from constants import SBA_CURRENT_SEASON from utils.logging import get_contextual_logger from views.embeds import EmbedTemplate, EmbedColors from utils.decorators import logged_command @@ -48,7 +47,7 @@ class MigrationExampleCommands(commands.Cog): """Old style team listing - basic embed only.""" await interaction.response.defer() - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season teams = await team_service.get_teams_by_season(season) if not teams: @@ -91,7 +90,7 @@ class MigrationExampleCommands(commands.Cog): """New style team listing - interactive with pagination and selection.""" await interaction.response.defer() - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season teams = await team_service.get_teams_by_season(season) if not teams: @@ -308,4 +307,5 @@ class MigrationExampleCommands(commands.Cog): async def setup(bot: commands.Bot): """Load the migration example commands cog.""" +from config import get_config await bot.add_cog(MigrationExampleCommands(bot)) \ No newline at end of file diff --git a/commands/help/main.py b/commands/help/main.py index 8e212ce..c8f6858 100644 --- a/commands/help/main.py +++ b/commands/help/main.py @@ -23,7 +23,6 @@ from views.help_commands import ( HelpCommandListView, create_help_topic_embed ) -from constants import HELP_EDITOR_ROLE_NAME from exceptions import BotException @@ -63,7 +62,7 @@ class HelpCommands(commands.Cog): return True # Check if user has the Help Editor role - role = discord.utils.get(interaction.guild.roles, name=HELP_EDITOR_ROLE_NAME) + role = discord.utils.get(interaction.guild.roles, name=get_config().help_editor_role_name) if role and role in interaction.user.roles: return True @@ -136,7 +135,7 @@ class HelpCommands(commands.Cog): if not self.has_help_edit_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can create help topics." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can create help topics." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -217,7 +216,7 @@ class HelpCommands(commands.Cog): if not self.has_help_edit_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can edit help topics." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can edit help topics." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -292,7 +291,7 @@ class HelpCommands(commands.Cog): if not self.has_help_edit_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can delete help topics." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can delete help topics." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -369,6 +368,7 @@ class HelpCommands(commands.Cog): show_deleted: bool = False ): """Browse all help topics with optional category filter.""" +from config import get_config await interaction.response.defer() try: diff --git a/commands/league/info.py b/commands/league/info.py index c4ba9ad..7e2f203 100644 --- a/commands/league/info.py +++ b/commands/league/info.py @@ -8,7 +8,7 @@ import discord from discord.ext import commands from services import league_service -from constants import SBA_CURRENT_SEASON +from config import get_config from utils.logging import get_contextual_logger from utils.decorators import logged_command from exceptions import BotException diff --git a/commands/league/schedule.py b/commands/league/schedule.py index 9ac062a..2f964af 100644 --- a/commands/league/schedule.py +++ b/commands/league/schedule.py @@ -12,7 +12,6 @@ from discord.ext import commands from services.schedule_service import schedule_service from utils.logging import get_contextual_logger from utils.decorators import logged_command -from constants import SBA_CURRENT_SEASON from views.embeds import EmbedColors, EmbedTemplate @@ -43,7 +42,7 @@ class ScheduleCommands(commands.Cog): """Display game schedule for a week or team.""" await interaction.response.defer() - search_season = season or SBA_CURRENT_SEASON + search_season = season or get_config().sba_current_season if team: # Show team schedule @@ -73,7 +72,7 @@ class ScheduleCommands(commands.Cog): # """Display recent game results.""" # await interaction.response.defer() - # search_season = season or SBA_CURRENT_SEASON + # search_season = season or get_config().sba_current_season # if week: # # Show specific week results @@ -350,4 +349,5 @@ class ScheduleCommands(commands.Cog): async def setup(bot: commands.Bot): """Load the schedule commands cog.""" +from config import get_config await bot.add_cog(ScheduleCommands(bot)) \ No newline at end of file diff --git a/commands/league/standings.py b/commands/league/standings.py index 2fe2f57..f3a59f7 100644 --- a/commands/league/standings.py +++ b/commands/league/standings.py @@ -11,7 +11,6 @@ from discord.ext import commands from services.standings_service import standings_service from utils.logging import get_contextual_logger from utils.decorators import logged_command -from constants import SBA_CURRENT_SEASON from views.embeds import EmbedColors, EmbedTemplate @@ -40,7 +39,7 @@ class StandingsCommands(commands.Cog): """Display league standings by division.""" await interaction.response.defer() - search_season = season or SBA_CURRENT_SEASON + search_season = season or get_config().sba_current_season if division: # Show specific division @@ -65,7 +64,7 @@ class StandingsCommands(commands.Cog): """Display playoff picture with division leaders and wild card race.""" await interaction.response.defer() - search_season = season or SBA_CURRENT_SEASON + search_season = season or get_config().sba_current_season self.logger.debug("Fetching playoff picture", season=search_season) playoff_data = await standings_service.get_playoff_picture(search_season) @@ -250,4 +249,5 @@ class StandingsCommands(commands.Cog): async def setup(bot: commands.Bot): """Load the standings commands cog.""" +from config import get_config await bot.add_cog(StandingsCommands(bot)) \ No newline at end of file diff --git a/commands/players/info.py b/commands/players/info.py index b6b00e4..aebf542 100644 --- a/commands/players/info.py +++ b/commands/players/info.py @@ -12,7 +12,6 @@ from services.player_service import player_service from services.stats_service import stats_service from utils.logging import get_contextual_logger from utils.decorators import logged_command -from constants import SBA_CURRENT_SEASON from views.embeds import EmbedColors, EmbedTemplate from models.team import RosterType @@ -27,7 +26,7 @@ async def player_name_autocomplete( try: # Use the dedicated search endpoint to get matching players - players = await player_service.search_players(current, limit=25, season=SBA_CURRENT_SEASON) + players = await player_service.search_players(current, limit=25, season=get_config().sba_current_season) # Convert to discord choices, limiting to 25 (Discord's max) choices = [] @@ -78,7 +77,7 @@ class PlayerInfoCommands(commands.Cog): self.logger.debug("Response deferred") # Search for player by name (use season parameter or default to current) - search_season = season or SBA_CURRENT_SEASON + search_season = season or get_config().sba_current_season self.logger.debug("Starting player search", api_call="get_players_by_name", season=search_season) players = await player_service.get_players_by_name(name, search_season) self.logger.info("Player search completed", players_found=len(players), season=search_season) @@ -173,6 +172,7 @@ class PlayerInfoCommands(commands.Cog): pitching_stats=None ) -> discord.Embed: """Create a comprehensive player embed with statistics.""" +from config import get_config # Determine embed color based on team embed_color = EmbedColors.PRIMARY if hasattr(player, 'team') and player.team and hasattr(player.team, 'color'): diff --git a/commands/profile/images.py b/commands/profile/images.py index cdf6cd8..4f590cb 100644 --- a/commands/profile/images.py +++ b/commands/profile/images.py @@ -16,7 +16,6 @@ from services.player_service import player_service from services.team_service import team_service from utils.logging import get_contextual_logger from utils.decorators import logged_command -from constants import SBA_CURRENT_SEASON from views.embeds import EmbedColors, EmbedTemplate from views.base import BaseView from models.player import Player @@ -230,6 +229,7 @@ class ImageCommands(commands.Cog): image_url: str ): """Update a player's image (fancy card or headshot).""" +from config import get_config # Defer response for potentially slow operations await interaction.response.defer(ephemeral=True) @@ -279,7 +279,7 @@ class ImageCommands(commands.Cog): # Step 3: Find player self.logger.debug("Searching for player", player_name=player_name) - players = await player_service.get_players_by_name(player_name, SBA_CURRENT_SEASON) + players = await player_service.get_players_by_name(player_name, get_config().sba_current_season) if not players: self.logger.warning("Player not found", player_name=player_name) @@ -316,7 +316,7 @@ class ImageCommands(commands.Cog): # Step 4: Check permissions has_permission, permission_error = await can_edit_player_image( - interaction, player, SBA_CURRENT_SEASON, self.logger + interaction, player, get_config().sba_current_season, self.logger ) if not has_permission: diff --git a/commands/teams/info.py b/commands/teams/info.py index a4a52f9..e342d79 100644 --- a/commands/teams/info.py +++ b/commands/teams/info.py @@ -9,7 +9,6 @@ from discord.ext import commands from services import team_service, player_service from models.team import RosterType, Team -from constants import SBA_CURRENT_SEASON from utils.logging import get_contextual_logger from utils.decorators import logged_command from exceptions import BotException @@ -36,7 +35,7 @@ class TeamInfoCommands(commands.Cog): await interaction.response.defer() # Use current season if not specified - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season # Get team by abbreviation team = await team_service.get_team_by_abbrev(abbrev, season) @@ -67,7 +66,7 @@ class TeamInfoCommands(commands.Cog): """List all teams in a season.""" await interaction.response.defer() - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season teams = await team_service.get_teams_by_season(season) @@ -129,6 +128,7 @@ class TeamInfoCommands(commands.Cog): async def _create_team_embed(self, team: Team, standings_data: Optional[dict] = None) -> discord.Embed: """Create a rich embed for team information.""" +from config import get_config embed = EmbedTemplate.create_base_embed( title=f"{team.abbrev} - {team.lname}", description=f"Season {team.season} Team Information", diff --git a/commands/teams/roster.py b/commands/teams/roster.py index 2890106..e8e0814 100644 --- a/commands/teams/roster.py +++ b/commands/teams/roster.py @@ -10,7 +10,6 @@ from discord.ext import commands from models.player import Player from services import team_service, player_service from models.team import Team -from constants import SBA_CURRENT_SEASON from utils.logging import get_contextual_logger from utils.decorators import logged_command from exceptions import BotException @@ -41,7 +40,7 @@ class TeamRosterCommands(commands.Cog): await interaction.response.defer() # Get team by abbreviation - team = await team_service.get_team_by_abbrev(abbrev, SBA_CURRENT_SEASON) + team = await team_service.get_team_by_abbrev(abbrev, get_config().sba_current_season) if team is None: self.logger.info("Team not found", team_abbrev=abbrev) @@ -141,6 +140,7 @@ class TeamRosterCommands(commands.Cog): def _create_player_list_embed(self, team: Team, roster_name: str, players: List[Dict[str, Any]]) -> discord.Embed: """Create an embed with detailed player list.""" +from config import get_config roster_titles = { 'active': 'Active Roster', 'longil': 'Minor League', diff --git a/commands/transactions/dropadd.py b/commands/transactions/dropadd.py index c2ccfb1..5a5096e 100644 --- a/commands/transactions/dropadd.py +++ b/commands/transactions/dropadd.py @@ -13,7 +13,6 @@ from utils.logging import get_contextual_logger from utils.decorators import logged_command from utils.autocomplete import player_autocomplete from utils.team_utils import validate_user_has_team -from constants import SBA_CURRENT_SEASON from services.transaction_builder import ( TransactionBuilder, @@ -127,7 +126,7 @@ class DropAddCommands(commands.Cog): """ try: # Find player using the new search endpoint - players = await player_service.search_players(player_name, limit=10, season=SBA_CURRENT_SEASON) + players = await player_service.search_players(player_name, limit=10, season=get_config().sba_current_season) if not players: self.logger.error(f"Player not found: {player_name}") return False, f"Player '{player_name}' not found" @@ -232,4 +231,5 @@ class DropAddCommands(commands.Cog): async def setup(bot): """Setup function for the cog.""" +from config import get_config await bot.add_cog(DropAddCommands(bot)) \ No newline at end of file diff --git a/commands/transactions/management.py b/commands/transactions/management.py index 3aac3fc..1aab9ff 100644 --- a/commands/transactions/management.py +++ b/commands/transactions/management.py @@ -15,7 +15,6 @@ from utils.decorators import logged_command from utils.team_utils import get_user_major_league_team from views.embeds import EmbedColors, EmbedTemplate from views.base import PaginationView -from constants import SBA_CURRENT_SEASON from services.transaction_service import transaction_service from services.roster_service import roster_service @@ -122,7 +121,7 @@ class TransactionCommands(commands.Cog): await interaction.response.defer() # Get user's team - team = await get_user_major_league_team(interaction.user.id, SBA_CURRENT_SEASON) + team = await get_user_major_league_team(interaction.user.id, get_config().sba_current_season) if not team: await interaction.followup.send( @@ -132,9 +131,9 @@ class TransactionCommands(commands.Cog): return # Get transactions in parallel - pending_task = transaction_service.get_pending_transactions(team.abbrev, SBA_CURRENT_SEASON) - frozen_task = transaction_service.get_frozen_transactions(team.abbrev, SBA_CURRENT_SEASON) - processed_task = transaction_service.get_processed_transactions(team.abbrev, SBA_CURRENT_SEASON) + pending_task = transaction_service.get_pending_transactions(team.abbrev, get_config().sba_current_season) + frozen_task = transaction_service.get_frozen_transactions(team.abbrev, get_config().sba_current_season) + processed_task = transaction_service.get_processed_transactions(team.abbrev, get_config().sba_current_season) pending_transactions = await pending_task frozen_transactions = await frozen_task @@ -145,7 +144,7 @@ class TransactionCommands(commands.Cog): if show_cancelled: cancelled_transactions = await transaction_service.get_team_transactions( team.abbrev, - SBA_CURRENT_SEASON, + get_config().sba_current_season, cancelled=True ) @@ -197,16 +196,16 @@ class TransactionCommands(commands.Cog): # Get target team if team: - target_team = await team_service.get_team_by_abbrev(team.upper(), SBA_CURRENT_SEASON) + target_team = await team_service.get_team_by_abbrev(team.upper(), get_config().sba_current_season) if not target_team: await interaction.followup.send( - f"❌ Could not find team '{team}' in season {SBA_CURRENT_SEASON}.", + f"❌ Could not find team '{team}' in season {get_config().sba_current_season}.", ephemeral=True ) return else: # Get user's team - user_teams = await team_service.get_teams_by_owner(interaction.user.id, SBA_CURRENT_SEASON) + user_teams = await team_service.get_teams_by_owner(interaction.user.id, get_config().sba_current_season) if not user_teams: await interaction.followup.send( "❌ You don't appear to own a team. Please specify a team abbreviation.", @@ -283,7 +282,7 @@ class TransactionCommands(commands.Cog): embed = EmbedTemplate.create_base_embed( title=f"📋 Transaction Status - {team.abbrev}", - description=f"{team.lname} • Season {SBA_CURRENT_SEASON}", + description=f"{team.lname} • Season {get_config().sba_current_season}", color=EmbedColors.INFO ) @@ -320,7 +319,7 @@ class TransactionCommands(commands.Cog): # No pending transactions - create single page embed = EmbedTemplate.create_base_embed( title=f"📋 Transaction Status - {team.abbrev}", - description=f"{team.lname} • Season {SBA_CURRENT_SEASON}", + description=f"{team.lname} • Season {get_config().sba_current_season}", color=EmbedColors.INFO ) @@ -350,7 +349,7 @@ class TransactionCommands(commands.Cog): if frozen_transactions: embed = EmbedTemplate.create_base_embed( title=f"📋 Transaction Status - {team.abbrev}", - description=f"{team.lname} • Season {SBA_CURRENT_SEASON}", + description=f"{team.lname} • Season {get_config().sba_current_season}", color=EmbedColors.INFO ) @@ -371,7 +370,7 @@ class TransactionCommands(commands.Cog): if processed_transactions: embed = EmbedTemplate.create_base_embed( title=f"📋 Transaction Status - {team.abbrev}", - description=f"{team.lname} • Season {SBA_CURRENT_SEASON}", + description=f"{team.lname} • Season {get_config().sba_current_season}", color=EmbedColors.INFO ) @@ -392,7 +391,7 @@ class TransactionCommands(commands.Cog): if cancelled_transactions: embed = EmbedTemplate.create_base_embed( title=f"📋 Transaction Status - {team.abbrev}", - description=f"{team.lname} • Season {SBA_CURRENT_SEASON}", + description=f"{team.lname} • Season {get_config().sba_current_season}", color=EmbedColors.INFO ) @@ -437,7 +436,7 @@ class TransactionCommands(commands.Cog): embed = EmbedTemplate.create_base_embed( title=f"{status_emoji} Roster Check - {team.abbrev}", - description=f"{team.lname} • Season {SBA_CURRENT_SEASON}", + description=f"{team.lname} • Season {get_config().sba_current_season}", color=embed_color ) @@ -521,4 +520,5 @@ class TransactionCommands(commands.Cog): async def setup(bot: commands.Bot): """Load the transaction commands cog.""" +from config import get_config await bot.add_cog(TransactionCommands(bot)) \ No newline at end of file diff --git a/commands/transactions/trade.py b/commands/transactions/trade.py index a5cf7d5..f381ab6 100644 --- a/commands/transactions/trade.py +++ b/commands/transactions/trade.py @@ -13,7 +13,6 @@ from utils.logging import get_contextual_logger from utils.decorators import logged_command from utils.autocomplete import player_autocomplete, major_league_team_autocomplete, team_autocomplete from utils.team_utils import validate_user_has_team, get_team_by_abbrev_with_validation -from constants import SBA_CURRENT_SEASON from services.trade_builder import ( TradeBuilder, @@ -282,7 +281,7 @@ class TradeCommands(commands.Cog): return # Find the player - players = await player_service.search_players(player_name, limit=10, season=SBA_CURRENT_SEASON) + players = await player_service.search_players(player_name, limit=10, season=get_config().sba_current_season) if not players: await interaction.followup.send( f"❌ Player '{player_name}' not found.", @@ -392,7 +391,7 @@ class TradeCommands(commands.Cog): return # Find the player - players = await player_service.search_players(player_name, limit=10, season=SBA_CURRENT_SEASON) + players = await player_service.search_players(player_name, limit=10, season=get_config().sba_current_season) if not players: await interaction.followup.send( f"❌ Player '{player_name}' not found.", @@ -532,4 +531,5 @@ class TradeCommands(commands.Cog): async def setup(bot): """Setup function for the cog.""" +from config import get_config await bot.add_cog(TradeCommands(bot)) \ No newline at end of file diff --git a/commands/utilities/charts.py b/commands/utilities/charts.py index 4c7dcac..3053f5b 100644 --- a/commands/utilities/charts.py +++ b/commands/utilities/charts.py @@ -14,7 +14,6 @@ from utils.logging import get_contextual_logger, set_discord_context from services.chart_service import get_chart_service, Chart from views.embeds import EmbedTemplate, EmbedColors from exceptions import BotException -from constants import HELP_EDITOR_ROLE_NAME # Standalone autocomplete functions @@ -79,7 +78,7 @@ def has_manage_permission(interaction: discord.Interaction) -> bool: return True # Check if user has the Help Editor role - help_editor_role = discord.utils.get(interaction.guild.roles, name=HELP_EDITOR_ROLE_NAME) + help_editor_role = discord.utils.get(interaction.guild.roles, name=get_config().help_editor_role_name) if help_editor_role and help_editor_role in interaction.user.roles: return True @@ -255,7 +254,7 @@ class ChartManageGroup(app_commands.Group): if not has_manage_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can manage charts." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage charts." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -311,7 +310,7 @@ class ChartManageGroup(app_commands.Group): if not has_manage_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can manage charts." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage charts." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -370,7 +369,7 @@ class ChartManageGroup(app_commands.Group): if not has_manage_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can manage charts." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage charts." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -446,7 +445,7 @@ class ChartCategoryGroup(app_commands.Group): if not has_manage_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can manage categories." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage categories." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -507,7 +506,7 @@ class ChartCategoryGroup(app_commands.Group): if not has_manage_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can manage categories." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage categories." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -548,7 +547,7 @@ class ChartCategoryGroup(app_commands.Group): if not has_manage_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can manage categories." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage categories." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -599,7 +598,7 @@ class ChartCategoryGroup(app_commands.Group): if not has_manage_permission(interaction): embed = EmbedTemplate.error( title="Permission Denied", - description=f"Only administrators and users with the **{HELP_EDITOR_ROLE_NAME}** role can manage categories." + description=f"Only administrators and users with the **{get_config().help_editor_role_name}** role can manage categories." ) await interaction.response.send_message(embed=embed, ephemeral=True) return @@ -634,6 +633,7 @@ class ChartCategoryGroup(app_commands.Group): async def setup(bot: commands.Bot): """Setup function for chart commands.""" +from config import get_config await bot.add_cog(ChartCommands(bot)) bot.tree.add_command(ChartManageGroup()) bot.tree.add_command(ChartCategoryGroup()) diff --git a/commands/voice/channels.py b/commands/voice/channels.py index 3ddb54e..9c3257e 100644 --- a/commands/voice/channels.py +++ b/commands/voice/channels.py @@ -15,7 +15,6 @@ from services.schedule_service import ScheduleService from services.league_service import league_service from utils.logging import get_contextual_logger from utils.decorators import logged_command -from constants import SBA_CURRENT_SEASON from views.embeds import EmbedTemplate from models.team import RosterType @@ -60,7 +59,7 @@ class VoiceChannelCommands(commands.Cog): Returns: Team object or None if not found """ - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season teams = await team_service.get_teams_by_owner(user_id, season) return teams[0] if teams else None @@ -75,7 +74,7 @@ class VoiceChannelCommands(commands.Cog): Returns: Major League Team object or None if not found """ - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season teams = await team_service.get_teams_by_owner(user_id, season) # Filter to only Major League teams (3-character abbreviations) @@ -352,6 +351,7 @@ class VoiceChannelCommands(commands.Cog): @commands.command(name="private") async def deprecated_private_voice(self, ctx: commands.Context): """Deprecated command - redirect to new slash command.""" +from config import get_config embed = EmbedTemplate.info( title="Command Deprecated", description=( diff --git a/config.py b/config.py index af763d3..51eb628 100644 --- a/config.py +++ b/config.py @@ -3,6 +3,11 @@ Configuration management for Discord Bot v2.0 """ from pydantic_settings import BaseSettings, SettingsConfigDict +# Baseball position constants (static, not configurable) +PITCHER_POSITIONS = {"SP", "RP", "P"} +POSITION_FIELDERS = {"C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "OF", "DH"} +ALL_POSITIONS = PITCHER_POSITIONS | POSITION_FIELDERS + class BotConfig(BaseSettings): """Application configuration with environment variable support.""" diff --git a/constants.py b/constants.py deleted file mode 100644 index 1cab807..0000000 --- a/constants.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Application constants for Discord Bot v2.0 - -Most constants are now configurable via environment variables. -See config.py for default values and .env.example for configuration options. -""" -from config import get_config - -# Load configuration -_config = get_config() - -# Discord Limits (configurable) -DISCORD_EMBED_LIMIT = _config.discord_embed_limit -DISCORD_FIELD_VALUE_LIMIT = _config.discord_field_value_limit -DISCORD_EMBED_DESCRIPTION_LIMIT = _config.discord_embed_description_limit - -# League Constants (configurable) -WEEKS_PER_SEASON = _config.weeks_per_season -GAMES_PER_WEEK = _config.games_per_week -MODERN_STATS_START_SEASON = _config.modern_stats_start_season - -# Current Season Constants (configurable) -SBA_CURRENT_SEASON = _config.sba_current_season -PD_CURRENT_SEASON = _config.pd_current_season - -# API Constants (configurable) -API_VERSION = _config.api_version -DEFAULT_TIMEOUT = _config.default_timeout -MAX_RETRIES = _config.max_retries - -# Baseball Positions (static) -PITCHER_POSITIONS = {"SP", "RP", "P"} -POSITION_FIELDERS = {"C", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "OF", "DH"} -ALL_POSITIONS = PITCHER_POSITIONS | POSITION_FIELDERS - -# Draft Constants (configurable) -DEFAULT_PICK_MINUTES = _config.default_pick_minutes -DRAFT_ROUNDS = _config.draft_rounds - -# Special Team IDs (configurable) -FREE_AGENT_TEAM_ID = _config.free_agent_team_id - -# Role Names (configurable) -HELP_EDITOR_ROLE_NAME = _config.help_editor_role_name -SBA_PLAYERS_ROLE_NAME = _config.sba_players_role_name - -# Channel Names (configurable) -SBA_NETWORK_NEWS_CHANNEL = _config.sba_network_news_channel - -# Base URLs (configurable) -SBA_BASE_URL = _config.sba_base_url - -# Note: Google Sheets credentials path is managed via config.py -# Access it with: get_config().sheets_credentials_path \ No newline at end of file diff --git a/services/league_service.py b/services/league_service.py index 6e21445..cce1015 100644 --- a/services/league_service.py +++ b/services/league_service.py @@ -8,7 +8,6 @@ from typing import Optional, List, Dict, Any from services.base_service import BaseService from models.current import Current -from constants import SBA_CURRENT_SEASON from exceptions import APIException logger = logging.getLogger(f'{__name__}.LeagueService') @@ -63,7 +62,7 @@ class LeagueService(BaseService[Current]): List of standings data or None if not available """ try: - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season client = await self.get_client() data = await client.get('standings', params=[('season', str(season))]) @@ -96,7 +95,7 @@ class LeagueService(BaseService[Current]): List of division standings or None if not available """ try: - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season client = await self.get_client() data = await client.get(f'standings/division/{division_id}', params=[('season', str(season))]) @@ -113,6 +112,7 @@ class LeagueService(BaseService[Current]): async def get_league_leaders(self, stat_type: str = 'batting', season: Optional[int] = None, limit: int = 10) -> Optional[List[Dict[str, Any]]]: """ +from config import get_config Get league leaders for a specific statistic category. Args: @@ -124,7 +124,7 @@ class LeagueService(BaseService[Current]): List of league leaders or None if not available """ try: - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season client = await self.get_client() params = [ diff --git a/services/player_service.py b/services/player_service.py index 7a94d1c..4022485 100644 --- a/services/player_service.py +++ b/services/player_service.py @@ -8,7 +8,6 @@ from typing import Optional, List, TYPE_CHECKING from services.base_service import BaseService from models.player import Player -from constants import FREE_AGENT_TEAM_ID, SBA_CURRENT_SEASON from exceptions import APIException if TYPE_CHECKING: @@ -192,8 +191,7 @@ class PlayerService(BaseService[Player]): """ try: if season is None: - from constants import SBA_CURRENT_SEASON - season = SBA_CURRENT_SEASON + season = get_config().sba_current_season # Use the existing name-based search that actually works players = await self.get_players_by_name(query, season) @@ -233,7 +231,7 @@ class PlayerService(BaseService[Player]): List of free agent players """ try: - params = [('team_id', FREE_AGENT_TEAM_ID), ('season', str(season))] + params = [('team_id', get_config().free_agent_team_id), ('season', str(season))] players = await self.get_all_items(params=params) logger.debug(f"Retrieved {len(players)} free agents") @@ -253,7 +251,7 @@ class PlayerService(BaseService[Player]): Returns: True if player is a free agent """ - return player.team_id == FREE_AGENT_TEAM_ID + return player.team_id == get_config().free_agent_team_id async def get_players_by_position(self, position: str, season: int) -> List[Player]: """ @@ -280,6 +278,7 @@ class PlayerService(BaseService[Player]): async def update_player(self, player_id: int, updates: dict) -> Optional[Player]: """ +from config import get_config Update player information. Args: diff --git a/services/team_service.py b/services/team_service.py index 20f8afd..68a80a9 100644 --- a/services/team_service.py +++ b/services/team_service.py @@ -8,7 +8,6 @@ from typing import Optional, List, Dict, Any from services.base_service import BaseService from models.team import Team, RosterType -from constants import SBA_CURRENT_SEASON from exceptions import APIException logger = logging.getLogger(f'{__name__}.TeamService') @@ -69,7 +68,7 @@ class TeamService(BaseService[Team]): List of Team instances owned by the user, optionally filtered by type """ try: - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season params = [ ('owner_id', str(owner_id)), ('season', str(season)) @@ -109,7 +108,7 @@ class TeamService(BaseService[Team]): Team instance or None if not found """ try: - season = season or SBA_CURRENT_SEASON + season = season or get_config().sba_current_season params = [ ('team_abbrev', abbrev.upper()), ('season', str(season)) @@ -300,12 +299,13 @@ class TeamService(BaseService[Team]): async def get_current_season_teams(self) -> List[Team]: """ +from config import get_config Get all teams for the current season. Returns: List of teams in current season """ - return await self.get_teams_by_season(SBA_CURRENT_SEASON) + return await self.get_teams_by_season(get_config().sba_current_season) # Global service instance diff --git a/services/trade_builder.py b/services/trade_builder.py index 0cdebb1..e7a9588 100644 --- a/services/trade_builder.py +++ b/services/trade_builder.py @@ -15,7 +15,6 @@ from services.transaction_builder import TransactionBuilder, RosterValidationRes from services.team_service import team_service from services.roster_service import roster_service from services.league_service import league_service -from constants import SBA_CURRENT_SEASON, FREE_AGENT_TEAM_ID logger = logging.getLogger(f'{__name__}.TradeBuilder') @@ -66,7 +65,7 @@ class TradeBuilder: Extends the functionality of TransactionBuilder to support trades between teams. """ - def __init__(self, initiated_by: int, initiating_team: Team, season: int = SBA_CURRENT_SEASON): + def __init__(self, initiated_by: int, initiating_team: Team, season: int = get_config().sba_current_season): """ Initialize trade builder. @@ -190,7 +189,7 @@ class TradeBuilder: Tuple of (success: bool, error_message: str) """ # Validate player is not from Free Agency - if player.team_id == FREE_AGENT_TEAM_ID: + if player.team_id == get_config().free_agent_team_id: return False, f"Cannot add {player.name} from Free Agency. Players must be traded from teams within the organizations involved in the trade." # Validate player has a valid team assignment @@ -467,6 +466,7 @@ def get_trade_builder(user_id: int, initiating_team: Team) -> TradeBuilder: def clear_trade_builder(user_id: int) -> None: """Clear trade builder for a user.""" +from config import get_config trade_key = f"{user_id}:trade" if trade_key in _active_trade_builders: del _active_trade_builders[trade_key] diff --git a/services/transaction_builder.py b/services/transaction_builder.py index e6e27cb..9d812f2 100644 --- a/services/transaction_builder.py +++ b/services/transaction_builder.py @@ -19,7 +19,6 @@ from services.roster_service import roster_service from services.transaction_service import transaction_service from services.league_service import league_service from models.team import RosterType -from constants import SBA_CURRENT_SEASON logger = logging.getLogger(f'{__name__}.TransactionBuilder') @@ -136,7 +135,7 @@ class RosterValidationResult: class TransactionBuilder: """Interactive transaction builder for complex multi-move transactions.""" - def __init__(self, team: Team, user_id: int, season: int = SBA_CURRENT_SEASON): + def __init__(self, team: Team, user_id: int, season: int = get_config().sba_current_season): """ Initialize transaction builder. @@ -523,6 +522,7 @@ def get_transaction_builder(user_id: int, team: Team) -> TransactionBuilder: def clear_transaction_builder(user_id: int) -> None: """Clear transaction builder for a user.""" +from config import get_config if user_id in _active_builders: del _active_builders[user_id] logger.info(f"Cleared transaction builder for user {user_id}") \ No newline at end of file diff --git a/test_real_data.py b/test_real_data.py index 01135ac..a7c4dc4 100644 --- a/test_real_data.py +++ b/test_real_data.py @@ -22,7 +22,6 @@ os.environ.setdefault('TESTING', 'true') from services.player_service import player_service from utils.logging import get_contextual_logger, set_discord_context from api.client import cleanup_global_client -from constants import SBA_CURRENT_SEASON logger = get_contextual_logger('test_real_data') @@ -68,7 +67,7 @@ async def test_player_search(): try: # Test 1: Search for a common name (should find multiple) logger.info("Testing search for common player name") - players = await player_service.get_players_by_name("Smith", SBA_CURRENT_SEASON) + players = await player_service.get_players_by_name("Smith", get_config().sba_current_season) logger.info("Common name search completed", search_term="Smith", results_found=len(players)) @@ -82,7 +81,7 @@ async def test_player_search(): # Test 2: Search for specific player (exact match) logger.info("Testing search for specific player") - players = await player_service.get_players_by_name("Mike Trout", SBA_CURRENT_SEASON) + players = await player_service.get_players_by_name("Mike Trout", get_config().sba_current_season) logger.info("Specific player search completed", search_term="Mike Trout", results_found=len(players)) @@ -142,10 +141,9 @@ async def test_player_service_methods(): try: # Test get_all with limit (need to include season) - from constants import SBA_CURRENT_SEASON logger.info("Testing get_all with limit") players, total_count = await player_service.get_all(params=[ - ('season', str(SBA_CURRENT_SEASON)), + ('season', str(get_config().sba_current_season)), ('limit', '10') ]) @@ -154,7 +152,7 @@ async def test_player_service_methods(): retrieved_count=len(players), total_count=total_count, limit=10, - season=SBA_CURRENT_SEASON) + season=get_config().sba_current_season) if players: print(" Sample players:") @@ -165,7 +163,7 @@ async def test_player_service_methods(): if players: test_position = players[0].primary_position logger.info("Testing position search", position=test_position) - position_players = await player_service.get_players_by_position(test_position, SBA_CURRENT_SEASON) + position_players = await player_service.get_players_by_position(test_position, get_config().sba_current_season) print(f" ✅ Found {len(position_players)} players at position {test_position}") logger.info("Position search completed", @@ -194,7 +192,8 @@ async def test_api_connectivity(): try: from api.client import get_global_client - + from config import get_config + logger.info("Testing basic API connection") client = await get_global_client() diff --git a/tests/test_constants.py b/tests/test_constants.py index 7c4b483..e95de44 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -1,95 +1,86 @@ """ -Tests for application constants +Tests for application configuration -Validates that constants have sensible values. +Validates that config values have sensible defaults. """ import pytest -from constants import ( - DISCORD_EMBED_LIMIT, - DISCORD_FIELD_VALUE_LIMIT, - DISCORD_EMBED_DESCRIPTION_LIMIT, - WEEKS_PER_SEASON, - GAMES_PER_WEEK, - MODERN_STATS_START_SEASON, - API_VERSION, - DEFAULT_TIMEOUT, - MAX_RETRIES, - PITCHER_POSITIONS, - POSITION_FIELDERS, - ALL_POSITIONS, - DEFAULT_PICK_MINUTES, - DRAFT_ROUNDS -) +from config import get_config, PITCHER_POSITIONS, POSITION_FIELDERS, ALL_POSITIONS class TestDiscordLimits: """Test Discord API limits are reasonable.""" - + def test_discord_limits_are_positive(self): """Test that all Discord limits are positive integers.""" - assert DISCORD_EMBED_LIMIT > 0 - assert DISCORD_FIELD_VALUE_LIMIT > 0 - assert DISCORD_EMBED_DESCRIPTION_LIMIT > 0 - - assert isinstance(DISCORD_EMBED_LIMIT, int) - assert isinstance(DISCORD_FIELD_VALUE_LIMIT, int) - assert isinstance(DISCORD_EMBED_DESCRIPTION_LIMIT, int) - + config = get_config() + assert config.discord_embed_limit > 0 + assert config.discord_field_value_limit > 0 + assert config.discord_embed_description_limit > 0 + + assert isinstance(config.discord_embed_limit, int) + assert isinstance(config.discord_field_value_limit, int) + assert isinstance(config.discord_embed_description_limit, int) + def test_discord_limits_hierarchy(self): """Test that Discord limits have sensible relationships.""" + config = get_config() # Description should be larger than field values - assert DISCORD_EMBED_DESCRIPTION_LIMIT > DISCORD_FIELD_VALUE_LIMIT - + assert config.discord_embed_description_limit > config.discord_field_value_limit + # Total embed limit should be larger than description limit - assert DISCORD_EMBED_LIMIT > DISCORD_EMBED_DESCRIPTION_LIMIT + assert config.discord_embed_limit > config.discord_embed_description_limit class TestLeagueConstants: """Test league-specific constants.""" - + def test_league_constants_are_positive(self): """Test that league constants are positive.""" - assert WEEKS_PER_SEASON > 0 - assert GAMES_PER_WEEK > 0 - assert MODERN_STATS_START_SEASON > 0 - - assert isinstance(WEEKS_PER_SEASON, int) - assert isinstance(GAMES_PER_WEEK, int) - assert isinstance(MODERN_STATS_START_SEASON, int) - + config = get_config() + assert config.weeks_per_season > 0 + assert config.games_per_week > 0 + assert config.modern_stats_start_season > 0 + + assert isinstance(config.weeks_per_season, int) + assert isinstance(config.games_per_week, int) + assert isinstance(config.modern_stats_start_season, int) + def test_league_constants_are_reasonable(self): """Test that league constants have reasonable values.""" + config = get_config() # Baseball season should be reasonable length - assert 10 <= WEEKS_PER_SEASON <= 30 - + assert 10 <= config.weeks_per_season <= 30 + # Games per week should be reasonable - assert 1 <= GAMES_PER_WEEK <= 7 - + assert 1 <= config.games_per_week <= 7 + # Modern stats era should be reasonable - assert 1 <= MODERN_STATS_START_SEASON <= 20 + assert 1 <= config.modern_stats_start_season <= 20 class TestAPIConstants: """Test API-related constants.""" - + def test_api_version_format(self): """Test that API version is properly formatted.""" - assert isinstance(API_VERSION, str) - assert API_VERSION.startswith("v") - assert API_VERSION[1:].isdigit() # Should be like "v3" - + config = get_config() + assert isinstance(config.api_version, str) + assert config.api_version.startswith("v") + assert config.api_version[1:].isdigit() # Should be like "v3" + def test_timeout_and_retry_values(self): """Test that timeout and retry values are reasonable.""" - assert DEFAULT_TIMEOUT > 0 - assert MAX_RETRIES > 0 - - assert isinstance(DEFAULT_TIMEOUT, int) - assert isinstance(MAX_RETRIES, int) - + config = get_config() + assert config.default_timeout > 0 + assert config.max_retries > 0 + + assert isinstance(config.default_timeout, int) + assert isinstance(config.max_retries, int) + # Should be reasonable values - assert 1 <= DEFAULT_TIMEOUT <= 60 # 1-60 seconds - assert 1 <= MAX_RETRIES <= 10 # 1-10 retries + assert 1 <= config.default_timeout <= 60 # 1-60 seconds + assert 1 <= config.max_retries <= 10 # 1-10 retries class TestPositionConstants: @@ -137,19 +128,21 @@ class TestPositionConstants: class TestDraftConstants: """Test draft-related constants.""" - + def test_draft_constants_are_positive(self): """Test that draft constants are positive.""" - assert DEFAULT_PICK_MINUTES > 0 - assert DRAFT_ROUNDS > 0 - - assert isinstance(DEFAULT_PICK_MINUTES, int) - assert isinstance(DRAFT_ROUNDS, int) - + config = get_config() + assert config.default_pick_minutes > 0 + assert config.draft_rounds > 0 + + assert isinstance(config.default_pick_minutes, int) + assert isinstance(config.draft_rounds, int) + def test_draft_constants_are_reasonable(self): """Test that draft constants have reasonable values.""" + config = get_config() # Pick minutes should be reasonable - assert 1 <= DEFAULT_PICK_MINUTES <= 60 - + assert 1 <= config.default_pick_minutes <= 60 + # Draft rounds should be reasonable for fantasy baseball - assert 10 <= DRAFT_ROUNDS <= 50 \ No newline at end of file + assert 10 <= config.draft_rounds <= 50 \ No newline at end of file diff --git a/tests/test_services_player_service.py b/tests/test_services_player_service.py index 8da0fd3..fac643f 100644 --- a/tests/test_services_player_service.py +++ b/tests/test_services_player_service.py @@ -6,7 +6,6 @@ from unittest.mock import AsyncMock from services.player_service import PlayerService, player_service from models.player import Player -from constants import FREE_AGENT_TEAM_ID from exceptions import APIException @@ -168,8 +167,8 @@ class TestPlayerService: mock_data = { 'count': 2, 'players': [ - self.create_player_data(1, 'Free Agent 1', team_id=FREE_AGENT_TEAM_ID), - self.create_player_data(2, 'Free Agent 2', team_id=FREE_AGENT_TEAM_ID) + self.create_player_data(1, 'Free Agent 1', team_id=get_config().free_agent_team_id), + self.create_player_data(2, 'Free Agent 2', team_id=get_config().free_agent_team_id) ] } mock_client.get.return_value = mock_data @@ -177,14 +176,14 @@ class TestPlayerService: result = await player_service_instance.get_free_agents(season=12) assert len(result) == 2 - assert all(p.team_id == FREE_AGENT_TEAM_ID for p in result) - mock_client.get.assert_called_once_with('players', params=[('team_id', FREE_AGENT_TEAM_ID), ('season', '12')]) + assert all(p.team_id == get_config().free_agent_team_id for p in result) + mock_client.get.assert_called_once_with('players', params=[('team_id', get_config().free_agent_team_id), ('season', '12')]) @pytest.mark.asyncio async def test_is_free_agent(self, player_service_instance): """Test free agent checking.""" # Create test players with all required fields - free_agent_data = self.create_player_data(1, 'Free Agent', team_id=FREE_AGENT_TEAM_ID) + free_agent_data = self.create_player_data(1, 'Free Agent', team_id=get_config().free_agent_team_id) regular_player_data = self.create_player_data(2, 'Regular Player', team_id=5) free_agent = Player.from_api_data(free_agent_data) @@ -312,8 +311,7 @@ class TestPlayerServiceExtras: async def test_player_service_additional_methods(self): """Test additional PlayerService methods for coverage.""" from services.player_service import PlayerService - from constants import FREE_AGENT_TEAM_ID - + mock_client = AsyncMock() player_service = PlayerService() player_service._client = mock_client @@ -340,6 +338,7 @@ class TestGlobalPlayerServiceInstance: @pytest.mark.asyncio async def test_service_independence(self): """Test that service instances are independent.""" +from config import get_config service1 = PlayerService() service2 = PlayerService() diff --git a/tests/test_services_team_service.py b/tests/test_services_team_service.py index 7de1867..3352d39 100644 --- a/tests/test_services_team_service.py +++ b/tests/test_services_team_service.py @@ -6,7 +6,6 @@ from unittest.mock import AsyncMock from services.team_service import TeamService, team_service from models.team import Team -from constants import SBA_CURRENT_SEASON from exceptions import APIException @@ -259,8 +258,8 @@ class TestTeamService: mock_data = { 'count': 2, 'teams': [ - self.create_team_data(1, 'TEA', season=SBA_CURRENT_SEASON), - self.create_team_data(2, 'TEB', season=SBA_CURRENT_SEASON) + self.create_team_data(1, 'TEA', season=get_config().sba_current_season), + self.create_team_data(2, 'TEB', season=get_config().sba_current_season) ] } mock_client.get.return_value = mock_data @@ -268,8 +267,8 @@ class TestTeamService: result = await team_service_instance.get_current_season_teams() assert len(result) == 2 - assert all(team.season == SBA_CURRENT_SEASON for team in result) - mock_client.get.assert_called_once_with('teams', params=[('season', str(SBA_CURRENT_SEASON))]) + assert all(team.season == get_config().sba_current_season for team in result) + mock_client.get.assert_called_once_with('teams', params=[('season', str(get_config().sba_current_season))]) @pytest.mark.asyncio async def test_error_handling(self, team_service_instance, mock_client): @@ -315,6 +314,7 @@ class TestGlobalTeamServiceInstance: def test_team_service_global(self): """Test global team service instance.""" +from config import get_config assert isinstance(team_service, TeamService) assert team_service.model_class == Team assert team_service.endpoint == 'teams' diff --git a/tests/test_services_trade_builder.py b/tests/test_services_trade_builder.py index 3df47aa..13f72c0 100644 --- a/tests/test_services_trade_builder.py +++ b/tests/test_services_trade_builder.py @@ -16,7 +16,6 @@ from services.trade_builder import ( ) from models.trade import TradeStatus from models.team import RosterType, Team -from constants import FREE_AGENT_TEAM_ID from tests.factories import PlayerFactory, TeamFactory @@ -155,7 +154,7 @@ class TestTradeBuilder: fa_player = PlayerFactory.create( id=100, name="FA Player", - team_id=FREE_AGENT_TEAM_ID + team_id=get_config().free_agent_team_id ) # Try to add player from FA (should fail) @@ -549,6 +548,7 @@ class TestTradeValidationResult: # Mock participant validations from services.transaction_builder import RosterValidationResult +from config import get_config team1_validation = RosterValidationResult( is_legal=False, diff --git a/utils/autocomplete.py b/utils/autocomplete.py index 046d2bb..dd8e08e 100644 --- a/utils/autocomplete.py +++ b/utils/autocomplete.py @@ -10,7 +10,6 @@ from discord import app_commands from services.player_service import player_service from services.team_service import team_service from utils.team_utils import get_user_major_league_team -from constants import SBA_CURRENT_SEASON async def player_autocomplete( @@ -37,7 +36,7 @@ async def player_autocomplete( user_team = await get_user_major_league_team(interaction.user.id) # Search for players using the search endpoint - players = await player_service.search_players(current, limit=50, season=SBA_CURRENT_SEASON) + players = await player_service.search_players(current, limit=50, season=get_config().sba_current_season) # Separate players by team (user's team vs others) user_team_players = [] @@ -105,7 +104,7 @@ async def team_autocomplete( try: # Get all teams for current season - teams = await team_service.get_teams_by_season(SBA_CURRENT_SEASON) + teams = await team_service.get_teams_by_season(get_config().sba_current_season) # Filter teams by current input and limit to 25 matching_teams = [ @@ -146,10 +145,11 @@ async def major_league_team_autocomplete( try: # Get all teams for current season - all_teams = await team_service.get_teams_by_season(SBA_CURRENT_SEASON) + all_teams = await team_service.get_teams_by_season(get_config().sba_current_season) # Filter to only Major League teams using the model's helper method from models.team import RosterType +from config import get_config ml_teams = [ team for team in all_teams if team.roster_type() == RosterType.MAJOR_LEAGUE diff --git a/utils/team_utils.py b/utils/team_utils.py index a08c275..6a3f4f7 100644 --- a/utils/team_utils.py +++ b/utils/team_utils.py @@ -8,12 +8,11 @@ import discord from models.team import Team from services.team_service import team_service -from constants import SBA_CURRENT_SEASON async def get_user_major_league_team( user_id: int, - season: int = SBA_CURRENT_SEASON + season: int = get_config().sba_current_season ) -> Optional[Team]: """ Get the major league team owned by a Discord user. @@ -47,7 +46,7 @@ async def get_user_major_league_team( async def validate_user_has_team( interaction: discord.Interaction, - season: int = SBA_CURRENT_SEASON + season: int = get_config().sba_current_season ) -> Optional[Team]: """ Validate that a user has a major league team and send error message if not. @@ -76,7 +75,7 @@ async def validate_user_has_team( async def get_team_by_abbrev_with_validation( team_abbrev: str, interaction: discord.Interaction, - season: int = SBA_CURRENT_SEASON + season: int = get_config().sba_current_season ) -> Optional[Team]: """ Get a team by abbreviation with standard error messaging. @@ -89,6 +88,7 @@ async def get_team_by_abbrev_with_validation( Returns: Team object if found, None if not (error message already sent) """ +from config import get_config try: team = await team_service.get_team_by_abbrev(team_abbrev, season) diff --git a/views/embeds.py b/views/embeds.py index 1c0b266..e6779e0 100644 --- a/views/embeds.py +++ b/views/embeds.py @@ -9,7 +9,6 @@ from dataclasses import dataclass import discord -from constants import SBA_CURRENT_SEASON @dataclass(frozen=True) @@ -154,7 +153,7 @@ class SBAEmbedTemplate(EmbedTemplate): embed.add_field( name="Season", - value=str(season or SBA_CURRENT_SEASON), + value=str(season or get_config().sba_current_season), inline=True ) @@ -191,7 +190,7 @@ class SBAEmbedTemplate(EmbedTemplate): embed = EmbedTemplate.create_base_embed( title=f"{team_abbrev} - {team_name}", - description=f"Season {season or SBA_CURRENT_SEASON} Team Information", + description=f"Season {season or get_config().sba_current_season} Team Information", color=color ) @@ -200,7 +199,7 @@ class SBAEmbedTemplate(EmbedTemplate): embed.add_field(name="Short Name", value=short_name, inline=True) embed.add_field(name="Abbreviation", value=team_abbrev, inline=True) - embed.add_field(name="Season", value=str(season or SBA_CURRENT_SEASON), inline=True) + embed.add_field(name="Season", value=str(season or get_config().sba_current_season), inline=True) if stadium: embed.add_field(name="Stadium", value=stadium, inline=True) @@ -275,7 +274,7 @@ class SBAEmbedTemplate(EmbedTemplate): embed = EmbedTemplate.create_base_embed( title=f"{team_abbrev} - {roster_type}", - description=f"{team_name} • Season {season or SBA_CURRENT_SEASON}", + description=f"{team_name} • Season {season or get_config().sba_current_season}", color=color ) @@ -385,6 +384,7 @@ class EmbedBuilder: def author(self, name: str, url: Optional[str] = None, icon_url: Optional[str] = None) -> 'EmbedBuilder': """Set embed author.""" +from config import get_config self._embed.set_author(name=name, url=url, icon_url=icon_url) return self