From 65ad72c2993aaecdbdbb368cf4cd8aa8730a0170 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Mon, 2 Mar 2026 22:36:15 -0600 Subject: [PATCH 01/25] fix: remove debug print(req.scope) from get_docs route (#31) Co-Authored-By: Claude Sonnet 4.6 --- app/main.py | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/app/main.py b/app/main.py index 057e321..3968b19 100644 --- a/app/main.py +++ b/app/main.py @@ -6,15 +6,42 @@ from fastapi.openapi.utils import get_openapi # from fastapi.templating import Jinja2Templates from .routers_v2 import ( - current, awards, teams, rarity, cardsets, players, packtypes, packs, cards, events, results, rewards, decisions, - batstats, pitstats, notifications, paperdex, gamerewards, gauntletrewards, gauntletruns, battingcards, - battingcardratings, pitchingcards, pitchingcardratings, cardpositions, scouting, mlbplayers, stratgame, stratplays) + current, + awards, + teams, + rarity, + cardsets, + players, + packtypes, + packs, + cards, + events, + results, + rewards, + decisions, + batstats, + pitstats, + notifications, + paperdex, + gamerewards, + gauntletrewards, + gauntletruns, + battingcards, + battingcardratings, + pitchingcards, + pitchingcardratings, + cardpositions, + scouting, + mlbplayers, + stratgame, + stratplays, +) app = FastAPI( # root_path='/api', - responses={404: {'description': 'Not found'}}, - docs_url='/api/docs', - redoc_url='/api/redoc' + responses={404: {"description": "Not found"}}, + docs_url="/api/docs", + redoc_url="/api/redoc", ) # app.mount("/static", StaticFiles(directory="storage/static"), name="static") @@ -53,10 +80,11 @@ app.include_router(decisions.router) @app.get("/api/docs", include_in_schema=False) async def get_docs(req: Request): - print(req.scope) - return get_swagger_ui_html(openapi_url=req.scope.get('root_path')+'/openapi.json', title='Swagger') + return get_swagger_ui_html( + openapi_url=req.scope.get("root_path") + "/openapi.json", title="Swagger" + ) @app.get("/api/openapi.json", include_in_schema=False) async def openapi(): - return get_openapi(title='Paper Dynasty API', version=f'0.1.1', routes=app.routes) + return get_openapi(title="Paper Dynasty API", version=f"0.1.1", routes=app.routes) From 2a660e9c196135ffa7aee83d386aafab7a5589eb Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 10:52:36 -0600 Subject: [PATCH 02/25] docs: add next-release branch workflow to CLAUDE.md Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 4757d65..6efe82a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,6 +51,13 @@ docker build -t paper-dynasty-db . # Build image - DB connection errors → verify `POSTGRES_HOST` points to correct container name - **CI/CD**: Gitea Actions on PR to `main` — builds Docker image, auto-generates CalVer version (`YYYY.MM.BUILD`) on merge +### Release Workflow +1. Create feature/fix branches off `next-release` (e.g., `fix/card-pricing`) +2. When done, merge the branch into `next-release` — this is the staging branch where changes accumulate +3. When ready to release, open a PR from `next-release` → `main` +4. CI builds Docker image on PR; CalVer tag is created on merge +5. Deploy the new image to production + ## Important - Docker image installs only Playwright Chromium (not all browsers) to optimize size From 3e15acbb9d3410a7289fe8ef627c1b9e216db237 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 15:31:59 -0600 Subject: [PATCH 03/25] fix: respect is_ai=False in get_teams filter (#22) `all_teams.where(Team.is_ai)` always filtered for AI teams regardless of the caller's intent. Match the existing has_guide pattern and use explicit boolean comparison so False is handled correctly. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/teams.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index 696daf6..0b275fb 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -150,7 +150,10 @@ async def get_teams( all_teams = all_teams.where(Team.has_guide == True) if is_ai is not None: - all_teams = all_teams.where(Team.is_ai) + if not is_ai: + all_teams = all_teams.where(Team.is_ai == False) + else: + all_teams = all_teams.where(Team.is_ai == True) if event_id is not None: all_teams = all_teams.where(Team.event_id == event_id) From 9fc7a9449e8d262d3eb54b0b5d093aff86cf9e6f Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 15:21:24 -0600 Subject: [PATCH 04/25] fix: correct inverted TESTING env check and leading space in .env (#23) - Change `== 'False'` to `== 'True'` so TESTING=True routes to dev URL - Fix leading space on TESTING=TRUE in .env so the var is actually set - Update .env comment to match corrected logic Co-Authored-By: Claude Sonnet 4.6 --- .env | 4 +- app/dependencies.py | 168 +++++++++++++++++++++++++++++--------------- 2 files changed, 113 insertions(+), 59 deletions(-) diff --git a/.env b/.env index 1c05f70..985ca39 100644 --- a/.env +++ b/.env @@ -59,9 +59,9 @@ API_TOKEN=Tp3aO3jhYve5NJF1IqOmJTmk # PRIVATE_IN_SCHEMA=true # Testing mode -# Set to 'False' to use development database URL (pddev.manticorum.com) +# Set to 'True' to use development database URL (pddev.manticorum.com) # Leave unset or set to any other value for production - TESTING=TRUE +TESTING=True # ============================================================================= # EXAMPLE CONFIGURATIONS diff --git a/app/dependencies.py b/app/dependencies.py index f311a73..7120c59 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -5,33 +5,37 @@ import os import requests from fastapi.security import OAuth2PasswordBearer -date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}' +date = f"{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}" LOG_DATA = { - 'filename': f'logs/database/{date}.log', - 'format': '%(asctime)s - database - %(levelname)s - %(message)s', - 'log_level': logging.INFO if os.environ.get('LOG_LEVEL') == 'INFO' else 'WARN' + "filename": f"logs/database/{date}.log", + "format": "%(asctime)s - database - %(levelname)s - %(message)s", + "log_level": logging.INFO if os.environ.get("LOG_LEVEL") == "INFO" else "WARN", } logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] + filename=LOG_DATA["filename"], + format=LOG_DATA["format"], + level=LOG_DATA["log_level"], ) oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") master_debug = False -DB_URL = 'https://pd.manticorum.com/api/' +DB_URL = "https://pd.manticorum.com/api/" AUTH_TOKEN = f'{os.environ.get("API_TOKEN")}' -AUTH_HEADER = {'Authorization': f'Bearer {AUTH_TOKEN}'} +AUTH_HEADER = {"Authorization": f"Bearer {AUTH_TOKEN}"} -priv_help = False if not os.environ.get('PRIVATE_IN_SCHEMA') else os.environ.get('PRIVATE_IN_SCHEMA').upper() +priv_help = ( + False + if not os.environ.get("PRIVATE_IN_SCHEMA") + else os.environ.get("PRIVATE_IN_SCHEMA").upper() +) PRIVATE_IN_SCHEMA = True if priv_help else False -if os.environ.get('TESTING') == 'False': - DB_URL = 'https://pddev.manticorum.com/api/' +if os.environ.get("TESTING") == "True": + DB_URL = "https://pddev.manticorum.com/api/" def valid_token(token): @@ -42,33 +46,41 @@ def int_timestamp(datetime_obj: datetime) -> int: return int(datetime.datetime.timestamp(datetime_obj) * 1000) -def mround(x, prec=2, base=.05): +def mround(x, prec=2, base=0.05): return round(base * round(float(x) / base), prec) def param_char(other_params): if other_params: - return '&' + return "&" else: - return '?' + return "?" -def get_req_url(endpoint: str, api_ver: int = 2, object_id: int = None, params: list = None): +def get_req_url( + endpoint: str, api_ver: int = 2, object_id: int = None, params: list = None +): req_url = f'{DB_URL}/v{api_ver}/{endpoint}{"/" if object_id is not None else ""}{object_id if object_id is not None else ""}' if params: other_params = False for x in params: - req_url += f'{param_char(other_params)}{x[0]}={x[1]}' + req_url += f"{param_char(other_params)}{x[0]}={x[1]}" other_params = True return req_url -async def db_get(endpoint: str, api_ver: int = 2, object_id: int = None, params: list = None, none_okay: bool = True, - timeout: int = 3): +async def db_get( + endpoint: str, + api_ver: int = 2, + object_id: int = None, + params: list = None, + none_okay: bool = True, + timeout: int = 3, +): req_url = get_req_url(endpoint, api_ver=api_ver, object_id=object_id, params=params) - log_string = f'get:\n{endpoint} id: {object_id} params: {params}' + log_string = f"get:\n{endpoint} id: {object_id} params: {params}" logging.info(log_string) if master_debug else logging.debug(log_string) retries = 0 @@ -77,37 +89,51 @@ async def db_get(endpoint: str, api_ver: int = 2, object_id: int = None, params: resp = requests.get(req_url, timeout=timeout) break except requests.ReadTimeout as e: - logging.error(f'Get Timeout: {req_url} / retries: {retries} / timeout: {timeout}') + logging.error( + f"Get Timeout: {req_url} / retries: {retries} / timeout: {timeout}" + ) if retries > 1: - raise ConnectionError(f'DB: The internet was a bit too slow for me to grab the data I needed. Please ' - f'hang on a few extra seconds and try again.') + raise ConnectionError( + f"DB: The internet was a bit too slow for me to grab the data I needed. Please " + f"hang on a few extra seconds and try again." + ) timeout += [2, 5][retries] retries += 1 if resp.status_code == 200: data = resp.json() - log_string = f'{data}' + log_string = f"{data}" if master_debug: - logging.info(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.info( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) else: - logging.debug(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.debug( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) return data elif none_okay: data = resp.json() - log_string = f'{data}' + log_string = f"{data}" if master_debug: - logging.info(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.info( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) else: - logging.debug(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.debug( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) return None else: logging.warning(resp.text) - raise ValueError(f'DB: {resp.text}') + raise ValueError(f"DB: {resp.text}") -async def db_patch(endpoint: str, object_id: int, params: list, api_ver: int = 2, timeout: int = 3): +async def db_patch( + endpoint: str, object_id: int, params: list, api_ver: int = 2, timeout: int = 3 +): req_url = get_req_url(endpoint, api_ver=api_ver, object_id=object_id, params=params) - log_string = f'patch:\n{endpoint} {params}' + log_string = f"patch:\n{endpoint} {params}" logging.info(log_string) if master_debug else logging.debug(log_string) retries = 0 @@ -116,60 +142,80 @@ async def db_patch(endpoint: str, object_id: int, params: list, api_ver: int = 2 resp = requests.patch(req_url, headers=AUTH_HEADER, timeout=timeout) break except requests.Timeout as e: - logging.error(f'Patch Timeout: {req_url} / retries: {retries} / timeout: {timeout}') + logging.error( + f"Patch Timeout: {req_url} / retries: {retries} / timeout: {timeout}" + ) if retries > 1: - raise ConnectionError(f'DB: The internet was a bit too slow for me to grab the data I needed. Please ' - f'hang on a few extra seconds and try again.') + raise ConnectionError( + f"DB: The internet was a bit too slow for me to grab the data I needed. Please " + f"hang on a few extra seconds and try again." + ) timeout += [min(3, timeout), min(5, timeout)][retries] retries += 1 if resp.status_code == 200: data = resp.json() - log_string = f'{data}' + log_string = f"{data}" if master_debug: - logging.info(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.info( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) else: - logging.debug(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.debug( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) return data else: logging.warning(resp.text) - raise ValueError(f'DB: {resp.text}') + raise ValueError(f"DB: {resp.text}") -async def db_post(endpoint: str, api_ver: int = 2, payload: dict = None, timeout: int = 3): +async def db_post( + endpoint: str, api_ver: int = 2, payload: dict = None, timeout: int = 3 +): req_url = get_req_url(endpoint, api_ver=api_ver) - log_string = f'post:\n{endpoint} payload: {payload}\ntype: {type(payload)}' + log_string = f"post:\n{endpoint} payload: {payload}\ntype: {type(payload)}" logging.info(log_string) if master_debug else logging.debug(log_string) retries = 0 while True: try: - resp = requests.post(req_url, json=payload, headers=AUTH_HEADER, timeout=timeout) + resp = requests.post( + req_url, json=payload, headers=AUTH_HEADER, timeout=timeout + ) break except requests.Timeout as e: - logging.error(f'Post Timeout: {req_url} / retries: {retries} / timeout: {timeout}') + logging.error( + f"Post Timeout: {req_url} / retries: {retries} / timeout: {timeout}" + ) if retries > 1: - raise ConnectionError(f'DB: The internet was a bit too slow for me to grab the data I needed. Please ' - f'hang on a few extra seconds and try again.') + raise ConnectionError( + f"DB: The internet was a bit too slow for me to grab the data I needed. Please " + f"hang on a few extra seconds and try again." + ) timeout += [min(3, timeout), min(5, timeout)][retries] retries += 1 if resp.status_code == 200: data = resp.json() - log_string = f'{data}' + log_string = f"{data}" if master_debug: - logging.info(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.info( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) else: - logging.debug(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.debug( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) return data else: logging.warning(resp.text) - raise ValueError(f'DB: {resp.text}') + raise ValueError(f"DB: {resp.text}") async def db_delete(endpoint: str, object_id: int, api_ver: int = 2, timeout=3): req_url = get_req_url(endpoint, api_ver=api_ver, object_id=object_id) - log_string = f'delete:\n{endpoint} {object_id}' + log_string = f"delete:\n{endpoint} {object_id}" logging.info(log_string) if master_debug else logging.debug(log_string) retries = 0 @@ -178,21 +224,29 @@ async def db_delete(endpoint: str, object_id: int, api_ver: int = 2, timeout=3): resp = requests.delete(req_url, headers=AUTH_HEADER, timeout=timeout) break except requests.ReadTimeout as e: - logging.error(f'Delete Timeout: {req_url} / retries: {retries} / timeout: {timeout}') + logging.error( + f"Delete Timeout: {req_url} / retries: {retries} / timeout: {timeout}" + ) if retries > 1: - raise ConnectionError(f'DB: The internet was a bit too slow for me to grab the data I needed. Please ' - f'hang on a few extra seconds and try again.') + raise ConnectionError( + f"DB: The internet was a bit too slow for me to grab the data I needed. Please " + f"hang on a few extra seconds and try again." + ) timeout += [min(3, timeout), min(5, timeout)][retries] retries += 1 if resp.status_code == 200: data = resp.json() - log_string = f'{data}' + log_string = f"{data}" if master_debug: - logging.info(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.info( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) else: - logging.debug(f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}') + logging.debug( + f'return: {log_string[:1200]}{" [ S N I P P E D ]" if len(log_string) > 1200 else ""}' + ) return True else: logging.warning(resp.text) - raise ValueError(f'DB: {resp.text}') + raise ValueError(f"DB: {resp.text}") From 6a2e8a2d2a5fb724ec840c8de8231ab27e518f95 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 12:06:21 -0600 Subject: [PATCH 05/25] fix: remove dead roster fields from CSV in v1_cards_get_one (#25) Card model has no roster1/2/3 fields. Accessing them would raise AttributeError at runtime. Removed the non-existent columns from the CSV header and data row. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/cards.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/routers_v2/cards.py b/app/routers_v2/cards.py index a501021..8627ef4 100644 --- a/app/routers_v2/cards.py +++ b/app/routers_v2/cards.py @@ -141,9 +141,8 @@ async def v1_cards_get_one(card_id, csv: Optional[bool] = False): if csv: data_list = [ - ['id', 'player', 'team', 'pack', 'value', 'roster1', 'roster2', 'roster3'], - [this_card.id, this_card.player, this_card.team.abbrev, this_card.pack, this_card.value, - this_card.roster1.name, this_card.roster2.name, this_card.roster3.name] + ['id', 'player', 'team', 'pack', 'value'], + [this_card.id, this_card.player, this_card.team.abbrev, this_card.pack, this_card.value] ] return_val = DataFrame(data_list).to_csv(header=False, index=False) From 6130eb993f0c580db69fd78b18a8aece7abece8c Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 14:05:21 -0600 Subject: [PATCH 06/25] fix: use Field(default_factory) for offense_col random default (#24) Pydantic evaluates bare `random.randint(1, 3)` once at class definition time, so every PlayerModel instance shared the same value. Replaced with `pydantic.Field(default_factory=...)` so a new random value is generated per instance. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/mlbplayers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routers_v2/mlbplayers.py b/app/routers_v2/mlbplayers.py index b6b59ed..dd42c73 100644 --- a/app/routers_v2/mlbplayers.py +++ b/app/routers_v2/mlbplayers.py @@ -37,7 +37,7 @@ class PlayerModel(pydantic.BaseModel): key_fangraphs: int = None key_bbref: str = None key_retro: str = None - offense_col: int = random.randint(1, 3) + offense_col: int = pydantic.Field(default_factory=lambda: random.randint(1, 3)) class PlayerList(pydantic.BaseModel): From 8d86b3fec6b7da9a0bb7812c4f4bc3944d550bd1 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Mon, 2 Mar 2026 23:37:24 -0600 Subject: [PATCH 07/25] fix: replace 467 manual db.close() calls with middleware (#30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add db_session_middleware to main.py that opens the connection at the start of each request and closes it in a try/finally block, ensuring connections are always returned even on uncaught exceptions. Remove all individual db.close() calls from 30 router files in app/routers_v2/ — the middleware now handles all code paths. Co-Authored-By: Claude Sonnet 4.6 --- app/main.py | 12 ++++++ app/routers_v2/admin.py | 2 - app/routers_v2/awards.py | 12 ------ app/routers_v2/batstats.py | 9 ----- app/routers_v2/battingcardratings.py | 17 --------- app/routers_v2/battingcards.py | 14 ------- app/routers_v2/cardpositions.py | 8 ---- app/routers_v2/cards.py | 23 ------------ app/routers_v2/cardsets.py | 18 --------- app/routers_v2/current.py | 14 ------- app/routers_v2/decisions.py | 11 ------ app/routers_v2/events.py | 16 -------- app/routers_v2/gamerewards.py | 14 ------- app/routers_v2/gauntletrewards.py | 9 ----- app/routers_v2/gauntletruns.py | 10 ----- app/routers_v2/mlbplayers.py | 21 ----------- app/routers_v2/notifications.py | 15 -------- app/routers_v2/packs.py | 20 ---------- app/routers_v2/packtypes.py | 15 -------- app/routers_v2/paperdex.py | 16 -------- app/routers_v2/pitchingcardratings.py | 16 -------- app/routers_v2/pitchingcards.py | 14 ------- app/routers_v2/pitstats.py | 7 ---- app/routers_v2/players.py | 29 -------------- app/routers_v2/rarity.py | 16 -------- app/routers_v2/results.py | 23 ------------ app/routers_v2/rewards.py | 15 -------- app/routers_v2/scouting.py | 3 -- app/routers_v2/stratgame.py | 10 ----- app/routers_v2/stratplays.py | 16 -------- app/routers_v2/teams.py | 54 --------------------------- 31 files changed, 12 insertions(+), 467 deletions(-) diff --git a/app/main.py b/app/main.py index 3968b19..ddcafe3 100644 --- a/app/main.py +++ b/app/main.py @@ -5,6 +5,7 @@ from fastapi.openapi.utils import get_openapi # from fastapi.staticfiles import StaticFiles # from fastapi.templating import Jinja2Templates +from .db_engine import db from .routers_v2 import ( current, awards, @@ -78,6 +79,17 @@ app.include_router(stratplays.router) app.include_router(decisions.router) +@app.middleware("http") +async def db_session_middleware(request: Request, call_next): + try: + db.connect(reuse_if_open=True) + response = await call_next(request) + return response + finally: + if not db.is_closed(): + db.close() + + @app.get("/api/docs", include_in_schema=False) async def get_docs(req: Request): return get_swagger_ui_html( diff --git a/app/routers_v2/admin.py b/app/routers_v2/admin.py index 89eba55..350897b 100644 --- a/app/routers_v2/admin.py +++ b/app/routers_v2/admin.py @@ -20,7 +20,6 @@ router = APIRouter( async def stl_cardinals_fix(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. This event has been logged.' @@ -29,7 +28,6 @@ async def stl_cardinals_fix(token: str = Depends(oauth2_scheme)): p_query = Player.update(mlbclub='St Louis Cardinals', franchise='St Louis Cardinals').where( Player.mlbclub == 'St. Louis Cardinals' ).execute() - db.close() return {'detail': f'Removed the period from St Louis'} diff --git a/app/routers_v2/awards.py b/app/routers_v2/awards.py index 27c170c..e871c17 100644 --- a/app/routers_v2/awards.py +++ b/app/routers_v2/awards.py @@ -41,7 +41,6 @@ async def get_awards( all_awards = Award.select().order_by(Award.id) if all_awards.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'There are no awards to filter') if name is not None: @@ -65,7 +64,6 @@ async def get_awards( ]) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -73,7 +71,6 @@ async def get_awards( for x in all_awards: return_val['awards'].append(model_to_dict(x)) - db.close() return return_val @@ -82,7 +79,6 @@ async def get_one_award(award_id, csv: Optional[bool] = None): try: this_award = Award.get_by_id(award_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No award found with id {award_id}') if csv: @@ -93,12 +89,10 @@ async def get_one_award(award_id, csv: Optional[bool] = None): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_award) - db.close() return return_val @@ -106,7 +100,6 @@ async def get_one_award(award_id, csv: Optional[bool] = None): async def post_awards(award: AwardModel, 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 awards. This event has been logged.' @@ -124,10 +117,8 @@ async def post_awards(award: AwardModel, token: str = Depends(oauth2_scheme)): saved = this_award.save() if saved == 1: return_val = model_to_dict(this_award) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail='Well slap my ass and call me a teapot; I could not save that roster' @@ -138,7 +129,6 @@ async def post_awards(award: AwardModel, token: str = Depends(oauth2_scheme)): async def delete_award(award_id, 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 delete awards. This event has been logged.' @@ -146,11 +136,9 @@ async def delete_award(award_id, token: str = Depends(oauth2_scheme)): try: this_award = Award.get_by_id(award_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No award found with id {award_id}') count = this_award.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Award {award_id} has been deleted') diff --git a/app/routers_v2/batstats.py b/app/routers_v2/batstats.py index 485a479..909c9bf 100644 --- a/app/routers_v2/batstats.py +++ b/app/routers_v2/batstats.py @@ -119,7 +119,6 @@ async def get_batstats( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -127,7 +126,6 @@ async def get_batstats( for x in all_stats: return_val['stats'].append(model_to_dict(x, recurse=False)) - db.close() return return_val @@ -166,7 +164,6 @@ async def get_player_stats( ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -175,7 +172,6 @@ async def get_player_stats( for x in all_stats: logging.debug(f'this_line: {model_to_dict(x)}') return_val = model_to_dict(all_stats[0]) - db.close() return return_val @@ -183,7 +179,6 @@ async def get_player_stats( async def post_batstats(stats: BattingStatModel, 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 stats. This event has been logged.' @@ -232,7 +227,6 @@ async def post_batstats(stats: BattingStatModel, token: str = Depends(oauth2_sch with db.atomic(): BattingStat.bulk_create(new_stats, batch_size=15) - db.close() raise HTTPException(status_code=200, detail=f'{len(new_stats)} batting lines have been added') @@ -241,7 +235,6 @@ async def post_batstats(stats: BattingStatModel, token: str = Depends(oauth2_sch async def delete_batstat(stat_id, 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 delete stats. This event has been logged.' @@ -249,11 +242,9 @@ async def delete_batstat(stat_id, token: str = Depends(oauth2_scheme)): try: this_stat = BattingStat.get_by_id(stat_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No stat found with id {stat_id}') count = this_stat.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Stat {stat_id} has been deleted') diff --git a/app/routers_v2/battingcardratings.py b/app/routers_v2/battingcardratings.py index ffe1a48..8e2d448 100644 --- a/app/routers_v2/battingcardratings.py +++ b/app/routers_v2/battingcardratings.py @@ -158,7 +158,6 @@ async def get_card_ratings( logging.debug(f"Team: {this_team} / has_guide: {this_team.has_guide}") if this_team is None or ts != this_team.team_hash() or this_team.has_guide != 1: logging.warning(f"Team_id {team_id} attempted to pull ratings") - db.close() raise HTTPException( status_code=401, detail="You are not authorized to pull card ratings." ) @@ -195,7 +194,6 @@ async def get_card_ratings( x["player_id"] = x["battingcard"]["player"]["player_id"] del x["battingcard"], x["player"] - db.close() return Response( content=pd.DataFrame(return_vals).to_csv(index=False), media_type="text/csv" ) @@ -207,7 +205,6 @@ async def get_card_ratings( model_to_dict(x, recurse=not short_output) for x in all_ratings ], } - db.close() return return_val @@ -328,7 +325,6 @@ def get_scouting_dfs(cardset_id: list = None): name=f"Throw C", ) ) - db.close() logging.debug(f"series_list: {series_list}") return bat_df.join(series_list) @@ -340,7 +336,6 @@ async def get_card_scouting(team_id: int, ts: str): logging.debug(f"Team: {this_team} / has_guide: {this_team.has_guide}") if this_team is None or ts != this_team.team_hash() or this_team.has_guide != 1: logging.warning(f"Team_id {team_id} attempted to pull ratings") - db.close() return ( "Your team does not have the ratings guide enabled. If you have purchased a copy ping Cal to " "make sure it is enabled on your team. If you are interested you can pick it up here (thank you!): " @@ -363,7 +358,6 @@ async def get_card_scouting(team_id: int, ts: str): async def post_calc_scouting(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 calculate card ratings." ) @@ -400,7 +394,6 @@ async def get_basic_scouting(cardset_id: list = Query(default=None)): async def post_calc_basic(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 calculate basic ratings." ) @@ -647,20 +640,17 @@ async def post_calc_basic(token: str = Depends(oauth2_scheme)): async def get_one_rating(ratings_id: int, 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 pull card ratings." ) this_rating = BattingCardRatings.get_or_none(BattingCardRatings.id == ratings_id) if this_rating is None: - db.close() raise HTTPException( status_code=404, detail=f"BattingCardRating id {ratings_id} not found" ) r_data = model_to_dict(this_rating) - db.close() return r_data @@ -673,7 +663,6 @@ async def get_player_ratings( ): if not valid_token(token): logging.warning(f"Bad Token: {token}") - db.close() raise HTTPException( status_code=401, detail="You are not authorized to pull card ratings." ) @@ -694,7 +683,6 @@ async def get_player_ratings( "count": all_ratings.count(), "ratings": [model_to_dict(x, recurse=not short_output) for x in all_ratings], } - db.close() return return_val @@ -702,7 +690,6 @@ async def get_player_ratings( async def put_ratings(ratings: RatingsList, 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 card ratings." ) @@ -730,7 +717,6 @@ async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)) # Use PostgreSQL-compatible upsert helper upsert_batting_card_ratings(new_ratings, batch_size=30) - db.close() return f"Updated ratings: {updates}; new ratings: {len(new_ratings)}" @@ -738,20 +724,17 @@ async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)) async def delete_rating(ratings_id: int, 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 card ratings." ) this_rating = BattingCardRatings.get_or_none(BattingCardRatings.id == ratings_id) if this_rating is None: - db.close() raise HTTPException( status_code=404, detail=f"BattingCardRating id {ratings_id} not found" ) count = this_rating.delete_instance() - db.close() if count == 1: return f"Rating {this_rating} has been deleted" diff --git a/app/routers_v2/battingcards.py b/app/routers_v2/battingcards.py index 8c8cc91..055738c 100644 --- a/app/routers_v2/battingcards.py +++ b/app/routers_v2/battingcards.py @@ -65,7 +65,6 @@ async def get_batting_cards( "count": all_cards.count(), "cards": [model_to_dict(x, recurse=not short_output) for x in all_cards], } - db.close() return return_val @@ -73,13 +72,11 @@ async def get_batting_cards( async def get_one_card(card_id: int): this_card = BattingCard.get_or_none(BattingCard.id == card_id) if this_card is None: - db.close() raise HTTPException( status_code=404, detail=f"BattingCard id {card_id} not found" ) r_card = model_to_dict(this_card) - db.close() return r_card @@ -99,7 +96,6 @@ async def get_player_cards( "count": all_cards.count(), "cards": [model_to_dict(x, recurse=not short_output) for x in all_cards], } - db.close() return return_val @@ -107,7 +103,6 @@ async def get_player_cards( async def put_cards(cards: BattingCardList, 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 batting cards. This event has been logged.", @@ -157,7 +152,6 @@ async def put_cards(cards: BattingCardList, token: str = Depends(oauth2_scheme)) # Use PostgreSQL-compatible upsert helper upsert_batting_cards(new_cards, batch_size=30) - db.close() return f"Updated cards: {updates}; new cards: {len(new_cards)}" @@ -177,7 +171,6 @@ async def patch_card( ): 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 batting cards. This event has been logged.", @@ -185,7 +178,6 @@ async def patch_card( this_card = BattingCard.get_or_none(BattingCard.id == card_id) if this_card is None: - db.close() raise HTTPException( status_code=404, detail=f"BattingCard id {card_id} not found" ) @@ -211,10 +203,8 @@ async def patch_card( if this_card.save() == 1: return_val = model_to_dict(this_card) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail="Well slap my ass and call me a teapot; I could not save that card", @@ -225,7 +215,6 @@ async def patch_card( async def delete_card(card_id: int, 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 delete batting cards. This event has been logged.", @@ -233,13 +222,11 @@ async def delete_card(card_id: int, token: str = Depends(oauth2_scheme)): this_card = BattingCard.get_or_none(BattingCard.id == card_id) if this_card is None: - db.close() raise HTTPException( status_code=404, detail=f"BattingCard id {card_id} not found" ) count = this_card.delete_instance() - db.close() if count == 1: return f"Card {this_card} has been deleted" @@ -253,7 +240,6 @@ async def delete_card(card_id: int, token: str = Depends(oauth2_scheme)): async def delete_all_cards(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 delete batting cards. This event has been logged.", diff --git a/app/routers_v2/cardpositions.py b/app/routers_v2/cardpositions.py index 7863bbb..206c292 100644 --- a/app/routers_v2/cardpositions.py +++ b/app/routers_v2/cardpositions.py @@ -95,7 +95,6 @@ async def get_card_positions( "count": all_pos.count(), "positions": [model_to_dict(x, recurse=not short_output) for x in all_pos], } - db.close() return return_val @@ -103,13 +102,11 @@ async def get_card_positions( async def get_one_position(position_id: int): this_pos = CardPosition.get_or_none(CardPosition.id == position_id) if this_pos is None: - db.close() raise HTTPException( status_code=404, detail=f"CardPosition id {position_id} not found" ) r_data = model_to_dict(this_pos) - db.close() return r_data @@ -117,7 +114,6 @@ async def get_one_position(position_id: int): async def put_positions(positions: PositionList, 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 card positions. This event has been logged.", @@ -149,7 +145,6 @@ async def put_positions(positions: PositionList, token: str = Depends(oauth2_sch # Use PostgreSQL-compatible upsert helper upsert_card_positions(new_cards, batch_size=30) - db.close() return f"Updated cards: {updates}; new cards: {len(new_cards)}" @@ -157,7 +152,6 @@ async def put_positions(positions: PositionList, token: str = Depends(oauth2_sch async def delete_position(position_id: int, 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 delete card positions. This event has been logged.", @@ -165,13 +159,11 @@ async def delete_position(position_id: int, token: str = Depends(oauth2_scheme)) this_pos = CardPosition.get_or_none(CardPosition.id == position_id) if this_pos is None: - db.close() raise HTTPException( status_code=404, detail=f"CardPosition id {position_id} not found" ) count = this_pos.delete_instance() - db.close() if count == 1: return f"Card Position {this_pos} has been deleted" diff --git a/app/routers_v2/cards.py b/app/routers_v2/cards.py index 8627ef4..ca294ff 100644 --- a/app/routers_v2/cards.py +++ b/app/routers_v2/cards.py @@ -47,21 +47,18 @@ async def get_cards( try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No team found with id {team_id}') all_cards = all_cards.where(Card.team == this_team) if player_id is not None: try: this_player = Player.get_by_id(player_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No player found with id {player_id}') all_cards = all_cards.where(Card.player == this_player) if pack_id is not None: try: this_pack = Pack.get_by_id(pack_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}') all_cards = all_cards.where(Card.pack == this_pack) if value is not None: @@ -108,7 +105,6 @@ async def get_cards( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -127,7 +123,6 @@ async def get_cards( # return_val['cards'].append(model_to_dict(x)) - db.close() return return_val @@ -136,7 +131,6 @@ async def v1_cards_get_one(card_id, csv: Optional[bool] = False): try: this_card = Card.get_by_id(card_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No card found with id {card_id}') if csv: @@ -146,12 +140,10 @@ async def v1_cards_get_one(card_id, csv: Optional[bool] = False): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_card) - db.close() return return_val @@ -159,7 +151,6 @@ async def v1_cards_get_one(card_id, csv: Optional[bool] = False): async def v1_cards_post(cards: CardModel, 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 cards. This event has been logged.' @@ -194,7 +185,6 @@ async def v1_cards_post(cards: CardModel, token: str = Depends(oauth2_scheme)): cost_query = Player.update(cost=Player.cost + 1).where(Player.player_id << player_ids) cost_query.execute() # sheets.post_new_cards(SHEETS_AUTH, lc_id) - db.close() raise HTTPException(status_code=200, detail=f'{len(new_cards)} cards have been added') @@ -218,7 +208,6 @@ async def v1_cards_legal_check( rarity_name: str, card_id: list = Query(default=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='Unauthorized' @@ -251,14 +240,12 @@ async def v1_cards_legal_check( async def v1_cards_post_update(starting_id: int, 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 update card lists. This event has been logged.' ) # sheets.post_new_cards(SHEETS_AUTH, starting_id) - db.close() raise HTTPException(status_code=200, detail=f'Just sent cards to sheets starting at ID {starting_id}') @@ -266,7 +253,6 @@ async def v1_cards_post_update(starting_id: int, token: str = Depends(oauth2_sch async def v1_cards_post_delete(del_ids: str, 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 delete card lists. This event has been logged.' @@ -280,7 +266,6 @@ async def v1_cards_post_delete(del_ids: str, token: str = Depends(oauth2_scheme) async def v1_cards_wipe_team(team_id: int, 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 wipe teams. This event has been logged.' @@ -293,7 +278,6 @@ async def v1_cards_wipe_team(team_id: int, token: str = Depends(oauth2_scheme)): raise HTTPException(status_code=404, detail=f'Team {team_id} not found') t_query = Card.update(team=None).where(Card.team == this_team).execute() - db.close() return f'Wiped {t_query} cards' @@ -304,7 +288,6 @@ async def v1_cards_patch( roster3_id: 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 cards. This event has been logged.' @@ -312,7 +295,6 @@ async def v1_cards_patch( try: this_card = Card.get_by_id(card_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No card found with id {card_id}') if player_id is not None: @@ -337,10 +319,8 @@ async def v1_cards_patch( if this_card.save() == 1: return_val = model_to_dict(this_card) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail='Well slap my ass and call me a teapot; I could not save that rarity' @@ -351,7 +331,6 @@ async def v1_cards_patch( async def v1_cards_delete(card_id, 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 delete packs. This event has been logged.' @@ -359,11 +338,9 @@ async def v1_cards_delete(card_id, token: str = Depends(oauth2_scheme)): try: this_card = Card.get_by_id(card_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No cards found with id {card_id}') count = this_card.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Card {card_id} has been deleted') diff --git a/app/routers_v2/cardsets.py b/app/routers_v2/cardsets.py index 10fb981..6ca4a38 100644 --- a/app/routers_v2/cardsets.py +++ b/app/routers_v2/cardsets.py @@ -36,7 +36,6 @@ async def get_cardsets( all_cardsets = Cardset.select().order_by(Cardset.id) if all_cardsets.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'There are no cardsets to filter') if name is not None: @@ -56,7 +55,6 @@ async def get_cardsets( all_cardsets = all_cardsets.where(Cardset.ranked_legal == ranked_legal) if all_cardsets.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'No cardsets found') if csv: @@ -72,7 +70,6 @@ async def get_cardsets( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -80,7 +77,6 @@ async def get_cardsets( for x in all_cardsets: return_val['cardsets'].append(model_to_dict(x)) - db.close() return return_val @@ -115,7 +111,6 @@ async def search_cardsets( all_cardsets = all_cardsets.where(Cardset.event == this_event) except Exception as e: logging.error(f'Failed to find event {event_id}: {e}') - db.close() raise HTTPException(status_code=404, detail=f'Event id {event_id} not found') # Convert to list for sorting @@ -153,7 +148,6 @@ async def search_cardsets( 'cardsets': [model_to_dict(x) for x in limited_results] } - db.close() return return_val @@ -162,7 +156,6 @@ async def get_one_cardset(cardset_id, csv: Optional[bool] = False): try: this_cardset = Cardset.get_by_id(cardset_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}') if csv: @@ -172,11 +165,9 @@ async def get_one_cardset(cardset_id, csv: Optional[bool] = False): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_cardset) - db.close() return return_val @@ -184,7 +175,6 @@ async def get_one_cardset(cardset_id, csv: Optional[bool] = False): async def post_cardsets(cardset: CardsetModel, 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 cardsets. This event has been logged.' @@ -192,7 +182,6 @@ async def post_cardsets(cardset: CardsetModel, token: str = Depends(oauth2_schem dupe_set = Cardset.get_or_none(Cardset.name == cardset.name) if dupe_set: - db.close() raise HTTPException(status_code=400, detail=f'There is already a cardset using {cardset.name}') this_cardset = Cardset(**cardset.__dict__) @@ -200,7 +189,6 @@ async def post_cardsets(cardset: CardsetModel, token: str = Depends(oauth2_schem saved = this_cardset.save() if saved == 1: return_val = model_to_dict(this_cardset) - db.close() return return_val else: raise HTTPException( @@ -216,7 +204,6 @@ async def patch_cardsets( 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 cardsets. This event has been logged.' @@ -224,7 +211,6 @@ async def patch_cardsets( try: this_cardset = Cardset.get_by_id(cardset_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}') if name is not None: @@ -242,7 +228,6 @@ async def patch_cardsets( if this_cardset.save() == 1: return_val = model_to_dict(this_cardset) - db.close() return return_val else: raise HTTPException( @@ -255,7 +240,6 @@ async def patch_cardsets( async def delete_cardsets(cardset_id, 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 delete cardsets. This event has been logged.' @@ -263,11 +247,9 @@ async def delete_cardsets(cardset_id, token: str = Depends(oauth2_scheme)): try: this_cardset = Cardset.get_by_id(cardset_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}') count = this_cardset.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Cardset {cardset_id} has been deleted') diff --git a/app/routers_v2/current.py b/app/routers_v2/current.py index a8b9545..dc5fd27 100644 --- a/app/routers_v2/current.py +++ b/app/routers_v2/current.py @@ -40,11 +40,9 @@ async def get_current(season: Optional[int] = None, csv: Optional[bool] = False) ] return_val = DataFrame(current_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(current) - db.close() return return_val @@ -53,7 +51,6 @@ async def get_one_current(current_id, csv: Optional[bool] = False): try: current = Current.get_by_id(current_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No current found with id {current_id}') if csv: @@ -63,11 +60,9 @@ async def get_one_current(current_id, csv: Optional[bool] = False): ] return_val = DataFrame(current_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(current) - db.close() return return_val @@ -75,7 +70,6 @@ async def get_one_current(current_id, csv: Optional[bool] = False): async def post_current(current: CurrentModel, 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 current. This event has been logged.' @@ -83,7 +77,6 @@ async def post_current(current: CurrentModel, token: str = Depends(oauth2_scheme dupe_curr = Current.get_or_none(Current.season == current.season) if dupe_curr: - db.close() raise HTTPException(status_code=400, detail=f'There is already a current for season {current.season}') this_curr = Current( @@ -96,7 +89,6 @@ async def post_current(current: CurrentModel, token: str = Depends(oauth2_scheme saved = this_curr.save() if saved == 1: return_val = model_to_dict(this_curr) - db.close() return return_val else: raise HTTPException(status_code=418, detail='Well slap my ass and call me a teapot; I could not save that team') @@ -109,7 +101,6 @@ async def patch_current( live_scoreboard: 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 current. This event has been logged.' @@ -117,7 +108,6 @@ async def patch_current( try: current = Current.get_by_id(current_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No current found with id {current_id}') if season is not None: @@ -133,7 +123,6 @@ async def patch_current( if current.save() == 1: return_val = model_to_dict(current) - db.close() return return_val else: raise HTTPException( @@ -146,7 +135,6 @@ async def patch_current( async def delete_current(current_id, 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 delete current. This event has been logged.' @@ -154,11 +142,9 @@ async def delete_current(current_id, token: str = Depends(oauth2_scheme)): try: this_curr = Current.get_by_id(current_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No current found with id {current_id}') count = this_curr.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Current {current_id} has been deleted') diff --git a/app/routers_v2/decisions.py b/app/routers_v2/decisions.py index 0e84b29..5c3d189 100644 --- a/app/routers_v2/decisions.py +++ b/app/routers_v2/decisions.py @@ -112,7 +112,6 @@ async def get_decisions( "count": all_dec.count(), "decisions": [model_to_dict(x, recurse=not short_output) for x in all_dec], } - db.close() if csv: return_vals = return_dec["decisions"] @@ -136,7 +135,6 @@ async def get_decisions( exclude = first + ["lob_all", "lob_all_rate", "lob_2outs", "rbi%"] output = output[first + [col for col in output.columns if col not in exclude]] - db.close() return Response( content=pd.DataFrame(output).to_csv(index=False), media_type="text/csv" ) @@ -189,7 +187,6 @@ async def get_decisions_for_rest( return_dec.append(this_val) - db.close() return Response( content=pd.DataFrame(return_dec).to_csv(index=False, header=False), media_type="text/csv", @@ -216,7 +213,6 @@ async def patch_decision( 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" ) @@ -242,10 +238,8 @@ async def patch_decision( 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}" ) @@ -277,7 +271,6 @@ async def post_decisions(dec_list: DecisionList, token: str = Depends(oauth2_sch with db.atomic(): # Use PostgreSQL-compatible upsert helper upsert_decisions(new_dec, batch_size=10) - db.close() return f"Inserted {len(new_dec)} decisions" @@ -290,13 +283,11 @@ async def delete_decision(decision_id: int, token: str = Depends(oauth2_scheme)) 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" @@ -314,11 +305,9 @@ async def delete_decisions_game(game_id: int, token: str = Depends(oauth2_scheme 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}" diff --git a/app/routers_v2/events.py b/app/routers_v2/events.py index 0fcad4c..3674426 100644 --- a/app/routers_v2/events.py +++ b/app/routers_v2/events.py @@ -54,7 +54,6 @@ async def v1_events_get( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -62,7 +61,6 @@ async def v1_events_get( for x in all_events: return_val['events'].append(model_to_dict(x)) - db.close() return return_val @@ -71,7 +69,6 @@ async def v1_events_get_one(event_id, csv: Optional[bool] = False): try: this_event = Event.get_by_id(event_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No event found with id {event_id}') if csv: @@ -82,12 +79,10 @@ async def v1_events_get_one(event_id, csv: Optional[bool] = False): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_event) - db.close() return return_val @@ -95,7 +90,6 @@ async def v1_events_get_one(event_id, csv: Optional[bool] = False): async def v1_events_post(event: EventModel, 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 events. This event has been logged.' @@ -103,7 +97,6 @@ async def v1_events_post(event: EventModel, token: str = Depends(oauth2_scheme)) dupe_event = Event.get_or_none(Event.name == event.name) if dupe_event: - db.close() raise HTTPException(status_code=400, detail=f'There is already an event using {event.name}') this_event = Event( @@ -118,10 +111,8 @@ async def v1_events_post(event: EventModel, token: str = Depends(oauth2_scheme)) saved = this_event.save() if saved == 1: return_val = model_to_dict(this_event) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail='Well slap my ass and call me a teapot; I could not save that cardset' @@ -135,7 +126,6 @@ async def v1_events_patch( 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 events. This event has been logged.' @@ -143,7 +133,6 @@ async def v1_events_patch( try: this_event = Event.get_by_id(event_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No event found with id {event_id}') if name is not None: @@ -161,10 +150,8 @@ async def v1_events_patch( if this_event.save() == 1: return_val = model_to_dict(this_event) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail='Well slap my ass and call me a teapot; I could not save that event' @@ -175,7 +162,6 @@ async def v1_events_patch( async def v1_events_delete(event_id, 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 delete events. This event has been logged.' @@ -183,11 +169,9 @@ async def v1_events_delete(event_id, token: str = Depends(oauth2_scheme)): try: this_event = Event.get_by_id(event_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No event found with id {event_id}') count = this_event.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Event {event_id} has been deleted') diff --git a/app/routers_v2/gamerewards.py b/app/routers_v2/gamerewards.py index 9f19d65..20e6d68 100644 --- a/app/routers_v2/gamerewards.py +++ b/app/routers_v2/gamerewards.py @@ -54,7 +54,6 @@ async def v1_gamerewards_get( ]) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -62,7 +61,6 @@ async def v1_gamerewards_get( for x in all_rewards: return_val['gamerewards'].append(model_to_dict(x)) - db.close() return return_val @@ -71,7 +69,6 @@ async def v1_gamerewards_get_one(gamereward_id, csv: Optional[bool] = None): try: this_game_reward = GameRewards.get_by_id(gamereward_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No game reward found with id {gamereward_id}') if csv: @@ -82,12 +79,10 @@ async def v1_gamerewards_get_one(gamereward_id, csv: Optional[bool] = None): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_game_reward) - db.close() return return_val @@ -95,7 +90,6 @@ async def v1_gamerewards_get_one(gamereward_id, csv: Optional[bool] = None): async def v1_gamerewards_post(game_reward: GameRewardModel, 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 game rewards. This event has been logged.' @@ -111,10 +105,8 @@ async def v1_gamerewards_post(game_reward: GameRewardModel, token: str = Depends saved = this_award.save() if saved == 1: return_val = model_to_dict(this_award) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail='Well slap my ass and call me a teapot; I could not save that roster' @@ -127,7 +119,6 @@ async def v1_gamerewards_patch( player_id: Optional[int] = None, money: 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 gamerewards. This event has been logged.' @@ -135,7 +126,6 @@ async def v1_gamerewards_patch( try: this_game_reward = GameRewards.get_by_id(game_reward_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No game reward found with id {game_reward_id}') if name is not None: @@ -158,7 +148,6 @@ async def v1_gamerewards_patch( if this_game_reward.save() == 1: return_val = model_to_dict(this_game_reward) - db.close() return return_val else: raise HTTPException( @@ -171,7 +160,6 @@ async def v1_gamerewards_patch( async def v1_gamerewards_delete(gamereward_id, 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 delete awards. This event has been logged.' @@ -179,11 +167,9 @@ async def v1_gamerewards_delete(gamereward_id, token: str = Depends(oauth2_schem try: this_award = GameRewards.get_by_id(gamereward_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No award found with id {gamereward_id}') count = this_award.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Game Reward {gamereward_id} has been deleted') diff --git a/app/routers_v2/gauntletrewards.py b/app/routers_v2/gauntletrewards.py index 03127b6..9f76bf5 100644 --- a/app/routers_v2/gauntletrewards.py +++ b/app/routers_v2/gauntletrewards.py @@ -55,7 +55,6 @@ async def v1_gauntletreward_get( for x in all_rewards: return_val["rewards"].append(model_to_dict(x)) - db.close() return return_val @@ -64,14 +63,12 @@ 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 @@ -87,7 +84,6 @@ async def v1_gauntletreward_patch( ): 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.", @@ -95,7 +91,6 @@ async def v1_gauntletreward_patch( 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: @@ -111,10 +106,8 @@ async def v1_gauntletreward_patch( 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}") @@ -124,7 +117,6 @@ async def v1_gauntletreward_post( ): 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.", @@ -137,7 +129,6 @@ async def v1_gauntletreward_post( 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" diff --git a/app/routers_v2/gauntletruns.py b/app/routers_v2/gauntletruns.py index e67859c..f664319 100644 --- a/app/routers_v2/gauntletruns.py +++ b/app/routers_v2/gauntletruns.py @@ -82,7 +82,6 @@ async def get_gauntletruns( for x in all_gauntlets: return_val['runs'].append(model_to_dict(x)) - db.close() return return_val @@ -91,11 +90,9 @@ async def get_one_gauntletrun(gauntletrun_id): try: this_gauntlet = GauntletRun.get_by_id(gauntletrun_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No gauntlet found with id {gauntletrun_id}') return_val = model_to_dict(this_gauntlet) - db.close() return return_val @@ -106,7 +103,6 @@ async def patch_gauntletrun( 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 runs. This event has been logged.' @@ -114,7 +110,6 @@ async def patch_gauntletrun( this_run = GauntletRun.get_or_none(GauntletRun.id == gauntletrun_id) if this_run is None: - db.close() raise KeyError(f'Gauntlet Run ID {gauntletrun_id} not found') if team_id is not None: @@ -138,10 +133,8 @@ async def patch_gauntletrun( if this_run.save(): r_curr = model_to_dict(this_run) - db.close() return r_curr else: - db.close() raise DatabaseError(f'Unable to patch gauntlet run {gauntletrun_id}') @@ -149,7 +142,6 @@ async def patch_gauntletrun( async def post_gauntletrun(gauntletrun: GauntletRunModel, 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.' @@ -169,10 +161,8 @@ async def post_gauntletrun(gauntletrun: GauntletRunModel, token: str = Depends(o if this_run.save(): r_run = model_to_dict(this_run) - db.close() return r_run else: - db.close() raise DatabaseError(f'Unable to post gauntlet run') diff --git a/app/routers_v2/mlbplayers.py b/app/routers_v2/mlbplayers.py index dd42c73..0737f30 100644 --- a/app/routers_v2/mlbplayers.py +++ b/app/routers_v2/mlbplayers.py @@ -111,14 +111,12 @@ async def get_players( if csv: return_val = query_to_csv(all_players) - db.close() return Response(content=return_val, media_type="text/csv") return_val = { "count": all_players.count(), "players": [model_to_dict(x) for x in all_players], } - db.close() return return_val @@ -126,13 +124,11 @@ async def get_players( async def get_one_player(player_id: int): this_player = MlbPlayer.get_or_none(MlbPlayer.id == player_id) if this_player is None: - db.close() raise HTTPException( status_code=404, detail=f"MlbPlayer id {player_id} not found" ) r_data = model_to_dict(this_player) - db.close() return r_data @@ -150,7 +146,6 @@ async def patch_player( ): 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 mlb players. This event has been logged.", @@ -158,7 +153,6 @@ async def patch_player( this_player = MlbPlayer.get_or_none(MlbPlayer.id == player_id) if this_player is None: - db.close() raise HTTPException( status_code=404, detail=f"MlbPlayer id {player_id} not found" ) @@ -180,10 +174,8 @@ async def patch_player( if this_player.save() == 1: return_val = model_to_dict(this_player) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail="Well slap my ass and call me a teapot; I could not save that player", @@ -194,7 +186,6 @@ async def patch_player( async def post_players(players: PlayerList, 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 mlb players. This event has been logged.", @@ -209,7 +200,6 @@ async def post_players(players: PlayerList, token: str = Depends(oauth2_scheme)) | (MlbPlayer.key_bbref == x.key_bbref) ) if dupes.count() > 0: - db.close() raise HTTPException( status_code=400, detail=f"{x.first_name} {x.last_name} has a key already in the database", @@ -221,7 +211,6 @@ async def post_players(players: PlayerList, token: str = Depends(oauth2_scheme)) # Use PostgreSQL-compatible upsert helper # Note: Duplicate check is already done above, so this is effectively just insert upsert_mlb_players(new_players, batch_size=15) - db.close() return f"Inserted {len(new_players)} new MLB players" @@ -230,7 +219,6 @@ async def post_players(players: PlayerList, token: str = Depends(oauth2_scheme)) async def post_one_player(player: PlayerModel, 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 mlb players. This event has been logged.", @@ -245,7 +233,6 @@ async def post_one_player(player: PlayerModel, token: str = Depends(oauth2_schem logging.info(f"POST /mlbplayers/one - dupes found:") for x in dupes: logging.info(f"{x}") - db.close() raise HTTPException( status_code=400, detail=f"{player.first_name} {player.last_name} has a key already in the database", @@ -255,7 +242,6 @@ async def post_one_player(player: PlayerModel, token: str = Depends(oauth2_schem saved = new_player.save() if saved == 1: return_val = model_to_dict(new_player) - db.close() return return_val else: raise HTTPException( @@ -268,7 +254,6 @@ async def post_one_player(player: PlayerModel, token: str = Depends(oauth2_schem async def delete_player(player_id: int, 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 delete mlb players. This event has been logged.", @@ -276,13 +261,11 @@ async def delete_player(player_id: int, token: str = Depends(oauth2_scheme)): this_player = MlbPlayer.get_or_none(MlbPlayer.id == player_id) if this_player is None: - db.close() raise HTTPException( status_code=404, detail=f"MlbPlayer id {player_id} not found" ) count = this_player.delete_instance() - db.close() if count == 1: raise HTTPException( @@ -301,7 +284,6 @@ async def update_columns( ): if not valid_token(token): logging.warning(f"Bad Token: {token}") - db.close() raise HTTPException( status_code=401, detail="You are not authorized to update mlb players. This event has been logged.", @@ -327,7 +309,6 @@ async def update_columns( logging.info(f"Updated {count} batting cards for {x.first_name} {x.last_name}") update_card_urls(x) - db.close() return f"Updated {total_count} batting cards" @@ -338,7 +319,6 @@ async def update_names( ): if not valid_token(token): logging.warning(f"Bad Token: {token}") - db.close() raise HTTPException( status_code=401, detail="You are not authorized to update mlb players. This event has been logged.", @@ -360,7 +340,6 @@ async def update_names( logging.info(f"Update {count} player records for {x.first_name} {x.last_name}") update_card_urls(x) - db.close() return f"Updated {total_count} names" diff --git a/app/routers_v2/notifications.py b/app/routers_v2/notifications.py index 5708f4b..233d5aa 100644 --- a/app/routers_v2/notifications.py +++ b/app/routers_v2/notifications.py @@ -38,7 +38,6 @@ async def get_notifs( all_notif = Notification.select().order_by(Notification.id) if all_notif.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'There are no notifications to filter') if created_after is not None: @@ -66,7 +65,6 @@ async def get_notifs( ]) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -74,7 +72,6 @@ async def get_notifs( for x in all_notif: return_val['notifs'].append(model_to_dict(x)) - db.close() return return_val @@ -83,7 +80,6 @@ async def get_one_notif(notif_id, csv: Optional[bool] = None): try: this_notif = Notification.get_by_id(notif_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}') if csv: @@ -94,12 +90,10 @@ async def get_one_notif(notif_id, csv: Optional[bool] = None): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_notif) - db.close() return return_val @@ -107,7 +101,6 @@ async def get_one_notif(notif_id, csv: Optional[bool] = None): async def post_notif(notif: NotifModel, 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 notifications. This event has been logged.' @@ -126,10 +119,8 @@ async def post_notif(notif: NotifModel, token: str = Depends(oauth2_scheme)): saved = this_notif.save() if saved == 1: return_val = model_to_dict(this_notif) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail='Well slap my ass and call me a teapot; I could not save that notification' @@ -143,7 +134,6 @@ async def patch_notif( ack: Optional[bool] = 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 notifications. This event has been logged.' @@ -151,7 +141,6 @@ async def patch_notif( try: this_notif = Notification.get_by_id(notif_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}') if title is not None: @@ -171,7 +160,6 @@ async def patch_notif( if this_notif.save() == 1: return_val = model_to_dict(this_notif) - db.close() return return_val else: raise HTTPException( @@ -184,7 +172,6 @@ async def patch_notif( async def delete_notif(notif_id, 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 delete notifications. This event has been logged.' @@ -192,11 +179,9 @@ async def delete_notif(notif_id, token: str = Depends(oauth2_scheme)): try: this_notif = Notification.get_by_id(notif_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}') count = this_notif.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Notification {notif_id} has been deleted') diff --git a/app/routers_v2/packs.py b/app/routers_v2/packs.py index 6a1af12..ba4ada0 100644 --- a/app/routers_v2/packs.py +++ b/app/routers_v2/packs.py @@ -41,21 +41,18 @@ async def get_packs( all_packs = Pack.select() if all_packs.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'There are no packs to filter') if team_id is not None: try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No team found with id {team_id}') all_packs = all_packs.where(Pack.team == this_team) if pack_type_id is not None: try: this_pack_type = PackType.get_by_id(pack_type_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No pack type found with id {pack_type_id}') all_packs = all_packs.where(Pack.pack_type == this_pack_type) @@ -63,7 +60,6 @@ async def get_packs( try: this_pack_team = Team.get_by_id(pack_team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No team found with id {pack_team_id}') all_packs = all_packs.where(Pack.pack_team == this_pack_team) elif exact_match: @@ -73,7 +69,6 @@ async def get_packs( try: this_pack_cardset = Cardset.get_by_id(pack_cardset_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No cardset found with id {pack_cardset_id}') all_packs = all_packs.where(Pack.pack_cardset == this_pack_cardset) elif exact_match: @@ -103,7 +98,6 @@ async def get_packs( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -111,7 +105,6 @@ async def get_packs( for x in all_packs: return_val['packs'].append(model_to_dict(x)) - db.close() return return_val @@ -120,7 +113,6 @@ async def get_one_pack(pack_id, csv: Optional[bool] = False): try: this_pack = Pack.get_by_id(pack_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}') if csv: @@ -131,12 +123,10 @@ async def get_one_pack(pack_id, csv: Optional[bool] = False): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_pack) - db.close() return return_val @@ -144,7 +134,6 @@ async def get_one_pack(pack_id, csv: Optional[bool] = False): async def post_pack(packs: PackModel, 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 packs. This event has been logged.' @@ -163,7 +152,6 @@ async def post_pack(packs: PackModel, token: str = Depends(oauth2_scheme)): with db.atomic(): Pack.bulk_create(new_packs, batch_size=15) - db.close() raise HTTPException(status_code=200, detail=f'{len(new_packs)} packs have been added') @@ -172,7 +160,6 @@ async def post_pack(packs: PackModel, token: str = Depends(oauth2_scheme)): async def post_one_pack(pack: PackPydantic, 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 packs. This event has been logged.' @@ -189,7 +176,6 @@ async def post_one_pack(pack: PackPydantic, token: str = Depends(oauth2_scheme)) saved = this_pack.save() if saved == 1: return_val = model_to_dict(this_pack) - db.close() return return_val else: raise HTTPException( @@ -204,7 +190,6 @@ async def patch_pack( pack_team_id: Optional[int] = None, pack_cardset_id: 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 packs. This event has been logged.' @@ -212,7 +197,6 @@ async def patch_pack( try: this_pack = Pack.get_by_id(pack_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}') if team_id is not None: @@ -237,7 +221,6 @@ async def patch_pack( if this_pack.save() == 1: return_val = model_to_dict(this_pack) - db.close() return return_val else: raise HTTPException( @@ -250,7 +233,6 @@ async def patch_pack( async def delete_pack(pack_id, 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 delete packs. This event has been logged.' @@ -258,11 +240,9 @@ async def delete_pack(pack_id, token: str = Depends(oauth2_scheme)): try: this_pack = Pack.get_by_id(pack_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No packs found with id {pack_id}') count = this_pack.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Pack {pack_id} has been deleted') diff --git a/app/routers_v2/packtypes.py b/app/routers_v2/packtypes.py index c0ad637..df3f7d7 100644 --- a/app/routers_v2/packtypes.py +++ b/app/routers_v2/packtypes.py @@ -34,7 +34,6 @@ async def get_packtypes( all_packtypes = PackType.select().order_by(PackType.id) if all_packtypes.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'There are no packtypes to filter') if name is not None: @@ -60,7 +59,6 @@ async def get_packtypes( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -68,7 +66,6 @@ async def get_packtypes( for x in all_packtypes: return_val['packtypes'].append(model_to_dict(x)) - db.close() return return_val @@ -77,7 +74,6 @@ async def get_one_packtype(packtype_id, csv: Optional[bool] = False): try: this_packtype = PackType.get_by_id(packtype_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}') if csv: @@ -87,12 +83,10 @@ async def get_one_packtype(packtype_id, csv: Optional[bool] = False): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_packtype) - db.close() return return_val @@ -100,7 +94,6 @@ async def get_one_packtype(packtype_id, csv: Optional[bool] = False): async def post_packtypes(packtype: PacktypeModel, 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 packtypes. This event has been logged.' @@ -108,7 +101,6 @@ async def post_packtypes(packtype: PacktypeModel, token: str = Depends(oauth2_sc dupe_packtype = PackType.get_or_none(PackType.name == packtype.name) if dupe_packtype: - db.close() raise HTTPException(status_code=400, detail=f'There is already a packtype using {packtype.name}') this_packtype = PackType( @@ -122,7 +114,6 @@ async def post_packtypes(packtype: PacktypeModel, token: str = Depends(oauth2_sc saved = this_packtype.save() if saved == 1: return_val = model_to_dict(this_packtype) - db.close() return return_val else: raise HTTPException( @@ -137,7 +128,6 @@ async def patch_packtype( cost: Optional[int] = None, available: Optional[bool] = 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 packtypes. This event has been logged.' @@ -145,7 +135,6 @@ async def patch_packtype( try: this_packtype = PackType.get_by_id(packtype_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}') if name is not None: @@ -161,7 +150,6 @@ async def patch_packtype( if this_packtype.save() == 1: return_val = model_to_dict(this_packtype) - db.close() return return_val else: raise HTTPException( @@ -174,7 +162,6 @@ async def patch_packtype( async def delete_packtype(packtype_id, 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 delete packtypes. This event has been logged.' @@ -182,11 +169,9 @@ async def delete_packtype(packtype_id, token: str = Depends(oauth2_scheme)): try: this_packtype = PackType.get_by_id(packtype_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}') count = this_packtype.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Packtype {packtype_id} has been deleted') diff --git a/app/routers_v2/paperdex.py b/app/routers_v2/paperdex.py index 957e733..4df6258 100644 --- a/app/routers_v2/paperdex.py +++ b/app/routers_v2/paperdex.py @@ -34,7 +34,6 @@ async def get_paperdex( all_dex = Paperdex.select().join(Player).join(Cardset).order_by(Paperdex.id) if all_dex.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'There are no paperdex to filter') if team_id is not None: @@ -67,7 +66,6 @@ async def get_paperdex( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -75,7 +73,6 @@ async def get_paperdex( for x in all_dex: return_val['paperdex'].append(model_to_dict(x, recurse=not flat)) - db.close() return return_val @@ -84,7 +81,6 @@ async def get_one_paperdex(paperdex_id, csv: Optional[bool] = False): try: this_dex = Paperdex.get_by_id(paperdex_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}') if csv: @@ -94,12 +90,10 @@ async def get_one_paperdex(paperdex_id, csv: Optional[bool] = False): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_dex) - db.close() return return_val @@ -107,7 +101,6 @@ async def get_one_paperdex(paperdex_id, csv: Optional[bool] = False): async def post_paperdex(paperdex: PaperdexModel, 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 paperdex. This event has been logged.' @@ -116,7 +109,6 @@ async def post_paperdex(paperdex: PaperdexModel, token: str = Depends(oauth2_sch dupe_dex = Paperdex.get_or_none(Paperdex.team_id == paperdex.team_id, Paperdex.player_id == paperdex.player_id) if dupe_dex: return_val = model_to_dict(dupe_dex) - db.close() return return_val this_dex = Paperdex( @@ -128,7 +120,6 @@ async def post_paperdex(paperdex: PaperdexModel, token: str = Depends(oauth2_sch saved = this_dex.save() if saved == 1: return_val = model_to_dict(this_dex) - db.close() return return_val else: raise HTTPException( @@ -143,7 +134,6 @@ async def patch_paperdex( 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 paperdex. This event has been logged.' @@ -151,7 +141,6 @@ async def patch_paperdex( try: this_dex = Paperdex.get_by_id(paperdex_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}') if team_id is not None: @@ -163,7 +152,6 @@ async def patch_paperdex( if this_dex.save() == 1: return_val = model_to_dict(this_dex) - db.close() return return_val else: raise HTTPException( @@ -176,7 +164,6 @@ async def patch_paperdex( async def delete_paperdex(paperdex_id, 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 delete rewards. This event has been logged.' @@ -184,11 +171,9 @@ async def delete_paperdex(paperdex_id, token: str = Depends(oauth2_scheme)): try: this_dex = Paperdex.get_by_id(paperdex_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}') count = this_dex.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Paperdex {this_dex} has been deleted') @@ -200,7 +185,6 @@ async def delete_paperdex(paperdex_id, token: str = Depends(oauth2_scheme)): async def wipe_ai_paperdex(token: str = Depends(oauth2_scheme)): if not valid_token(token): logging.warning(f'Bad Token: {token}') - db.close() raise HTTPException( status_code=401, detail='Unauthorized' diff --git a/app/routers_v2/pitchingcardratings.py b/app/routers_v2/pitchingcardratings.py index 80b589d..fea857e 100644 --- a/app/routers_v2/pitchingcardratings.py +++ b/app/routers_v2/pitchingcardratings.py @@ -153,7 +153,6 @@ async def get_card_ratings( ): if not valid_token(token): logging.warning(f"Bad Token: {token}") - db.close() raise HTTPException( status_code=401, detail="You are not authorized to pull card ratings." ) @@ -177,7 +176,6 @@ async def get_card_ratings( if csv: return_val = query_to_csv(all_ratings) - db.close() return Response(content=return_val, media_type="text/csv") else: @@ -187,7 +185,6 @@ async def get_card_ratings( model_to_dict(x, recurse=not short_output) for x in all_ratings ], } - db.close() return return_val @@ -246,7 +243,6 @@ def get_scouting_dfs(cardset_id: list = None): dict([(x.player.player_id, x.error) for x in positions]), name=f"Error P" ), ] - db.close() logging.debug(f"series_list: {series_list}") return pit_df.join(series_list) @@ -258,7 +254,6 @@ async def get_card_scouting(team_id: int, ts: str): logging.debug(f"Team: {this_team} / has_guide: {this_team.has_guide}") if this_team is None or ts != this_team.team_hash() or this_team.has_guide != 1: logging.warning(f"Team_id {team_id} attempted to pull ratings") - db.close() return ( "Your team does not have the ratings guide enabled. If you have purchased a copy ping Cal to " "make sure it is enabled on your team. If you are interested you can pick it up here (thank you!): " @@ -281,7 +276,6 @@ async def get_card_scouting(team_id: int, ts: str): async def post_calc_scouting(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 calculate card ratings." ) @@ -318,7 +312,6 @@ async def get_basic_scouting(): async def post_calc_basic(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 calculate basic ratings." ) @@ -498,20 +491,17 @@ async def post_calc_basic(token: str = Depends(oauth2_scheme)): async def get_one_rating(ratings_id: int, 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 pull card ratings." ) this_rating = PitchingCardRatings.get_or_none(PitchingCardRatings.id == ratings_id) if this_rating is None: - db.close() raise HTTPException( status_code=404, detail=f"PitchingCardRating id {ratings_id} not found" ) r_data = model_to_dict(this_rating) - db.close() return r_data @@ -535,7 +525,6 @@ async def get_player_ratings( "count": all_ratings.count(), "ratings": [model_to_dict(x, recurse=not short_output) for x in all_ratings], } - db.close() return return_val @@ -543,7 +532,6 @@ async def get_player_ratings( async def put_ratings(ratings: RatingsList, 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 card ratings." ) @@ -571,7 +559,6 @@ async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)) # Use PostgreSQL-compatible upsert helper upsert_pitching_card_ratings(new_ratings, batch_size=30) - db.close() return f"Updated ratings: {updates}; new ratings: {len(new_ratings)}" @@ -579,20 +566,17 @@ async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)) async def delete_rating(ratings_id: int, 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 card ratings." ) this_rating = PitchingCardRatings.get_or_none(PitchingCardRatings.id == ratings_id) if this_rating is None: - db.close() raise HTTPException( status_code=404, detail=f"PitchingCardRating id {ratings_id} not found" ) count = this_rating.delete_instance() - db.close() if count == 1: return f"Rating {this_rating} has been deleted" diff --git a/app/routers_v2/pitchingcards.py b/app/routers_v2/pitchingcards.py index 9bc060b..43e238c 100644 --- a/app/routers_v2/pitchingcards.py +++ b/app/routers_v2/pitchingcards.py @@ -62,7 +62,6 @@ async def get_pitching_cards( "count": all_cards.count(), "cards": [model_to_dict(x, recurse=not short_output) for x in all_cards], } - db.close() return return_val @@ -70,13 +69,11 @@ async def get_pitching_cards( async def get_one_card(card_id: int): this_card = PitchingCard.get_or_none(PitchingCard.id == card_id) if this_card is None: - db.close() raise HTTPException( status_code=404, detail=f"PitchingCard id {card_id} not found" ) r_card = model_to_dict(this_card) - db.close() return r_card @@ -96,7 +93,6 @@ async def get_player_cards( "count": all_cards.count(), "cards": [model_to_dict(x, recurse=not short_output) for x in all_cards], } - db.close() return return_val @@ -104,7 +100,6 @@ async def get_player_cards( async def put_cards(cards: PitchingCardList, 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 pitching cards. This event has been logged.", @@ -153,7 +148,6 @@ async def put_cards(cards: PitchingCardList, token: str = Depends(oauth2_scheme) # Use PostgreSQL-compatible upsert helper upsert_pitching_cards(new_cards, batch_size=30) - db.close() return f"Updated cards: {updates}; new cards: {len(new_cards)}" @@ -171,7 +165,6 @@ async def patch_card( ): 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 pitching cards. This event has been logged.", @@ -179,7 +172,6 @@ async def patch_card( this_card = PitchingCard.get_or_none(PitchingCard.id == card_id) if this_card is None: - db.close() raise HTTPException( status_code=404, detail=f"PitchingCard id {card_id} not found" ) @@ -201,10 +193,8 @@ async def patch_card( if this_card.save() == 1: return_val = model_to_dict(this_card) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail="Well slap my ass and call me a teapot; I could not save that card", @@ -215,7 +205,6 @@ async def patch_card( async def delete_card(card_id: int, 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 delete pitching cards. This event has been logged.", @@ -223,11 +212,9 @@ async def delete_card(card_id: int, token: str = Depends(oauth2_scheme)): this_card = PitchingCard.get_or_none(PitchingCard.id == card_id) if this_card is None: - db.close() raise HTTPException(status_code=404, detail=f"Pitching id {card_id} not found") count = this_card.delete_instance() - db.close() if count == 1: return f"Card {this_card} has been deleted" @@ -241,7 +228,6 @@ async def delete_card(card_id: int, token: str = Depends(oauth2_scheme)): async def delete_all_cards(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 delete pitching cards. This event has been logged.", diff --git a/app/routers_v2/pitstats.py b/app/routers_v2/pitstats.py index 58a564d..5219ca0 100644 --- a/app/routers_v2/pitstats.py +++ b/app/routers_v2/pitstats.py @@ -108,7 +108,6 @@ async def get_pit_stats( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -116,7 +115,6 @@ async def get_pit_stats( for x in all_stats: return_val['stats'].append(model_to_dict(x, recurse=False)) - db.close() return return_val @@ -124,7 +122,6 @@ async def get_pit_stats( async def post_pitstat(stats: PitchingStatModel, 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 stats. This event has been logged.' @@ -164,7 +161,6 @@ async def post_pitstat(stats: PitchingStatModel, token: str = Depends(oauth2_sch with db.atomic(): PitchingStat.bulk_create(new_stats, batch_size=15) - db.close() raise HTTPException(status_code=200, detail=f'{len(new_stats)} pitching lines have been added') @@ -173,7 +169,6 @@ async def post_pitstat(stats: PitchingStatModel, token: str = Depends(oauth2_sch async def delete_pitstat(stat_id, 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 delete stats. This event has been logged.' @@ -181,11 +176,9 @@ async def delete_pitstat(stat_id, token: str = Depends(oauth2_scheme)): try: this_stat = PitchingStat.get_by_id(stat_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No stat found with id {stat_id}') count = this_stat.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Stat {stat_id} has been deleted') diff --git a/app/routers_v2/players.py b/app/routers_v2/players.py index 37f20d6..bd0cba0 100644 --- a/app/routers_v2/players.py +++ b/app/routers_v2/players.py @@ -152,7 +152,6 @@ async def get_players( ): all_players = Player.select() if all_players.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f"There are no players to filter") if name is not None: @@ -239,7 +238,6 @@ async def get_players( if csv: card_vals = [model_to_dict(x) for x in all_players] - db.close() for x in card_vals: x["player_name"] = x["p_name"] @@ -328,7 +326,6 @@ async def get_players( # return_val['players'].append(model_to_dict(x, recurse=not flat)) - db.close() return return_val @@ -479,7 +476,6 @@ async def get_random_player( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type="text/csv") else: @@ -497,7 +493,6 @@ async def get_random_player( return_val["players"].append(this_record) # return_val['players'].append(model_to_dict(x)) - db.close() return return_val @@ -591,7 +586,6 @@ async def search_players( return_val["players"].append(this_record) - db.close() return return_val @@ -600,7 +594,6 @@ async def get_one_player(player_id, csv: Optional[bool] = False): try: this_player = Player.get_by_id(player_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No player found with id {player_id}" ) @@ -660,7 +653,6 @@ async def get_one_player(player_id, csv: Optional[bool] = False): ] ) - db.close() return Response(content=return_val, media_type="text/csv") else: return_val = model_to_dict(this_player) @@ -668,7 +660,6 @@ async def get_one_player(player_id, csv: Optional[bool] = False): return_val["paperdex"] = {"count": this_dex.count(), "paperdex": []} for x in this_dex: return_val["paperdex"]["paperdex"].append(model_to_dict(x, recurse=False)) - db.close() return return_val @@ -686,7 +677,6 @@ async def get_batter_card( try: this_player = Player.get_by_id(player_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No player found with id {player_id}" ) @@ -701,7 +691,6 @@ async def get_batter_card( ) and html is False ): - db.close() return FileResponse( path=f"storage/cards/cardset-{this_player.cardset.id}/{card_type}/{player_id}-{d}-v{variant}.png", media_type="image/png", @@ -788,7 +777,6 @@ async def get_batter_card( html_response = templates.TemplateResponse("player_card.html", card_data) if html: - db.close() return html_response updates = 0 @@ -843,7 +831,6 @@ async def get_batter_card( # save_as=f'{player_id}-{d}-v{variant}.png' # ) - db.close() return FileResponse(path=file_path, media_type="image/png", headers=headers) @@ -882,7 +869,6 @@ async def v1_players_patch( ): 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 players. This event has been logged.", @@ -891,7 +877,6 @@ async def v1_players_patch( try: this_player = Player.get_by_id(player_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No player found with id {player_id}" ) @@ -915,7 +900,6 @@ async def v1_players_patch( try: this_cardset = Cardset.get_by_id(cardset_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No cardset found with id {cardset_id}" ) @@ -924,7 +908,6 @@ async def v1_players_patch( try: this_rarity = Rarity.get_by_id(rarity_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No rarity found with id {rarity_id}" ) @@ -986,7 +969,6 @@ async def v1_players_patch( if this_player.save() == 1: return_val = model_to_dict(this_player) - db.close() return return_val else: raise HTTPException( @@ -999,7 +981,6 @@ async def v1_players_patch( async def put_players(players: PlayerModel, 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 players. This event has been logged.", @@ -1068,7 +1049,6 @@ async def put_players(players: PlayerModel, token: str = Depends(oauth2_scheme)) with db.atomic(): # Use PostgreSQL-compatible upsert helper (preserves SQLite compatibility) upsert_players(new_players, batch_size=15) - db.close() # sheets.update_all_players(SHEETS_AUTH) raise HTTPException( @@ -1080,7 +1060,6 @@ async def put_players(players: PlayerModel, token: str = Depends(oauth2_scheme)) async def post_players(new_player: PlayerPydantic, 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 players. This event has been logged.", @@ -1091,7 +1070,6 @@ async def post_players(new_player: PlayerPydantic, token: str = Depends(oauth2_s & (Player.cardset_id == new_player.cardset_id) ) if dupe_query.count() != 0: - db.close() raise HTTPException( status_code=400, detail=f"This appears to be a duplicate with player {dupe_query[0].player_id}", @@ -1104,7 +1082,6 @@ async def post_players(new_player: PlayerPydantic, token: str = Depends(oauth2_s p_id = Player.insert(new_player.dict()).execute() return_val = model_to_dict(Player.get_by_id(p_id)) - db.close() return return_val @@ -1114,7 +1091,6 @@ async def post_image_reset( ): if not valid_token(token): logging.warning(f"Bad Token: {token}") - db.close() raise HTTPException( status_code=401, detail="You are not authorized to modify players. This event has been logged.", @@ -1122,7 +1098,6 @@ async def post_image_reset( this_player = Player.get_or_none(Player.player_id == player_id) if this_player is None: - db.close() raise HTTPException(status_code=404, detail=f"Player ID {player_id} not found") now = datetime.datetime.now() @@ -1143,7 +1118,6 @@ async def post_image_reset( this_player.save() r_player = model_to_dict(this_player) - db.close() return r_player @@ -1151,7 +1125,6 @@ async def post_image_reset( async def delete_player(player_id, 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 delete players. This event has been logged.", @@ -1160,13 +1133,11 @@ async def delete_player(player_id, token: str = Depends(oauth2_scheme)): try: this_player = Player.get_by_id(player_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No player found with id {player_id}" ) count = this_player.delete_instance() - db.close() if count == 1: raise HTTPException( diff --git a/app/routers_v2/rarity.py b/app/routers_v2/rarity.py index a0b756a..7635f78 100644 --- a/app/routers_v2/rarity.py +++ b/app/routers_v2/rarity.py @@ -31,7 +31,6 @@ async def get_rarities(value: Optional[int] = None, name: Optional[str] = None, all_rarities = Rarity.select().order_by(Rarity.id) if all_rarities.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'There are no rarities to filter') if value is not None: @@ -44,7 +43,6 @@ async def get_rarities(value: Optional[int] = None, name: Optional[str] = None, all_rarities = all_rarities.where(Rarity.value <= max_value) if all_rarities.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'No rarities found') if csv: @@ -57,7 +55,6 @@ async def get_rarities(value: Optional[int] = None, name: Optional[str] = None, ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -65,7 +62,6 @@ async def get_rarities(value: Optional[int] = None, name: Optional[str] = None, for x in all_rarities: return_val['rarities'].append(model_to_dict(x)) - db.close() return return_val @@ -74,7 +70,6 @@ async def get_one_rarity(rarity_id, csv: Optional[bool] = False): try: this_rarity = Rarity.get_by_id(rarity_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}') if csv: @@ -87,11 +82,9 @@ async def get_one_rarity(rarity_id, csv: Optional[bool] = False): ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_rarity) - db.close() return return_val @@ -99,7 +92,6 @@ async def get_one_rarity(rarity_id, csv: Optional[bool] = False): async def post_rarity(rarity: RarityModel, 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 rarities. This event has been logged.' @@ -107,7 +99,6 @@ async def post_rarity(rarity: RarityModel, token: str = Depends(oauth2_scheme)): dupe_team = Rarity.get_or_none(Rarity.name) if dupe_team: - db.close() raise HTTPException(status_code=400, detail=f'There is already a rarity using {rarity.name}') this_rarity = Rarity( @@ -119,7 +110,6 @@ async def post_rarity(rarity: RarityModel, token: str = Depends(oauth2_scheme)): saved = this_rarity.save() if saved == 1: return_val = model_to_dict(this_rarity) - db.close() return return_val else: raise HTTPException( @@ -134,7 +124,6 @@ async def patch_rarity( 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 rarities. This event has been logged.' @@ -142,7 +131,6 @@ async def patch_rarity( try: this_rarity = Rarity.get_by_id(rarity_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}') if value is not None: @@ -154,7 +142,6 @@ async def patch_rarity( if this_rarity.save() == 1: return_val = model_to_dict(this_rarity) - db.close() return return_val else: raise HTTPException( @@ -167,7 +154,6 @@ async def patch_rarity( async def v1_rarities_delete(rarity_id, 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 delete rarities. This event has been logged.' @@ -175,11 +161,9 @@ async def v1_rarities_delete(rarity_id, token: str = Depends(oauth2_scheme)): try: this_rarity = Rarity.get_by_id(rarity_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}') count = this_rarity.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Rarity {rarity_id} has been deleted') diff --git a/app/routers_v2/results.py b/app/routers_v2/results.py index d863f43..0d54cd3 100644 --- a/app/routers_v2/results.py +++ b/app/routers_v2/results.py @@ -56,7 +56,6 @@ async def get_results( this_team = Team.get_by_id(away_team_id) all_results = all_results.where(Result.away_team == this_team) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No team found with id {away_team_id}') if home_team_id is not None: @@ -64,7 +63,6 @@ async def get_results( this_team = Team.get_by_id(home_team_id) all_results = all_results.where(Result.home_team == this_team) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No team found with id {home_team_id}') if team_one_id is not None: @@ -72,7 +70,6 @@ async def get_results( this_team = Team.get_by_id(team_one_id) all_results = all_results.where((Result.home_team == this_team) | (Result.away_team == this_team)) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No team found with id {team_one_id}') if team_two_id is not None: @@ -80,7 +77,6 @@ async def get_results( this_team = Team.get_by_id(team_two_id) all_results = all_results.where((Result.home_team == this_team) | (Result.away_team == this_team)) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No team found with id {team_two_id}') if away_score_min is not None: @@ -153,7 +149,6 @@ async def get_results( ]) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -161,7 +156,6 @@ async def get_results( for x in all_results: return_val['results'].append(model_to_dict(x)) - db.close() return return_val @@ -170,7 +164,6 @@ async def get_one_results(result_id, csv: Optional[bool] = None): try: this_result = Result.get_by_id(result_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No result found with id {result_id}') if csv: @@ -184,12 +177,10 @@ async def get_one_results(result_id, csv: Optional[bool] = None): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_result) - db.close() return return_val @@ -243,7 +234,6 @@ async def get_team_results( ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -254,7 +244,6 @@ async def get_team_results( 'casual_wins': c_wins, 'casual_losses': c_loss, } - db.close() return return_val @@ -262,7 +251,6 @@ async def get_team_results( async def post_result(result: ResultModel, 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 results. This event has been logged.' @@ -273,12 +261,10 @@ async def post_result(result: ResultModel, token: str = Depends(oauth2_scheme)): if result.ranked: if not result.away_team_ranking: - db.close() error = f'Ranked game did not include away team ({result.away_team_id}) ranking.' logging.error(error) raise DataError(error) if not result.home_team_ranking: - db.close() error = f'Ranked game did not include home team ({result.home_team_id}) ranking.' logging.error(error) raise DataError(error) @@ -328,10 +314,8 @@ async def post_result(result: ResultModel, token: str = Depends(oauth2_scheme)): if saved == 1: return_val = model_to_dict(this_result) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail='Well slap my ass and call me a teapot; I could not save that roster' @@ -347,7 +331,6 @@ async def patch_result( 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 results. This event has been logged.' @@ -355,7 +338,6 @@ async def patch_result( try: this_result = Result.get_by_id(result_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No result found with id {result_id}') if away_team_id is not None: @@ -396,10 +378,8 @@ async def patch_result( if this_result.save() == 1: return_val = model_to_dict(this_result) - db.close() return return_val else: - db.close() raise HTTPException( status_code=418, detail='Well slap my ass and call me a teapot; I could not save that event' @@ -410,7 +390,6 @@ async def patch_result( async def delete_result(result_id, 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 results. This event has been logged.' @@ -418,11 +397,9 @@ async def delete_result(result_id, token: str = Depends(oauth2_scheme)): try: this_result = Result.get_by_id(result_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No result found with id {result_id}') count = this_result.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Result {result_id} has been deleted') diff --git a/app/routers_v2/rewards.py b/app/routers_v2/rewards.py index 4f79e63..81395b1 100644 --- a/app/routers_v2/rewards.py +++ b/app/routers_v2/rewards.py @@ -36,7 +36,6 @@ async def get_rewards( all_rewards = Reward.select().order_by(Reward.id) if all_rewards.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'There are no rewards to filter') if name is not None: @@ -55,7 +54,6 @@ async def get_rewards( all_rewards = all_rewards.where(Reward.week == week) if all_rewards.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f'No rewards found') if csv: @@ -68,7 +66,6 @@ async def get_rewards( ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: @@ -76,7 +73,6 @@ async def get_rewards( for x in all_rewards: return_val['rewards'].append(model_to_dict(x, recurse=not flat)) - db.close() return return_val @@ -85,7 +81,6 @@ async def get_one_reward(reward_id, csv: Optional[bool] = False): try: this_reward = Reward.get_by_id(reward_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}') if csv: @@ -95,12 +90,10 @@ async def get_one_reward(reward_id, csv: Optional[bool] = False): ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - db.close() return Response(content=return_val, media_type='text/csv') else: return_val = model_to_dict(this_reward) - db.close() return return_val @@ -108,7 +101,6 @@ async def get_one_reward(reward_id, csv: Optional[bool] = False): async def post_rewards(reward: RewardModel, 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 rewards. This event has been logged.' @@ -123,7 +115,6 @@ async def post_rewards(reward: RewardModel, token: str = Depends(oauth2_scheme)) saved = this_reward.save() if saved == 1: return_val = model_to_dict(this_reward) - db.close() return return_val else: raise HTTPException( @@ -138,7 +129,6 @@ async def patch_reward( 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 rewards. This event has been logged.' @@ -146,7 +136,6 @@ async def patch_reward( try: this_reward = Reward.get_by_id(reward_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}') if name is not None: @@ -159,7 +148,6 @@ async def patch_reward( if this_reward.save() == 1: return_val = model_to_dict(this_reward) - db.close() return return_val else: raise HTTPException( @@ -172,7 +160,6 @@ async def patch_reward( async def delete_reward(reward_id, 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 delete rewards. This event has been logged.' @@ -180,11 +167,9 @@ async def delete_reward(reward_id, token: str = Depends(oauth2_scheme)): try: this_reward = Reward.get_by_id(reward_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}') count = this_reward.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f'Reward {reward_id} has been deleted') diff --git a/app/routers_v2/scouting.py b/app/routers_v2/scouting.py index b412ae6..78464f4 100644 --- a/app/routers_v2/scouting.py +++ b/app/routers_v2/scouting.py @@ -50,7 +50,6 @@ async def get_player_keys(player_id: list = Query(default=None)): return_val = {'count': len(all_keys), 'keys': [ dict(x) for x in all_keys ]} - db.close() return return_val @@ -58,7 +57,6 @@ async def get_player_keys(player_id: list = Query(default=None)): def live_update_batting(files: BattingFiles, cardset_id: int, 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 initiate live updates.' @@ -93,7 +91,6 @@ def live_update_batting(files: BattingFiles, cardset_id: int, token: str = Depen def live_update_pitching(files: BattingFiles, 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 initiate live updates.' diff --git a/app/routers_v2/stratgame.py b/app/routers_v2/stratgame.py index 9843df2..c2b7ac2 100644 --- a/app/routers_v2/stratgame.py +++ b/app/routers_v2/stratgame.py @@ -83,7 +83,6 @@ async def get_games( x['home_abbrev'] = x['home_team']['abbrev'] del x['away_team'], x['home_team'] - db.close() output = pd.DataFrame(return_vals)[[ 'id', 'away_abbrev', 'home_abbrev', 'away_score', 'home_score', 'away_team_value', 'home_team_value', 'game_type', 'season', 'week', 'short_game', 'ranked' @@ -94,7 +93,6 @@ async def get_games( return_val = {'count': all_games.count(), 'games': [ model_to_dict(x, recurse=not short_output) for x in all_games ]} - db.close() return return_val @@ -102,11 +100,9 @@ async def get_games( async def get_one_game(game_id: int): this_game = StratGame.get_or_none(StratGame.id == game_id) if not this_game: - db.close() raise HTTPException(status_code=404, detail=f'StratGame ID {game_id} not found') g_result = model_to_dict(this_game) - db.close() return g_result @@ -120,7 +116,6 @@ async def patch_game( this_game = StratGame.get_or_none(StratGame.id == game_id) if not this_game: - db.close() raise HTTPException(status_code=404, detail=f'StratGame ID {game_id} not found') if away_score is not None: @@ -132,10 +127,8 @@ async def patch_game( if this_game.save() == 1: g_result = model_to_dict(this_game) - db.close() return g_result else: - db.close() raise HTTPException(status_code=500, detail=f'Unable to patch game {game_id}') @@ -150,7 +143,6 @@ async def post_game(this_game: GameModel, token: str = Depends(oauth2_scheme)): saved = this_game.save() if saved == 1: return_val = model_to_dict(this_game) - db.close() return return_val else: raise HTTPException( @@ -167,11 +159,9 @@ async def delete_game(game_id: int, token: str = Depends(oauth2_scheme)): this_game = StratGame.get_or_none(StratGame.id == game_id) if not this_game: - db.close() raise HTTPException(status_code=404, detail=f'StratGame ID {game_id} not found') count = this_game.delete_instance() - db.close() if count == 1: return f'StratGame {game_id} has been deleted' diff --git a/app/routers_v2/stratplays.py b/app/routers_v2/stratplays.py index 95aecb2..0b15eee 100644 --- a/app/routers_v2/stratplays.py +++ b/app/routers_v2/stratplays.py @@ -367,7 +367,6 @@ async def get_plays( x["runner_team"], ) - db.close() return Response( content=pd.DataFrame(return_vals).to_csv(index=False), media_type="text/csv" ) @@ -376,7 +375,6 @@ async def get_plays( "count": all_plays.count(), "plays": [model_to_dict(x, recurse=not short_output) for x in all_plays], } - db.close() return return_plays @@ -808,12 +806,10 @@ async def get_batting_totals( exclude = first + ["lob_all", "lob_all_rate", "lob_2outs", "rbi%"] output = output[first + [col for col in output.columns if col not in exclude]] - db.close() return Response( content=pd.DataFrame(output).to_csv(index=False), media_type="text/csv" ) - db.close() return return_stats @@ -1175,7 +1171,6 @@ async def get_pitching_totals( "rbi%": rbi_rate, } ) - db.close() if csv: return_vals = return_stats["stats"] @@ -1209,7 +1204,6 @@ async def get_pitching_totals( exclude = first + ["lob_2outs", "rbi%"] output = output[first + [col for col in output.columns if col not in exclude]] - db.close() return Response( content=pd.DataFrame(output).to_csv(index=False), media_type="text/csv" ) @@ -1227,7 +1221,6 @@ async def get_game_summary( ): this_game = StratGame.get_or_none(StratGame.id == game_id) if this_game is None: - db.close() raise HTTPException(status_code=404, detail=f"Game {game_id} not found") game_plays = StratPlay.select().where(StratPlay.game_id == game_id) @@ -1405,10 +1398,8 @@ async def get_game_summary( @router.get("/{play_id}") async def get_one_play(play_id: int): if StratPlay.get_or_none(StratPlay.id == play_id) is None: - db.close() raise HTTPException(status_code=404, detail=f"Play ID {play_id} not found") r_play = model_to_dict(StratPlay.get_by_id(play_id)) - db.close() return r_play @@ -1421,12 +1412,10 @@ async def patch_play( raise HTTPException(status_code=401, detail="Unauthorized") if StratPlay.get_or_none(StratPlay.id == play_id) is None: - db.close() raise HTTPException(status_code=404, detail=f"Play ID {play_id} not found") StratPlay.update(**new_play.dict()).where(StratPlay.id == play_id).execute() r_play = model_to_dict(StratPlay.get_by_id(play_id)) - db.close() return r_play @@ -1476,7 +1465,6 @@ async def post_plays(p_list: PlayList, token: str = Depends(oauth2_scheme)): with db.atomic(): # Use PostgreSQL-compatible upsert helper upsert_strat_plays(new_plays, batch_size=20) - db.close() return f"Inserted {len(new_plays)} plays" @@ -1489,11 +1477,9 @@ async def delete_play(play_id: int, token: str = Depends(oauth2_scheme)): this_play = StratPlay.get_or_none(StratPlay.id == play_id) if not this_play: - db.close() raise HTTPException(status_code=404, detail=f"Play ID {play_id} not found") count = this_play.delete_instance() - db.close() if count == 1: return f"Play {play_id} has been deleted" @@ -1511,11 +1497,9 @@ async def delete_plays_game(game_id: int, token: str = Depends(oauth2_scheme)): 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 = StratPlay.delete().where(StratPlay.game == this_game).execute() - db.close() if count > 0: return f"Deleted {count} plays matching Game ID {game_id}" diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index 0b275fb..29cb690 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -166,7 +166,6 @@ async def get_teams( if csv: return_val = query_to_csv(all_teams, exclude=[Team.career]) - db.close() return Response(content=return_val, media_type="text/csv") else: @@ -174,7 +173,6 @@ async def get_teams( for x in all_teams: return_teams["teams"].append(model_to_dict(x)) - db.close() return return_teams @@ -183,7 +181,6 @@ async def get_one_team(team_id, inc_packs: bool = True, csv: Optional[bool] = Fa try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") p_query = Pack.select().where( @@ -198,7 +195,6 @@ async def get_one_team(team_id, inc_packs: bool = True, csv: Optional[bool] = Fa if inc_packs: return_val["sealed_packs"] = [model_to_dict(x) for x in p_query] - db.close() return return_val @@ -287,7 +283,6 @@ def get_scouting_dfs(allowed_players, position: str): ) ) - db.close() def get_total_ops(df_data): ops_vl = df_data["obp_vl"] + df_data["slg_vl"] @@ -314,11 +309,9 @@ async def get_team_lineup( """ this_team = Team.get_or_none(Team.id == team_id) if this_team is None: - db.close() raise HTTPException(status_code=404, detail=f"Team id {team_id} not found") if difficulty_name not in CARDSETS.keys() and difficulty_name != "exhibition": - db.close() raise HTTPException( status_code=400, detail=f"Difficulty name {difficulty_name} not a valid check", @@ -332,7 +325,6 @@ async def get_team_lineup( if difficulty_name == "exhibition": logging.info(f"pulling an exhibition lineup") if cardset_id is None: - db.close() raise HTTPException( status_code=400, detail=f"Must provide at least one cardset_id for exhibition lineups", @@ -631,11 +623,9 @@ async def get_team_sp( ) this_team = Team.get_or_none(Team.id == team_id) if this_team is None: - db.close() raise HTTPException(status_code=404, detail=f"Team id {team_id} not found") if difficulty_name not in CARDSETS.keys() and difficulty_name != "exhibition": - db.close() raise HTTPException( status_code=400, detail=f"Difficulty name {difficulty_name} not a valid check", @@ -646,7 +636,6 @@ async def get_team_sp( if difficulty_name == "exhibition": logging.info(f"pulling an exhibition lineup") if cardset_id is None: - db.close() raise HTTPException( status_code=400, detail=f"Must provide at least one cardset_id for exhibition lineups", @@ -710,13 +699,11 @@ async def get_team_sp( if all_starters is not None and len(all_starters.index) >= sp_rank: this_player_id = all_starters.iloc[sp_rank - 1].player this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False) - db.close() return this_player if all_starters is not None and len(all_starters.index) > 0: this_player_id = all_starters.iloc[len(all_starters.index) - 1].player this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False) - db.close() return this_player # Include backup cardsets @@ -729,13 +716,11 @@ async def get_team_sp( if all_starters is not None and len(all_starters.index) >= sp_rank: this_player_id = all_starters.iloc[sp_rank - 1].player this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False) - db.close() return this_player if all_starters is not None and len(all_starters.index) > 0: this_player_id = all_starters.iloc[len(all_starters.index) - 1].player this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False) - db.close() return this_player raise HTTPException( @@ -758,11 +743,9 @@ async def get_team_rp( ) this_team = Team.get_or_none(Team.id == team_id) if this_team is None: - db.close() raise HTTPException(status_code=404, detail=f"Team id {team_id} not found") if difficulty_name not in CARDSETS.keys() and difficulty_name != "exhibition": - db.close() raise HTTPException( status_code=400, detail=f"Difficulty name {difficulty_name} not a valid check", @@ -776,7 +759,6 @@ async def get_team_rp( if difficulty_name == "exhibition": logging.info(f"pulling an exhibition RP") if cardset_id is None: - db.close() raise HTTPException( status_code=400, detail=f"Must provide at least one cardset_id for exhibition lineups", @@ -848,7 +830,6 @@ async def get_team_rp( this_player = model_to_dict( Player.get_by_id(this_player_id), recurse=False ) - db.close() return this_player elif need == "setup": @@ -873,7 +854,6 @@ async def get_team_rp( this_player = model_to_dict( Player.get_by_id(this_player_id), recurse=False ) - db.close() return this_player elif need == "length" or len(used_pitcher_ids) > 4: @@ -907,7 +887,6 @@ async def get_team_rp( this_player = model_to_dict( Player.get_by_id(this_player_id), recurse=False ) - db.close() return this_player elif need == "middle": @@ -932,7 +911,6 @@ async def get_team_rp( this_player = model_to_dict( Player.get_by_id(this_player_id), recurse=False ) - db.close() return this_player logging.info(f"Falling to last chance pitcher") @@ -948,7 +926,6 @@ async def get_team_rp( if all_relievers is not None: this_player_id = all_relievers.iloc[len(all_relievers.index) - 1].player this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False) - db.close() return this_player raise HTTPException(status_code=400, detail=f"No RP found for Team {team_id}") @@ -1034,7 +1011,6 @@ async def get_team_record(team_id: int, season: int): # team_games = lg_query.where((StratGame.away_team_id == x) | (StratGame.home_team_id == x)) # for game in team_games: - db.close() return standings @@ -1043,12 +1019,10 @@ async def team_buy_players(team_id: int, ids: str, ts: str): try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if ts != this_team.team_hash(): logging.warning(f"Bad Team Secret: {ts} ({this_team.team_hash()})") - db.close() raise HTTPException( status_code=401, detail=f"You are not authorized to buy {this_team.abbrev} cards. This event has been logged.", @@ -1065,7 +1039,6 @@ async def team_buy_players(team_id: int, ids: str, ts: str): try: this_player = Player.get_by_id(player_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No player found with id {player_id} /// " @@ -1078,7 +1051,6 @@ async def team_buy_players(team_id: int, ids: str, ts: str): f"{this_player} was not purchased. {this_team.lname} only has {this_team.wallet}₼, but " f"{this_player} costs {this_player.cost}₼." ) - db.close() raise HTTPException( 200, detail=f"{this_player} was not purchased. {this_team.lname} only has {this_team.wallet}₼, but " @@ -1135,7 +1107,6 @@ async def team_buy_packs( try: this_packtype = PackType.get_by_id(packtype_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No pack type found with id {packtype_id}" ) @@ -1143,12 +1114,10 @@ async def team_buy_packs( try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if ts != this_team.team_hash(): logging.warning(f"Bad Team Secret: {ts} ({this_team.team_hash()})") - db.close() logging.warning( f"team: {this_team} / pack_type: {this_packtype} / secret: {ts} / " f"actual: {this_team.team_hash()}" @@ -1161,7 +1130,6 @@ async def team_buy_packs( # check wallet balance total_cost = this_packtype.cost * quantity if this_team.wallet < total_cost: - db.close() raise HTTPException( 200, detail=f"{this_packtype} was not purchased. {this_team.lname} only has {this_team.wallet} bucks, but " @@ -1189,7 +1157,6 @@ async def team_buy_packs( with db.atomic(): Pack.bulk_create(all_packs, batch_size=15) - db.close() raise HTTPException( status_code=200, @@ -1203,12 +1170,10 @@ async def team_sell_cards(team_id: int, ids: str, ts: str): try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if ts != this_team.team_hash(): logging.warning(f"Bad Team Secret: {ts} ({this_team.team_hash()})") - db.close() raise HTTPException( status_code=401, detail=f"You are not authorized to sell {this_team.abbrev} cards. This event has been logged.", @@ -1223,7 +1188,6 @@ async def team_sell_cards(team_id: int, ids: str, ts: str): try: this_card = Card.get_by_id(card_id) except Exception: - db.close() raise HTTPException( status_code=404, detail=f"No card found with id {card_id}" ) @@ -1292,11 +1256,9 @@ async def get_team_cards(team_id, csv: Optional[bool] = True): try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if not csv: - db.close() raise HTTPException( status_code=400, detail="The /teams/{team_id}/cards endpoint only supports csv output.", @@ -1310,11 +1272,9 @@ async def get_team_cards(team_id, csv: Optional[bool] = True): .order_by(-Card.player.rarity.value, Card.player.p_name) ) if all_cards.count() == 0: - db.close() raise HTTPException(status_code=404, detail=f"No cards found") card_vals = [model_to_dict(x) for x in all_cards] - db.close() for x in card_vals: x.update(x["player"]) @@ -1359,7 +1319,6 @@ async def get_team_cards(team_id, csv: Optional[bool] = True): async def post_team(team: TeamModel, 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 teams. This event has been logged.", @@ -1367,7 +1326,6 @@ async def post_team(team: TeamModel, token: str = Depends(oauth2_scheme)): dupe_team = Team.get_or_none(Team.season == team.season, Team.abbrev == team.abbrev) if dupe_team: - db.close() raise HTTPException( status_code=400, detail=f"There is already a season {team.season} team using {team.abbrev}", @@ -1395,7 +1353,6 @@ async def post_team(team: TeamModel, token: str = Depends(oauth2_scheme)): saved = this_team.save() if saved == 1: return_team = model_to_dict(this_team) - db.close() return return_team else: raise HTTPException( @@ -1408,7 +1365,6 @@ async def post_team(team: TeamModel, token: str = Depends(oauth2_scheme)): async def team_season_update(new_season: int, 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 teams. This event has been logged.", @@ -1420,7 +1376,6 @@ async def team_season_update(new_season: int, token: str = Depends(oauth2_scheme current = Current.latest() current.season = new_season current.save() - db.close() return { "detail": f"Team rankings, season, guides, and wallets updated for season {new_season}" @@ -1433,7 +1388,6 @@ async def team_update_money( ): if not valid_token(token): logging.warning(f"Bad Token: {token}") - db.close() raise HTTPException( status_code=401, detail="You are not authorized to adjust wallets. This event has been logged.", @@ -1442,14 +1396,12 @@ async def team_update_money( try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") this_team.wallet += delta if this_team.save() == 1: return_team = model_to_dict(this_team) - db.close() return return_team else: raise HTTPException( @@ -1481,7 +1433,6 @@ async def patch_team( ): if not valid_token(token): logging.warning(f"Bad Token: {token}") - db.close() raise HTTPException( status_code=401, detail="You are not authorized to delete teams. This event has been logged.", @@ -1489,7 +1440,6 @@ async def patch_team( try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if abbrev is not None: @@ -1533,7 +1483,6 @@ async def patch_team( if this_team.save() == 1: return_team = model_to_dict(this_team) - db.close() return return_team else: raise HTTPException( @@ -1546,7 +1495,6 @@ async def patch_team( async def delete_team(team_id, 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 delete teams. This event has been logged.", @@ -1554,11 +1502,9 @@ async def delete_team(team_id, token: str = Depends(oauth2_scheme)): try: this_team = Team.get_by_id(team_id) except Exception: - db.close() raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") count = this_team.delete_instance() - db.close() if count == 1: raise HTTPException(status_code=200, detail=f"Team {team_id} has been deleted") From bcdbf2add1b8efa8c5ab04b69632ce6be0ed9f4f Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 15:50:43 -0600 Subject: [PATCH 08/25] fix: remove unused imports in PR #33 files Removed 55 unused imports across 26 router files. Most were `db` imports left over after the db.close() removal in the previous commit, plus additional stale imports (scipy.stats, chunked, copy, base64, Html2Image, pandas.DataFrame, pydantic.validator, etc.) that were already unused. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/admin.py | 2 +- app/routers_v2/awards.py | 2 +- app/routers_v2/battingcardratings.py | 5 +---- app/routers_v2/battingcards.py | 2 +- app/routers_v2/cardpositions.py | 2 +- app/routers_v2/cardsets.py | 2 +- app/routers_v2/current.py | 2 +- app/routers_v2/decisions.py | 4 +--- app/routers_v2/events.py | 2 +- app/routers_v2/gamerewards.py | 2 +- app/routers_v2/gauntletrewards.py | 2 +- app/routers_v2/gauntletruns.py | 2 +- app/routers_v2/mlbplayers.py | 3 --- app/routers_v2/notifications.py | 2 +- app/routers_v2/packtypes.py | 2 +- app/routers_v2/paperdex.py | 2 +- app/routers_v2/pitchingcardratings.py | 3 +-- app/routers_v2/pitchingcards.py | 2 +- app/routers_v2/players.py | 3 --- app/routers_v2/rarity.py | 2 +- app/routers_v2/results.py | 2 +- app/routers_v2/rewards.py | 2 +- app/routers_v2/scouting.py | 10 +++------- app/routers_v2/stratgame.py | 5 ++--- app/routers_v2/stratplays.py | 2 -- app/routers_v2/teams.py | 1 - 26 files changed, 25 insertions(+), 45 deletions(-) diff --git a/app/routers_v2/admin.py b/app/routers_v2/admin.py index 350897b..d4819c9 100644 --- a/app/routers_v2/admin.py +++ b/app/routers_v2/admin.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends, HTTPException import logging -from ..db_engine import db, Player +from ..db_engine import Player from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA logging.basicConfig( diff --git a/app/routers_v2/awards.py b/app/routers_v2/awards.py index e871c17..6914bfe 100644 --- a/app/routers_v2/awards.py +++ b/app/routers_v2/awards.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Award, model_to_dict +from ..db_engine import Award, model_to_dict from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA logging.basicConfig( diff --git a/app/routers_v2/battingcardratings.py b/app/routers_v2/battingcardratings.py index 8e2d448..971cc43 100644 --- a/app/routers_v2/battingcardratings.py +++ b/app/routers_v2/battingcardratings.py @@ -2,8 +2,7 @@ import os from fastapi import APIRouter, Depends, HTTPException, Query, Response from fastapi.responses import FileResponse -from scipy import stats -from typing import Literal, Optional, List +from typing import Literal, List import logging import pandas as pd import pydantic @@ -13,10 +12,8 @@ from ..db_engine import ( db, BattingCardRatings, model_to_dict, - chunked, BattingCard, Player, - query_to_csv, Team, CardPosition, ) diff --git a/app/routers_v2/battingcards.py b/app/routers_v2/battingcards.py index 055738c..439db81 100644 --- a/app/routers_v2/battingcards.py +++ b/app/routers_v2/battingcards.py @@ -5,7 +5,7 @@ from typing import Literal, Optional, List import logging import pydantic -from ..db_engine import db, BattingCard, model_to_dict, fn, chunked, Player, MlbPlayer +from ..db_engine import db, BattingCard, model_to_dict, fn, Player, MlbPlayer from ..db_helpers import upsert_batting_cards from ..dependencies import oauth2_scheme, valid_token, LOG_DATA diff --git a/app/routers_v2/cardpositions.py b/app/routers_v2/cardpositions.py index 206c292..8e26ed8 100644 --- a/app/routers_v2/cardpositions.py +++ b/app/routers_v2/cardpositions.py @@ -4,7 +4,7 @@ import logging import pydantic from pydantic import root_validator -from ..db_engine import db, CardPosition, model_to_dict, chunked, Player, fn +from ..db_engine import db, CardPosition, model_to_dict, Player, fn from ..db_helpers import upsert_card_positions from ..dependencies import oauth2_scheme, valid_token, LOG_DATA diff --git a/app/routers_v2/cardsets.py b/app/routers_v2/cardsets.py index 6ca4a38..8052264 100644 --- a/app/routers_v2/cardsets.py +++ b/app/routers_v2/cardsets.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Cardset, model_to_dict, fn, Event +from ..db_engine import Cardset, model_to_dict, fn, Event from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/current.py b/app/routers_v2/current.py index dc5fd27..9a68029 100644 --- a/app/routers_v2/current.py +++ b/app/routers_v2/current.py @@ -4,7 +4,7 @@ from typing import Optional import logging import pydantic -from ..db_engine import db, Current, model_to_dict +from ..db_engine import Current, model_to_dict from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA logging.basicConfig( diff --git a/app/routers_v2/decisions.py b/app/routers_v2/decisions.py index 5c3d189..dc0d990 100644 --- a/app/routers_v2/decisions.py +++ b/app/routers_v2/decisions.py @@ -1,6 +1,5 @@ from fastapi import APIRouter, Depends, HTTPException, Query, Response -from typing import List, Optional, Literal -import copy +from typing import List, Optional import logging import pandas as pd import pydantic @@ -11,7 +10,6 @@ from ..db_engine import ( StratGame, Player, model_to_dict, - chunked, fn, Team, Card, diff --git a/app/routers_v2/events.py b/app/routers_v2/events.py index 3674426..6908170 100644 --- a/app/routers_v2/events.py +++ b/app/routers_v2/events.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Event, model_to_dict, fn +from ..db_engine import Event, model_to_dict, fn from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/gamerewards.py b/app/routers_v2/gamerewards.py index 20e6d68..c99377c 100644 --- a/app/routers_v2/gamerewards.py +++ b/app/routers_v2/gamerewards.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, GameRewards, model_to_dict +from ..db_engine import GameRewards, model_to_dict from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/gauntletrewards.py b/app/routers_v2/gauntletrewards.py index 9f76bf5..0540c87 100644 --- a/app/routers_v2/gauntletrewards.py +++ b/app/routers_v2/gauntletrewards.py @@ -3,7 +3,7 @@ from typing import Optional, List import logging import pydantic -from ..db_engine import db, GauntletReward, model_to_dict, chunked, DatabaseError +from ..db_engine import db, GauntletReward, model_to_dict, DatabaseError from ..db_helpers import upsert_gauntlet_rewards from ..dependencies import oauth2_scheme, valid_token, LOG_DATA diff --git a/app/routers_v2/gauntletruns.py b/app/routers_v2/gauntletruns.py index f664319..8ceac15 100644 --- a/app/routers_v2/gauntletruns.py +++ b/app/routers_v2/gauntletruns.py @@ -4,7 +4,7 @@ from typing import Optional import logging import pydantic -from ..db_engine import db, GauntletRun, model_to_dict, DatabaseError +from ..db_engine import GauntletRun, model_to_dict, DatabaseError from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/mlbplayers.py b/app/routers_v2/mlbplayers.py index 0737f30..bbe0c9f 100644 --- a/app/routers_v2/mlbplayers.py +++ b/app/routers_v2/mlbplayers.py @@ -5,17 +5,14 @@ from fastapi import APIRouter, Depends, HTTPException, Response, Query from typing import Optional, List import logging import pydantic -from pandas import DataFrame from ..db_engine import ( db, MlbPlayer, Player, BattingCard, - PitchingCard, model_to_dict, fn, - chunked, query_to_csv, ) from ..db_helpers import upsert_mlb_players diff --git a/app/routers_v2/notifications.py b/app/routers_v2/notifications.py index 233d5aa..7357b99 100644 --- a/app/routers_v2/notifications.py +++ b/app/routers_v2/notifications.py @@ -5,7 +5,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Notification, model_to_dict, fn +from ..db_engine import Notification, model_to_dict, fn from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/packtypes.py b/app/routers_v2/packtypes.py index df3f7d7..fb08c3a 100644 --- a/app/routers_v2/packtypes.py +++ b/app/routers_v2/packtypes.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, PackType, model_to_dict, fn +from ..db_engine import PackType, model_to_dict, fn from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/paperdex.py b/app/routers_v2/paperdex.py index 4df6258..08264e9 100644 --- a/app/routers_v2/paperdex.py +++ b/app/routers_v2/paperdex.py @@ -5,7 +5,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Paperdex, model_to_dict, Player, Cardset, Team +from ..db_engine import Paperdex, model_to_dict, Player, Cardset, Team from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/pitchingcardratings.py b/app/routers_v2/pitchingcardratings.py index fea857e..e1df819 100644 --- a/app/routers_v2/pitchingcardratings.py +++ b/app/routers_v2/pitchingcardratings.py @@ -2,7 +2,7 @@ import os from fastapi import APIRouter, Depends, HTTPException, Query, Response from fastapi.responses import FileResponse -from typing import Literal, Optional, List +from typing import Literal, List import logging import pandas as pd import pydantic @@ -12,7 +12,6 @@ from ..db_engine import ( db, PitchingCardRatings, model_to_dict, - chunked, PitchingCard, Player, query_to_csv, diff --git a/app/routers_v2/pitchingcards.py b/app/routers_v2/pitchingcards.py index 43e238c..ef00340 100644 --- a/app/routers_v2/pitchingcards.py +++ b/app/routers_v2/pitchingcards.py @@ -5,7 +5,7 @@ from typing import Literal, Optional, List import logging import pydantic -from ..db_engine import db, PitchingCard, model_to_dict, chunked, Player, fn, MlbPlayer +from ..db_engine import db, PitchingCard, model_to_dict, Player, fn, MlbPlayer from ..db_helpers import upsert_pitching_cards from ..dependencies import oauth2_scheme, valid_token, LOG_DATA diff --git a/app/routers_v2/players.py b/app/routers_v2/players.py index bd0cba0..4c38e73 100644 --- a/app/routers_v2/players.py +++ b/app/routers_v2/players.py @@ -1,12 +1,10 @@ import datetime import os.path -import base64 import pandas as pd from fastapi import APIRouter, Depends, HTTPException, Request, Response, Query from fastapi.responses import FileResponse from fastapi.templating import Jinja2Templates -from html2image import Html2Image from typing import Optional, List, Literal import logging import pydantic @@ -19,7 +17,6 @@ from ..db_engine import ( Player, model_to_dict, fn, - chunked, Paperdex, Cardset, Rarity, diff --git a/app/routers_v2/rarity.py b/app/routers_v2/rarity.py index 7635f78..e37156e 100644 --- a/app/routers_v2/rarity.py +++ b/app/routers_v2/rarity.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Rarity, model_to_dict, fn +from ..db_engine import Rarity, model_to_dict, fn from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/results.py b/app/routers_v2/results.py index 0d54cd3..c706a41 100644 --- a/app/routers_v2/results.py +++ b/app/routers_v2/results.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Result, model_to_dict, Team, DataError +from ..db_engine import Result, model_to_dict, Team, DataError from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/rewards.py b/app/routers_v2/rewards.py index 81395b1..86f46ff 100644 --- a/app/routers_v2/rewards.py +++ b/app/routers_v2/rewards.py @@ -5,7 +5,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Reward, model_to_dict, fn +from ..db_engine import Reward, model_to_dict, fn from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/scouting.py b/app/routers_v2/scouting.py index 78464f4..818983c 100644 --- a/app/routers_v2/scouting.py +++ b/app/routers_v2/scouting.py @@ -1,13 +1,9 @@ -import csv -from datetime import datetime -from fastapi import APIRouter, Depends, HTTPException, Response, Query -from typing import Optional +from fastapi import APIRouter, Depends, HTTPException, Query import logging import pydantic -import pandas as pd -from ..db_engine import db, model_to_dict, fn, query_to_csv, complex_data_to_csv, Player, BattingCardRatings -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, int_timestamp +from ..db_engine import Player +from ..dependencies import oauth2_scheme, valid_token, LOG_DATA from ..player_scouting import get_player_ids logging.basicConfig( diff --git a/app/routers_v2/stratgame.py b/app/routers_v2/stratgame.py index c2b7ac2..eb18b9e 100644 --- a/app/routers_v2/stratgame.py +++ b/app/routers_v2/stratgame.py @@ -1,11 +1,10 @@ from fastapi import APIRouter, Depends, HTTPException, Query, Response -from typing import Literal, Optional, List +from typing import Optional, List import logging import pandas as pd import pydantic -from pydantic import validator -from ..db_engine import db, StratGame, model_to_dict, chunked, PitchingCard, Player, query_to_csv, Team, fn +from ..db_engine import StratGame, model_to_dict, fn from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( diff --git a/app/routers_v2/stratplays.py b/app/routers_v2/stratplays.py index 0b15eee..41b1830 100644 --- a/app/routers_v2/stratplays.py +++ b/app/routers_v2/stratplays.py @@ -13,11 +13,9 @@ from ..db_engine import ( Team, Player, model_to_dict, - chunked, fn, SQL, Case, - complex_data_to_csv, Decision, ) from ..db_helpers import upsert_strat_plays diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index 29cb690..c6b27ca 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -36,7 +36,6 @@ from ..dependencies import ( oauth2_scheme, valid_token, LOG_DATA, - int_timestamp, PRIVATE_IN_SCHEMA, ) From 86b4338b6665408243ceceb76372e17b853afbe7 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 16:01:52 -0600 Subject: [PATCH 09/25] fix: remove duplicate ranking_max filter in get_teams (#21) Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/teams.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index c6b27ca..8347724 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -138,9 +138,6 @@ async def get_teams( if ranking_max is not None: all_teams = all_teams.where(Team.ranking <= ranking_max) - if ranking_max is not None: - all_teams = all_teams.where(Team.ranking <= ranking_max) - if has_guide is not None: # Use boolean comparison (PostgreSQL-compatible) if not has_guide: From 5f86c8cb207ecd74bb836b83d8307ee10f076261 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 16:32:41 -0600 Subject: [PATCH 10/25] fix: add type annotations to untyped path parameters (#27) Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/packs.py | 2 +- app/routers_v2/players.py | 4 ++-- app/routers_v2/teams.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/routers_v2/packs.py b/app/routers_v2/packs.py index ba4ada0..19ab178 100644 --- a/app/routers_v2/packs.py +++ b/app/routers_v2/packs.py @@ -109,7 +109,7 @@ async def get_packs( @router.get('/{pack_id}') -async def get_one_pack(pack_id, csv: Optional[bool] = False): +async def get_one_pack(pack_id: int, csv: Optional[bool] = False): try: this_pack = Pack.get_by_id(pack_id) except Exception: diff --git a/app/routers_v2/players.py b/app/routers_v2/players.py index 4c38e73..343ff58 100644 --- a/app/routers_v2/players.py +++ b/app/routers_v2/players.py @@ -587,7 +587,7 @@ async def search_players( @router.get("/{player_id}") -async def get_one_player(player_id, csv: Optional[bool] = False): +async def get_one_player(player_id: int, csv: Optional[bool] = False): try: this_player = Player.get_by_id(player_id) except Exception: @@ -1119,7 +1119,7 @@ async def post_image_reset( @router.delete("/{player_id}") -async def delete_player(player_id, token: str = Depends(oauth2_scheme)): +async def delete_player(player_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): logging.warning(f"Bad Token: {token}") raise HTTPException( diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index 8347724..35ee7d9 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -173,7 +173,7 @@ async def get_teams( @router.get("/{team_id}") -async def get_one_team(team_id, inc_packs: bool = True, csv: Optional[bool] = False): +async def get_one_team(team_id: int, inc_packs: bool = True, csv: Optional[bool] = False): try: this_team = Team.get_by_id(team_id) except Exception: From ae8c20ea1ce04d3a21eb7020c0ba5bc15faf248f Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 17:03:19 -0600 Subject: [PATCH 11/25] fix: batch-fetch PitchingCardRatings instead of per-row queries (#19) Replace two get_or_none calls per row in sort_pitchers and sort_starters with a single batched SELECT for all card IDs, reducing N*2 queries to 1. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/teams.py | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index 35ee7d9..5a93553 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -279,7 +279,6 @@ def get_scouting_dfs(allowed_players, position: str): ) ) - def get_total_ops(df_data): ops_vl = df_data["obp_vl"] + df_data["slg_vl"] ops_vr = df_data["obp_vr"] + df_data["slg_vr"] @@ -587,15 +586,18 @@ def sort_pitchers(pitching_card_query) -> DataFrame | None: pitcher_df = pd.DataFrame(all_s).set_index("player", drop=False) logging.debug(f"pitcher_df: {pitcher_df}") + card_ids = pitcher_df["id"].tolist() + ratings_map = { + (r.pitchingcard_id, r.vs_hand): r + for r in PitchingCardRatings.select().where( + (PitchingCardRatings.pitchingcard_id << card_ids) + & (PitchingCardRatings.vs_hand << ["L", "R"]) + ) + } + def get_total_ops(df_data): - vlval = PitchingCardRatings.get_or_none( - PitchingCardRatings.pitchingcard_id == df_data["id"], - PitchingCardRatings.vs_hand == "L", - ) - vrval = PitchingCardRatings.get_or_none( - PitchingCardRatings.pitchingcard_id == df_data["id"], - PitchingCardRatings.vs_hand == "R", - ) + vlval = ratings_map.get((df_data["id"], "L")) + vrval = ratings_map.get((df_data["id"], "R")) ops_vl = vlval.obp + vlval.slg ops_vr = vrval.obp + vrval.slg @@ -664,15 +666,18 @@ async def get_team_sp( starter_df = pd.DataFrame(all_s).set_index("player", drop=False) logging.debug(f"starter_df: {starter_df}") + card_ids = starter_df["id"].tolist() + ratings_map = { + (r.pitchingcard_id, r.vs_hand): r + for r in PitchingCardRatings.select().where( + (PitchingCardRatings.pitchingcard_id << card_ids) + & (PitchingCardRatings.vs_hand << ["L", "R"]) + ) + } + def get_total_ops(df_data): - vlval = PitchingCardRatings.get_or_none( - PitchingCardRatings.pitchingcard_id == df_data["id"], - PitchingCardRatings.vs_hand == "L", - ) - vrval = PitchingCardRatings.get_or_none( - PitchingCardRatings.pitchingcard_id == df_data["id"], - PitchingCardRatings.vs_hand == "R", - ) + vlval = ratings_map.get((df_data["id"], "L")) + vrval = ratings_map.get((df_data["id"], "R")) ops_vl = vlval.obp + vlval.slg ops_vr = vrval.obp + vrval.slg From 053fcbab0557519e54eb578d09c68341223b10f9 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 18:06:35 -0600 Subject: [PATCH 12/25] =?UTF-8?q?fix:=20centralize=20logging=20config=20in?= =?UTF-8?q?=20main.py=20=E2=80=94=20remove=20basicConfig=20from=2032=20fil?= =?UTF-8?q?es=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved logging.basicConfig() to app/main.py as the single source of truth. Removed duplicate (no-op) calls from app/db_engine.py, app/dependencies.py, and all 30 router files in app/routers_v2/. Removed the now-unused LOG_DATA dict and date/log_level locals from dependencies.py and db_engine.py. Co-Authored-By: Claude Sonnet 4.6 --- app/db_engine.py | 16 +++++++--------- app/dependencies.py | 15 --------------- app/main.py | 11 +++++++++++ app/routers_v2/admin.py | 7 +------ app/routers_v2/awards.py | 7 +------ app/routers_v2/batstats.py | 7 +------ app/routers_v2/battingcardratings.py | 7 +------ app/routers_v2/battingcards.py | 7 +------ app/routers_v2/cardpositions.py | 7 +------ app/routers_v2/cards.py | 7 +------ app/routers_v2/cardsets.py | 7 +------ app/routers_v2/current.py | 7 +------ app/routers_v2/decisions.py | 7 +------ app/routers_v2/events.py | 7 +------ app/routers_v2/gamerewards.py | 7 +------ app/routers_v2/gauntletrewards.py | 7 +------ app/routers_v2/gauntletruns.py | 7 +------ app/routers_v2/mlbplayers.py | 7 +------ app/routers_v2/notifications.py | 7 +------ app/routers_v2/packs.py | 7 +------ app/routers_v2/packtypes.py | 7 +------ app/routers_v2/paperdex.py | 7 +------ app/routers_v2/pitchingcardratings.py | 7 +------ app/routers_v2/pitchingcards.py | 7 +------ app/routers_v2/pitstats.py | 7 +------ app/routers_v2/players.py | 7 +------ app/routers_v2/rarity.py | 7 +------ app/routers_v2/results.py | 7 +------ app/routers_v2/rewards.py | 7 +------ app/routers_v2/scouting.py | 7 +------ app/routers_v2/stratgame.py | 7 +------ app/routers_v2/stratplays.py | 7 +------ app/routers_v2/teams.py | 6 ------ 33 files changed, 47 insertions(+), 204 deletions(-) diff --git a/app/db_engine.py b/app/db_engine.py index b7849b0..9cf697b 100644 --- a/app/db_engine.py +++ b/app/db_engine.py @@ -36,14 +36,6 @@ else: pragmas={"journal_mode": "wal", "cache_size": -1 * 64000, "synchronous": 0}, ) -date = f"{datetime.now().year}-{datetime.now().month}-{datetime.now().day}" -log_level = logging.INFO if os.environ.get("LOG_LEVEL") == "INFO" else "WARN" -logging.basicConfig( - filename=f"logs/database/{date}.log", - format="%(asctime)s - database - %(levelname)s - %(message)s", - level=log_level, -) - # 2025, 2005 ranked_cardsets = [24, 25, 26, 27, 28, 29] LIVE_CARDSET_ID = 27 @@ -925,7 +917,13 @@ CardPosition.add_index(pos_index) if not SKIP_TABLE_CREATION: db.create_tables( - [BattingCard, BattingCardRatings, PitchingCard, PitchingCardRatings, CardPosition], + [ + BattingCard, + BattingCardRatings, + PitchingCard, + PitchingCardRatings, + CardPosition, + ], safe=True, ) diff --git a/app/dependencies.py b/app/dependencies.py index 7120c59..40902d8 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -5,21 +5,6 @@ import os import requests from fastapi.security import OAuth2PasswordBearer -date = f"{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}" -LOG_DATA = { - "filename": f"logs/database/{date}.log", - "format": "%(asctime)s - database - %(levelname)s - %(message)s", - "log_level": logging.INFO if os.environ.get("LOG_LEVEL") == "INFO" else "WARN", -} - - -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) - - oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") master_debug = False DB_URL = "https://pd.manticorum.com/api/" diff --git a/app/main.py b/app/main.py index ddcafe3..55d7233 100644 --- a/app/main.py +++ b/app/main.py @@ -1,7 +1,18 @@ +import logging +import os +from datetime import datetime + from fastapi import FastAPI, Request from fastapi.openapi.docs import get_swagger_ui_html from fastapi.openapi.utils import get_openapi +_log_date = f"{datetime.now().year}-{datetime.now().month}-{datetime.now().day}" +logging.basicConfig( + filename=f"logs/database/{_log_date}.log", + format="%(asctime)s - database - %(levelname)s - %(message)s", + level=logging.INFO if os.environ.get("LOG_LEVEL") == "INFO" else logging.WARNING, +) + # from fastapi.staticfiles import StaticFiles # from fastapi.templating import Jinja2Templates diff --git a/app/routers_v2/admin.py b/app/routers_v2/admin.py index d4819c9..552de3c 100644 --- a/app/routers_v2/admin.py +++ b/app/routers_v2/admin.py @@ -2,13 +2,8 @@ from fastapi import APIRouter, Depends, HTTPException import logging from ..db_engine import Player -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA +from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/admin', diff --git a/app/routers_v2/awards.py b/app/routers_v2/awards.py index 6914bfe..10d5f5d 100644 --- a/app/routers_v2/awards.py +++ b/app/routers_v2/awards.py @@ -5,13 +5,8 @@ import pydantic from pandas import DataFrame from ..db_engine import Award, model_to_dict -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA +from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/awards', diff --git a/app/routers_v2/batstats.py b/app/routers_v2/batstats.py index 909c9bf..20514ee 100644 --- a/app/routers_v2/batstats.py +++ b/app/routers_v2/batstats.py @@ -7,13 +7,8 @@ import pydantic from pandas import DataFrame from ..db_engine import db, BattingStat, model_to_dict, fn, Card, Player, Current -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA +from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/batstats', diff --git a/app/routers_v2/battingcardratings.py b/app/routers_v2/battingcardratings.py index 971cc43..c0d5364 100644 --- a/app/routers_v2/battingcardratings.py +++ b/app/routers_v2/battingcardratings.py @@ -18,13 +18,8 @@ from ..db_engine import ( CardPosition, ) from ..db_helpers import upsert_batting_card_ratings -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA +from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/battingcardratings", tags=["battingcardratings"]) RATINGS_FILE = "storage/batting-ratings.csv" diff --git a/app/routers_v2/battingcards.py b/app/routers_v2/battingcards.py index 439db81..8359daa 100644 --- a/app/routers_v2/battingcards.py +++ b/app/routers_v2/battingcards.py @@ -7,13 +7,8 @@ import pydantic from ..db_engine import db, BattingCard, model_to_dict, fn, Player, MlbPlayer from ..db_helpers import upsert_batting_cards -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/battingcards", tags=["battingcards"]) diff --git a/app/routers_v2/cardpositions.py b/app/routers_v2/cardpositions.py index 8e26ed8..1b08926 100644 --- a/app/routers_v2/cardpositions.py +++ b/app/routers_v2/cardpositions.py @@ -6,13 +6,8 @@ from pydantic import root_validator from ..db_engine import db, CardPosition, model_to_dict, Player, fn from ..db_helpers import upsert_card_positions -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/cardpositions", tags=["cardpositions"]) diff --git a/app/routers_v2/cards.py b/app/routers_v2/cards.py index ca294ff..5a7a52a 100644 --- a/app/routers_v2/cards.py +++ b/app/routers_v2/cards.py @@ -5,13 +5,8 @@ import pydantic from pandas import DataFrame from ..db_engine import db, Card, model_to_dict, Team, Player, Pack, Paperdex, CARDSETS -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/cards', diff --git a/app/routers_v2/cardsets.py b/app/routers_v2/cardsets.py index 8052264..ed1db28 100644 --- a/app/routers_v2/cardsets.py +++ b/app/routers_v2/cardsets.py @@ -5,13 +5,8 @@ import pydantic from pandas import DataFrame from ..db_engine import Cardset, model_to_dict, fn, Event -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/cardsets', diff --git a/app/routers_v2/current.py b/app/routers_v2/current.py index 9a68029..186eda5 100644 --- a/app/routers_v2/current.py +++ b/app/routers_v2/current.py @@ -5,13 +5,8 @@ import logging import pydantic from ..db_engine import Current, model_to_dict -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA +from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/current', diff --git a/app/routers_v2/decisions.py b/app/routers_v2/decisions.py index dc0d990..f0f1030 100644 --- a/app/routers_v2/decisions.py +++ b/app/routers_v2/decisions.py @@ -16,13 +16,8 @@ from ..db_engine import ( StratPlay, ) from ..db_helpers import upsert_decisions -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/decisions", tags=["decisions"]) diff --git a/app/routers_v2/events.py b/app/routers_v2/events.py index 6908170..076c943 100644 --- a/app/routers_v2/events.py +++ b/app/routers_v2/events.py @@ -5,13 +5,8 @@ import pydantic from pandas import DataFrame from ..db_engine import Event, model_to_dict, fn -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/events', diff --git a/app/routers_v2/gamerewards.py b/app/routers_v2/gamerewards.py index c99377c..3d17e51 100644 --- a/app/routers_v2/gamerewards.py +++ b/app/routers_v2/gamerewards.py @@ -5,13 +5,8 @@ import pydantic from pandas import DataFrame from ..db_engine import GameRewards, model_to_dict -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/gamerewards', diff --git a/app/routers_v2/gauntletrewards.py b/app/routers_v2/gauntletrewards.py index 0540c87..308c52d 100644 --- a/app/routers_v2/gauntletrewards.py +++ b/app/routers_v2/gauntletrewards.py @@ -5,13 +5,8 @@ import pydantic from ..db_engine import db, GauntletReward, model_to_dict, DatabaseError from ..db_helpers import upsert_gauntlet_rewards -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/gauntletrewards", tags=["gauntletrewards"]) diff --git a/app/routers_v2/gauntletruns.py b/app/routers_v2/gauntletruns.py index 8ceac15..c8ff43d 100644 --- a/app/routers_v2/gauntletruns.py +++ b/app/routers_v2/gauntletruns.py @@ -5,13 +5,8 @@ import logging import pydantic from ..db_engine import GauntletRun, model_to_dict, DatabaseError -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/gauntletruns', diff --git a/app/routers_v2/mlbplayers.py b/app/routers_v2/mlbplayers.py index bbe0c9f..c8499c6 100644 --- a/app/routers_v2/mlbplayers.py +++ b/app/routers_v2/mlbplayers.py @@ -16,13 +16,8 @@ from ..db_engine import ( query_to_csv, ) from ..db_helpers import upsert_mlb_players -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/mlbplayers", tags=["mlbplayers"]) diff --git a/app/routers_v2/notifications.py b/app/routers_v2/notifications.py index 7357b99..57d36c8 100644 --- a/app/routers_v2/notifications.py +++ b/app/routers_v2/notifications.py @@ -6,13 +6,8 @@ import pydantic from pandas import DataFrame from ..db_engine import Notification, model_to_dict, fn -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/notifs', diff --git a/app/routers_v2/packs.py b/app/routers_v2/packs.py index 19ab178..a347c97 100644 --- a/app/routers_v2/packs.py +++ b/app/routers_v2/packs.py @@ -7,13 +7,8 @@ import pydantic from pandas import DataFrame from ..db_engine import db, Cardset, model_to_dict, Pack, Team, PackType -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/packs', diff --git a/app/routers_v2/packtypes.py b/app/routers_v2/packtypes.py index fb08c3a..fedbce7 100644 --- a/app/routers_v2/packtypes.py +++ b/app/routers_v2/packtypes.py @@ -5,13 +5,8 @@ import pydantic from pandas import DataFrame from ..db_engine import PackType, model_to_dict, fn -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/packtypes', diff --git a/app/routers_v2/paperdex.py b/app/routers_v2/paperdex.py index 08264e9..d04c084 100644 --- a/app/routers_v2/paperdex.py +++ b/app/routers_v2/paperdex.py @@ -6,13 +6,8 @@ import pydantic from pandas import DataFrame from ..db_engine import Paperdex, model_to_dict, Player, Cardset, Team -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/paperdex', diff --git a/app/routers_v2/pitchingcardratings.py b/app/routers_v2/pitchingcardratings.py index e1df819..90a18c6 100644 --- a/app/routers_v2/pitchingcardratings.py +++ b/app/routers_v2/pitchingcardratings.py @@ -19,13 +19,8 @@ from ..db_engine import ( CardPosition, ) from ..db_helpers import upsert_pitching_card_ratings -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/pitchingcardratings", tags=["pitchingcardratings"]) RATINGS_FILE = "storage/pitching-ratings.csv" diff --git a/app/routers_v2/pitchingcards.py b/app/routers_v2/pitchingcards.py index ef00340..d40265a 100644 --- a/app/routers_v2/pitchingcards.py +++ b/app/routers_v2/pitchingcards.py @@ -7,13 +7,8 @@ import pydantic from ..db_engine import db, PitchingCard, model_to_dict, Player, fn, MlbPlayer from ..db_helpers import upsert_pitching_cards -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/pitchingcards", tags=["pitchingcards"]) diff --git a/app/routers_v2/pitstats.py b/app/routers_v2/pitstats.py index 5219ca0..f30d71a 100644 --- a/app/routers_v2/pitstats.py +++ b/app/routers_v2/pitstats.py @@ -6,13 +6,8 @@ import pydantic from pandas import DataFrame from ..db_engine import db, PitchingStat, model_to_dict, Card, Player, Current -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/pitstats', diff --git a/app/routers_v2/players.py b/app/routers_v2/players.py index 343ff58..43ccf9f 100644 --- a/app/routers_v2/players.py +++ b/app/routers_v2/players.py @@ -28,7 +28,7 @@ from ..db_engine import ( MlbPlayer, ) from ..db_helpers import upsert_players -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token # Franchise normalization: Convert city+team names to city-agnostic team names # This enables cross-era player matching (e.g., 'Oakland Athletics' -> 'Athletics') @@ -73,11 +73,6 @@ def normalize_franchise(franchise: str) -> str: return FRANCHISE_NORMALIZE.get(titled, titled) -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/players", tags=["players"]) diff --git a/app/routers_v2/rarity.py b/app/routers_v2/rarity.py index e37156e..dd771b9 100644 --- a/app/routers_v2/rarity.py +++ b/app/routers_v2/rarity.py @@ -5,13 +5,8 @@ import pydantic from pandas import DataFrame from ..db_engine import Rarity, model_to_dict, fn -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/rarities', diff --git a/app/routers_v2/results.py b/app/routers_v2/results.py index c706a41..295a8ac 100644 --- a/app/routers_v2/results.py +++ b/app/routers_v2/results.py @@ -5,13 +5,8 @@ import pydantic from pandas import DataFrame from ..db_engine import Result, model_to_dict, Team, DataError -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/results', diff --git a/app/routers_v2/rewards.py b/app/routers_v2/rewards.py index 86f46ff..54ddc05 100644 --- a/app/routers_v2/rewards.py +++ b/app/routers_v2/rewards.py @@ -6,13 +6,8 @@ import pydantic from pandas import DataFrame from ..db_engine import Reward, model_to_dict, fn -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/rewards', diff --git a/app/routers_v2/scouting.py b/app/routers_v2/scouting.py index 818983c..2cd9c49 100644 --- a/app/routers_v2/scouting.py +++ b/app/routers_v2/scouting.py @@ -3,14 +3,9 @@ import logging import pydantic from ..db_engine import Player -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token from ..player_scouting import get_player_ids -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/scouting', diff --git a/app/routers_v2/stratgame.py b/app/routers_v2/stratgame.py index eb18b9e..3fb32b8 100644 --- a/app/routers_v2/stratgame.py +++ b/app/routers_v2/stratgame.py @@ -5,13 +5,8 @@ import pandas as pd import pydantic from ..db_engine import StratGame, model_to_dict, fn -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA['filename'], - format=LOG_DATA['format'], - level=LOG_DATA['log_level'] -) router = APIRouter( prefix='/api/v2/games', diff --git a/app/routers_v2/stratplays.py b/app/routers_v2/stratplays.py index 41b1830..b4bd7cc 100644 --- a/app/routers_v2/stratplays.py +++ b/app/routers_v2/stratplays.py @@ -19,13 +19,8 @@ from ..db_engine import ( Decision, ) from ..db_helpers import upsert_strat_plays -from ..dependencies import oauth2_scheme, valid_token, LOG_DATA +from ..dependencies import oauth2_scheme, valid_token -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/plays", tags=["plays"]) diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index 5a93553..5320f3a 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -35,15 +35,9 @@ from ..db_engine import ( from ..dependencies import ( oauth2_scheme, valid_token, - LOG_DATA, PRIVATE_IN_SCHEMA, ) -logging.basicConfig( - filename=LOG_DATA["filename"], - format=LOG_DATA["format"], - level=LOG_DATA["log_level"], -) router = APIRouter(prefix="/api/v2/teams", tags=["teams"]) From 870c753bf79444089a342d67084a0fe038cf7305 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 19:32:05 -0600 Subject: [PATCH 13/25] fix: document SQLite synchronous=0 pragma in db_engine.py (#20) Add explanatory comment clarifying that synchronous=OFF is a dev-only trade-off (production uses PostgreSQL), and describing the crash-corruption risk and how WAL mode partially mitigates it. Co-Authored-By: Claude Sonnet 4.6 --- app/db_engine.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/db_engine.py b/app/db_engine.py index 9cf697b..290b160 100644 --- a/app/db_engine.py +++ b/app/db_engine.py @@ -30,7 +30,15 @@ if DATABASE_TYPE.lower() == "postgresql": autorollback=True, # Automatically rollback failed transactions ) else: - # Default SQLite configuration for local development + # SQLite configuration for local development only. + # Production always uses PostgreSQL (see DATABASE_TYPE env var). + # + # synchronous=0 (OFF): SQLite skips fsync() after every write, maximising + # throughput at the cost of durability — a hard crash could corrupt the DB. + # This is an acceptable trade-off in dev where data loss is tolerable and + # write speed matters. WAL journal mode reduces (but does not eliminate) + # the corruption window by keeping the main database file consistent while + # writes land in the WAL file first. db = SqliteDatabase( "storage/pd_master.db", pragmas={"journal_mode": "wal", "cache_size": -1 * 64000, "synchronous": 0}, From 0948db0b491762b3350d749ca0a344434c7c2f1d Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 20:03:50 -0600 Subject: [PATCH 14/25] fix: guard against None rating objects in pitcher sorting functions (#13) Add None checks for vlval/vrval in get_total_ops inside sort_pitchers() and sort_starters(). Returns float("inf") when ratings are missing so pitchers without ratings sort to the end rather than raising AttributeError. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/teams.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index 5320f3a..a6dc46c 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -593,6 +593,8 @@ def sort_pitchers(pitching_card_query) -> DataFrame | None: vlval = ratings_map.get((df_data["id"], "L")) vrval = ratings_map.get((df_data["id"], "R")) + if vlval is None or vrval is None: + return float("inf") ops_vl = vlval.obp + vlval.slg ops_vr = vrval.obp + vrval.slg # TODO: should this be max?? @@ -673,6 +675,8 @@ async def get_team_sp( vlval = ratings_map.get((df_data["id"], "L")) vrval = ratings_map.get((df_data["id"], "R")) + if vlval is None or vrval is None: + return float("inf") ops_vl = vlval.obp + vlval.slg ops_vr = vrval.obp + vrval.slg return (ops_vr + ops_vl + min(ops_vl, ops_vr)) / 3 From 0166c7dda40e71ee46ac6c66f73441c6cca1f691 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 20:31:40 -0600 Subject: [PATCH 15/25] fix: compute CSV after appending data row in get_one_player (#12) return_val was assigned from DataFrame(data_list).to_csv() before the player data row was appended to data_list, so the CSV response contained only the header row. Moved the to_csv() call to after the append. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/players.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routers_v2/players.py b/app/routers_v2/players.py index 43ccf9f..d79daf7 100644 --- a/app/routers_v2/players.py +++ b/app/routers_v2/players.py @@ -617,7 +617,6 @@ async def get_one_player(player_id: int, csv: Optional[bool] = False): "description", ] ] - return_val = DataFrame(data_list).to_csv(header=False, index=False) data_list.append( [ this_player.id, @@ -644,6 +643,7 @@ async def get_one_player(player_id: int, csv: Optional[bool] = False): this_player.description, ] ) + return_val = DataFrame(data_list).to_csv(header=False, index=False) return Response(content=return_val, media_type="text/csv") else: From f1d289a0e966aa3073fe643052691dd5a92139d2 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 21:02:47 -0600 Subject: [PATCH 16/25] fix: consolidate redundant double-query in get_one_play (#14) Reuse the result of get_or_none instead of discarding it and calling get_by_id again, eliminating one unnecessary round-trip per request. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/stratplays.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/routers_v2/stratplays.py b/app/routers_v2/stratplays.py index b4bd7cc..2fc8150 100644 --- a/app/routers_v2/stratplays.py +++ b/app/routers_v2/stratplays.py @@ -1390,10 +1390,10 @@ async def get_game_summary( @router.get("/{play_id}") async def get_one_play(play_id: int): - if StratPlay.get_or_none(StratPlay.id == play_id) is None: + play = StratPlay.get_or_none(StratPlay.id == play_id) + if play is None: raise HTTPException(status_code=404, detail=f"Play ID {play_id} not found") - r_play = model_to_dict(StratPlay.get_by_id(play_id)) - return r_play + return model_to_dict(play) @router.patch("/{play_id}") From 35389cac2427d1b439255f56f6ca37c0921815cd Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 23:02:11 -0600 Subject: [PATCH 17/25] fix: remove plaintext bearer token from warning logs (#7) Replace all logging.warning(f'Bad Token: {token}') calls with logging.warning('Bad Token: [REDACTED]') across 30 router files. Full bearer tokens were being written to log files on auth failures. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/admin.py | 2 +- app/routers_v2/awards.py | 4 ++-- app/routers_v2/batstats.py | 4 ++-- app/routers_v2/battingcardratings.py | 14 +++++++------- app/routers_v2/battingcards.py | 8 ++++---- app/routers_v2/cardpositions.py | 4 ++-- app/routers_v2/cards.py | 16 ++++++++-------- app/routers_v2/cardsets.py | 6 +++--- app/routers_v2/current.py | 6 +++--- app/routers_v2/decisions.py | 8 ++++---- app/routers_v2/events.py | 6 +++--- app/routers_v2/gamerewards.py | 6 +++--- app/routers_v2/gauntletrewards.py | 4 ++-- app/routers_v2/gauntletruns.py | 4 ++-- app/routers_v2/mlbplayers.py | 14 +++++++------- app/routers_v2/notifications.py | 6 +++--- app/routers_v2/packs.py | 8 ++++---- app/routers_v2/packtypes.py | 6 +++--- app/routers_v2/paperdex.py | 8 ++++---- app/routers_v2/pitchingcardratings.py | 12 ++++++------ app/routers_v2/pitchingcards.py | 8 ++++---- app/routers_v2/pitstats.py | 4 ++-- app/routers_v2/players.py | 10 +++++----- app/routers_v2/rarity.py | 6 +++--- app/routers_v2/results.py | 6 +++--- app/routers_v2/rewards.py | 6 +++--- app/routers_v2/scouting.py | 4 ++-- app/routers_v2/stratgame.py | 6 +++--- app/routers_v2/stratplays.py | 8 ++++---- app/routers_v2/teams.py | 10 +++++----- 30 files changed, 107 insertions(+), 107 deletions(-) diff --git a/app/routers_v2/admin.py b/app/routers_v2/admin.py index 552de3c..13238a9 100644 --- a/app/routers_v2/admin.py +++ b/app/routers_v2/admin.py @@ -14,7 +14,7 @@ router = APIRouter( @router.post('/stl-fix', include_in_schema=PRIVATE_IN_SCHEMA) async def stl_cardinals_fix(token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post. This event has been logged.' diff --git a/app/routers_v2/awards.py b/app/routers_v2/awards.py index 10d5f5d..30cfbe2 100644 --- a/app/routers_v2/awards.py +++ b/app/routers_v2/awards.py @@ -94,7 +94,7 @@ async def get_one_award(award_id, csv: Optional[bool] = None): @router.post('', include_in_schema=PRIVATE_IN_SCHEMA) async def post_awards(award: AwardModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post awards. This event has been logged.' @@ -123,7 +123,7 @@ async def post_awards(award: AwardModel, token: str = Depends(oauth2_scheme)): @router.delete('/{award_id}', include_in_schema=PRIVATE_IN_SCHEMA) async def delete_award(award_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete awards. This event has been logged.' diff --git a/app/routers_v2/batstats.py b/app/routers_v2/batstats.py index 20514ee..d0dfd05 100644 --- a/app/routers_v2/batstats.py +++ b/app/routers_v2/batstats.py @@ -173,7 +173,7 @@ async def get_player_stats( @router.post('', include_in_schema=PRIVATE_IN_SCHEMA) async def post_batstats(stats: BattingStatModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post stats. This event has been logged.' @@ -229,7 +229,7 @@ async def post_batstats(stats: BattingStatModel, token: str = Depends(oauth2_sch @router.delete('/{stat_id}', include_in_schema=PRIVATE_IN_SCHEMA) async def delete_batstat(stat_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete stats. This event has been logged.' diff --git a/app/routers_v2/battingcardratings.py b/app/routers_v2/battingcardratings.py index c0d5364..f945c56 100644 --- a/app/routers_v2/battingcardratings.py +++ b/app/routers_v2/battingcardratings.py @@ -154,7 +154,7 @@ async def get_card_ratings( status_code=401, detail="You are not authorized to pull card ratings." ) # elif not valid_token(token): - # logging.warning(f'Bad Token: {token}') + # logging.warning('Bad Token: [REDACTED]') # db.close() # raise HTTPException( # status_code=401, @@ -349,7 +349,7 @@ async def get_card_scouting(team_id: int, ts: str): @router.post("/calculate/scouting", include_in_schema=PRIVATE_IN_SCHEMA) async def post_calc_scouting(token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to calculate card ratings." ) @@ -385,7 +385,7 @@ async def get_basic_scouting(cardset_id: list = Query(default=None)): @router.post("/calculate/basic", include_in_schema=PRIVATE_IN_SCHEMA) async def post_calc_basic(token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to calculate basic ratings." ) @@ -631,7 +631,7 @@ async def post_calc_basic(token: str = Depends(oauth2_scheme)): @router.get("/{ratings_id}") async def get_one_rating(ratings_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to pull card ratings." ) @@ -654,7 +654,7 @@ async def get_player_ratings( token: str = Depends(oauth2_scheme), ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to pull card ratings." ) @@ -681,7 +681,7 @@ async def get_player_ratings( @router.put("", include_in_schema=PRIVATE_IN_SCHEMA) async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post card ratings." ) @@ -715,7 +715,7 @@ async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)) @router.delete("/{ratings_id}", include_in_schema=PRIVATE_IN_SCHEMA) async def delete_rating(ratings_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post card ratings." ) diff --git a/app/routers_v2/battingcards.py b/app/routers_v2/battingcards.py index 8359daa..dad54e4 100644 --- a/app/routers_v2/battingcards.py +++ b/app/routers_v2/battingcards.py @@ -97,7 +97,7 @@ async def get_player_cards( @router.put("") async def put_cards(cards: BattingCardList, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post batting cards. This event has been logged.", @@ -165,7 +165,7 @@ async def patch_card( token: str = Depends(oauth2_scheme), ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to patch batting cards. This event has been logged.", @@ -209,7 +209,7 @@ async def patch_card( @router.delete("/{card_id}") async def delete_card(card_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete batting cards. This event has been logged.", @@ -234,7 +234,7 @@ async def delete_card(card_id: int, token: str = Depends(oauth2_scheme)): @router.delete("") async def delete_all_cards(token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete batting cards. This event has been logged.", diff --git a/app/routers_v2/cardpositions.py b/app/routers_v2/cardpositions.py index 1b08926..f7ca724 100644 --- a/app/routers_v2/cardpositions.py +++ b/app/routers_v2/cardpositions.py @@ -108,7 +108,7 @@ async def get_one_position(position_id: int): @router.put("") async def put_positions(positions: PositionList, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post card positions. This event has been logged.", @@ -146,7 +146,7 @@ async def put_positions(positions: PositionList, token: str = Depends(oauth2_sch @router.delete("/{position_id}") async def delete_position(position_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete card positions. This event has been logged.", diff --git a/app/routers_v2/cards.py b/app/routers_v2/cards.py index 5a7a52a..419b236 100644 --- a/app/routers_v2/cards.py +++ b/app/routers_v2/cards.py @@ -145,7 +145,7 @@ async def v1_cards_get_one(card_id, csv: Optional[bool] = False): @router.post('') async def v1_cards_post(cards: CardModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post cards. This event has been logged.' @@ -187,7 +187,7 @@ async def v1_cards_post(cards: CardModel, token: str = Depends(oauth2_scheme)): # @router.post('/ai-update') # async def v1_cards_ai_update(token: str = Depends(oauth2_scheme)): # if not valid_token(token): -# logging.warning(f'Bad Token: {token}') +# logging.warning('Bad Token: [REDACTED]') # db.close() # raise HTTPException( # status_code=401, @@ -202,7 +202,7 @@ async def v1_cards_post(cards: CardModel, token: str = Depends(oauth2_scheme)): async def v1_cards_legal_check( rarity_name: str, card_id: list = Query(default=None), token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='Unauthorized' @@ -234,7 +234,7 @@ async def v1_cards_legal_check( @router.post('/post-update/{starting_id}') async def v1_cards_post_update(starting_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to update card lists. This event has been logged.' @@ -247,7 +247,7 @@ async def v1_cards_post_update(starting_id: int, token: str = Depends(oauth2_sch @router.post('/post-delete') async def v1_cards_post_delete(del_ids: str, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete card lists. This event has been logged.' @@ -260,7 +260,7 @@ async def v1_cards_post_delete(del_ids: str, token: str = Depends(oauth2_scheme) @router.post('/wipe-team/{team_id}') async def v1_cards_wipe_team(team_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to wipe teams. This event has been logged.' @@ -282,7 +282,7 @@ async def v1_cards_patch( value: Optional[int] = None, variant: Optional[int] = None, roster1_id: Optional[int] = None, roster2_id: Optional[int] = None, roster3_id: Optional[int] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch cards. This event has been logged.' @@ -325,7 +325,7 @@ async def v1_cards_patch( @router.delete('/{card_id}') async def v1_cards_delete(card_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete packs. This event has been logged.' diff --git a/app/routers_v2/cardsets.py b/app/routers_v2/cardsets.py index ed1db28..610680b 100644 --- a/app/routers_v2/cardsets.py +++ b/app/routers_v2/cardsets.py @@ -169,7 +169,7 @@ async def get_one_cardset(cardset_id, csv: Optional[bool] = False): @router.post('') async def post_cardsets(cardset: CardsetModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post cardsets. This event has been logged.' @@ -198,7 +198,7 @@ async def patch_cardsets( for_purchase: Optional[bool] = None, total_cards: Optional[int] = None, ranked_legal: Optional[bool] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch cardsets. This event has been logged.' @@ -234,7 +234,7 @@ async def patch_cardsets( @router.delete('/{cardset_id}') async def delete_cardsets(cardset_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete cardsets. This event has been logged.' diff --git a/app/routers_v2/current.py b/app/routers_v2/current.py index 186eda5..c122229 100644 --- a/app/routers_v2/current.py +++ b/app/routers_v2/current.py @@ -64,7 +64,7 @@ async def get_one_current(current_id, csv: Optional[bool] = False): @router.post('', include_in_schema=PRIVATE_IN_SCHEMA) async def post_current(current: CurrentModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post current. This event has been logged.' @@ -95,7 +95,7 @@ async def patch_current( gsheet_template: Optional[str] = None, gsheet_version: Optional[str] = None, live_scoreboard: Optional[int] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch current. This event has been logged.' @@ -129,7 +129,7 @@ async def patch_current( @router.delete('/{current_id}', include_in_schema=PRIVATE_IN_SCHEMA) async def delete_current(current_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete current. This event has been logged.' diff --git a/app/routers_v2/decisions.py b/app/routers_v2/decisions.py index f0f1030..82d2930 100644 --- a/app/routers_v2/decisions.py +++ b/app/routers_v2/decisions.py @@ -201,7 +201,7 @@ async def patch_decision( token: str = Depends(oauth2_scheme), ): if not valid_token(token): - logging.warning(f"patch_decision - Bad Token: {token}") + logging.warning("patch_decision - Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") this_dec = Decision.get_or_none(Decision.id == decision_id) @@ -241,7 +241,7 @@ async def patch_decision( @router.post("") async def post_decisions(dec_list: DecisionList, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"post_decisions - Bad Token: {token}") + logging.warning("post_decisions - Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") new_dec = [] @@ -271,7 +271,7 @@ async def post_decisions(dec_list: DecisionList, token: str = Depends(oauth2_sch @router.delete("/{decision_id}") async def delete_decision(decision_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"delete_decision - Bad Token: {token}") + logging.warning("delete_decision - Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") this_dec = Decision.get_or_none(Decision.id == decision_id) @@ -293,7 +293,7 @@ async def delete_decision(decision_id: int, token: str = Depends(oauth2_scheme)) @router.delete("/game/{game_id}") async def delete_decisions_game(game_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"delete_decisions_game - Bad Token: {token}") + logging.warning("delete_decisions_game - Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") this_game = StratGame.get_or_none(StratGame.id == game_id) diff --git a/app/routers_v2/events.py b/app/routers_v2/events.py index 076c943..ce368a6 100644 --- a/app/routers_v2/events.py +++ b/app/routers_v2/events.py @@ -84,7 +84,7 @@ async def v1_events_get_one(event_id, csv: Optional[bool] = False): @router.post('') async def v1_events_post(event: EventModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post events. This event has been logged.' @@ -120,7 +120,7 @@ async def v1_events_patch( url: Optional[str] = None, thumbnail: Optional[str] = None, active: Optional[bool] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch events. This event has been logged.' @@ -156,7 +156,7 @@ async def v1_events_patch( @router.delete('/{event_id}') async def v1_events_delete(event_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete events. This event has been logged.' diff --git a/app/routers_v2/gamerewards.py b/app/routers_v2/gamerewards.py index 3d17e51..bef669f 100644 --- a/app/routers_v2/gamerewards.py +++ b/app/routers_v2/gamerewards.py @@ -84,7 +84,7 @@ async def v1_gamerewards_get_one(gamereward_id, csv: Optional[bool] = None): @router.post('') async def v1_gamerewards_post(game_reward: GameRewardModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post game rewards. This event has been logged.' @@ -113,7 +113,7 @@ async def v1_gamerewards_patch( game_reward_id: int, name: Optional[str] = None, pack_type_id: Optional[int] = None, player_id: Optional[int] = None, money: Optional[int] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch gamerewards. This event has been logged.' @@ -154,7 +154,7 @@ async def v1_gamerewards_patch( @router.delete('/{gamereward_id}') async def v1_gamerewards_delete(gamereward_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete awards. This event has been logged.' diff --git a/app/routers_v2/gauntletrewards.py b/app/routers_v2/gauntletrewards.py index 308c52d..0a3ef1b 100644 --- a/app/routers_v2/gauntletrewards.py +++ b/app/routers_v2/gauntletrewards.py @@ -78,7 +78,7 @@ async def v1_gauntletreward_patch( token: str = Depends(oauth2_scheme), ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to patch gauntlet rewards. This event has been logged.", @@ -111,7 +111,7 @@ async def v1_gauntletreward_post( gauntletreward: GauntletRewardList, token: str = Depends(oauth2_scheme) ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post gauntlets. This event has been logged.", diff --git a/app/routers_v2/gauntletruns.py b/app/routers_v2/gauntletruns.py index c8ff43d..ad3f101 100644 --- a/app/routers_v2/gauntletruns.py +++ b/app/routers_v2/gauntletruns.py @@ -97,7 +97,7 @@ async def patch_gauntletrun( gsheet: Optional[str] = None, created: Optional[bool] = None, ended: Optional[bool] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch gauntlet runs. This event has been logged.' @@ -136,7 +136,7 @@ async def patch_gauntletrun( @router.post('') async def post_gauntletrun(gauntletrun: GauntletRunModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post gauntlets. This event has been logged.' diff --git a/app/routers_v2/mlbplayers.py b/app/routers_v2/mlbplayers.py index c8499c6..2cdb29c 100644 --- a/app/routers_v2/mlbplayers.py +++ b/app/routers_v2/mlbplayers.py @@ -137,7 +137,7 @@ async def patch_player( token: str = Depends(oauth2_scheme), ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to patch mlb players. This event has been logged.", @@ -177,7 +177,7 @@ async def patch_player( @router.post("") async def post_players(players: PlayerList, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post mlb players. This event has been logged.", @@ -210,7 +210,7 @@ async def post_players(players: PlayerList, token: str = Depends(oauth2_scheme)) @router.post("/one") async def post_one_player(player: PlayerModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post mlb players. This event has been logged.", @@ -245,7 +245,7 @@ async def post_one_player(player: PlayerModel, token: str = Depends(oauth2_schem @router.delete("/{player_id}") async def delete_player(player_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete mlb players. This event has been logged.", @@ -275,7 +275,7 @@ async def update_columns( mlbplayer_id: Optional[int] = None, token: str = Depends(oauth2_scheme) ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to update mlb players. This event has been logged.", @@ -310,7 +310,7 @@ async def update_names( mlbplayer_id: Optional[int] = None, token: str = Depends(oauth2_scheme) ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to update mlb players. This event has been logged.", @@ -338,7 +338,7 @@ async def update_names( # @router.post('/link-players') # async def post_players(token: str = Depends(oauth2_scheme)): # if not valid_token(token): -# logging.warning(f'Bad Token: {token}') +# logging.warning('Bad Token: [REDACTED]') # db.close() # raise HTTPException( # status_code=401, diff --git a/app/routers_v2/notifications.py b/app/routers_v2/notifications.py index 57d36c8..c29012a 100644 --- a/app/routers_v2/notifications.py +++ b/app/routers_v2/notifications.py @@ -95,7 +95,7 @@ async def get_one_notif(notif_id, csv: Optional[bool] = None): @router.post('') async def post_notif(notif: NotifModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post notifications. This event has been logged.' @@ -128,7 +128,7 @@ async def patch_notif( field_name: Optional[str] = None, message: Optional[str] = None, about: Optional[str] = None, ack: Optional[bool] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch notifications. This event has been logged.' @@ -166,7 +166,7 @@ async def patch_notif( @router.delete('/{notif_id}') async def delete_notif(notif_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete notifications. This event has been logged.' diff --git a/app/routers_v2/packs.py b/app/routers_v2/packs.py index a347c97..19b43c9 100644 --- a/app/routers_v2/packs.py +++ b/app/routers_v2/packs.py @@ -128,7 +128,7 @@ async def get_one_pack(pack_id: int, csv: Optional[bool] = False): @router.post('') async def post_pack(packs: PackModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post packs. This event has been logged.' @@ -154,7 +154,7 @@ async def post_pack(packs: PackModel, token: str = Depends(oauth2_scheme)): @router.post('/one') async def post_one_pack(pack: PackPydantic, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post packs. This event has been logged.' @@ -184,7 +184,7 @@ async def patch_pack( pack_id, team_id: Optional[int] = None, pack_type_id: Optional[int] = None, open_time: Optional[int] = None, pack_team_id: Optional[int] = None, pack_cardset_id: Optional[int] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch packs. This event has been logged.' @@ -227,7 +227,7 @@ async def patch_pack( @router.delete('/{pack_id}') async def delete_pack(pack_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete packs. This event has been logged.' diff --git a/app/routers_v2/packtypes.py b/app/routers_v2/packtypes.py index fedbce7..5ce5195 100644 --- a/app/routers_v2/packtypes.py +++ b/app/routers_v2/packtypes.py @@ -88,7 +88,7 @@ async def get_one_packtype(packtype_id, csv: Optional[bool] = False): @router.post('') async def post_packtypes(packtype: PacktypeModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post packtypes. This event has been logged.' @@ -122,7 +122,7 @@ async def patch_packtype( packtype_id, name: Optional[str] = None, card_count: Optional[int] = None, description: Optional[str] = None, cost: Optional[int] = None, available: Optional[bool] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch packtypes. This event has been logged.' @@ -156,7 +156,7 @@ async def patch_packtype( @router.delete('/{packtype_id}') async def delete_packtype(packtype_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete packtypes. This event has been logged.' diff --git a/app/routers_v2/paperdex.py b/app/routers_v2/paperdex.py index d04c084..6328906 100644 --- a/app/routers_v2/paperdex.py +++ b/app/routers_v2/paperdex.py @@ -95,7 +95,7 @@ async def get_one_paperdex(paperdex_id, csv: Optional[bool] = False): @router.post('') async def post_paperdex(paperdex: PaperdexModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post paperdex. This event has been logged.' @@ -128,7 +128,7 @@ async def patch_paperdex( paperdex_id, team_id: Optional[int] = None, player_id: Optional[int] = None, created: Optional[int] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch paperdex. This event has been logged.' @@ -158,7 +158,7 @@ async def patch_paperdex( @router.delete('/{paperdex_id}') async def delete_paperdex(paperdex_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete rewards. This event has been logged.' @@ -179,7 +179,7 @@ async def delete_paperdex(paperdex_id, token: str = Depends(oauth2_scheme)): @router.post('/wipe-ai') async def wipe_ai_paperdex(token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='Unauthorized' diff --git a/app/routers_v2/pitchingcardratings.py b/app/routers_v2/pitchingcardratings.py index 90a18c6..fcff541 100644 --- a/app/routers_v2/pitchingcardratings.py +++ b/app/routers_v2/pitchingcardratings.py @@ -146,7 +146,7 @@ async def get_card_ratings( token: str = Depends(oauth2_scheme), ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to pull card ratings." ) @@ -269,7 +269,7 @@ async def get_card_scouting(team_id: int, ts: str): @router.post("/calculate/scouting") async def post_calc_scouting(token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to calculate card ratings." ) @@ -305,7 +305,7 @@ async def get_basic_scouting(): @router.post("/calculate/basic") async def post_calc_basic(token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to calculate basic ratings." ) @@ -484,7 +484,7 @@ async def post_calc_basic(token: str = Depends(oauth2_scheme)): @router.get("/{ratings_id}") async def get_one_rating(ratings_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to pull card ratings." ) @@ -525,7 +525,7 @@ async def get_player_ratings( @router.put("") async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post card ratings." ) @@ -559,7 +559,7 @@ async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)) @router.delete("/{ratings_id}") async def delete_rating(ratings_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post card ratings." ) diff --git a/app/routers_v2/pitchingcards.py b/app/routers_v2/pitchingcards.py index d40265a..7e4eb75 100644 --- a/app/routers_v2/pitchingcards.py +++ b/app/routers_v2/pitchingcards.py @@ -94,7 +94,7 @@ async def get_player_cards( @router.put("") async def put_cards(cards: PitchingCardList, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post pitching cards. This event has been logged.", @@ -159,7 +159,7 @@ async def patch_card( token: str = Depends(oauth2_scheme), ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to patch pitching cards. This event has been logged.", @@ -199,7 +199,7 @@ async def patch_card( @router.delete("/{card_id}") async def delete_card(card_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete pitching cards. This event has been logged.", @@ -222,7 +222,7 @@ async def delete_card(card_id: int, token: str = Depends(oauth2_scheme)): @router.delete("") async def delete_all_cards(token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete pitching cards. This event has been logged.", diff --git a/app/routers_v2/pitstats.py b/app/routers_v2/pitstats.py index f30d71a..c5f812c 100644 --- a/app/routers_v2/pitstats.py +++ b/app/routers_v2/pitstats.py @@ -116,7 +116,7 @@ async def get_pit_stats( @router.post('') async def post_pitstat(stats: PitchingStatModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post stats. This event has been logged.' @@ -163,7 +163,7 @@ async def post_pitstat(stats: PitchingStatModel, token: str = Depends(oauth2_sch @router.delete('/{stat_id}') async def delete_pitstat(stat_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete stats. This event has been logged.' diff --git a/app/routers_v2/players.py b/app/routers_v2/players.py index d79daf7..5313bae 100644 --- a/app/routers_v2/players.py +++ b/app/routers_v2/players.py @@ -860,7 +860,7 @@ async def v1_players_patch( token: str = Depends(oauth2_scheme), ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to patch players. This event has been logged.", @@ -972,7 +972,7 @@ async def v1_players_patch( @router.put("") async def put_players(players: PlayerModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post players. This event has been logged.", @@ -1051,7 +1051,7 @@ async def put_players(players: PlayerModel, token: str = Depends(oauth2_scheme)) @router.post("") async def post_players(new_player: PlayerPydantic, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post players. This event has been logged.", @@ -1082,7 +1082,7 @@ async def post_image_reset( player_id: int, dev: bool = False, token: str = Depends(oauth2_scheme) ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to modify players. This event has been logged.", @@ -1116,7 +1116,7 @@ async def post_image_reset( @router.delete("/{player_id}") async def delete_player(player_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete players. This event has been logged.", diff --git a/app/routers_v2/rarity.py b/app/routers_v2/rarity.py index dd771b9..057daa8 100644 --- a/app/routers_v2/rarity.py +++ b/app/routers_v2/rarity.py @@ -86,7 +86,7 @@ async def get_one_rarity(rarity_id, csv: Optional[bool] = False): @router.post('') async def post_rarity(rarity: RarityModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post rarities. This event has been logged.' @@ -118,7 +118,7 @@ async def patch_rarity( rarity_id, value: Optional[int] = None, name: Optional[str] = None, color: Optional[str] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch rarities. This event has been logged.' @@ -148,7 +148,7 @@ async def patch_rarity( @router.delete('/{rarity_id}') async def v1_rarities_delete(rarity_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete rarities. This event has been logged.' diff --git a/app/routers_v2/results.py b/app/routers_v2/results.py index 295a8ac..173d145 100644 --- a/app/routers_v2/results.py +++ b/app/routers_v2/results.py @@ -245,7 +245,7 @@ async def get_team_results( @router.post('') async def post_result(result: ResultModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post results. This event has been logged.' @@ -325,7 +325,7 @@ async def patch_result( season: Optional[int] = None, short_game: Optional[bool] = None, game_type: Optional[str] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch results. This event has been logged.' @@ -384,7 +384,7 @@ async def patch_result( @router.delete('/{result_id}') async def delete_result(result_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post results. This event has been logged.' diff --git a/app/routers_v2/rewards.py b/app/routers_v2/rewards.py index 54ddc05..6886e99 100644 --- a/app/routers_v2/rewards.py +++ b/app/routers_v2/rewards.py @@ -95,7 +95,7 @@ async def get_one_reward(reward_id, csv: Optional[bool] = False): @router.post('') async def post_rewards(reward: RewardModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to post rewards. This event has been logged.' @@ -123,7 +123,7 @@ async def patch_reward( reward_id, name: Optional[str] = None, team_id: Optional[int] = None, created: Optional[int] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to patch rewards. This event has been logged.' @@ -154,7 +154,7 @@ async def patch_reward( @router.delete('/{reward_id}') async def delete_reward(reward_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to delete rewards. This event has been logged.' diff --git a/app/routers_v2/scouting.py b/app/routers_v2/scouting.py index 2cd9c49..1b727a5 100644 --- a/app/routers_v2/scouting.py +++ b/app/routers_v2/scouting.py @@ -47,7 +47,7 @@ async def get_player_keys(player_id: list = Query(default=None)): @router.post('/live-update/batting') def live_update_batting(files: BattingFiles, cardset_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to initiate live updates.' @@ -81,7 +81,7 @@ def live_update_batting(files: BattingFiles, cardset_id: int, token: str = Depen @router.post('/live-update/pitching') def live_update_pitching(files: BattingFiles, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'Bad Token: {token}') + logging.warning('Bad Token: [REDACTED]') raise HTTPException( status_code=401, detail='You are not authorized to initiate live updates.' diff --git a/app/routers_v2/stratgame.py b/app/routers_v2/stratgame.py index 3fb32b8..1a3fa7b 100644 --- a/app/routers_v2/stratgame.py +++ b/app/routers_v2/stratgame.py @@ -105,7 +105,7 @@ async def patch_game( game_id: int, game_type: Optional[str] = None, away_score: Optional[int] = None, home_score: Optional[int] = None, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'patch_game - Bad Token: {token}') + logging.warning('patch_game - Bad Token: [REDACTED]') raise HTTPException(status_code=401, detail='Unauthorized') this_game = StratGame.get_or_none(StratGame.id == game_id) @@ -129,7 +129,7 @@ async def patch_game( @router.post('') async def post_game(this_game: GameModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'post_games - Bad Token: {token}') + logging.warning('post_games - Bad Token: [REDACTED]') raise HTTPException(status_code=401, detail='Unauthorized') this_game = StratGame(**this_game.dict()) @@ -148,7 +148,7 @@ async def post_game(this_game: GameModel, token: str = Depends(oauth2_scheme)): @router.delete('/{game_id}') async def delete_game(game_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f'delete_game - Bad Token: {token}') + logging.warning('delete_game - Bad Token: [REDACTED]') raise HTTPException(status_code=401, detail='Unauthorized') this_game = StratGame.get_or_none(StratGame.id == game_id) diff --git a/app/routers_v2/stratplays.py b/app/routers_v2/stratplays.py index 2fc8150..287ad5b 100644 --- a/app/routers_v2/stratplays.py +++ b/app/routers_v2/stratplays.py @@ -1401,7 +1401,7 @@ async def patch_play( play_id: int, new_play: PlayModel, token: str = Depends(oauth2_scheme) ): if not valid_token(token): - logging.warning(f"patch_play - Bad Token: {token}") + logging.warning("patch_play - Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") if StratPlay.get_or_none(StratPlay.id == play_id) is None: @@ -1415,7 +1415,7 @@ async def patch_play( @router.post("") async def post_plays(p_list: PlayList, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"post_plays - Bad Token: {token}") + logging.warning("post_plays - Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") new_plays = [] @@ -1465,7 +1465,7 @@ async def post_plays(p_list: PlayList, token: str = Depends(oauth2_scheme)): @router.delete("/{play_id}") async def delete_play(play_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"delete_play - Bad Token: {token}") + logging.warning("delete_play - Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") this_play = StratPlay.get_or_none(StratPlay.id == play_id) @@ -1485,7 +1485,7 @@ async def delete_play(play_id: int, token: str = Depends(oauth2_scheme)): @router.delete("/game/{game_id}") async def delete_plays_game(game_id: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"delete_plays_game - Bad Token: {token}") + logging.warning("delete_plays_game - Bad Token: [REDACTED]") raise HTTPException(status_code=401, detail="Unauthorized") this_game = StratGame.get_or_none(StratGame.id == game_id) diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index a6dc46c..a2b31d8 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -1317,7 +1317,7 @@ async def get_team_cards(team_id, csv: Optional[bool] = True): @router.post("", include_in_schema=PRIVATE_IN_SCHEMA) async def post_team(team: TeamModel, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post teams. This event has been logged.", @@ -1363,7 +1363,7 @@ async def post_team(team: TeamModel, token: str = Depends(oauth2_scheme)): @router.post("/new-season/{new_season}", include_in_schema=PRIVATE_IN_SCHEMA) async def team_season_update(new_season: int, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to post teams. This event has been logged.", @@ -1386,7 +1386,7 @@ async def team_update_money( team_id: int, delta: int, token: str = Depends(oauth2_scheme) ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to adjust wallets. This event has been logged.", @@ -1431,7 +1431,7 @@ async def patch_team( abbrev: Optional[str] = None, ): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete teams. This event has been logged.", @@ -1493,7 +1493,7 @@ async def patch_team( @router.delete("/{team_id}", include_in_schema=PRIVATE_IN_SCHEMA) async def delete_team(team_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning(f"Bad Token: {token}") + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, detail="You are not authorized to delete teams. This event has been logged.", From 19ac5ffd0ad43e8bf1b8be8b01c8efdd986f49bc Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 23:31:26 -0600 Subject: [PATCH 18/25] fix: use constant-time comparison for bearer token validation (#8) Co-Authored-By: Claude Sonnet 4.6 --- app/dependencies.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/dependencies.py b/app/dependencies.py index 40902d8..592bf65 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -1,4 +1,5 @@ import datetime +import hmac import logging import os @@ -24,7 +25,7 @@ if os.environ.get("TESTING") == "True": def valid_token(token): - return token == AUTH_TOKEN + return hmac.compare_digest(token, AUTH_TOKEN) def int_timestamp(datetime_obj: datetime) -> int: From 5e182bedacd7e6c59da686414636c6f12983a9d1 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Wed, 4 Mar 2026 21:32:00 -0600 Subject: [PATCH 19/25] feat: add scout_opportunities and scout_claims tables and API endpoints (#44) Support the Discord bot's new scouting feature where players can scout cards from other teams' opened packs. Stores opportunities with expiry timestamps and tracks which teams claim which cards. Co-Authored-By: Claude Opus 4.6 --- app/db_engine.py | 35 ++++++++ app/main.py | 4 + app/routers_v2/scout_claims.py | 91 +++++++++++++++++++ app/routers_v2/scout_opportunities.py | 123 ++++++++++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 app/routers_v2/scout_claims.py create mode 100644 app/routers_v2/scout_opportunities.py diff --git a/app/db_engine.py b/app/db_engine.py index 290b160..06492f9 100644 --- a/app/db_engine.py +++ b/app/db_engine.py @@ -1070,6 +1070,41 @@ if not SKIP_TABLE_CREATION: db.create_tables([StratGame, StratPlay, Decision], safe=True) +class ScoutOpportunity(BaseModel): + pack = ForeignKeyField(Pack, null=True) + opener_team = ForeignKeyField(Team) + card_ids = CharField() # JSON array of card IDs + expires_at = BigIntegerField() + created = BigIntegerField() + + class Meta: + database = db + table_name = "scout_opportunity" + + +class ScoutClaim(BaseModel): + scout_opportunity = ForeignKeyField(ScoutOpportunity) + card = ForeignKeyField(Card) + claimed_by_team = ForeignKeyField(Team) + created = BigIntegerField() + + class Meta: + database = db + table_name = "scout_claim" + + +scout_claim_index = ModelIndex( + ScoutClaim, + (ScoutClaim.scout_opportunity, ScoutClaim.claimed_by_team), + unique=True, +) +ScoutClaim.add_index(scout_claim_index) + + +if not SKIP_TABLE_CREATION: + db.create_tables([ScoutOpportunity, ScoutClaim], safe=True) + + db.close() # scout_db = SqliteDatabase( diff --git a/app/main.py b/app/main.py index 55d7233..64cbfc2 100644 --- a/app/main.py +++ b/app/main.py @@ -47,6 +47,8 @@ from .routers_v2 import ( mlbplayers, stratgame, stratplays, + scout_opportunities, + scout_claims, ) app = FastAPI( @@ -88,6 +90,8 @@ app.include_router(mlbplayers.router) app.include_router(stratgame.router) app.include_router(stratplays.router) app.include_router(decisions.router) +app.include_router(scout_opportunities.router) +app.include_router(scout_claims.router) @app.middleware("http") diff --git a/app/routers_v2/scout_claims.py b/app/routers_v2/scout_claims.py new file mode 100644 index 0000000..501a1b4 --- /dev/null +++ b/app/routers_v2/scout_claims.py @@ -0,0 +1,91 @@ +from datetime import datetime +from fastapi import APIRouter, Depends, HTTPException +from typing import Optional +import logging +import pydantic + +from ..db_engine import ScoutClaim, ScoutOpportunity, model_to_dict +from ..dependencies import oauth2_scheme, valid_token + +router = APIRouter(prefix="/api/v2/scout_claims", tags=["scout_claims"]) + + +class ScoutClaimModel(pydantic.BaseModel): + scout_opportunity_id: int + card_id: int + claimed_by_team_id: int + + +@router.get("") +async def get_scout_claims( + scout_opportunity_id: Optional[int] = None, claimed_by_team_id: Optional[int] = None +): + + query = ScoutClaim.select().order_by(ScoutClaim.id) + + if scout_opportunity_id is not None: + query = query.where(ScoutClaim.scout_opportunity_id == scout_opportunity_id) + if claimed_by_team_id is not None: + query = query.where(ScoutClaim.claimed_by_team_id == claimed_by_team_id) + + results = [model_to_dict(x, recurse=False) for x in query] + return {"count": len(results), "results": results} + + +@router.get("/{claim_id}") +async def get_one_scout_claim(claim_id: int): + try: + claim = ScoutClaim.get_by_id(claim_id) + except Exception: + raise HTTPException( + status_code=404, detail=f"No scout claim found with id {claim_id}" + ) + + return model_to_dict(claim) + + +@router.post("") +async def post_scout_claim(claim: ScoutClaimModel, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f"Bad Token: {token}") + raise HTTPException( + status_code=401, + detail="You are not authorized to post scout claims. This event has been logged.", + ) + + claim_data = claim.dict() + claim_data["created"] = int(datetime.timestamp(datetime.now()) * 1000) + + this_claim = ScoutClaim(**claim_data) + saved = this_claim.save() + + if saved == 1: + return model_to_dict(this_claim) + else: + raise HTTPException(status_code=418, detail="Could not save scout claim") + + +@router.delete("/{claim_id}") +async def delete_scout_claim(claim_id: int, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f"Bad Token: {token}") + raise HTTPException( + status_code=401, + detail="You are not authorized to delete scout claims. This event has been logged.", + ) + try: + claim = ScoutClaim.get_by_id(claim_id) + except Exception: + raise HTTPException( + status_code=404, detail=f"No scout claim found with id {claim_id}" + ) + + count = claim.delete_instance() + if count == 1: + raise HTTPException( + status_code=200, detail=f"Scout claim {claim_id} has been deleted" + ) + else: + raise HTTPException( + status_code=500, detail=f"Scout claim {claim_id} was not deleted" + ) diff --git a/app/routers_v2/scout_opportunities.py b/app/routers_v2/scout_opportunities.py new file mode 100644 index 0000000..25a3a75 --- /dev/null +++ b/app/routers_v2/scout_opportunities.py @@ -0,0 +1,123 @@ +import json +from datetime import datetime +from fastapi import APIRouter, Depends, HTTPException +from typing import Optional, List +import logging +import pydantic + +from ..db_engine import ScoutOpportunity, ScoutClaim, model_to_dict, fn +from ..dependencies import oauth2_scheme, valid_token + +router = APIRouter(prefix="/api/v2/scout_opportunities", tags=["scout_opportunities"]) + + +class ScoutOpportunityModel(pydantic.BaseModel): + pack_id: Optional[int] = None + opener_team_id: int + card_ids: List[int] + expires_at: int + created: Optional[int] = None + + +def opportunity_to_dict(opp, recurse=True): + """Convert a ScoutOpportunity to dict with card_ids deserialized.""" + result = model_to_dict(opp, recurse=recurse) + if isinstance(result.get("card_ids"), str): + result["card_ids"] = json.loads(result["card_ids"]) + return result + + +@router.get("") +async def get_scout_opportunities( + claimed: Optional[bool] = None, + expired_before: Optional[int] = None, + opener_team_id: Optional[int] = None, +): + + query = ScoutOpportunity.select().order_by(ScoutOpportunity.id) + + if opener_team_id is not None: + query = query.where(ScoutOpportunity.opener_team_id == opener_team_id) + + if expired_before is not None: + query = query.where(ScoutOpportunity.expires_at < expired_before) + + if claimed is not None: + # Check whether any scout_claims exist for each opportunity + claim_subquery = ScoutClaim.select(ScoutClaim.scout_opportunity) + if claimed: + query = query.where(ScoutOpportunity.id.in_(claim_subquery)) + else: + query = query.where(ScoutOpportunity.id.not_in(claim_subquery)) + + results = [opportunity_to_dict(x, recurse=False) for x in query] + return {"count": len(results), "results": results} + + +@router.get("/{opportunity_id}") +async def get_one_scout_opportunity(opportunity_id: int): + try: + opp = ScoutOpportunity.get_by_id(opportunity_id) + except Exception: + raise HTTPException( + status_code=404, + detail=f"No scout opportunity found with id {opportunity_id}", + ) + + return opportunity_to_dict(opp) + + +@router.post("") +async def post_scout_opportunity( + opportunity: ScoutOpportunityModel, token: str = Depends(oauth2_scheme) +): + if not valid_token(token): + logging.warning(f"Bad Token: {token}") + raise HTTPException( + status_code=401, + detail="You are not authorized to post scout opportunities. This event has been logged.", + ) + + opp_data = opportunity.dict() + opp_data["card_ids"] = json.dumps(opp_data["card_ids"]) + if opp_data["created"] is None: + opp_data["created"] = int(datetime.timestamp(datetime.now()) * 1000) + + this_opp = ScoutOpportunity(**opp_data) + saved = this_opp.save() + + if saved == 1: + return opportunity_to_dict(this_opp) + else: + raise HTTPException(status_code=418, detail="Could not save scout opportunity") + + +@router.delete("/{opportunity_id}") +async def delete_scout_opportunity( + opportunity_id: int, token: str = Depends(oauth2_scheme) +): + if not valid_token(token): + logging.warning(f"Bad Token: {token}") + raise HTTPException( + status_code=401, + detail="You are not authorized to delete scout opportunities. This event has been logged.", + ) + try: + opp = ScoutOpportunity.get_by_id(opportunity_id) + except Exception: + raise HTTPException( + status_code=404, + detail=f"No scout opportunity found with id {opportunity_id}", + ) + + count = opp.delete_instance() + if count == 1: + raise HTTPException( + status_code=200, + detail=f"Scout opportunity {opportunity_id} has been deleted", + ) + else: + raise HTTPException( + status_code=500, + detail=f"Scout opportunity {opportunity_id} was not deleted", + ) From 37439626ed88da68f2183591fac7940b0f5c9cd9 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Wed, 4 Mar 2026 21:33:28 -0600 Subject: [PATCH 20/25] chore: add PostgreSQL migration for scout tables (#44) Creates scout_opportunity and scout_claim tables with foreign keys, unique constraint on (opportunity, team), and expires_at index. Already applied to dev database. Co-Authored-By: Claude Opus 4.6 --- ...6-03-04_add_scout_opportunities_claims.sql | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 migrations/2026-03-04_add_scout_opportunities_claims.sql diff --git a/migrations/2026-03-04_add_scout_opportunities_claims.sql b/migrations/2026-03-04_add_scout_opportunities_claims.sql new file mode 100644 index 0000000..aaa97e9 --- /dev/null +++ b/migrations/2026-03-04_add_scout_opportunities_claims.sql @@ -0,0 +1,57 @@ +-- Migration: Add scout_opportunity and scout_claim tables +-- Date: 2026-03-04 +-- Issue: #44 +-- Purpose: Support the scouting feature where players can scout cards +-- from other teams' opened packs within a 30-minute window. +-- +-- Run on dev first, verify with: +-- SELECT count(*) FROM scout_opportunity; +-- SELECT count(*) FROM scout_claim; +-- +-- Rollback: See DROP statements at bottom of file + +-- ============================================ +-- FORWARD MIGRATION +-- ============================================ + +BEGIN; + +CREATE TABLE IF NOT EXISTS scout_opportunity ( + id SERIAL PRIMARY KEY, + pack_id INTEGER REFERENCES pack(id) ON DELETE SET NULL, + opener_team_id INTEGER NOT NULL REFERENCES team(id) ON DELETE CASCADE, + card_ids VARCHAR(255) NOT NULL, -- JSON array of card IDs, e.g. "[10, 11, 12]" + expires_at BIGINT NOT NULL, -- Unix ms timestamp, 30 min after creation + created BIGINT NOT NULL -- Unix ms timestamp +); + +CREATE TABLE IF NOT EXISTS scout_claim ( + id SERIAL PRIMARY KEY, + scout_opportunity_id INTEGER NOT NULL REFERENCES scout_opportunity(id) ON DELETE CASCADE, + card_id INTEGER NOT NULL REFERENCES card(id) ON DELETE CASCADE, + claimed_by_team_id INTEGER NOT NULL REFERENCES team(id) ON DELETE CASCADE, + created BIGINT NOT NULL -- Unix ms timestamp, auto-set on creation +); + +-- Unique constraint: one claim per team per opportunity +CREATE UNIQUE INDEX IF NOT EXISTS scout_claim_opportunity_team_uniq + ON scout_claim (scout_opportunity_id, claimed_by_team_id); + +-- Index for the common query: find unclaimed, expired opportunities +CREATE INDEX IF NOT EXISTS scout_opportunity_expires_at_idx + ON scout_opportunity (expires_at); + +COMMIT; + +-- ============================================ +-- VERIFICATION QUERIES +-- ============================================ +-- \d scout_opportunity +-- \d scout_claim +-- SELECT indexname FROM pg_indexes WHERE tablename IN ('scout_opportunity', 'scout_claim'); + +-- ============================================ +-- ROLLBACK (if needed) +-- ============================================ +-- DROP TABLE IF EXISTS scout_claim CASCADE; +-- DROP TABLE IF EXISTS scout_opportunity CASCADE; From 62b205bde2cd6cb0c25eb42d3cf7acdc20f074ba Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 17:34:08 -0600 Subject: [PATCH 21/25] fix: batch BattingCard/BattingCardRatings lookups in lineup builder (#18) Replace per-player get_or_none() calls in get_bratings() with two bulk SELECT queries before the position loop, keyed by player_id and card+hand. This reduces DB round trips from O(3N) to O(2) for all lineup difficulties. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/teams.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index a2b31d8..ff93512 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -354,17 +354,35 @@ async def get_team_lineup( "DH": {"player": None, "vl": None, "vr": None, "ops": 0}, } + # Batch-fetch BattingCards and ratings for all candidate players to avoid + # per-player DB round trips inside the lineup construction loop below. + if backup_players is not None: + _batch_bcards = BattingCard.select().where( + (BattingCard.player << legal_players) + | (BattingCard.player << backup_players) + ) + else: + _batch_bcards = BattingCard.select().where(BattingCard.player << legal_players) + _batting_cards_by_player = {bc.player_id: bc for bc in _batch_bcards} + _all_bratings = ( + BattingCardRatings.select().where( + BattingCardRatings.battingcard << list(_batting_cards_by_player.values()) + ) + if _batting_cards_by_player + else [] + ) + _ratings_by_card_hand = {} + for _r in _all_bratings: + _ratings_by_card_hand.setdefault(_r.battingcard_id, {})[_r.vs_hand] = _r + def get_bratings(player_id): - this_bcard = BattingCard.get_or_none(BattingCard.player_id == player_id) - vl_ratings = BattingCardRatings.get_or_none( - BattingCardRatings.battingcard == this_bcard, - BattingCardRatings.vs_hand == "L", + this_bcard = _batting_cards_by_player.get(player_id) + card_ratings = ( + _ratings_by_card_hand.get(this_bcard.id, {}) if this_bcard else {} ) + vl_ratings = card_ratings.get("L") + vr_ratings = card_ratings.get("R") vl_ops = vl_ratings.obp + vl_ratings.slg - vr_ratings = BattingCardRatings.get_or_none( - BattingCardRatings.battingcard == this_bcard, - BattingCardRatings.vs_hand == "R", - ) vr_ops = vr_ratings.obp + vr_ratings.slg return ( model_to_dict(vl_ratings), From 0c042165b7b26ac7a0cb110da94b8ff21e27df05 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 19:05:16 -0600 Subject: [PATCH 22/25] fix: replace broad except Exception blocks with DoesNotExist (#15) Replace 71 broad `except Exception` blocks in 19 router files with the specific `peewee.DoesNotExist` exception. GET endpoints that call `Model.get_by_id()` now only catch the expected DoesNotExist error, allowing real DB failures (connection errors, etc.) to propagate as 500s rather than being masked as 404s. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/awards.py | 6 +++--- app/routers_v2/batstats.py | 4 ++-- app/routers_v2/cards.py | 16 ++++++++-------- app/routers_v2/cardsets.py | 12 ++++++------ app/routers_v2/current.py | 8 ++++---- app/routers_v2/events.py | 8 ++++---- app/routers_v2/gamerewards.py | 8 ++++---- app/routers_v2/gauntletrewards.py | 4 ++-- app/routers_v2/gauntletruns.py | 4 ++-- app/routers_v2/notifications.py | 8 ++++---- app/routers_v2/packs.py | 16 ++++++++-------- app/routers_v2/packtypes.py | 8 ++++---- app/routers_v2/paperdex.py | 8 ++++---- app/routers_v2/pitstats.py | 4 ++-- app/routers_v2/players.py | 15 ++++++++------- app/routers_v2/rarity.py | 8 ++++---- app/routers_v2/results.py | 18 +++++++++--------- app/routers_v2/rewards.py | 8 ++++---- app/routers_v2/teams.py | 25 +++++++++++++------------ 19 files changed, 95 insertions(+), 93 deletions(-) diff --git a/app/routers_v2/awards.py b/app/routers_v2/awards.py index 30cfbe2..3d79030 100644 --- a/app/routers_v2/awards.py +++ b/app/routers_v2/awards.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import Award, model_to_dict +from ..db_engine import Award, model_to_dict, DoesNotExist from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA @@ -73,7 +73,7 @@ async def get_awards( async def get_one_award(award_id, csv: Optional[bool] = None): try: this_award = Award.get_by_id(award_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No award found with id {award_id}') if csv: @@ -130,7 +130,7 @@ async def delete_award(award_id, token: str = Depends(oauth2_scheme)): ) try: this_award = Award.get_by_id(award_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No award found with id {award_id}') count = this_award.delete_instance() diff --git a/app/routers_v2/batstats.py b/app/routers_v2/batstats.py index d0dfd05..95c9db9 100644 --- a/app/routers_v2/batstats.py +++ b/app/routers_v2/batstats.py @@ -6,7 +6,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, BattingStat, model_to_dict, fn, Card, Player, Current +from ..db_engine import db, BattingStat, model_to_dict, fn, Card, Player, Current, DoesNotExist from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA @@ -236,7 +236,7 @@ async def delete_batstat(stat_id, token: str = Depends(oauth2_scheme)): ) try: this_stat = BattingStat.get_by_id(stat_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No stat found with id {stat_id}') count = this_stat.delete_instance() diff --git a/app/routers_v2/cards.py b/app/routers_v2/cards.py index 419b236..96b9774 100644 --- a/app/routers_v2/cards.py +++ b/app/routers_v2/cards.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Card, model_to_dict, Team, Player, Pack, Paperdex, CARDSETS +from ..db_engine import db, Card, model_to_dict, Team, Player, Pack, Paperdex, CARDSETS, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -41,19 +41,19 @@ async def get_cards( if team_id is not None: try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No team found with id {team_id}') all_cards = all_cards.where(Card.team == this_team) if player_id is not None: try: this_player = Player.get_by_id(player_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No player found with id {player_id}') all_cards = all_cards.where(Card.player == this_player) if pack_id is not None: try: this_pack = Pack.get_by_id(pack_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}') all_cards = all_cards.where(Card.pack == this_pack) if value is not None: @@ -125,7 +125,7 @@ async def get_cards( async def v1_cards_get_one(card_id, csv: Optional[bool] = False): try: this_card = Card.get_by_id(card_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No card found with id {card_id}') if csv: @@ -268,7 +268,7 @@ async def v1_cards_wipe_team(team_id: int, token: str = Depends(oauth2_scheme)): try: this_team = Team.get_by_id(team_id) - except Exception as e: + except DoesNotExist as e: logging.error(f'/cards/wipe-team/{team_id} - could not find team') raise HTTPException(status_code=404, detail=f'Team {team_id} not found') @@ -289,7 +289,7 @@ async def v1_cards_patch( ) try: this_card = Card.get_by_id(card_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No card found with id {card_id}') if player_id is not None: @@ -332,7 +332,7 @@ async def v1_cards_delete(card_id, token: str = Depends(oauth2_scheme)): ) try: this_card = Card.get_by_id(card_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No cards found with id {card_id}') count = this_card.delete_instance() diff --git a/app/routers_v2/cardsets.py b/app/routers_v2/cardsets.py index 610680b..a92e55a 100644 --- a/app/routers_v2/cardsets.py +++ b/app/routers_v2/cardsets.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import Cardset, model_to_dict, fn, Event +from ..db_engine import Cardset, model_to_dict, fn, Event, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -41,7 +41,7 @@ async def get_cardsets( try: this_event = Event.get_by_id(event_id) all_cardsets = all_cardsets.where(Cardset.event == this_event) - except Exception as e: + except DoesNotExist as e: logging.error(f'Failed to find event {event_id}: {e}') raise HTTPException(status_code=404, detail=f'Event id {event_id} not found') if in_packs is not None: @@ -104,7 +104,7 @@ async def search_cardsets( try: this_event = Event.get_by_id(event_id) all_cardsets = all_cardsets.where(Cardset.event == this_event) - except Exception as e: + except DoesNotExist as e: logging.error(f'Failed to find event {event_id}: {e}') raise HTTPException(status_code=404, detail=f'Event id {event_id} not found') @@ -150,7 +150,7 @@ async def search_cardsets( async def get_one_cardset(cardset_id, csv: Optional[bool] = False): try: this_cardset = Cardset.get_by_id(cardset_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}') if csv: @@ -205,7 +205,7 @@ async def patch_cardsets( ) try: this_cardset = Cardset.get_by_id(cardset_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}') if name is not None: @@ -241,7 +241,7 @@ async def delete_cardsets(cardset_id, token: str = Depends(oauth2_scheme)): ) try: this_cardset = Cardset.get_by_id(cardset_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}') count = this_cardset.delete_instance() diff --git a/app/routers_v2/current.py b/app/routers_v2/current.py index c122229..2184291 100644 --- a/app/routers_v2/current.py +++ b/app/routers_v2/current.py @@ -4,7 +4,7 @@ from typing import Optional import logging import pydantic -from ..db_engine import Current, model_to_dict +from ..db_engine import Current, model_to_dict, DoesNotExist from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA @@ -45,7 +45,7 @@ async def get_current(season: Optional[int] = None, csv: Optional[bool] = False) async def get_one_current(current_id, csv: Optional[bool] = False): try: current = Current.get_by_id(current_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No current found with id {current_id}') if csv: @@ -102,7 +102,7 @@ async def patch_current( ) try: current = Current.get_by_id(current_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No current found with id {current_id}') if season is not None: @@ -136,7 +136,7 @@ async def delete_current(current_id, token: str = Depends(oauth2_scheme)): ) try: this_curr = Current.get_by_id(current_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No current found with id {current_id}') count = this_curr.delete_instance() diff --git a/app/routers_v2/events.py b/app/routers_v2/events.py index ce368a6..cd989cb 100644 --- a/app/routers_v2/events.py +++ b/app/routers_v2/events.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import Event, model_to_dict, fn +from ..db_engine import Event, model_to_dict, fn, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -63,7 +63,7 @@ async def v1_events_get( async def v1_events_get_one(event_id, csv: Optional[bool] = False): try: this_event = Event.get_by_id(event_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No event found with id {event_id}') if csv: @@ -127,7 +127,7 @@ async def v1_events_patch( ) try: this_event = Event.get_by_id(event_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No event found with id {event_id}') if name is not None: @@ -163,7 +163,7 @@ async def v1_events_delete(event_id, token: str = Depends(oauth2_scheme)): ) try: this_event = Event.get_by_id(event_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No event found with id {event_id}') count = this_event.delete_instance() diff --git a/app/routers_v2/gamerewards.py b/app/routers_v2/gamerewards.py index bef669f..724f12a 100644 --- a/app/routers_v2/gamerewards.py +++ b/app/routers_v2/gamerewards.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import GameRewards, model_to_dict +from ..db_engine import GameRewards, model_to_dict, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -63,7 +63,7 @@ async def v1_gamerewards_get( async def v1_gamerewards_get_one(gamereward_id, csv: Optional[bool] = None): try: this_game_reward = GameRewards.get_by_id(gamereward_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No game reward found with id {gamereward_id}') if csv: @@ -120,7 +120,7 @@ async def v1_gamerewards_patch( ) try: this_game_reward = GameRewards.get_by_id(game_reward_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No game reward found with id {game_reward_id}') if name is not None: @@ -161,7 +161,7 @@ async def v1_gamerewards_delete(gamereward_id, token: str = Depends(oauth2_schem ) try: this_award = GameRewards.get_by_id(gamereward_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No award found with id {gamereward_id}') count = this_award.delete_instance() diff --git a/app/routers_v2/gauntletrewards.py b/app/routers_v2/gauntletrewards.py index 0a3ef1b..6022da6 100644 --- a/app/routers_v2/gauntletrewards.py +++ b/app/routers_v2/gauntletrewards.py @@ -3,7 +3,7 @@ from typing import Optional, List import logging import pydantic -from ..db_engine import db, GauntletReward, model_to_dict, DatabaseError +from ..db_engine import db, GauntletReward, model_to_dict, DatabaseError, DoesNotExist from ..db_helpers import upsert_gauntlet_rewards from ..dependencies import oauth2_scheme, valid_token @@ -57,7 +57,7 @@ async def v1_gauntletreward_get( async def v1_gauntletreward_get_one(gauntletreward_id): try: this_reward = GauntletReward.get_by_id(gauntletreward_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No gauntlet reward found with id {gauntletreward_id}", diff --git a/app/routers_v2/gauntletruns.py b/app/routers_v2/gauntletruns.py index ad3f101..cc85eeb 100644 --- a/app/routers_v2/gauntletruns.py +++ b/app/routers_v2/gauntletruns.py @@ -4,7 +4,7 @@ from typing import Optional import logging import pydantic -from ..db_engine import GauntletRun, model_to_dict, DatabaseError +from ..db_engine import GauntletRun, model_to_dict, DatabaseError, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -84,7 +84,7 @@ async def get_gauntletruns( async def get_one_gauntletrun(gauntletrun_id): try: this_gauntlet = GauntletRun.get_by_id(gauntletrun_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No gauntlet found with id {gauntletrun_id}') return_val = model_to_dict(this_gauntlet) diff --git a/app/routers_v2/notifications.py b/app/routers_v2/notifications.py index c29012a..7fec666 100644 --- a/app/routers_v2/notifications.py +++ b/app/routers_v2/notifications.py @@ -5,7 +5,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import Notification, model_to_dict, fn +from ..db_engine import Notification, model_to_dict, fn, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -74,7 +74,7 @@ async def get_notifs( async def get_one_notif(notif_id, csv: Optional[bool] = None): try: this_notif = Notification.get_by_id(notif_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}') if csv: @@ -135,7 +135,7 @@ async def patch_notif( ) try: this_notif = Notification.get_by_id(notif_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}') if title is not None: @@ -173,7 +173,7 @@ async def delete_notif(notif_id, token: str = Depends(oauth2_scheme)): ) try: this_notif = Notification.get_by_id(notif_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}') count = this_notif.delete_instance() diff --git a/app/routers_v2/packs.py b/app/routers_v2/packs.py index 19b43c9..77b3739 100644 --- a/app/routers_v2/packs.py +++ b/app/routers_v2/packs.py @@ -6,7 +6,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Cardset, model_to_dict, Pack, Team, PackType +from ..db_engine import db, Cardset, model_to_dict, Pack, Team, PackType, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -41,20 +41,20 @@ async def get_packs( if team_id is not None: try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No team found with id {team_id}') all_packs = all_packs.where(Pack.team == this_team) if pack_type_id is not None: try: this_pack_type = PackType.get_by_id(pack_type_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No pack type found with id {pack_type_id}') all_packs = all_packs.where(Pack.pack_type == this_pack_type) if pack_team_id is not None: try: this_pack_team = Team.get_by_id(pack_team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No team found with id {pack_team_id}') all_packs = all_packs.where(Pack.pack_team == this_pack_team) elif exact_match: @@ -63,7 +63,7 @@ async def get_packs( if pack_cardset_id is not None: try: this_pack_cardset = Cardset.get_by_id(pack_cardset_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No cardset found with id {pack_cardset_id}') all_packs = all_packs.where(Pack.pack_cardset == this_pack_cardset) elif exact_match: @@ -107,7 +107,7 @@ async def get_packs( async def get_one_pack(pack_id: int, csv: Optional[bool] = False): try: this_pack = Pack.get_by_id(pack_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}') if csv: @@ -191,7 +191,7 @@ async def patch_pack( ) try: this_pack = Pack.get_by_id(pack_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}') if team_id is not None: @@ -234,7 +234,7 @@ async def delete_pack(pack_id, token: str = Depends(oauth2_scheme)): ) try: this_pack = Pack.get_by_id(pack_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No packs found with id {pack_id}') count = this_pack.delete_instance() diff --git a/app/routers_v2/packtypes.py b/app/routers_v2/packtypes.py index 5ce5195..a6940a8 100644 --- a/app/routers_v2/packtypes.py +++ b/app/routers_v2/packtypes.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import PackType, model_to_dict, fn +from ..db_engine import PackType, model_to_dict, fn, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -68,7 +68,7 @@ async def get_packtypes( async def get_one_packtype(packtype_id, csv: Optional[bool] = False): try: this_packtype = PackType.get_by_id(packtype_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}') if csv: @@ -129,7 +129,7 @@ async def patch_packtype( ) try: this_packtype = PackType.get_by_id(packtype_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}') if name is not None: @@ -163,7 +163,7 @@ async def delete_packtype(packtype_id, token: str = Depends(oauth2_scheme)): ) try: this_packtype = PackType.get_by_id(packtype_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}') count = this_packtype.delete_instance() diff --git a/app/routers_v2/paperdex.py b/app/routers_v2/paperdex.py index 6328906..41e309d 100644 --- a/app/routers_v2/paperdex.py +++ b/app/routers_v2/paperdex.py @@ -5,7 +5,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import Paperdex, model_to_dict, Player, Cardset, Team +from ..db_engine import Paperdex, model_to_dict, Player, Cardset, Team, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -75,7 +75,7 @@ async def get_paperdex( async def get_one_paperdex(paperdex_id, csv: Optional[bool] = False): try: this_dex = Paperdex.get_by_id(paperdex_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}') if csv: @@ -135,7 +135,7 @@ async def patch_paperdex( ) try: this_dex = Paperdex.get_by_id(paperdex_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}') if team_id is not None: @@ -165,7 +165,7 @@ async def delete_paperdex(paperdex_id, token: str = Depends(oauth2_scheme)): ) try: this_dex = Paperdex.get_by_id(paperdex_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}') count = this_dex.delete_instance() diff --git a/app/routers_v2/pitstats.py b/app/routers_v2/pitstats.py index c5f812c..82f3883 100644 --- a/app/routers_v2/pitstats.py +++ b/app/routers_v2/pitstats.py @@ -5,7 +5,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import db, PitchingStat, model_to_dict, Card, Player, Current +from ..db_engine import db, PitchingStat, model_to_dict, Card, Player, Current, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -170,7 +170,7 @@ async def delete_pitstat(stat_id, token: str = Depends(oauth2_scheme)): ) try: this_stat = PitchingStat.get_by_id(stat_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No stat found with id {stat_id}') count = this_stat.delete_instance() diff --git a/app/routers_v2/players.py b/app/routers_v2/players.py index 5313bae..dd842f8 100644 --- a/app/routers_v2/players.py +++ b/app/routers_v2/players.py @@ -12,7 +12,7 @@ from pandas import DataFrame from playwright.async_api import async_playwright from ..card_creation import get_batter_card_data, get_pitcher_card_data -from ..db_engine import ( +from ..db_engine import (, DoesNotExist db, Player, model_to_dict, @@ -26,6 +26,7 @@ from ..db_engine import ( PitchingCardRatings, CardPosition, MlbPlayer, + DoesNotExist, ) from ..db_helpers import upsert_players from ..dependencies import oauth2_scheme, valid_token @@ -585,7 +586,7 @@ async def search_players( async def get_one_player(player_id: int, csv: Optional[bool] = False): try: this_player = Player.get_by_id(player_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No player found with id {player_id}" ) @@ -668,7 +669,7 @@ async def get_batter_card( ): try: this_player = Player.get_by_id(player_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No player found with id {player_id}" ) @@ -868,7 +869,7 @@ async def v1_players_patch( try: this_player = Player.get_by_id(player_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No player found with id {player_id}" ) @@ -891,7 +892,7 @@ async def v1_players_patch( if cardset_id is not None: try: this_cardset = Cardset.get_by_id(cardset_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No cardset found with id {cardset_id}" ) @@ -899,7 +900,7 @@ async def v1_players_patch( if rarity_id is not None: try: this_rarity = Rarity.get_by_id(rarity_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No rarity found with id {rarity_id}" ) @@ -1124,7 +1125,7 @@ async def delete_player(player_id: int, token: str = Depends(oauth2_scheme)): try: this_player = Player.get_by_id(player_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No player found with id {player_id}" ) diff --git a/app/routers_v2/rarity.py b/app/routers_v2/rarity.py index 057daa8..71f490d 100644 --- a/app/routers_v2/rarity.py +++ b/app/routers_v2/rarity.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import Rarity, model_to_dict, fn +from ..db_engine import Rarity, model_to_dict, fn, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -64,7 +64,7 @@ async def get_rarities(value: Optional[int] = None, name: Optional[str] = None, async def get_one_rarity(rarity_id, csv: Optional[bool] = False): try: this_rarity = Rarity.get_by_id(rarity_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}') if csv: @@ -125,7 +125,7 @@ async def patch_rarity( ) try: this_rarity = Rarity.get_by_id(rarity_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}') if value is not None: @@ -155,7 +155,7 @@ async def v1_rarities_delete(rarity_id, token: str = Depends(oauth2_scheme)): ) try: this_rarity = Rarity.get_by_id(rarity_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}') count = this_rarity.delete_instance() diff --git a/app/routers_v2/results.py b/app/routers_v2/results.py index 173d145..cabfcc5 100644 --- a/app/routers_v2/results.py +++ b/app/routers_v2/results.py @@ -4,7 +4,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import Result, model_to_dict, Team, DataError +from ..db_engine import Result, model_to_dict, Team, DataError, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -50,28 +50,28 @@ async def get_results( try: this_team = Team.get_by_id(away_team_id) all_results = all_results.where(Result.away_team == this_team) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No team found with id {away_team_id}') if home_team_id is not None: try: this_team = Team.get_by_id(home_team_id) all_results = all_results.where(Result.home_team == this_team) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No team found with id {home_team_id}') if team_one_id is not None: try: this_team = Team.get_by_id(team_one_id) all_results = all_results.where((Result.home_team == this_team) | (Result.away_team == this_team)) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No team found with id {team_one_id}') if team_two_id is not None: try: this_team = Team.get_by_id(team_two_id) all_results = all_results.where((Result.home_team == this_team) | (Result.away_team == this_team)) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No team found with id {team_two_id}') if away_score_min is not None: @@ -158,7 +158,7 @@ async def get_results( async def get_one_results(result_id, csv: Optional[bool] = None): try: this_result = Result.get_by_id(result_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No result found with id {result_id}') if csv: @@ -185,7 +185,7 @@ async def get_team_results( all_results = Result.select().where((Result.away_team_id == team_id) | (Result.home_team_id == team_id)).order_by(Result.id) try: this_team = Team.get_by_id(team_id) - except Exception as e: + except DoesNotExist as e: logging.error(f'Unknown team id {team_id} trying to pull team results') raise HTTPException(404, f'Team id {team_id} not found') @@ -332,7 +332,7 @@ async def patch_result( ) try: this_result = Result.get_by_id(result_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No result found with id {result_id}') if away_team_id is not None: @@ -391,7 +391,7 @@ async def delete_result(result_id, token: str = Depends(oauth2_scheme)): ) try: this_result = Result.get_by_id(result_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No result found with id {result_id}') count = this_result.delete_instance() diff --git a/app/routers_v2/rewards.py b/app/routers_v2/rewards.py index 6886e99..ab6aa43 100644 --- a/app/routers_v2/rewards.py +++ b/app/routers_v2/rewards.py @@ -5,7 +5,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import Reward, model_to_dict, fn +from ..db_engine import Reward, model_to_dict, fn, DoesNotExist from ..dependencies import oauth2_scheme, valid_token @@ -75,7 +75,7 @@ async def get_rewards( async def get_one_reward(reward_id, csv: Optional[bool] = False): try: this_reward = Reward.get_by_id(reward_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}') if csv: @@ -130,7 +130,7 @@ async def patch_reward( ) try: this_reward = Reward.get_by_id(reward_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}') if name is not None: @@ -161,7 +161,7 @@ async def delete_reward(reward_id, token: str = Depends(oauth2_scheme)): ) try: this_reward = Reward.get_by_id(reward_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}') count = this_reward.delete_instance() diff --git a/app/routers_v2/teams.py b/app/routers_v2/teams.py index a2b31d8..d5b11b2 100644 --- a/app/routers_v2/teams.py +++ b/app/routers_v2/teams.py @@ -8,7 +8,7 @@ import logging import pydantic from pandas import DataFrame -from ..db_engine import ( +from ..db_engine import (, DoesNotExist db, Team, model_to_dict, @@ -31,6 +31,7 @@ from ..db_engine import ( PitchingCardRatings, StratGame, LIVE_PROMO_CARDSET_ID, + DoesNotExist, ) from ..dependencies import ( oauth2_scheme, @@ -170,7 +171,7 @@ async def get_teams( async def get_one_team(team_id: int, inc_packs: bool = True, csv: Optional[bool] = False): try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") p_query = Pack.select().where( @@ -1017,7 +1018,7 @@ async def get_team_record(team_id: int, season: int): async def team_buy_players(team_id: int, ids: str, ts: str): try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if ts != this_team.team_hash(): @@ -1037,7 +1038,7 @@ async def team_buy_players(team_id: int, ids: str, ts: str): if player_id != "": try: this_player = Player.get_by_id(player_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No player found with id {player_id} /// " @@ -1105,14 +1106,14 @@ async def team_buy_packs( ): try: this_packtype = PackType.get_by_id(packtype_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No pack type found with id {packtype_id}" ) try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if ts != this_team.team_hash(): @@ -1168,7 +1169,7 @@ async def team_buy_packs( async def team_sell_cards(team_id: int, ids: str, ts: str): try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if ts != this_team.team_hash(): @@ -1186,7 +1187,7 @@ async def team_sell_cards(team_id: int, ids: str, ts: str): if card_id != "": try: this_card = Card.get_by_id(card_id) - except Exception: + except DoesNotExist: raise HTTPException( status_code=404, detail=f"No card found with id {card_id}" ) @@ -1254,7 +1255,7 @@ async def get_team_cards(team_id, csv: Optional[bool] = True): """ try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if not csv: @@ -1394,7 +1395,7 @@ async def team_update_money( try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") this_team.wallet += delta @@ -1438,7 +1439,7 @@ async def patch_team( ) try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") if abbrev is not None: @@ -1500,7 +1501,7 @@ async def delete_team(team_id, token: str = Depends(oauth2_scheme)): ) try: this_team = Team.get_by_id(team_id) - except Exception: + except DoesNotExist: raise HTTPException(status_code=404, detail=f"No team found with id {team_id}") count = this_team.delete_instance() From 7b494faa99bb3d37b788ac7739898669823a47f6 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 22:32:32 -0600 Subject: [PATCH 23/25] fix: remove broken live_update_batting stub endpoint (#10) The endpoint iterated over `files.vl_basic` (a string, not parsed CSV), causing it to loop over individual characters. The body contained only `pass` with TODO comments and no running stats logic. Removed the endpoint entirely along with the dead commented-out csv helper code. The `BattingFiles` model is retained as it is still used by `live_update_pitching`. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/scouting.py | 65 +++++++------------------------------- 1 file changed, 12 insertions(+), 53 deletions(-) diff --git a/app/routers_v2/scouting.py b/app/routers_v2/scouting.py index 1b727a5..0e51f87 100644 --- a/app/routers_v2/scouting.py +++ b/app/routers_v2/scouting.py @@ -6,19 +6,15 @@ from ..db_engine import Player from ..dependencies import oauth2_scheme, valid_token from ..player_scouting import get_player_ids - -router = APIRouter( - prefix='/api/v2/scouting', - tags=['scouting'] -) +router = APIRouter(prefix="/api/v2/scouting", tags=["scouting"]) class BattingFiles(pydantic.BaseModel): - vl_basic: str = 'vl-basic.csv' - vl_rate: str = 'vl-rate.csv' - vr_basic: str = 'vr-basic.csv' - vr_rate: str = 'vr-rate.csv' - running: str = 'running.csv' + vl_basic: str = "vl-basic.csv" + vl_rate: str = "vl-rate.csv" + vr_basic: str = "vr-basic.csv" + vr_rate: str = "vr-rate.csv" + running: str = "running.csv" # def csv_file_to_dataframe(filename: str) -> pd.DataFrame | None: @@ -28,63 +24,26 @@ class BattingFiles(pydantic.BaseModel): # for row in reader: -@router.get('/playerkeys') +@router.get("/playerkeys") async def get_player_keys(player_id: list = Query(default=None)): all_keys = [] for x in player_id: this_player = Player.get_or_none(Player.player_id == x) if this_player is not None: - this_keys = get_player_ids(this_player.bbref_id, id_type='bbref') + this_keys = get_player_ids(this_player.bbref_id, id_type="bbref") if this_keys is not None: all_keys.append(this_keys) - return_val = {'count': len(all_keys), 'keys': [ - dict(x) for x in all_keys - ]} + return_val = {"count": len(all_keys), "keys": [dict(x) for x in all_keys]} return return_val -@router.post('/live-update/batting') -def live_update_batting(files: BattingFiles, cardset_id: int, token: str = Depends(oauth2_scheme)): - if not valid_token(token): - logging.warning('Bad Token: [REDACTED]') - raise HTTPException( - status_code=401, - detail='You are not authorized to initiate live updates.' - ) - - data = {} # : { 'vL': [combined vl stat data], 'vR': [combined vr stat data] } - for row in files.vl_basic: - if row['pa'] >= 20: - data[row['fgid']]['vL'] = row - for row in files.vl_rate: - if row['fgid'] in data.keys(): - data[row['fgid']]['vL'].extend(row) - - for row in files.vr_basic: - if row['pa'] >= 40 and row['fgid'] in data.keys(): - data[row['fgid']]['vR'] = row - for row in files.vr_rate: - if row['fgid'] in data.keys(): - data[row['fgid']]['vR'].extend(row) - - for x in data.items(): - pass - # Create BattingCardRating object for vL - # Create BattingCardRating object for vR - - # Read running stats and create/update BattingCard object - - return files.dict() - - -@router.post('/live-update/pitching') +@router.post("/live-update/pitching") def live_update_pitching(files: BattingFiles, token: str = Depends(oauth2_scheme)): if not valid_token(token): - logging.warning('Bad Token: [REDACTED]') + logging.warning("Bad Token: [REDACTED]") raise HTTPException( - status_code=401, - detail='You are not authorized to initiate live updates.' + status_code=401, detail="You are not authorized to initiate live updates." ) return files.dict() From 44b6222ad5b110c4e5e227b6192698d4c9e373b9 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Wed, 4 Mar 2026 09:33:13 -0600 Subject: [PATCH 24/25] fix: refactor Roster from 26 FK columns to RosterSlot junction table (#29) - Remove card_1..card_26 FK columns from Roster ORM model - Add RosterSlot model with (roster, slot, card) and a unique index on (roster, slot) - Activate get_cards() helper on Roster using the new junction table - Register RosterSlot in create_tables for SQLite dev environments - Add migrations/migrate_roster_junction_table.py to backfill existing data Co-Authored-By: Claude Sonnet 4.6 --- app/db_engine.py | 54 ++++++----------- migrations/migrate_roster_junction_table.py | 65 +++++++++++++++++++++ 2 files changed, 84 insertions(+), 35 deletions(-) create mode 100644 migrations/migrate_roster_junction_table.py diff --git a/app/db_engine.py b/app/db_engine.py index 06492f9..30e7d7c 100644 --- a/app/db_engine.py +++ b/app/db_engine.py @@ -498,51 +498,34 @@ class Roster(BaseModel): team = ForeignKeyField(Team) name = CharField() roster_num = IntegerField() - card_1 = ForeignKeyField(Card) - card_2 = ForeignKeyField(Card) - card_3 = ForeignKeyField(Card) - card_4 = ForeignKeyField(Card) - card_5 = ForeignKeyField(Card) - card_6 = ForeignKeyField(Card) - card_7 = ForeignKeyField(Card) - card_8 = ForeignKeyField(Card) - card_9 = ForeignKeyField(Card) - card_10 = ForeignKeyField(Card) - card_11 = ForeignKeyField(Card) - card_12 = ForeignKeyField(Card) - card_13 = ForeignKeyField(Card) - card_14 = ForeignKeyField(Card) - card_15 = ForeignKeyField(Card) - card_16 = ForeignKeyField(Card) - card_17 = ForeignKeyField(Card) - card_18 = ForeignKeyField(Card) - card_19 = ForeignKeyField(Card) - card_20 = ForeignKeyField(Card) - card_21 = ForeignKeyField(Card) - card_22 = ForeignKeyField(Card) - card_23 = ForeignKeyField(Card) - card_24 = ForeignKeyField(Card) - card_25 = ForeignKeyField(Card) - card_26 = ForeignKeyField(Card) def __str__(self): return f"{self.team} Roster" - # def get_cards(self, team): - # all_cards = Card.select().where(Card.roster == self) - # this_roster = [] - # return [this_roster.card1, this_roster.card2, this_roster.card3, this_roster.card4, this_roster.card5, - # this_roster.card6, this_roster.card7, this_roster.card8, this_roster.card9, this_roster.card10, - # this_roster.card11, this_roster.card12, this_roster.card13, this_roster.card14, this_roster.card15, - # this_roster.card16, this_roster.card17, this_roster.card18, this_roster.card19, this_roster.card20, - # this_roster.card21, this_roster.card22, this_roster.card23, this_roster.card24, this_roster.card25, - # this_roster.card26] + def get_cards(self): + return ( + Card.select() + .join(RosterSlot) + .where(RosterSlot.roster == self) + .order_by(RosterSlot.slot) + ) class Meta: database = db table_name = "roster" +class RosterSlot(BaseModel): + roster = ForeignKeyField(Roster, backref="slots") + slot = IntegerField() + card = ForeignKeyField(Card, backref="roster_slots") + + class Meta: + database = db + table_name = "rosterslot" + indexes = ((("roster", "slot"), True),) + + class Result(BaseModel): away_team = ForeignKeyField(Team) home_team = ForeignKeyField(Team) @@ -744,6 +727,7 @@ if not SKIP_TABLE_CREATION: db.create_tables( [ Roster, + RosterSlot, BattingStat, PitchingStat, Result, diff --git a/migrations/migrate_roster_junction_table.py b/migrations/migrate_roster_junction_table.py new file mode 100644 index 0000000..419c606 --- /dev/null +++ b/migrations/migrate_roster_junction_table.py @@ -0,0 +1,65 @@ +""" +Migration: Replace 26 FK columns on Roster with RosterSlot junction table. + +Creates the `rosterslot` table and migrates existing lineup data from the +card_1..card_26 columns. Safe to re-run (skips rosters already migrated). + +Usage: + python migrations/migrate_roster_junction_table.py +""" + +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) + +from app.db_engine import db, Roster, RosterSlot + +SLOTS = 26 + + +def migrate(): + db.connect(reuse_if_open=True) + + # Create the table if it doesn't exist yet + db.create_tables([RosterSlot], safe=True) + + # Read raw rows from the old schema via plain SQL so we don't depend on + # the ORM model knowing about the legacy card_N columns. + cursor = db.execute_sql("SELECT * FROM roster") + columns = [desc[0] for desc in cursor.description] + + migrated = 0 + skipped = 0 + + with db.atomic(): + for row in cursor.fetchall(): + row_dict = dict(zip(columns, row)) + roster_id = row_dict["id"] + + already_migrated = ( + RosterSlot.select().where(RosterSlot.roster == roster_id).exists() + ) + if already_migrated: + skipped += 1 + continue + + slots_to_insert = [] + for slot_num in range(1, SLOTS + 1): + col = f"card_{slot_num}_id" + card_id = row_dict.get(col) + if card_id is not None: + slots_to_insert.append( + {"roster": roster_id, "slot": slot_num, "card": card_id} + ) + + if slots_to_insert: + RosterSlot.insert_many(slots_to_insert).execute() + migrated += 1 + + print(f"Migration complete: {migrated} rosters migrated, {skipped} already done.") + db.close() + + +if __name__ == "__main__": + migrate() From 10983138a986c608561eb4c798f738560f713e54 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Thu, 5 Mar 2026 15:40:06 -0600 Subject: [PATCH 25/25] ci: Use docker-tags composite action for multi-channel release support Adds next-release branch trigger and replaces separate dev/production build steps with the shared docker-tags action for tag resolution. Co-Authored-By: Claude Opus 4.6 --- .gitea/workflows/build.yml | 51 +++++++++++++++----------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 6c32bfc..eeeb242 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -3,6 +3,7 @@ # CI/CD pipeline for Paper Dynasty Database API: # - Builds Docker images on every push/PR # - Auto-generates CalVer version (YYYY.MM.BUILD) on main branch merges +# - Supports multi-channel releases: stable (main), rc (next-release), dev (PRs) # - Pushes to Docker Hub and creates git tag on main # - Sends Discord notifications on success/failure @@ -12,6 +13,7 @@ on: push: branches: - main + - next-release pull_request: branches: - main @@ -39,30 +41,20 @@ jobs: id: calver uses: cal/gitea-actions/calver@main - # Dev build: push with dev + dev-SHA tags (PR/feature branches) - - name: Build Docker image (dev) - if: github.ref != 'refs/heads/main' - uses: https://github.com/docker/build-push-action@v5 + - name: Resolve Docker tags + id: tags + uses: cal/gitea-actions/docker-tags@main with: - context: . - push: true - tags: | - manticorum67/paper-dynasty-database:dev - manticorum67/paper-dynasty-database:dev-${{ steps.calver.outputs.sha_short }} - cache-from: type=registry,ref=manticorum67/paper-dynasty-database:buildcache - cache-to: type=registry,ref=manticorum67/paper-dynasty-database:buildcache,mode=max + image: manticorum67/paper-dynasty-database + version: ${{ steps.calver.outputs.version }} + sha_short: ${{ steps.calver.outputs.sha_short }} - # Production build: push with latest + CalVer tags (main only) - - name: Build Docker image (production) - if: github.ref == 'refs/heads/main' + - name: Build and push Docker image uses: https://github.com/docker/build-push-action@v5 with: context: . push: true - tags: | - manticorum67/paper-dynasty-database:latest - manticorum67/paper-dynasty-database:${{ steps.calver.outputs.version }} - manticorum67/paper-dynasty-database:${{ steps.calver.outputs.version_sha }} + tags: ${{ steps.tags.outputs.tags }} cache-from: type=registry,ref=manticorum67/paper-dynasty-database:buildcache cache-to: type=registry,ref=manticorum67/paper-dynasty-database:buildcache,mode=max @@ -77,38 +69,35 @@ jobs: run: | echo "## Docker Build Successful" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY + echo "**Channel:** \`${{ steps.tags.outputs.channel }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY echo "**Image Tags:**" >> $GITHUB_STEP_SUMMARY - echo "- \`manticorum67/paper-dynasty-database:latest\`" >> $GITHUB_STEP_SUMMARY - echo "- \`manticorum67/paper-dynasty-database:${{ steps.calver.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY - echo "- \`manticorum67/paper-dynasty-database:${{ steps.calver.outputs.version_sha }}\`" >> $GITHUB_STEP_SUMMARY + IFS=',' read -ra TAG_ARRAY <<< "${{ steps.tags.outputs.tags }}" + for tag in "${TAG_ARRAY[@]}"; do + echo "- \`${tag}\`" >> $GITHUB_STEP_SUMMARY + done echo "" >> $GITHUB_STEP_SUMMARY echo "**Build Details:**" >> $GITHUB_STEP_SUMMARY echo "- Branch: \`${{ steps.calver.outputs.branch }}\`" >> $GITHUB_STEP_SUMMARY echo "- Commit: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY echo "- Timestamp: \`${{ steps.calver.outputs.timestamp }}\`" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - if [ "${{ github.ref }}" == "refs/heads/main" ]; then - echo "Pushed to Docker Hub!" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Pull with: \`docker pull manticorum67/paper-dynasty-database:latest\`" >> $GITHUB_STEP_SUMMARY - else - echo "_PR build - image not pushed to Docker Hub_" >> $GITHUB_STEP_SUMMARY - fi + echo "Pull with: \`docker pull manticorum67/paper-dynasty-database:${{ steps.tags.outputs.primary_tag }}\`" >> $GITHUB_STEP_SUMMARY - name: Discord Notification - Success - if: success() && github.ref == 'refs/heads/main' + if: success() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next-release') uses: cal/gitea-actions/discord-notify@main with: webhook_url: ${{ secrets.DISCORD_WEBHOOK }} title: "Paper Dynasty Database" status: success version: ${{ steps.calver.outputs.version }} - image_tag: ${{ steps.calver.outputs.version_sha }} + image_tag: ${{ steps.tags.outputs.primary_tag }} commit_sha: ${{ steps.calver.outputs.sha_short }} timestamp: ${{ steps.calver.outputs.timestamp }} - name: Discord Notification - Failure - if: failure() && github.ref == 'refs/heads/main' + if: failure() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next-release') uses: cal/gitea-actions/discord-notify@main with: webhook_url: ${{ secrets.DISCORD_WEBHOOK }}