commit
92ba5129a1
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
3079
app/card_creation.py
Normal file
3079
app/card_creation.py
Normal file
File diff suppressed because one or more lines are too long
1005
app/db_engine.py
Normal file
1005
app/db_engine.py
Normal file
File diff suppressed because it is too large
Load Diff
195
app/dependencies.py
Normal file
195
app/dependencies.py
Normal file
@ -0,0 +1,195 @@
|
||||
import datetime
|
||||
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/'
|
||||
AUTH_TOKEN = f'{os.environ.get("API_TOKEN")}'
|
||||
AUTH_HEADER = {'Authorization': f'Bearer {AUTH_TOKEN}'}
|
||||
|
||||
|
||||
if os.environ.get('TESTING') == 'False':
|
||||
DB_URL = 'https://pddev.manticorum.com/api/'
|
||||
|
||||
|
||||
def valid_token(token):
|
||||
return 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):
|
||||
return round(base * round(float(x) / base), prec)
|
||||
|
||||
|
||||
def param_char(other_params):
|
||||
if other_params:
|
||||
return '&'
|
||||
else:
|
||||
return '?'
|
||||
|
||||
|
||||
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]}'
|
||||
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):
|
||||
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}'
|
||||
logging.info(log_string) if master_debug else logging.debug(log_string)
|
||||
|
||||
retries = 0
|
||||
while True:
|
||||
try:
|
||||
resp = requests.get(req_url, timeout=timeout)
|
||||
break
|
||||
except requests.ReadTimeout as e:
|
||||
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.')
|
||||
timeout += [2, 5][retries]
|
||||
retries += 1
|
||||
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
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 ""}')
|
||||
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}'
|
||||
if master_debug:
|
||||
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 ""}')
|
||||
return None
|
||||
else:
|
||||
logging.warning(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):
|
||||
req_url = get_req_url(endpoint, api_ver=api_ver, object_id=object_id, params=params)
|
||||
log_string = f'patch:\n{endpoint} {params}'
|
||||
logging.info(log_string) if master_debug else logging.debug(log_string)
|
||||
|
||||
retries = 0
|
||||
while True:
|
||||
try:
|
||||
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}')
|
||||
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.')
|
||||
timeout += [min(3, timeout), min(5, timeout)][retries]
|
||||
retries += 1
|
||||
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
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 ""}')
|
||||
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}')
|
||||
|
||||
|
||||
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)}'
|
||||
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)
|
||||
break
|
||||
except requests.Timeout as e:
|
||||
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.')
|
||||
timeout += [min(3, timeout), min(5, timeout)][retries]
|
||||
retries += 1
|
||||
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
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 ""}')
|
||||
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}')
|
||||
|
||||
|
||||
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}'
|
||||
logging.info(log_string) if master_debug else logging.debug(log_string)
|
||||
|
||||
retries = 0
|
||||
while True:
|
||||
try:
|
||||
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}')
|
||||
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.')
|
||||
timeout += [min(3, timeout), min(5, timeout)][retries]
|
||||
retries += 1
|
||||
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
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 ""}')
|
||||
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}')
|
||||
46
app/main.py
Normal file
46
app/main.py
Normal file
@ -0,0 +1,46 @@
|
||||
import os
|
||||
|
||||
from fastapi import FastAPI
|
||||
# from fastapi.staticfiles import StaticFiles
|
||||
# from fastapi.templating import Jinja2Templates
|
||||
|
||||
from .routers_v2 import (
|
||||
current, teams, rarity, cardsets, players, packtypes, packs, cards, events, results, rewards, decisions,
|
||||
batstats, pitstats, notifications, paperdex, gamerewards, gauntletrewards, gauntletruns, battingcards,
|
||||
battingcardratings, pitchingcards, pitchingcardratings, cardpositions, scouting, mlbplayers, stratgame, stratplays)
|
||||
|
||||
app = FastAPI(
|
||||
responses={404: {'description': 'Not found'}}
|
||||
)
|
||||
|
||||
# app.mount("/static", StaticFiles(directory="storage/static"), name="static")
|
||||
# templates = Jinja2Templates(directory=os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
app.include_router(current.router)
|
||||
app.include_router(teams.router)
|
||||
app.include_router(rarity.router)
|
||||
app.include_router(cardsets.router)
|
||||
app.include_router(players.router)
|
||||
app.include_router(packtypes.router)
|
||||
app.include_router(packs.router)
|
||||
app.include_router(cards.router)
|
||||
app.include_router(events.router)
|
||||
app.include_router(results.router)
|
||||
app.include_router(rewards.router)
|
||||
app.include_router(batstats.router)
|
||||
app.include_router(pitstats.router)
|
||||
app.include_router(notifications.router)
|
||||
app.include_router(paperdex.router)
|
||||
app.include_router(gamerewards.router)
|
||||
app.include_router(gauntletrewards.router)
|
||||
app.include_router(gauntletruns.router)
|
||||
app.include_router(battingcards.router)
|
||||
app.include_router(battingcardratings.router)
|
||||
app.include_router(pitchingcards.router)
|
||||
app.include_router(pitchingcardratings.router)
|
||||
app.include_router(cardpositions.router)
|
||||
app.include_router(scouting.router)
|
||||
app.include_router(mlbplayers.router)
|
||||
app.include_router(stratgame.router)
|
||||
app.include_router(stratplays.router)
|
||||
app.include_router(decisions.router)
|
||||
27
app/player_scouting.py
Normal file
27
app/player_scouting.py
Normal file
@ -0,0 +1,27 @@
|
||||
from typing import Literal, Optional
|
||||
from pybaseball import playerid_reverse_lookup
|
||||
|
||||
import pydantic
|
||||
|
||||
|
||||
class PlayerIds(pydantic.BaseModel):
|
||||
bbref: str = None
|
||||
fangraphs: int = None
|
||||
retro: str = None
|
||||
mlbam: int = None
|
||||
|
||||
|
||||
def get_player_ids(player_id: str, id_type: Literal['bbref', 'fangraphs']) -> PlayerIds | None:
|
||||
q = playerid_reverse_lookup([player_id], key_type=id_type)
|
||||
if len(q.values) == 0:
|
||||
return None
|
||||
else:
|
||||
return PlayerIds(
|
||||
bbref=q.loc[0].key_bbref,
|
||||
fangraphs=q.loc[0].key_fangraphs,
|
||||
retro=q.loc[0].key_retro,
|
||||
mlbam=q.loc[0].key_mlbam
|
||||
)
|
||||
|
||||
|
||||
|
||||
0
app/routers_v2/__init__.py
Normal file
0
app/routers_v2/__init__.py
Normal file
35
app/routers_v2/admin.py
Normal file
35
app/routers_v2/admin.py
Normal file
@ -0,0 +1,35 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
import logging
|
||||
|
||||
from ..db_engine import db, Player
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/admin',
|
||||
tags=['admin']
|
||||
)
|
||||
|
||||
|
||||
@router.post('/stl-fix')
|
||||
async def stl_cardinals_fix(token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post. This event has been logged.'
|
||||
)
|
||||
|
||||
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'}
|
||||
|
||||
155
app/routers_v2/awards.py
Normal file
155
app/routers_v2/awards.py
Normal file
@ -0,0 +1,155 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/awards',
|
||||
tags=['awards']
|
||||
)
|
||||
|
||||
|
||||
class AwardModel(pydantic.BaseModel):
|
||||
name: str
|
||||
season: int
|
||||
timing: str = 'In-Season'
|
||||
card_id: Optional[int] = None
|
||||
team_id: Optional[int] = None
|
||||
image: Optional[str] = None
|
||||
|
||||
|
||||
@app.get('/api/v1/awards')
|
||||
async def get_awards(
|
||||
name: Optional[str] = None, season: Optional[int] = None, timing: Optional[str] = None,
|
||||
card_id: Optional[int] = None, team_id: Optional[int] = None, image: Optional[str] = None,
|
||||
csv: Optional[bool] = None):
|
||||
all_awards = Award.select()
|
||||
|
||||
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:
|
||||
all_awards = all_awards.where(Award.name == name)
|
||||
if season is not None:
|
||||
all_awards = all_awards.where(Award.season == season)
|
||||
if timing is not None:
|
||||
all_awards = all_awards.where(Award.timing == timing)
|
||||
if card_id is not None:
|
||||
all_awards = all_awards.where(Award.card_id == card_id)
|
||||
if team_id is not None:
|
||||
all_awards = all_awards.where(Award.team_id == team_id)
|
||||
if image is not None:
|
||||
all_awards = all_awards.where(Award.image == image)
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'name', 'season', 'timing', 'card', 'team', 'image']]
|
||||
for line in all_awards:
|
||||
data_list.append([
|
||||
line.id, line.name, line.season, line.timing, line.card, line.team, line.image
|
||||
])
|
||||
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 = {'count': all_awards.count(), 'awards': []}
|
||||
for x in all_awards:
|
||||
return_val['awards'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@app.get('/api/v1/awards/{award_id}')
|
||||
async def get_one_award(award_id, csv: Optional[bool] = None):
|
||||
try:
|
||||
this_award = Award.get_by_id(award_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No award found with id {award_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'name', 'season', 'timing', 'card', 'team', 'image'],
|
||||
[this_award.id, this_award.name, this_award.season, this_award.timing, this_award.card,
|
||||
this_award.team, this_award.image]
|
||||
]
|
||||
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
|
||||
|
||||
|
||||
@app.post('/api/v1/awards')
|
||||
async def post_awards(award: AwardModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post awards. This event has been logged.'
|
||||
)
|
||||
|
||||
this_award = Award(
|
||||
name=award.name,
|
||||
season=award.season,
|
||||
timing=award.season,
|
||||
card_id=award.card_id,
|
||||
team_id=award.team_id,
|
||||
image=award.image
|
||||
)
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@app.delete('/api/v1/awards/{award_id}')
|
||||
async def delete_award(award_id, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete awards. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_award = Award.get_by_id(award_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No award found with id {award_id}')
|
||||
|
||||
count = this_award.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
raise HTTPException(status_code=200, detail=f'Award {award_id} has been deleted')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Award {award_id} was not deleted')
|
||||
|
||||
|
||||
459
app/routers_v2/batstats.py
Normal file
459
app/routers_v2/batstats.py
Normal file
@ -0,0 +1,459 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional, List
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/batstats',
|
||||
tags=['batstats']
|
||||
)
|
||||
|
||||
|
||||
class BatStat(pydantic.BaseModel):
|
||||
card_id: int
|
||||
team_id: int
|
||||
roster_num: int
|
||||
vs_team_id: int
|
||||
pos: str
|
||||
pa: Optional[int] = 0
|
||||
ab: Optional[int] = 0
|
||||
run: Optional[int] = 0
|
||||
hit: Optional[int] = 0
|
||||
rbi: Optional[int] = 0
|
||||
double: Optional[int] = 0
|
||||
triple: Optional[int] = 0
|
||||
hr: Optional[int] = 0
|
||||
bb: Optional[int] = 0
|
||||
so: Optional[int] = 0
|
||||
hbp: Optional[int] = 0
|
||||
sac: Optional[int] = 0
|
||||
ibb: Optional[int] = 0
|
||||
gidp: Optional[int] = 0
|
||||
sb: Optional[int] = 0
|
||||
cs: Optional[int] = 0
|
||||
bphr: Optional[int] = 0
|
||||
bpfo: Optional[int] = 0
|
||||
bp1b: Optional[int] = 0
|
||||
bplo: Optional[int] = 0
|
||||
xch: Optional[int] = 0
|
||||
xhit: Optional[int] = 0
|
||||
error: Optional[int] = 0
|
||||
pb: Optional[int] = 0
|
||||
sbc: Optional[int] = 0
|
||||
csc: Optional[int] = 0
|
||||
week: int
|
||||
season: int
|
||||
created: Optional[int] = int(datetime.timestamp(datetime.now())*100000)
|
||||
game_id: int
|
||||
|
||||
|
||||
class BattingStatModel(pydantic.BaseModel):
|
||||
stats: List[BatStat]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_batstats(
|
||||
card_id: int = None, player_id: int = None, team_id: int = None, vs_team_id: int = None, week: int = None,
|
||||
season: int = None, week_start: int = None, week_end: int = None, created: int = None, csv: bool = None):
|
||||
all_stats = BattingStat.select().join(Card).join(Player)
|
||||
|
||||
if season is not None:
|
||||
all_stats = all_stats.where(BattingStat.season == season)
|
||||
else:
|
||||
curr = Current.latest()
|
||||
all_stats = all_stats.where(BattingStat.season == curr.season)
|
||||
|
||||
if card_id is not None:
|
||||
all_stats = all_stats.where(BattingStat.card_id == card_id)
|
||||
if player_id is not None:
|
||||
all_stats = all_stats.where(BattingStat.card.player.player_id == player_id)
|
||||
if team_id is not None:
|
||||
all_stats = all_stats.where(BattingStat.team_id == team_id)
|
||||
if vs_team_id is not None:
|
||||
all_stats = all_stats.where(BattingStat.vs_team_id == vs_team_id)
|
||||
if week is not None:
|
||||
all_stats = all_stats.where(BattingStat.week == week)
|
||||
if week_start is not None:
|
||||
all_stats = all_stats.where(BattingStat.week >= week_start)
|
||||
if week_end is not None:
|
||||
all_stats = all_stats.where(BattingStat.week <= week_end)
|
||||
if created is not None:
|
||||
all_stats = all_stats.where(BattingStat.created == created)
|
||||
|
||||
# if all_stats.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No batting stats found')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'card_id', 'player_id', 'cardset', 'team', 'vs_team', 'pos', 'pa', 'ab', 'run', 'hit', 'rbi', 'double',
|
||||
'triple', 'hr', 'bb', 'so', 'hbp', 'sac', 'ibb', 'gidp', 'sb', 'cs', 'bphr', 'bpfo', 'bp1b',
|
||||
'bplo', 'xch', 'xhit', 'error', 'pb', 'sbc', 'csc', 'week', 'season', 'created', 'game_id', 'roster_num']]
|
||||
for line in all_stats:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.card.id, line.card.player.player_id, line.card.player.cardset.name, line.team.abbrev, line.vs_team.abbrev,
|
||||
line.pos, line.pa, line.ab, line.run, line.hit, line.rbi, line.double, line.triple, line.hr,
|
||||
line.bb, line.so, line.hbp, line.sac, line.ibb, line.gidp, line.sb, line.cs, line.bphr, line.bpfo,
|
||||
line.bp1b, line.bplo, line.xch, line.xhit, line.error, line.pb, line.sbc, line.csc, line.week,
|
||||
line.season, line.created, line.game_id, line.roster_num
|
||||
]
|
||||
)
|
||||
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 = {'count': all_stats.count(), 'stats': []}
|
||||
for x in all_stats:
|
||||
return_val['stats'].append(model_to_dict(x, recurse=False))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/player/{player_id}')
|
||||
async def get_player_stats(
|
||||
player_id: int, team_id: int = None, vs_team_id: int = None, week_start: int = None, week_end: int = None,
|
||||
csv: bool = None):
|
||||
all_stats = (BattingStat
|
||||
.select(fn.COUNT(BattingStat.created).alias('game_count'))
|
||||
.join(Card)
|
||||
.group_by(BattingStat.card)
|
||||
.where(BattingStat.card.player == player_id)).scalar()
|
||||
|
||||
if team_id is not None:
|
||||
all_stats = all_stats.where(BattingStat.team_id == team_id)
|
||||
if vs_team_id is not None:
|
||||
all_stats = all_stats.where(BattingStat.vs_team_id == vs_team_id)
|
||||
if week_start is not None:
|
||||
all_stats = all_stats.where(BattingStat.week >= week_start)
|
||||
if week_end is not None:
|
||||
all_stats = all_stats.where(BattingStat.week <= week_end)
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
[
|
||||
'pa', 'ab', 'run', 'hit', 'rbi', 'double', 'triple', 'hr', 'bb', 'so', 'hbp', 'sac', 'ibb', 'gidp',
|
||||
'sb', 'cs', 'bphr', 'bpfo', 'bp1b', 'bplo', 'xch', 'xhit', 'error', 'pb', 'sbc', 'csc',
|
||||
],[
|
||||
all_stats.pa_sum, all_stats.ab_sum, all_stats.run, all_stats.hit_sum, all_stats.rbi_sum,
|
||||
all_stats.double_sum, all_stats.triple_sum, all_stats.hr_sum, all_stats.bb_sum, all_stats.so_sum,
|
||||
all_stats.hbp_sum, all_stats.sac, all_stats.ibb_sum, all_stats.gidp_sum, all_stats.sb_sum,
|
||||
all_stats.cs_sum, all_stats.bphr_sum, all_stats.bpfo_sum, all_stats.bp1b_sum, all_stats.bplo_sum,
|
||||
all_stats.xch, all_stats.xhit_sum, all_stats.error_sum, all_stats.pb_sum, all_stats.sbc_sum,
|
||||
all_stats.csc_sum
|
||||
]
|
||||
]
|
||||
return_val = DataFrame(data_list).to_csv(header=False, index=False)
|
||||
|
||||
db.close()
|
||||
return Response(content=return_val, media_type='text/csv')
|
||||
|
||||
else:
|
||||
logging.debug(f'stat pull query: {all_stats}\n')
|
||||
# logging.debug(f'result 0: {all_stats[0]}\n')
|
||||
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('')
|
||||
async def post_batstats(stats: BattingStatModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post stats. This event has been logged.'
|
||||
)
|
||||
|
||||
new_stats = []
|
||||
for x in stats.stats:
|
||||
this_stat = BattingStat(
|
||||
card_id=x.card_id,
|
||||
team_id=x.team_id,
|
||||
roster_num=x.roster_num,
|
||||
vs_team_id=x.vs_team_id,
|
||||
pos=x.pos,
|
||||
pa=x.pa,
|
||||
ab=x.ab,
|
||||
run=x.run,
|
||||
hit=x.hit,
|
||||
rbi=x.rbi,
|
||||
double=x.double,
|
||||
triple=x.triple,
|
||||
hr=x.hr,
|
||||
bb=x.bb,
|
||||
so=x.so,
|
||||
hbp=x.hbp,
|
||||
sac=x.sac,
|
||||
ibb=x.ibb,
|
||||
gidp=x.gidp,
|
||||
sb=x.sb,
|
||||
cs=x.cs,
|
||||
bphr=x.bphr,
|
||||
bpfo=x.bpfo,
|
||||
bp1b=x.bp1b,
|
||||
bplo=x.bplo,
|
||||
xch=x.xch,
|
||||
xhit=x.xhit,
|
||||
error=x.error,
|
||||
pb=x.pb,
|
||||
sbc=x.sbc,
|
||||
csc=x.csc,
|
||||
week=x.week,
|
||||
season=x.season,
|
||||
created=x.created,
|
||||
game_id=x.game_id
|
||||
)
|
||||
new_stats.append(this_stat)
|
||||
|
||||
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')
|
||||
|
||||
|
||||
@router.delete('/{stat_id}')
|
||||
async def delete_batstat(stat_id, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete stats. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_stat = BattingStat.get_by_id(stat_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No stat found with id {stat_id}')
|
||||
|
||||
count = this_stat.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
raise HTTPException(status_code=200, detail=f'Stat {stat_id} has been deleted')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Stat {stat_id} was not deleted')
|
||||
|
||||
|
||||
# @app.get('/api/v1/plays/batting')
|
||||
# async def get_batting_totals(
|
||||
# player_id: list = Query(default=None), team_id: list = Query(default=None), min_pa: Optional[int] = 1,
|
||||
# season: list = Query(default=None), position: list = Query(default=None),
|
||||
# group_by: Literal['team', 'player', 'playerteam', 'playergame', 'teamgame', 'league'] = 'player',
|
||||
# sort: Optional[str] = None, limit: Optional[int] = None, short_output: Optional[bool] = False):
|
||||
# all_stats = BattingStat.select(
|
||||
# BattingStat.card, BattingStat.game_id, BattingStat.team, BattingStat.vs_team, BattingStat.pos,
|
||||
# BattingStat.card.player.alias('player'),
|
||||
# fn.SUM(BattingStat.pa).alias('sum_pa'), fn.SUM(BattingStat.ab).alias('sum_ab'),
|
||||
# fn.SUM(BattingStat.run).alias('sum_run'), fn.SUM(BattingStat.so).alias('sum_so'),
|
||||
# fn.SUM(BattingStat.hit).alias('sum_hit'), fn.SUM(BattingStat.rbi).alias('sum_rbi'),
|
||||
# fn.SUM(BattingStat.double).alias('sum_double'), fn.SUM(BattingStat.triple).alias('sum_triple'),
|
||||
# fn.SUM(BattingStat.hr).alias('sum_hr'), fn.SUM(BattingStat.bb).alias('sum_bb'),
|
||||
# fn.SUM(BattingStat.hbp).alias('sum_hbp'), fn.SUM(BattingStat.sac).alias('sum_sac'),
|
||||
# fn.SUM(BattingStat.ibb).alias('sum_ibb'), fn.SUM(BattingStat.gidp).alias('sum_gidp'),
|
||||
# fn.SUM(BattingStat.sb).alias('sum_sb'), fn.SUM(BattingStat.cs).alias('sum_cs'),
|
||||
# fn.SUM(BattingStat.bphr).alias('sum_bphr'), fn.SUM(BattingStat.bpfo).alias('sum_bpfo'),
|
||||
# fn.SUM(BattingStat.bp1b).alias('sum_bp1b'), fn.SUM(BattingStat.bplo).alias('sum_bplo')
|
||||
# ).having(
|
||||
# fn.SUM(BattingStat.pa) >= min_pa
|
||||
# ).join(Card)
|
||||
#
|
||||
# if player_id is not None:
|
||||
# # all_players = Player.select().where(Player.id << player_id)
|
||||
# all_cards = Card.select().where(Card.player_id << player_id)
|
||||
# all_stats = all_stats.where(BattingStat.card << all_cards)
|
||||
# if team_id is not None:
|
||||
# all_teams = Team.select().where(Team.id << team_id)
|
||||
# all_stats = all_stats.where(BattingStat.team << all_teams)
|
||||
# if season is not None:
|
||||
# all_stats = all_stats.where(BattingStat.season << season)
|
||||
# if position is not None:
|
||||
# all_stats = all_stats.where(BattingStat.pos << position)
|
||||
#
|
||||
# if group_by == 'player':
|
||||
# all_stats = all_stats.group_by(SQL('player'))
|
||||
# elif group_by == 'playerteam':
|
||||
# all_stats = all_stats.group_by(SQL('player'), BattingStat.team)
|
||||
# elif group_by == 'playergame':
|
||||
# all_stats = all_stats.group_by(SQL('player'), BattingStat.game_id)
|
||||
# elif group_by == 'team':
|
||||
# all_stats = all_stats.group_by(BattingStat.team)
|
||||
# elif group_by == 'teamgame':
|
||||
# all_stats = all_stats.group_by(BattingStat.team, BattingStat.game_id)
|
||||
# elif group_by == 'league':
|
||||
# all_stats = all_stats.group_by(BattingStat.season)
|
||||
#
|
||||
# if sort == 'pa-desc':
|
||||
# all_stats = all_stats.order_by(SQL('sum_pa').desc())
|
||||
# elif sort == 'newest':
|
||||
# all_stats = all_stats.order_by(-BattingStat.game_id)
|
||||
# elif sort == 'oldest':
|
||||
# all_stats = all_stats.order_by(BattingStat.game_id)
|
||||
#
|
||||
# if limit is not None:
|
||||
# if limit < 1:
|
||||
# limit = 1
|
||||
# all_stats = all_stats.limit(limit)
|
||||
#
|
||||
# logging.info(f'bat_plays query: {all_stats}')
|
||||
#
|
||||
# return_stats = {
|
||||
# 'count': all_stats.count(),
|
||||
# 'stats': [{
|
||||
# 'player': x.card.player_id if short_output else model_to_dict(x.card.player, recurse=False),
|
||||
# 'team': x.team_id if short_output else model_to_dict(x.team, recurse=False),
|
||||
# 'pa': x.sum_pa,
|
||||
# 'ab': x.sum_ab,
|
||||
# 'run': x.sum_run,
|
||||
# 'hit': x.sum_hit,
|
||||
# 'rbi': x.sum_rbi,
|
||||
# 'double': x.sum_double,
|
||||
# 'triple': x.sum_triple,
|
||||
# 'hr': x.sum_hr,
|
||||
# 'bb': x.sum_bb,
|
||||
# 'so': x.sum_so,
|
||||
# 'hbp': x.sum_hbp,
|
||||
# 'sac': x.sum_sac,
|
||||
# 'ibb': x.sum_ibb,
|
||||
# 'gidp': x.sum_gidp,
|
||||
# 'sb': x.sum_sb,
|
||||
# 'cs': x.sum_cs,
|
||||
# 'bphr': x.sum_bphr,
|
||||
# 'bpfo': x.sum_bpfo,
|
||||
# 'bp1b': x.sum_bp1b,
|
||||
# 'bplo': x.sum_bplo,
|
||||
# 'avg': x.sum_hit / max(x.sum_ab, 1),
|
||||
# 'obp': (x.sum_hit + x.sum_bb + x.sum_hbp + x.sum_ibb) / max(x.sum_pa, 1),
|
||||
# 'slg': (x.sum_hr * 4 + x.sum_triple * 3 + x.sum_double * 2 +
|
||||
# (x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr)) / max(x.sum_ab, 1),
|
||||
# 'ops': ((x.sum_hit + x.sum_bb + x.sum_hbp + x.sum_ibb) / max(x.sum_pa, 1)) +
|
||||
# ((x.sum_hr * 4 + x.sum_triple * 3 + x.sum_double * 2 +
|
||||
# (x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr)) / max(x.sum_ab, 1)),
|
||||
# 'woba': (.69 * x.sum_bb + .72 * x.sum_hbp + .89 * (x.sum_hit - x.sum_double - x.sum_triple - x.sum_hr) +
|
||||
# 1.27 * x.sum_double + 1.62 * x.sum_triple + 2.1 * x.sum_hr) / max(x.sum_pa - x.sum_ibb, 1),
|
||||
# 'game': x.game_id
|
||||
# } for x in all_stats]
|
||||
# }
|
||||
#
|
||||
# db.close()
|
||||
# return return_stats
|
||||
#
|
||||
#
|
||||
# @app.get('/api/v1/plays/pitching')
|
||||
# async def get_pitching_totals(
|
||||
# player_id: list = Query(default=None), team_id: list = Query(default=None), season: list = Query(default=None),
|
||||
# group_by: Literal['team', 'player', 'playerteam', 'playergame', 'teamgame', 'league'] = 'player',
|
||||
# min_pa: Optional[int] = 1,
|
||||
# sort: Optional[str] = None, limit: Optional[int] = None, short_output: Optional[bool] = False):
|
||||
# all_stats = PitchingStat.select(
|
||||
# PitchingStat.card, PitchingStat.team, PitchingStat.game_id, PitchingStat.vs_team,
|
||||
# PitchingStat.card.player.alias('player'), fn.SUM(PitchingStat.ip).alias('sum_ip'),
|
||||
# fn.SUM(PitchingStat.hit).alias('sum_hit'), fn.SUM(PitchingStat.run).alias('sum_run'),
|
||||
# fn.SUM(PitchingStat.erun).alias('sum_erun'), fn.SUM(PitchingStat.so).alias('sum_so'),
|
||||
# fn.SUM(PitchingStat.bb).alias('sum_bb'), fn.SUM(PitchingStat.hbp).alias('sum_hbp'),
|
||||
# fn.SUM(PitchingStat.wp).alias('sum_wp'), fn.SUM(PitchingStat.balk).alias('sum_balk'),
|
||||
# fn.SUM(PitchingStat.hr).alias('sum_hr'), fn.SUM(PitchingStat.ir).alias('sum_ir'),
|
||||
# fn.SUM(PitchingStat.irs).alias('sum_irs'), fn.SUM(PitchingStat.gs).alias('sum_gs'),
|
||||
# fn.SUM(PitchingStat.win).alias('sum_win'), fn.SUM(PitchingStat.loss).alias('sum_loss'),
|
||||
# fn.SUM(PitchingStat.hold).alias('sum_hold'), fn.SUM(PitchingStat.sv).alias('sum_sv'),
|
||||
# fn.SUM(PitchingStat.bsv).alias('sum_bsv'), fn.COUNT(PitchingStat.game_id).alias('sum_games')
|
||||
# ).having(
|
||||
# fn.SUM(PitchingStat.ip) >= max(min_pa / 3, 1)
|
||||
# ).join(Card)
|
||||
#
|
||||
# if player_id is not None:
|
||||
# all_cards = Card.select().where(Card.player_id << player_id)
|
||||
# all_stats = all_stats.where(PitchingStat.card << all_cards)
|
||||
# if team_id is not None:
|
||||
# all_teams = Team.select().where(Team.id << team_id)
|
||||
# all_stats = all_stats.where(PitchingStat.team << all_teams)
|
||||
# if season is not None:
|
||||
# all_stats = all_stats.where(PitchingStat.season << season)
|
||||
#
|
||||
# if group_by == 'player':
|
||||
# all_stats = all_stats.group_by(SQL('player'))
|
||||
# elif group_by == 'playerteam':
|
||||
# all_stats = all_stats.group_by(SQL('player'), PitchingStat.team)
|
||||
# elif group_by == 'playergame':
|
||||
# all_stats = all_stats.group_by(SQL('player'), PitchingStat.game_id)
|
||||
# elif group_by == 'team':
|
||||
# all_stats = all_stats.group_by(PitchingStat.team)
|
||||
# elif group_by == 'teamgame':
|
||||
# all_stats = all_stats.group_by(PitchingStat.team, PitchingStat.game_id)
|
||||
# elif group_by == 'league':
|
||||
# all_stats = all_stats.group_by(PitchingStat.season)
|
||||
#
|
||||
# if sort == 'pa-desc':
|
||||
# all_stats = all_stats.order_by(SQL('sum_pa').desc())
|
||||
# elif sort == 'newest':
|
||||
# all_stats = all_stats.order_by(-PitchingStat.game_id)
|
||||
# elif sort == 'oldest':
|
||||
# all_stats = all_stats.order_by(PitchingStat.game_id)
|
||||
#
|
||||
# if limit is not None:
|
||||
# if limit < 1:
|
||||
# limit = 1
|
||||
# all_stats = all_stats.limit(limit)
|
||||
#
|
||||
# logging.info(f'bat_plays query: {all_stats}')
|
||||
#
|
||||
# return_stats = {
|
||||
# 'count': all_stats.count(),
|
||||
# 'stats': [{
|
||||
# 'player': x.card.player_id if short_output else model_to_dict(x.card.player, recurse=False),
|
||||
# 'team': x.team_id if short_output else model_to_dict(x.team, recurse=False),
|
||||
# 'tbf': None,
|
||||
# 'outs': round(x.sum_ip * 3),
|
||||
# 'games': x.sum_games,
|
||||
# 'gs': x.sum_gs,
|
||||
# 'win': x.sum_win,
|
||||
# 'loss': x.sum_loss,
|
||||
# 'hold': x.sum_hold,
|
||||
# 'save': x.sum_sv,
|
||||
# 'bsave': x.sum_bsv,
|
||||
# 'ir': x.sum_ir,
|
||||
# 'ir_sc': x.sum_irs,
|
||||
# 'runs': x.sum_run,
|
||||
# 'e_runs': x.sum_erun,
|
||||
# 'hits': x.sum_hit,
|
||||
# 'hr': x.sum_hr,
|
||||
# 'bb': x.sum_bb,
|
||||
# 'so': x.sum_so,
|
||||
# 'hbp': x.sum_hbp,
|
||||
# 'wp': x.sum_wp,
|
||||
# 'balk': x.sum_balk,
|
||||
# 'era': (x.sum_erun * 27) / round(x.sum_ip * 3),
|
||||
# 'whip': (x.sum_bb + x.sum_hit) / x.sum_ip,
|
||||
# 'avg': None,
|
||||
# 'obp': None,
|
||||
# 'woba': None,
|
||||
# 'k/9': x.sum_so * 9 / x.sum_ip,
|
||||
# 'bb/9': x.sum_bb * 9 / x.sum_ip,
|
||||
# 'k/bb': x.sum_so / max(x.sum_bb, .1),
|
||||
# 'game': None,
|
||||
# 'lob_2outs': None,
|
||||
# 'rbi%': None
|
||||
# } for x in all_stats]
|
||||
# }
|
||||
# db.close()
|
||||
# return return_stats
|
||||
|
||||
289
app/routers_v2/battingcardratings.py
Normal file
289
app/routers_v2/battingcardratings.py
Normal file
@ -0,0 +1,289 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from typing import Literal, Optional, List
|
||||
import logging
|
||||
import pandas as pd
|
||||
import pydantic
|
||||
from pydantic import validator, root_validator
|
||||
|
||||
from ..db_engine import db, BattingCardRatings, model_to_dict, chunked, BattingCard, Player, query_to_csv, Team
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/battingcardratings',
|
||||
tags=['battingcardratings']
|
||||
)
|
||||
|
||||
|
||||
class BattingCardRatingsModel(pydantic.BaseModel):
|
||||
battingcard_id: int
|
||||
vs_hand: Literal['R', 'L', 'vR', 'vL']
|
||||
homerun: float = 0.0
|
||||
bp_homerun: float = 0.0
|
||||
triple: float = 0.0
|
||||
double_three: float = 0.0
|
||||
double_two: float = 0.0
|
||||
double_pull: float = 0.0
|
||||
single_two: float = 0.0
|
||||
single_one: float = 0.0
|
||||
single_center: float = 0.0
|
||||
bp_single: float = 0.0
|
||||
hbp: float = 0.0
|
||||
walk: float = 0.0
|
||||
strikeout: float = 0.0
|
||||
lineout: float = 0.0
|
||||
popout: float = 0.0
|
||||
flyout_a: float = 0.0
|
||||
flyout_bq: float = 0.0
|
||||
flyout_lf_b: float = 0.0
|
||||
flyout_rf_b: float = 0.0
|
||||
groundout_a: float = 0.0
|
||||
groundout_b: float = 0.0
|
||||
groundout_c: float = 0.0
|
||||
avg: float = 0.0
|
||||
obp: float = 0.0
|
||||
slg: float = 0.0
|
||||
pull_rate: float = 0.0
|
||||
center_rate: float = 0.0
|
||||
slap_rate: float = 0.0
|
||||
|
||||
@validator("avg", always=True)
|
||||
def avg_validator(cls, v, values, **kwargs):
|
||||
return (values['homerun'] + values['bp_homerun'] / 2 + values['triple'] + values['double_three'] +
|
||||
values['double_two'] + values['double_pull'] + values['single_two'] + values['single_one'] +
|
||||
values['single_center'] + values['bp_single'] / 2) / 108
|
||||
|
||||
@validator("obp", always=True)
|
||||
def obp_validator(cls, v, values, **kwargs):
|
||||
return ((values['hbp'] + values['walk']) / 108) + values['avg']
|
||||
|
||||
@validator("slg", always=True)
|
||||
def slg_validator(cls, v, values, **kwargs):
|
||||
return (values['homerun'] * 4 + values['bp_homerun'] * 2 + values['triple'] * 3 + values['double_three'] * 2 +
|
||||
values['double_two'] * 2 + values['double_pull'] * 2 + values['single_two'] + values['single_one'] +
|
||||
values['single_center'] + values['bp_single'] / 2) / 108
|
||||
|
||||
@root_validator
|
||||
def validate_chance_total(cls, values):
|
||||
total_chances = (
|
||||
values['homerun'] + values['bp_homerun'] + values['triple'] + values['double_three'] +
|
||||
values['double_two'] + values['double_pull'] + values['single_two'] + values['single_one'] +
|
||||
values['single_center'] + values['bp_single'] + values['hbp'] + values['walk'] +
|
||||
values['strikeout'] + values['lineout'] + values['popout'] + values['flyout_a'] +
|
||||
values['flyout_bq'] + values['flyout_lf_b'] + values['flyout_rf_b'] + values['groundout_a'] +
|
||||
values['groundout_b'] + values['groundout_c'])
|
||||
|
||||
if round(total_chances) != 108:
|
||||
raise ValueError("Must have exactly 108 chances on the card")
|
||||
return values
|
||||
|
||||
|
||||
class RatingsList(pydantic.BaseModel):
|
||||
ratings: List[BattingCardRatingsModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_card_ratings(
|
||||
team_id: int, ts: str, battingcard_id: list = Query(default=None), cardset_id: list = Query(default=None),
|
||||
vs_hand: Literal['R', 'L', 'vR', 'vL'] = None, short_output: bool = False, csv: bool = False):
|
||||
this_team = Team.get_or_none(Team.id == team_id)
|
||||
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}')
|
||||
# db.close()
|
||||
# raise HTTPException(
|
||||
# status_code=401,
|
||||
# detail='You are not authorized to pull card ratings.'
|
||||
# )
|
||||
|
||||
all_ratings = BattingCardRatings.select()
|
||||
|
||||
if battingcard_id is not None:
|
||||
all_ratings = all_ratings.where(BattingCardRatings.battingcard_id << battingcard_id)
|
||||
if vs_hand is not None:
|
||||
all_ratings = all_ratings.where(BattingCardRatings.vs_hand == vs_hand[-1])
|
||||
if cardset_id is not None:
|
||||
set_players = Player.select(Player.player_id).where(Player.cardset_id << cardset_id)
|
||||
set_cards = BattingCard.select(BattingCard.id).where(BattingCard.player << set_players)
|
||||
all_ratings = all_ratings.where(BattingCardRatings.battingcard << set_cards)
|
||||
|
||||
if csv:
|
||||
# return_val = query_to_csv(all_ratings)
|
||||
return_vals = [model_to_dict(x) for x in all_ratings]
|
||||
for x in return_vals:
|
||||
x.update(x['battingcard'])
|
||||
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')
|
||||
|
||||
else:
|
||||
return_val = {'count': all_ratings.count(), 'ratings': [
|
||||
model_to_dict(x, recurse=not short_output) for x in all_ratings
|
||||
]}
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/scouting')
|
||||
async def get_card_scouting(team_id: int, ts: str, cardset_id: list = Query(default=None)):
|
||||
this_team = Team.get_or_none(Team.id == team_id)
|
||||
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!): ' \
|
||||
'https://ko-fi.com/manticorum/shop'
|
||||
|
||||
all_ratings = BattingCardRatings.select()
|
||||
if cardset_id is not None:
|
||||
set_players = Player.select(Player.player_id).where(Player.cardset_id << cardset_id)
|
||||
set_cards = BattingCard.select(BattingCard.id).where(BattingCard.player << set_players)
|
||||
all_ratings = all_ratings.where(BattingCardRatings.battingcard << set_cards)
|
||||
|
||||
vl_query = all_ratings.where(BattingCardRatings.vs_hand == 'L')
|
||||
vr_query = all_ratings.where(BattingCardRatings.vs_hand == 'R')
|
||||
|
||||
vl_vals = [model_to_dict(x) for x in vl_query]
|
||||
for x in vl_vals:
|
||||
x.update(x['battingcard'])
|
||||
x['player_id'] = x['battingcard']['player']['player_id']
|
||||
x['player_name'] = x['battingcard']['player']['p_name']
|
||||
x['rarity'] = x['battingcard']['player']['rarity']['name']
|
||||
x['cardset_id'] = x['battingcard']['player']['cardset']['id']
|
||||
x['cardset_name'] = x['battingcard']['player']['cardset']['name']
|
||||
del x['battingcard']
|
||||
del x['player']
|
||||
|
||||
vr_vals = [model_to_dict(x) for x in vr_query]
|
||||
for x in vr_vals:
|
||||
x['player_id'] = x['battingcard']['player']['player_id']
|
||||
del x['battingcard']
|
||||
|
||||
vl = pd.DataFrame(vl_vals)
|
||||
vr = pd.DataFrame(vr_vals)
|
||||
db.close()
|
||||
|
||||
output = pd.merge(vl, vr, on='player_id', suffixes=('_vl', '_vr'))
|
||||
first = ['player_id', 'player_name', 'cardset_name', 'rarity', 'hand', 'variant']
|
||||
exclude = first + ['id_vl', 'id_vr', 'vs_hand_vl', 'vs_hand_vr']
|
||||
output = output[first + [col for col in output.columns if col not in exclude]].sort_values(by=['player_id'])
|
||||
# output = output.sort_values(by=['player_id'])
|
||||
return Response(content=pd.DataFrame(output).to_csv(index=False), media_type='text/csv')
|
||||
|
||||
|
||||
@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()
|
||||
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
|
||||
|
||||
|
||||
@router.get('/player/{player_id}')
|
||||
async def get_player_ratings(
|
||||
player_id: int, variant: list = Query(default=None), short_output: bool = False,
|
||||
token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to pull card ratings.'
|
||||
)
|
||||
|
||||
all_cards = BattingCard.select().where(BattingCard.player_id == player_id).order_by(BattingCard.variant)
|
||||
if variant is not None:
|
||||
all_cards = all_cards.where(BattingCard.variant << variant)
|
||||
|
||||
all_ratings = BattingCardRatings.select().where(BattingCardRatings.battingcard << all_cards)
|
||||
|
||||
return_val = {'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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post card ratings.'
|
||||
)
|
||||
|
||||
new_ratings = []
|
||||
updates = 0
|
||||
for x in ratings.ratings:
|
||||
try:
|
||||
BattingCardRatings.get(
|
||||
(BattingCardRatings.battingcard_id == x.battingcard_id) & (BattingCardRatings.vs_hand == x.vs_hand)
|
||||
)
|
||||
updates += BattingCardRatings.update(x.dict()).where(
|
||||
(BattingCardRatings.battingcard_id == x.battingcard_id) & (BattingCardRatings.vs_hand == x.vs_hand)
|
||||
).execute()
|
||||
except BattingCardRatings.DoesNotExist:
|
||||
new_ratings.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_ratings, 30):
|
||||
BattingCardRatings.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
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()
|
||||
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'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Rating {this_rating} could not be deleted')
|
||||
|
||||
|
||||
224
app/routers_v2/battingcards.py
Normal file
224
app/routers_v2/battingcards.py
Normal file
@ -0,0 +1,224 @@
|
||||
import random
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import Literal, Optional, List
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, BattingCard, model_to_dict, fn, chunked, Player, MlbPlayer
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/battingcards',
|
||||
tags=['battingcards']
|
||||
)
|
||||
|
||||
|
||||
class BattingCardModel(pydantic.BaseModel):
|
||||
player_id: int
|
||||
variant: int = 0
|
||||
steal_low: int = 3
|
||||
steal_high: int = 20
|
||||
steal_auto: bool = False
|
||||
steal_jump: float = 0
|
||||
bunting: str = 'C'
|
||||
hit_and_run: str = 'C'
|
||||
running: int = 10
|
||||
offense_col: int = None
|
||||
hand: Literal['R', 'L', 'S'] = 'R'
|
||||
|
||||
|
||||
class BattingCardList(pydantic.BaseModel):
|
||||
cards: List[BattingCardModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_batting_cards(
|
||||
player_id: list = Query(default=None), player_name: list = Query(default=None),
|
||||
cardset_id: list = Query(default=None), short_output: bool = False, limit: Optional[int] = None):
|
||||
all_cards = BattingCard.select()
|
||||
if player_id is not None:
|
||||
all_cards = all_cards.where(BattingCard.player_id << player_id)
|
||||
if cardset_id is not None:
|
||||
all_players = Player.select().where(Player.cardset_id << cardset_id)
|
||||
all_cards = all_cards.where(BattingCard.player << all_players)
|
||||
if player_name is not None:
|
||||
name_list = [x.lower() for x in player_name]
|
||||
all_players = Player.select().where(fn.lower(Player.p_name) << name_list)
|
||||
all_cards = all_cards.where(BattingCard.player << all_players)
|
||||
|
||||
if limit is not None:
|
||||
all_cards = all_cards.limit(limit)
|
||||
|
||||
return_val = {'count': all_cards.count(), 'cards': [
|
||||
model_to_dict(x, recurse=not short_output) for x in all_cards
|
||||
]}
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{card_id}')
|
||||
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
|
||||
|
||||
|
||||
@router.get('/player/{player_id}')
|
||||
async def get_player_cards(player_id: int, variant: list = Query(default=None), short_output: bool = False):
|
||||
all_cards = BattingCard.select().where(BattingCard.player_id == player_id).order_by(BattingCard.variant)
|
||||
if variant is not None:
|
||||
all_cards = all_cards.where(BattingCard.variant << variant)
|
||||
|
||||
return_val = {'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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post batting cards. This event has been logged.'
|
||||
)
|
||||
|
||||
new_cards = []
|
||||
updates = 0
|
||||
logging.info(f'here!')
|
||||
|
||||
for x in cards.cards:
|
||||
try:
|
||||
old = BattingCard.get(
|
||||
(BattingCard.player_id == x.player_id) & (BattingCard.variant == x.variant)
|
||||
)
|
||||
|
||||
if x.offense_col is None:
|
||||
x.offense_col = old.offense_col
|
||||
updates += BattingCard.update(x.dict()).where(
|
||||
(BattingCard.player_id == x.player_id) & (BattingCard.variant == x.variant)
|
||||
).execute()
|
||||
except BattingCard.DoesNotExist:
|
||||
if x.offense_col is None:
|
||||
this_player = Player.get_or_none(Player.player_id == x.player_id)
|
||||
mlb_player = MlbPlayer.get_or_none(MlbPlayer.key_bbref == this_player.bbref_id)
|
||||
if mlb_player is not None:
|
||||
logging.info(f'setting offense_col to {mlb_player.offense_col} for {this_player.p_name}')
|
||||
x.offense_col = mlb_player.offense_col
|
||||
else:
|
||||
logging.info(f'randomly setting offense_col for {this_player.p_name}')
|
||||
x.offense_col = random.randint(1, 3)
|
||||
logging.debug(f'x.dict(): {x.dict()}')
|
||||
new_cards.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_cards, 30):
|
||||
BattingCard.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
db.close()
|
||||
return f'Updated cards: {updates}; new cards: {len(new_cards)}'
|
||||
|
||||
|
||||
@router.patch('/{card_id}')
|
||||
async def patch_card(
|
||||
card_id: int, steal_low: Optional[int] = None, steal_high: Optional[int] = None,
|
||||
steal_auto: Optional[bool] = None, steal_jump: Optional[float] = None, bunting: Optional[str] = None,
|
||||
hit_and_run: Optional[str] = None, running: Optional[int] = None, offense_col: Optional[int] = None,
|
||||
hand: Literal['R', 'L', 'S'] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch batting cards. This event has been logged.'
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
if steal_low is not None:
|
||||
this_card.steal_low = steal_low
|
||||
if steal_high is not None:
|
||||
this_card.steal_high = steal_high
|
||||
if steal_auto is not None:
|
||||
this_card.steal_auto = steal_auto
|
||||
if steal_jump is not None:
|
||||
this_card.steal_jump = steal_jump
|
||||
if bunting is not None:
|
||||
this_card.bunting = bunting
|
||||
if hit_and_run is not None:
|
||||
this_card.hit_and_run = hit_and_run
|
||||
if running is not None:
|
||||
this_card.running = running
|
||||
if offense_col is not None:
|
||||
this_card.offense_col = offense_col
|
||||
if hand is not None:
|
||||
this_card.hand = hand
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete batting cards. This event has been logged.'
|
||||
)
|
||||
|
||||
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'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Card {this_card} could not be deleted')
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete batting cards. This event has been logged.'
|
||||
)
|
||||
|
||||
d_query = BattingCard.delete()
|
||||
d_query.execute()
|
||||
|
||||
return f'Deleted {d_query.count()} batting cards'
|
||||
158
app/routers_v2/cardpositions.py
Normal file
158
app/routers_v2/cardpositions.py
Normal file
@ -0,0 +1,158 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import Literal, Optional, List
|
||||
import logging
|
||||
import pydantic
|
||||
from pydantic import root_validator
|
||||
|
||||
from ..db_engine import db, CardPosition, model_to_dict, chunked, Player, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/cardpositions',
|
||||
tags=['cardpositions']
|
||||
)
|
||||
|
||||
|
||||
class CardPositionModel(pydantic.BaseModel):
|
||||
player_id: int
|
||||
variant: int = 0
|
||||
position: Literal['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'DH']
|
||||
innings: int = 1
|
||||
range: int = 5
|
||||
error: int = 0
|
||||
arm: Optional[int] = None
|
||||
pb: Optional[int] = None
|
||||
overthrow: Optional[int] = None
|
||||
|
||||
@root_validator
|
||||
def position_validator(cls, values):
|
||||
if values['position'] in ['C', 'LF', 'CF', 'RF'] and values['arm'] is None:
|
||||
raise ValueError(f'{values["position"]} must have an arm rating')
|
||||
if values['position'] == 'C' and (values['pb'] is None or values['overthrow'] is None):
|
||||
raise ValueError('Catchers must have a pb and overthrow rating')
|
||||
return values
|
||||
|
||||
|
||||
class PositionList(pydantic.BaseModel):
|
||||
positions: List[CardPositionModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_card_positions(
|
||||
player_id: list = Query(default=None), position: list = Query(default=None), min_innings: Optional[int] = 1,
|
||||
r: list = Query(default=None), e: list = Query(default=None), arm: list = Query(default=None),
|
||||
pb: list = Query(default=None), overthrow: list = Query(default=None), cardset_id: list = Query(default=None),
|
||||
short_output: Optional[bool] = False, sort: Optional[str] = 'innings-desc'):
|
||||
all_pos = CardPosition.select().where(CardPosition.innings >= min_innings).order_by(
|
||||
CardPosition.player, CardPosition.position, CardPosition.variant
|
||||
)
|
||||
|
||||
if player_id is not None:
|
||||
all_pos = all_pos.where(CardPosition.player_id << player_id)
|
||||
if position is not None:
|
||||
p_list = [x.lower() for x in position]
|
||||
all_pos = all_pos.where(fn.Lower(CardPosition.position) << p_list)
|
||||
if r is not None:
|
||||
all_pos = all_pos.where(CardPosition.range << r)
|
||||
if e is not None:
|
||||
all_pos = all_pos.where(CardPosition.error << e)
|
||||
if arm is not None:
|
||||
all_pos = all_pos.where(CardPosition.arm << arm)
|
||||
if pb is not None:
|
||||
all_pos = all_pos.where(CardPosition.pb << pb)
|
||||
if overthrow is not None:
|
||||
all_pos = all_pos.where(CardPosition.overthrow << overthrow)
|
||||
if cardset_id is not None:
|
||||
all_players = Player.select().where(Player.cardset_id << cardset_id)
|
||||
all_pos = all_pos.where(CardPosition.player << all_players)
|
||||
|
||||
if sort == 'innings-desc':
|
||||
all_pos = all_pos.order_by(CardPosition.innings.desc())
|
||||
elif sort == 'innings-asc':
|
||||
all_pos = all_pos.order_by(CardPosition.innings)
|
||||
elif sort == 'range-desc':
|
||||
all_pos = all_pos.order_by(CardPosition.range.desc())
|
||||
elif sort == 'range-asc':
|
||||
all_pos = all_pos.order_by(CardPosition.range)
|
||||
|
||||
return_val = {'count': all_pos.count(), 'positions': [
|
||||
model_to_dict(x, recurse=not short_output) for x in all_pos
|
||||
]}
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{position_id}')
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post card positions. This event has been logged.'
|
||||
)
|
||||
|
||||
new_cards = []
|
||||
updates = 0
|
||||
|
||||
for x in positions.positions:
|
||||
try:
|
||||
CardPosition.get(
|
||||
(CardPosition.player_id == x.player_id) & (CardPosition.variant == x.variant) &
|
||||
(CardPosition.position == x.position)
|
||||
)
|
||||
updates += CardPosition.update(x.dict()).where(
|
||||
(CardPosition.player_id == x.player_id) & (CardPosition.variant == x.variant) &
|
||||
(CardPosition.position == x.position)
|
||||
).execute()
|
||||
except CardPosition.DoesNotExist:
|
||||
new_cards.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_cards, 30):
|
||||
CardPosition.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete card positions. This event has been logged.'
|
||||
)
|
||||
|
||||
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'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Card Position {this_pos} could not be deleted')
|
||||
356
app/routers_v2/cards.py
Normal file
356
app/routers_v2/cards.py
Normal file
@ -0,0 +1,356 @@
|
||||
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, Card, model_to_dict, Team, Player, Pack, Paperdex, CARDSETS
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/cards',
|
||||
tags=['cards']
|
||||
)
|
||||
|
||||
|
||||
class CardPydantic(pydantic.BaseModel):
|
||||
player_id: int
|
||||
team_id: int
|
||||
pack_id: int
|
||||
value: Optional[int] = 0
|
||||
|
||||
|
||||
class CardModel(pydantic.BaseModel):
|
||||
cards: List[CardPydantic]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_cards(
|
||||
player_id: Optional[int] = None, team_id: Optional[int] = None, pack_id: Optional[int] = None,
|
||||
value: Optional[int] = None, min_value: Optional[int] = None, max_value: Optional[int] = None,
|
||||
order_by: Optional[str] = None, limit: Optional[int] = None, dupes: Optional[bool] = None,
|
||||
csv: Optional[bool] = None):
|
||||
all_cards = Card.select()
|
||||
|
||||
# if all_cards.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'There are no cards to filter')
|
||||
|
||||
if team_id is not None:
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
all_cards = all_cards.where(Card.team == this_team)
|
||||
if player_id is not None:
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No player found with id {player_id}')
|
||||
all_cards = all_cards.where(Card.player == this_player)
|
||||
if pack_id is not None:
|
||||
try:
|
||||
this_pack = Pack.get_by_id(pack_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}')
|
||||
all_cards = all_cards.where(Card.pack == this_pack)
|
||||
if value is not None:
|
||||
all_cards = all_cards.where(Card.value == value)
|
||||
if min_value is not None:
|
||||
all_cards = all_cards.where(Card.value >= min_value)
|
||||
if max_value is not None:
|
||||
all_cards = all_cards.where(Card.value <= max_value)
|
||||
if order_by is not None:
|
||||
if order_by.lower() == 'new':
|
||||
all_cards = all_cards.order_by(-Card.id)
|
||||
if limit is not None:
|
||||
all_cards = all_cards.limit(limit)
|
||||
if dupes:
|
||||
if team_id is None:
|
||||
raise HTTPException(status_code=400, detail='Dupe checking must include a team_id')
|
||||
logging.info(f'dupe check')
|
||||
p_query = Card.select(Card.player).where(Card.team_id == team_id)
|
||||
seen = set()
|
||||
dupes = []
|
||||
for x in p_query:
|
||||
if x.player.player_id in seen:
|
||||
dupes.append(x.player_id)
|
||||
else:
|
||||
seen.add(x.player_id)
|
||||
all_cards = all_cards.where(Card.player_id << dupes)
|
||||
|
||||
# if all_cards.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No cards found')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'player', 'cardset', 'rarity', 'team', 'pack', 'value']]
|
||||
for line in all_cards:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.player.p_name, line.player.cardset, line.player.rarity, line.team.abbrev, line.pack,
|
||||
line.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 = {'count': all_cards.count(), 'cards': []}
|
||||
for x in all_cards:
|
||||
|
||||
this_record = model_to_dict(x)
|
||||
logging.debug(f'this_record: {this_record}')
|
||||
|
||||
this_dex = Paperdex.select().where(Paperdex.player == x)
|
||||
this_record['player']['paperdex'] = {'count': this_dex.count(), 'paperdex': []}
|
||||
for y in this_dex:
|
||||
this_record['player']['paperdex']['paperdex'].append(model_to_dict(y, recurse=False))
|
||||
|
||||
return_val['cards'].append(this_record)
|
||||
|
||||
# return_val['cards'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{card_id}')
|
||||
async def v1_cards_get_one(card_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
this_card = Card.get_by_id(card_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No card found with id {card_id}')
|
||||
|
||||
if csv:
|
||||
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]
|
||||
]
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post cards. This event has been logged.'
|
||||
)
|
||||
last_card = Card.select(Card.id).order_by(-Card.id).limit(1)
|
||||
lc_id = last_card[0].id
|
||||
|
||||
new_cards = []
|
||||
player_ids = []
|
||||
inc_dex = True
|
||||
this_team = Team.get_by_id(cards.cards[0].team_id)
|
||||
if this_team.is_ai or 'Gauntlet' in this_team.abbrev:
|
||||
inc_dex = False
|
||||
|
||||
# new_dex = []
|
||||
# now = int(datetime.timestamp(datetime.now()) * 1000)
|
||||
for x in cards.cards:
|
||||
this_card = Card(
|
||||
player_id=x.player_id,
|
||||
team_id=x.team_id,
|
||||
pack_id=x.pack_id,
|
||||
value=x.value
|
||||
)
|
||||
if inc_dex:
|
||||
Paperdex.get_or_create(team_id=x.team_id, player_id=x.player_id)
|
||||
player_ids.append(x.player_id)
|
||||
new_cards.append(this_card)
|
||||
|
||||
with db.atomic():
|
||||
Card.bulk_create(new_cards, batch_size=15)
|
||||
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')
|
||||
|
||||
|
||||
# @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}')
|
||||
# db.close()
|
||||
# raise HTTPException(
|
||||
# status_code=401,
|
||||
# detail='You are not authorized to update AI cards. This event has been logged.'
|
||||
# )
|
||||
#
|
||||
# sheets.send_ai_cards(SHEETS_AUTH)
|
||||
# raise HTTPException(status_code=200, detail=f'Just sent AI cards to sheets')
|
||||
|
||||
|
||||
@router.post('/legal-check/{rarity_name}')
|
||||
async def v1_cards_legal_check(
|
||||
rarity_name: str, card_id: list = Query(default=None), token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='Unauthorized'
|
||||
)
|
||||
if rarity_name not in CARDSETS.keys():
|
||||
return f'Rarity name {rarity_name} not a valid check'
|
||||
|
||||
bad_cards = []
|
||||
all_cards = Card.select().where(Card.id << card_id)
|
||||
|
||||
for x in all_cards:
|
||||
if x.player.cardset_id not in CARDSETS[rarity_name]:
|
||||
if x.player.p_name in x.player.description:
|
||||
bad_cards.append(x.player.description)
|
||||
else:
|
||||
bad_cards.append(f'{x.player.description} {x.player.p_name}')
|
||||
|
||||
return {'count': len(bad_cards), 'bad_cards': bad_cards}
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete card lists. This event has been logged.'
|
||||
)
|
||||
|
||||
logging.info(f'del_ids: {del_ids} / type: {type(del_ids)}')
|
||||
# sheets.post_deletion(SHEETS_AUTH, del_ids.split(','))
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to wipe teams. This event has been logged.'
|
||||
)
|
||||
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception 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'
|
||||
|
||||
|
||||
@router.patch('/{card_id}')
|
||||
async def v1_cards_patch(
|
||||
card_id, player_id: Optional[int] = None, team_id: Optional[int] = None, pack_id: Optional[int] = None,
|
||||
value: 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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No card found with id {card_id}')
|
||||
|
||||
if player_id is not None:
|
||||
this_card.player_id = player_id
|
||||
if team_id is not None:
|
||||
if team_id == 0:
|
||||
this_card.team_id = None
|
||||
else:
|
||||
this_card.team_id = team_id
|
||||
if pack_id is not None:
|
||||
this_card.pack_id = pack_id
|
||||
if value is not None:
|
||||
this_card.value = value
|
||||
if roster1_id is not None:
|
||||
this_card.roster1_id = roster1_id
|
||||
if roster2_id is not None:
|
||||
this_card.roster2_id = roster2_id
|
||||
if roster3_id is not None:
|
||||
this_card.roster3_id = roster3_id
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Card {card_id} was not deleted')
|
||||
204
app/routers_v2/cardsets.py
Normal file
204
app/routers_v2/cardsets.py
Normal file
@ -0,0 +1,204 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/cardsets',
|
||||
tags=['cardsets']
|
||||
)
|
||||
|
||||
|
||||
class CardsetModel(pydantic.BaseModel):
|
||||
name: str
|
||||
description: str
|
||||
event_id: Optional[int] = None
|
||||
in_packs: Optional[bool] = True
|
||||
total_cards: int = 0
|
||||
for_purchase: Optional[bool] = True
|
||||
ranked_legal: Optional[bool] = True
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_cardsets(
|
||||
name: Optional[str] = None, in_desc: Optional[str] = None, event_id: Optional[int] = None,
|
||||
in_packs: Optional[bool] = None, ranked_legal: Optional[bool] = None, csv: Optional[bool] = None):
|
||||
all_cardsets = Cardset.select()
|
||||
|
||||
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:
|
||||
all_cardsets = all_cardsets.where(fn.Lower(Cardset.name) == name.lower())
|
||||
if in_desc is not None:
|
||||
all_cardsets = all_cardsets.where(fn.Lower(Cardset.description).contains(in_desc.lower()))
|
||||
if event_id is not None:
|
||||
try:
|
||||
this_event = Event.get_by_id(event_id)
|
||||
all_cardsets = all_cardsets.where(Cardset.event == this_event)
|
||||
except Exception 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:
|
||||
all_cardsets = all_cardsets.where(Cardset.in_packs == in_packs)
|
||||
if ranked_legal is not None:
|
||||
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:
|
||||
data_list = [[
|
||||
'id', 'name', 'description', 'event_id', 'in_packs', 'for_purchase', 'total_cards', 'ranked_legal'
|
||||
]]
|
||||
for line in all_cardsets:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.name, line.description, line.event.id if line.event else '', line.in_packs,
|
||||
line.for_purchase, line.total_cards, line.ranked_legal
|
||||
]
|
||||
)
|
||||
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 = {'count': all_cardsets.count(), 'cardsets': []}
|
||||
for x in all_cardsets:
|
||||
return_val['cardsets'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{cardset_id}')
|
||||
async def get_one_cardset(cardset_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
this_cardset = Cardset.get_by_id(cardset_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'name', 'description'],
|
||||
[this_cardset.id, this_cardset.name, this_cardset.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_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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post cardsets. This event has been logged.'
|
||||
)
|
||||
|
||||
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__)
|
||||
|
||||
saved = this_cardset.save()
|
||||
if saved == 1:
|
||||
return_val = model_to_dict(this_cardset)
|
||||
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 cardset'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{cardset_id}')
|
||||
async def patch_cardsets(
|
||||
cardset_id, name: Optional[str] = None, description: Optional[str] = None, in_packs: Optional[bool] = None,
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}')
|
||||
|
||||
if name is not None:
|
||||
this_cardset.name = name
|
||||
if description is not None:
|
||||
this_cardset.description = description
|
||||
if in_packs is not None:
|
||||
this_cardset.in_packs = in_packs
|
||||
if for_purchase is not None:
|
||||
this_cardset.for_purchase = for_purchase
|
||||
if total_cards is not None:
|
||||
this_cardset.total_cards = total_cards
|
||||
if ranked_legal is not None:
|
||||
this_cardset.ranked_legal = ranked_legal
|
||||
|
||||
if this_cardset.save() == 1:
|
||||
return_val = model_to_dict(this_cardset)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Cardset {cardset_id} was not deleted')
|
||||
|
||||
|
||||
166
app/routers_v2/current.py
Normal file
166
app/routers_v2/current.py
Normal file
@ -0,0 +1,166 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from pandas import DataFrame
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/current',
|
||||
tags=['current']
|
||||
)
|
||||
|
||||
|
||||
class CurrentModel(pydantic.BaseModel):
|
||||
season: int
|
||||
week: int
|
||||
gsheet_template: str
|
||||
gsheet_version: str
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_current(season: Optional[int] = None, csv: Optional[bool] = False):
|
||||
if season:
|
||||
current = Current.get_or_none(season=season)
|
||||
else:
|
||||
current = Current.latest()
|
||||
|
||||
if csv:
|
||||
current_list = [
|
||||
['id', 'season', 'week'],
|
||||
[current.id, current.season, current.week]
|
||||
]
|
||||
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.get('/{current_id}')
|
||||
async def get_one_current(current_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
current = Current.get_by_id(current_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No current found with id {current_id}')
|
||||
|
||||
if csv:
|
||||
current_list = [
|
||||
['id', 'season', 'week'],
|
||||
[current.id, current.season, current.week]
|
||||
]
|
||||
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('')
|
||||
async def post_current(current: CurrentModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post current. This event has been logged.'
|
||||
)
|
||||
|
||||
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(
|
||||
season=current.season,
|
||||
week=current.week,
|
||||
gsheet_template=current.gsheet_template,
|
||||
gsheet_version=current.gsheet_version
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
|
||||
@router.patch('/{current_id}')
|
||||
async def patch_current(
|
||||
current_id: int, season: Optional[int] = None, week: Optional[int] = None,
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No current found with id {current_id}')
|
||||
|
||||
if season is not None:
|
||||
current.season = season
|
||||
if week is not None:
|
||||
current.week = week
|
||||
if gsheet_template is not None:
|
||||
current.gsheet_template = gsheet_template
|
||||
if gsheet_version is not None:
|
||||
current.gsheet_version = gsheet_version
|
||||
if live_scoreboard is not None:
|
||||
current.live_scoreboard = live_scoreboard
|
||||
|
||||
if current.save() == 1:
|
||||
return_val = model_to_dict(current)
|
||||
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 current'
|
||||
)
|
||||
|
||||
|
||||
@router.delete('/{current_id}')
|
||||
async def delete_current(current_id, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete current. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_curr = Current.get_by_id(current_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No current found with id {current_id}')
|
||||
|
||||
count = this_curr.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
raise HTTPException(status_code=200, detail=f'Current {current_id} has been deleted')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Current {current_id} was not deleted')
|
||||
230
app/routers_v2/decisions.py
Normal file
230
app/routers_v2/decisions.py
Normal file
@ -0,0 +1,230 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from typing import List, Optional, Literal
|
||||
import copy
|
||||
import logging
|
||||
import pandas as pd
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Decision, StratGame, Player, model_to_dict, chunked, fn, Team
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/decisions',
|
||||
tags=['decisions']
|
||||
)
|
||||
|
||||
|
||||
class DecisionModel(pydantic.BaseModel):
|
||||
game_id: int
|
||||
season: int
|
||||
week: int
|
||||
pitcher_id: int
|
||||
pitcher_team_id: int
|
||||
win: int = 0
|
||||
loss: int = 0
|
||||
hold: int = 0
|
||||
is_save: int = 0
|
||||
is_start: bool = False
|
||||
b_save: int = 0
|
||||
irunners: int = 0
|
||||
irunners_scored: int = 0
|
||||
rest_ip: float = 0
|
||||
rest_required: int = 0
|
||||
|
||||
|
||||
class DecisionList(pydantic.BaseModel):
|
||||
decisions: List[DecisionModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_decisions(
|
||||
season: list = Query(default=None), week: list = Query(default=None), team_id: list = Query(default=None),
|
||||
win: Optional[int] = None, loss: Optional[int] = None, hold: Optional[int] = None, save: Optional[int] = None,
|
||||
b_save: Optional[int] = None, irunners: list = Query(default=None), irunners_scored: list = Query(default=None),
|
||||
game_type: list = Query(default=None),
|
||||
game_id: list = Query(default=None), player_id: list = Query(default=None), csv: Optional[bool] = False,
|
||||
limit: Optional[int] = 100, page_num: Optional[int] = 1, short_output: Optional[bool] = False):
|
||||
all_dec = Decision.select().order_by(-Decision.season, -Decision.week, -Decision.id)
|
||||
|
||||
if season is not None:
|
||||
all_dec = all_dec.where(Decision.season << season)
|
||||
if week is not None:
|
||||
all_dec = all_dec.where(Decision.week << week)
|
||||
if game_id is not None:
|
||||
all_dec = all_dec.where(Decision.game_id << game_id)
|
||||
if player_id is not None:
|
||||
all_dec = all_dec.where(Decision.pitcher_id << player_id)
|
||||
if team_id is not None:
|
||||
all_dec = all_dec.where(Decision.pitcher_team_id << team_id)
|
||||
if win is not None:
|
||||
all_dec = all_dec.where(Decision.win == win)
|
||||
if loss is not None:
|
||||
all_dec = all_dec.where(Decision.loss == loss)
|
||||
if hold is not None:
|
||||
all_dec = all_dec.where(Decision.hold == hold)
|
||||
if save is not None:
|
||||
all_dec = all_dec.where(Decision.save == save)
|
||||
if b_save is not None:
|
||||
all_dec = all_dec.where(Decision.b_save == b_save)
|
||||
if irunners is not None:
|
||||
all_dec = all_dec.where(Decision.irunners << irunners)
|
||||
if irunners_scored is not None:
|
||||
all_dec = all_dec.where(Decision.irunners_scored << irunners_scored)
|
||||
|
||||
if game_type is not None:
|
||||
all_types = [x.lower() for x in game_type]
|
||||
all_games = StratGame.select().where(fn.Lower(StratGame.game_type) << all_types)
|
||||
all_dec = all_dec.where(Decision.game << all_games)
|
||||
if limit < 1:
|
||||
limit = 1
|
||||
if limit > 100:
|
||||
limit = 100
|
||||
all_dec = all_dec.paginate(page_num, limit)
|
||||
|
||||
return_dec = {
|
||||
'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']
|
||||
if len(return_vals) == 0:
|
||||
return Response(content=pd.DataFrame().to_csv(index=False), media_type='text/csv')
|
||||
|
||||
for x in return_vals:
|
||||
x['game_id'] = x['game']['id']
|
||||
x['game_type'] = x['game']['game_type']
|
||||
x['player_id'] = x['pitcher']['player_id']
|
||||
x['player_name'] = x['pitcher']['p_name']
|
||||
x['player_cardset'] = x['pitcher']['cardset']['name']
|
||||
x['team_id'] = x['pitcher_team']['id']
|
||||
x['team_abbrev'] = x['pitcher_team']['abbrev']
|
||||
del x['pitcher'], x['pitcher_team'], x['game']
|
||||
|
||||
output = pd.DataFrame(return_vals)
|
||||
first = ['player_id', 'player_name', 'player_cardset', 'team_id', 'team_abbrev']
|
||||
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')
|
||||
|
||||
return return_dec
|
||||
|
||||
|
||||
@router.patch('/{decision_id}')
|
||||
async def patch_decision(
|
||||
decision_id: int, win: Optional[int] = None, loss: Optional[int] = None, hold: Optional[int] = None,
|
||||
save: Optional[int] = None, b_save: Optional[int] = None, irunners: Optional[int] = None,
|
||||
irunners_scored: Optional[int] = None, rest_ip: Optional[int] = None, rest_required: Optional[int] = None,
|
||||
token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_decision - Bad Token: {token}')
|
||||
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')
|
||||
|
||||
if win is not None:
|
||||
this_dec.win = win
|
||||
if loss is not None:
|
||||
this_dec.loss = loss
|
||||
if hold is not None:
|
||||
this_dec.hold = hold
|
||||
if save is not None:
|
||||
this_dec.is_save = save
|
||||
if b_save is not None:
|
||||
this_dec.b_save = b_save
|
||||
if irunners is not None:
|
||||
this_dec.irunners = irunners
|
||||
if irunners_scored is not None:
|
||||
this_dec.irunners_scored = irunners_scored
|
||||
if rest_ip is not None:
|
||||
this_dec.rest_ip = rest_ip
|
||||
if rest_required is not None:
|
||||
this_dec.rest_required = rest_required
|
||||
|
||||
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}')
|
||||
|
||||
|
||||
@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}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_dec = []
|
||||
for x in dec_list.decisions:
|
||||
if StratGame.get_or_none(StratGame.id == x.game_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Game ID {x.game_id} not found')
|
||||
if Player.get_or_none(Player.player_id == x.pitcher_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Player ID {x.pitcher_id} not found')
|
||||
if Team.get_or_none(Team.id == x.pitcher_team_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.pitcher_team_id} not found')
|
||||
|
||||
new_dec.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_dec, 10):
|
||||
Decision.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_dec)} decisions'
|
||||
|
||||
|
||||
@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}')
|
||||
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'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Decision {decision_id} could not be deleted')
|
||||
|
||||
|
||||
@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}')
|
||||
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}'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'No decisions matching Game ID {game_id} were deleted')
|
||||
|
||||
|
||||
|
||||
195
app/routers_v2/events.py
Normal file
195
app/routers_v2/events.py
Normal file
@ -0,0 +1,195 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/events',
|
||||
tags=['events']
|
||||
)
|
||||
|
||||
|
||||
class EventModel(pydantic.BaseModel):
|
||||
name: str
|
||||
short_desc: str
|
||||
long_desc: str
|
||||
url: Optional[str] = None
|
||||
thumbnail: Optional[str] = None
|
||||
active: Optional[bool] = False
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def v1_events_get(
|
||||
name: Optional[str] = None, in_desc: Optional[str] = None, active: Optional[bool] = None,
|
||||
csv: Optional[bool] = None):
|
||||
all_events = Event.select()
|
||||
|
||||
if name is not None:
|
||||
all_events = all_events.where(fn.Lower(Event.name) == name.lower())
|
||||
if in_desc is not None:
|
||||
all_events = all_events.where(
|
||||
(fn.Lower(Event.short_desc).contains(in_desc.lower())) |
|
||||
(fn.Lower(Event.long_desc).contains(in_desc.lower()))
|
||||
)
|
||||
if active is not None:
|
||||
all_events = all_events.where(Event.active == active)
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'name', 'short_desc', 'long_desc', 'url', 'thumbnail', 'active']]
|
||||
for line in all_events:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.name, line.short_desc, line.long_desc, line.url, line.thumbnail, line.active
|
||||
]
|
||||
)
|
||||
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 = {'count': all_events.count(), 'events': []}
|
||||
for x in all_events:
|
||||
return_val['events'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{event_id}')
|
||||
async def v1_events_get_one(event_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
this_event = Event.get_by_id(event_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No event found with id {event_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'name', 'short_desc', 'long_desc', 'url', 'thumbnail', 'active'],
|
||||
[this_event.id, this_event.name, this_event.short_desc, this_event.long_desc, this_event.url,
|
||||
this_event.thumbnail, this_event.active]
|
||||
]
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post events. This event has been logged.'
|
||||
)
|
||||
|
||||
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(
|
||||
name=event.name,
|
||||
short_desc=event.short_desc,
|
||||
long_desc=event.long_desc,
|
||||
url=event.url,
|
||||
thumbnail=event.thumbnail,
|
||||
active=event.active
|
||||
)
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{event_id}')
|
||||
async def v1_events_patch(
|
||||
event_id, name: Optional[str] = None, short_desc: Optional[str] = None, long_desc: Optional[str] = None,
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No event found with id {event_id}')
|
||||
|
||||
if name is not None:
|
||||
this_event.name = name
|
||||
if short_desc is not None:
|
||||
this_event.short_desc = short_desc
|
||||
if long_desc is not None:
|
||||
this_event.long_desc = long_desc
|
||||
if url is not None:
|
||||
this_event.url = url
|
||||
if thumbnail is not None:
|
||||
this_event.thumbnail = thumbnail
|
||||
if active is not None:
|
||||
this_event.active = active
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Event {event_id} was not deleted')
|
||||
192
app/routers_v2/gamerewards.py
Normal file
192
app/routers_v2/gamerewards.py
Normal file
@ -0,0 +1,192 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/gamerewards',
|
||||
tags=['gamerewards']
|
||||
)
|
||||
|
||||
|
||||
class GameRewardModel(pydantic.BaseModel):
|
||||
name: str
|
||||
pack_type_id: Optional[int] = None
|
||||
player_id: Optional[int] = None
|
||||
money: Optional[int] = None
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def v1_gamerewards_get(
|
||||
name: Optional[str] = None, pack_type_id: Optional[int] = None, player_id: Optional[int] = None,
|
||||
money: Optional[int] = None, csv: Optional[bool] = None):
|
||||
all_rewards = GameRewards.select()
|
||||
|
||||
# if all_rewards.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'There are no awards to filter')
|
||||
|
||||
if name is not None:
|
||||
all_rewards = all_rewards.where(GameRewards.name == name)
|
||||
if pack_type_id is not None:
|
||||
all_rewards = all_rewards.where(GameRewards.pack_type_id == pack_type_id)
|
||||
if player_id is not None:
|
||||
all_rewards = all_rewards.where(GameRewards.player_id == player_id)
|
||||
if money is not None:
|
||||
all_rewards = all_rewards.where(GameRewards.money == money)
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'pack_type_id', 'player_id', 'money']]
|
||||
for line in all_rewards:
|
||||
data_list.append([
|
||||
line.id, line.pack_type_id if line.pack_type else None, line.player_id if line.player else None,
|
||||
line.money
|
||||
])
|
||||
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 = {'count': all_rewards.count(), 'gamerewards': []}
|
||||
for x in all_rewards:
|
||||
return_val['gamerewards'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{gameaward_id}')
|
||||
async def v1_gamerewards_get_one(gamereward_id, csv: Optional[bool] = None):
|
||||
try:
|
||||
this_game_reward = GameRewards.get_by_id(gamereward_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No game reward found with id {gamereward_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'pack_type_id', 'player_id', 'money'],
|
||||
[this_game_reward.id, this_game_reward.pack_type_id if this_game_reward.pack_type else None,
|
||||
this_game_reward.player_id if this_game_reward.player else None, this_game_reward.money]
|
||||
]
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post game rewards. This event has been logged.'
|
||||
)
|
||||
|
||||
this_award = GameRewards(
|
||||
name=game_reward.name,
|
||||
pack_type_id=game_reward.pack_type_id,
|
||||
player_id=game_reward.player_id,
|
||||
money=game_reward.money
|
||||
)
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{game_reward_id}')
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No game reward found with id {game_reward_id}')
|
||||
|
||||
if name is not None:
|
||||
this_game_reward.name = name
|
||||
if pack_type_id is not None:
|
||||
if not pack_type_id:
|
||||
this_game_reward.pack_type_id = None
|
||||
else:
|
||||
this_game_reward.pack_type_id = pack_type_id
|
||||
if player_id is not None:
|
||||
if not player_id:
|
||||
this_game_reward.player_id = None
|
||||
else:
|
||||
this_game_reward.player_id = player_id
|
||||
if money is not None:
|
||||
if not money:
|
||||
this_game_reward.money = None
|
||||
else:
|
||||
this_game_reward.money = money
|
||||
|
||||
if this_game_reward.save() == 1:
|
||||
return_val = model_to_dict(this_game_reward)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Game Reward {gamereward_id} was not deleted')
|
||||
|
||||
139
app/routers_v2/gauntletrewards.py
Normal file
139
app/routers_v2/gauntletrewards.py
Normal file
@ -0,0 +1,139 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import Optional, List
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, GauntletReward, model_to_dict, chunked, DatabaseError
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/gauntletrewards',
|
||||
tags=['gauntletrewards']
|
||||
)
|
||||
|
||||
|
||||
class GauntletRewardModel(pydantic.BaseModel):
|
||||
name: str
|
||||
gauntlet_id: Optional[int] = 0
|
||||
reward_id: Optional[int] = 0
|
||||
win_num: Optional[int] = 0
|
||||
loss_max: Optional[int] = 1
|
||||
|
||||
|
||||
class GauntletRewardList(pydantic.BaseModel):
|
||||
rewards: List[GauntletRewardModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def v1_gauntletreward_get(
|
||||
name: Optional[str] = None, gauntlet_id: Optional[int] = None, reward_id: list = Query(default=None),
|
||||
win_num: Optional[int] = None, loss_max: Optional[int] = None):
|
||||
all_rewards = GauntletReward.select()
|
||||
|
||||
if name is not None:
|
||||
all_rewards = all_rewards.where(GauntletReward.name == name)
|
||||
if gauntlet_id is not None:
|
||||
all_rewards = all_rewards.where(GauntletReward.gauntlet_id == gauntlet_id)
|
||||
if reward_id is not None:
|
||||
all_rewards = all_rewards.where(GauntletReward.reward_id << reward_id)
|
||||
if win_num is not None:
|
||||
all_rewards = all_rewards.where(GauntletReward.win_num == win_num)
|
||||
if loss_max is not None:
|
||||
all_rewards = all_rewards.where(GauntletReward.loss_max >= loss_max)
|
||||
|
||||
all_rewards = all_rewards.order_by(-GauntletReward.loss_max, GauntletReward.win_num)
|
||||
|
||||
return_val = {'count': all_rewards.count(), 'rewards': []}
|
||||
for x in all_rewards:
|
||||
return_val['rewards'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{gauntletreward_id}')
|
||||
async def v1_gauntletreward_get_one(gauntletreward_id):
|
||||
try:
|
||||
this_reward = GauntletReward.get_by_id(gauntletreward_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No gauntlet reward found with id {gauntletreward_id}')
|
||||
|
||||
return_val = model_to_dict(this_reward)
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.patch('/{gauntletreward_id}')
|
||||
async def v1_gauntletreward_patch(
|
||||
gauntletreward_id, name: Optional[str] = None, gauntlet_id: Optional[int] = None,
|
||||
reward_id: Optional[int] = None, win_num: Optional[int] = None, loss_max: Optional[int] = None,
|
||||
token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch gauntlet rewards. This event has been logged.'
|
||||
)
|
||||
|
||||
this_reward = GauntletReward.get_or_none(GauntletReward.id == gauntletreward_id)
|
||||
if this_reward is None:
|
||||
db.close()
|
||||
raise KeyError(f'Gauntlet Reward ID {gauntletreward_id} not found')
|
||||
|
||||
if gauntlet_id is not None:
|
||||
this_reward.gauntlet_id = gauntlet_id
|
||||
if reward_id is not None:
|
||||
this_reward.reward_id = reward_id
|
||||
if win_num is not None:
|
||||
this_reward.win_num = win_num
|
||||
if loss_max is not None:
|
||||
this_reward.loss_max = loss_max
|
||||
if name is not None:
|
||||
this_reward.name = name
|
||||
|
||||
if this_reward.save():
|
||||
r_curr = model_to_dict(this_reward)
|
||||
db.close()
|
||||
return r_curr
|
||||
else:
|
||||
db.close()
|
||||
raise DatabaseError(f'Unable to patch gauntlet reward {gauntletreward_id}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def v1_gauntletreward_post(gauntletreward: GauntletRewardList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post gauntlets. This event has been logged.'
|
||||
)
|
||||
|
||||
all_rewards = []
|
||||
for x in gauntletreward.rewards:
|
||||
all_rewards.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(all_rewards, 15):
|
||||
GauntletReward.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(all_rewards)} gauntlet rewards'
|
||||
|
||||
|
||||
@router.delete('/{gauntletreward_id}')
|
||||
async def v1_gauntletreward_delete(gauntletreward_id):
|
||||
if GauntletReward.delete_by_id(gauntletreward_id) == 1:
|
||||
return f'Deleted gauntlet reward ID {gauntletreward_id}'
|
||||
|
||||
raise DatabaseError(f'Unable to delete gauntlet run {gauntletreward_id}')
|
||||
|
||||
169
app/routers_v2/gauntletruns.py
Normal file
169
app/routers_v2/gauntletruns.py
Normal file
@ -0,0 +1,169 @@
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/gauntletruns',
|
||||
tags=['notifs']
|
||||
)
|
||||
|
||||
|
||||
class GauntletRunModel(pydantic.BaseModel):
|
||||
team_id: int
|
||||
gauntlet_id: int
|
||||
wins: Optional[int] = 0
|
||||
losses: Optional[int] = 0
|
||||
gsheet: Optional[str] = None
|
||||
created: Optional[int] = int(datetime.timestamp(datetime.now())*1000)
|
||||
ended: Optional[int] = 0
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_gauntletruns(
|
||||
team_id: list = Query(default=None), wins: Optional[int] = None, wins_min: Optional[int] = None,
|
||||
wins_max: Optional[int] = None, losses: Optional[int] = None, losses_min: Optional[int] = None,
|
||||
losses_max: Optional[int] = None, gsheet: Optional[str] = None, created_after: Optional[int] = None,
|
||||
created_before: Optional[int] = None, ended_after: Optional[int] = None, ended_before: Optional[int] = None,
|
||||
is_active: Optional[bool] = None, gauntlet_id: list = Query(default=None), season: list = Query(default=None)):
|
||||
all_gauntlets = GauntletRun.select()
|
||||
|
||||
if team_id is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.team_id << team_id)
|
||||
if wins is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.wins == wins)
|
||||
if wins_min is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.wins >= wins_min)
|
||||
if wins_max is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.wins <= wins_max)
|
||||
if losses is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.losses == losses)
|
||||
if losses_min is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.losses >= losses_min)
|
||||
if losses_max is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.losses <= losses_max)
|
||||
if gsheet is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.gsheet == gsheet)
|
||||
if created_after is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.created >= created_after)
|
||||
if created_before is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.created <= created_before)
|
||||
if ended_after is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.ended >= ended_after)
|
||||
if ended_before is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.ended <= ended_before)
|
||||
if is_active is not None:
|
||||
if is_active is True:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.ended == 0)
|
||||
else:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.ended != 0)
|
||||
if gauntlet_id is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.gauntlet_id << gauntlet_id)
|
||||
if season is not None:
|
||||
all_gauntlets = all_gauntlets.where(GauntletRun.team.season << season)
|
||||
|
||||
return_val = {'count': all_gauntlets.count(), 'runs': []}
|
||||
for x in all_gauntlets:
|
||||
return_val['runs'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{gauntletrun_id}')
|
||||
async def get_one_gauntletrun(gauntletrun_id):
|
||||
try:
|
||||
this_gauntlet = GauntletRun.get_by_id(gauntletrun_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No gauntlet found with id {gauntletrun_id}')
|
||||
|
||||
return_val = model_to_dict(this_gauntlet)
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.patch('/{gauntletrun_id}')
|
||||
async def patch_gauntletrun(
|
||||
gauntletrun_id, team_id: Optional[int] = None, wins: Optional[int] = None, losses: Optional[int] = None,
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch gauntlet runs. This event has been logged.'
|
||||
)
|
||||
|
||||
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:
|
||||
this_run.team_id = team_id
|
||||
if wins is not None:
|
||||
this_run.wins = wins
|
||||
if losses is not None:
|
||||
this_run.losses = losses
|
||||
if gsheet is not None:
|
||||
this_run.gsheet = gsheet
|
||||
if created is not None:
|
||||
if created is True:
|
||||
this_run.created = int(datetime.timestamp(datetime.now())*1000)
|
||||
else:
|
||||
this_run.created = None
|
||||
if ended is not None:
|
||||
if ended is True:
|
||||
this_run.ended = int(datetime.timestamp(datetime.now())*1000)
|
||||
else:
|
||||
this_run.ended = 0
|
||||
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post gauntlets. This event has been logged.'
|
||||
)
|
||||
|
||||
this_run = GauntletRun(**gauntletrun.dict())
|
||||
|
||||
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')
|
||||
|
||||
|
||||
@router.delete('/{gauntletrun_id}')
|
||||
async def delete_gauntletrun(gauntletrun_id):
|
||||
if GauntletRun.delete_by_id(gauntletrun_id) == 1:
|
||||
return f'Deleted gauntlet run ID {gauntletrun_id}'
|
||||
|
||||
raise DatabaseError(f'Unable to delete gauntlet run {gauntletrun_id}')
|
||||
|
||||
189
app/routers_v2/mlbplayers.py
Normal file
189
app/routers_v2/mlbplayers.py
Normal file
@ -0,0 +1,189 @@
|
||||
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, model_to_dict, fn, chunked, query_to_csv
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/mlbplayers',
|
||||
tags=['mlbplayers']
|
||||
)
|
||||
|
||||
|
||||
class PlayerModel(pydantic.BaseModel):
|
||||
first_name: str
|
||||
last_name: str
|
||||
key_fangraphs: int = None
|
||||
key_bbref: str = None
|
||||
key_retro: str = None
|
||||
key_mlbam: int = None
|
||||
offense_col: int = None
|
||||
|
||||
|
||||
class PlayerList(pydantic.BaseModel):
|
||||
players: List[PlayerModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_players(
|
||||
full_name: list = Query(default=None), first_name: list = Query(default=None),
|
||||
last_name: list = Query(default=None), key_fangraphs: list = Query(default=None),
|
||||
key_bbref: list = Query(default=None), key_retro: list = Query(default=None),
|
||||
key_mlbam: list = Query(default=None), offense_col: list = Query(default=None), csv: Optional[bool] = False):
|
||||
all_players = MlbPlayer.select()
|
||||
|
||||
if full_name is not None:
|
||||
name_list = [x.lower() for x in full_name]
|
||||
all_players = all_players.where(
|
||||
fn.lower(MlbPlayer.first_name) + ' ' + fn.lower(MlbPlayer.last_name) << name_list
|
||||
)
|
||||
if first_name is not None:
|
||||
all_players = all_players.where(MlbPlayer.first_name << first_name)
|
||||
if first_name is not None:
|
||||
all_players = all_players.where(MlbPlayer.first_name << first_name)
|
||||
if last_name is not None:
|
||||
all_players = all_players.where(MlbPlayer.last_name << last_name)
|
||||
if key_fangraphs is not None:
|
||||
all_players = all_players.where(MlbPlayer.key_fangraphs << key_fangraphs)
|
||||
if key_bbref is not None:
|
||||
all_players = all_players.where(MlbPlayer.key_bbref << key_bbref)
|
||||
if key_retro is not None:
|
||||
all_players = all_players.where(MlbPlayer.key_retro << key_retro)
|
||||
if key_mlbam is not None:
|
||||
all_players = all_players.where(MlbPlayer.key_mlbam << key_mlbam)
|
||||
if offense_col is not None:
|
||||
all_players = all_players.where(MlbPlayer.offense_col << offense_col)
|
||||
|
||||
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
|
||||
|
||||
|
||||
@router.get('/{player_id}')
|
||||
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
|
||||
|
||||
|
||||
@router.patch('/{player_id}')
|
||||
async def patch_player(
|
||||
player_id: int, first_name: Optional[str] = None, last_name: Optional[str] = None,
|
||||
key_fangraphs: Optional[str] = None, key_bbref: Optional[str] = None, key_retro: Optional[str] = None,
|
||||
key_mlbam: Optional[str] = None, offense_col: Optional[str] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch mlb players. This event has been logged.'
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
if first_name is not None:
|
||||
this_player.first_name = first_name
|
||||
if last_name is not None:
|
||||
this_player.last_name = last_name
|
||||
if key_fangraphs is not None:
|
||||
this_player.key_fangraphs = key_fangraphs
|
||||
if key_bbref is not None:
|
||||
this_player.key_bbref = key_bbref
|
||||
if key_retro is not None:
|
||||
this_player.key_retro = key_retro
|
||||
if key_mlbam is not None:
|
||||
this_player.key_mlbam = key_mlbam
|
||||
if offense_col is not None:
|
||||
this_player.offense_col = offense_col
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post mlb players. This event has been logged.'
|
||||
)
|
||||
|
||||
new_players = []
|
||||
for x in players.players:
|
||||
dupes = MlbPlayer.select().where(
|
||||
(MlbPlayer.key_fangraphs == x.key_fangraphs) | (MlbPlayer.key_mlbam == x.key_mlbam) |
|
||||
(MlbPlayer.key_retro == x.key_retro) | (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'
|
||||
)
|
||||
|
||||
new_players.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_players, 15):
|
||||
MlbPlayer.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_players)} new MLB players'
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete mlb players. This event has been logged.'
|
||||
)
|
||||
|
||||
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(status_code=200, detail=f'Player {player_id} has been deleted')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Player {player_id} was not deleted')
|
||||
201
app/routers_v2/notifications.py
Normal file
201
app/routers_v2/notifications.py
Normal file
@ -0,0 +1,201 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/notifs',
|
||||
tags=['notifs']
|
||||
)
|
||||
|
||||
|
||||
class NotifModel(pydantic.BaseModel):
|
||||
created: int
|
||||
title: str
|
||||
desc: Optional[str] = None
|
||||
field_name: str
|
||||
message: str
|
||||
about: Optional[str] = 'blank'
|
||||
ack: Optional[bool] = False
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_notifs(
|
||||
created_after: Optional[int] = None, title: Optional[str] = None, desc: Optional[str] = None,
|
||||
field_name: Optional[str] = None, in_desc: Optional[str] = None, about: Optional[str] = None,
|
||||
ack: Optional[bool] = None, csv: Optional[bool] = None):
|
||||
all_notif = Notification.select()
|
||||
|
||||
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:
|
||||
all_notif = all_notif.where(Notification.created < created_after)
|
||||
if title is not None:
|
||||
all_notif = all_notif.where(Notification.title == title)
|
||||
if desc is not None:
|
||||
all_notif = all_notif.where(Notification.desc == desc)
|
||||
if field_name is not None:
|
||||
all_notif = all_notif.where(Notification.field_name == field_name)
|
||||
if in_desc is not None:
|
||||
all_notif = all_notif.where(fn.Lower(Notification.desc).contains(in_desc.lower()))
|
||||
if about is not None:
|
||||
all_notif = all_notif.where(Notification.about == about)
|
||||
if ack is not None:
|
||||
all_notif = all_notif.where(Notification.ack == ack)
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'created', 'title', 'desc', 'field_name', 'message', 'about', 'ack']]
|
||||
for line in all_notif:
|
||||
data_list.append([
|
||||
line.id, line.created, line.title, line.desc, line.field_name, line.message, line.about, line.ack
|
||||
])
|
||||
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 = {'count': all_notif.count(), 'notifs': []}
|
||||
for x in all_notif:
|
||||
return_val['notifs'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{notif_id}')
|
||||
async def get_one_notif(notif_id, csv: Optional[bool] = None):
|
||||
try:
|
||||
this_notif = Notification.get_by_id(notif_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'created', 'title', 'desc', 'field_name', 'message', 'about', 'ack'],
|
||||
[this_notif.id, this_notif.created, this_notif.title, this_notif.desc, this_notif.field_name,
|
||||
this_notif.message, this_notif.about, this_notif.ack]
|
||||
]
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post notifications. This event has been logged.'
|
||||
)
|
||||
|
||||
logging.info(f'new notif: {notif}')
|
||||
this_notif = Notification(
|
||||
created=notif.created,
|
||||
title=notif.title,
|
||||
desc=notif.desc,
|
||||
field_name=notif.field_name,
|
||||
message=notif.message,
|
||||
about=notif.about,
|
||||
)
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{notif_id}')
|
||||
async def patch_notif(
|
||||
notif_id, created: Optional[int] = None, title: Optional[str] = None, desc: Optional[str] = None,
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No notification found with id {notif_id}')
|
||||
|
||||
if title is not None:
|
||||
this_notif.title = title
|
||||
if desc is not None:
|
||||
this_notif.desc = desc
|
||||
if field_name is not None:
|
||||
this_notif.field_name = field_name
|
||||
if message is not None:
|
||||
this_notif.message = message
|
||||
if about is not None:
|
||||
this_notif.about = about
|
||||
if ack is not None:
|
||||
this_notif.ack = ack
|
||||
if created is not None:
|
||||
this_notif.created = created
|
||||
|
||||
if this_notif.save() == 1:
|
||||
return_val = model_to_dict(this_notif)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Notification {notif_id} was not deleted')
|
||||
259
app/routers_v2/packs.py
Normal file
259
app/routers_v2/packs.py
Normal file
@ -0,0 +1,259 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional, List
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/packs',
|
||||
tags=['packs']
|
||||
)
|
||||
|
||||
|
||||
class PackPydantic(pydantic.BaseModel):
|
||||
team_id: int
|
||||
pack_type_id: int
|
||||
pack_team_id: Optional[int] = None
|
||||
pack_cardset_id: Optional[int] = None
|
||||
open_time: Optional[str] = None
|
||||
|
||||
|
||||
class PackModel(pydantic.BaseModel):
|
||||
packs: List[PackPydantic]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_packs(
|
||||
team_id: Optional[int] = None, pack_type_id: Optional[int] = None, opened: Optional[bool] = None,
|
||||
limit: Optional[int] = None, new_to_old: Optional[bool] = None, pack_team_id: Optional[int] = None,
|
||||
pack_cardset_id: Optional[int] = None, exact_match: Optional[bool] = False, csv: Optional[bool] = None):
|
||||
all_packs = Pack.select()
|
||||
|
||||
if all_packs.count() == 0:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'There are no packs to filter')
|
||||
|
||||
if team_id is not None:
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
all_packs = all_packs.where(Pack.team == this_team)
|
||||
if pack_type_id is not None:
|
||||
try:
|
||||
this_pack_type = PackType.get_by_id(pack_type_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No pack type found with id {pack_type_id}')
|
||||
all_packs = all_packs.where(Pack.pack_type == this_pack_type)
|
||||
|
||||
if pack_team_id is not None:
|
||||
try:
|
||||
this_pack_team = Team.get_by_id(pack_team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {pack_team_id}')
|
||||
all_packs = all_packs.where(Pack.pack_team == this_pack_team)
|
||||
elif exact_match:
|
||||
all_packs = all_packs.where(Pack.pack_team == None)
|
||||
|
||||
if pack_cardset_id is not None:
|
||||
try:
|
||||
this_pack_cardset = Cardset.get_by_id(pack_cardset_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No cardset found with id {pack_cardset_id}')
|
||||
all_packs = all_packs.where(Pack.pack_cardset == this_pack_cardset)
|
||||
elif exact_match:
|
||||
all_packs = all_packs.where(Pack.pack_cardset == None)
|
||||
|
||||
if opened is not None:
|
||||
all_packs = all_packs.where(Pack.open_time.is_null(not opened))
|
||||
if limit is not None:
|
||||
all_packs = all_packs.limit(limit)
|
||||
if new_to_old is not None:
|
||||
all_packs = all_packs.order_by(-Pack.id)
|
||||
|
||||
# if all_packs.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No packs found')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'team', 'pack_type', 'open_time']]
|
||||
for line in all_packs:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.team.abbrev, line.pack_type.name,
|
||||
datetime.fromtimestamp(line.open_time) if line.open_time else 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 = {'count': all_packs.count(), '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):
|
||||
try:
|
||||
this_pack = Pack.get_by_id(pack_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'team', 'pack_type', 'open_time'],
|
||||
[this_pack.id, this_pack.team.abbrev, this_pack.pack_type.name,
|
||||
datetime.fromtimestamp(this_pack.open_time) if this_pack.open_time else 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_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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post packs. This event has been logged.'
|
||||
)
|
||||
|
||||
new_packs = []
|
||||
for x in packs.packs:
|
||||
this_player = Pack(
|
||||
team_id=x.team_id,
|
||||
pack_type_id=x.pack_type_id,
|
||||
pack_team_id=x.pack_team_id,
|
||||
pack_cardset_id=x.pack_cardset_id,
|
||||
open_time=x.open_time if x.open_time != "" else None
|
||||
)
|
||||
new_packs.append(this_player)
|
||||
|
||||
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')
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post packs. This event has been logged.'
|
||||
)
|
||||
|
||||
this_pack = Pack(
|
||||
team_id=pack.team_id,
|
||||
pack_type_id=pack.pack_type_id,
|
||||
pack_team_id=pack.pack_team_id,
|
||||
pack_cardset_id=pack.pack_cardset_id,
|
||||
open_time=pack.open_time
|
||||
)
|
||||
|
||||
saved = this_pack.save()
|
||||
if saved == 1:
|
||||
return_val = model_to_dict(this_pack)
|
||||
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 cardset'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{pack_id}')
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No pack found with id {pack_id}')
|
||||
|
||||
if team_id is not None:
|
||||
this_pack.team_id = team_id
|
||||
if pack_type_id is not None:
|
||||
this_pack.pack_type_id = pack_type_id
|
||||
if pack_team_id is not None:
|
||||
this_pack.pack_team_id = pack_team_id
|
||||
if pack_cardset_id is not None:
|
||||
this_pack.pack_cardset_id = pack_cardset_id
|
||||
if open_time is not None:
|
||||
this_pack.open_time = open_time
|
||||
|
||||
if this_pack.save() == 1:
|
||||
return_val = model_to_dict(this_pack)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Pack {pack_id} was not deleted')
|
||||
194
app/routers_v2/packtypes.py
Normal file
194
app/routers_v2/packtypes.py
Normal file
@ -0,0 +1,194 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/packtypes',
|
||||
tags=['packtypes']
|
||||
)
|
||||
|
||||
|
||||
class PacktypeModel(pydantic.BaseModel):
|
||||
name: str
|
||||
card_count: int
|
||||
description: str
|
||||
cost: int
|
||||
available: Optional[bool] = True
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_packtypes(
|
||||
name: Optional[str] = None, card_count: Optional[int] = None, in_desc: Optional[str] = None,
|
||||
available: Optional[bool] = None, csv: Optional[bool] = None):
|
||||
all_packtypes = PackType.select()
|
||||
|
||||
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:
|
||||
all_packtypes = all_packtypes.where(fn.Lower(PackType.name) == name.lower())
|
||||
if card_count is not None:
|
||||
all_packtypes = all_packtypes.where(PackType.card_count == card_count)
|
||||
if in_desc is not None:
|
||||
all_packtypes = all_packtypes.where(fn.Lower(PackType.description).contains(in_desc.lower()))
|
||||
if available is not None:
|
||||
all_packtypes = all_packtypes.where(PackType.available == available)
|
||||
|
||||
# if all_packtypes.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No packtypes found')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'name', 'card_count', 'description']]
|
||||
for line in all_packtypes:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.name, line.card_count, line.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 = {'count': all_packtypes.count(), 'packtypes': []}
|
||||
for x in all_packtypes:
|
||||
return_val['packtypes'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{packtype_id}')
|
||||
async def get_one_packtype(packtype_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
this_packtype = PackType.get_by_id(packtype_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'name', 'card_count', 'description'],
|
||||
[this_packtype.id, this_packtype.name, this_packtype.card_count, this_packtype.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_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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post packtypes. This event has been logged.'
|
||||
)
|
||||
|
||||
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(
|
||||
name=packtype.name,
|
||||
card_count=packtype.card_count,
|
||||
description=packtype.description,
|
||||
cost=packtype.cost,
|
||||
available=packtype.available
|
||||
)
|
||||
|
||||
saved = this_packtype.save()
|
||||
if saved == 1:
|
||||
return_val = model_to_dict(this_packtype)
|
||||
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 cardset'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{packtype_id}')
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No packtype found with id {packtype_id}')
|
||||
|
||||
if name is not None:
|
||||
this_packtype.name = name
|
||||
if card_count is not None:
|
||||
this_packtype.card_count = card_count
|
||||
if description is not None:
|
||||
this_packtype.description = description
|
||||
if cost is not None:
|
||||
this_packtype.cost = cost
|
||||
if available is not None:
|
||||
this_packtype.available = available
|
||||
|
||||
if this_packtype.save() == 1:
|
||||
return_val = model_to_dict(this_packtype)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Packtype {packtype_id} was not deleted')
|
||||
207
app/routers_v2/paperdex.py
Normal file
207
app/routers_v2/paperdex.py
Normal file
@ -0,0 +1,207 @@
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/paperdex',
|
||||
tags=['paperdex']
|
||||
)
|
||||
|
||||
|
||||
class PaperdexModel(pydantic.BaseModel):
|
||||
team_id: int
|
||||
player_id: int
|
||||
created: Optional[int] = int(datetime.timestamp(datetime.now())*1000)
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_paperdex(
|
||||
team_id: Optional[int] = None, player_id: Optional[int] = None, created_after: Optional[int] = None,
|
||||
cardset_id: Optional[int] = None, created_before: Optional[int] = None, flat: Optional[bool] = False,
|
||||
csv: Optional[bool] = None):
|
||||
all_dex = Paperdex.select().join(Player).join(Cardset)
|
||||
|
||||
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:
|
||||
all_dex = all_dex.where(Paperdex.team_id == team_id)
|
||||
if player_id is not None:
|
||||
all_dex = all_dex.where(Paperdex.player_id == player_id)
|
||||
if cardset_id is not None:
|
||||
all_sets = Cardset.select().where(Cardset.id == cardset_id)
|
||||
all_dex = all_dex.where(Paperdex.player.cardset.id == cardset_id)
|
||||
if created_after is not None:
|
||||
all_dex = all_dex.where(Paperdex.created >= created_after)
|
||||
if created_before is not None:
|
||||
all_dex = all_dex.where(Paperdex.created <= created_before)
|
||||
|
||||
# if all_dex.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No paperdex found')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'team_id', 'player_id', 'created']]
|
||||
for line in all_dex:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.team.id, line.player.player_id, line.created
|
||||
]
|
||||
)
|
||||
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 = {'count': all_dex.count(), 'paperdex': []}
|
||||
for x in all_dex:
|
||||
return_val['paperdex'].append(model_to_dict(x, recurse=not flat))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{paperdex_id}')
|
||||
async def get_one_paperdex(paperdex_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
this_dex = Paperdex.get_by_id(paperdex_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'team_id', 'player_id', 'created'],
|
||||
[this_dex.id, this_dex.team.id, this_dex.player.id, this_dex.created]
|
||||
]
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post paperdex. This event has been logged.'
|
||||
)
|
||||
|
||||
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(
|
||||
team_id=paperdex.team_id,
|
||||
player_id=paperdex.player_id,
|
||||
created=paperdex.created
|
||||
)
|
||||
|
||||
saved = this_dex.save()
|
||||
if saved == 1:
|
||||
return_val = model_to_dict(this_dex)
|
||||
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 dex'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{paperdex_id}')
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No paperdex found with id {paperdex_id}')
|
||||
|
||||
if team_id is not None:
|
||||
this_dex.team_id = team_id
|
||||
if player_id is not None:
|
||||
this_dex.player_id = player_id
|
||||
if created is not None:
|
||||
this_dex.created = created
|
||||
|
||||
if this_dex.save() == 1:
|
||||
return_val = model_to_dict(this_dex)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Paperdex {this_dex} was not deleted')
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='Unauthorized'
|
||||
)
|
||||
|
||||
g_teams = Team.select().where(Team.abbrev.contains('Gauntlet'))
|
||||
count = Paperdex.delete().where(Paperdex.team << g_teams).execute()
|
||||
return f'Deleted {count} records'
|
||||
267
app/routers_v2/pitchingcardratings.py
Normal file
267
app/routers_v2/pitchingcardratings.py
Normal file
@ -0,0 +1,267 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from typing import Literal, Optional, List
|
||||
import logging
|
||||
import pandas as pd
|
||||
import pydantic
|
||||
from pydantic import validator, root_validator
|
||||
|
||||
from ..db_engine import db, PitchingCardRatings, model_to_dict, chunked, PitchingCard, Player, query_to_csv, Team
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/pitchingcardratings',
|
||||
tags=['pitchingcardratings']
|
||||
)
|
||||
|
||||
|
||||
class PitchingCardRatingsModel(pydantic.BaseModel):
|
||||
pitchingcard_id: int
|
||||
vs_hand: Literal['R', 'L', 'vR', 'vL']
|
||||
homerun: float = 0.0
|
||||
bp_homerun: float = 0.0
|
||||
triple: float = 0.0
|
||||
double_three: float = 0.0
|
||||
double_two: float = 0.0
|
||||
double_cf: float = 0.0
|
||||
single_two: float = 0.0
|
||||
single_one: float = 0.0
|
||||
single_center: float = 0.0
|
||||
bp_single: float = 0.0
|
||||
hbp: float = 0.0
|
||||
walk: float = 0.0
|
||||
strikeout: float = 0.0
|
||||
flyout_lf_b: float = 0.0
|
||||
flyout_cf_b: float = 0.0
|
||||
flyout_rf_b: float = 0.0
|
||||
groundout_a: float = 0.0
|
||||
groundout_b: float = 0.0
|
||||
xcheck_p: float = 0.0
|
||||
xcheck_c: float = 0.0
|
||||
xcheck_1b: float = 0.0
|
||||
xcheck_2b: float = 0.0
|
||||
xcheck_3b: float = 0.0
|
||||
xcheck_ss: float = 0.0
|
||||
xcheck_lf: float = 0.0
|
||||
xcheck_cf: float = 0.0
|
||||
xcheck_rf: float = 0.0
|
||||
avg: float = 0.0
|
||||
obp: float = 0.0
|
||||
slg: float = 0.0
|
||||
|
||||
@validator("avg", always=True)
|
||||
def avg_validator(cls, v, values, **kwargs):
|
||||
return (values['homerun'] + values['bp_homerun'] / 2 + values['triple'] + values['double_three'] +
|
||||
values['double_two'] + values['double_cf'] + values['single_two'] + values['single_one'] +
|
||||
values['single_center'] + values['bp_single'] / 2) / 108
|
||||
|
||||
@validator("obp", always=True)
|
||||
def obp_validator(cls, v, values, **kwargs):
|
||||
return ((values['hbp'] + values['walk']) / 108) + values['avg']
|
||||
|
||||
@validator("slg", always=True)
|
||||
def slg_validator(cls, v, values, **kwargs):
|
||||
return (values['homerun'] * 4 + values['bp_homerun'] * 2 + values['triple'] * 3 + values['double_three'] * 2 +
|
||||
values['double_two'] * 2 + values['double_cf'] * 2 + values['single_two'] + values['single_one'] +
|
||||
values['single_center'] + values['bp_single'] / 2) / 108
|
||||
|
||||
@root_validator
|
||||
def validate_chance_total(cls, values):
|
||||
total_chances = (
|
||||
values['homerun'] + values['bp_homerun'] + values['triple'] + values['double_three'] +
|
||||
values['double_two'] + values['double_cf'] + values['single_two'] + values['single_one'] +
|
||||
values['single_center'] + values['bp_single'] + values['hbp'] + values['walk'] +
|
||||
values['strikeout'] + values['flyout_lf_b'] + values['flyout_cf_b'] + values['flyout_rf_b'] +
|
||||
values['groundout_a'] + values['groundout_b'] + values['xcheck_p'] + values['xcheck_c'] +
|
||||
values['xcheck_1b'] + values['xcheck_2b'] + values['xcheck_3b'] + values['xcheck_ss'] +
|
||||
values['xcheck_lf'] + values['xcheck_cf'] + values['xcheck_rf'])
|
||||
|
||||
if round(total_chances) != 108:
|
||||
raise ValueError("Must have exactly 108 chances on the card")
|
||||
return values
|
||||
|
||||
|
||||
class RatingsList(pydantic.BaseModel):
|
||||
ratings: List[PitchingCardRatingsModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_card_ratings(
|
||||
pitchingcard_id: list = Query(default=None), vs_hand: Literal['R', 'L', 'vR', 'vL'] = None,
|
||||
short_output: bool = False, csv: bool = False, cardset_id: list = Query(default=None),
|
||||
token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to pull card ratings.'
|
||||
)
|
||||
|
||||
all_ratings = PitchingCardRatings.select()
|
||||
|
||||
if pitchingcard_id is not None:
|
||||
all_ratings = all_ratings.where(PitchingCardRatings.pitchingcard_id << pitchingcard_id)
|
||||
if vs_hand is not None:
|
||||
all_ratings = all_ratings.where(PitchingCardRatings.vs_hand == vs_hand[-1])
|
||||
if cardset_id is not None:
|
||||
set_players = Player.select(Player.player_id).where(Player.cardset_id << cardset_id)
|
||||
set_cards = PitchingCard.select(PitchingCard.id).where(PitchingCard.player << set_players)
|
||||
all_ratings = all_ratings.where(PitchingCardRatings.pitchingcard << set_cards)
|
||||
|
||||
if csv:
|
||||
return_val = query_to_csv(all_ratings)
|
||||
db.close()
|
||||
return Response(content=return_val, media_type='text/csv')
|
||||
|
||||
else:
|
||||
return_val = {'count': all_ratings.count(), 'ratings': [
|
||||
model_to_dict(x, recurse=not short_output) for x in all_ratings
|
||||
]}
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/scouting')
|
||||
async def get_card_scouting(team_id: int, ts: str, cardset_id: list = Query(default=None)):
|
||||
this_team = Team.get_or_none(Team.id == team_id)
|
||||
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!): ' \
|
||||
'https://ko-fi.com/manticorum/shop'
|
||||
|
||||
all_ratings = PitchingCardRatings.select()
|
||||
if cardset_id is not None:
|
||||
set_players = Player.select(Player.player_id).where(Player.cardset_id << cardset_id)
|
||||
set_cards = PitchingCard.select(PitchingCard.id).where(PitchingCard.player << set_players)
|
||||
all_ratings = all_ratings.where(PitchingCardRatings.pitchingcard << set_cards)
|
||||
|
||||
vl_query = all_ratings.where(PitchingCardRatings.vs_hand == 'L')
|
||||
vr_query = all_ratings.where(PitchingCardRatings.vs_hand == 'R')
|
||||
|
||||
vl_vals = [model_to_dict(x) for x in vl_query]
|
||||
for x in vl_vals:
|
||||
x.update(x['pitchingcard'])
|
||||
x['player_id'] = x['pitchingcard']['player']['player_id']
|
||||
x['player_name'] = x['pitchingcard']['player']['p_name']
|
||||
x['rarity'] = x['pitchingcard']['player']['rarity']['name']
|
||||
x['cardset_id'] = x['pitchingcard']['player']['cardset']['id']
|
||||
x['cardset_name'] = x['pitchingcard']['player']['cardset']['name']
|
||||
del x['pitchingcard']
|
||||
del x['player']
|
||||
|
||||
vr_vals = [model_to_dict(x) for x in vr_query]
|
||||
for x in vr_vals:
|
||||
x['player_id'] = x['pitchingcard']['player']['player_id']
|
||||
del x['pitchingcard']
|
||||
|
||||
vl = pd.DataFrame(vl_vals)
|
||||
vr = pd.DataFrame(vr_vals)
|
||||
db.close()
|
||||
|
||||
output = pd.merge(vl, vr, on='player_id', suffixes=('_vl', '_vr'))
|
||||
first = ['player_id', 'player_name', 'cardset_name', 'rarity', 'hand', 'variant']
|
||||
exclude = first + ['id_vl', 'id_vr', 'vs_hand_vl', 'vs_hand_vr']
|
||||
output = output[first + [col for col in output.columns if col not in exclude]].sort_values(by=['player_id'])
|
||||
# output = output.sort_values(by=['player_id'])
|
||||
return Response(content=pd.DataFrame(output).to_csv(index=False), media_type='text/csv')
|
||||
|
||||
|
||||
@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()
|
||||
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
|
||||
|
||||
|
||||
@router.get('/player/{player_id}')
|
||||
async def get_player_ratings(player_id: int, variant: list = Query(default=None), short_output: bool = False):
|
||||
all_cards = PitchingCard.select().where(PitchingCard.player_id == player_id).order_by(PitchingCard.variant)
|
||||
if variant is not None:
|
||||
all_cards = all_cards.where(PitchingCard.variant << variant)
|
||||
|
||||
all_ratings = PitchingCardRatings.select().where(PitchingCardRatings.pitchingcard << all_cards)
|
||||
|
||||
return_val = {'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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post card ratings.'
|
||||
)
|
||||
|
||||
new_ratings = []
|
||||
updates = 0
|
||||
for x in ratings.ratings:
|
||||
try:
|
||||
PitchingCardRatings.get(
|
||||
(PitchingCardRatings.pitchingcard_id == x.pitchingcard_id) & (PitchingCardRatings.vs_hand == x.vs_hand)
|
||||
)
|
||||
updates += PitchingCardRatings.update(x.dict()).where(
|
||||
(PitchingCardRatings.pitchingcard_id == x.pitchingcard_id) & (PitchingCardRatings.vs_hand == x.vs_hand)
|
||||
).execute()
|
||||
except PitchingCardRatings.DoesNotExist:
|
||||
new_ratings.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_ratings, 30):
|
||||
PitchingCardRatings.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
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()
|
||||
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'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Rating {this_rating} could not be deleted')
|
||||
|
||||
218
app/routers_v2/pitchingcards.py
Normal file
218
app/routers_v2/pitchingcards.py
Normal file
@ -0,0 +1,218 @@
|
||||
import random
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import Literal, Optional, List
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, PitchingCard, model_to_dict, chunked, Player, fn, MlbPlayer
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/pitchingcards',
|
||||
tags=['pitchingcards']
|
||||
)
|
||||
|
||||
|
||||
class PitchingCardModel(pydantic.BaseModel):
|
||||
player_id: int
|
||||
variant: int = 0
|
||||
balk: int = 0
|
||||
wild_pitch: int = 0
|
||||
hold: int = 0
|
||||
starter_rating: int = 1
|
||||
relief_rating: int = 0
|
||||
closer_rating: int = None
|
||||
batting: str = "#1WR-C"
|
||||
offense_col: int = None
|
||||
hand: Literal['R', 'L', 'S'] = 'R'
|
||||
|
||||
|
||||
class PitchingCardList(pydantic.BaseModel):
|
||||
cards: List[PitchingCardModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_pitching_cards(
|
||||
player_id: list = Query(default=None), player_name: list = Query(default=None),
|
||||
cardset_id: list = Query(default=None), short_output: bool = False, limit: Optional[int] = None):
|
||||
all_cards = PitchingCard.select()
|
||||
if player_id is not None:
|
||||
all_cards = all_cards.where(PitchingCard.player_id << player_id)
|
||||
if cardset_id is not None:
|
||||
all_players = Player.select().where(Player.cardset_id << cardset_id)
|
||||
all_cards = all_cards.where(PitchingCard.player << all_players)
|
||||
if player_name is not None:
|
||||
name_list = [x.lower() for x in player_name]
|
||||
all_players = Player.select().where(fn.lower(Player.p_name) << name_list)
|
||||
all_cards = all_cards.where(PitchingCard.player << all_players)
|
||||
|
||||
if limit is not None:
|
||||
all_cards = all_cards.limit(limit)
|
||||
|
||||
return_val = {'count': all_cards.count(), 'cards': [
|
||||
model_to_dict(x, recurse=not short_output) for x in all_cards
|
||||
]}
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{card_id}')
|
||||
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
|
||||
|
||||
|
||||
@router.get('/player/{player_id}')
|
||||
async def get_player_cards(player_id: int, variant: list = Query(default=None), short_output: bool = False):
|
||||
all_cards = PitchingCard.select().where(PitchingCard.player_id == player_id).order_by(PitchingCard.variant)
|
||||
if variant is not None:
|
||||
all_cards = all_cards.where(PitchingCard.variant << variant)
|
||||
|
||||
return_val = {'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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post pitching cards. This event has been logged.'
|
||||
)
|
||||
|
||||
new_cards = []
|
||||
updates = 0
|
||||
|
||||
for x in cards.cards:
|
||||
try:
|
||||
old = PitchingCard.get(
|
||||
(PitchingCard.player_id == x.player_id) & (PitchingCard.variant == x.variant)
|
||||
)
|
||||
|
||||
if x.offense_col is None:
|
||||
x.offense_col = old.offense_col
|
||||
updates += PitchingCard.update(x.dict()).where(
|
||||
(PitchingCard.player_id == x.player_id) & (PitchingCard.variant == x.variant)
|
||||
).execute()
|
||||
except PitchingCard.DoesNotExist:
|
||||
if x.offense_col is None:
|
||||
this_player = Player.get_or_none(Player.player_id == x.player_id)
|
||||
mlb_player = MlbPlayer.get_or_none(MlbPlayer.key_bbref == this_player.bbref_id)
|
||||
if mlb_player is not None:
|
||||
logging.info(f'setting offense_col to {mlb_player.offense_col} for {this_player.p_name}')
|
||||
x.offense_col = mlb_player.offense_col
|
||||
else:
|
||||
logging.info(f'randomly setting offense_col for {this_player.p_name}')
|
||||
x.offense_col = random.randint(1, 3)
|
||||
logging.debug(f'x.dict(): {x.dict()}')
|
||||
new_cards.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_cards, 30):
|
||||
PitchingCard.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
db.close()
|
||||
return f'Updated cards: {updates}; new cards: {len(new_cards)}'
|
||||
|
||||
|
||||
@router.patch('/{card_id}')
|
||||
async def patch_card(
|
||||
card_id: int, balk: Optional[int] = None, wild_pitch: Optional[int] = None, hold: Optional[int] = None,
|
||||
starter_rating: Optional[int] = None, relief_rating: Optional[int] = None, closer_rating: Optional[int] = None,
|
||||
batting: Optional[int] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch pitching cards. This event has been logged.'
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
if balk is not None:
|
||||
this_card.balk = balk
|
||||
if wild_pitch is not None:
|
||||
this_card.wild_pitch = wild_pitch
|
||||
if hold is not None:
|
||||
this_card.hold = hold
|
||||
if starter_rating is not None:
|
||||
this_card.starter_rating = starter_rating
|
||||
if relief_rating is not None:
|
||||
this_card.relief_rating = relief_rating
|
||||
if closer_rating is not None:
|
||||
this_card.closer_rating = closer_rating
|
||||
if batting is not None:
|
||||
this_card.batting = batting
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete pitching cards. This event has been logged.'
|
||||
)
|
||||
|
||||
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'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Card {this_card} could not be deleted')
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete pitching cards. This event has been logged.'
|
||||
)
|
||||
|
||||
d_query = PitchingCard.delete()
|
||||
d_query.execute()
|
||||
|
||||
return f'Deleted {d_query.count()} pitching cards'
|
||||
191
app/routers_v2/pitstats.py
Normal file
191
app/routers_v2/pitstats.py
Normal file
@ -0,0 +1,191 @@
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional, List
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/pitstats',
|
||||
tags=['pitstats']
|
||||
)
|
||||
|
||||
|
||||
class PitStat(pydantic.BaseModel):
|
||||
card_id: int
|
||||
team_id: int
|
||||
vs_team_id: int
|
||||
roster_num: int
|
||||
ip: float
|
||||
hit: Optional[int] = 0
|
||||
run: Optional[int] = 0
|
||||
erun: Optional[int] = 0
|
||||
so: Optional[int] = 0
|
||||
bb: Optional[int] = 0
|
||||
hbp: Optional[int] = 0
|
||||
wp: Optional[int] = 0
|
||||
balk: Optional[int] = 0
|
||||
hr: Optional[int] = 0
|
||||
ir: Optional[int] = 0
|
||||
irs: Optional[int] = 0
|
||||
gs: Optional[int] = 0
|
||||
win: Optional[int] = 0
|
||||
loss: Optional[int] = 0
|
||||
hold: Optional[int] = 0
|
||||
sv: Optional[int] = 0
|
||||
bsv: Optional[int] = 0
|
||||
week: int
|
||||
season: int
|
||||
created: Optional[int] = int(datetime.timestamp(datetime.now())*100000)
|
||||
game_id: int
|
||||
|
||||
|
||||
class PitchingStatModel(pydantic.BaseModel):
|
||||
stats: List[PitStat]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_pit_stats(
|
||||
card_id: int = None, player_id: int = None, team_id: int = None, vs_team_id: int = None, week: int = None,
|
||||
season: int = None, week_start: int = None, week_end: int = None, created: int = None, gs: bool = None,
|
||||
csv: bool = None):
|
||||
all_stats = PitchingStat.select().join(Card).join(Player)
|
||||
logging.debug(f'pit query:\n\n{all_stats}')
|
||||
|
||||
if season is not None:
|
||||
all_stats = all_stats.where(PitchingStat.season == season)
|
||||
else:
|
||||
curr = Current.latest()
|
||||
all_stats = all_stats.where(PitchingStat.season == curr.season)
|
||||
|
||||
if card_id is not None:
|
||||
all_stats = all_stats.where(PitchingStat.card_id == card_id)
|
||||
if player_id is not None:
|
||||
all_stats = all_stats.where(PitchingStat.card.player.player_id == player_id)
|
||||
if team_id is not None:
|
||||
all_stats = all_stats.where(PitchingStat.team_id == team_id)
|
||||
if vs_team_id is not None:
|
||||
all_stats = all_stats.where(PitchingStat.vs_team_id == vs_team_id)
|
||||
if week is not None:
|
||||
all_stats = all_stats.where(PitchingStat.week == week)
|
||||
if week_start is not None:
|
||||
all_stats = all_stats.where(PitchingStat.week >= week_start)
|
||||
if week_end is not None:
|
||||
all_stats = all_stats.where(PitchingStat.week <= week_end)
|
||||
if created is not None:
|
||||
all_stats = all_stats.where(PitchingStat.created <= created)
|
||||
if gs is not None:
|
||||
all_stats = all_stats.where(PitchingStat.gs == 1 if gs else 0)
|
||||
|
||||
# if all_stats.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No pitching stats found')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'card_id', 'player_id', 'cardset', 'team', 'vs_team', 'ip', 'hit', 'run', 'erun', 'so', 'bb', 'hbp',
|
||||
'wp', 'balk', 'hr', 'ir', 'irs', 'gs', 'win', 'loss', 'hold', 'sv', 'bsv', 'week', 'season',
|
||||
'created', 'game_id', 'roster_num']]
|
||||
for line in all_stats:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.card.id, line.card.player.player_id, line.card.player.cardset.name, line.team.abbrev,
|
||||
line.vs_team.abbrev, line.ip, line.hit,
|
||||
line.run, line.erun, line.so, line.bb, line.hbp, line.wp, line.balk, line.hr, line.ir, line.irs,
|
||||
line.gs, line.win, line.loss, line.hold, line.sv, line.bsv, line.week, line.season, line.created,
|
||||
line.game_id, line.roster_num
|
||||
]
|
||||
)
|
||||
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 = {'count': all_stats.count(), '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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post stats. This event has been logged.'
|
||||
)
|
||||
|
||||
new_stats = []
|
||||
for x in stats.stats:
|
||||
this_stat = PitchingStat(
|
||||
card_id=x.card_id,
|
||||
team_id=x.team_id,
|
||||
vs_team_id=x.vs_team_id,
|
||||
roster_num=x.roster_num,
|
||||
ip=x.ip,
|
||||
hit=x.hit,
|
||||
run=x.run,
|
||||
erun=x.erun,
|
||||
so=x.so,
|
||||
bb=x.bb,
|
||||
hbp=x.hbp,
|
||||
wp=x.wp,
|
||||
balk=x.balk,
|
||||
hr=x.hr,
|
||||
ir=x.ir,
|
||||
irs=x.irs,
|
||||
gs=x.gs,
|
||||
win=x.win,
|
||||
loss=x.loss,
|
||||
hold=x.hold,
|
||||
sv=x.sv,
|
||||
bsv=x.bsv,
|
||||
week=x.week,
|
||||
season=x.season,
|
||||
created=x.created,
|
||||
game_id=x.game_id
|
||||
)
|
||||
new_stats.append(this_stat)
|
||||
|
||||
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')
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Stat {stat_id} was not deleted')
|
||||
708
app/routers_v2/players.py
Normal file
708
app/routers_v2/players.py
Normal file
@ -0,0 +1,708 @@
|
||||
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
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..card_creation import get_batter_card_data, get_pitcher_card_data
|
||||
from ..db_engine import db, Player, model_to_dict, fn, chunked, Paperdex, Cardset, Rarity, BattingCard, \
|
||||
BattingCardRatings, PitchingCard, PitchingCardRatings, CardPosition
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/players',
|
||||
tags=['players']
|
||||
)
|
||||
|
||||
|
||||
templates = Jinja2Templates(directory="storage/templates")
|
||||
|
||||
|
||||
class PlayerPydantic(pydantic.BaseModel):
|
||||
player_id: int = None
|
||||
p_name: str
|
||||
cost: int
|
||||
image: str
|
||||
image2: Optional[str] = None
|
||||
mlbclub: str
|
||||
franchise: str
|
||||
cardset_id: int
|
||||
set_num: int
|
||||
rarity_id: int
|
||||
pos_1: str
|
||||
pos_2: Optional[str] = None
|
||||
pos_3: Optional[str] = None
|
||||
pos_4: Optional[str] = None
|
||||
pos_5: Optional[str] = None
|
||||
pos_6: Optional[str] = None
|
||||
pos_7: Optional[str] = None
|
||||
pos_8: Optional[str] = None
|
||||
headshot: Optional[str] = None
|
||||
vanity_card: Optional[str] = None
|
||||
strat_code: Optional[str] = None
|
||||
bbref_id: Optional[str] = None
|
||||
fangr_id: Optional[str] = None
|
||||
description: str
|
||||
quantity: Optional[int] = 999
|
||||
|
||||
|
||||
class PlayerModel(pydantic.BaseModel):
|
||||
players: List[PlayerPydantic]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_players(
|
||||
name: Optional[str] = None, value: Optional[int] = None, min_cost: Optional[int] = None,
|
||||
max_cost: Optional[int] = None, has_image2: Optional[bool] = None, mlbclub: Optional[str] = None,
|
||||
franchise: Optional[str] = None, cardset_id: list = Query(default=None), rarity_id: list = Query(default=None),
|
||||
pos_include: list = Query(default=None), pos_exclude: list = Query(default=None), has_headshot: Optional[bool] = None,
|
||||
has_vanity_card: Optional[bool] = None, strat_code: Optional[str] = None, bbref_id: Optional[str] = None,
|
||||
fangr_id: Optional[str] = None, inc_dex: Optional[bool] = True, in_desc: Optional[str] = None,
|
||||
flat: Optional[bool] = False, sort_by: Optional[str] = False, cardset_id_exclude: list = Query(default=None),
|
||||
limit: Optional[int] = None, csv: Optional[bool] = None, short_output: Optional[bool] = False):
|
||||
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:
|
||||
all_players = all_players.where(fn.Lower(Player.p_name) == name.lower())
|
||||
if value is not None:
|
||||
all_players = all_players.where(Player.cost == value)
|
||||
if min_cost is not None:
|
||||
all_players = all_players.where(Player.cost >= min_cost)
|
||||
if max_cost is not None:
|
||||
all_players = all_players.where(Player.cost <= max_cost)
|
||||
if has_image2 is not None:
|
||||
all_players = all_players.where(Player.image2.is_null(not has_image2))
|
||||
if mlbclub is not None:
|
||||
all_players = all_players.where(fn.Lower(Player.mlbclub) == mlbclub.lower())
|
||||
if franchise is not None:
|
||||
all_players = all_players.where(fn.Lower(Player.franchise) == franchise.lower())
|
||||
if cardset_id is not None:
|
||||
all_players = all_players.where(Player.cardset_id << cardset_id)
|
||||
if cardset_id_exclude is not None:
|
||||
all_players = all_players.where(Player.cardset_id.not_in(cardset_id_exclude))
|
||||
if rarity_id is not None:
|
||||
all_players = all_players.where(Player.rarity_id << rarity_id)
|
||||
if pos_include is not None:
|
||||
p_list = [x.upper() for x in pos_include]
|
||||
all_players = all_players.where(
|
||||
(Player.pos_1 << p_list) | (Player.pos_2 << p_list) | (Player.pos_3 << p_list) | (Player.pos_4 << p_list) |
|
||||
(Player.pos_5 << p_list) | (Player.pos_6 << p_list) | (Player.pos_7 << p_list) | (Player.pos_8 << p_list)
|
||||
)
|
||||
if has_headshot is not None:
|
||||
all_players = all_players.where(Player.headshot.is_null(not has_headshot))
|
||||
if has_vanity_card is not None:
|
||||
all_players = all_players.where(Player.vanity_card.is_null(not has_vanity_card))
|
||||
if strat_code is not None:
|
||||
all_players = all_players.where(Player.strat_code == strat_code)
|
||||
if bbref_id is not None:
|
||||
all_players = all_players.where(Player.bbref_id == bbref_id)
|
||||
if fangr_id is not None:
|
||||
all_players = all_players.where(Player.fangr_id == fangr_id)
|
||||
if in_desc is not None:
|
||||
all_players = all_players.where(fn.Lower(Player.description).contains(in_desc.lower()))
|
||||
|
||||
if sort_by is not None:
|
||||
if sort_by == 'cost-desc':
|
||||
all_players = all_players.order_by(-Player.cost)
|
||||
elif sort_by == 'cost-asc':
|
||||
all_players = all_players.order_by(Player.cost)
|
||||
elif sort_by == 'name-asc':
|
||||
all_players = all_players.order_by(Player.p_name)
|
||||
elif sort_by == 'name-desc':
|
||||
all_players = all_players.order_by(-Player.p_name)
|
||||
elif sort_by == 'rarity-desc':
|
||||
all_players = all_players.order_by(Player.rarity)
|
||||
elif sort_by == 'rarity-asc':
|
||||
all_players = all_players.order_by(-Player.rarity)
|
||||
|
||||
final_players = []
|
||||
# logging.info(f'pos_exclude: {type(pos_exclude)} - {pos_exclude} - is None: {pos_exclude is None}')
|
||||
for x in all_players:
|
||||
if pos_exclude is not None and set([x.upper() for x in pos_exclude]).intersection(x.get_all_pos()):
|
||||
pass
|
||||
else:
|
||||
final_players.append(x)
|
||||
|
||||
if limit is not None and len(final_players) >= limit:
|
||||
break
|
||||
|
||||
# if len(final_players) == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No players found')
|
||||
|
||||
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']
|
||||
x['cardset_name'] = x['cardset']['name']
|
||||
x['rarity'] = x['rarity']['name']
|
||||
x['for_purchase'] = x['cardset']['for_purchase']
|
||||
x['ranked_legal'] = x['cardset']['ranked_legal']
|
||||
if x['player_name'] not in x['description']:
|
||||
x['description'] = f'{x["description"]} {x["player_name"]}'
|
||||
|
||||
card_df = pd.DataFrame(card_vals)
|
||||
output = card_df[[
|
||||
'player_id', 'player_name', 'cost', 'image', 'image2', 'mlbclub', 'franchise', 'cardset_name', 'rarity',
|
||||
'pos_1', 'pos_2', 'pos_3', 'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'headshot', 'vanity_card',
|
||||
'fangr_id', 'bbref_id', 'description', 'for_purchase', 'ranked_legal'
|
||||
]]
|
||||
return Response(content=pd.DataFrame(output).to_csv(index=False), media_type='text/csv')
|
||||
|
||||
# all_players.order_by(-Player.rarity.value, Player.p_name)
|
||||
# data_list = [['id', 'name', 'value', 'image', 'image2', 'mlbclub', 'franchise', 'cardset', 'rarity', 'pos_1',
|
||||
# 'pos_2', 'pos_3', 'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'headshot', 'vanity_card',
|
||||
# 'strat_code', 'bbref_id', 'description', 'for_purchase', 'ranked_legal']]
|
||||
# for line in final_players:
|
||||
# data_list.append(
|
||||
# [
|
||||
# line.player_id, line.p_name, line.cost, line.image, line.image2, line.mlbclub, line.franchise,
|
||||
# line.cardset, line.rarity, line.pos_1, line.pos_2, line.pos_3, line.pos_4, line.pos_5, line.pos_6,
|
||||
# line.pos_7, line.pos_8, line.headshot, line.vanity_card, line.strat_code, line.bbref_id,
|
||||
# line.description, line.cardset.for_purchase, line.cardset.ranked_legal
|
||||
# # line.description, line.cardset.in_packs, line.quantity
|
||||
# ]
|
||||
# )
|
||||
# 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 = {'count': len(final_players), 'players': []}
|
||||
for x in final_players:
|
||||
|
||||
this_record = model_to_dict(x, recurse=not (flat or short_output))
|
||||
|
||||
if inc_dex:
|
||||
this_dex = Paperdex.select().where(Paperdex.player == x)
|
||||
this_record['paperdex'] = {'count': this_dex.count(), 'paperdex': []}
|
||||
for y in this_dex:
|
||||
this_record['paperdex']['paperdex'].append(model_to_dict(y, recurse=False))
|
||||
|
||||
return_val['players'].append(this_record)
|
||||
|
||||
# return_val['players'].append(model_to_dict(x, recurse=not flat))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/random')
|
||||
async def get_random_player(
|
||||
min_cost: Optional[int] = None, max_cost: Optional[int] = None, in_packs: Optional[bool] = None,
|
||||
min_rarity: Optional[int] = None, max_rarity: Optional[int] = None, limit: Optional[int] = None,
|
||||
pos_include: Optional[str] = None, pos_exclude: Optional[str] = None, franchise: Optional[str] = None,
|
||||
mlbclub: Optional[str] = None, cardset_id: list = Query(default=None), pos_inc: list = Query(default=None),
|
||||
pos_exc: list = Query(default=None), csv: Optional[bool] = None):
|
||||
all_players = (Player
|
||||
.select()
|
||||
.join(Cardset)
|
||||
.switch(Player)
|
||||
.join(Rarity)
|
||||
.order_by(fn.Random()))
|
||||
|
||||
if min_cost is not None:
|
||||
all_players = all_players.where(Player.cost >= min_cost)
|
||||
if max_cost is not None:
|
||||
all_players = all_players.where(Player.cost <= max_cost)
|
||||
if in_packs is not None:
|
||||
if in_packs:
|
||||
all_players = all_players.where(Player.cardset.in_packs)
|
||||
if min_rarity is not None:
|
||||
all_players = all_players.where(Player.rarity.value >= min_rarity)
|
||||
if max_rarity is not None:
|
||||
all_players = all_players.where(Player.rarity.value <= max_rarity)
|
||||
if pos_include is not None:
|
||||
all_players = all_players.where(
|
||||
(fn.lower(Player.pos_1) == pos_include.lower()) | (fn.lower(Player.pos_2) == pos_include.lower()) |
|
||||
(fn.lower(Player.pos_3) == pos_include.lower()) | (fn.lower(Player.pos_4) == pos_include.lower()) |
|
||||
(fn.lower(Player.pos_5) == pos_include.lower()) | (fn.lower(Player.pos_6) == pos_include.lower()) |
|
||||
(fn.lower(Player.pos_7) == pos_include.lower()) | (fn.lower(Player.pos_8) == pos_include.lower())
|
||||
)
|
||||
if franchise is not None:
|
||||
all_players = all_players.where(fn.Lower(Player.franchise) == franchise.lower())
|
||||
if mlbclub is not None:
|
||||
all_players = all_players.where(fn.Lower(Player.mlbclub) == mlbclub.lower())
|
||||
if cardset_id is not None:
|
||||
all_players = all_players.where(Player.cardset_id << cardset_id)
|
||||
if pos_inc is not None:
|
||||
p_list = [x.upper() for x in pos_inc]
|
||||
all_players = all_players.where(
|
||||
(Player.pos_1 << p_list) | (Player.pos_2 << p_list) | (Player.pos_3 << p_list) | (Player.pos_4 << p_list) |
|
||||
(Player.pos_5 << p_list) | (Player.pos_6 << p_list) | (Player.pos_7 << p_list) | (Player.pos_8 << p_list)
|
||||
)
|
||||
# if pos_exc is not None:
|
||||
# p_list = [x.upper() for x in pos_exc]
|
||||
# logging.info(f'starting query: {all_players}\n\np_list: {p_list}\n\n')
|
||||
# all_players = all_players.where(
|
||||
# Player.pos_1.not_in(p_list) & Player.pos_2.not_in(p_list) & Player.pos_3.not_in(p_list) &
|
||||
# Player.pos_4.not_in(p_list) & Player.pos_5.not_in(p_list) & Player.pos_6.not_in(p_list) &
|
||||
# Player.pos_7.not_in(p_list) & Player.pos_8.not_in(p_list)
|
||||
# )
|
||||
# logging.info(f'post pos query: {all_players}')
|
||||
|
||||
if pos_exclude is not None and pos_exc is None:
|
||||
final_players = [x for x in all_players if pos_exclude not in x.get_all_pos()]
|
||||
elif pos_exc is not None and pos_exclude is None:
|
||||
final_players = []
|
||||
p_list = [x.upper() for x in pos_exc]
|
||||
for x in all_players:
|
||||
if limit is not None and len(final_players) >= limit:
|
||||
break
|
||||
if not set(p_list).intersection(x.get_all_pos()):
|
||||
final_players.append(x)
|
||||
else:
|
||||
final_players = all_players
|
||||
|
||||
if limit is not None:
|
||||
final_players = final_players[:limit]
|
||||
|
||||
# if len(final_players) == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No players found')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'name', 'cost', 'image', 'image2', 'mlbclub', 'franchise', 'cardset', 'rarity', 'pos_1',
|
||||
'pos_2', 'pos_3', 'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'headshot', 'vanity_card',
|
||||
'strat_code', 'bbref_id', 'description']]
|
||||
for line in final_players:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.p_name, line.cost, line.image, line.image2,
|
||||
line.mlbclub, line.franchise, line.cardset.name, line.rarity.name,
|
||||
line.pos_1, line.pos_2, line.pos_3, line.pos_4, line.pos_5,
|
||||
line.pos_6, line.pos_7, line.pos_8, line.headshot, line.vanity_card,
|
||||
line.strat_code, line.bbref_id, line.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 = {'count': len(final_players), 'players': []}
|
||||
for x in final_players:
|
||||
this_record = model_to_dict(x)
|
||||
|
||||
this_dex = Paperdex.select().where(Paperdex.player == x)
|
||||
this_record['paperdex'] = {'count': this_dex.count(), 'paperdex': []}
|
||||
for y in this_dex:
|
||||
this_record['paperdex']['paperdex'].append(model_to_dict(y, recurse=False))
|
||||
|
||||
return_val['players'].append(this_record)
|
||||
# return_val['players'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{player_id}')
|
||||
async def get_one_player(player_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No player found with id {player_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'name', 'cost', 'image', 'image2', 'mlbclub', 'franchise', 'cardset', 'rarity', 'pos_1',
|
||||
'pos_2', 'pos_3', 'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'headshot', 'vanity_card',
|
||||
'strat_code', 'bbref_id', 'description']]
|
||||
return_val = DataFrame(data_list).to_csv(header=False, index=False)
|
||||
data_list.append(
|
||||
[
|
||||
this_player.id, this_player.p_name, this_player.cost, this_player.image, this_player.image2,
|
||||
this_player.mlbclub, this_player.franchise, this_player.cardset.name, this_player.rarity.name,
|
||||
this_player.pos_1, this_player.pos_2, this_player.pos_3, this_player.pos_4, this_player.pos_5,
|
||||
this_player.pos_6, this_player.pos_7, this_player.pos_8, this_player.headshot, this_player.vanity_card,
|
||||
this_player.strat_code, this_player.bbref_id, this_player.description
|
||||
]
|
||||
)
|
||||
|
||||
db.close()
|
||||
return Response(content=return_val, media_type='text/csv')
|
||||
else:
|
||||
return_val = model_to_dict(this_player)
|
||||
this_dex = Paperdex.select().where(Paperdex.player == this_player)
|
||||
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
|
||||
|
||||
|
||||
@router.get('/{player_id}/{card_type}card')
|
||||
async def get_batter_card(
|
||||
request: Request, player_id: int, card_type: Literal['batting', 'pitching'], variant: int = 0, d: str = None,
|
||||
html: Optional[bool] = False):
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No player found with id {player_id}')
|
||||
|
||||
if os.path.isfile(f'storage/cards/cardset-{this_player.cardset.id}/{player_id}-{d}-v{variant}.png') and html is False:
|
||||
db.close()
|
||||
return FileResponse(
|
||||
path=f'storage/cards/cardset-{this_player.cardset.id}/{player_id}-{d}-v{variant}.png',
|
||||
media_type='image/png'
|
||||
)
|
||||
|
||||
all_pos = CardPosition.select().where(CardPosition.player == this_player).order_by(CardPosition.innings.desc())
|
||||
|
||||
if card_type == 'batting':
|
||||
this_bc = BattingCard.get_or_none(BattingCard.player == this_player, BattingCard.variant == variant)
|
||||
if this_bc is None:
|
||||
raise HTTPException(status_code=404, detail=f'Batting card not found for id {player_id}, variant {variant}')
|
||||
|
||||
rating_vl = BattingCardRatings.get_or_none(
|
||||
BattingCardRatings.battingcard == this_bc, BattingCardRatings.vs_hand == 'L')
|
||||
rating_vr = BattingCardRatings.get_or_none(
|
||||
BattingCardRatings.battingcard == this_bc, BattingCardRatings.vs_hand == 'R')
|
||||
if None in [rating_vr, rating_vl]:
|
||||
raise HTTPException(status_code=404, detail=f'Ratings not found for batting card {this_bc.id}')
|
||||
|
||||
card_data = get_batter_card_data(this_player, this_bc, rating_vl, rating_vr, all_pos)
|
||||
if this_player.description in this_player.cardset.name:
|
||||
card_data['cardset_name'] = this_player.cardset.name
|
||||
else:
|
||||
card_data['cardset_name'] = this_player.description
|
||||
card_data['request'] = request
|
||||
html_response = templates.TemplateResponse("player_card.html", card_data)
|
||||
|
||||
else:
|
||||
this_pc = PitchingCard.get_or_none(PitchingCard.player == this_player, PitchingCard.variant == variant)
|
||||
if this_pc is None:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f'Pitching card not found for id {player_id}, variant {variant}')
|
||||
|
||||
rating_vl = PitchingCardRatings.get_or_none(
|
||||
PitchingCardRatings.pitchingcard == this_pc, PitchingCardRatings.vs_hand == 'L')
|
||||
rating_vr = PitchingCardRatings.get_or_none(
|
||||
PitchingCardRatings.pitchingcard == this_pc, PitchingCardRatings.vs_hand == 'R')
|
||||
if None in [rating_vr, rating_vl]:
|
||||
raise HTTPException(status_code=404, detail=f'Ratings not found for pitching card {this_pc.id}')
|
||||
|
||||
card_data = get_pitcher_card_data(this_player, this_pc, rating_vl, rating_vr, all_pos)
|
||||
if this_player.description in this_player.cardset.name:
|
||||
card_data['cardset_name'] = this_player.cardset.name
|
||||
else:
|
||||
card_data['cardset_name'] = this_player.description
|
||||
card_data['request'] = request
|
||||
html_response = templates.TemplateResponse("player_card.html", card_data)
|
||||
|
||||
if html:
|
||||
db.close()
|
||||
return html_response
|
||||
|
||||
updates = 0
|
||||
if card_type == 'batting':
|
||||
updates += BattingCardRatings.update(card_data['new_ratings_vl'].dict()).where(
|
||||
(BattingCardRatings.id == rating_vl.id)
|
||||
).execute()
|
||||
updates += BattingCardRatings.update(card_data['new_ratings_vr'].dict()).where(
|
||||
(BattingCardRatings.id == rating_vr.id)
|
||||
).execute()
|
||||
else:
|
||||
updates += PitchingCardRatings.update(card_data['new_ratings_vl'].dict()).where(
|
||||
(PitchingCardRatings.id == rating_vl.id)
|
||||
).execute()
|
||||
updates += PitchingCardRatings.update(card_data['new_ratings_vr'].dict()).where(
|
||||
(PitchingCardRatings.id == rating_vr.id)
|
||||
).execute()
|
||||
|
||||
logging.info(f'Rating updates: {updates}')
|
||||
|
||||
hti = Html2Image(
|
||||
browser='chromium',
|
||||
size=(1200, 600),
|
||||
output_path=f'storage/cards/cardset-{this_player.cardset.id}/',
|
||||
custom_flags=['--no-sandbox', '--disable-remote-debugging', '--headless', '--disable-gpu',
|
||||
'--disable-software-rasterizer', '--disable-dev-shm-usage']
|
||||
)
|
||||
|
||||
logging.debug(f'body:\n{html_response.body.decode("UTF-8")}')
|
||||
x = hti.screenshot(
|
||||
html_str=str(html_response.body.decode("UTF-8")),
|
||||
save_as=f'{player_id}-{d}-v{variant}.png'
|
||||
)
|
||||
|
||||
db.close()
|
||||
return FileResponse(path=x[0], media_type='image/png')
|
||||
|
||||
|
||||
# @router.get('/{player_id}/pitchingcard')
|
||||
# async def get_pitcher_card(
|
||||
# request: Request, player_id: int, variant: int = 0, d: str = None, html: Optional[bool] = False)
|
||||
|
||||
|
||||
@router.patch('/{player_id}')
|
||||
async def v1_players_patch(
|
||||
player_id, name: Optional[str] = None, image: Optional[str] = None, image2: Optional[str] = None,
|
||||
mlbclub: Optional[str] = None, franchise: Optional[str] = None, cardset_id: Optional[int] = None,
|
||||
rarity_id: Optional[int] = None, pos_1: Optional[str] = None, pos_2: Optional[str] = None,
|
||||
pos_3: Optional[str] = None, pos_4: Optional[str] = None, pos_5: Optional[str] = None,
|
||||
pos_6: Optional[str] = None, pos_7: Optional[str] = None, pos_8: Optional[str] = None,
|
||||
headshot: Optional[str] = None, vanity_card: Optional[str] = None, strat_code: Optional[str] = None,
|
||||
bbref_id: Optional[str] = None, description: Optional[str] = None, cost: Optional[int] = None,
|
||||
fangr_id: Optional[str] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to patch players. This event has been logged.'
|
||||
)
|
||||
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No player found with id {player_id}')
|
||||
|
||||
if cost is not None:
|
||||
this_player.cost = cost
|
||||
if name is not None:
|
||||
this_player.p_name = name
|
||||
if image is not None:
|
||||
this_player.image = image
|
||||
if image2 is not None:
|
||||
if image2.lower() == 'false':
|
||||
this_player.image2 = None
|
||||
else:
|
||||
this_player.image2 = image2
|
||||
if mlbclub is not None:
|
||||
this_player.mlbclub = mlbclub
|
||||
if franchise is not None:
|
||||
this_player.franchise = franchise
|
||||
if cardset_id is not None:
|
||||
try:
|
||||
this_cardset = Cardset.get_by_id(cardset_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No cardset found with id {cardset_id}')
|
||||
this_player.cardset = this_cardset
|
||||
if rarity_id is not None:
|
||||
try:
|
||||
this_rarity = Rarity.get_by_id(rarity_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}')
|
||||
this_player.rarity = this_rarity
|
||||
if pos_1 is not None:
|
||||
if pos_1 in ['None', 'False', '']:
|
||||
this_player.pos_1 = None
|
||||
else:
|
||||
this_player.pos_1 = pos_1
|
||||
if pos_2 is not None:
|
||||
if pos_2 in ['None', 'False', '']:
|
||||
this_player.pos_2 = None
|
||||
else:
|
||||
this_player.pos_2 = pos_2
|
||||
if pos_3 is not None:
|
||||
if pos_3 in ['None', 'False', '']:
|
||||
this_player.pos_3 = None
|
||||
else:
|
||||
this_player.pos_3 = pos_3
|
||||
if pos_4 is not None:
|
||||
if pos_4 in ['None', 'False', '']:
|
||||
this_player.pos_4 = None
|
||||
else:
|
||||
this_player.pos_4 = pos_4
|
||||
if pos_5 is not None:
|
||||
if pos_5 in ['None', 'False', '']:
|
||||
this_player.pos_5 = None
|
||||
else:
|
||||
this_player.pos_5 = pos_5
|
||||
if pos_6 is not None:
|
||||
if pos_6 in ['None', 'False', '']:
|
||||
this_player.pos_6 = None
|
||||
else:
|
||||
this_player.pos_6 = pos_6
|
||||
if pos_7 is not None:
|
||||
if pos_7 in ['None', 'False', '']:
|
||||
this_player.pos_7 = None
|
||||
else:
|
||||
this_player.pos_7 = pos_7
|
||||
if pos_8 is not None:
|
||||
if pos_8 in ['None', 'False', '']:
|
||||
this_player.pos_8 = None
|
||||
else:
|
||||
this_player.pos_8 = pos_8
|
||||
if headshot is not None:
|
||||
this_player.headshot = headshot
|
||||
if vanity_card is not None:
|
||||
this_player.vanity_card = vanity_card
|
||||
if strat_code is not None:
|
||||
this_player.strat_code = strat_code
|
||||
if bbref_id is not None:
|
||||
this_player.bbref_id = bbref_id
|
||||
if fangr_id is not None:
|
||||
this_player.fangr_id = fangr_id
|
||||
if description is not None:
|
||||
this_player.description = description
|
||||
|
||||
if this_player.save() == 1:
|
||||
return_val = model_to_dict(this_player)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post players. This event has been logged.'
|
||||
)
|
||||
|
||||
new_players = []
|
||||
for x in players.players:
|
||||
# this_player = Player(
|
||||
# player_id=x.player_id,
|
||||
# p_name=x.p_name,
|
||||
# cost=x.cost,
|
||||
# image=x.image,
|
||||
# image2=x.image2,
|
||||
# mlbclub=x.mlbclub,
|
||||
# franchise=x.franchise,
|
||||
# cardset_id=x.cardset_id,
|
||||
# rarity_id=x.rarity_id,
|
||||
# set_num=x.set_num,
|
||||
# pos_1=x.pos_1,
|
||||
# pos_2=x.pos_2,
|
||||
# pos_3=x.pos_3,
|
||||
# pos_4=x.pos_4,
|
||||
# pos_5=x.pos_5,
|
||||
# pos_6=x.pos_6,
|
||||
# pos_7=x.pos_7,
|
||||
# pos_8=x.pos_8,
|
||||
# headshot=x.headshot,
|
||||
# vanity_card=x.vanity_card,
|
||||
# strat_code=x.strat_code,
|
||||
# fangr_id=x.fangr_id,
|
||||
# bbref_id=x.bbref_id,
|
||||
# description=x.description
|
||||
# )
|
||||
# new_players.append(this_player)
|
||||
new_players.append({
|
||||
'player_id': x.player_id,
|
||||
'p_name': x.p_name,
|
||||
'cost': x.cost,
|
||||
'image': x.image,
|
||||
'image2': x.image2,
|
||||
'mlbclub': x.mlbclub.title(),
|
||||
'franchise': x.franchise.title(),
|
||||
'cardset_id': x.cardset_id,
|
||||
'rarity_id': x.rarity_id,
|
||||
'set_num': x.set_num,
|
||||
'pos_1': x.pos_1,
|
||||
'pos_2': x.pos_2,
|
||||
'pos_3': x.pos_3,
|
||||
'pos_4': x.pos_4,
|
||||
'pos_5': x.pos_5,
|
||||
'pos_6': x.pos_6,
|
||||
'pos_7': x.pos_7,
|
||||
'pos_8': x.pos_8,
|
||||
'headshot': x.headshot,
|
||||
'vanity_card': x.vanity_card,
|
||||
'strat_code': x.strat_code,
|
||||
'fangr_id': x.fangr_id,
|
||||
'bbref_id': x.bbref_id,
|
||||
'description': x.description
|
||||
})
|
||||
|
||||
logging.info(f'new_players: {new_players}')
|
||||
|
||||
with db.atomic():
|
||||
# Player.bulk_create(new_players, batch_size=15)
|
||||
for batch in chunked(new_players, 15):
|
||||
logging.info(f'batch: {batch}')
|
||||
Player.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
# sheets.update_all_players(SHEETS_AUTH)
|
||||
raise HTTPException(status_code=200, detail=f'{len(new_players)} players have been added')
|
||||
|
||||
|
||||
@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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post players. This event has been logged.'
|
||||
)
|
||||
|
||||
dupe_query = Player.select().where(
|
||||
(Player.bbref_id == new_player.bbref_id) & (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}'
|
||||
)
|
||||
|
||||
p_query = Player.select(Player.player_id).order_by(-Player.player_id).limit(1)
|
||||
new_id = p_query[0].player_id + 1
|
||||
|
||||
new_player.player_id = new_id
|
||||
p_id = Player.insert(new_player.dict()).execute()
|
||||
|
||||
return_val = model_to_dict(Player.get_by_id(p_id))
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.delete('/{player_id}')
|
||||
async def delete_player(player_id, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete players. This event has been logged.'
|
||||
)
|
||||
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No player found with id {player_id}')
|
||||
|
||||
count = this_player.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
raise HTTPException(status_code=200, detail=f'Player {player_id} has been deleted')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Player {player_id} was not deleted')
|
||||
187
app/routers_v2/rarity.py
Normal file
187
app/routers_v2/rarity.py
Normal file
@ -0,0 +1,187 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/rarities',
|
||||
tags=['rarities']
|
||||
)
|
||||
|
||||
|
||||
class RarityModel(pydantic.BaseModel):
|
||||
value: int
|
||||
name: str
|
||||
color: str
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_rarities(value: Optional[int] = None, name: Optional[str] = None, min_value: Optional[int] = None,
|
||||
max_value: Optional[int] = None, csv: Optional[bool] = None):
|
||||
all_rarities = Rarity.select()
|
||||
|
||||
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:
|
||||
all_rarities = all_rarities.where(Rarity.value == value)
|
||||
if name is not None:
|
||||
all_rarities = all_rarities.where(fn.Lower(Rarity.name) == name.lower())
|
||||
if min_value is not None:
|
||||
all_rarities = all_rarities.where(Rarity.value >= min_value)
|
||||
if max_value is not 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:
|
||||
data_list = [['id', 'value', 'name']]
|
||||
for line in all_rarities:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.value, line.name
|
||||
]
|
||||
)
|
||||
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 = {'count': all_rarities.count(), 'rarities': []}
|
||||
for x in all_rarities:
|
||||
return_val['rarities'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{rarity_id}')
|
||||
async def get_one_rarity(rarity_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
this_rarity = Rarity.get_by_id(rarity_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'value', 'name']]
|
||||
for line in this_rarity:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.value, line.name
|
||||
]
|
||||
)
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post rarities. This event has been logged.'
|
||||
)
|
||||
|
||||
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(
|
||||
value=rarity.value,
|
||||
name=rarity.name,
|
||||
color=rarity.color
|
||||
)
|
||||
|
||||
saved = this_rarity.save()
|
||||
if saved == 1:
|
||||
return_val = model_to_dict(this_rarity)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{rarity_id}')
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No rarity found with id {rarity_id}')
|
||||
|
||||
if value is not None:
|
||||
this_rarity.value = value
|
||||
if name is not None:
|
||||
this_rarity.name = name
|
||||
if color is not None:
|
||||
this_rarity.color = color
|
||||
|
||||
if this_rarity.save() == 1:
|
||||
return_val = model_to_dict(this_rarity)
|
||||
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 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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Rarity {rarity_id} was not deleted')
|
||||
431
app/routers_v2/results.py
Normal file
431
app/routers_v2/results.py
Normal file
@ -0,0 +1,431 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/results',
|
||||
tags=['results']
|
||||
)
|
||||
|
||||
|
||||
class ResultModel(pydantic.BaseModel):
|
||||
away_team_id: int
|
||||
home_team_id: int
|
||||
away_score: int
|
||||
home_score: int
|
||||
away_team_value: Optional[int] = None
|
||||
home_team_value: Optional[int] = None
|
||||
away_team_ranking: Optional[int] = None
|
||||
home_team_ranking: Optional[int] = None
|
||||
scorecard: str
|
||||
week: int
|
||||
season: int
|
||||
ranked: bool
|
||||
short_game: bool
|
||||
game_type: str
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_results(
|
||||
away_team_id: Optional[int] = None, home_team_id: Optional[int] = None, team_one_id: Optional[int] = None,
|
||||
team_two_id: Optional[int] = None, away_score_min: Optional[int] = None, away_score_max: Optional[int] = None,
|
||||
home_score_min: Optional[int] = None, home_score_max: Optional[int] = None, bothscore_min: Optional[int] = None,
|
||||
bothscore_max: Optional[int] = None, season: Optional[int] = None, week: Optional[int] = None,
|
||||
week_start: Optional[int] = None, week_end: Optional[int] = None, ranked: Optional[bool] = None,
|
||||
short_game: Optional[bool] = None, game_type: Optional[str] = None, vs_ai: Optional[bool] = None,
|
||||
csv: Optional[bool] = None):
|
||||
all_results = Result.select()
|
||||
|
||||
# if all_results.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'There are no results to filter')
|
||||
|
||||
if away_team_id is not None:
|
||||
try:
|
||||
this_team = Team.get_by_id(away_team_id)
|
||||
all_results = all_results.where(Result.away_team == this_team)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {away_team_id}')
|
||||
|
||||
if home_team_id is not None:
|
||||
try:
|
||||
this_team = Team.get_by_id(home_team_id)
|
||||
all_results = all_results.where(Result.home_team == this_team)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {home_team_id}')
|
||||
|
||||
if team_one_id is not None:
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_two_id}')
|
||||
|
||||
if away_score_min is not None:
|
||||
all_results = all_results.where(Result.away_score >= away_score_min)
|
||||
|
||||
if away_score_max is not None:
|
||||
all_results = all_results.where(Result.away_score <= away_score_max)
|
||||
|
||||
if home_score_min is not None:
|
||||
all_results = all_results.where(Result.home_score >= home_score_min)
|
||||
|
||||
if home_score_max is not None:
|
||||
all_results = all_results.where(Result.home_score <= home_score_max)
|
||||
|
||||
if bothscore_min is not None:
|
||||
all_results = all_results.where((Result.home_score >= bothscore_min) & (Result.away_score >= bothscore_min))
|
||||
|
||||
if bothscore_max is not None:
|
||||
all_results = all_results.where((Result.home_score <= bothscore_max) & (Result.away_score <= bothscore_max))
|
||||
|
||||
if season is not None:
|
||||
all_results = all_results.where(Result.season == season)
|
||||
|
||||
if week is not None:
|
||||
all_results = all_results.where(Result.week == week)
|
||||
|
||||
if ranked is not None:
|
||||
all_results = all_results.where(Result.ranked == ranked)
|
||||
|
||||
if short_game is not None:
|
||||
all_results = all_results.where(Result.short_game == short_game)
|
||||
|
||||
if week_start is not None:
|
||||
all_results = all_results.where(Result.week >= week_start)
|
||||
|
||||
if week_end is not None:
|
||||
all_results = all_results.where(Result.week <= week_end)
|
||||
|
||||
if game_type is not None:
|
||||
all_results = all_results.where(Result.game_type == game_type)
|
||||
|
||||
all_results = all_results.order_by(Result.id)
|
||||
# Not functional
|
||||
# if vs_ai is not None:
|
||||
# AwayTeam = Team.alias()
|
||||
# all_results = all_results.join(
|
||||
# Team, on=Result.home_team
|
||||
# ).switch(Result).join(
|
||||
# Team, on=(AwayTeam.id == Result.away_team).alias('a_team')
|
||||
# )
|
||||
#
|
||||
# if vs_ai:
|
||||
# all_results = all_results.where(
|
||||
# (Result.home_team.is_ai == 1) | (Result.a_team.is_ai == 1)
|
||||
# )
|
||||
# else:
|
||||
# all_results = all_results.where(
|
||||
# (Result.home_team.is_ai == 0) & (Result.a_team.is_ai == 0)
|
||||
# )
|
||||
# logging.info(f'Result Query:\n\n{all_results}')
|
||||
|
||||
if csv:
|
||||
data_list = [['id', 'away_abbrev', 'home_abbrev', 'away_score', 'home_score', 'away_tv', 'home_tv',
|
||||
'game_type', 'season', 'week', 'short_game', 'ranked']]
|
||||
for line in all_results:
|
||||
data_list.append([
|
||||
line.id, line.away_team.abbrev, line.home_team.abbrev, line.away_score, line.home_score,
|
||||
line.away_team_value, line.home_team_value, line.game_type if line.game_type else 'minor-league',
|
||||
line.season, line.week, line.short_game, line.ranked
|
||||
])
|
||||
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 = {'count': all_results.count(), 'results': []}
|
||||
for x in all_results:
|
||||
return_val['results'].append(model_to_dict(x))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{result_id}')
|
||||
async def get_one_results(result_id, csv: Optional[bool] = None):
|
||||
try:
|
||||
this_result = Result.get_by_id(result_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No result found with id {result_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'away_abbrev', 'home_abbrev', 'away_score', 'home_score', 'away_tv', 'home_tv', 'game_type',
|
||||
'season', 'week', 'game_type'],
|
||||
[this_result.id, this_result.away_team.abbrev, this_result.away_team.abbrev, this_result.away_score,
|
||||
this_result.home_score, this_result.away_team_value, this_result.home_team_value,
|
||||
this_result.game_type if this_result.game_type else 'minor-league',
|
||||
this_result.season, this_result.week, this_result.game_type]
|
||||
]
|
||||
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
|
||||
|
||||
|
||||
@router.get('/team/{team_id}')
|
||||
async def get_team_results(
|
||||
team_id: int, season: Optional[int] = None, week: Optional[int] = None, csv: Optional[bool] = False):
|
||||
all_results = Result.select().where((Result.away_team_id == team_id) | (Result.home_team_id == team_id))
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception 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')
|
||||
|
||||
if season is not None:
|
||||
all_results = all_results.where(Result.season == season)
|
||||
else:
|
||||
all_results = all_results.where(Result.season == this_team.season)
|
||||
|
||||
if week is not None:
|
||||
all_results = all_results.where(Result.week == week)
|
||||
|
||||
r_wins, r_loss, c_wins, c_loss = 0, 0, 0, 0
|
||||
for x in all_results:
|
||||
if x.away_team_id == team_id:
|
||||
if x.away_score > x.home_score:
|
||||
if x.ranked:
|
||||
r_wins += 1
|
||||
else:
|
||||
c_wins += 1
|
||||
else:
|
||||
if x.ranked:
|
||||
r_loss += 1
|
||||
else:
|
||||
c_loss += 1
|
||||
elif x.home_team_id == team_id:
|
||||
if x.away_score > x.home_score:
|
||||
if x.ranked:
|
||||
r_loss += 1
|
||||
else:
|
||||
c_loss += 1
|
||||
else:
|
||||
if x.ranked:
|
||||
r_wins += 1
|
||||
else:
|
||||
c_wins += 1
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['team_id', 'ranked_wins', 'ranked_losses', 'casual_wins', 'casual_losses', 'team_ranking'],
|
||||
[team_id, r_wins, r_loss, c_wins, c_loss, this_team.ranking]
|
||||
]
|
||||
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 = {
|
||||
'team': model_to_dict(this_team),
|
||||
'ranked_wins': r_wins,
|
||||
'ranked_losses': r_loss,
|
||||
'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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post results. This event has been logged.'
|
||||
)
|
||||
|
||||
this_result = Result(**result.__dict__)
|
||||
saved = this_result.save()
|
||||
|
||||
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)
|
||||
|
||||
k_value = 20 if result.short_game else 60
|
||||
ratio = (result.home_team_ranking - result.away_team_ranking) / 400
|
||||
exp_score = 1 / (1 + (10 ** ratio))
|
||||
away_win = True if result.away_score > result.home_score else False
|
||||
total_delta = k_value * exp_score
|
||||
high_delta = total_delta * exp_score if exp_score > .5 else total_delta * (1 - exp_score)
|
||||
low_delta = total_delta - high_delta
|
||||
|
||||
# exp_score > .5 means away team is favorite
|
||||
if exp_score > .5 and away_win:
|
||||
final_delta = low_delta
|
||||
away_delta = low_delta * 3
|
||||
home_delta = -low_delta
|
||||
elif away_win:
|
||||
final_delta = high_delta
|
||||
away_delta = high_delta * 3
|
||||
home_delta = -high_delta
|
||||
elif exp_score <= .5 and not away_win:
|
||||
final_delta = low_delta
|
||||
away_delta = -low_delta
|
||||
home_delta = low_delta * 3
|
||||
elif not away_win:
|
||||
final_delta = high_delta
|
||||
away_delta = -high_delta
|
||||
home_delta = high_delta * 3
|
||||
else:
|
||||
final_delta = 0
|
||||
away_delta = 0
|
||||
home_delta = 0
|
||||
|
||||
logging.debug(f'/results ranking deltas\n\nk_value: {k_value} / ratio: {ratio} / '
|
||||
f'exp_score: {exp_score} / away_win: {away_win} / total_delta: {total_delta} / '
|
||||
f'high_delta: {high_delta} / low_delta: {low_delta} / final_delta: {final_delta} / ')
|
||||
|
||||
away_team = Team.get_by_id(result.away_team_id)
|
||||
away_team.ranking += away_delta
|
||||
away_team.save()
|
||||
logging.info(f'Just updated {away_team.abbrev} ranking to {away_team.ranking}')
|
||||
home_team = Team.get_by_id(result.home_team_id)
|
||||
home_team.ranking += home_delta
|
||||
home_team.save()
|
||||
logging.info(f'Just updated {home_team.abbrev} ranking to {home_team.ranking}')
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{result_id}')
|
||||
async def patch_result(
|
||||
result_id, away_team_id: Optional[int] = None, home_team_id: Optional[int] = None,
|
||||
away_score: Optional[int] = None, home_score: Optional[int] = None, away_team_value: Optional[int] = None,
|
||||
home_team_value: Optional[int] = None, scorecard: Optional[str] = None, week: Optional[int] = None,
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No result found with id {result_id}')
|
||||
|
||||
if away_team_id is not None:
|
||||
this_result.away_team_id = away_team_id
|
||||
|
||||
if home_team_id is not None:
|
||||
this_result.home_team_id = home_team_id
|
||||
|
||||
if away_score is not None:
|
||||
this_result.away_score = away_score
|
||||
|
||||
if home_score is not None:
|
||||
this_result.home_score = home_score
|
||||
|
||||
if away_team_value is not None:
|
||||
this_result.away_team_value = away_team_value
|
||||
|
||||
if home_team_value is not None:
|
||||
this_result.home_team_value = home_team_value
|
||||
|
||||
if scorecard is not None:
|
||||
this_result.scorecard = scorecard
|
||||
|
||||
if week is not None:
|
||||
this_result.week = week
|
||||
|
||||
if season is not None:
|
||||
this_result.season = season
|
||||
|
||||
if game_type is not None:
|
||||
this_result.game_type = game_type
|
||||
|
||||
if short_game is not None:
|
||||
if not short_game:
|
||||
this_result.short_game = None
|
||||
else:
|
||||
this_result.short_game = short_game
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Result {result_id} was not deleted')
|
||||
|
||||
187
app/routers_v2/rewards.py
Normal file
187
app/routers_v2/rewards.py
Normal file
@ -0,0 +1,187 @@
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||
from typing import Optional
|
||||
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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/rewards',
|
||||
tags=['rewards']
|
||||
)
|
||||
|
||||
|
||||
class RewardModel(pydantic.BaseModel):
|
||||
name: str
|
||||
season: int
|
||||
week: int
|
||||
team_id: int
|
||||
created: Optional[int] = int(datetime.timestamp(datetime.now())*1000)
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_rewards(
|
||||
name: Optional[str] = None, in_name: Optional[str] = None, team_id: Optional[int] = None,
|
||||
season: Optional[int] = None, week: Optional[int] = None, created_after: Optional[int] = None,
|
||||
flat: Optional[bool] = False, csv: Optional[bool] = None):
|
||||
all_rewards = Reward.select()
|
||||
|
||||
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:
|
||||
all_rewards = all_rewards.where(fn.Lower(Reward.name) == name.lower())
|
||||
if team_id is not None:
|
||||
all_rewards = all_rewards.where(Reward.team_id == team_id)
|
||||
if created_after is not None:
|
||||
all_rewards = all_rewards.where(Reward.created >= created_after)
|
||||
if in_name is not None:
|
||||
all_rewards = all_rewards.where(fn.Lower(Reward.name).contains(in_name.lower()))
|
||||
if season is not None:
|
||||
all_rewards = all_rewards.where(Reward.season == season)
|
||||
if week is not None:
|
||||
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:
|
||||
data_list = [['id', 'name', 'team', 'daily', 'created']]
|
||||
for line in all_rewards:
|
||||
data_list.append(
|
||||
[
|
||||
line.id, line.name, line.team.id, line.daily, line.created
|
||||
]
|
||||
)
|
||||
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 = {'count': all_rewards.count(), 'rewards': []}
|
||||
for x in all_rewards:
|
||||
return_val['rewards'].append(model_to_dict(x, recurse=not flat))
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{reward_id}')
|
||||
async def get_one_reward(reward_id, csv: Optional[bool] = False):
|
||||
try:
|
||||
this_reward = Reward.get_by_id(reward_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}')
|
||||
|
||||
if csv:
|
||||
data_list = [
|
||||
['id', 'name', 'card_count', 'description'],
|
||||
[this_reward.id, this_reward.name, this_reward.team.id, this_reward.daily, this_reward.created]
|
||||
]
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post rewards. This event has been logged.'
|
||||
)
|
||||
|
||||
this_reward = Reward(**reward.dict())
|
||||
|
||||
saved = this_reward.save()
|
||||
if saved == 1:
|
||||
return_val = model_to_dict(this_reward)
|
||||
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 cardset'
|
||||
)
|
||||
|
||||
|
||||
@router.patch('/{reward_id}')
|
||||
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()
|
||||
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()
|
||||
raise HTTPException(status_code=404, detail=f'No reward found with id {reward_id}')
|
||||
|
||||
if name is not None:
|
||||
this_reward.name = name
|
||||
if team_id is not None:
|
||||
this_reward.team_id = team_id
|
||||
if created is not None:
|
||||
this_reward.created = created
|
||||
|
||||
if this_reward.save() == 1:
|
||||
return_val = model_to_dict(this_reward)
|
||||
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 rarity'
|
||||
)
|
||||
|
||||
|
||||
@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()
|
||||
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()
|
||||
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')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Reward {reward_id} was not deleted')
|
||||
|
||||
|
||||
102
app/routers_v2/scouting.py
Normal file
102
app/routers_v2/scouting.py
Normal file
@ -0,0 +1,102 @@
|
||||
import csv
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, Query
|
||||
from typing import Optional
|
||||
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 ..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']
|
||||
)
|
||||
|
||||
|
||||
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'
|
||||
|
||||
|
||||
# def csv_file_to_dataframe(filename: str) -> pd.DataFrame | None:
|
||||
# with open(filename, 'r', encoding='utf8') as file:
|
||||
# reader = csv.reader(file)
|
||||
#
|
||||
# for row in reader:
|
||||
|
||||
|
||||
@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')
|
||||
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 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')
|
||||
def live_update_pitching(files: BattingFiles, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to initiate live updates.'
|
||||
)
|
||||
|
||||
return files.dict()
|
||||
178
app/routers_v2/stratgame.py
Normal file
178
app/routers_v2/stratgame.py
Normal file
@ -0,0 +1,178 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from typing import Literal, Optional, List
|
||||
import logging
|
||||
import pandas as pd
|
||||
import pydantic
|
||||
from pydantic import validator, root_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
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/games',
|
||||
tags=['games']
|
||||
)
|
||||
|
||||
|
||||
class GameModel(pydantic.BaseModel):
|
||||
season: int
|
||||
game_type: str
|
||||
away_team_id: int
|
||||
home_team_id: int
|
||||
week: int = 1
|
||||
away_score: int = 0
|
||||
home_score: int = 0
|
||||
away_team_value: int = None
|
||||
home_team_value: int = None
|
||||
away_team_ranking: int = None
|
||||
home_team_ranking: int = None
|
||||
ranked: bool = False
|
||||
short_game: bool = False
|
||||
forfeit: bool = False
|
||||
|
||||
|
||||
class GameList(pydantic.BaseModel):
|
||||
games: List[GameModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_games(
|
||||
season: list = Query(default=None), forfeit: Optional[bool] = None, away_team_id: list = Query(default=None),
|
||||
home_team_id: list = Query(default=None), team1_id: list = Query(default=None),
|
||||
team2_id: list = Query(default=None), game_type: list = Query(default=None), ranked: Optional[bool] = None,
|
||||
short_game: Optional[bool] = None, csv: Optional[bool] = False, short_output: bool = False):
|
||||
all_games = StratGame.select()
|
||||
|
||||
if season is not None:
|
||||
all_games = all_games.where(StratGame.season << season)
|
||||
if forfeit is not None:
|
||||
all_games = all_games.where(StratGame.forfeit == forfeit)
|
||||
if away_team_id is not None:
|
||||
all_games = all_games.where(StratGame.away_team_id << away_team_id)
|
||||
if home_team_id is not None:
|
||||
all_games = all_games.where(StratGame.home_team_id << home_team_id)
|
||||
if team1_id is not None:
|
||||
all_games = all_games.where(
|
||||
(StratGame.away_team_id << team1_id) | (StratGame.home_team_id << team1_id)
|
||||
)
|
||||
if team2_id is not None:
|
||||
all_games = all_games.where(
|
||||
(StratGame.away_team_id << team2_id) | (StratGame.home_team_id << team2_id)
|
||||
)
|
||||
if game_type is not None:
|
||||
g_list = [x.lower() for x in game_type]
|
||||
all_games = all_games.where(fn.Lower(StratGame.game_type) << g_list)
|
||||
if ranked is not None:
|
||||
all_games = all_games.where(StratGame.ranked == ranked)
|
||||
if short_game is not None:
|
||||
all_games = all_games.where(StratGame.short_game == short_game)
|
||||
|
||||
if csv:
|
||||
return_vals = [model_to_dict(x) for x in all_games]
|
||||
for x in return_vals:
|
||||
x['away_abbrev'] = x['away_team']['abbrev']
|
||||
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'
|
||||
]]
|
||||
|
||||
return Response(content=output.to_csv(index=False), media_type='text/csv')
|
||||
|
||||
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
|
||||
|
||||
|
||||
@router.get('/{game_id}')
|
||||
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
|
||||
|
||||
|
||||
@router.patch('/{game_id}')
|
||||
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}')
|
||||
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:
|
||||
this_game.away_score = away_score
|
||||
if home_score is not None:
|
||||
this_game.home_score = home_score
|
||||
if game_type is not None:
|
||||
this_game.game_type = game_type
|
||||
|
||||
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}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_game = StratGame(**this_game.dict())
|
||||
|
||||
saved = this_game.save()
|
||||
if saved == 1:
|
||||
return_val = model_to_dict(this_game)
|
||||
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 game'
|
||||
)
|
||||
|
||||
|
||||
@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}')
|
||||
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'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'StratGame {game_id} could not be deleted')
|
||||
|
||||
|
||||
1054
app/routers_v2/stratplays.py
Normal file
1054
app/routers_v2/stratplays.py
Normal file
File diff suppressed because it is too large
Load Diff
997
app/routers_v2/teams.py
Normal file
997
app/routers_v2/teams.py
Normal file
@ -0,0 +1,997 @@
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, Query
|
||||
from typing import Optional, Literal
|
||||
import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Team, model_to_dict, fn, Pack, Card, Player, Paperdex, Notification, PackType, \
|
||||
Rarity, Current, query_to_csv, complex_data_to_csv, CARDSETS, CardPosition, BattingCardRatings, BattingCard, \
|
||||
PitchingCard, PitchingCardRatings
|
||||
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, int_timestamp
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_DATA['filename'],
|
||||
format=LOG_DATA['format'],
|
||||
level=LOG_DATA['log_level']
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v2/teams',
|
||||
tags=['teams']
|
||||
)
|
||||
|
||||
|
||||
class TeamModel(pydantic.BaseModel):
|
||||
abbrev: str
|
||||
sname: str
|
||||
lname: str
|
||||
gmid: int
|
||||
gmname: str
|
||||
wallet: int = 0
|
||||
gsheet: str
|
||||
team_value: int = 0
|
||||
collection_value: int = 0
|
||||
logo: Optional[str] = None
|
||||
color: Optional[str] = None
|
||||
season: int
|
||||
ps_shiny: Optional[int] = 0
|
||||
ranking: Optional[int] = 1000
|
||||
has_guide: Optional[bool] = False
|
||||
is_ai: Optional[bool] = False
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_teams(
|
||||
season: Optional[int] = None, gm_id: Optional[int] = None, abbrev: Optional[str] = None,
|
||||
tv_min: Optional[int] = None, tv_max: Optional[int] = None, cv_min: Optional[int] = None,
|
||||
cv_max: Optional[int] = None, ps_shiny_min: Optional[int] = None, ps_shiny_max: Optional[int] = None,
|
||||
ranking_min: Optional[int] = None, ranking_max: Optional[int] = None, has_guide: Optional[bool] = None,
|
||||
sname: Optional[str] = None, lname: Optional[str] = None, is_ai: Optional[bool] = None,
|
||||
event_id: Optional[int] = None, limit: Optional[int] = None, csv: Optional[bool] = False):
|
||||
"""
|
||||
Param: season: int
|
||||
Param: team_abbrev: string
|
||||
Param: owner_id: int
|
||||
"""
|
||||
if season:
|
||||
all_teams = Team.select_season(season)
|
||||
else:
|
||||
all_teams = Team.select()
|
||||
|
||||
# if all_teams.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'There are no teams to filter')
|
||||
|
||||
if gm_id is not None:
|
||||
all_teams = all_teams.where(Team.gmid == gm_id)
|
||||
|
||||
if abbrev is not None:
|
||||
all_teams = all_teams.where(fn.Lower(Team.abbrev) == abbrev.lower())
|
||||
|
||||
if sname is not None:
|
||||
all_teams = all_teams.where(fn.Lower(Team.sname) == sname.lower())
|
||||
|
||||
if lname is not None:
|
||||
all_teams = all_teams.where(fn.Lower(Team.lname) == lname.lower())
|
||||
|
||||
if tv_min is not None:
|
||||
all_teams = all_teams.where(Team.team_value >= tv_min)
|
||||
|
||||
if tv_max is not None:
|
||||
all_teams = all_teams.where(Team.team_value <= tv_max)
|
||||
|
||||
if cv_min is not None:
|
||||
all_teams = all_teams.where(Team.collection_value >= cv_min)
|
||||
|
||||
if cv_max is not None:
|
||||
all_teams = all_teams.where(Team.collection_value <= cv_max)
|
||||
|
||||
if ps_shiny_min is not None:
|
||||
all_teams = all_teams.where(Team.career >= ps_shiny_min)
|
||||
|
||||
if ps_shiny_max is not None:
|
||||
all_teams = all_teams.where(Team.career <= ps_shiny_max)
|
||||
|
||||
if ranking_min is not None:
|
||||
all_teams = all_teams.where(Team.ranking >= ranking_min)
|
||||
|
||||
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:
|
||||
if not has_guide:
|
||||
all_teams = all_teams.where(Team.has_guide == 0)
|
||||
else:
|
||||
all_teams = all_teams.where(Team.has_guide == 1)
|
||||
|
||||
if is_ai is not None:
|
||||
all_teams = all_teams.where(Team.is_ai)
|
||||
|
||||
if event_id is not None:
|
||||
all_teams = all_teams.where(Team.event_id == event_id)
|
||||
|
||||
if limit is not None:
|
||||
all_teams = all_teams.limit(limit)
|
||||
|
||||
if csv:
|
||||
return_val = query_to_csv(all_teams, exclude=[Team.career])
|
||||
db.close()
|
||||
return Response(content=return_val, media_type='text/csv')
|
||||
|
||||
else:
|
||||
return_teams = {'count': all_teams.count(), '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):
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
|
||||
p_query = Pack.select().where((Pack.team == this_team) & (Pack.open_time.is_null(True)))
|
||||
if csv:
|
||||
data = model_to_dict(this_team)
|
||||
data['sealed_packs'] = p_query.count()
|
||||
return_val = complex_data_to_csv([data])
|
||||
else:
|
||||
return_val = model_to_dict(this_team)
|
||||
if inc_packs:
|
||||
return_val['sealed_packs'] = [model_to_dict(x) for x in p_query]
|
||||
|
||||
db.close()
|
||||
return return_val
|
||||
|
||||
|
||||
@router.get('/{team_id}/lineup/{difficulty_name}')
|
||||
async def get_team_lineup(team_id: int, difficulty_name: str, pitcher_name: str, d_rank: int = 5, o_rank: int = 5):
|
||||
"""
|
||||
d_rank: int - 10: best overall, 9: prioritize range, 8: prioritize error
|
||||
"""
|
||||
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():
|
||||
db.close()
|
||||
raise HTTPException(status_code=400, detail=f'Difficulty name {difficulty_name} not a valid check')
|
||||
|
||||
# all_players = Player.select().where(
|
||||
# (fn.Lower(Player.p_name) != pitcher_name.lower()) & (Player.mlbclub == this_team.lname)
|
||||
# )
|
||||
all_players = Player.select().where(Player.mlbclub == this_team.lname)
|
||||
|
||||
legal_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['primary'])
|
||||
if 'secondary' in CARDSETS[difficulty_name]:
|
||||
backup_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['secondary'])
|
||||
else:
|
||||
backup_players = None
|
||||
|
||||
logging.info(f'legal_players: {legal_players.count()}')
|
||||
if backup_players is not None:
|
||||
logging.info(f'backup_players: {backup_players.count()}')
|
||||
player_names = []
|
||||
starting_nine = {
|
||||
'C': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
|
||||
'1B': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
|
||||
'2B': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
|
||||
'3B': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
|
||||
'SS': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
|
||||
'LF': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
|
||||
'CF': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
|
||||
'RF': {'player': None, 'vl': None, 'vr': None, 'ops': 0},
|
||||
'DH': {'player': None, 'vl': None, 'vr': None, 'ops': 0}
|
||||
}
|
||||
|
||||
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'
|
||||
)
|
||||
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), model_to_dict(vr_ratings), (vl_ops + vr_ops + min(vl_ops, vr_ops)) / 3
|
||||
|
||||
# IDEA: Rank guys by their bat per-position and take the best one that meets a threshold of defensive ability
|
||||
for position in starting_nine.keys():
|
||||
if position == 'DH':
|
||||
# all_bcards = BattingCard.select().where(BattingCard.player << legal_players)
|
||||
# all_batters = BattingCardRatings.select().where(
|
||||
# BattingCardRatings.battingcard << all_bcards
|
||||
# ).order_by(BattingCardRatings.obp + BattingCardRatings.sl)
|
||||
#
|
||||
# for x in all_batters:
|
||||
# if x.battingcard.player.p_name not in player_names:
|
||||
# starting_nine['DH'] = x.battingcard.player
|
||||
# break
|
||||
logging.info(f'Searching for a DH!')
|
||||
dh_query = legal_players.order_by(Player.cost.desc())
|
||||
for x in dh_query:
|
||||
logging.info(f'checking {x.p_name} for {position}')
|
||||
if x.p_name not in player_names and 'P' not in x.pos_1:
|
||||
logging.info(f'adding!')
|
||||
starting_nine['DH']['player'] = model_to_dict(x)
|
||||
try:
|
||||
vl, vr, total_ops = get_bratings(x.player_id)
|
||||
except AttributeError as e:
|
||||
logging.info(f'Could not find batting lines')
|
||||
else:
|
||||
starting_nine['DH']['vl'] = vl
|
||||
starting_nine['DH']['vr'] = vr
|
||||
starting_nine['DH']['ops'] = total_ops
|
||||
player_names.append(x.p_name)
|
||||
break
|
||||
|
||||
if starting_nine['DH']['player'] is None:
|
||||
dh_query = backup_players.order_by(Player.cost.desc())
|
||||
for x in dh_query:
|
||||
logging.info(f'checking {x.p_name} for {position}')
|
||||
if x.p_name not in player_names:
|
||||
logging.info(f'adding!')
|
||||
starting_nine['DH']['player'] = model_to_dict(x)
|
||||
try:
|
||||
vl, vr, total_ops = get_bratings(x.player_id)
|
||||
except AttributeError as e:
|
||||
logging.info(f'Could not find batting lines')
|
||||
else:
|
||||
vl, vr, total_ops = get_bratings(x.player_id)
|
||||
starting_nine['DH']['vl'] = vl
|
||||
starting_nine['DH']['vr'] = vr
|
||||
starting_nine['DH']['ops'] = total_ops
|
||||
player_names.append(x.p_name)
|
||||
break
|
||||
|
||||
else:
|
||||
pos_group = CardPosition.select().where(
|
||||
(CardPosition.position == position) & (CardPosition.player << legal_players)
|
||||
)
|
||||
backup_group = CardPosition.select().where(
|
||||
(CardPosition.position == position) & (CardPosition.player << backup_players)
|
||||
)
|
||||
if difficulty_name in ['minor-league', 'gauntlet-3']:
|
||||
pos_group = pos_group.order_by(CardPosition.innings.desc())
|
||||
elif d_rank == 10:
|
||||
pos_group = pos_group.order_by((CardPosition.range * 5) + CardPosition.error)
|
||||
elif d_rank == 9:
|
||||
pos_group = pos_group.order_by(CardPosition.range)
|
||||
elif d_rank == 8:
|
||||
pos_group = pos_group.order_by(CardPosition.error.desc())
|
||||
|
||||
logging.info(f'pos_group: {pos_group}\n{starting_nine}\n{player_names}\n\n')
|
||||
if difficulty_name in ['minor-league', 'gauntlet-3']:
|
||||
for x in pos_group:
|
||||
logging.info(f'checking {x.player.p_name} for {position}')
|
||||
if x.player.p_name not in player_names and x.player.p_name.lower() != pitcher_name:
|
||||
logging.info(f'adding!')
|
||||
starting_nine[position]['player'] = model_to_dict(x.player)
|
||||
vl, vr, total_ops = get_bratings(x.player.player_id)
|
||||
starting_nine[position]['vl'] = vl
|
||||
starting_nine[position]['vr'] = vr
|
||||
starting_nine[position]['ops'] = total_ops
|
||||
player_names.append(x.player.p_name)
|
||||
break
|
||||
|
||||
if starting_nine[position]['player'] is None:
|
||||
for x in backup_group:
|
||||
logging.info(f'checking {x.player.p_name} for {position}')
|
||||
if x.player.p_name not in player_names and x.player.p_name.lower() != pitcher_name:
|
||||
logging.info(f'adding!')
|
||||
starting_nine[position]['player'] = model_to_dict(x.player)
|
||||
vl, vr, total_ops = get_bratings(x.player.player_id)
|
||||
starting_nine[position]['vl'] = vl
|
||||
starting_nine[position]['vr'] = vr
|
||||
starting_nine[position]['ops'] = total_ops
|
||||
player_names.append(x.player.p_name)
|
||||
break
|
||||
|
||||
# all_bcards = BattingCard.select().where(BattingCard.player << starting_nine.values())
|
||||
# all_ratings = BattingCardRatings.select().where(BattingCardRatings.battingcard << all_bcards)
|
||||
#
|
||||
# vl_query = all_ratings.where(BattingCardRatings.vs_hand == 'L')
|
||||
# vr_query = all_ratings.where(BattingCardRatings.vs_hand == 'R')
|
||||
#
|
||||
# vl_vals = [model_to_dict(x) for x in vl_query]
|
||||
# for x in vl_vals:
|
||||
# x.update(x['battingcard'])
|
||||
# x['player_id'] = x['battingcard']['player']['player_id']
|
||||
# x['player_name'] = x['battingcard']['player']['p_name']
|
||||
# x['rarity'] = x['battingcard']['player']['rarity']['name']
|
||||
# x['cardset_id'] = x['battingcard']['player']['cardset']['id']
|
||||
# x['cardset_name'] = x['battingcard']['player']['cardset']['name']
|
||||
# del x['player']
|
||||
#
|
||||
# vr_vals = [model_to_dict(x) for x in vr_query]
|
||||
# for x in vr_vals:
|
||||
# x['player_id'] = x['battingcard']['player']['player_id']
|
||||
# del x['battingcard']
|
||||
#
|
||||
# vl = pd.DataFrame(vl_vals)
|
||||
# vr = pd.DataFrame(vr_vals)
|
||||
# db.close()
|
||||
#
|
||||
# output = pd.merge(vl, vr, on='player_id', suffixes=('_vl', '_vr'))
|
||||
#
|
||||
# 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']
|
||||
# return (ops_vr + ops_vl + min(ops_vl, ops_vr)) / 3
|
||||
# output['total_OPS'] = output.apply(get_total_ops, axis=1)
|
||||
# output = output.sort_values(by=['total_OPS'], ascending=False)
|
||||
|
||||
sorted_nine = sorted(starting_nine.items(), key=lambda item: item[1]['ops'], reverse=True)
|
||||
return {
|
||||
'json': dict(sorted_nine),
|
||||
'array': sorted_nine
|
||||
}
|
||||
|
||||
|
||||
def sort_pitchers(pitching_card_query) -> DataFrame | None:
|
||||
all_s = [model_to_dict(x, recurse=False) for x in pitching_card_query]
|
||||
if len(all_s) == 0:
|
||||
logging.error(f'Empty pitching_card_query: {pitching_card_query}')
|
||||
return None
|
||||
|
||||
pitcher_df = pd.DataFrame(all_s).set_index('player', drop=False)
|
||||
logging.info(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')
|
||||
|
||||
ops_vl = vlval.obp + vlval.slg
|
||||
ops_vr = vrval.obp + vrval.slg
|
||||
return (ops_vr + ops_vl + min(ops_vl, ops_vr)) / 3
|
||||
|
||||
pitcher_df['total_ops'] = pitcher_df.apply(get_total_ops, axis=1)
|
||||
return pitcher_df.sort_values(by='total_ops')
|
||||
|
||||
|
||||
@router.get('/{team_id}/sp/{difficulty_name}')
|
||||
async def get_team_sp(team_id: int, difficulty_name: str, sp_rank: int):
|
||||
logging.info(f'get_team_sp - team_id: {team_id} / difficulty_name: {difficulty_name} / sp_rank: {sp_rank}')
|
||||
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():
|
||||
db.close()
|
||||
raise HTTPException(status_code=400, detail=f'Difficulty name {difficulty_name} not a valid check')
|
||||
|
||||
all_players = Player.select().where(Player.mlbclub == this_team.lname)
|
||||
|
||||
legal_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['primary'])
|
||||
if 'secondary' in CARDSETS[difficulty_name]:
|
||||
backup_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['secondary'])
|
||||
else:
|
||||
backup_players = None
|
||||
|
||||
def sort_starters(starter_query) -> DataFrame | None:
|
||||
all_s = [model_to_dict(x, recurse=False) for x in starter_query]
|
||||
if len(all_s) == 0:
|
||||
logging.error(f'Empty starter_query: {starter_query}')
|
||||
return None
|
||||
|
||||
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')
|
||||
|
||||
ops_vl = vlval.obp + vlval.slg
|
||||
ops_vr = vrval.obp + vrval.slg
|
||||
return (ops_vr + ops_vl + min(ops_vl, ops_vr)) / 3
|
||||
|
||||
starter_df['total_ops'] = starter_df.apply(get_total_ops, axis=1)
|
||||
return starter_df.sort_values(by='total_ops')
|
||||
|
||||
# Find SP in primary cardsets
|
||||
s_query = PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << legal_players) & (PitchingCard.starter_rating >= 4)
|
||||
)
|
||||
all_starters = sort_starters(s_query)
|
||||
logging.debug(f'sorted: {all_starters}')
|
||||
|
||||
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
|
||||
s_query = PitchingCard.select().where(
|
||||
(PitchingCard.player << backup_players) & (PitchingCard.starter_rating >= 4)
|
||||
)
|
||||
all_starters = sort_starters(s_query)
|
||||
logging.debug(f'sorted: {all_starters}')
|
||||
|
||||
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(status_code=500, detail=f'No SP #{sp_rank} found for Team {team_id}')
|
||||
|
||||
|
||||
@router.get('/{team_id}/rp/{difficulty_name}')
|
||||
async def get_team_rp(
|
||||
team_id: int, difficulty_name: str, need: Literal['length', 'setup', 'closer', 'middle'],
|
||||
used_pitcher_ids: list = Query(default=[])):
|
||||
logging.info(f'get_team_rp - team_id: {team_id} / difficulty_name: {difficulty_name} / need: {need} '
|
||||
f'/ used_pitcher_ids: {used_pitcher_ids}')
|
||||
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():
|
||||
db.close()
|
||||
raise HTTPException(status_code=400, detail=f'Difficulty name {difficulty_name} not a valid check')
|
||||
|
||||
all_players = Player.select().where(
|
||||
(Player.mlbclub == this_team.lname) & (Player.player_id.not_in(used_pitcher_ids))
|
||||
)
|
||||
|
||||
legal_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['primary'])
|
||||
if 'secondary' in CARDSETS[difficulty_name]:
|
||||
backup_players = all_players.where(Player.cardset_id << CARDSETS[difficulty_name]['secondary'])
|
||||
else:
|
||||
backup_players = None
|
||||
|
||||
if need == 'closer':
|
||||
for query in [PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << legal_players) & (PitchingCard.closer_rating >= 3) &
|
||||
(PitchingCard.starter_rating == 1)),
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << legal_players) & (PitchingCard.closer_rating >= 1) &
|
||||
(PitchingCard.starter_rating == 1)),
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << backup_players) & (PitchingCard.closer_rating >= 3) &
|
||||
(PitchingCard.starter_rating == 1)),
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << backup_players) & (PitchingCard.closer_rating >= 1) &
|
||||
(PitchingCard.starter_rating == 1)),
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << backup_players) & (PitchingCard.starter_rating < 4))
|
||||
]:
|
||||
all_relievers = sort_pitchers(query)
|
||||
|
||||
if all_relievers is not None:
|
||||
this_player_id = all_relievers.iloc[0].player
|
||||
this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False)
|
||||
db.close()
|
||||
return this_player
|
||||
|
||||
elif need == 'setup':
|
||||
for query in [PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << legal_players) & (PitchingCard.starter_rating == 1)),
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << backup_players) & (PitchingCard.starter_rating < 4))
|
||||
]:
|
||||
all_relievers = sort_pitchers(query)
|
||||
|
||||
if all_relievers is not None and len(all_relievers.index) >= 2:
|
||||
this_player_id = all_relievers.iloc[1].player
|
||||
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:
|
||||
for query in [PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << legal_players) & (PitchingCard.relief_rating >= 3) &
|
||||
(PitchingCard.starter_rating < 4)),
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << legal_players) & (PitchingCard.relief_rating >= 2) &
|
||||
(PitchingCard.starter_rating < 4)),
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << backup_players) & (PitchingCard.relief_rating >= 2) &
|
||||
(PitchingCard.starter_rating < 4))
|
||||
]:
|
||||
all_relievers = sort_pitchers(query)
|
||||
|
||||
if all_relievers is not None:
|
||||
this_player_id = all_relievers.iloc[0].player
|
||||
this_player = model_to_dict(Player.get_by_id(this_player_id), recurse=False)
|
||||
db.close()
|
||||
return this_player
|
||||
|
||||
elif need == 'middle':
|
||||
for query in [PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << legal_players) & (PitchingCard.starter_rating == 1)),
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << backup_players) & (PitchingCard.starter_rating < 4))
|
||||
]:
|
||||
all_relievers = sort_pitchers(query)
|
||||
|
||||
if all_relievers is not None and len(all_relievers.index) >= 3:
|
||||
this_player_id = all_relievers.iloc[2].player
|
||||
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')
|
||||
all_relievers = sort_pitchers(
|
||||
PitchingCard.select().join(Player).where(
|
||||
(PitchingCard.player << backup_players) | (PitchingCard.player << legal_players)
|
||||
)
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
|
||||
@router.get('/{team_id}/buy/players')
|
||||
async def team_buy_players(team_id: int, ids: str, ts: str):
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
|
||||
if ts != this_team.team_hash():
|
||||
logging.warning(f'Bad Team Secret: {ts} ({this_team.team_hash()})')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=f'You are not authorized to buy {this_team.abbrev} cards. This event has been logged.'
|
||||
)
|
||||
|
||||
last_card = Card.select(Card.id).order_by(-Card.id).limit(1)
|
||||
lc_id = last_card[0].id
|
||||
|
||||
all_ids = ids.split(',')
|
||||
conf_message = ''
|
||||
total_cost = 0
|
||||
for player_id in all_ids:
|
||||
if player_id != '':
|
||||
try:
|
||||
this_player = Player.get_by_id(player_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No player found with id {player_id} /// '
|
||||
f'{conf_message} purchased')
|
||||
|
||||
# check wallet balance
|
||||
if this_team.wallet < this_player.cost:
|
||||
logging.info(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 '
|
||||
f'{this_player} costs {this_player.cost}₼. /// {conf_message} purchased'
|
||||
)
|
||||
|
||||
# Create player card and update cost
|
||||
buy_price = this_player.cost
|
||||
total_cost += buy_price
|
||||
this_card = Card(
|
||||
player_id=this_player.player_id,
|
||||
team_id=this_team.id,
|
||||
value=buy_price
|
||||
)
|
||||
Paperdex.get_or_create(team_id=team_id, player_id=this_player.player_id)
|
||||
this_card.save()
|
||||
this_player.change_on_buy()
|
||||
|
||||
# Deduct card cost from team
|
||||
logging.info(f'{this_team.abbrev} starting wallet: {this_team.wallet}')
|
||||
this_team.wallet -= buy_price
|
||||
this_team.save()
|
||||
logging.info(f'{this_team.abbrev} ending wallet: {this_team.wallet}')
|
||||
|
||||
# Post a notification
|
||||
if this_player.rarity.value >= 2:
|
||||
new_notif = Notification(
|
||||
created=int_timestamp(datetime.now()),
|
||||
title=f'Price Change',
|
||||
desc='Modified by buying and selling',
|
||||
field_name=f'{this_player.description} '
|
||||
f'{this_player.p_name if this_player.p_name not in this_player.description else ""}',
|
||||
message=f'From {buy_price}₼ 📈 to **{this_player.cost}**₼',
|
||||
about=f'Player-{this_player.player_id}'
|
||||
)
|
||||
new_notif.save()
|
||||
|
||||
conf_message += f'{buy_price}₼ for {this_player.rarity.name} {this_player.p_name} ' \
|
||||
f'({this_player.cardset.name}), '
|
||||
|
||||
# sheets.post_new_cards(SHEETS_AUTH, lc_id)
|
||||
|
||||
raise HTTPException(status_code=200, detail=f'{conf_message} purchased. /// Total Cost: {total_cost}₼ /// '
|
||||
f'Final Wallet: {this_team.wallet}')
|
||||
|
||||
|
||||
@router.get('/{team_id}/buy/pack/{packtype_id}')
|
||||
async def team_buy_packs(team_id: int, packtype_id: int, ts: str, quantity: Optional[int] = 1):
|
||||
try:
|
||||
this_packtype = PackType.get_by_id(packtype_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No pack type found with id {packtype_id}')
|
||||
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
|
||||
if ts != this_team.team_hash():
|
||||
logging.warning(f'Bad Team Secret: {ts} ({this_team.team_hash()})')
|
||||
db.close()
|
||||
logging.warning(f'team: {this_team} / pack_type: {this_packtype} / secret: {ts} / '
|
||||
f'actual: {this_team.team_hash()}')
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=f'You are not authorized to buy {this_team.abbrev} packs. This event has been logged.'
|
||||
)
|
||||
|
||||
# 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 '
|
||||
f'{this_packtype} costs {this_packtype.cost}.'
|
||||
)
|
||||
|
||||
all_packs = []
|
||||
for i in range(quantity):
|
||||
all_packs.append(Pack(team_id=this_team.id, pack_type_id=this_packtype.id))
|
||||
|
||||
# Deduct card cost from team
|
||||
logging.info(f'{this_team.abbrev} starting wallet: {this_team.wallet}')
|
||||
this_team.wallet -= total_cost
|
||||
this_team.save()
|
||||
logging.info(f'{this_team.abbrev} ending wallet: {this_team.wallet}')
|
||||
|
||||
with db.atomic():
|
||||
Pack.bulk_create(all_packs, batch_size=15)
|
||||
db.close()
|
||||
|
||||
raise HTTPException(
|
||||
status_code=200,
|
||||
detail=f'Quantity {quantity} {this_packtype.name} pack{"s" if quantity > 1 else ""} have been purchased by '
|
||||
f'{this_team.lname} for {total_cost} bucks. You may close this window.'
|
||||
)
|
||||
|
||||
|
||||
@router.get('/{team_id}/sell/cards')
|
||||
async def team_sell_cards(team_id: int, ids: str, ts: str):
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
|
||||
if ts != this_team.team_hash():
|
||||
logging.warning(f'Bad Team Secret: {ts} ({this_team.team_hash()})')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail=f'You are not authorized to sell {this_team.abbrev} cards. This event has been logged.'
|
||||
)
|
||||
|
||||
all_ids = ids.split(',')
|
||||
del_ids = []
|
||||
conf_message = ''
|
||||
total_cost = 0
|
||||
for card_id in all_ids:
|
||||
if card_id != '':
|
||||
try:
|
||||
this_card = Card.get_by_id(card_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No card found with id {card_id}')
|
||||
|
||||
del_ids.append(card_id)
|
||||
this_player = this_card.player
|
||||
|
||||
if this_card.team != this_team:
|
||||
raise HTTPException(status_code=401,
|
||||
detail=f'Card id {card_id} ({this_player.p_name}) belongs to '
|
||||
f'{this_card.team.abbrev} and cannot be sold. /// {conf_message} sold')
|
||||
|
||||
orig_price = this_player.cost
|
||||
sell_price = round(this_player.cost * .5)
|
||||
total_cost += sell_price
|
||||
|
||||
# credit selling team's wallet
|
||||
if this_team.wallet is None:
|
||||
this_team.wallet = sell_price
|
||||
else:
|
||||
this_team.wallet += sell_price
|
||||
this_team.save()
|
||||
|
||||
# decrease price of player
|
||||
this_player.change_on_sell()
|
||||
this_card.delete_instance()
|
||||
|
||||
# post a notification
|
||||
if this_player.rarity.value >= 2:
|
||||
new_notif = Notification(
|
||||
created=int_timestamp(datetime.now()),
|
||||
title=f'Price Change',
|
||||
desc='Modified by buying and selling',
|
||||
field_name=f'{this_player.description} '
|
||||
f'{this_player.p_name if this_player.p_name not in this_player.description else ""}',
|
||||
message=f'From {orig_price}₼ 📉 to **{this_player.cost}**₼',
|
||||
about=f'Player-{this_player.id}'
|
||||
)
|
||||
new_notif.save()
|
||||
|
||||
conf_message += f'{sell_price}₼ for {this_player.rarity.name} {this_player.p_name} ' \
|
||||
f'({this_player.cardset.name}), '
|
||||
|
||||
# sheets.post_deletion(SHEETS_AUTH, del_ids)
|
||||
raise HTTPException(status_code=200, detail=f'{conf_message} sold. /// Total Earned: {total_cost}₼ /// '
|
||||
f'Final Wallet: {this_team.wallet}')
|
||||
|
||||
|
||||
@router.get('/{team_id}/cards')
|
||||
async def get_team_cards(team_id, csv: Optional[bool] = True):
|
||||
"""
|
||||
CSV output specifically targeting team roster sheet
|
||||
|
||||
Parameters
|
||||
----------
|
||||
team_id
|
||||
csv
|
||||
"""
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
|
||||
if not csv:
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail='The /teams/{team_id}/cards endpoint only supports csv output.'
|
||||
)
|
||||
|
||||
all_cards = (Card
|
||||
.select()
|
||||
.join(Player)
|
||||
.join(Rarity)
|
||||
.where(Card.team == this_team)
|
||||
.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'])
|
||||
x['player_id'] = x['player']['player_id']
|
||||
x['player_name'] = x['player']['p_name']
|
||||
x['cardset_id'] = x['player']['cardset']['id']
|
||||
x['cardset_name'] = x['player']['cardset']['name']
|
||||
x['rarity'] = x['player']['rarity']['name']
|
||||
x['card_id'] = x['id']
|
||||
|
||||
card_df = pd.DataFrame(card_vals)
|
||||
output = card_df[[
|
||||
'cardset_name', 'player_name', 'rarity', 'image', 'image2', 'pos_1', 'pos_2', 'pos_3', 'pos_4', 'pos_5',
|
||||
'pos_6', 'pos_7', 'pos_8', 'cost', 'mlbclub', 'franchise', 'fangr_id', 'bbref_id', 'player_id', 'card_id']]
|
||||
return Response(content=pd.DataFrame(output).to_csv(index=False), media_type='text/csv')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_team(team: TeamModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post teams. This event has been logged.'
|
||||
)
|
||||
|
||||
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}')
|
||||
|
||||
this_team = Team(
|
||||
abbrev=team.abbrev,
|
||||
sname=team.sname,
|
||||
lname=team.lname,
|
||||
gmid=team.gmid,
|
||||
gmname=team.gmname,
|
||||
wallet=team.wallet,
|
||||
gsheet=team.gsheet,
|
||||
team_value=team.team_value,
|
||||
collection_value=team.collection_value,
|
||||
logo=team.logo,
|
||||
color=team.color,
|
||||
ranking=team.ranking,
|
||||
season=team.season,
|
||||
career=team.ps_shiny,
|
||||
has_guide=team.has_guide,
|
||||
is_ai=team.is_ai
|
||||
)
|
||||
|
||||
saved = this_team.save()
|
||||
if saved == 1:
|
||||
return_team = model_to_dict(this_team)
|
||||
db.close()
|
||||
return return_team
|
||||
else:
|
||||
raise HTTPException(status_code=418, detail='Well slap my ass and call me a teapot; I could not save that team')
|
||||
|
||||
|
||||
@router.post('/new-season/{new_season}')
|
||||
async def team_season_update(new_season: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to post teams. This event has been logged.'
|
||||
)
|
||||
|
||||
r_query = Team.update(ranking=1000, season=new_season, wallet=Team.wallet + 250).execute()
|
||||
current = Current.latest()
|
||||
current.season = new_season
|
||||
current.save()
|
||||
db.close()
|
||||
|
||||
return {'detail': f'Team rankings, season, and wallet updated for season {new_season}'}
|
||||
|
||||
|
||||
@router.post('/{team_id}/money/{delta}')
|
||||
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()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to adjust wallets. This event has been logged.'
|
||||
)
|
||||
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
|
||||
this_team.wallet += delta
|
||||
|
||||
if this_team.save() == 1:
|
||||
return_team = model_to_dict(this_team)
|
||||
db.close()
|
||||
return return_team
|
||||
else:
|
||||
raise HTTPException(status_code=418, detail='Well slap my ass and call me a teapot; I could not save that team')
|
||||
|
||||
|
||||
@router.patch('/{team_id}')
|
||||
async def patch_team(
|
||||
team_id, sname: Optional[str] = None, lname: Optional[str] = None, gmid: Optional[int] = None,
|
||||
gmname: Optional[str] = None, gsheet: Optional[str] = None, team_value: Optional[int] = None,
|
||||
collection_value: Optional[int] = None, logo: Optional[str] = None, color: Optional[str] = None,
|
||||
season: Optional[int] = None, ps_shiny: Optional[int] = None, wallet_delta: Optional[int] = None,
|
||||
has_guide: Optional[bool] = None, is_ai: Optional[bool] = None, ranking: Optional[int] = None,
|
||||
token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete teams. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
|
||||
if sname is not None:
|
||||
this_team.sname = sname
|
||||
if lname is not None:
|
||||
this_team.lname = lname
|
||||
if gmid is not None:
|
||||
this_team.gmid = gmid
|
||||
if gmname is not None:
|
||||
this_team.gmname = gmname
|
||||
if gsheet is not None:
|
||||
this_team.gsheet = gsheet
|
||||
if team_value is not None:
|
||||
this_team.team_value = team_value
|
||||
if collection_value is not None:
|
||||
this_team.collection_value = collection_value
|
||||
if logo is not None:
|
||||
this_team.logo = logo
|
||||
if color is not None:
|
||||
this_team.color = color
|
||||
if season is not None:
|
||||
this_team.season = season
|
||||
if ps_shiny is not None:
|
||||
this_team.career = ps_shiny
|
||||
if ranking is not None:
|
||||
this_team.ranking = ranking
|
||||
if wallet_delta is not None:
|
||||
this_team.wallet += wallet_delta
|
||||
if has_guide is not None:
|
||||
if has_guide:
|
||||
this_team.has_guide = 1
|
||||
else:
|
||||
this_team.has_guide = 0
|
||||
if is_ai is not None:
|
||||
if is_ai:
|
||||
this_team.is_ai = 1
|
||||
else:
|
||||
this_team.is_ai = 0
|
||||
|
||||
if this_team.save() == 1:
|
||||
return_team = model_to_dict(this_team)
|
||||
db.close()
|
||||
return return_team
|
||||
else:
|
||||
raise HTTPException(status_code=418, detail='Well slap my ass and call me a teapot; I could not save that team')
|
||||
|
||||
|
||||
@router.delete('/{team_id}')
|
||||
async def delete_team(team_id, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'Bad Token: {token}')
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail='You are not authorized to delete teams. This event has been logged.'
|
||||
)
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
|
||||
|
||||
count = this_team.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
raise HTTPException(status_code=200, detail=f'Team {team_id} has been deleted')
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Team {team_id} was not deleted')
|
||||
517
db_engine.py
517
db_engine.py
@ -1,9 +1,13 @@
|
||||
import math
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
import logging
|
||||
import os
|
||||
|
||||
from pandas import DataFrame
|
||||
from peewee import *
|
||||
from peewee import ModelSelect
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
|
||||
db = SqliteDatabase(
|
||||
'storage/pd_master.db',
|
||||
@ -23,6 +27,62 @@ logging.basicConfig(
|
||||
)
|
||||
|
||||
|
||||
def model_csv_headers(this_obj, exclude=None) -> List:
|
||||
data = model_to_dict(this_obj, recurse=False, exclude=exclude)
|
||||
return [x for x in data.keys()]
|
||||
|
||||
|
||||
def model_to_csv(this_obj, exclude=None) -> List:
|
||||
data = model_to_dict(this_obj, recurse=False, exclude=exclude)
|
||||
return [x for x in data.values()]
|
||||
|
||||
|
||||
def query_to_csv(all_items: ModelSelect, exclude=None):
|
||||
if all_items.count() == 0:
|
||||
data_list = [['No data found']]
|
||||
else:
|
||||
data_list = [model_csv_headers(all_items[0], exclude=exclude)]
|
||||
for x in all_items:
|
||||
data_list.append(model_to_csv(x, exclude=exclude))
|
||||
|
||||
return DataFrame(data_list).to_csv(header=False, index=False)
|
||||
|
||||
|
||||
def complex_data_to_csv(complex_data: List):
|
||||
if len(complex_data) == 0:
|
||||
data_list = [['No data found']]
|
||||
else:
|
||||
data_list = [[x for x in complex_data[0].keys()]]
|
||||
for line in complex_data:
|
||||
logging.debug(f'line: {line}')
|
||||
this_row = []
|
||||
for key in line:
|
||||
logging.debug(f'key: {key}')
|
||||
if line[key] is None:
|
||||
this_row.append('')
|
||||
|
||||
elif isinstance(line[key], dict):
|
||||
if 'name' in line[key]:
|
||||
this_row.append(line[key]['name'])
|
||||
elif 'abbrev' in line[key]:
|
||||
this_row.append(line[key]['abbrev'])
|
||||
else:
|
||||
this_row.append(line[key]['id'])
|
||||
|
||||
elif isinstance(line[key], int) and line[key] > 100000000:
|
||||
this_row.append(f"'{line[key]}")
|
||||
|
||||
elif isinstance(line[key], str) and ',' in line[key]:
|
||||
this_row.append(line[key].replace(",", "-_-"))
|
||||
|
||||
else:
|
||||
this_row.append(line[key])
|
||||
|
||||
data_list.append(this_row)
|
||||
|
||||
return DataFrame(data_list).to_csv(header=False, index=False)
|
||||
|
||||
|
||||
class BaseModel(Model):
|
||||
class Meta:
|
||||
database = db
|
||||
@ -84,6 +144,19 @@ class Cardset(BaseModel):
|
||||
db.create_tables([Cardset])
|
||||
|
||||
|
||||
class MlbPlayer(BaseModel):
|
||||
first_name = CharField()
|
||||
last_name = CharField()
|
||||
key_fangraphs = IntegerField(null=True)
|
||||
key_bbref = CharField(null=True)
|
||||
key_retro = CharField(null=True)
|
||||
key_mlbam = IntegerField(null=True)
|
||||
offense_col = IntegerField(default=1)
|
||||
|
||||
|
||||
db.create_tables([MlbPlayer])
|
||||
|
||||
|
||||
class Player(BaseModel):
|
||||
player_id = IntegerField(primary_key=True)
|
||||
p_name = CharField()
|
||||
@ -110,6 +183,7 @@ class Player(BaseModel):
|
||||
fangr_id = CharField(null=True)
|
||||
description = CharField()
|
||||
quantity = IntegerField(default=999)
|
||||
mlbplayer = ForeignKeyField(MlbPlayer, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.cardset} {self.p_name} ({self.rarity.name})'
|
||||
@ -321,11 +395,35 @@ class Roster(BaseModel):
|
||||
# this_roster.card26]
|
||||
|
||||
|
||||
class Result(BaseModel):
|
||||
away_team = ForeignKeyField(Team)
|
||||
home_team = ForeignKeyField(Team)
|
||||
away_score = IntegerField()
|
||||
home_score = IntegerField()
|
||||
away_team_value = IntegerField(null=True)
|
||||
home_team_value = IntegerField(null=True)
|
||||
away_team_ranking = IntegerField(null=True)
|
||||
home_team_ranking = IntegerField(null=True)
|
||||
scorecard = CharField()
|
||||
week = IntegerField()
|
||||
season = IntegerField()
|
||||
ranked = BooleanField()
|
||||
short_game = BooleanField()
|
||||
game_type = CharField(null=True)
|
||||
|
||||
@staticmethod
|
||||
def select_season(season=None):
|
||||
if not season:
|
||||
season = Current.get().season
|
||||
return Result.select().where(Result.season == season)
|
||||
|
||||
|
||||
class BattingStat(BaseModel):
|
||||
card = ForeignKeyField(Card)
|
||||
team = ForeignKeyField(Team)
|
||||
roster_num = IntegerField()
|
||||
vs_team = ForeignKeyField(Team)
|
||||
result = ForeignKeyField(Result, null=True)
|
||||
pos = CharField()
|
||||
pa = IntegerField()
|
||||
ab = IntegerField()
|
||||
@ -364,6 +462,7 @@ class PitchingStat(BaseModel):
|
||||
team = ForeignKeyField(Team)
|
||||
roster_num = IntegerField()
|
||||
vs_team = ForeignKeyField(Team)
|
||||
result = ForeignKeyField(Result, null=True)
|
||||
ip = FloatField()
|
||||
hit = IntegerField()
|
||||
run = IntegerField()
|
||||
@ -388,29 +487,6 @@ class PitchingStat(BaseModel):
|
||||
game_id = IntegerField()
|
||||
|
||||
|
||||
class Result(BaseModel):
|
||||
away_team = ForeignKeyField(Team)
|
||||
home_team = ForeignKeyField(Team)
|
||||
away_score = IntegerField()
|
||||
home_score = IntegerField()
|
||||
away_team_value = IntegerField(null=True)
|
||||
home_team_value = IntegerField(null=True)
|
||||
away_team_ranking = IntegerField(null=True)
|
||||
home_team_ranking = IntegerField(null=True)
|
||||
scorecard = CharField()
|
||||
week = IntegerField()
|
||||
season = IntegerField()
|
||||
ranked = BooleanField()
|
||||
short_game = BooleanField()
|
||||
game_type = CharField(null=True)
|
||||
|
||||
@staticmethod
|
||||
def select_season(season=None):
|
||||
if not season:
|
||||
season = Current.get().season
|
||||
return Result.select().where(Result.season == season)
|
||||
|
||||
|
||||
class Award(BaseModel):
|
||||
name = CharField()
|
||||
season = IntegerField()
|
||||
@ -484,6 +560,7 @@ db.create_tables([
|
||||
|
||||
class BattingCard(BaseModel):
|
||||
player = ForeignKeyField(Player)
|
||||
variant = IntegerField()
|
||||
steal_low = IntegerField()
|
||||
steal_high = IntegerField()
|
||||
steal_auto = BooleanField()
|
||||
@ -495,9 +572,16 @@ class BattingCard(BaseModel):
|
||||
hand = CharField(default='R')
|
||||
|
||||
|
||||
bc_index = ModelIndex(BattingCard, (BattingCard.player, BattingCard.variant), unique=True)
|
||||
BattingCard.add_index(bc_index)
|
||||
|
||||
|
||||
class BattingCardRatings(BaseModel):
|
||||
battingcard = ForeignKeyField(BattingCard)
|
||||
vs_hand = FloatField()
|
||||
vs_hand = CharField(default='R')
|
||||
pull_rate = FloatField()
|
||||
center_rate = FloatField()
|
||||
slap_rate = FloatField()
|
||||
homerun = FloatField()
|
||||
bp_homerun = FloatField()
|
||||
triple = FloatField()
|
||||
@ -525,20 +609,33 @@ class BattingCardRatings(BaseModel):
|
||||
slg = FloatField(null=True)
|
||||
|
||||
|
||||
bcr_index = ModelIndex(
|
||||
BattingCardRatings, (BattingCardRatings.battingcard, BattingCardRatings.vs_hand), unique=True
|
||||
)
|
||||
BattingCardRatings.add_index(bcr_index)
|
||||
|
||||
|
||||
class PitchingCard(BaseModel):
|
||||
player = ForeignKeyField(Player)
|
||||
variant = IntegerField()
|
||||
balk = IntegerField()
|
||||
wild_pitch = IntegerField(null=True)
|
||||
hold = CharField()
|
||||
wild_pitch = IntegerField()
|
||||
hold = IntegerField()
|
||||
starter_rating = IntegerField()
|
||||
relief_rating = IntegerField()
|
||||
closer_rating = IntegerField(null=True)
|
||||
batting = CharField(null=True)
|
||||
offense_col = IntegerField()
|
||||
hand = CharField(default='R')
|
||||
|
||||
|
||||
pc_index = ModelIndex(PitchingCard, (PitchingCard.player, PitchingCard.variant), unique=True)
|
||||
PitchingCard.add_index(pc_index)
|
||||
|
||||
|
||||
class PitchingCardRatings(BaseModel):
|
||||
pitchingcard = ForeignKeyField(PitchingCard)
|
||||
vs_hand = CharField()
|
||||
vs_hand = CharField(default='R')
|
||||
homerun = FloatField()
|
||||
bp_homerun = FloatField()
|
||||
triple = FloatField()
|
||||
@ -552,8 +649,9 @@ class PitchingCardRatings(BaseModel):
|
||||
hbp = FloatField()
|
||||
walk = FloatField()
|
||||
strikeout = FloatField()
|
||||
fo_slap = FloatField()
|
||||
fo_center = FloatField()
|
||||
flyout_lf_b = FloatField()
|
||||
flyout_cf_b = FloatField()
|
||||
flyout_rf_b = FloatField()
|
||||
groundout_a = FloatField()
|
||||
groundout_b = FloatField()
|
||||
xcheck_p = FloatField()
|
||||
@ -570,10 +668,15 @@ class PitchingCardRatings(BaseModel):
|
||||
slg = FloatField(null=True)
|
||||
|
||||
|
||||
pcr_index = ModelIndex(
|
||||
PitchingCardRatings, (PitchingCardRatings.pitchingcard, PitchingCardRatings.vs_hand), unique=True
|
||||
)
|
||||
PitchingCardRatings.add_index(pcr_index)
|
||||
|
||||
|
||||
class CardPosition(BaseModel):
|
||||
player = ForeignKeyField(Player)
|
||||
batting = ForeignKeyField(BattingCard, null=True)
|
||||
pitching = ForeignKeyField(PitchingCard, null=True)
|
||||
variant = IntegerField()
|
||||
position = CharField()
|
||||
innings = IntegerField()
|
||||
range = IntegerField()
|
||||
@ -583,182 +686,188 @@ class CardPosition(BaseModel):
|
||||
overthrow = IntegerField(null=True)
|
||||
|
||||
|
||||
pos_index = ModelIndex(
|
||||
CardPosition, (CardPosition.player, CardPosition.variant, CardPosition.position), unique=True
|
||||
)
|
||||
CardPosition.add_index(pos_index)
|
||||
|
||||
|
||||
db.create_tables([BattingCard, BattingCardRatings, PitchingCard, PitchingCardRatings, CardPosition])
|
||||
|
||||
|
||||
db.close()
|
||||
|
||||
scout_db = SqliteDatabase(
|
||||
'storage/card_creation.db',
|
||||
pragmas={
|
||||
'journal_mode': 'wal',
|
||||
'cache_size': -1 * 64000,
|
||||
'synchronous': 0
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class BaseModelScout(Model):
|
||||
class Meta:
|
||||
database = scout_db
|
||||
|
||||
|
||||
class ScoutCardset(BaseModelScout):
|
||||
set_title = CharField()
|
||||
set_subtitle = CharField(null=True)
|
||||
|
||||
|
||||
class ScoutPlayer(BaseModelScout):
|
||||
sba_id = IntegerField(primary_key=True)
|
||||
name = CharField()
|
||||
fg_id = IntegerField()
|
||||
br_id = CharField()
|
||||
offense_col = IntegerField()
|
||||
hand = CharField(default='R')
|
||||
|
||||
|
||||
scout_db.create_tables([ScoutCardset, ScoutPlayer])
|
||||
|
||||
|
||||
class BatterRatings(BaseModelScout):
|
||||
id = CharField(unique=True, primary_key=True)
|
||||
player = ForeignKeyField(ScoutPlayer)
|
||||
cardset = ForeignKeyField(ScoutCardset)
|
||||
vs_hand = FloatField()
|
||||
is_prep = BooleanField()
|
||||
homerun = FloatField()
|
||||
bp_homerun = FloatField()
|
||||
triple = FloatField()
|
||||
double_three = FloatField()
|
||||
double_two = FloatField()
|
||||
double_pull = FloatField()
|
||||
single_two = FloatField()
|
||||
single_one = FloatField()
|
||||
single_center = FloatField()
|
||||
bp_single = FloatField()
|
||||
hbp = FloatField()
|
||||
walk = FloatField()
|
||||
strikeout = FloatField()
|
||||
lineout = FloatField()
|
||||
popout = FloatField()
|
||||
flyout_a = FloatField()
|
||||
flyout_bq = FloatField()
|
||||
flyout_lf_b = FloatField()
|
||||
flyout_rf_b = FloatField()
|
||||
groundout_a = FloatField()
|
||||
groundout_b = FloatField()
|
||||
groundout_c = FloatField()
|
||||
avg = FloatField(null=True)
|
||||
obp = FloatField(null=True)
|
||||
slg = FloatField(null=True)
|
||||
|
||||
|
||||
class PitcherRatings(BaseModelScout):
|
||||
id = CharField(unique=True, primary_key=True)
|
||||
player = ForeignKeyField(ScoutPlayer)
|
||||
cardset = ForeignKeyField(ScoutCardset)
|
||||
vs_hand = CharField()
|
||||
is_prep = BooleanField()
|
||||
homerun = FloatField()
|
||||
bp_homerun = FloatField()
|
||||
triple = FloatField()
|
||||
double_three = FloatField()
|
||||
double_two = FloatField()
|
||||
double_cf = FloatField()
|
||||
single_two = FloatField()
|
||||
single_one = FloatField()
|
||||
single_center = FloatField()
|
||||
bp_single = FloatField()
|
||||
hbp = FloatField()
|
||||
walk = FloatField()
|
||||
strikeout = FloatField()
|
||||
fo_slap = FloatField()
|
||||
fo_center = FloatField()
|
||||
groundout_a = FloatField()
|
||||
groundout_b = FloatField()
|
||||
xcheck_p = FloatField()
|
||||
xcheck_c = FloatField()
|
||||
xcheck_1b = FloatField()
|
||||
xcheck_2b = FloatField()
|
||||
xcheck_3b = FloatField()
|
||||
xcheck_ss = FloatField()
|
||||
xcheck_lf = FloatField()
|
||||
xcheck_cf = FloatField()
|
||||
xcheck_rf = FloatField()
|
||||
avg = FloatField(null=True)
|
||||
obp = FloatField(null=True)
|
||||
slg = FloatField(null=True)
|
||||
|
||||
|
||||
# scout_db.create_tables([BatterRatings, PitcherRatings])
|
||||
|
||||
|
||||
class CardColumns(BaseModelScout):
|
||||
id = CharField(unique=True, primary_key=True)
|
||||
player = ForeignKeyField(ScoutPlayer)
|
||||
hand = CharField()
|
||||
b_ratings = ForeignKeyField(BatterRatings, null=True)
|
||||
p_ratings = ForeignKeyField(PitcherRatings, null=True)
|
||||
one_dice = CharField()
|
||||
one_results = CharField()
|
||||
one_splits = CharField()
|
||||
two_dice = CharField()
|
||||
two_results = CharField()
|
||||
two_splits = CharField()
|
||||
three_dice = CharField()
|
||||
three_results = CharField()
|
||||
three_splits = CharField()
|
||||
|
||||
|
||||
class Position(BaseModelScout):
|
||||
player = ForeignKeyField(ScoutPlayer)
|
||||
cardset = ForeignKeyField(ScoutCardset)
|
||||
position = CharField()
|
||||
innings = IntegerField()
|
||||
range = IntegerField()
|
||||
error = IntegerField()
|
||||
arm = CharField(null=True)
|
||||
pb = IntegerField(null=True)
|
||||
overthrow = IntegerField(null=True)
|
||||
|
||||
|
||||
class BatterData(BaseModelScout):
|
||||
player = ForeignKeyField(ScoutPlayer)
|
||||
cardset = ForeignKeyField(ScoutCardset)
|
||||
stealing = CharField()
|
||||
st_low = IntegerField()
|
||||
st_high = IntegerField()
|
||||
st_auto = BooleanField()
|
||||
st_jump = FloatField()
|
||||
bunting = CharField(null=True)
|
||||
hit_and_run = CharField(null=True)
|
||||
running = CharField()
|
||||
|
||||
|
||||
class PitcherData(BaseModelScout):
|
||||
player = ForeignKeyField(ScoutPlayer)
|
||||
cardset = ForeignKeyField(ScoutCardset)
|
||||
balk = IntegerField(null=True)
|
||||
wild_pitch = IntegerField(null=True)
|
||||
hold = CharField()
|
||||
starter_rating = IntegerField()
|
||||
relief_rating = IntegerField()
|
||||
closer_rating = IntegerField(null=True)
|
||||
batting = CharField(null=True)
|
||||
|
||||
|
||||
scout_db.create_tables([CardColumns, Position, BatterData, PitcherData])
|
||||
|
||||
|
||||
class CardOutput(BaseModelScout):
|
||||
name = CharField()
|
||||
hand = CharField()
|
||||
positions = CharField()
|
||||
stealing = CharField()
|
||||
bunting = CharField()
|
||||
hitandrun = CharField()
|
||||
running = CharField()
|
||||
|
||||
|
||||
scout_db.close()
|
||||
# scout_db = SqliteDatabase(
|
||||
# 'storage/card_creation.db',
|
||||
# pragmas={
|
||||
# 'journal_mode': 'wal',
|
||||
# 'cache_size': -1 * 64000,
|
||||
# 'synchronous': 0
|
||||
# }
|
||||
# )
|
||||
#
|
||||
#
|
||||
# class BaseModelScout(Model):
|
||||
# class Meta:
|
||||
# database = scout_db
|
||||
#
|
||||
#
|
||||
# class ScoutCardset(BaseModelScout):
|
||||
# set_title = CharField()
|
||||
# set_subtitle = CharField(null=True)
|
||||
#
|
||||
#
|
||||
# class ScoutPlayer(BaseModelScout):
|
||||
# sba_id = IntegerField(primary_key=True)
|
||||
# name = CharField()
|
||||
# fg_id = IntegerField()
|
||||
# br_id = CharField()
|
||||
# offense_col = IntegerField()
|
||||
# hand = CharField(default='R')
|
||||
#
|
||||
#
|
||||
# scout_db.create_tables([ScoutCardset, ScoutPlayer])
|
||||
#
|
||||
#
|
||||
# class BatterRatings(BaseModelScout):
|
||||
# id = CharField(unique=True, primary_key=True)
|
||||
# player = ForeignKeyField(ScoutPlayer)
|
||||
# cardset = ForeignKeyField(ScoutCardset)
|
||||
# vs_hand = FloatField()
|
||||
# is_prep = BooleanField()
|
||||
# homerun = FloatField()
|
||||
# bp_homerun = FloatField()
|
||||
# triple = FloatField()
|
||||
# double_three = FloatField()
|
||||
# double_two = FloatField()
|
||||
# double_pull = FloatField()
|
||||
# single_two = FloatField()
|
||||
# single_one = FloatField()
|
||||
# single_center = FloatField()
|
||||
# bp_single = FloatField()
|
||||
# hbp = FloatField()
|
||||
# walk = FloatField()
|
||||
# strikeout = FloatField()
|
||||
# lineout = FloatField()
|
||||
# popout = FloatField()
|
||||
# flyout_a = FloatField()
|
||||
# flyout_bq = FloatField()
|
||||
# flyout_lf_b = FloatField()
|
||||
# flyout_rf_b = FloatField()
|
||||
# groundout_a = FloatField()
|
||||
# groundout_b = FloatField()
|
||||
# groundout_c = FloatField()
|
||||
# avg = FloatField(null=True)
|
||||
# obp = FloatField(null=True)
|
||||
# slg = FloatField(null=True)
|
||||
#
|
||||
#
|
||||
# class PitcherRatings(BaseModelScout):
|
||||
# id = CharField(unique=True, primary_key=True)
|
||||
# player = ForeignKeyField(ScoutPlayer)
|
||||
# cardset = ForeignKeyField(ScoutCardset)
|
||||
# vs_hand = CharField()
|
||||
# is_prep = BooleanField()
|
||||
# homerun = FloatField()
|
||||
# bp_homerun = FloatField()
|
||||
# triple = FloatField()
|
||||
# double_three = FloatField()
|
||||
# double_two = FloatField()
|
||||
# double_cf = FloatField()
|
||||
# single_two = FloatField()
|
||||
# single_one = FloatField()
|
||||
# single_center = FloatField()
|
||||
# bp_single = FloatField()
|
||||
# hbp = FloatField()
|
||||
# walk = FloatField()
|
||||
# strikeout = FloatField()
|
||||
# fo_slap = FloatField()
|
||||
# fo_center = FloatField()
|
||||
# groundout_a = FloatField()
|
||||
# groundout_b = FloatField()
|
||||
# xcheck_p = FloatField()
|
||||
# xcheck_c = FloatField()
|
||||
# xcheck_1b = FloatField()
|
||||
# xcheck_2b = FloatField()
|
||||
# xcheck_3b = FloatField()
|
||||
# xcheck_ss = FloatField()
|
||||
# xcheck_lf = FloatField()
|
||||
# xcheck_cf = FloatField()
|
||||
# xcheck_rf = FloatField()
|
||||
# avg = FloatField(null=True)
|
||||
# obp = FloatField(null=True)
|
||||
# slg = FloatField(null=True)
|
||||
#
|
||||
#
|
||||
# # scout_db.create_tables([BatterRatings, PitcherRatings])
|
||||
#
|
||||
#
|
||||
# class CardColumns(BaseModelScout):
|
||||
# id = CharField(unique=True, primary_key=True)
|
||||
# player = ForeignKeyField(ScoutPlayer)
|
||||
# hand = CharField()
|
||||
# b_ratings = ForeignKeyField(BatterRatings, null=True)
|
||||
# p_ratings = ForeignKeyField(PitcherRatings, null=True)
|
||||
# one_dice = CharField()
|
||||
# one_results = CharField()
|
||||
# one_splits = CharField()
|
||||
# two_dice = CharField()
|
||||
# two_results = CharField()
|
||||
# two_splits = CharField()
|
||||
# three_dice = CharField()
|
||||
# three_results = CharField()
|
||||
# three_splits = CharField()
|
||||
#
|
||||
#
|
||||
# class Position(BaseModelScout):
|
||||
# player = ForeignKeyField(ScoutPlayer)
|
||||
# cardset = ForeignKeyField(ScoutCardset)
|
||||
# position = CharField()
|
||||
# innings = IntegerField()
|
||||
# range = IntegerField()
|
||||
# error = IntegerField()
|
||||
# arm = CharField(null=True)
|
||||
# pb = IntegerField(null=True)
|
||||
# overthrow = IntegerField(null=True)
|
||||
#
|
||||
#
|
||||
# class BatterData(BaseModelScout):
|
||||
# player = ForeignKeyField(ScoutPlayer)
|
||||
# cardset = ForeignKeyField(ScoutCardset)
|
||||
# stealing = CharField()
|
||||
# st_low = IntegerField()
|
||||
# st_high = IntegerField()
|
||||
# st_auto = BooleanField()
|
||||
# st_jump = FloatField()
|
||||
# bunting = CharField(null=True)
|
||||
# hit_and_run = CharField(null=True)
|
||||
# running = CharField()
|
||||
#
|
||||
#
|
||||
# class PitcherData(BaseModelScout):
|
||||
# player = ForeignKeyField(ScoutPlayer)
|
||||
# cardset = ForeignKeyField(ScoutCardset)
|
||||
# balk = IntegerField(null=True)
|
||||
# wild_pitch = IntegerField(null=True)
|
||||
# hold = CharField()
|
||||
# starter_rating = IntegerField()
|
||||
# relief_rating = IntegerField()
|
||||
# closer_rating = IntegerField(null=True)
|
||||
# batting = CharField(null=True)
|
||||
#
|
||||
#
|
||||
# scout_db.create_tables([CardColumns, Position, BatterData, PitcherData])
|
||||
#
|
||||
#
|
||||
# class CardOutput(BaseModelScout):
|
||||
# name = CharField()
|
||||
# hand = CharField()
|
||||
# positions = CharField()
|
||||
# stealing = CharField()
|
||||
# bunting = CharField()
|
||||
# hitandrun = CharField()
|
||||
# running = CharField()
|
||||
#
|
||||
#
|
||||
# scout_db.close()
|
||||
|
||||
|
||||
@ -11,23 +11,28 @@ migrator = SqliteMigrator(db_engine.db)
|
||||
|
||||
|
||||
# pitcher_injury = IntegerField(null=True)
|
||||
# pos_1 = CharField(default='None')
|
||||
# offense_col = IntegerField(null=True)
|
||||
# pos_2 = CharField(null=True)
|
||||
# last_game = CharField(null=True)
|
||||
# game_type = CharField(null=True)
|
||||
# pack_type = ForeignKeyField(PackType, default=1, to_field='id', field_type=int)
|
||||
mlb_player = ForeignKeyField(db_engine.MlbPlayer, field=db_engine.MlbPlayer.id, null=True)
|
||||
result = ForeignKeyField(db_engine.Result, field=db_engine.Result.id, null=True)
|
||||
# active_theme = ForeignKeyField(PackTheme, to_field='id', field_type=int, null=True)
|
||||
# active_theme = ForeignKeyField(db_engine.PackTheme, field=db_engine.PackTheme.id, null=True) # for careers
|
||||
# game_type = CharField(null=True)
|
||||
# pack_team = ForeignKeyField(db_engine.Team, field=db_engine.Team.id, null=True)
|
||||
pack_cardset = ForeignKeyField(db_engine.Cardset, field=db_engine.Cardset.id, null=True)
|
||||
# pack_cardset = ForeignKeyField(db_engine.Cardset, field=db_engine.Cardset.id, null=True)
|
||||
pull_rate = FloatField(default=0.333)
|
||||
|
||||
migrate(
|
||||
# migrator.add_column('current', 'active_theme_id', active_theme),
|
||||
# migrator.add_column('pack', 'pack_team_id', pack_team),
|
||||
migrator.add_column('pack', 'pack_cardset_id', pack_cardset),
|
||||
# migrator.add_column('player', 'mlbplayer_id', mlb_player),
|
||||
migrator.add_column('battingstat', 'result_id', result),
|
||||
migrator.add_column('pitchingstat', 'result_id', result),
|
||||
# migrator.add_column('battingcardratings', 'pull_rate', pull_rate),
|
||||
# migrator.rename_column('cardset', 'available', 'for_purchase')
|
||||
# migrator.add_column('player', 'pos_1', pos_1),
|
||||
# migrator.add_column('player', 'offense_col', offense_col),
|
||||
# migrator.add_column('comment_tbl', 'comment', comment_field),
|
||||
# migrator.rename_column('story', 'pub_date', 'publish_date'),
|
||||
# migrator.drop_column('story', 'some_old_field'),
|
||||
|
||||
@ -4,3 +4,8 @@ peewee
|
||||
python-multipart
|
||||
pandas
|
||||
pygsheets
|
||||
pybaseball
|
||||
python-multipart
|
||||
requests
|
||||
html2image
|
||||
jinja2
|
||||
|
||||
225
venv/share/man/man1/ttx.1
Normal file
225
venv/share/man/man1/ttx.1
Normal file
@ -0,0 +1,225 @@
|
||||
.Dd May 18, 2004
|
||||
.\" ttx is not specific to any OS, but contrary to what groff_mdoc(7)
|
||||
.\" seems to imply, entirely omitting the .Os macro causes 'BSD' to
|
||||
.\" be used, so I give a zero-width space as its argument.
|
||||
.Os \&
|
||||
.\" The "FontTools Manual" argument apparently has no effect in
|
||||
.\" groff 1.18.1. I think it is a bug in the -mdoc groff package.
|
||||
.Dt TTX 1 "FontTools Manual"
|
||||
.Sh NAME
|
||||
.Nm ttx
|
||||
.Nd tool for manipulating TrueType and OpenType fonts
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Bk
|
||||
.Op Ar option ...
|
||||
.Ek
|
||||
.Bk
|
||||
.Ar file ...
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a tool for manipulating TrueType and OpenType fonts. It can convert
|
||||
TrueType and OpenType fonts to and from an
|
||||
.Tn XML Ns -based format called
|
||||
.Tn TTX .
|
||||
.Tn TTX
|
||||
files have a
|
||||
.Ql .ttx
|
||||
extension.
|
||||
.Pp
|
||||
For each
|
||||
.Ar file
|
||||
argument it is given,
|
||||
.Nm
|
||||
detects whether it is a
|
||||
.Ql .ttf ,
|
||||
.Ql .otf
|
||||
or
|
||||
.Ql .ttx
|
||||
file and acts accordingly: if it is a
|
||||
.Ql .ttf
|
||||
or
|
||||
.Ql .otf
|
||||
file, it generates a
|
||||
.Ql .ttx
|
||||
file; if it is a
|
||||
.Ql .ttx
|
||||
file, it generates a
|
||||
.Ql .ttf
|
||||
or
|
||||
.Ql .otf
|
||||
file.
|
||||
.Pp
|
||||
By default, every output file is created in the same directory as the
|
||||
corresponding input file and with the same name except for the
|
||||
extension, which is substituted appropriately.
|
||||
.Nm
|
||||
never overwrites existing files; if necessary, it appends a suffix to
|
||||
the output file name before the extension, as in
|
||||
.Pa Arial#1.ttf .
|
||||
.Ss "General options"
|
||||
.Bl -tag -width ".Fl t Ar table"
|
||||
.It Fl h
|
||||
Display usage information.
|
||||
.It Fl d Ar dir
|
||||
Write the output files to directory
|
||||
.Ar dir
|
||||
instead of writing every output file to the same directory as the
|
||||
corresponding input file.
|
||||
.It Fl o Ar file
|
||||
Write the output to
|
||||
.Ar file
|
||||
instead of writing it to the same directory as the
|
||||
corresponding input file.
|
||||
.It Fl v
|
||||
Be verbose. Write more messages to the standard output describing what
|
||||
is being done.
|
||||
.It Fl a
|
||||
Allow virtual glyphs ID's on compile or decompile.
|
||||
.El
|
||||
.Ss "Dump options"
|
||||
The following options control the process of dumping font files
|
||||
(TrueType or OpenType) to
|
||||
.Tn TTX
|
||||
files.
|
||||
.Bl -tag -width ".Fl t Ar table"
|
||||
.It Fl l
|
||||
List table information. Instead of dumping the font to a
|
||||
.Tn TTX
|
||||
file, display minimal information about each table.
|
||||
.It Fl t Ar table
|
||||
Dump table
|
||||
.Ar table .
|
||||
This option may be given multiple times to dump several tables at
|
||||
once. When not specified, all tables are dumped.
|
||||
.It Fl x Ar table
|
||||
Exclude table
|
||||
.Ar table
|
||||
from the list of tables to dump. This option may be given multiple
|
||||
times to exclude several tables from the dump. The
|
||||
.Fl t
|
||||
and
|
||||
.Fl x
|
||||
options are mutually exclusive.
|
||||
.It Fl s
|
||||
Split tables. Dump each table to a separate
|
||||
.Tn TTX
|
||||
file and write (under the name that would have been used for the output
|
||||
file if the
|
||||
.Fl s
|
||||
option had not been given) one small
|
||||
.Tn TTX
|
||||
file containing references to the individual table dump files. This
|
||||
file can be used as input to
|
||||
.Nm
|
||||
as long as the referenced files can be found in the same directory.
|
||||
.It Fl i
|
||||
.\" XXX: I suppose OpenType programs (exist and) are also affected.
|
||||
Don't disassemble TrueType instructions. When this option is specified,
|
||||
all TrueType programs (glyph programs, the font program and the
|
||||
pre-program) are written to the
|
||||
.Tn TTX
|
||||
file as hexadecimal data instead of
|
||||
assembly. This saves some time and results in smaller
|
||||
.Tn TTX
|
||||
files.
|
||||
.It Fl y Ar n
|
||||
When decompiling a TrueType Collection (TTC) file,
|
||||
decompile font number
|
||||
.Ar n ,
|
||||
starting from 0.
|
||||
.El
|
||||
.Ss "Compilation options"
|
||||
The following options control the process of compiling
|
||||
.Tn TTX
|
||||
files into font files (TrueType or OpenType):
|
||||
.Bl -tag -width ".Fl t Ar table"
|
||||
.It Fl m Ar fontfile
|
||||
Merge the input
|
||||
.Tn TTX
|
||||
file
|
||||
.Ar file
|
||||
with
|
||||
.Ar fontfile .
|
||||
No more than one
|
||||
.Ar file
|
||||
argument can be specified when this option is used.
|
||||
.It Fl b
|
||||
Don't recalculate glyph bounding boxes. Use the values in the
|
||||
.Tn TTX
|
||||
file as is.
|
||||
.El
|
||||
.Sh "THE TTX FILE FORMAT"
|
||||
You can find some information about the
|
||||
.Tn TTX
|
||||
file format in
|
||||
.Pa documentation.html .
|
||||
In particular, you will find in that file the list of tables understood by
|
||||
.Nm
|
||||
and the relations between TrueType GlyphIDs and the glyph names used in
|
||||
.Tn TTX
|
||||
files.
|
||||
.Sh EXAMPLES
|
||||
In the following examples, all files are read from and written to the
|
||||
current directory. Additionally, the name given for the output file
|
||||
assumes in every case that it did not exist before
|
||||
.Nm
|
||||
was invoked.
|
||||
.Pp
|
||||
Dump the TrueType font contained in
|
||||
.Pa FreeSans.ttf
|
||||
to
|
||||
.Pa FreeSans.ttx :
|
||||
.Pp
|
||||
.Dl ttx FreeSans.ttf
|
||||
.Pp
|
||||
Compile
|
||||
.Pa MyFont.ttx
|
||||
into a TrueType or OpenType font file:
|
||||
.Pp
|
||||
.Dl ttx MyFont.ttx
|
||||
.Pp
|
||||
List the tables in
|
||||
.Pa FreeSans.ttf
|
||||
along with some information:
|
||||
.Pp
|
||||
.Dl ttx -l FreeSans.ttf
|
||||
.Pp
|
||||
Dump the
|
||||
.Sq cmap
|
||||
table from
|
||||
.Pa FreeSans.ttf
|
||||
to
|
||||
.Pa FreeSans.ttx :
|
||||
.Pp
|
||||
.Dl ttx -t cmap FreeSans.ttf
|
||||
.Sh NOTES
|
||||
On MS\-Windows and MacOS,
|
||||
.Nm
|
||||
is available as a graphical application to which files can be dropped.
|
||||
.Sh SEE ALSO
|
||||
.Pa documentation.html
|
||||
.Pp
|
||||
.Xr fontforge 1 ,
|
||||
.Xr ftinfo 1 ,
|
||||
.Xr gfontview 1 ,
|
||||
.Xr xmbdfed 1 ,
|
||||
.Xr Font::TTF 3pm
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
was written by
|
||||
.An -nosplit
|
||||
.An "Just van Rossum" Aq just@letterror.com .
|
||||
.Pp
|
||||
This manual page was written by
|
||||
.An "Florent Rougon" Aq f.rougon@free.fr
|
||||
for the Debian GNU/Linux system based on the existing FontTools
|
||||
documentation. It may be freely used, modified and distributed without
|
||||
restrictions.
|
||||
.\" For Emacs:
|
||||
.\" Local Variables:
|
||||
.\" fill-column: 72
|
||||
.\" sentence-end: "[.?!][]\"')}]*\\($\\| $\\| \\| \\)[ \n]*"
|
||||
.\" sentence-end-double-space: t
|
||||
.\" End:
|
||||
Loading…
Reference in New Issue
Block a user