paper-dynasty-database/app/routers_v2/gauntletrewards.py
Cal Corum 0cba52cea5 PostgreSQL migration: Complete code preparation phase
- Add db_helpers.py with cross-database upsert functions for SQLite/PostgreSQL
- Replace 12 on_conflict_replace() calls with PostgreSQL-compatible upserts
- Add unique indexes: StratPlay(game, play_num), Decision(game, pitcher)
- Add max_length to Team model fields (abbrev, sname, lname)
- Fix boolean comparison in teams.py (== 0/1 to == False/True)
- Create migrate_to_postgres.py with ID-preserving migration logic
- Create audit_sqlite.py for pre-migration data integrity checks
- Add PROJECT_PLAN.json for migration tracking
- Add .secrets/ to .gitignore for credentials

Audit results: 658,963 records across 29 tables, 2,390 orphaned stats (expected)

Based on Major Domo migration lessons learned (33 issues resolved there)
2026-01-25 23:05:54 -06:00

151 lines
4.6 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from typing import Optional, List
import logging
import pydantic
from ..db_engine import db, GauntletReward, model_to_dict, chunked, DatabaseError
from ..db_helpers import upsert_gauntlet_rewards
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/gauntletrewards", tags=["gauntletrewards"])
class GauntletRewardModel(pydantic.BaseModel):
name: str
gauntlet_id: Optional[int] = 0
reward_id: Optional[int] = 0
win_num: Optional[int] = 0
loss_max: Optional[int] = 1
class GauntletRewardList(pydantic.BaseModel):
rewards: List[GauntletRewardModel]
@router.get("")
async def v1_gauntletreward_get(
name: Optional[str] = None,
gauntlet_id: Optional[int] = None,
reward_id: list = Query(default=None),
win_num: Optional[int] = None,
loss_max: Optional[int] = None,
):
all_rewards = GauntletReward.select()
if name is not None:
all_rewards = all_rewards.where(GauntletReward.name == name)
if gauntlet_id is not None:
all_rewards = all_rewards.where(GauntletReward.gauntlet_id == gauntlet_id)
if reward_id is not None:
all_rewards = all_rewards.where(GauntletReward.reward_id << reward_id)
if win_num is not None:
all_rewards = all_rewards.where(GauntletReward.win_num == win_num)
if loss_max is not None:
all_rewards = all_rewards.where(GauntletReward.loss_max >= loss_max)
all_rewards = all_rewards.order_by(-GauntletReward.loss_max, GauntletReward.win_num)
return_val = {"count": all_rewards.count(), "rewards": []}
for x in all_rewards:
return_val["rewards"].append(model_to_dict(x))
db.close()
return return_val
@router.get("/{gauntletreward_id}")
async def v1_gauntletreward_get_one(gauntletreward_id):
try:
this_reward = GauntletReward.get_by_id(gauntletreward_id)
except Exception:
db.close()
raise HTTPException(
status_code=404,
detail=f"No gauntlet reward found with id {gauntletreward_id}",
)
return_val = model_to_dict(this_reward)
db.close()
return return_val
@router.patch("/{gauntletreward_id}")
async def v1_gauntletreward_patch(
gauntletreward_id,
name: Optional[str] = None,
gauntlet_id: Optional[int] = None,
reward_id: Optional[int] = None,
win_num: Optional[int] = None,
loss_max: Optional[int] = None,
token: str = Depends(oauth2_scheme),
):
if not valid_token(token):
logging.warning(f"Bad Token: {token}")
db.close()
raise HTTPException(
status_code=401,
detail="You are not authorized to patch gauntlet rewards. This event has been logged.",
)
this_reward = GauntletReward.get_or_none(GauntletReward.id == gauntletreward_id)
if this_reward is None:
db.close()
raise KeyError(f"Gauntlet Reward ID {gauntletreward_id} not found")
if gauntlet_id is not None:
this_reward.gauntlet_id = gauntlet_id
if reward_id is not None:
this_reward.reward_id = reward_id
if win_num is not None:
this_reward.win_num = win_num
if loss_max is not None:
this_reward.loss_max = loss_max
if name is not None:
this_reward.name = name
if this_reward.save():
r_curr = model_to_dict(this_reward)
db.close()
return r_curr
else:
db.close()
raise DatabaseError(f"Unable to patch gauntlet reward {gauntletreward_id}")
@router.post("")
async def v1_gauntletreward_post(
gauntletreward: GauntletRewardList, token: str = Depends(oauth2_scheme)
):
if not valid_token(token):
logging.warning(f"Bad Token: {token}")
db.close()
raise HTTPException(
status_code=401,
detail="You are not authorized to post gauntlets. This event has been logged.",
)
all_rewards = []
for x in gauntletreward.rewards:
all_rewards.append(x.dict())
with db.atomic():
# Use PostgreSQL-compatible upsert helper
upsert_gauntlet_rewards(all_rewards, batch_size=15)
db.close()
return f"Inserted {len(all_rewards)} gauntlet rewards"
@router.delete("/{gauntletreward_id}")
async def v1_gauntletreward_delete(gauntletreward_id):
if GauntletReward.delete_by_id(gauntletreward_id) == 1:
return f"Deleted gauntlet reward ID {gauntletreward_id}"
raise DatabaseError(f"Unable to delete gauntlet run {gauntletreward_id}")