Major Features Added: • Admin Management System: Complete admin command suite with user moderation, system control, and bot maintenance tools • Enhanced Player Commands: Added batting/pitching statistics with concurrent API calls and improved embed design • League Standings: Full standings system with division grouping, playoff picture, and wild card visualization • Game Schedules: Comprehensive schedule system with team filtering, series organization, and proper home/away indicators New Admin Commands (12 total): • /admin-status, /admin-help, /admin-reload, /admin-sync, /admin-clear • /admin-announce, /admin-maintenance • /admin-timeout, /admin-untimeout, /admin-kick, /admin-ban, /admin-unban, /admin-userinfo Enhanced Player Display: • Team logo positioned beside player name using embed author • Smart thumbnail priority: fancycard → headshot → team logo fallback • Concurrent batting/pitching stats fetching for performance • Rich statistics display with team colors and comprehensive metrics New Models & Services: • BattingStats, PitchingStats, TeamStandings, Division, Game models • StatsService, StandingsService, ScheduleService for data management • CustomCommand system with CRUD operations and cleanup tasks Bot Architecture Improvements: • Admin commands integrated into bot.py with proper loading • Permission checks and safety guards for moderation commands • Enhanced error handling and comprehensive audit logging • All 227 tests passing with new functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
"""
|
|
Game model for SBA games
|
|
|
|
Represents individual games with scores, teams, and metadata.
|
|
"""
|
|
from typing import Optional
|
|
from pydantic import Field
|
|
|
|
from models.base import SBABaseModel
|
|
from models.team import Team
|
|
|
|
|
|
class Game(SBABaseModel):
|
|
"""Game model representing an SBA game."""
|
|
|
|
# Override base model to make id required for database entities
|
|
id: int = Field(..., description="Game ID from database")
|
|
|
|
# Game metadata
|
|
season: int = Field(..., description="Season number")
|
|
week: int = Field(..., description="Week number")
|
|
game_num: Optional[int] = Field(None, description="Game number within series")
|
|
season_type: str = Field(..., description="Season type (regular/playoff)")
|
|
|
|
# Teams
|
|
away_team: Team = Field(..., description="Away team object")
|
|
home_team: Team = Field(..., description="Home team object")
|
|
|
|
# Scores (optional for future games)
|
|
away_score: Optional[int] = Field(None, description="Away team score")
|
|
home_score: Optional[int] = Field(None, description="Home team score")
|
|
|
|
# Managers (who managed this specific game)
|
|
away_manager: Optional[dict] = Field(None, description="Away team manager for this game")
|
|
home_manager: Optional[dict] = Field(None, description="Home team manager for this game")
|
|
|
|
# Links
|
|
scorecard_url: Optional[str] = Field(None, description="Google Sheets scorecard URL")
|
|
|
|
@property
|
|
def is_completed(self) -> bool:
|
|
"""Check if the game has been played (has scores)."""
|
|
return self.away_score is not None and self.home_score is not None
|
|
|
|
@property
|
|
def winner(self) -> Optional[Team]:
|
|
"""Get the winning team (if game is completed)."""
|
|
if not self.is_completed:
|
|
return None
|
|
return self.home_team if self.home_score > self.away_score else self.away_team
|
|
|
|
@property
|
|
def loser(self) -> Optional[Team]:
|
|
"""Get the losing team (if game is completed)."""
|
|
if not self.is_completed:
|
|
return None
|
|
return self.away_team if self.home_score > self.away_score else self.home_team
|
|
|
|
@property
|
|
def score_display(self) -> str:
|
|
"""Display score as string."""
|
|
if not self.is_completed:
|
|
return "vs"
|
|
return f"{self.away_score}-{self.home_score}"
|
|
|
|
@property
|
|
def matchup_display(self) -> str:
|
|
"""Display matchup with score/@."""
|
|
if self.is_completed:
|
|
return f"{self.away_team.abbrev} {self.score_display} {self.home_team.abbrev}"
|
|
else:
|
|
return f"{self.away_team.abbrev} @ {self.home_team.abbrev}"
|
|
|
|
@property
|
|
def series_game_display(self) -> Optional[str]:
|
|
"""Display series game number if available."""
|
|
if self.game_num:
|
|
return f"Game {self.game_num}"
|
|
return None
|
|
|
|
def __str__(self):
|
|
return f"Week {self.week}: {self.matchup_display}" |