70 lines
2.6 KiB
Python
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),
|
|
}
|