Compare commits

..

24 Commits

Author SHA1 Message Date
Cal Corum
c94cff002a fix: batch Paperdex lookups to avoid N+1 queries (#17)
Replace per-player/card Paperdex.select().where() calls with a single
batched query grouped by player_id. Eliminates N+1 queries in:
- players list endpoint (get_players, with inc_dex flag)
- players by team endpoint
- cards list endpoint (also materializes query to avoid double count())

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 15:33:38 -06:00
cal
8227b57875 Merge pull request 'feat: add scout opportunities and claims API (#44)' (#59) from feat/scout-opportunities-claims into next-release
Reviewed-on: #59
2026-03-05 03:45:57 +00:00
Cal Corum
37439626ed 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 <noreply@anthropic.com>
2026-03-05 03:45:38 +00:00
Cal Corum
5e182bedac 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 <noreply@anthropic.com>
2026-03-05 03:45:38 +00:00
cal
9711f63da5 Merge pull request 'fix: use constant-time comparison for bearer token validation (#8)' (#56) from ai/paper-dynasty-database#8 into next-release
Reviewed-on: #56
2026-03-05 03:44:13 +00:00
Cal Corum
19ac5ffd0a fix: use constant-time comparison for bearer token validation (#8)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 03:43:59 +00:00
cal
551fccd9f2 Merge pull request 'fix: remove plaintext bearer token from warning logs (#7)' (#55) from ai/paper-dynasty-database#7 into next-release
Reviewed-on: #55
2026-03-05 03:43:40 +00:00
Cal Corum
35389cac24 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 <noreply@anthropic.com>
2026-03-05 03:43:27 +00:00
cal
8283425b84 Merge pull request 'fix: consolidate redundant double-query in get_one_play (#14)' (#52) from ai/paper-dynasty-database#14 into next-release
Reviewed-on: #52
2026-03-05 03:36:25 +00:00
Cal Corum
f1d289a0e9 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 <noreply@anthropic.com>
2026-03-05 03:35:59 +00:00
cal
110493d1b0 Merge pull request 'fix: compute CSV after appending data row in get_one_player (#12)' (#51) from ai/paper-dynasty-database-12 into next-release
Reviewed-on: #51
2026-03-05 03:32:21 +00:00
Cal Corum
0166c7dda4 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 <noreply@anthropic.com>
2026-03-05 03:32:09 +00:00
cal
d1728a804f Merge pull request 'fix: guard against None rating objects in pitcher sorting functions (#13)' (#50) from ai/paper-dynasty-database#13 into next-release
Reviewed-on: #50
2026-03-05 03:29:48 +00:00
Cal Corum
0948db0b49 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 <noreply@anthropic.com>
2026-03-05 03:25:28 +00:00
cal
1651ff1738 Merge pull request 'fix: document SQLite synchronous=0 pragma in db_engine.py (#20)' (#49) from ai/paper-dynasty-database#20 into next-release
Reviewed-on: #49
2026-03-05 03:23:50 +00:00
Cal Corum
870c753bf7 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 <noreply@anthropic.com>
2026-03-05 03:23:30 +00:00
cal
341296b573 Merge pull request 'fix: centralize logging config in main.py (#26)' (#46) from ai/paper-dynasty-database#26 into next-release
Reviewed-on: #46
2026-03-05 03:22:28 +00:00
Cal Corum
053fcbab05 fix: centralize logging config in main.py — remove basicConfig from 32 files (#26)
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 <noreply@anthropic.com>
2026-03-05 03:22:02 +00:00
cal
e7c8b59201 Merge pull request 'fix: batch-fetch PitchingCardRatings instead of per-row queries (#19)' (#44) from ai/paper-dynasty-database#19 into next-release
Reviewed-on: #44
2026-03-05 03:19:58 +00:00
Cal Corum
ae8c20ea1c 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 <noreply@anthropic.com>
2026-03-05 03:19:43 +00:00
cal
9096a4b976 Merge pull request 'fix: add type annotations to untyped path parameters (#27)' (#43) from ai/paper-dynasty-database#27 into next-release
Reviewed-on: #43
2026-03-05 03:18:49 +00:00
Cal Corum
5f86c8cb20 fix: add type annotations to untyped path parameters (#27)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 03:18:38 +00:00
cal
20f727a119 Merge pull request 'fix: remove duplicate ranking_max filter in get_teams (#21)' (#42) from ai/paper-dynasty-database#21 into next-release
Reviewed-on: #42
2026-03-05 03:17:50 +00:00
Cal Corum
86b4338b66 fix: remove duplicate ranking_max filter in get_teams (#21)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 16:01:52 -06:00
36 changed files with 509 additions and 342 deletions

View File

@ -30,20 +30,20 @@ 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},
)
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 +925,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,
)
@ -1064,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(

View File

@ -1,25 +1,11 @@
import datetime
import hmac
import logging
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/"
@ -39,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:

View File

@ -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
@ -36,6 +47,8 @@ from .routers_v2 import (
mlbplayers,
stratgame,
stratplays,
scout_opportunities,
scout_claims,
)
app = FastAPI(
@ -77,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")

View File

@ -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',
@ -19,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.'

View File

@ -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',
@ -99,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.'
@ -128,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.'

View File

@ -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',
@ -178,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.'
@ -234,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.'

View File

@ -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"
@ -159,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,
@ -354,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."
)
@ -390,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."
)
@ -636,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."
)
@ -659,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."
)
@ -686,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."
)
@ -720,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."
)

View File

@ -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"])
@ -102,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.",
@ -170,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.",
@ -214,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.",
@ -239,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.",

View File

@ -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"])
@ -113,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.",
@ -151,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.",

View File

@ -5,13 +5,7 @@ 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
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
from ..dependencies import oauth2_scheme, valid_token
router = APIRouter(prefix="/api/v2/cards", tags=["cards"])
@ -183,7 +177,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.",
@ -229,7 +223,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,
@ -247,7 +241,7 @@ async def v1_cards_legal_check(
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")
if rarity_name not in CARDSETS.keys():
return f"Rarity name {rarity_name} not a valid check"
@ -282,7 +276,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.",
@ -298,7 +292,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.",
@ -311,7 +305,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.",
@ -341,7 +335,7 @@ async def v1_cards_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 cards. This event has been logged.",
@ -384,7 +378,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.",

View File

@ -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',
@ -174,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.'
@ -203,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.'
@ -239,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.'

View File

@ -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',
@ -69,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.'
@ -100,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.'
@ -134,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.'

View File

@ -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"])
@ -206,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)
@ -246,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 = []
@ -276,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)
@ -298,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)

View File

@ -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',
@ -89,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.'
@ -125,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.'
@ -161,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.'

View File

@ -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',
@ -89,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.'
@ -118,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.'
@ -159,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.'

View File

@ -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"])
@ -83,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.",
@ -116,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.",

View File

@ -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',
@ -102,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.'
@ -141,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.'

View File

@ -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"])
@ -142,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.",
@ -182,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.",
@ -215,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.",
@ -250,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.",
@ -280,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.",
@ -315,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.",
@ -343,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,

View File

@ -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',
@ -100,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.'
@ -133,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.'
@ -171,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.'

View File

@ -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',
@ -109,7 +104,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:
@ -133,7 +128,7 @@ async def get_one_pack(pack_id, 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.'
@ -159,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.'
@ -189,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.'
@ -232,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.'

View File

@ -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',
@ -93,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.'
@ -127,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.'
@ -161,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.'

View File

@ -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',
@ -100,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.'
@ -133,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.'
@ -163,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.'
@ -184,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'

View File

@ -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"
@ -151,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."
)
@ -274,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."
)
@ -310,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."
)
@ -489,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."
)
@ -530,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."
)
@ -564,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."
)

View File

@ -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"])
@ -99,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.",
@ -164,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.",
@ -204,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.",
@ -227,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.",

View File

@ -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',
@ -121,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.'
@ -168,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.'

View File

@ -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"])
@ -596,7 +591,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:
@ -631,7 +626,6 @@ async def get_one_player(player_id, csv: Optional[bool] = False):
"description",
]
]
return_val = DataFrame(data_list).to_csv(header=False, index=False)
data_list.append(
[
this_player.id,
@ -658,6 +652,7 @@ async def get_one_player(player_id, 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:
@ -874,7 +869,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.",
@ -986,7 +981,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.",
@ -1065,7 +1060,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.",
@ -1096,7 +1091,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.",
@ -1128,9 +1123,9 @@ 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}")
logging.warning("Bad Token: [REDACTED]")
raise HTTPException(
status_code=401,
detail="You are not authorized to delete players. This event has been logged.",

View File

@ -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',
@ -91,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.'
@ -123,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.'
@ -153,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.'

View File

@ -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',
@ -250,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.'
@ -330,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.'
@ -389,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.'

View File

@ -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',
@ -100,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.'
@ -128,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.'
@ -159,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.'

View File

@ -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"
)

View File

@ -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",
)

View File

@ -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',
@ -52,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.'
@ -86,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.'

View File

@ -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',
@ -110,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)
@ -134,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())
@ -153,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)

View File

@ -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"])
@ -1395,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}")
@ -1406,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:
@ -1420,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 = []
@ -1470,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)
@ -1490,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)

View File

@ -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"])
@ -138,9 +132,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:
@ -176,7 +167,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:
@ -282,7 +273,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"]
@ -590,16 +580,21 @@ 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}")
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",
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 = 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??
@ -667,16 +662,21 @@ async def get_team_sp(
starter_df = pd.DataFrame(all_s).set_index("player", drop=False)
logging.debug(f"starter_df: {starter_df}")
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",
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 = 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
@ -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.",

View File

@ -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;