Merge pull request 'Release: Scouting API, roster refactor, and bug fixes' (#61) from next-release into main
Some checks failed
Build Docker Image / build (push) Failing after 7m5s
Some checks failed
Build Docker Image / build (push) Failing after 7m5s
Reviewed-on: #61
This commit is contained in:
commit
7b8b7b9c01
4
.env
4
.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
|
||||
|
||||
@ -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 }}
|
||||
|
||||
@ -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
|
||||
|
||||
115
app/db_engine.py
115
app/db_engine.py
@ -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
|
||||
@ -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,
|
||||
@ -925,7 +909,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 +1054,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(
|
||||
|
||||
@ -1,74 +1,72 @@
|
||||
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/'
|
||||
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):
|
||||
return token == AUTH_TOKEN
|
||||
return hmac.compare_digest(token, AUTH_TOKEN)
|
||||
|
||||
|
||||
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 +75,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 +128,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 +210,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}")
|
||||
|
||||
73
app/main.py
73
app/main.py
@ -1,20 +1,61 @@
|
||||
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
|
||||
|
||||
from .db_engine import db
|
||||
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,
|
||||
scout_opportunities,
|
||||
scout_claims,
|
||||
)
|
||||
|
||||
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")
|
||||
@ -49,14 +90,28 @@ 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")
|
||||
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):
|
||||
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)
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
import logging
|
||||
|
||||
from ..db_engine import db, Player
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA
|
||||
from ..db_engine import Player
|
||||
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,8 +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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post. This event has been logged.'
|
||||
@ -29,7 +23,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'}
|
||||
|
||||
|
||||
@ -4,14 +4,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Award, model_to_dict
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA
|
||||
from ..db_engine import Award, model_to_dict, DoesNotExist
|
||||
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',
|
||||
@ -41,7 +36,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 +59,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 +66,6 @@ async def get_awards(
|
||||
for x in all_awards:
|
||||
return_val['awards'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -81,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No award found with id {award_id}')
|
||||
|
||||
if csv:
|
||||
@ -93,20 +84,17 @@ 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
|
||||
|
||||
|
||||
@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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post awards. This event has been logged.'
|
||||
@ -124,10 +112,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'
|
||||
@ -137,20 +123,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete awards. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_award = Award.get_by_id(award_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -6,14 +6,9 @@ import logging
|
||||
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 ..db_engine import db, BattingStat, model_to_dict, fn, Card, Player, Current, DoesNotExist
|
||||
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',
|
||||
@ -119,7 +114,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 +121,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 +159,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,15 +167,13 @@ 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
|
||||
|
||||
|
||||
@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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post stats. This event has been logged.'
|
||||
@ -232,7 +222,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')
|
||||
|
||||
@ -240,20 +229,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete stats. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_stat = BattingStat.get_by_id(stat_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -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,21 +12,14 @@ from ..db_engine import (
|
||||
db,
|
||||
BattingCardRatings,
|
||||
model_to_dict,
|
||||
chunked,
|
||||
BattingCard,
|
||||
Player,
|
||||
query_to_csv,
|
||||
Team,
|
||||
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"
|
||||
@ -158,12 +150,11 @@ 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."
|
||||
)
|
||||
# elif not valid_token(token):
|
||||
# logging.warning(f'Bad Token: {token}')
|
||||
# logging.warning('Bad Token: [REDACTED]')
|
||||
# db.close()
|
||||
# raise HTTPException(
|
||||
# status_code=401,
|
||||
@ -195,7 +186,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 +197,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 +317,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 +328,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!): "
|
||||
@ -362,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401, detail="You are not authorized to calculate card ratings."
|
||||
)
|
||||
@ -399,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401, detail="You are not authorized to calculate basic ratings."
|
||||
)
|
||||
@ -646,21 +631,18 @@ 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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
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
|
||||
|
||||
|
||||
@ -672,8 +654,7 @@ async def get_player_ratings(
|
||||
token: str = Depends(oauth2_scheme),
|
||||
):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401, detail="You are not authorized to pull card ratings."
|
||||
)
|
||||
@ -694,15 +675,13 @@ 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
|
||||
|
||||
|
||||
@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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401, detail="You are not authorized to post card ratings."
|
||||
)
|
||||
@ -730,28 +709,24 @@ 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)}"
|
||||
|
||||
|
||||
@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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
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"
|
||||
|
||||
@ -5,15 +5,10 @@ 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
|
||||
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"])
|
||||
|
||||
@ -65,7 +60,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 +67,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,15 +91,13 @@ 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
|
||||
|
||||
|
||||
@router.put("")
|
||||
async def put_cards(cards: BattingCardList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post batting cards. This event has been logged.",
|
||||
@ -157,7 +147,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)}"
|
||||
|
||||
|
||||
@ -176,8 +165,7 @@ async def patch_card(
|
||||
token: str = Depends(oauth2_scheme),
|
||||
):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to patch batting cards. This event has been logged.",
|
||||
@ -185,7 +173,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 +198,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",
|
||||
@ -224,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete batting cards. This event has been logged.",
|
||||
@ -233,13 +217,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"
|
||||
@ -252,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete batting cards. This event has been logged.",
|
||||
|
||||
@ -4,15 +4,10 @@ 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
|
||||
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"])
|
||||
|
||||
@ -95,7 +90,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,21 +97,18 @@ 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
|
||||
|
||||
|
||||
@router.put("")
|
||||
async def put_positions(positions: PositionList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post card positions. This event has been logged.",
|
||||
@ -149,15 +140,13 @@ 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)}"
|
||||
|
||||
|
||||
@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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete card positions. This event has been logged.",
|
||||
@ -165,13 +154,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"
|
||||
|
||||
@ -4,14 +4,9 @@ import logging
|
||||
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 ..db_engine import db, Card, model_to_dict, Team, Player, Pack, Paperdex, CARDSETS, DoesNotExist
|
||||
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',
|
||||
@ -46,22 +41,19 @@ async def get_cards(
|
||||
if team_id is not None:
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
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:
|
||||
db.close()
|
||||
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:
|
||||
db.close()
|
||||
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:
|
||||
@ -108,7 +100,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 +118,6 @@ async def get_cards(
|
||||
|
||||
# return_val['cards'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -135,32 +125,27 @@ 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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No card found with id {card_id}')
|
||||
|
||||
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)
|
||||
|
||||
db.close()
|
||||
return Response(content=return_val, media_type='text/csv')
|
||||
|
||||
else:
|
||||
return_val = model_to_dict(this_card)
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post cards. This event has been logged.'
|
||||
@ -195,7 +180,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')
|
||||
|
||||
@ -203,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,
|
||||
@ -218,8 +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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='Unauthorized'
|
||||
@ -251,23 +234,20 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
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}')
|
||||
|
||||
|
||||
@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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete card lists. This event has been logged.'
|
||||
@ -280,8 +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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to wipe teams. This event has been logged.'
|
||||
@ -289,12 +268,11 @@ 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')
|
||||
|
||||
t_query = Card.update(team=None).where(Card.team == this_team).execute()
|
||||
db.close()
|
||||
return f'Wiped {t_query} cards'
|
||||
|
||||
|
||||
@ -304,16 +282,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch cards. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_card = Card.get_by_id(card_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No card found with id {card_id}')
|
||||
|
||||
if player_id is not None:
|
||||
@ -338,10 +314,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,20 +325,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete packs. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_card = Card.get_by_id(card_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -4,14 +4,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Cardset, model_to_dict, fn, Event
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import Cardset, model_to_dict, fn, Event, DoesNotExist
|
||||
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',
|
||||
@ -36,7 +31,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:
|
||||
@ -47,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:
|
||||
@ -56,7 +50,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 +65,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 +72,6 @@ async def get_cardsets(
|
||||
for x in all_cardsets:
|
||||
return_val['cardsets'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -113,9 +104,8 @@ 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}')
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Event id {event_id} not found')
|
||||
|
||||
# Convert to list for sorting
|
||||
@ -153,7 +143,6 @@ async def search_cardsets(
|
||||
'cardsets': [model_to_dict(x) for x in limited_results]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -161,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}')
|
||||
|
||||
if csv:
|
||||
@ -172,19 +160,16 @@ 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
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_cardsets(cardset: CardsetModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post cardsets. This event has been logged.'
|
||||
@ -192,7 +177,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 +184,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(
|
||||
@ -215,16 +198,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch cardsets. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_cardset = Cardset.get_by_id(cardset_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}')
|
||||
|
||||
if name is not None:
|
||||
@ -242,7 +223,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(
|
||||
@ -254,20 +234,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete cardsets. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_cardset = Cardset.get_by_id(cardset_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -4,14 +4,9 @@ from typing import Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Current, model_to_dict
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA
|
||||
from ..db_engine import Current, model_to_dict, DoesNotExist
|
||||
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',
|
||||
@ -40,11 +35,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
|
||||
|
||||
|
||||
@ -52,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No current found with id {current_id}')
|
||||
|
||||
if csv:
|
||||
@ -63,19 +55,16 @@ 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
|
||||
|
||||
|
||||
@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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post current. This event has been logged.'
|
||||
@ -83,7 +72,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 +84,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')
|
||||
@ -108,16 +95,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch current. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
current = Current.get_by_id(current_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No current found with id {current_id}')
|
||||
|
||||
if season is not None:
|
||||
@ -133,7 +118,6 @@ async def patch_current(
|
||||
|
||||
if current.save() == 1:
|
||||
return_val = model_to_dict(current)
|
||||
db.close()
|
||||
return return_val
|
||||
else:
|
||||
raise HTTPException(
|
||||
@ -145,20 +129,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete current. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_curr = Current.get_by_id(current_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -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,20 +10,14 @@ from ..db_engine import (
|
||||
StratGame,
|
||||
Player,
|
||||
model_to_dict,
|
||||
chunked,
|
||||
fn,
|
||||
Team,
|
||||
Card,
|
||||
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"])
|
||||
|
||||
@ -112,7 +105,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 +128,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 +180,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",
|
||||
@ -211,12 +201,11 @@ 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)
|
||||
if this_dec is None:
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"Decision ID {decision_id} not found"
|
||||
)
|
||||
@ -242,10 +231,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}"
|
||||
)
|
||||
@ -254,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 = []
|
||||
@ -277,7 +264,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"
|
||||
|
||||
@ -285,18 +271,16 @@ 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)
|
||||
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"
|
||||
@ -309,16 +293,14 @@ 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)
|
||||
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}"
|
||||
|
||||
@ -4,14 +4,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Event, model_to_dict, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import Event, model_to_dict, fn, DoesNotExist
|
||||
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',
|
||||
@ -54,7 +49,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 +56,6 @@ async def v1_events_get(
|
||||
for x in all_events:
|
||||
return_val['events'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -70,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No event found with id {event_id}')
|
||||
|
||||
if csv:
|
||||
@ -82,20 +74,17 @@ 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
|
||||
|
||||
|
||||
@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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post events. This event has been logged.'
|
||||
@ -103,7 +92,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 +106,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'
|
||||
@ -134,16 +120,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch events. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_event = Event.get_by_id(event_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No event found with id {event_id}')
|
||||
|
||||
if name is not None:
|
||||
@ -161,10 +145,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'
|
||||
@ -174,20 +156,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete events. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_event = Event.get_by_id(event_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -4,14 +4,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, GameRewards, model_to_dict
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import GameRewards, model_to_dict, DoesNotExist
|
||||
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',
|
||||
@ -54,7 +49,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 +56,6 @@ async def v1_gamerewards_get(
|
||||
for x in all_rewards:
|
||||
return_val['gamerewards'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -70,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No game reward found with id {gamereward_id}')
|
||||
|
||||
if csv:
|
||||
@ -82,20 +74,17 @@ 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
|
||||
|
||||
|
||||
@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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post game rewards. This event has been logged.'
|
||||
@ -111,10 +100,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'
|
||||
@ -126,16 +113,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch gamerewards. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_game_reward = GameRewards.get_by_id(game_reward_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No game reward found with id {game_reward_id}')
|
||||
|
||||
if name is not None:
|
||||
@ -158,7 +143,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(
|
||||
@ -170,20 +154,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete awards. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_award = GameRewards.get_by_id(gamereward_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -3,15 +3,10 @@ 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, DoesNotExist
|
||||
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"])
|
||||
|
||||
@ -55,7 +50,6 @@ async def v1_gauntletreward_get(
|
||||
for x in all_rewards:
|
||||
return_val["rewards"].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -63,15 +57,13 @@ async def v1_gauntletreward_get(
|
||||
async def v1_gauntletreward_get_one(gauntletreward_id):
|
||||
try:
|
||||
this_reward = GauntletReward.get_by_id(gauntletreward_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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
|
||||
|
||||
|
||||
@ -86,8 +78,7 @@ async def v1_gauntletreward_patch(
|
||||
token: str = Depends(oauth2_scheme),
|
||||
):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to patch gauntlet rewards. This event has been logged.",
|
||||
@ -95,7 +86,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 +101,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}")
|
||||
|
||||
|
||||
@ -123,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post gauntlets. This event has been logged.",
|
||||
@ -137,7 +124,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"
|
||||
|
||||
|
||||
@ -4,14 +4,9 @@ from typing import Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, GauntletRun, model_to_dict, DatabaseError
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import GauntletRun, model_to_dict, DatabaseError, DoesNotExist
|
||||
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',
|
||||
@ -82,7 +77,6 @@ async def get_gauntletruns(
|
||||
for x in all_gauntlets:
|
||||
return_val['runs'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -90,12 +84,10 @@ async def get_gauntletruns(
|
||||
async def get_one_gauntletrun(gauntletrun_id):
|
||||
try:
|
||||
this_gauntlet = GauntletRun.get_by_id(gauntletrun_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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
|
||||
|
||||
|
||||
@ -105,8 +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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch gauntlet runs. This event has been logged.'
|
||||
@ -114,7 +105,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,18 +128,15 @@ 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}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_gauntletrun(gauntletrun: GauntletRunModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post gauntlets. This event has been logged.'
|
||||
@ -169,10 +156,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')
|
||||
|
||||
|
||||
|
||||
@ -5,27 +5,19 @@ 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
|
||||
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"])
|
||||
|
||||
@ -37,7 +29,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):
|
||||
@ -111,14 +103,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 +116,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
|
||||
|
||||
|
||||
@ -149,8 +137,7 @@ async def patch_player(
|
||||
token: str = Depends(oauth2_scheme),
|
||||
):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to patch mlb players. This event has been logged.",
|
||||
@ -158,7 +145,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 +166,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",
|
||||
@ -193,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post mlb players. This event has been logged.",
|
||||
@ -209,7 +192,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 +203,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"
|
||||
|
||||
@ -229,8 +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}")
|
||||
db.close()
|
||||
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 +225,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 +234,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(
|
||||
@ -267,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete mlb players. This event has been logged.",
|
||||
@ -276,13 +253,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(
|
||||
@ -300,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to update mlb players. This event has been logged.",
|
||||
@ -327,7 +301,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"
|
||||
|
||||
|
||||
@ -337,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to update mlb players. This event has been logged.",
|
||||
@ -360,14 +332,13 @@ 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"
|
||||
|
||||
|
||||
# @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,
|
||||
|
||||
@ -5,14 +5,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Notification, model_to_dict, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import Notification, model_to_dict, fn, DoesNotExist
|
||||
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',
|
||||
@ -38,7 +33,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 +60,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 +67,6 @@ async def get_notifs(
|
||||
for x in all_notif:
|
||||
return_val['notifs'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -82,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}')
|
||||
|
||||
if csv:
|
||||
@ -94,20 +85,17 @@ 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
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_notif(notif: NotifModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post notifications. This event has been logged.'
|
||||
@ -126,10 +114,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'
|
||||
@ -142,16 +128,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch notifications. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_notif = Notification.get_by_id(notif_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}')
|
||||
|
||||
if title is not None:
|
||||
@ -171,7 +155,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(
|
||||
@ -183,20 +166,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete notifications. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_notif = Notification.get_by_id(notif_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -6,14 +6,9 @@ import logging
|
||||
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 ..db_engine import db, Cardset, model_to_dict, Pack, Team, PackType, DoesNotExist
|
||||
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',
|
||||
@ -41,29 +36,25 @@ 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()
|
||||
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:
|
||||
db.close()
|
||||
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:
|
||||
db.close()
|
||||
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:
|
||||
@ -72,8 +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:
|
||||
db.close()
|
||||
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:
|
||||
@ -103,7 +93,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,16 +100,14 @@ async def get_packs(
|
||||
for x in all_packs:
|
||||
return_val['packs'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}')
|
||||
|
||||
if csv:
|
||||
@ -131,20 +118,17 @@ 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
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_pack(packs: PackModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post packs. This event has been logged.'
|
||||
@ -163,7 +147,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')
|
||||
|
||||
@ -171,8 +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}')
|
||||
db.close()
|
||||
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 +171,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(
|
||||
@ -203,16 +184,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch packs. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_pack = Pack.get_by_id(pack_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}')
|
||||
|
||||
if team_id is not None:
|
||||
@ -237,7 +216,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(
|
||||
@ -249,20 +227,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete packs. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_pack = Pack.get_by_id(pack_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -4,14 +4,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, PackType, model_to_dict, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import PackType, model_to_dict, fn, DoesNotExist
|
||||
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',
|
||||
@ -34,7 +29,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 +54,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 +61,6 @@ async def get_packtypes(
|
||||
for x in all_packtypes:
|
||||
return_val['packtypes'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -76,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}')
|
||||
|
||||
if csv:
|
||||
@ -87,20 +78,17 @@ 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
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_packtypes(packtype: PacktypeModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post packtypes. This event has been logged.'
|
||||
@ -108,7 +96,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 +109,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(
|
||||
@ -136,16 +122,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch packtypes. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_packtype = PackType.get_by_id(packtype_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}')
|
||||
|
||||
if name is not None:
|
||||
@ -161,7 +145,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(
|
||||
@ -173,20 +156,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete packtypes. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_packtype = PackType.get_by_id(packtype_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -5,14 +5,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Paperdex, model_to_dict, Player, Cardset, Team
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import Paperdex, model_to_dict, Player, Cardset, Team, DoesNotExist
|
||||
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',
|
||||
@ -34,7 +29,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 +61,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 +68,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
|
||||
|
||||
|
||||
@ -83,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}')
|
||||
|
||||
if csv:
|
||||
@ -94,20 +85,17 @@ 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
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_paperdex(paperdex: PaperdexModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post paperdex. This event has been logged.'
|
||||
@ -116,7 +104,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 +115,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(
|
||||
@ -142,16 +128,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch paperdex. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_dex = Paperdex.get_by_id(paperdex_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}')
|
||||
|
||||
if team_id is not None:
|
||||
@ -163,7 +147,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(
|
||||
@ -175,20 +158,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete rewards. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_dex = Paperdex.get_by_id(paperdex_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
@ -199,8 +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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='Unauthorized'
|
||||
|
||||
@ -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,
|
||||
@ -20,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"
|
||||
@ -152,8 +146,7 @@ async def get_card_ratings(
|
||||
token: str = Depends(oauth2_scheme),
|
||||
):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401, detail="You are not authorized to pull card ratings."
|
||||
)
|
||||
@ -177,7 +170,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 +179,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 +237,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 +248,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!): "
|
||||
@ -280,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401, detail="You are not authorized to calculate card ratings."
|
||||
)
|
||||
@ -317,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401, detail="You are not authorized to calculate basic ratings."
|
||||
)
|
||||
@ -497,21 +484,18 @@ 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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
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,15 +519,13 @@ 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
|
||||
|
||||
|
||||
@router.put("")
|
||||
async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401, detail="You are not authorized to post card ratings."
|
||||
)
|
||||
@ -571,28 +553,24 @@ 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)}"
|
||||
|
||||
|
||||
@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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
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"
|
||||
|
||||
@ -5,15 +5,10 @@ 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
|
||||
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"])
|
||||
|
||||
@ -62,7 +57,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 +64,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,15 +88,13 @@ 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
|
||||
|
||||
|
||||
@router.put("")
|
||||
async def put_cards(cards: PitchingCardList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post pitching cards. This event has been logged.",
|
||||
@ -153,7 +143,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)}"
|
||||
|
||||
|
||||
@ -170,8 +159,7 @@ async def patch_card(
|
||||
token: str = Depends(oauth2_scheme),
|
||||
):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to patch pitching cards. This event has been logged.",
|
||||
@ -179,7 +167,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 +188,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",
|
||||
@ -214,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete pitching cards. This event has been logged.",
|
||||
@ -223,11 +207,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"
|
||||
@ -240,8 +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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete pitching cards. This event has been logged.",
|
||||
|
||||
@ -5,14 +5,9 @@ import logging
|
||||
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 ..db_engine import db, PitchingStat, model_to_dict, Card, Player, Current, DoesNotExist
|
||||
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',
|
||||
@ -108,7 +103,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,15 +110,13 @@ 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
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_pitstat(stats: PitchingStatModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post stats. This event has been logged.'
|
||||
@ -164,7 +156,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')
|
||||
|
||||
@ -172,20 +163,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete stats. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_stat = PitchingStat.get_by_id(stat_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -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
|
||||
@ -14,12 +12,11 @@ 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,
|
||||
fn,
|
||||
chunked,
|
||||
Paperdex,
|
||||
Cardset,
|
||||
Rarity,
|
||||
@ -29,9 +26,10 @@ from ..db_engine import (
|
||||
PitchingCardRatings,
|
||||
CardPosition,
|
||||
MlbPlayer,
|
||||
DoesNotExist,
|
||||
)
|
||||
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')
|
||||
@ -76,11 +74,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"])
|
||||
|
||||
@ -152,7 +145,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 +231,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 +319,6 @@ async def get_players(
|
||||
|
||||
# return_val['players'].append(model_to_dict(x, recurse=not flat))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -479,7 +469,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 +486,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,16 +579,14 @@ async def search_players(
|
||||
|
||||
return_val["players"].append(this_record)
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"No player found with id {player_id}"
|
||||
)
|
||||
@ -632,7 +618,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,
|
||||
@ -659,8 +644,8 @@ 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)
|
||||
|
||||
db.close()
|
||||
return Response(content=return_val, media_type="text/csv")
|
||||
else:
|
||||
return_val = model_to_dict(this_player)
|
||||
@ -668,7 +653,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
|
||||
|
||||
|
||||
@ -685,8 +669,7 @@ async def get_batter_card(
|
||||
):
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"No player found with id {player_id}"
|
||||
)
|
||||
@ -701,7 +684,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 +770,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 +824,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)
|
||||
|
||||
|
||||
@ -881,8 +861,7 @@ async def v1_players_patch(
|
||||
token: str = Depends(oauth2_scheme),
|
||||
):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to patch players. This event has been logged.",
|
||||
@ -890,8 +869,7 @@ async def v1_players_patch(
|
||||
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"No player found with id {player_id}"
|
||||
)
|
||||
@ -914,8 +892,7 @@ async def v1_players_patch(
|
||||
if cardset_id is not None:
|
||||
try:
|
||||
this_cardset = Cardset.get_by_id(cardset_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"No cardset found with id {cardset_id}"
|
||||
)
|
||||
@ -923,8 +900,7 @@ async def v1_players_patch(
|
||||
if rarity_id is not None:
|
||||
try:
|
||||
this_rarity = Rarity.get_by_id(rarity_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"No rarity found with id {rarity_id}"
|
||||
)
|
||||
@ -986,7 +962,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(
|
||||
@ -998,8 +973,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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post players. This event has been logged.",
|
||||
@ -1068,7 +1042,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(
|
||||
@ -1079,8 +1052,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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post players. This event has been logged.",
|
||||
@ -1091,7 +1063,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 +1075,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
|
||||
|
||||
|
||||
@ -1113,8 +1083,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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to modify players. This event has been logged.",
|
||||
@ -1122,7 +1091,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,15 +1111,13 @@ async def post_image_reset(
|
||||
|
||||
this_player.save()
|
||||
r_player = model_to_dict(this_player)
|
||||
db.close()
|
||||
return r_player
|
||||
|
||||
|
||||
@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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete players. This event has been logged.",
|
||||
@ -1159,14 +1125,12 @@ async def delete_player(player_id, token: str = Depends(oauth2_scheme)):
|
||||
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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(
|
||||
|
||||
@ -4,14 +4,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Rarity, model_to_dict, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import Rarity, model_to_dict, fn, DoesNotExist
|
||||
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',
|
||||
@ -31,7 +26,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 +38,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 +50,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 +57,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
|
||||
|
||||
|
||||
@ -73,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}')
|
||||
|
||||
if csv:
|
||||
@ -87,19 +77,16 @@ 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
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_rarity(rarity: RarityModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post rarities. This event has been logged.'
|
||||
@ -107,7 +94,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 +105,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(
|
||||
@ -133,16 +118,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch rarities. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_rarity = Rarity.get_by_id(rarity_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}')
|
||||
|
||||
if value is not None:
|
||||
@ -154,7 +137,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(
|
||||
@ -166,20 +148,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete rarities. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_rarity = Rarity.get_by_id(rarity_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -4,14 +4,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Result, model_to_dict, Team, DataError
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import Result, model_to_dict, Team, DataError, DoesNotExist
|
||||
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',
|
||||
@ -55,32 +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:
|
||||
db.close()
|
||||
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:
|
||||
db.close()
|
||||
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:
|
||||
db.close()
|
||||
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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_two_id}')
|
||||
|
||||
if away_score_min is not None:
|
||||
@ -153,7 +144,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 +151,6 @@ async def get_results(
|
||||
for x in all_results:
|
||||
return_val['results'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@ -169,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No result found with id {result_id}')
|
||||
|
||||
if csv:
|
||||
@ -184,12 +172,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
|
||||
|
||||
|
||||
@ -199,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')
|
||||
|
||||
@ -243,7 +229,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,15 +239,13 @@ async def get_team_results(
|
||||
'casual_wins': c_wins,
|
||||
'casual_losses': c_loss,
|
||||
}
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_result(result: ResultModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post results. This event has been logged.'
|
||||
@ -273,12 +256,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 +309,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'
|
||||
@ -346,16 +325,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch results. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_result = Result.get_by_id(result_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No result found with id {result_id}')
|
||||
|
||||
if away_team_id is not None:
|
||||
@ -396,10 +373,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'
|
||||
@ -409,20 +384,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post results. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_result = Result.get_by_id(result_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
@ -5,14 +5,9 @@ import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Reward, model_to_dict, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import Reward, model_to_dict, fn, DoesNotExist
|
||||
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',
|
||||
@ -36,7 +31,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 +49,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 +61,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 +68,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
|
||||
|
||||
|
||||
@ -84,8 +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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}')
|
||||
|
||||
if csv:
|
||||
@ -95,20 +85,17 @@ 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
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_rewards(reward: RewardModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
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 +110,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(
|
||||
@ -137,16 +123,14 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch rewards. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_reward = Reward.get_by_id(reward_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}')
|
||||
|
||||
if name is not None:
|
||||
@ -159,7 +143,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(
|
||||
@ -171,20 +154,17 @@ 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}')
|
||||
db.close()
|
||||
logging.warning('Bad Token: [REDACTED]')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete rewards. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_reward = Reward.get_by_id(reward_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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')
|
||||
|
||||
91
app/routers_v2/scout_claims.py
Normal file
91
app/routers_v2/scout_claims.py
Normal 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"
|
||||
)
|
||||
123
app/routers_v2/scout_opportunities.py
Normal file
123
app/routers_v2/scout_opportunities.py
Normal 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",
|
||||
)
|
||||
@ -1,33 +1,20 @@
|
||||
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
|
||||
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',
|
||||
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:
|
||||
@ -37,66 +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
|
||||
]}
|
||||
db.close()
|
||||
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(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to initiate live updates.'
|
||||
)
|
||||
|
||||
data = {} # <fg id>: { '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(f'Bad Token: {token}')
|
||||
db.close()
|
||||
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()
|
||||
|
||||
@ -1,18 +1,12 @@
|
||||
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 ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
from ..db_engine import StratGame, model_to_dict, fn
|
||||
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',
|
||||
@ -83,7 +77,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 +87,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 +94,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
|
||||
|
||||
|
||||
@ -115,12 +105,11 @@ 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)
|
||||
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,17 +121,15 @@ 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}')
|
||||
|
||||
|
||||
@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())
|
||||
@ -150,7 +137,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(
|
||||
@ -162,16 +148,14 @@ 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)
|
||||
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'
|
||||
|
||||
@ -13,21 +13,14 @@ 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
|
||||
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"])
|
||||
|
||||
@ -367,7 +360,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 +368,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 +799,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 +1164,6 @@ async def get_pitching_totals(
|
||||
"rbi%": rbi_rate,
|
||||
}
|
||||
)
|
||||
db.close()
|
||||
|
||||
if csv:
|
||||
return_vals = return_stats["stats"]
|
||||
@ -1209,7 +1197,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 +1214,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)
|
||||
@ -1404,12 +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:
|
||||
db.close()
|
||||
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))
|
||||
db.close()
|
||||
return r_play
|
||||
return model_to_dict(play)
|
||||
|
||||
|
||||
@router.patch("/{play_id}")
|
||||
@ -1417,23 +1401,21 @@ 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:
|
||||
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
|
||||
|
||||
|
||||
@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 = []
|
||||
@ -1476,7 +1458,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"
|
||||
|
||||
@ -1484,16 +1465,14 @@ 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)
|
||||
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"
|
||||
@ -1506,16 +1485,14 @@ 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)
|
||||
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}"
|
||||
|
||||
@ -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,20 +31,14 @@ from ..db_engine import (
|
||||
PitchingCardRatings,
|
||||
StratGame,
|
||||
LIVE_PROMO_CARDSET_ID,
|
||||
DoesNotExist,
|
||||
)
|
||||
from ..dependencies import (
|
||||
oauth2_scheme,
|
||||
valid_token,
|
||||
LOG_DATA,
|
||||
int_timestamp,
|
||||
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"])
|
||||
|
||||
@ -139,9 +133,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:
|
||||
@ -150,7 +141,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)
|
||||
@ -163,7 +157,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:
|
||||
@ -171,16 +164,14 @@ async def get_teams(
|
||||
for x in all_teams:
|
||||
return_teams["teams"].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f"No team found with id {team_id}")
|
||||
|
||||
p_query = Pack.select().where(
|
||||
@ -195,7 +186,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
|
||||
|
||||
|
||||
@ -284,8 +274,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"]
|
||||
ops_vr = df_data["obp_vr"] + df_data["slg_vr"]
|
||||
@ -311,11 +299,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",
|
||||
@ -329,7 +315,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",
|
||||
@ -370,17 +355,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),
|
||||
@ -596,16 +599,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??
|
||||
@ -628,11 +636,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",
|
||||
@ -643,7 +649,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",
|
||||
@ -676,16 +681,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
|
||||
@ -707,13 +717,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
|
||||
@ -726,13 +734,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(
|
||||
@ -755,11 +761,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",
|
||||
@ -773,7 +777,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",
|
||||
@ -845,7 +848,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":
|
||||
@ -870,7 +872,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:
|
||||
@ -904,7 +905,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":
|
||||
@ -929,7 +929,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")
|
||||
@ -945,7 +944,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}")
|
||||
@ -1031,7 +1029,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
|
||||
|
||||
|
||||
@ -1039,13 +1036,11 @@ 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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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.",
|
||||
@ -1061,8 +1056,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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"No player found with id {player_id} /// "
|
||||
@ -1075,7 +1069,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 "
|
||||
@ -1131,21 +1124,18 @@ async def team_buy_packs(
|
||||
):
|
||||
try:
|
||||
this_packtype = PackType.get_by_id(packtype_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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()}"
|
||||
@ -1158,7 +1148,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 "
|
||||
@ -1186,7 +1175,6 @@ async def team_buy_packs(
|
||||
|
||||
with db.atomic():
|
||||
Pack.bulk_create(all_packs, batch_size=15)
|
||||
db.close()
|
||||
|
||||
raise HTTPException(
|
||||
status_code=200,
|
||||
@ -1199,13 +1187,11 @@ 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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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.",
|
||||
@ -1219,8 +1205,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:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"No card found with id {card_id}"
|
||||
)
|
||||
@ -1288,12 +1273,10 @@ async def get_team_cards(team_id, csv: Optional[bool] = True):
|
||||
"""
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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.",
|
||||
@ -1307,11 +1290,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"])
|
||||
@ -1355,8 +1336,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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post teams. This event has been logged.",
|
||||
@ -1364,7 +1344,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}",
|
||||
@ -1392,7 +1371,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(
|
||||
@ -1404,8 +1382,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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to post teams. This event has been logged.",
|
||||
@ -1417,7 +1394,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}"
|
||||
@ -1429,8 +1405,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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to adjust wallets. This event has been logged.",
|
||||
@ -1438,15 +1413,13 @@ async def team_update_money(
|
||||
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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(
|
||||
@ -1477,16 +1450,14 @@ async def patch_team(
|
||||
abbrev: Optional[str] = None,
|
||||
):
|
||||
if not valid_token(token):
|
||||
logging.warning(f"Bad Token: {token}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete teams. This event has been logged.",
|
||||
)
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
raise HTTPException(status_code=404, detail=f"No team found with id {team_id}")
|
||||
|
||||
if abbrev is not None:
|
||||
@ -1530,7 +1501,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(
|
||||
@ -1542,20 +1512,17 @@ 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}")
|
||||
db.close()
|
||||
logging.warning("Bad Token: [REDACTED]")
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="You are not authorized to delete teams. This event has been logged.",
|
||||
)
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
except DoesNotExist:
|
||||
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")
|
||||
|
||||
57
migrations/2026-03-04_add_scout_opportunities_claims.sql
Normal file
57
migrations/2026-03-04_add_scout_opportunities_claims.sql
Normal 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;
|
||||
65
migrations/migrate_roster_junction_table.py
Normal file
65
migrations/migrate_roster_junction_table.py
Normal file
@ -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()
|
||||
Loading…
Reference in New Issue
Block a user