"""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 aggregates StratPlay and Decision rows for a completed game and performs an additive upsert into player_season_stats. Idempotency is enforced by the service layer: re-delivery of the same game_id returns {"updated": 0, "skipped": true} without modifying stats. """ 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, token: str = Depends(oauth2_scheme)): """Increment season stats with batting and pitching deltas from a game. Calls update_season_stats(game_id) from the service layer which: - Aggregates all StratPlay rows by (player_id, team_id, season) - Merges Decision rows into pitching groups - Performs an additive ON CONFLICT upsert into player_season_stats - Guards against double-counting via the last_game FK check Response: {"updated": N, "skipped": false} - N: total player_season_stats rows upserted (batters + pitchers) - skipped: true when this game_id was already processed (idempotent re-delivery) 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) 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), }