This commit includes various enhancements across the bot architecture: **New Infrastructure:** - Added tests/factories.py - Factory classes for creating test data objects - Added PRE_LAUNCH_ROADMAP.md - Project planning and roadmap documentation **Model Enhancements:** - Updated models/roster.py - Enhanced roster data structures - Updated models/team.py - Improved team model definitions **Service Layer Improvements:** - Enhanced services/player_service.py - Better player data handling - Updated services/roster_service.py - Roster management improvements - Enhanced services/team_service.py - Team data service refinements - Updated services/transaction_service.py - Transaction processing enhancements **Command Updates:** - Updated commands/teams/info.py - Team information command improvements - Enhanced commands/voice/tracker.py - Voice channel tracking refinements **Background Tasks:** - Updated tasks/custom_command_cleanup.py - Automated cleanup improvements **View Components:** - Enhanced views/transaction_embed.py - Transaction embed UI improvements **Test Coverage Enhancements:** - Updated tests/test_commands_voice.py - Voice command test improvements - Enhanced tests/test_dropadd_integration.py - Integration test coverage - Updated tests/test_services_player_service.py - Player service test coverage - Enhanced tests/test_services_transaction_builder.py - Transaction builder tests - Updated tests/test_transactions_integration.py - Transaction integration tests - Enhanced tests/test_views_transaction_embed.py - UI component test coverage These changes collectively improve the bot's reliability, maintainability, and test coverage while adding essential infrastructure for continued development. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
97 lines
4.0 KiB
Python
97 lines
4.0 KiB
Python
"""
|
|
Team model for SBA teams
|
|
|
|
Represents a team in the league with all associated metadata.
|
|
"""
|
|
from typing import Optional
|
|
from enum import Enum
|
|
from pydantic import Field
|
|
|
|
from models.base import SBABaseModel
|
|
from models.division import Division
|
|
|
|
|
|
class RosterType(Enum):
|
|
"""Roster designation types."""
|
|
MAJOR_LEAGUE = "ml"
|
|
MINOR_LEAGUE = "mil"
|
|
INJURED_LIST = "il"
|
|
FREE_AGENCY = "fa"
|
|
|
|
|
|
class Team(SBABaseModel):
|
|
"""Team model representing an SBA team."""
|
|
|
|
# Override base model to make id required for database entities
|
|
id: int = Field(..., description="Team ID from database")
|
|
|
|
abbrev: str = Field(..., description="Team abbreviation (e.g., 'NYY')")
|
|
sname: str = Field(..., description="Short team name")
|
|
lname: str = Field(..., description="Long team name")
|
|
season: int = Field(..., description="Season number")
|
|
|
|
# Manager information
|
|
gmid: Optional[int] = Field(None, description="Primary general manager ID")
|
|
gmid2: Optional[int] = Field(None, description="Secondary general manager ID")
|
|
manager1_id: Optional[int] = Field(None, description="Primary manager ID")
|
|
manager2_id: Optional[int] = Field(None, description="Secondary manager ID")
|
|
|
|
# Team metadata
|
|
division_id: Optional[int] = Field(None, description="Division ID")
|
|
division: Optional[Division] = Field(None, description="Division object (populated from API)")
|
|
stadium: Optional[str] = Field(None, description="Home stadium name")
|
|
thumbnail: Optional[str] = Field(None, description="Team thumbnail URL")
|
|
color: Optional[str] = Field(None, description="Primary team color")
|
|
dice_color: Optional[str] = Field(None, description="Dice rolling color")
|
|
|
|
@classmethod
|
|
def from_api_data(cls, data: dict) -> 'Team':
|
|
"""
|
|
Create Team instance from API data, handling nested division structure.
|
|
|
|
The API returns division data as a nested object, but our model expects
|
|
both division_id (int) and division (optional Division object).
|
|
"""
|
|
# Make a copy to avoid modifying original data
|
|
team_data = data.copy()
|
|
|
|
# Handle nested division structure
|
|
if 'division' in team_data and isinstance(team_data['division'], dict):
|
|
division_data = team_data['division']
|
|
# Extract division_id from nested division object
|
|
team_data['division_id'] = division_data.get('id')
|
|
# Keep division object for optional population
|
|
if division_data.get('id'):
|
|
team_data['division'] = Division.from_api_data(division_data)
|
|
|
|
return super().from_api_data(team_data)
|
|
|
|
def roster_type(self) -> RosterType:
|
|
"""Determine the roster type based on team abbreviation."""
|
|
if len(self.abbrev) <= 3:
|
|
return RosterType.MAJOR_LEAGUE
|
|
|
|
# For teams with extended abbreviations, check suffix patterns
|
|
abbrev_lower = self.abbrev.lower()
|
|
|
|
# Pattern analysis:
|
|
# - Minor League: ends with 'mil' (e.g., NYYMIL, BHMMIL)
|
|
# - Injured List: ends with 'il' but not 'mil' (e.g., NYYIL, BOSIL)
|
|
# - Edge case: teams whose base abbrev ends in 'M' + 'IL' = 'MIL'
|
|
# Only applies if removing 'IL' gives us exactly a 3-char base team
|
|
|
|
if abbrev_lower.endswith('mil'):
|
|
# Check if this is actually [BaseTeam]IL where BaseTeam ends in 'M'
|
|
# E.g., BHMIL = BHM + IL (injured list), not minor league
|
|
if len(self.abbrev) == 5: # Exactly 5 chars: 3-char base + IL
|
|
potential_base = self.abbrev[:-2] # Remove 'IL'
|
|
if len(potential_base) == 3 and potential_base.upper().endswith('M'):
|
|
return RosterType.INJURED_LIST
|
|
return RosterType.MINOR_LEAGUE
|
|
elif abbrev_lower.endswith('il'):
|
|
return RosterType.INJURED_LIST
|
|
else:
|
|
return RosterType.MAJOR_LEAGUE
|
|
|
|
def __str__(self):
|
|
return f"{self.abbrev} - {self.lname}" |