paper-dynasty-database/app/routers_v2/season_stats.py
Cal Corum d10276525e docs: update stale docstrings to reflect full-recalculation approach
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:34:55 -05:00

70 lines
2.6 KiB
Python

"""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),
}