Implements full Google Sheets scorecard submission with: - Complete game data extraction (68 play fields, pitching decisions, box score) - Transaction rollback support at 3 states (plays/game/complete) - Duplicate game detection with confirmation dialog - Permission-based submission (GMs only) - Automated results posting to news channel - Automatic standings recalculation - Key plays display with WPA sorting New Components: - Play, Decision, Game models with full validation - SheetsService for Google Sheets integration - GameService, PlayService, DecisionService for data management - ConfirmationView for user confirmations - Discord helper utilities for channel operations Services Enhanced: - StandingsService: Added recalculate_standings() method - CustomCommandsService: Fixed creator endpoint path - Team/Player models: Added helper methods for display Configuration: - Added SHEETS_CREDENTIALS_PATH environment variable - Added SBA_NETWORK_NEWS_CHANNEL and role constants - Enabled pygsheets dependency Documentation: - Comprehensive README updates across all modules - Added command, service, model, and view documentation - Detailed workflow and error handling documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
58 lines
2.0 KiB
Python
58 lines
2.0 KiB
Python
"""
|
|
Pitching Decision Model
|
|
|
|
Tracks wins, losses, saves, holds, and other pitching decisions for game results.
|
|
This model matches the database schema at /database/app/routers_v3/decisions.py.
|
|
"""
|
|
from pydantic import Field
|
|
from models.base import SBABaseModel
|
|
|
|
|
|
class Decision(SBABaseModel):
|
|
"""
|
|
Pitching decision model for game results.
|
|
|
|
Tracks wins, losses, saves, holds, and other pitching decisions.
|
|
"""
|
|
|
|
game_id: int = Field(..., description="Game ID")
|
|
season: int = Field(..., description="Season number")
|
|
week: int = Field(..., description="Week number")
|
|
game_num: int = Field(..., description="Game number in series")
|
|
pitcher_id: int = Field(..., description="Pitcher's player ID")
|
|
team_id: int = Field(..., description="Team ID")
|
|
|
|
# Decision flags
|
|
win: int = Field(0, description="Win (1 or 0)")
|
|
loss: int = Field(0, description="Loss (1 or 0)")
|
|
hold: int = Field(0, description="Hold (1 or 0)")
|
|
is_save: int = Field(0, description="Save (1 or 0)")
|
|
b_save: int = Field(0, description="Blown save (1 or 0)")
|
|
|
|
# Pitcher information
|
|
is_start: bool = Field(False, description="Was this a start?")
|
|
irunners: int = Field(0, description="Inherited runners")
|
|
irunners_scored: int = Field(0, description="Inherited runners scored")
|
|
rest_ip: float = Field(0.0, description="Rest innings pitched")
|
|
rest_required: int = Field(0, description="Rest required")
|
|
|
|
def __repr__(self):
|
|
"""String representation showing key decision info."""
|
|
decision_type = ""
|
|
if self.win == 1:
|
|
decision_type = "W"
|
|
elif self.loss == 1:
|
|
decision_type = "L"
|
|
elif self.is_save == 1:
|
|
decision_type = "SV"
|
|
elif self.hold == 1:
|
|
decision_type = "HLD"
|
|
elif self.b_save == 1:
|
|
decision_type = "BS"
|
|
|
|
return (
|
|
f"Decision(pitcher_id={self.pitcher_id}, "
|
|
f"game_id={self.game_id}, "
|
|
f"type={decision_type or 'NONE'})"
|
|
)
|