major-domo-database/app/routers_v3/decisions.py
Cal Corum 06794c27a1
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m12s
fix: remove commented-out dead code blocks (#31)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 13:34:43 -06:00

270 lines
8.4 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from typing import List, Optional, Literal
import copy
import logging
import pydantic
from ..db_engine import (
db,
Decision,
StratGame,
Player,
model_to_dict,
chunked,
fn,
Team,
)
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/decisions", tags=["decisions"])
class DecisionModel(pydantic.BaseModel):
game_id: int
season: int
week: int
game_num: int
pitcher_id: int
team_id: int
win: int = 0
loss: int = 0
hold: int = 0
is_save: int = 0
is_start: bool = False
b_save: int = 0
irunners: int = 0
irunners_scored: int = 0
rest_ip: float = 0
rest_required: int = 0
class DecisionList(pydantic.BaseModel):
decisions: List[DecisionModel]
class DecisionReturnList(pydantic.BaseModel):
count: int
decisions: list[DecisionModel]
@router.get("")
@handle_db_errors
async def get_decisions(
season: list = Query(default=None),
week: list = Query(default=None),
game_num: list = Query(default=None),
s_type: Literal["regular", "post", "all", None] = None,
team_id: list = Query(default=None),
week_start: Optional[int] = None,
week_end: Optional[int] = None,
win: Optional[int] = None,
loss: Optional[int] = None,
hold: Optional[int] = None,
save: Optional[int] = None,
b_save: Optional[int] = None,
irunners: list = Query(default=None),
irunners_scored: list = Query(default=None),
game_id: list = Query(default=None),
player_id: list = Query(default=None),
limit: Optional[int] = None,
short_output: Optional[bool] = False,
):
all_dec = Decision.select().order_by(
-Decision.season, -Decision.week, -Decision.game_num
)
if season is not None:
all_dec = all_dec.where(Decision.season << season)
if week is not None:
all_dec = all_dec.where(Decision.week << week)
if game_num is not None:
all_dec = all_dec.where(Decision.game_num << game_num)
if game_id is not None:
all_dec = all_dec.where(Decision.game_id << game_id)
if player_id is not None:
all_dec = all_dec.where(Decision.pitcher << player_id)
if team_id is not None:
s8_teams = [int(x) for x in team_id if int(x) <= 350]
if season is not None and 8 in season or s8_teams:
all_teams = Team.select().where(Team.id << team_id)
all_games = StratGame.select().where(
(StratGame.away_team << all_teams) | (StratGame.home_team << all_teams)
)
all_dec = all_dec.where(Decision.game << all_games)
else:
all_teams = Team.select().where(Team.id << team_id)
all_dec = all_dec.where(Decision.team << all_teams)
if s_type is not None:
all_games = StratGame.select().where(StratGame.season_type == s_type)
all_dec = all_dec.where(Decision.game << all_games)
if week_start is not None:
all_dec = all_dec.where(Decision.week >= week_start)
if week_end is not None:
all_dec = all_dec.where(Decision.week <= week_end)
if win is not None:
all_dec = all_dec.where(Decision.win == win)
if loss is not None:
all_dec = all_dec.where(Decision.loss == loss)
if hold is not None:
all_dec = all_dec.where(Decision.hold == hold)
if save is not None:
all_dec = all_dec.where(Decision.save == save)
if b_save is not None:
all_dec = all_dec.where(Decision.b_save == b_save)
if irunners is not None:
all_dec = all_dec.where(Decision.irunners << irunners)
if irunners_scored is not None:
all_dec = all_dec.where(Decision.irunners_scored << irunners_scored)
if limit is not None:
if limit < 1:
limit = 1
all_dec = all_dec.limit(limit)
return_dec = {
"count": all_dec.count(),
"decisions": [model_to_dict(x, recurse=not short_output) for x in all_dec],
}
db.close()
return return_dec
@router.patch("/{decision_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_decision(
decision_id: int,
win: Optional[int] = None,
loss: Optional[int] = None,
hold: Optional[int] = None,
save: Optional[int] = None,
b_save: Optional[int] = None,
irunners: Optional[int] = None,
irunners_scored: Optional[int] = None,
rest_ip: Optional[int] = None,
rest_required: Optional[int] = None,
token: str = Depends(oauth2_scheme),
):
if not valid_token(token):
logger.warning(f"patch_decision - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_dec = Decision.get_or_none(Decision.id == decision_id)
if this_dec is None:
db.close()
raise HTTPException(
status_code=404, detail=f"Decision ID {decision_id} not found"
)
if win is not None:
this_dec.win = win
if loss is not None:
this_dec.loss = loss
if hold is not None:
this_dec.hold = hold
if save is not None:
this_dec.is_save = save
if b_save is not None:
this_dec.b_save = b_save
if irunners is not None:
this_dec.irunners = irunners
if irunners_scored is not None:
this_dec.irunners_scored = irunners_scored
if rest_ip is not None:
this_dec.rest_ip = rest_ip
if rest_required is not None:
this_dec.rest_required = rest_required
if this_dec.save() == 1:
d_result = model_to_dict(this_dec)
db.close()
return d_result
else:
db.close()
raise HTTPException(
status_code=500, detail=f"Unable to patch decision {decision_id}"
)
@router.post("", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_decisions(dec_list: DecisionList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f"post_decisions - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
new_dec = []
for x in dec_list.decisions:
if StratGame.get_or_none(StratGame.id == x.game_id) is None:
raise HTTPException(
status_code=404, detail=f"Game ID {x.game_id} not found"
)
if Player.get_or_none(Player.id == x.pitcher_id) is None:
raise HTTPException(
status_code=404, detail=f"Player ID {x.pitcher_id} not found"
)
new_dec.append(x.dict())
with db.atomic():
for batch in chunked(new_dec, 10):
Decision.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f"Inserted {len(new_dec)} decisions"
@router.delete("/{decision_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_decision(decision_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f"delete_decision - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_dec = Decision.get_or_none(Decision.id == decision_id)
if this_dec is None:
db.close()
raise HTTPException(
status_code=404, detail=f"Decision ID {decision_id} not found"
)
count = this_dec.delete_instance()
db.close()
if count == 1:
return f"Decision {decision_id} has been deleted"
else:
raise HTTPException(
status_code=500, detail=f"Decision {decision_id} could not be deleted"
)
@router.delete("/game/{game_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_decisions_game(game_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f"delete_decisions_game - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_game = StratGame.get_or_none(StratGame.id == game_id)
if not this_game:
db.close()
raise HTTPException(status_code=404, detail=f"Game ID {game_id} not found")
count = Decision.delete().where(Decision.game == this_game).execute()
db.close()
if count > 0:
return f"Deleted {count} decisions matching Game ID {game_id}"
else:
raise HTTPException(
status_code=500,
detail=f"No decisions matching Game ID {game_id} were deleted",
)