major-domo-database/app/routers_v3/managers.py
Cal Corum 16f3f8d8de
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m32s
Fix unbounded API queries causing Gunicorn worker timeouts
Add MAX_LIMIT=500 cap across all list endpoints, empty string
stripping middleware, and limit/offset to /transactions. Resolves #98.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:23:25 -05:00

187 lines
5.8 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from typing import List, Optional
import logging
import pydantic
from ..db_engine import db, Manager, Team, Current, model_to_dict, fn
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
MAX_LIMIT,
DEFAULT_LIMIT,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/managers", tags=["managers"])
class ManagerModel(pydantic.BaseModel):
name: str
image: Optional[str] = None
headline: Optional[str] = None
bio: Optional[str] = None
@router.get("")
@handle_db_errors
async def get_managers(
name: list = Query(default=None),
active: Optional[bool] = None,
short_output: Optional[bool] = False,
limit: int = Query(default=DEFAULT_LIMIT, ge=1, le=MAX_LIMIT),
offset: int = Query(default=0, ge=0),
):
if active is not None:
current = Current.latest()
t_query = Team.select_season(current.season)
t_query = t_query.where(
~(Team.abbrev.endswith("IL")) & ~(Team.abbrev.endswith("MiL"))
)
logger.info(f"tquery: {t_query}")
a_mgr = []
i_mgr = []
for x in t_query:
logger.info(f"Team: {x.abbrev} / mgr1: {x.manager1} / mgr2: {x.manager2}")
if x.manager1 is not None:
a_mgr.append(x.manager1)
logger.info(f"appending {x.manager1.name}")
if x.manager2 is not None:
a_mgr.append(x.manager2)
logger.info(f"appending {x.manager2.name}")
logger.info(f"a_mgr: {a_mgr}")
if active:
final_mgrs = [model_to_dict(y, recurse=not short_output) for y in a_mgr]
else:
logger.info(f"checking inactive")
for z in Manager.select():
logger.info(f"checking: {z.name}")
if z not in a_mgr:
logger.info(f"+inactive: {z.name}")
i_mgr.append(z)
final_mgrs = [model_to_dict(y, recurse=not short_output) for y in i_mgr]
total_count = len(final_mgrs)
final_mgrs = final_mgrs[offset : offset + limit]
return_managers = {"count": total_count, "managers": final_mgrs}
else:
all_managers = Manager.select()
if name is not None:
name_list = [x.lower() for x in name]
all_managers = all_managers.where(fn.Lower(Manager.name) << name_list)
total_count = all_managers.count()
all_managers = all_managers.offset(offset).limit(limit)
return_managers = {
"count": total_count,
"managers": [
model_to_dict(x, recurse=not short_output) for x in all_managers
],
}
db.close()
return return_managers
@router.get("/{manager_id}")
@handle_db_errors
async def get_one_manager(manager_id: int, short_output: Optional[bool] = False):
this_manager = Manager.get_or_none(Manager.id == manager_id)
if this_manager is not None:
r_manager = model_to_dict(this_manager, recurse=not short_output)
db.close()
return r_manager
else:
raise HTTPException(status_code=404, detail=f"Manager {manager_id} not found")
@router.patch("/{manager_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_manager(
manager_id: int,
name: Optional[str] = None,
image: Optional[str] = None,
headline: Optional[str] = None,
bio: Optional[str] = None,
token: str = Depends(oauth2_scheme),
):
if not valid_token(token):
logger.warning(f"patch_manager - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_manager = Manager.get_or_none(Manager.id == manager_id)
if this_manager is None:
db.close()
raise HTTPException(
status_code=404, detail=f"Manager ID {manager_id} not found"
)
if name is not None:
this_manager.name = name
if image is not None:
this_manager.image = image
if headline is not None:
this_manager.headline = headline
if bio is not None:
this_manager.bio = bio
if this_manager.save() == 1:
r_manager = model_to_dict(this_manager)
db.close()
return r_manager
else:
db.close()
raise HTTPException(
status_code=500, detail=f"Unable to patch manager {this_manager}"
)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_manager(new_manager: ManagerModel, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f"post_manager - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_manager = Manager(**new_manager.dict())
if this_manager.save():
r_manager = model_to_dict(this_manager)
db.close()
return r_manager
else:
db.close()
raise HTTPException(
status_code=500, detail=f"Unable to post manager {this_manager.name}"
)
@router.delete("/{manager_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_manager(manager_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f"delete_manager - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_manager = Manager.get_or_none(Manager.id == manager_id)
if this_manager is None:
db.close()
raise HTTPException(
status_code=404, detail=f"Manager ID {manager_id} not found"
)
count = this_manager.delete_instance()
db.close()
if count == 1:
return f"Manager {manager_id} has been deleted"
else:
raise HTTPException(
status_code=500, detail=f"Manager {manager_id} could not be deleted"
)