CLAUDE: Fix draft list operations and improve add success display

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 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2025-10-25 19:35:50 -05:00
parent 07f69ebd77
commit 5f69d495ab
3 changed files with 75 additions and 25 deletions

View File

@ -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(

View File

@ -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}"
return f"{self.team.abbrev} Draft Board #{self.rank}: {self.player.name}"

View File

@ -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}")