From 5f69d495ab1808622c87eeee1fc6e0644f427246 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sat, 25 Oct 2025 19:35:50 -0500 Subject: [PATCH] CLAUDE: Fix draft list operations and improve add success display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple fixes for draft list functionality: 1. **Model Fix (draft_list.py):** - API returns nested Team and Player objects, not just IDs - Changed team_id/player_id from fields to @property methods - Extract IDs from nested objects via properties - Fixes Pydantic validation errors on GET operations 2. **Service Fix (draft_list_service.py):** - Override _extract_items_and_count_from_response() for API quirk - GET returns items under 'picks' key (not 'draftlist') - Changed add_to_list() return type from single entry to full list - Return verification list instead of trying to create new DraftList - Fixes "Failed to add" error from validation issues 3. **Command Enhancement (list.py):** - Display full draft list on successful add (not just confirmation) - Show position where player was added - Reuse existing create_draft_list_embed() for consistency - Better UX - user sees complete context after adding player API Response Format: GET: {"count": N, "picks": [{team: {...}, player: {...}}]} POST: {"count": N, "draft_list": [{team_id: X, player_id: Y}]} This resolves: - Empty list after adding player (Pydantic validation) - "Add Failed" error despite successful operation - Poor UX with minimal success feedback 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- commands/draft/list.py | 16 ++++++---- models/draft_list.py | 30 +++++++++++-------- services/draft_list_service.py | 54 +++++++++++++++++++++++++++++----- 3 files changed, 75 insertions(+), 25 deletions(-) diff --git a/commands/draft/list.py b/commands/draft/list.py index 0ca44a4..6e2261c 100644 --- a/commands/draft/list.py +++ b/commands/draft/list.py @@ -173,14 +173,14 @@ class DraftListCommands(commands.Cog): return # Add to list - entry = await draft_list_service.add_to_list( + updated_list = await draft_list_service.add_to_list( config.sba_current_season, team.id, player_obj.id, rank ) - if not entry: + if not updated_list: embed = EmbedTemplate.error( "Add Failed", f"Failed to add {player_obj.name} to draft queue." @@ -188,11 +188,15 @@ class DraftListCommands(commands.Cog): await interaction.followup.send(embed=embed, ephemeral=True) return - # Success message - rank_str = f"#{entry.rank}" if entry.rank else "at end" - description = f"Added **{player_obj.name}** to your draft queue at position **{rank_str}**." + # Find the added entry to get its rank + added_entry = next((e for e in updated_list if e.player_id == player_obj.id), None) + rank_str = f"#{added_entry.rank}" if added_entry else "at end" + + # Success message with full draft list + success_msg = f"✅ Added **{player_obj.name}** at position **{rank_str}**" + embed = await create_draft_list_embed(team, updated_list) + embed.description = f"{success_msg}\n\n{embed.description}" - embed = EmbedTemplate.success("Player Added", description) await interaction.followup.send(embed=embed) @discord.app_commands.command( diff --git a/models/draft_list.py b/models/draft_list.py index e2364fd..920570f 100644 --- a/models/draft_list.py +++ b/models/draft_list.py @@ -13,22 +13,28 @@ from models.player import Player class DraftList(SBABaseModel): """Draft preference list entry for a team.""" - + season: int = Field(..., description="Draft season") - team_id: int = Field(..., description="Team ID that owns this list entry") rank: int = Field(..., description="Ranking of player on team's draft board") - player_id: int = Field(..., description="Player ID on the draft board") - - # Related objects (populated when needed) - team: Optional[Team] = Field(None, description="Team object (populated when needed)") - player: Optional[Player] = Field(None, description="Player object (populated when needed)") - + + # API returns nested objects (not just IDs) + team: Team = Field(..., description="Team object") + player: Player = Field(..., description="Player object") + + @property + def team_id(self) -> int: + """Extract team ID from nested team object.""" + return self.team.id + + @property + def player_id(self) -> int: + """Extract player ID from nested player object.""" + return self.player.id + @property def is_top_ranked(self) -> bool: """Check if this is the team's top-ranked available player.""" return self.rank == 1 - + def __str__(self): - team_str = self.team.abbrev if self.team else f"Team {self.team_id}" - player_str = self.player.name if self.player else f"Player {self.player_id}" - return f"{team_str} Draft Board #{self.rank}: {player_str}" \ No newline at end of file + return f"{self.team.abbrev} Draft Board #{self.rank}: {self.player.name}" \ No newline at end of file diff --git a/services/draft_list_service.py b/services/draft_list_service.py index 3ee0cd7..1c1b3a3 100644 --- a/services/draft_list_service.py +++ b/services/draft_list_service.py @@ -20,6 +20,9 @@ class DraftListService(BaseService[DraftList]): IMPORTANT: This service does NOT use caching decorators because draft lists change as users add/remove players from their auto-draft queues. + API QUIRK: GET endpoint returns items under 'picks' key, not 'draftlist'. + POST endpoint expects items under 'draft_list' key. + Features: - Get team's draft list (ranked by priority) - Add player to draft list @@ -33,6 +36,35 @@ class DraftListService(BaseService[DraftList]): super().__init__(DraftList, 'draftlist') logger.debug("DraftListService initialized") + def _extract_items_and_count_from_response(self, data): + """ + Override to handle API quirk: GET returns 'picks' instead of 'draftlist'. + + Args: + data: API response data + + Returns: + Tuple of (items list, total count) + """ + from typing import Any, Dict, List, Tuple + + if isinstance(data, list): + return data, len(data) + + if not isinstance(data, dict): + logger.warning(f"Unexpected response format: {type(data)}") + return [], 0 + + # Get count + count = data.get('count', 0) + + # API returns items under 'picks' key (not 'draftlist') + if 'picks' in data and isinstance(data['picks'], list): + return data['picks'], count or len(data['picks']) + + # Fallback to standard extraction + return super()._extract_items_and_count_from_response(data) + async def get_team_list( self, season: int, @@ -71,7 +103,7 @@ class DraftListService(BaseService[DraftList]): team_id: int, player_id: int, rank: Optional[int] = None - ) -> Optional[DraftList]: + ) -> Optional[List[DraftList]]: """ Add player to team's draft list. @@ -87,7 +119,7 @@ class DraftListService(BaseService[DraftList]): rank: Priority rank (1 = highest), None = add to end Returns: - Created DraftList entry or None if creation failed + Full updated draft list or None if operation failed """ try: # Get current list @@ -140,13 +172,21 @@ class DraftListService(BaseService[DraftList]): 'draft_list': draft_list_entries } - await client.post(self.endpoint, payload) + logger.debug(f"Posting draft list for team {team_id}: {len(draft_list_entries)} entries") + response = await client.post(self.endpoint, payload) + logger.debug(f"POST response: {response}") + + # Verify by fetching the list back (API returns full objects) + verification = await self.get_team_list(season, team_id) + logger.debug(f"Verification: found {len(verification)} entries after POST") + + # Verify the player was added + if not any(entry.player_id == player_id for entry in verification): + logger.error(f"Player {player_id} not found in list after POST - operation may have failed") + return None - # Return the created entry as a DraftList object - created_entry = DraftList.from_api_data(new_entry_data) logger.info(f"Added player {player_id} to team {team_id} draft list at rank {rank}") - - return created_entry + return verification # Return full updated list except Exception as e: logger.error(f"Error adding player {player_id} to draft list: {e}")