major-domo-v2/models/standings.py
Cal Corum 7b41520054 CLAUDE: Major bot enhancements - Admin commands, player stats, standings, schedules
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>
2025-08-28 15:32:38 -05:00

125 lines
4.5 KiB
Python

"""
Standings model for SBA teams
Represents team standings with wins, losses, and playoff positioning.
"""
from typing import Optional
from pydantic import Field
from models.base import SBABaseModel
from models.team import Team
class TeamStandings(SBABaseModel):
"""Team standings model representing league position and record."""
# Override base model to make id required for database entities
id: int = Field(..., description="Standings ID from database")
# Team information
team: Team = Field(..., description="Team object with full details")
# Win/Loss record
wins: int = Field(..., description="Total wins")
losses: int = Field(..., description="Total losses")
run_diff: int = Field(..., description="Run differential (runs scored - runs allowed)")
# Playoff positioning
div_gb: Optional[float] = Field(None, description="Games behind division leader")
div_e_num: Optional[int] = Field(None, description="Division elimination number")
wc_gb: Optional[float] = Field(None, description="Games behind wild card")
wc_e_num: Optional[int] = Field(None, description="Wild card elimination number")
# Home/Away splits
home_wins: int = Field(..., description="Home wins")
home_losses: int = Field(..., description="Home losses")
away_wins: int = Field(..., description="Away wins")
away_losses: int = Field(..., description="Away losses")
# Recent performance
last8_wins: int = Field(..., description="Wins in last 8 games")
last8_losses: int = Field(..., description="Losses in last 8 games")
streak_wl: str = Field(..., description="Current streak type (w/l)")
streak_num: int = Field(..., description="Current streak length")
# Close games
one_run_wins: int = Field(..., description="One-run game wins")
one_run_losses: int = Field(..., description="One-run game losses")
# Pythagorean record (expected wins/losses based on run differential)
pythag_wins: int = Field(..., description="Pythagorean wins")
pythag_losses: int = Field(..., description="Pythagorean losses")
# Divisional records
div1_wins: int = Field(..., description="Division 1 wins")
div1_losses: int = Field(..., description="Division 1 losses")
div2_wins: int = Field(..., description="Division 2 wins")
div2_losses: int = Field(..., description="Division 2 losses")
div3_wins: int = Field(..., description="Division 3 wins")
div3_losses: int = Field(..., description="Division 3 losses")
div4_wins: int = Field(..., description="Division 4 wins")
div4_losses: int = Field(..., description="Division 4 losses")
@property
def games_played(self) -> int:
"""Total games played."""
return self.wins + self.losses
@property
def winning_percentage(self) -> float:
"""Winning percentage."""
if self.games_played == 0:
return 0.0
return self.wins / self.games_played
@property
def home_record(self) -> str:
"""Home record as string."""
return f"{self.home_wins}-{self.home_losses}"
@property
def away_record(self) -> str:
"""Away record as string."""
return f"{self.away_wins}-{self.away_losses}"
@property
def last8_record(self) -> str:
"""Last 8 games record as string."""
return f"{self.last8_wins}-{self.last8_losses}"
@property
def current_streak(self) -> str:
"""Current streak formatted as string."""
streak_type = "W" if self.streak_wl.lower() == "w" else "L"
return f"{streak_type}{self.streak_num}"
@property
def division_gb_display(self) -> str:
"""Division games behind display."""
if self.div_gb is None:
return "-"
elif self.div_gb == 0.0:
return "-"
else:
return f"{self.div_gb:.1f}"
@property
def wild_card_gb_display(self) -> str:
"""Wild card games behind display."""
if self.wc_gb is None:
return "-"
elif self.wc_gb <= 0.0:
return "-"
else:
return f"{self.wc_gb:.1f}"
@property
def run_diff_display(self) -> str:
"""Run differential with +/- prefix."""
if self.run_diff > 0:
return f"+{self.run_diff}"
else:
return str(self.run_diff)
def __str__(self):
return f"{self.team.abbrev} {self.wins}-{self.losses} ({self.winning_percentage:.3f})"