Major fixes and improvements: Trade System Fixes: - Fix duplicate player moves in trade embed Player Exchanges section - Resolve "WVMiL not participating" error for Minor League destinations - Implement organizational authority model for ML/MiL/IL team relationships - Update Trade.cross_team_moves to deduplicate using moves_giving only Team Model Enhancements: - Rewrite roster_type() method using sname as definitive source per spec - Fix edge cases like "BHMIL" (Birmingham IL) vs "BHMMIL" - Update _get_base_abbrev() to use consistent sname-based logic - Add organizational lookup support in trade participation Autocomplete System: - Fix major_league_team_autocomplete invalid roster_type parameter - Implement client-side filtering using Team.roster_type() method - Add comprehensive test coverage for all autocomplete functions - Centralize autocomplete logic to shared utils functions Test Infrastructure: - Add 25 new tests for trade models and trade builder - Add 13 autocomplete function tests with error handling - Fix existing test failures with proper mocking patterns - Update dropadd tests to use shared autocomplete functions Documentation Updates: - Document trade model enhancements and deduplication fix - Add autocomplete function documentation with usage examples - Document organizational authority model and edge case handling - Update README files with recent fixes and implementation notes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
173 lines
5.4 KiB
Python
173 lines
5.4 KiB
Python
"""
|
|
Autocomplete Utilities
|
|
|
|
Shared autocomplete functions for Discord slash commands.
|
|
"""
|
|
from typing import List, Optional
|
|
import discord
|
|
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(
|
|
interaction: discord.Interaction,
|
|
current: str
|
|
) -> List[app_commands.Choice[str]]:
|
|
"""
|
|
Autocomplete for player names with team context prioritization.
|
|
|
|
Prioritizes players from the user's team first, then shows other players.
|
|
|
|
Args:
|
|
interaction: Discord interaction object
|
|
current: Current input from user
|
|
|
|
Returns:
|
|
List of player name choices (user's team players first)
|
|
"""
|
|
if len(current) < 2:
|
|
return []
|
|
|
|
try:
|
|
# Get user's team for prioritization
|
|
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)
|
|
|
|
# Separate players by team (user's team vs others)
|
|
user_team_players = []
|
|
other_players = []
|
|
|
|
for player in players:
|
|
# Check if player belongs to user's team (any roster section)
|
|
is_users_player = False
|
|
if user_team and hasattr(player, 'team') and player.team:
|
|
# Check if player is from user's major league team or has same base team
|
|
if (player.team.id == user_team.id or
|
|
(hasattr(player, 'team_id') and player.team_id == user_team.id)):
|
|
is_users_player = True
|
|
|
|
if is_users_player:
|
|
user_team_players.append(player)
|
|
else:
|
|
other_players.append(player)
|
|
|
|
# Format choices with team context
|
|
choices = []
|
|
|
|
# Add user's team players first (prioritized)
|
|
for player in user_team_players[:15]: # Limit user team players
|
|
team_info = f"{player.primary_position}"
|
|
if hasattr(player, 'team') and player.team:
|
|
team_info += f" - {player.team.abbrev}"
|
|
|
|
choice_name = f"{player.name} ({team_info})"
|
|
choices.append(app_commands.Choice(name=choice_name, value=player.name))
|
|
|
|
# Add other players (remaining slots)
|
|
remaining_slots = 25 - len(choices)
|
|
for player in other_players[:remaining_slots]:
|
|
team_info = f"{player.primary_position}"
|
|
if hasattr(player, 'team') and player.team:
|
|
team_info += f" - {player.team.abbrev}"
|
|
|
|
choice_name = f"{player.name} ({team_info})"
|
|
choices.append(app_commands.Choice(name=choice_name, value=player.name))
|
|
|
|
return choices
|
|
|
|
except Exception:
|
|
# Silently fail on autocomplete errors to avoid disrupting user experience
|
|
return []
|
|
|
|
|
|
async def team_autocomplete(
|
|
interaction: discord.Interaction,
|
|
current: str
|
|
) -> List[app_commands.Choice[str]]:
|
|
"""
|
|
Autocomplete for team abbreviations.
|
|
|
|
Args:
|
|
interaction: Discord interaction object
|
|
current: Current input from user
|
|
|
|
Returns:
|
|
List of team abbreviation choices
|
|
"""
|
|
if len(current) < 1:
|
|
return []
|
|
|
|
try:
|
|
# Get all teams for current season
|
|
teams = await team_service.get_teams_by_season(SBA_CURRENT_SEASON)
|
|
|
|
# Filter teams by current input and limit to 25
|
|
matching_teams = [
|
|
team for team in teams
|
|
if current.lower() in team.abbrev.lower() or current.lower() in team.sname.lower()
|
|
][:25]
|
|
|
|
choices = []
|
|
for team in matching_teams:
|
|
choice_name = f"{team.abbrev} - {team.sname}"
|
|
choices.append(app_commands.Choice(name=choice_name, value=team.abbrev))
|
|
|
|
return choices
|
|
|
|
except Exception:
|
|
# Silently fail on autocomplete errors
|
|
return []
|
|
|
|
|
|
async def major_league_team_autocomplete(
|
|
interaction: discord.Interaction,
|
|
current: str
|
|
) -> List[app_commands.Choice[str]]:
|
|
"""
|
|
Autocomplete for Major League team abbreviations only.
|
|
|
|
Used for trade commands where only ML team owners should be able to initiate trades.
|
|
|
|
Args:
|
|
interaction: Discord interaction object
|
|
current: Current input from user
|
|
|
|
Returns:
|
|
List of Major League team abbreviation choices
|
|
"""
|
|
if len(current) < 1:
|
|
return []
|
|
|
|
try:
|
|
# Get all teams for current season
|
|
all_teams = await team_service.get_teams_by_season(SBA_CURRENT_SEASON)
|
|
|
|
# Filter to only Major League teams using the model's helper method
|
|
from models.team import RosterType
|
|
ml_teams = [
|
|
team for team in all_teams
|
|
if team.roster_type() == RosterType.MAJOR_LEAGUE
|
|
]
|
|
|
|
# Filter teams by current input and limit to 25
|
|
matching_teams = [
|
|
team for team in ml_teams
|
|
if current.lower() in team.abbrev.lower() or current.lower() in team.sname.lower()
|
|
][:25]
|
|
|
|
choices = []
|
|
for team in matching_teams:
|
|
choice_name = f"{team.abbrev} - {team.sname}"
|
|
choices.append(app_commands.Choice(name=choice_name, value=team.abbrev))
|
|
|
|
return choices
|
|
|
|
except Exception:
|
|
# Silently fail on autocomplete errors
|
|
return [] |