"""Season stats API endpoints. Covers WP-13 (Post-Game Callback Integration): POST /api/v2/season-stats/update-game/{game_id} Delegates to app.services.season_stats.update_season_stats() which recomputes full-season stats from all StratPlay and Decision rows for every player who appeared in the game, then writes those totals into batting_season_stats and pitching_season_stats. Idempotency is enforced by the service layer: re-delivery of the same game_id returns {"updated": 0, "skipped": true} without modifying stats. Pass force=true to bypass the idempotency guard and force recalculation. """ import logging from fastapi import APIRouter, Depends, HTTPException from ..dependencies import oauth2_scheme, valid_token router = APIRouter(prefix="/api/v2/season-stats", tags=["season-stats"]) logger = logging.getLogger(__name__) @router.post("/update-game/{game_id}") async def update_game_season_stats( game_id: int, force: bool = False, token: str = Depends(oauth2_scheme) ): """Recalculate season stats from all StratPlay and Decision rows for a game. Calls update_season_stats(game_id, force=force) from the service layer which: - Recomputes full-season totals from all StratPlay rows for each player - Aggregates Decision rows for pitching win/loss/save/hold stats - Writes totals into batting_season_stats and pitching_season_stats - Guards against redundant work via the ProcessedGame ledger Query params: - force: if true, bypasses the idempotency guard and reprocesses a previously seen game_id (useful for correcting stats after data fixes) Response: {"updated": N, "skipped": false} - N: total player_season_stats rows upserted (batters + pitchers) - skipped: true when this game_id was already processed and force=false Errors from the service are logged but re-raised as 500 so the bot knows to retry. """ if not valid_token(token): logging.warning("Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") from ..services.season_stats import update_season_stats try: result = update_season_stats(game_id, force=force) except Exception as exc: logger.error("update-game/%d failed: %s", game_id, exc, exc_info=True) raise HTTPException( status_code=500, detail=f"Season stats update failed for game {game_id}: {exc}", ) updated = result.get("batters_updated", 0) + result.get("pitchers_updated", 0) return { "updated": updated, "skipped": result.get("skipped", False), }