commit
6faec225d3
@ -1,8 +1,8 @@
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:latest
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
COPY ./app /app/app
|
||||
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
1940
app/db_engine.py
Normal file
1940
app/db_engine.py
Normal file
File diff suppressed because it is too large
Load Diff
34
app/dependencies.py
Normal file
34
app/dependencies.py
Normal file
@ -0,0 +1,34 @@
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
|
||||
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']
|
||||
)
|
||||
|
||||
# date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}'
|
||||
# log_level = logging.INFO if os.environ.get('LOG_LEVEL') == 'INFO' else 'WARN'
|
||||
# logging.basicConfig(
|
||||
# filename=f'logs/database/{date}.log',
|
||||
# format='%(asctime)s - sba-database - %(levelname)s - %(message)s',
|
||||
# level=log_level
|
||||
# )
|
||||
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
||||
|
||||
|
||||
def valid_token(token):
|
||||
return token == os.environ.get('API_TOKEN')
|
||||
56
app/main.py
Normal file
56
app/main.py
Normal file
@ -0,0 +1,56 @@
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
|
||||
from fastapi import Depends, FastAPI, Request
|
||||
# from fastapi.openapi.docs import get_swagger_ui_html
|
||||
# from fastapi.openapi.utils import get_openapi
|
||||
|
||||
from .routers_v3 import current, players, results, schedules, standings, teams, transactions, battingstats, \
|
||||
pitchingstats, fieldingstats, draftpicks, draftlist, managers, awards, draftdata, keepers
|
||||
|
||||
date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}'
|
||||
log_level = logging.INFO if os.environ.get('LOG_LEVEL') == 'INFO' else 'WARN'
|
||||
logging.basicConfig(
|
||||
filename=f'logs/database/{date}.log',
|
||||
format='%(asctime)s - sba-database - %(levelname)s - %(message)s',
|
||||
level=log_level
|
||||
)
|
||||
|
||||
app = FastAPI(
|
||||
responses={404: {'description': 'Not found'}}
|
||||
)
|
||||
|
||||
|
||||
app.include_router(current.router)
|
||||
app.include_router(players.router)
|
||||
app.include_router(results.router)
|
||||
app.include_router(schedules.router)
|
||||
app.include_router(teams.router)
|
||||
app.include_router(transactions.router)
|
||||
app.include_router(standings.router)
|
||||
app.include_router(battingstats.router)
|
||||
app.include_router(pitchingstats.router)
|
||||
app.include_router(fieldingstats.router)
|
||||
app.include_router(draftpicks.router)
|
||||
app.include_router(draftlist.router)
|
||||
app.include_router(managers.router)
|
||||
app.include_router(awards.router)
|
||||
app.include_router(draftdata.router)
|
||||
app.include_router(keepers.router)
|
||||
|
||||
|
||||
# @app.get("/docs", include_in_schema=False)
|
||||
# async def get_docs(req: Request):
|
||||
# print(req.scope)
|
||||
# return get_swagger_ui_html(openapi_url=req.scope.get('root_path')+'/openapi.json', title='Swagger')
|
||||
#
|
||||
#
|
||||
# @app.get("/openapi.json", include_in_schema=False)
|
||||
# async def openapi():
|
||||
# return get_openapi(title='SBa Dev API', version=f'0.1.1', routes=app.routes)
|
||||
|
||||
|
||||
# @app.get("/api")
|
||||
# async def root():
|
||||
# return {"message": "Hello Bigger Applications!"}
|
||||
0
app/routers_v3/__init__.py
Normal file
0
app/routers_v3/__init__.py
Normal file
162
app/routers_v3/awards.py
Normal file
162
app/routers_v3/awards.py
Normal file
@ -0,0 +1,162 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Award, Team, Player, Manager, model_to_dict, chunked, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/awards',
|
||||
tags=['awards']
|
||||
)
|
||||
|
||||
|
||||
class AwardModel(pydantic.BaseModel):
|
||||
name: str
|
||||
season: int
|
||||
timing: Optional[str] = "In-Season"
|
||||
manager1_id: Optional[int] = None
|
||||
manager2_id: Optional[int] = None
|
||||
player_id: Optional[int] = None
|
||||
team_id: Optional[int] = None
|
||||
image: Optional[str] = None
|
||||
|
||||
|
||||
class AwardList(pydantic.BaseModel):
|
||||
count: int
|
||||
awards: List[AwardModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_awards(
|
||||
name: list = Query(default=None), season: Optional[int] = None, timing: Optional[str] = None,
|
||||
manager_id: list = Query(default=None), player_id: list = Query(default=None),
|
||||
team_id: list = Query(default=None), short_output: Optional[bool] = False):
|
||||
all_awards = Award.select()
|
||||
|
||||
if name is not None:
|
||||
name_list = [x.lower() for x in name]
|
||||
all_awards = all_awards.where(fn.Lower(Award.name) == name_list)
|
||||
if season is not None:
|
||||
all_awards = all_awards.where(Award.season == season)
|
||||
if timing is not None:
|
||||
all_awards = all_awards.where(fn.Lower(Award.timing) == timing.lower())
|
||||
if manager_id is not None:
|
||||
managers = Manager.select().where(Manager.id << manager_id)
|
||||
all_awards = all_awards.where(
|
||||
(Award.manager1 << managers) | (Award.manager2 << managers)
|
||||
)
|
||||
if player_id is not None:
|
||||
all_awards = all_awards.where(Award.player_id << player_id)
|
||||
if team_id is not None:
|
||||
all_awards = all_awards.where(Award.team_id << team_id)
|
||||
|
||||
return_awards = {
|
||||
'count': all_awards.count(),
|
||||
'awards': [model_to_dict(x, recurse=not short_output) for x in all_awards]
|
||||
}
|
||||
db.close()
|
||||
return return_awards
|
||||
|
||||
|
||||
@router.get('/{award_id}')
|
||||
async def get_one_award(award_id: int, short_output: Optional[bool] = False):
|
||||
this_award = Award.get_or_none(Award.id == award_id)
|
||||
if this_award is None:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Award ID {award_id} not found')
|
||||
|
||||
db.close()
|
||||
return model_to_dict(this_award, recurse=not short_output)
|
||||
|
||||
|
||||
@router.patch('/{award_id}')
|
||||
async def patch_award(
|
||||
award_id: int, name: Optional[str] = None, season: Optional[int] = None, timing: Optional[str] = None,
|
||||
image: Optional[str] = None, manager1_id: Optional[int] = None, manager2_id: Optional[int] = None,
|
||||
player_id: Optional[int] = None, team_id: Optional[int] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_player - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_award = Award.get_or_none(Award.id == award_id)
|
||||
if this_award is None:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Award ID {award_id} not found')
|
||||
|
||||
if name is not None:
|
||||
this_award.name = name
|
||||
if season is not None:
|
||||
this_award.season = season
|
||||
if timing is not None:
|
||||
this_award.timing = timing
|
||||
if image is not None:
|
||||
this_award.image = image
|
||||
if manager1_id is not None:
|
||||
this_award.manager1_id = manager1_id
|
||||
if manager2_id is not None:
|
||||
this_award.manager2_id = manager2_id
|
||||
if player_id is not None:
|
||||
this_award.player_id = player_id
|
||||
if team_id is not None:
|
||||
this_award.team_id = team_id
|
||||
|
||||
if this_award.save() == 1:
|
||||
r_award = model_to_dict(this_award)
|
||||
db.close()
|
||||
return r_award
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to patch award {award_id}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_award(award_list: AwardList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_player - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_awards = []
|
||||
for x in award_list.awards:
|
||||
if x.manager1_id is not None and Manager.get_or_none(Manager.id == x.manager1_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Manager ID {x.manager1_id} not found')
|
||||
if x.manager2_id is not None and Manager.get_or_none(Manager.id == x.manager2_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Manager ID {x.manager2_id} not found')
|
||||
if x.player_id is not None and Player.get_or_none(Player.id == x.player_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Player ID {x.player_id} not found')
|
||||
if x.team_id is not None and Team.get_or_none(Team.id == x.team_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.team_id} not found')
|
||||
|
||||
new_awards.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_awards, 15):
|
||||
Award.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_awards)} awards'
|
||||
|
||||
|
||||
@router.delete('/{award_id}')
|
||||
async def delete_award(award_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_player - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_award = Award.get_or_none(Award.id == award_id)
|
||||
if this_award is None:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Award ID {award_id} not found')
|
||||
|
||||
count = this_award.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
return f'Award {award_id} has been deleted'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Award {award_id} could not be deleted')
|
||||
|
||||
|
||||
|
||||
|
||||
306
app/routers_v3/battingstats.py
Normal file
306
app/routers_v3/battingstats.py
Normal file
@ -0,0 +1,306 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional, Literal
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, BattingStat, Team, Player, Current, model_to_dict, chunked, fn, per_season_weeks
|
||||
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/v3/battingstats',
|
||||
tags=['battingstats']
|
||||
)
|
||||
|
||||
|
||||
class BatStatModel(pydantic.BaseModel):
|
||||
player_id: int
|
||||
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
|
||||
xba: Optional[int] = 0
|
||||
xbt: 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
|
||||
roba: Optional[int] = 0
|
||||
robs: Optional[int] = 0
|
||||
raa: Optional[int] = 0
|
||||
rto: Optional[int] = 0
|
||||
week: int
|
||||
game: int
|
||||
season: int
|
||||
|
||||
|
||||
class BatStatList(pydantic.BaseModel):
|
||||
count: int
|
||||
stats: List[BatStatModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_batstats(
|
||||
season: int, s_type: Optional[str] = 'regular', team_abbrev: list = Query(default=None),
|
||||
player_name: list = Query(default=None), player_id: list = Query(default=None),
|
||||
week_start: Optional[int] = None, week_end: Optional[int] = None, game_num: list = Query(default=None),
|
||||
position: list = Query(default=None), limit: Optional[int] = None, sort: Optional[str] = None,
|
||||
short_output: Optional[bool] = True):
|
||||
if 'post' in s_type.lower():
|
||||
all_stats = BattingStat.post_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
elif s_type.lower() in ['combined', 'total', 'all']:
|
||||
all_stats = BattingStat.combined_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
else:
|
||||
all_stats = BattingStat.regular_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
|
||||
if position is not None:
|
||||
all_stats = all_stats.where(BattingStat.pos << [x.upper() for x in position])
|
||||
if team_abbrev is not None:
|
||||
t_query = Team.select().where(Team.abbrev << [x.upper() for x in team_abbrev])
|
||||
all_stats = all_stats.where(BattingStat.team << t_query)
|
||||
if player_name is not None or player_id is not None:
|
||||
if player_id:
|
||||
all_stats = all_stats.where(BattingStat.player_id << player_id)
|
||||
else:
|
||||
p_query = Player.select_season(season).where(fn.Lower(Player.name) << [x.lower() for x in player_name])
|
||||
all_stats = all_stats.where(BattingStat.player << p_query)
|
||||
if game_num:
|
||||
all_stats = all_stats.where(BattingStat.game == game_num)
|
||||
|
||||
start = 1
|
||||
end = Current.get(Current.season == season).week
|
||||
if week_start is not None:
|
||||
start = week_start
|
||||
if week_end is not None:
|
||||
end = min(week_end, end)
|
||||
if start > end:
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f'Start week {start} is after end week {end} - cannot pull stats'
|
||||
)
|
||||
all_stats = all_stats.where(
|
||||
(BattingStat.week >= start) & (BattingStat.week <= end)
|
||||
)
|
||||
|
||||
if limit:
|
||||
all_stats = all_stats.limit(limit)
|
||||
if sort:
|
||||
if sort == 'newest':
|
||||
all_stats = all_stats.order_by(-BattingStat.week, -BattingStat.game)
|
||||
|
||||
return_stats = {
|
||||
'count': all_stats.count(),
|
||||
'stats': [model_to_dict(x, recurse=not short_output) for x in all_stats],
|
||||
# 'stats': [{'id': x.id} for x in all_stats]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return return_stats
|
||||
|
||||
|
||||
@router.get('/totals')
|
||||
async def get_totalstats(
|
||||
season: int, s_type: Literal['regular', 'post', 'total', None] = None, team_abbrev: list = Query(default=None),
|
||||
team_id: list = Query(default=None), player_name: list = Query(default=None),
|
||||
week_start: Optional[int] = None, week_end: Optional[int] = None, game_num: list = Query(default=None),
|
||||
position: list = Query(default=None), sort: Optional[str] = None, player_id: list = Query(default=None),
|
||||
group_by: Literal['team', 'player', 'playerteam'] = 'player', short_output: Optional[bool] = False,
|
||||
min_pa: Optional[int] = 1, week: list = Query(default=None)):
|
||||
if sum(1 for x in [s_type, (week_start or week_end), week] if x is not None) > 1:
|
||||
raise HTTPException(status_code=400, detail=f'Only one of s_type, week_start/week_end, or week may be used.')
|
||||
|
||||
all_stats = (
|
||||
BattingStat
|
||||
.select(BattingStat.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.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.so).alias('sum_so'),
|
||||
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'),
|
||||
fn.SUM(BattingStat.xba).alias('sum_xba'), fn.SUM(BattingStat.xbt).alias('sum_xbt'),
|
||||
fn.SUM(BattingStat.xch).alias('sum_xch'), fn.SUM(BattingStat.xhit).alias('sum_xhit'),
|
||||
fn.SUM(BattingStat.error).alias('sum_error'), fn.SUM(BattingStat.pb).alias('sum_pb'),
|
||||
fn.SUM(BattingStat.sbc).alias('sum_sbc'), fn.SUM(BattingStat.csc).alias('sum_csc'),
|
||||
fn.SUM(BattingStat.roba).alias('sum_roba'), fn.SUM(BattingStat.robs).alias('sum_robs'),
|
||||
fn.SUM(BattingStat.raa).alias('sum_raa'), fn.SUM(BattingStat.rto).alias('sum_rto'),
|
||||
BattingStat.team)
|
||||
.where(BattingStat.season == season)
|
||||
.having(fn.SUM(BattingStat.pa) >= min_pa)
|
||||
)
|
||||
|
||||
if True in [s_type is not None, week_start is not None, week_end is not None]:
|
||||
weeks = {}
|
||||
if s_type is not None:
|
||||
weeks = per_season_weeks(season, s_type)
|
||||
elif week_start is not None or week_end is not None:
|
||||
if week_start is None or week_end is None:
|
||||
raise HTTPException(
|
||||
status_code=400, detail='Both week_start and week_end must be included if either is used.'
|
||||
)
|
||||
weeks['start'] = week_start
|
||||
if week_end < weeks['start']:
|
||||
raise HTTPException(status_code=400, detail='week_end must be greater than or equal to week_start')
|
||||
else:
|
||||
weeks['end'] = week_end
|
||||
|
||||
all_stats = all_stats.where(
|
||||
(BattingStat.week >= weeks['start']) & (BattingStat.week <= weeks['end'])
|
||||
)
|
||||
elif week is not None:
|
||||
all_stats = all_stats.where(BattingStat.week << week)
|
||||
|
||||
if game_num is not None:
|
||||
all_stats = all_stats.where(BattingStat.game << game_num)
|
||||
if position is not None:
|
||||
p_list = [x.upper() for x in position]
|
||||
all_players = Player.select().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)
|
||||
)
|
||||
all_stats = all_stats.where(BattingStat.player << all_players)
|
||||
if sort is not None:
|
||||
if sort == 'player':
|
||||
all_stats = all_stats.order_by(BattingStat.player)
|
||||
elif sort == 'team':
|
||||
all_stats = all_stats.order_by(BattingStat.team)
|
||||
if group_by is not None:
|
||||
if group_by == 'team':
|
||||
all_stats = all_stats.group_by(BattingStat.team)
|
||||
elif group_by == 'player':
|
||||
all_stats = all_stats.group_by(BattingStat.player)
|
||||
elif group_by == 'playerteam':
|
||||
all_stats = all_stats.group_by(BattingStat.team, BattingStat.player)
|
||||
|
||||
# if team_abbrev is None and team_id is None and player_name is None and player_id is None:
|
||||
# raise HTTPException(
|
||||
# status_code=400,
|
||||
# detail=f'Must include team_id/team_abbrev and/or player_name/player_id'
|
||||
# )
|
||||
|
||||
if team_id is not None:
|
||||
all_teams = Team.select().where(Team.id << team_id)
|
||||
all_stats = all_stats.where(BattingStat.team << all_teams)
|
||||
elif team_abbrev is not None:
|
||||
all_teams = Team.select().where(fn.Lower(Team.abbrev) << [x.lower() for x in team_abbrev])
|
||||
all_stats = all_stats.where(BattingStat.team << all_teams)
|
||||
|
||||
if player_name is not None:
|
||||
all_players = Player.select().where(fn.Lower(Player.name) << [x.lower() for x in player_name])
|
||||
all_stats = all_stats.where(BattingStat.player << all_players)
|
||||
elif player_id is not None:
|
||||
all_players = Player.select().where(Player.id << player_id)
|
||||
all_stats = all_stats.where(BattingStat.player << all_players)
|
||||
|
||||
return_stats = {
|
||||
'count': all_stats.count(),
|
||||
'stats': [{
|
||||
'player': x.player_id if short_output else model_to_dict(x.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
|
||||
} for x in all_stats]
|
||||
}
|
||||
db.close()
|
||||
return return_stats
|
||||
|
||||
|
||||
# @router.get('/career/{player_name}')
|
||||
# async def get_careerstats(
|
||||
# s_type: Literal['regular', 'post', 'total'] = 'regular', player_name: list = Query(default=None)):
|
||||
# pass # Keep Career Stats table and recalculate after posting stats
|
||||
|
||||
|
||||
@router.patch('/{stat_id}')
|
||||
async def patch_batstats(stat_id: int, new_stats: BatStatModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_batstats - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
if BattingStat.get_or_none(BattingStat.id == stat_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Stat ID {stat_id} not found')
|
||||
|
||||
BattingStat.update(**new_stats.dict()).where(BattingStat.id == stat_id).execute()
|
||||
r_stat = model_to_dict(BattingStat.get_by_id(stat_id))
|
||||
db.close()
|
||||
return r_stat
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_batstats(s_list: BatStatList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_batstats - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
all_stats = []
|
||||
|
||||
for x in s_list.stats:
|
||||
team = Team.get_or_none(Team.id == x.team_id)
|
||||
this_player = Player.get_or_none(Player.id == x.player_id)
|
||||
if team is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.team_id} not found')
|
||||
if this_player is None:
|
||||
raise HTTPException(status_code=404, detail=f'Player ID {x.player_id} not found')
|
||||
|
||||
all_stats.append(BattingStat(**x.dict()))
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(all_stats, 15):
|
||||
BattingStat.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
# Update career stats
|
||||
|
||||
db.close()
|
||||
return f'Added {len(all_stats)} batting lines'
|
||||
118
app/routers_v3/current.py
Normal file
118
app/routers_v3/current.py
Normal file
@ -0,0 +1,118 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from typing import Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Current, model_to_dict
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/current',
|
||||
tags=['current']
|
||||
)
|
||||
|
||||
|
||||
class CurrentModel(pydantic.BaseModel):
|
||||
week: Optional[int] = 0
|
||||
freeze: Optional[bool] = False
|
||||
season: int
|
||||
transcount: Optional[int] = 0
|
||||
bstatcount: Optional[int] = 0
|
||||
pstatcount: Optional[int] = 0
|
||||
bet_week: Optional[int] = 0
|
||||
trade_deadline: int
|
||||
pick_trade_start: int = 69
|
||||
pick_trade_end: int = 420
|
||||
playoffs_begin: int
|
||||
injury_count: Optional[int] = 0
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_current(season: Optional[int] = None):
|
||||
if season is not None:
|
||||
current = Current.get_or_none(season=season)
|
||||
else:
|
||||
current = Current.latest()
|
||||
|
||||
if current is not None:
|
||||
r_curr = model_to_dict(current)
|
||||
db.close()
|
||||
return r_curr
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@router.patch('/{current_id}')
|
||||
async def patch_current(
|
||||
current_id: int, season: Optional[int] = None, week: Optional[int] = None, freeze: Optional[bool] = None,
|
||||
transcount: Optional[int] = None, bstatcount: Optional[int] = None, pstatcount: Optional[int] = None,
|
||||
bet_week: Optional[int] = None, trade_deadline: Optional[int] = None, pick_trade_start: Optional[int] = None,
|
||||
pick_trade_end: Optional[int] = None, injury_count: Optional[int] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_current - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
try:
|
||||
current = Current.get_by_id(current_id)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=404, detail=f'Current id {current_id} not found')
|
||||
|
||||
if week is not None:
|
||||
current.week = week
|
||||
if season is not None:
|
||||
current.season = season
|
||||
if freeze is not None:
|
||||
current.freeze = freeze
|
||||
if transcount is not None:
|
||||
current.transcount = transcount
|
||||
if bstatcount is not None:
|
||||
current.bstatcount = bstatcount
|
||||
if pstatcount is not None:
|
||||
current.pstatcount = pstatcount
|
||||
if bet_week is not None:
|
||||
current.bet_week = bet_week
|
||||
if trade_deadline is not None:
|
||||
current.trade_deadline = trade_deadline
|
||||
if pick_trade_start is not None:
|
||||
current.pick_trade_start = pick_trade_start
|
||||
if pick_trade_end is not None:
|
||||
current.pick_trade_end = pick_trade_end
|
||||
if injury_count is not None:
|
||||
current.injury_count = injury_count
|
||||
|
||||
if current.save():
|
||||
r_curr = model_to_dict(current)
|
||||
db.close()
|
||||
return r_curr
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to patch current {current_id}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_current(new_current: CurrentModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_current - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_current = Current(**new_current.dict())
|
||||
|
||||
if this_current.save():
|
||||
r_curr = model_to_dict(this_current)
|
||||
db.close()
|
||||
return r_curr
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to post season {new_current.season} current')
|
||||
|
||||
|
||||
@router.delete('/{current_id}')
|
||||
async def delete_current(current_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_current - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
if Current.delete_by_id(current_id) == 1:
|
||||
return f'Deleted current ID {current_id}'
|
||||
|
||||
raise HTTPException(status_code=500, detail=f'Unable to delete current {current_id}')
|
||||
73
app/routers_v3/draftdata.py
Normal file
73
app/routers_v3/draftdata.py
Normal file
@ -0,0 +1,73 @@
|
||||
import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from typing import Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, DraftData, model_to_dict
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/draftdata',
|
||||
tags=['draftdata']
|
||||
)
|
||||
|
||||
|
||||
class DraftDataModel(pydantic.BaseModel):
|
||||
currentpick: int
|
||||
timer: bool
|
||||
pick_deadline: datetime.datetime
|
||||
result_channel_id = int
|
||||
ping_channel_id = int
|
||||
pick_minutes = int
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_draftdata():
|
||||
draft_data = DraftData.get_or_none()
|
||||
|
||||
if draft_data is not None:
|
||||
r_data = model_to_dict(draft_data)
|
||||
db.close()
|
||||
return r_data
|
||||
|
||||
raise HTTPException(status_code=404, detail=f'No draft data found')
|
||||
|
||||
|
||||
@router.patch('/{data_id}')
|
||||
async def patch_draftdata(
|
||||
data_id: int, currentpick: Optional[int] = None, timer: Optional[bool] = None,
|
||||
pick_deadline: Optional[datetime.datetime] = None, result_channel: Optional[int] = None,
|
||||
ping_channel: Optional[int] = None, pick_minutes: Optional[int] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_draftdata - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
draft_data = DraftData.get_or_none(DraftData.id == data_id)
|
||||
if draft_data is None:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No draft data found')
|
||||
|
||||
if currentpick is not None:
|
||||
draft_data.currentpick = currentpick
|
||||
if timer is not None:
|
||||
draft_data.timer = timer
|
||||
if pick_deadline is not None:
|
||||
draft_data.pick_deadline = pick_deadline
|
||||
if result_channel is not None:
|
||||
draft_data.result_channel = result_channel
|
||||
if ping_channel is not None:
|
||||
draft_data.ping_channel = ping_channel
|
||||
if pick_minutes is not None:
|
||||
draft_data.pick_minutes = pick_minutes
|
||||
|
||||
saved = draft_data.save()
|
||||
r_data = model_to_dict(draft_data)
|
||||
db.close()
|
||||
|
||||
if saved == 1:
|
||||
return r_data
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail='Updating draft data failed')
|
||||
|
||||
91
app/routers_v3/draftlist.py
Normal file
91
app/routers_v3/draftlist.py
Normal file
@ -0,0 +1,91 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, DraftList, Team, model_to_dict, chunked
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/draftlist',
|
||||
tags=['draftlist']
|
||||
)
|
||||
|
||||
|
||||
class DraftListModel(pydantic.BaseModel):
|
||||
season: int
|
||||
team_id: int
|
||||
rank: int
|
||||
player_id: int
|
||||
|
||||
|
||||
class DraftListList(pydantic.BaseModel):
|
||||
count: int
|
||||
draft_list: List[DraftListModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_draftlist(
|
||||
season: Optional[int], team_id: list = Query(default=None), token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'get_draftlist - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
all_list = DraftList.select()
|
||||
|
||||
if season is not None:
|
||||
all_list = all_list.where(DraftList.season == season)
|
||||
if team_id is not None:
|
||||
all_list = all_list.where(DraftList.team_id << team_id)
|
||||
|
||||
r_list = {
|
||||
'count': all_list.count(),
|
||||
'picks': [model_to_dict(x) for x in all_list]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return r_list
|
||||
|
||||
|
||||
@router.get('/team/{team_id}')
|
||||
async def get_team_draftlist(team_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_draftlist - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_team = Team.get_or_none(Team.id == team_id)
|
||||
if this_team is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {team_id} not found')
|
||||
|
||||
this_list = DraftList.select().where(DraftList.team == this_team)
|
||||
r_list = {
|
||||
'count': this_list.count(),
|
||||
'picks': [model_to_dict(x) for x in this_list]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return r_list
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_draftlist(draft_list: DraftListList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_draftlist - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_list = []
|
||||
this_team = Team.get_or_none(Team.id == draft_list.draft_list[0].team_id)
|
||||
if this_team is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {draft_list.draft_list[0].team_id} not found')
|
||||
|
||||
DraftList.delete().where(DraftList.team == this_team).execute()
|
||||
|
||||
for x in draft_list.draft_list:
|
||||
new_list.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_list, 15):
|
||||
DraftList.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
db.close()
|
||||
return f'Inserted {len(new_list)} list values'
|
||||
169
app/routers_v3/draftpicks.py
Normal file
169
app/routers_v3/draftpicks.py
Normal file
@ -0,0 +1,169 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, DraftPick, Team, model_to_dict, chunked
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/draftpicks',
|
||||
tags=['draftpicks']
|
||||
)
|
||||
|
||||
|
||||
class DraftPickModel(pydantic.BaseModel):
|
||||
overall: Optional[int] = None
|
||||
round: int
|
||||
origowner_id: int
|
||||
owner_id: Optional[int] = None
|
||||
season: int
|
||||
player_id: Optional[int] = None
|
||||
|
||||
|
||||
class DraftPickList(pydantic.BaseModel):
|
||||
picks: List[DraftPickModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_picks(
|
||||
season: int, owner_team_abbrev: list = Query(default=None), orig_team_abbrev: list = Query(default=None),
|
||||
owner_team_id: list = Query(default=None), orig_team_id: list = Query(default=None),
|
||||
pick_round_start: Optional[int] = None, pick_round_end: Optional[int] = None, traded: Optional[bool] = None,
|
||||
overall: Optional[int] = None, overall_start: Optional[int] = None, overall_end: Optional[int] = None,
|
||||
short_output: Optional[bool] = False, sort: Optional[str] = None, limit: Optional[int] = None,
|
||||
player_id: list = Query(default=None), player_taken: Optional[bool] = None):
|
||||
all_picks = DraftPick.select().where(DraftPick.season == season)
|
||||
|
||||
if owner_team_abbrev is not None:
|
||||
team_list = []
|
||||
for x in owner_team_abbrev:
|
||||
team_list.append(Team.get_season(x, season))
|
||||
all_picks = all_picks.where(
|
||||
(DraftPick.owner << team_list) | (DraftPick.owner << team_list)
|
||||
)
|
||||
|
||||
if orig_team_abbrev is not None:
|
||||
team_list = []
|
||||
for x in orig_team_abbrev:
|
||||
team_list.append(Team.get_season(x, season))
|
||||
all_picks = all_picks.where(
|
||||
(DraftPick.origowner << team_list) | (DraftPick.origowner << team_list)
|
||||
)
|
||||
|
||||
if owner_team_id is not None:
|
||||
all_picks = all_picks.where(
|
||||
(DraftPick.owner_id << owner_team_id) | (DraftPick.owner_id << owner_team_id)
|
||||
)
|
||||
|
||||
if orig_team_id is not None:
|
||||
all_picks = all_picks.where(
|
||||
(DraftPick.origowner_id << orig_team_id) | (DraftPick.origowner_id << orig_team_id)
|
||||
)
|
||||
|
||||
if pick_round_start is not None and pick_round_end is not None and pick_round_end < pick_round_start:
|
||||
raise HTTPException(status_code=400, detail=f'pick_round_end must be greater than or equal to pick_round_start')
|
||||
|
||||
if player_id is not None:
|
||||
all_picks = all_picks.where(DraftPick.player_id << player_id)
|
||||
if pick_round_start is not None:
|
||||
all_picks = all_picks.where(DraftPick.round >= pick_round_start)
|
||||
if pick_round_end is not None:
|
||||
all_picks = all_picks.where(DraftPick.round <= pick_round_end)
|
||||
if traded is not None:
|
||||
all_picks = all_picks.where(DraftPick.origowner != DraftPick.owner)
|
||||
if overall is not None:
|
||||
all_picks = all_picks.where(DraftPick.overall == overall)
|
||||
if overall_start is not None:
|
||||
all_picks = all_picks.where(DraftPick.overall >= overall_start)
|
||||
if overall_end is not None:
|
||||
all_picks = all_picks.where(DraftPick.overall <= overall_end)
|
||||
if player_taken is not None:
|
||||
all_picks = all_picks.where(DraftPick.player.is_null(not player_taken))
|
||||
if limit is not None:
|
||||
all_picks = all_picks.limit(limit)
|
||||
|
||||
if sort is not None:
|
||||
if sort == 'order-asc':
|
||||
all_picks = all_picks.order_by(DraftPick.overall)
|
||||
elif sort == 'order-desc':
|
||||
all_picks = all_picks.order_by(-DraftPick.overall)
|
||||
|
||||
return_picks = {'count': all_picks.count(), 'picks': []}
|
||||
for line in all_picks:
|
||||
return_picks['picks'].append(model_to_dict(line, recurse=not short_output))
|
||||
|
||||
db.close()
|
||||
return return_picks
|
||||
|
||||
|
||||
@router.get('/{pick_id}')
|
||||
async def get_one_pick(pick_id: int, short_output: Optional[bool] = False):
|
||||
this_pick = DraftPick.get_or_none(DraftPick.id == pick_id)
|
||||
if this_pick is not None:
|
||||
r_pick = model_to_dict(this_pick, recurse=not short_output)
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=f'Pick ID {pick_id} not found')
|
||||
db.close()
|
||||
return r_pick
|
||||
|
||||
|
||||
@router.patch('/{pick_id}')
|
||||
async def patch_pick(pick_id: int, new_pick: DraftPickModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_pick - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
if DraftPick.get_or_none(DraftPick.id == pick_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Pick ID {pick_id} not found')
|
||||
|
||||
DraftPick.update(**new_pick.dict()).where(DraftPick.id == pick_id).execute()
|
||||
r_pick = model_to_dict(DraftPick.get_by_id(pick_id))
|
||||
db.close()
|
||||
return r_pick
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_picks(p_list: DraftPickList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_picks - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_picks = []
|
||||
for pick in p_list.picks:
|
||||
dupe = DraftPick.get_or_none(DraftPick.season == pick.season, DraftPick.overall == pick.overall)
|
||||
if dupe:
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f'Pick # {pick.overall} already exists for season {pick.season}'
|
||||
)
|
||||
|
||||
new_picks.append(pick.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_picks, 15):
|
||||
DraftPick.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_picks)} picks'
|
||||
|
||||
|
||||
@router.delete('/{pick_id}')
|
||||
async def delete_pick(pick_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'delete_pick - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_pick = DraftPick.get_or_none(DraftPick.id == pick_id)
|
||||
if this_pick is None:
|
||||
raise HTTPException(status_code=404, detail=f'Pick ID {pick_id} not found')
|
||||
|
||||
count = this_pick.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
return f'Draft pick {pick_id} has been deleted'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Draft pick {pick_id} could not be deleted')
|
||||
|
||||
193
app/routers_v3/fieldingstats.py
Normal file
193
app/routers_v3/fieldingstats.py
Normal file
@ -0,0 +1,193 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional, Literal
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, BattingStat, Team, Player, Current, model_to_dict, chunked, fn, per_season_weeks
|
||||
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/v3/fieldingstats',
|
||||
tags=['fieldingstats']
|
||||
)
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_fieldingstats(
|
||||
season: int, s_type: Optional[str] = 'regular', team_abbrev: list = Query(default=None),
|
||||
player_name: list = Query(default=None), player_id: list = Query(default=None),
|
||||
week_start: Optional[int] = None, week_end: Optional[int] = None, game_num: list = Query(default=None),
|
||||
position: list = Query(default=None), limit: Optional[int] = None, sort: Optional[str] = None,
|
||||
short_output: Optional[bool] = True):
|
||||
if 'post' in s_type.lower():
|
||||
all_stats = BattingStat.post_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
elif s_type.lower() in ['combined', 'total', 'all']:
|
||||
all_stats = BattingStat.combined_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
else:
|
||||
all_stats = BattingStat.regular_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
|
||||
all_stats = all_stats.where(
|
||||
(BattingStat.xch > 0) | (BattingStat.pb > 0) | (BattingStat.sbc > 0)
|
||||
)
|
||||
|
||||
if position is not None:
|
||||
all_stats = all_stats.where(BattingStat.pos << [x.upper() for x in position])
|
||||
if team_abbrev is not None:
|
||||
t_query = Team.select().where(Team.abbrev << [x.upper() for x in team_abbrev])
|
||||
all_stats = all_stats.where(BattingStat.team << t_query)
|
||||
if player_name is not None or player_id is not None:
|
||||
if player_id:
|
||||
all_stats = all_stats.where(BattingStat.player_id << player_id)
|
||||
else:
|
||||
p_query = Player.select_season(season).where(fn.Lower(Player.name) << [x.lower() for x in player_name])
|
||||
all_stats = all_stats.where(BattingStat.player << p_query)
|
||||
if game_num:
|
||||
all_stats = all_stats.where(BattingStat.game == game_num)
|
||||
|
||||
start = 1
|
||||
end = Current.get(Current.season == season).week
|
||||
if week_start is not None:
|
||||
start = week_start
|
||||
if week_end is not None:
|
||||
end = min(week_end, end)
|
||||
if start > end:
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f'Start week {start} is after end week {end} - cannot pull stats'
|
||||
)
|
||||
all_stats = all_stats.where(
|
||||
(BattingStat.week >= start) & (BattingStat.week <= end)
|
||||
)
|
||||
|
||||
if limit:
|
||||
all_stats = all_stats.limit(limit)
|
||||
if sort:
|
||||
if sort == 'newest':
|
||||
all_stats = all_stats.order_by(-BattingStat.week, -BattingStat.game)
|
||||
|
||||
return_stats = {
|
||||
'count': all_stats.count(),
|
||||
'stats': [{
|
||||
'player': x.player_id if short_output else model_to_dict(x.player, recurse=False),
|
||||
'team': x.team_id if short_output else model_to_dict(x.team, recurse=False),
|
||||
'pos': x.pos,
|
||||
'xch': x.xch,
|
||||
'xhit': x.xhit,
|
||||
'error': x.error,
|
||||
'pb': x.pb,
|
||||
'sbc': x.sbc,
|
||||
'csc': x.csc,
|
||||
'week': x.week,
|
||||
'game': x.game,
|
||||
'season': x.season
|
||||
} for x in all_stats]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return return_stats
|
||||
|
||||
|
||||
@router.get('/totals')
|
||||
async def get_totalstats(
|
||||
season: int, s_type: Literal['regular', 'post', 'total', None] = None, team_abbrev: list = Query(default=None),
|
||||
team_id: list = Query(default=None), player_name: list = Query(default=None),
|
||||
week_start: Optional[int] = None, week_end: Optional[int] = None, game_num: list = Query(default=None),
|
||||
position: list = Query(default=None), sort: Optional[str] = None, player_id: list = Query(default=None),
|
||||
group_by: Literal['team', 'player', 'playerteam'] = 'player', short_output: Optional[bool] = False,
|
||||
min_ch: Optional[int] = 1, week: list = Query(default=None)):
|
||||
|
||||
all_stats = (
|
||||
BattingStat
|
||||
.select(BattingStat.player, BattingStat.pos, fn.SUM(BattingStat.xch).alias('sum_xch'),
|
||||
fn.SUM(BattingStat.xhit).alias('sum_xhit'), fn.SUM(BattingStat.error).alias('sum_error'),
|
||||
fn.SUM(BattingStat.pb).alias('sum_pb'), fn.SUM(BattingStat.sbc).alias('sum_sbc'),
|
||||
fn.SUM(BattingStat.csc).alias('sum_csc'), BattingStat.team)
|
||||
.where(BattingStat.season == season)
|
||||
.having(fn.SUM(BattingStat.xch) >= min_ch)
|
||||
)
|
||||
|
||||
if True in [s_type is not None, week_start is not None, week_end is not None]:
|
||||
weeks = {}
|
||||
if s_type is not None:
|
||||
weeks = per_season_weeks(season, s_type)
|
||||
elif week_start is not None or week_end is not None:
|
||||
if week_start is None or week_end is None:
|
||||
raise HTTPException(
|
||||
status_code=400, detail='Both week_start and week_end must be included if either is used.'
|
||||
)
|
||||
weeks['start'] = week_start
|
||||
if week_end < weeks['start']:
|
||||
raise HTTPException(status_code=400, detail='week_end must be greater than or equal to week_start')
|
||||
else:
|
||||
weeks['end'] = week_end
|
||||
|
||||
all_stats = all_stats.where(
|
||||
(BattingStat.week >= weeks['start']) & (BattingStat.week <= weeks['end'])
|
||||
)
|
||||
|
||||
elif week is not None:
|
||||
all_stats = all_stats.where(BattingStat.week << week)
|
||||
|
||||
if game_num is not None:
|
||||
all_stats = all_stats.where(BattingStat.game << game_num)
|
||||
if position is not None:
|
||||
p_list = [x.upper() for x in position]
|
||||
all_players = Player.select().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)
|
||||
)
|
||||
all_stats = all_stats.where(BattingStat.player << all_players)
|
||||
if sort is not None:
|
||||
if sort == 'player':
|
||||
all_stats = all_stats.order_by(BattingStat.player)
|
||||
elif sort == 'team':
|
||||
all_stats = all_stats.order_by(BattingStat.team)
|
||||
if group_by is not None:
|
||||
if group_by == 'team':
|
||||
all_stats = all_stats.group_by(BattingStat.pos, BattingStat.team)
|
||||
elif group_by == 'player':
|
||||
all_stats = all_stats.group_by(BattingStat.pos, BattingStat.player)
|
||||
elif group_by == 'playerteam':
|
||||
all_stats = all_stats.group_by(BattingStat.pos, BattingStat.team, BattingStat.player)
|
||||
if team_id is not None:
|
||||
all_teams = Team.select().where(Team.id << team_id)
|
||||
all_stats = all_stats.where(BattingStat.team << all_teams)
|
||||
elif team_abbrev is not None:
|
||||
all_teams = Team.select().where(fn.Lower(Team.abbrev) << [x.lower() for x in team_abbrev])
|
||||
all_stats = all_stats.where(BattingStat.team << all_teams)
|
||||
|
||||
if player_name is not None:
|
||||
all_players = Player.select().where(fn.Lower(Player.name) << [x.lower() for x in player_name])
|
||||
all_stats = all_stats.where(BattingStat.player << all_players)
|
||||
elif player_id is not None:
|
||||
all_players = Player.select().where(Player.id << player_id)
|
||||
all_stats = all_stats.where(BattingStat.player << all_players)
|
||||
|
||||
return_stats = {
|
||||
'count': sum(1 for i in all_stats if i.sum_xch + i.sum_sbc > 0),
|
||||
'stats': [{
|
||||
'player': x.player_id if short_output else model_to_dict(x.player, recurse=False),
|
||||
'team': x.team_id if short_output else model_to_dict(x.team, recurse=False),
|
||||
'pos': x.pos,
|
||||
'xch': x.sum_xch,
|
||||
'xhit': x.sum_xhit,
|
||||
'error': x.sum_error,
|
||||
'pb': x.sum_pb,
|
||||
'sbc': x.sum_sbc,
|
||||
'csc': x.sum_csc
|
||||
} for x in all_stats if x.sum_xch + x.sum_sbc > 0]
|
||||
}
|
||||
db.close()
|
||||
return return_stats
|
||||
113
app/routers_v3/keepers.py
Normal file
113
app/routers_v3/keepers.py
Normal file
@ -0,0 +1,113 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Keeper, Player, model_to_dict, chunked, 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/v3/keepers',
|
||||
tags=['keepers']
|
||||
)
|
||||
|
||||
|
||||
class KeeperModel(pydantic.BaseModel):
|
||||
season: int
|
||||
team_id: int
|
||||
player_id: int
|
||||
|
||||
|
||||
class KeeperList(pydantic.BaseModel):
|
||||
count: Optional[int] = None
|
||||
keepers: List[KeeperModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_keepers(
|
||||
season: list = Query(default=None), team_id: list = Query(default=None), player_id: list = Query(default=None),
|
||||
short_output: bool = False):
|
||||
all_keepers = Keeper.select()
|
||||
|
||||
if season is not None:
|
||||
all_keepers = all_keepers.where(Keeper.season << season)
|
||||
if team_id is not None:
|
||||
all_keepers = all_keepers.where(Keeper.team_id << team_id)
|
||||
if player_id is not None:
|
||||
all_keepers = all_keepers.where(Keeper.player_id << player_id)
|
||||
|
||||
return_keepers = {
|
||||
'count': all_keepers.count(),
|
||||
'keepers': [model_to_dict(x, recurse=not short_output) for x in all_keepers]
|
||||
}
|
||||
db.close()
|
||||
return return_keepers
|
||||
|
||||
|
||||
@router.patch('/{keeper_id}')
|
||||
async def patch_keeper(
|
||||
keeper_id: int, season: Optional[int] = None, team_id: Optional[int] = None, player_id: Optional[int] = None,
|
||||
token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_keeper - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_keeper = Keeper.get_or_none(Keeper.id == keeper_id)
|
||||
if not this_keeper:
|
||||
raise HTTPException(status_code=404, detail=f'Keeper ID {keeper_id} not found')
|
||||
|
||||
if season is not None:
|
||||
this_keeper.season = season
|
||||
if player_id is not None:
|
||||
this_keeper.player_id = player_id
|
||||
if team_id is not None:
|
||||
this_keeper.team_id = team_id
|
||||
|
||||
if this_keeper.save():
|
||||
r_keeper = model_to_dict(this_keeper)
|
||||
db.close()
|
||||
return r_keeper
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to patch keeper {keeper_id}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_keepers(k_list: KeeperList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_keepers - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_keepers = []
|
||||
for keeper in k_list.keepers:
|
||||
new_keepers.append(keeper.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_keepers, 14):
|
||||
Keeper.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_keepers)} keepers'
|
||||
|
||||
|
||||
@router.delete('/{keeper_id}')
|
||||
async def delete_keeper(keeper_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'delete_keeper - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_keeper = Keeper.get_or_none(Keeper.id == keeper_id)
|
||||
if not this_keeper:
|
||||
raise HTTPException(status_code=404, detail=f'Keeper ID {keeper_id} not found')
|
||||
|
||||
count = this_keeper.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
return f'Keeper ID {keeper_id} has been deleted'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Keeper ID {keeper_id} could not be deleted')
|
||||
|
||||
|
||||
152
app/routers_v3/managers.py
Normal file
152
app/routers_v3/managers.py
Normal file
@ -0,0 +1,152 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Manager, Team, Current, model_to_dict, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/managers',
|
||||
tags=['managers']
|
||||
)
|
||||
|
||||
|
||||
class ManagerModel(pydantic.BaseModel):
|
||||
name: str
|
||||
image: Optional[str] = None
|
||||
headline: Optional[str] = None
|
||||
bio: Optional[str] = None
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_managers(
|
||||
name: list = Query(default=None), active: Optional[bool] = None, short_output: Optional[bool] = False):
|
||||
if active is not None:
|
||||
current = Current.latest()
|
||||
t_query = Team.select_season(current.season)
|
||||
t_query = t_query.where(
|
||||
~(Team.abbrev.endswith('IL')) & ~(Team.abbrev.endswith('MiL'))
|
||||
)
|
||||
logging.info(f'tquery: {t_query}')
|
||||
a_mgr = []
|
||||
i_mgr = []
|
||||
|
||||
for x in t_query:
|
||||
logging.info(f'Team: {x.abbrev} / mgr1: {x.manager1} / mgr2: {x.manager2}')
|
||||
if x.manager1 is not None:
|
||||
a_mgr.append(x.manager1)
|
||||
logging.info(f'appending {x.manager1.name}')
|
||||
if x.manager2 is not None:
|
||||
a_mgr.append(x.manager2)
|
||||
logging.info(f'appending {x.manager2.name}')
|
||||
|
||||
logging.info(f'a_mgr: {a_mgr}')
|
||||
if active:
|
||||
final_mgrs = [model_to_dict(y, recurse=not short_output) for y in a_mgr]
|
||||
else:
|
||||
logging.info(f'checking inactive')
|
||||
for z in Manager.select():
|
||||
logging.info(f'checking: {z.name}')
|
||||
if z not in a_mgr:
|
||||
logging.info(f'+inactive: {z.name}')
|
||||
i_mgr.append(z)
|
||||
final_mgrs = [model_to_dict(y, recurse=not short_output) for y in i_mgr]
|
||||
|
||||
return_managers = {
|
||||
'count': len(final_mgrs),
|
||||
'managers': final_mgrs
|
||||
}
|
||||
|
||||
else:
|
||||
all_managers = Manager.select()
|
||||
if name is not None:
|
||||
name_list = [x.lower() for x in name]
|
||||
all_managers = all_managers.where(fn.Lower(Manager.name) << name_list)
|
||||
|
||||
return_managers = {
|
||||
'count': all_managers.count(),
|
||||
'managers': [model_to_dict(x, recurse=not short_output) for x in all_managers]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return return_managers
|
||||
|
||||
|
||||
@router.get('/{manager_id}')
|
||||
async def get_one_manager(manager_id: int, short_output: Optional[bool] = False):
|
||||
this_manager = Manager.get_or_none(Manager.id == manager_id)
|
||||
if this_manager is not None:
|
||||
r_manager = model_to_dict(this_manager, recurse=not short_output)
|
||||
db.close()
|
||||
return r_manager
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=f'Manager {manager_id} not found')
|
||||
|
||||
|
||||
@router.patch('/{manager_id}')
|
||||
async def patch_manager(
|
||||
manager_id: int, name: Optional[str] = None, image: Optional[str] = None, headline: Optional[str] = None,
|
||||
bio: Optional[str] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_manager - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_manager = Manager.get_or_none(Manager.id == manager_id)
|
||||
if this_manager is None:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Manager ID {manager_id} not found')
|
||||
|
||||
if name is not None:
|
||||
this_manager.name = name
|
||||
if image is not None:
|
||||
this_manager.image = image
|
||||
if headline is not None:
|
||||
this_manager.headline = headline
|
||||
if bio is not None:
|
||||
this_manager.bio = bio
|
||||
|
||||
if this_manager.save() == 1:
|
||||
r_manager = model_to_dict(this_manager)
|
||||
db.close()
|
||||
return r_manager
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to patch manager {this_manager}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_manager(new_manager: ManagerModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_manager - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_manager = Manager(**new_manager.dict())
|
||||
|
||||
if this_manager.save():
|
||||
r_manager = model_to_dict(this_manager)
|
||||
db.close()
|
||||
return r_manager
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to post manager {this_manager.name}')
|
||||
|
||||
|
||||
@router.delete('/{manager_id}')
|
||||
async def delete_manager(manager_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'delete_manager - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_manager = Manager.get_or_none(Manager.id == manager_id)
|
||||
if this_manager is None:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Manager ID {manager_id} not found')
|
||||
|
||||
count = this_manager.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
return f'Manager {manager_id} has been deleted'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Manager {manager_id} could not be deleted')
|
||||
267
app/routers_v3/pitchingstats.py
Normal file
267
app/routers_v3/pitchingstats.py
Normal file
@ -0,0 +1,267 @@
|
||||
import datetime
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional, Literal
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, PitchingStat, Team, Player, Current, model_to_dict, chunked, fn, per_season_weeks
|
||||
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/v3/pitchingstats',
|
||||
tags=['pitchingstats']
|
||||
)
|
||||
|
||||
|
||||
class PitStatModel(pydantic.BaseModel):
|
||||
player_id: int
|
||||
team_id: int
|
||||
ip: Optional[float] = 0.0
|
||||
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
|
||||
gs: Optional[int] = 0
|
||||
win: Optional[int] = 0
|
||||
loss: Optional[int] = 0
|
||||
hold: Optional[int] = 0
|
||||
sv: Optional[int] = 0
|
||||
bsv: Optional[int] = 0
|
||||
ir: Optional[int] = 0
|
||||
irs: Optional[int] = 0
|
||||
week: int
|
||||
game: int
|
||||
season: int
|
||||
|
||||
|
||||
class PitStatList(pydantic.BaseModel):
|
||||
count: int
|
||||
stats: List[PitStatModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_pitstats(
|
||||
season: int, s_type: Optional[str] = 'regular', team_abbrev: list = Query(default=None),
|
||||
player_name: list = Query(default=None), player_id: list = Query(default=None),
|
||||
week_start: Optional[int] = None, week_end: Optional[int] = None, game_num: list = Query(default=None),
|
||||
limit: Optional[int] = None, ip_min: Optional[float] = None, sort: Optional[str] = None,
|
||||
short_output: Optional[bool] = True):
|
||||
if 'post' in s_type.lower():
|
||||
all_stats = PitchingStat.post_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
elif s_type.lower() in ['combined', 'total', 'all']:
|
||||
all_stats = PitchingStat.combined_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
else:
|
||||
all_stats = PitchingStat.regular_season(season)
|
||||
if all_stats.count() == 0:
|
||||
db.close()
|
||||
return {'count': 0, 'stats': []}
|
||||
|
||||
if team_abbrev is not None:
|
||||
t_query = Team.select().where(Team.abbrev << [x.upper() for x in team_abbrev])
|
||||
all_stats = all_stats.where(PitchingStat.team << t_query)
|
||||
if player_name is not None or player_id is not None:
|
||||
if player_id:
|
||||
all_stats = all_stats.where(PitchingStat.player_id << player_id)
|
||||
else:
|
||||
p_query = Player.select_season(season).where(fn.Lower(Player.name) << [x.lower() for x in player_name])
|
||||
all_stats = all_stats.where(PitchingStat.player << p_query)
|
||||
if game_num:
|
||||
all_stats = all_stats.where(PitchingStat.game == game_num)
|
||||
if ip_min is not None:
|
||||
all_stats = all_stats.where(PitchingStat.ip >= ip_min)
|
||||
|
||||
start = 1
|
||||
end = Current.get(Current.season == season).week
|
||||
if week_start is not None:
|
||||
start = week_start
|
||||
if week_end is not None:
|
||||
end = min(week_end, end)
|
||||
if start > end:
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f'Start week {start} is after end week {end} - cannot pull stats'
|
||||
)
|
||||
all_stats = all_stats.where(
|
||||
(PitchingStat.week >= start) & (PitchingStat.week <= end)
|
||||
)
|
||||
|
||||
if limit:
|
||||
all_stats = all_stats.limit(limit)
|
||||
if sort:
|
||||
if sort == 'newest':
|
||||
all_stats = all_stats.order_by(-PitchingStat.week, -PitchingStat.game)
|
||||
|
||||
return_stats = {
|
||||
'count': all_stats.count(),
|
||||
'stats': [model_to_dict(x, recurse=not short_output) for x in all_stats]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return return_stats
|
||||
|
||||
|
||||
@router.get('/totals')
|
||||
async def get_totalstats(
|
||||
season: int, s_type: Literal['regular', 'post', 'total', None] = None, team_abbrev: list = Query(default=None),
|
||||
team_id: list = Query(default=None), player_name: list = Query(default=None),
|
||||
week_start: Optional[int] = None, week_end: Optional[int] = None, game_num: list = Query(default=None),
|
||||
is_sp: Optional[bool] = None, ip_min: Optional[float] = 0.25, sort: Optional[str] = None,
|
||||
player_id: list = Query(default=None), short_output: Optional[bool] = False,
|
||||
group_by: Literal['team', 'player', 'playerteam'] = 'player', week: list = Query(default=None)):
|
||||
if sum(1 for x in [s_type, (week_start or week_end), week] if x is not None) > 1:
|
||||
raise HTTPException(status_code=400, detail=f'Only one of s_type, week_start/week_end, or week may be used.')
|
||||
|
||||
all_stats = (
|
||||
PitchingStat
|
||||
.select(PitchingStat.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.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.SUM(PitchingStat.irs).alias('sum_irs'),
|
||||
fn.SUM(PitchingStat.gs).alias('sum_gs'), PitchingStat.team)
|
||||
.where(PitchingStat.season == season)
|
||||
.having(fn.SUM(PitchingStat.ip) >= ip_min)
|
||||
)
|
||||
|
||||
if True in [s_type is not None, week_start is not None, week_end is not None]:
|
||||
weeks = {}
|
||||
if s_type is not None:
|
||||
weeks = per_season_weeks(season, s_type)
|
||||
elif week_start is not None or week_end is not None:
|
||||
if week_start is None or week_end is None:
|
||||
raise HTTPException(
|
||||
status_code=400, detail='Both week_start and week_end must be included if either is used.'
|
||||
)
|
||||
weeks['start'] = week_start
|
||||
if week_end < weeks['start']:
|
||||
raise HTTPException(status_code=400, detail='week_end must be greater than or equal to week_start')
|
||||
else:
|
||||
weeks['end'] = week_end
|
||||
|
||||
all_stats = all_stats.where(
|
||||
(PitchingStat.week >= weeks['start']) & (PitchingStat.week <= weeks['end'])
|
||||
)
|
||||
|
||||
elif week is not None:
|
||||
all_stats = all_stats.where(PitchingStat.week << week)
|
||||
|
||||
if game_num is not None:
|
||||
all_stats = all_stats.where(PitchingStat.game << game_num)
|
||||
if is_sp is not None:
|
||||
if is_sp:
|
||||
all_stats = all_stats.where(PitchingStat.gs == 1)
|
||||
if not is_sp:
|
||||
all_stats = all_stats.where(PitchingStat.gs == 0)
|
||||
if sort is not None:
|
||||
if sort == 'player':
|
||||
all_stats = all_stats.order_by(PitchingStat.player)
|
||||
elif sort == 'team':
|
||||
all_stats = all_stats.order_by(PitchingStat.team)
|
||||
if group_by is not None:
|
||||
if group_by == 'team':
|
||||
all_stats = all_stats.group_by(PitchingStat.team)
|
||||
elif group_by == 'player':
|
||||
all_stats = all_stats.group_by(PitchingStat.player)
|
||||
elif group_by == 'playerteam':
|
||||
all_stats = all_stats.group_by(PitchingStat.team, PitchingStat.player)
|
||||
if team_id is not None:
|
||||
all_teams = Team.select().where(Team.id << team_id)
|
||||
all_stats = all_stats.where(PitchingStat.team << all_teams)
|
||||
elif team_abbrev is not None:
|
||||
all_teams = Team.select().where(fn.Lower(Team.abbrev) << [x.lower() for x in team_abbrev])
|
||||
all_stats = all_stats.where(PitchingStat.team << all_teams)
|
||||
if player_name is not None:
|
||||
all_players = Player.select().where(fn.Lower(Player.name) << [x.lower() for x in player_name])
|
||||
all_stats = all_stats.where(PitchingStat.player << all_players)
|
||||
elif player_id is not None:
|
||||
all_players = Player.select().where(Player.id << player_id)
|
||||
all_stats = all_stats.where(PitchingStat.player << all_players)
|
||||
|
||||
return_stats = {
|
||||
'count': all_stats.count(),
|
||||
'stats': [{
|
||||
'player': x.player_id if short_output else model_to_dict(x.player, recurse=False),
|
||||
'team': x.team_id if short_output else model_to_dict(x.team, recurse=False),
|
||||
'ip': x.sum_ip,
|
||||
'hit': x.sum_hit,
|
||||
'run': x.sum_run,
|
||||
'erun': x.sum_erun,
|
||||
'so': x.sum_so,
|
||||
'bb': x.sum_bb,
|
||||
'hbp': x.sum_hbp,
|
||||
'wp': x.sum_wp,
|
||||
'balk': x.sum_balk,
|
||||
'hr': x.sum_hr,
|
||||
'ir': x.sum_ir,
|
||||
'irs': x.sum_irs,
|
||||
'gs': x.sum_gs,
|
||||
'win': x.sum_win,
|
||||
'loss': x.sum_loss,
|
||||
'hold': x.sum_hold,
|
||||
'sv': x.sum_sv,
|
||||
'bsv': x.sum_bsv
|
||||
} for x in all_stats]
|
||||
}
|
||||
db.close()
|
||||
return return_stats
|
||||
|
||||
|
||||
@router.patch('/{stat_id}')
|
||||
async def patch_pitstats(stat_id: int, new_stats: PitStatModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_pitstats - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
if PitchingStat.get_or_none(PitchingStat.id == stat_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Stat ID {stat_id} not found')
|
||||
|
||||
PitchingStat.update(**new_stats.dict()).where(PitchingStat.id == stat_id).execute()
|
||||
r_stat = model_to_dict(PitchingStat.get_by_id(stat_id))
|
||||
db.close()
|
||||
return r_stat
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_pitstats(s_list: PitStatList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_pitstats - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
all_stats = []
|
||||
|
||||
for x in s_list.stats:
|
||||
team = Team.get_or_none(Team.id == x.team_id)
|
||||
this_player = Player.get_or_none(Player.id == x.player_id)
|
||||
if team is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.team_id} not found')
|
||||
if this_player is None:
|
||||
raise HTTPException(status_code=404, detail=f'Player ID {x.player_id} not found')
|
||||
|
||||
all_stats.append(PitchingStat(**x.dict()))
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(all_stats, 15):
|
||||
PitchingStat.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
db.close()
|
||||
return f'Added {len(all_stats)} batting lines'
|
||||
190
app/routers_v3/players.py
Normal file
190
app/routers_v3/players.py
Normal file
@ -0,0 +1,190 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
from pandas import DataFrame
|
||||
|
||||
from ..db_engine import db, Player, model_to_dict, chunked, 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/v3/players',
|
||||
tags=['players']
|
||||
)
|
||||
|
||||
|
||||
class PlayerModel(pydantic.BaseModel):
|
||||
name: str
|
||||
wara: float
|
||||
image: str
|
||||
image2: Optional[str] = None
|
||||
team_id: int
|
||||
season: int
|
||||
pitcher_injury: Optional[int] = None
|
||||
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
|
||||
vanity_card: Optional[str] = None
|
||||
headshot: Optional[str] = None
|
||||
last_game: Optional[str] = None
|
||||
last_game2: Optional[str] = None
|
||||
il_return: Optional[str] = None
|
||||
demotion_week: Optional[int] = None
|
||||
strat_code: Optional[str] = None
|
||||
bbref_id: Optional[str] = None
|
||||
injury_rating: Optional[str] = None
|
||||
|
||||
|
||||
class PlayerList(pydantic.BaseModel):
|
||||
players: List[PlayerModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_players(
|
||||
season: Optional[int], name: Optional[str] = None, team_id: list = Query(default=None),
|
||||
pos: list = Query(default=None), strat_code: list = Query(default=None), is_injured: Optional[bool] = None,
|
||||
sort: Optional[str] = None, short_output: Optional[bool] = False, csv: Optional[bool] = False):
|
||||
all_players = Player.select_season(season)
|
||||
|
||||
if team_id is not None:
|
||||
all_players = all_players.where(Player.team_id << team_id)
|
||||
|
||||
if strat_code is not None:
|
||||
code_list = [x.lower() for x in strat_code]
|
||||
all_players = all_players.where(fn.Lower(Player.strat_code) << code_list)
|
||||
|
||||
if name is not None:
|
||||
all_players = all_players.where(fn.lower(Player.name) == name.lower())
|
||||
|
||||
if pos is not None:
|
||||
p_list = [x.upper() for x in pos]
|
||||
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 is_injured is not None:
|
||||
all_players = all_players.where(Player.il_return.is_null(False))
|
||||
|
||||
if sort is not None:
|
||||
if sort == 'cost-asc':
|
||||
all_players = all_players.order_by(Player.wara)
|
||||
elif sort == 'cost-desc':
|
||||
all_players = all_players.order_by(-Player.wara)
|
||||
elif sort == 'name-asc':
|
||||
all_players = all_players.order_by(Player.name)
|
||||
elif sort == 'name-desc':
|
||||
all_players = all_players.order_by(-Player.name)
|
||||
|
||||
print(f'csv: {csv}')
|
||||
if csv:
|
||||
player_list = [
|
||||
['name', 'wara', 'image', 'image2', 'team', 'season', 'pitcher_injury', 'pos_1', 'pos_2', 'pos_3',
|
||||
'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', 'demotion_week',
|
||||
'headshot', 'vanity_card', 'strat_code', 'bbref_id', 'injury_rating', 'player_id']
|
||||
]
|
||||
for line in all_players:
|
||||
player_list.append(
|
||||
[
|
||||
line.name, line.wara, line.image, line.image2, line.team.abbrev, line.season, line.pitcher_injury,
|
||||
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.last_game, line.last_game2, line.il_return, line.demotion_week, line.headshot,
|
||||
line.vanity_card, line.strat_code.replace(",", "-_-"), line.bbref_id, line.injury_rating, line.id
|
||||
]
|
||||
)
|
||||
return_players = {
|
||||
'count': all_players.count(),
|
||||
'players': DataFrame(player_list).to_csv(header=False, index=False),
|
||||
'csv': True
|
||||
}
|
||||
|
||||
db.close()
|
||||
return Response(content=return_players['players'], media_type='text/csv')
|
||||
|
||||
else:
|
||||
return_players = {
|
||||
'count': all_players.count(),
|
||||
'players': [model_to_dict(x, recurse=not short_output) for x in all_players]
|
||||
}
|
||||
db.close()
|
||||
return return_players
|
||||
|
||||
|
||||
@router.get('/{player_id}')
|
||||
async def get_one_player(player_id: int, short_output: Optional[bool] = False):
|
||||
this_player = Player.get_or_none(Player.id == player_id)
|
||||
if this_player:
|
||||
r_player = model_to_dict(this_player, recurse=not short_output)
|
||||
else:
|
||||
r_player = None
|
||||
db.close()
|
||||
return r_player
|
||||
|
||||
|
||||
@router.patch('/{player_id}')
|
||||
async def patch_player(
|
||||
player_id: int, new_player: PlayerModel, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_player - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
if Player.get_or_none(Player.id == player_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Player ID {player_id} not found')
|
||||
|
||||
Player.update(**new_player.dict()).where(Player.id == player_id).execute()
|
||||
r_player = model_to_dict(Player.get_by_id(player_id))
|
||||
db.close()
|
||||
return r_player
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_players(p_list: PlayerList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_players - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_players = []
|
||||
for player in p_list.players:
|
||||
dupe = Player.get_or_none(Player.season == player.season, Player.name == player.name)
|
||||
if dupe:
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f'Player name {player.name} already in use in Season {player.season}'
|
||||
)
|
||||
|
||||
new_players.append(player.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_players, 15):
|
||||
Player.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_players)} 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'delete_player - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_player = Player.get_or_none(Player.id == player_id)
|
||||
if not this_player:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Player ID {player_id} not found')
|
||||
|
||||
count = this_player.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
return f'Player {player_id} has been deleted'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Player {player_id} could not be deleted')
|
||||
175
app/routers_v3/results.py
Normal file
175
app/routers_v3/results.py
Normal file
@ -0,0 +1,175 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Result, Team, model_to_dict, chunked
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/results',
|
||||
tags=['results']
|
||||
)
|
||||
|
||||
|
||||
class ResultModel(pydantic.BaseModel):
|
||||
week: int
|
||||
game: int
|
||||
awayteam_id: int
|
||||
hometeam_id: int
|
||||
awayscore: int
|
||||
homescore: int
|
||||
season: int
|
||||
scorecard_url: Optional[str] = None
|
||||
|
||||
|
||||
class ResultList(pydantic.BaseModel):
|
||||
results: List[ResultModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_results(
|
||||
season: int, team_abbrev: list = Query(default=None), week_start: Optional[int] = None,
|
||||
week_end: Optional[int] = None, game_num: list = Query(default=None),
|
||||
away_abbrev: list = Query(default=None), home_abbrev: list = Query(default=None),
|
||||
short_output: Optional[bool] = False):
|
||||
all_results = Result.select_season(season)
|
||||
|
||||
if team_abbrev is not None:
|
||||
team_list = []
|
||||
for x in team_abbrev:
|
||||
team_list.append(Team.get_season(x, season))
|
||||
all_results = all_results.where(
|
||||
(Result.awayteam << team_list) | (Result.hometeam << team_list)
|
||||
)
|
||||
|
||||
if away_abbrev is not None:
|
||||
team_list = []
|
||||
for x in away_abbrev:
|
||||
team_list.append(Team.get_season(x, season))
|
||||
all_results = all_results.where(Result.awayteam << team_list)
|
||||
|
||||
if home_abbrev is not None:
|
||||
team_list = []
|
||||
for x in home_abbrev:
|
||||
team_list.append(Team.get_season(x, season))
|
||||
all_results = all_results.where(Result.hometeam << team_list)
|
||||
|
||||
if game_num is not None:
|
||||
all_results = all_results.where(Result.game << game_num)
|
||||
|
||||
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)
|
||||
|
||||
return_results = {
|
||||
'count': all_results.count(),
|
||||
'results': [model_to_dict(x, recurse=not short_output) for x in all_results]
|
||||
}
|
||||
db.close()
|
||||
return return_results
|
||||
|
||||
|
||||
@router.get('/{result_id}')
|
||||
async def get_one_result(result_id: int, short_output: Optional[bool] = False):
|
||||
this_result = Result.get_or_none(Result.id == result_id)
|
||||
if this_result is not None:
|
||||
r_result = model_to_dict(this_result, recurse=not short_output)
|
||||
else:
|
||||
r_result = None
|
||||
db.close()
|
||||
return r_result
|
||||
|
||||
|
||||
@router.patch('/{result_id}')
|
||||
async def patch_result(
|
||||
result_id: int, week_num: Optional[int] = None, game_num: Optional[int] = None,
|
||||
away_team_id: Optional[int] = None, home_team_id: Optional[int] = None, away_score: Optional[int] = None,
|
||||
home_score: Optional[int] = None, season: Optional[int] = None, scorecard_url: Optional[str] = None,
|
||||
token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_player - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_result = Result.get_or_none(Result.id == result_id)
|
||||
if this_result is None:
|
||||
raise HTTPException(status_code=404, detail=f'Result ID {result_id} not found')
|
||||
|
||||
if week_num is not None:
|
||||
this_result.week = week_num
|
||||
|
||||
if game_num is not None:
|
||||
this_result.game = game_num
|
||||
|
||||
if away_team_id is not None:
|
||||
this_result.awayteam_id = away_team_id
|
||||
|
||||
if home_team_id is not None:
|
||||
this_result.hometeam_id = home_team_id
|
||||
|
||||
if away_score is not None:
|
||||
this_result.awayscore = away_score
|
||||
|
||||
if home_score is not None:
|
||||
this_result.homescore = home_score
|
||||
|
||||
if season is not None:
|
||||
this_result.season = season
|
||||
|
||||
if scorecard_url is not None:
|
||||
this_result.scorecard_url = scorecard_url
|
||||
|
||||
if this_result.save() == 1:
|
||||
r_result = model_to_dict(this_result)
|
||||
db.close()
|
||||
return r_result
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to patch result {result_id}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_results(result_list: ResultList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_player - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_results = []
|
||||
for x in result_list.results:
|
||||
if Team.get_or_none(Team.id == x.awayteam_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.awayteam_id} not found')
|
||||
if Team.get_or_none(Team.id == x.hometeam_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.hometeam_id} not found')
|
||||
|
||||
new_results.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_results, 15):
|
||||
Result.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_results)} results'
|
||||
|
||||
|
||||
@router.delete('/{result_id}')
|
||||
async def delete_result(result_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'delete_result - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_result = Result.get_or_none(Result.id == result_id)
|
||||
if not this_result:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Result ID {result_id} not found')
|
||||
|
||||
count = this_result.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
return f'Result {result_id} has been deleted'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Result {result_id} could not be deleted')
|
||||
|
||||
|
||||
157
app/routers_v3/schedules.py
Normal file
157
app/routers_v3/schedules.py
Normal file
@ -0,0 +1,157 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Schedule, Team, model_to_dict, chunked
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/schedules',
|
||||
tags=['schedules']
|
||||
)
|
||||
|
||||
|
||||
class ScheduleModel(pydantic.BaseModel):
|
||||
week: int
|
||||
awayteam_id: int
|
||||
hometeam_id: int
|
||||
gamecount: int
|
||||
season: int
|
||||
|
||||
|
||||
class ScheduleList(pydantic.BaseModel):
|
||||
schedules: List[ScheduleModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_schedules(
|
||||
season: int, team_abbrev: list = Query(default=None), away_abbrev: list = Query(default=None),
|
||||
home_abbrev: list = Query(default=None), week_start: Optional[int] = None, week_end: Optional[int] = None,
|
||||
short_output: Optional[bool] = True):
|
||||
all_sched = Schedule.select_season(season)
|
||||
|
||||
if team_abbrev is not None:
|
||||
team_list = []
|
||||
for x in team_abbrev:
|
||||
team_list.append(Team.get_season(x, season))
|
||||
all_sched = all_sched.where(
|
||||
(Schedule.awayteam << team_list) | (Schedule.hometeam << team_list)
|
||||
)
|
||||
|
||||
if away_abbrev is not None:
|
||||
team_list = []
|
||||
for x in away_abbrev:
|
||||
team_list.append(Team.get_season(x, season))
|
||||
all_sched = all_sched.where(Schedule.awayteam << team_list)
|
||||
|
||||
if home_abbrev is not None:
|
||||
team_list = []
|
||||
for x in home_abbrev:
|
||||
team_list.append(Team.get_season(x, season))
|
||||
all_sched = all_sched.where(Schedule.hometeam << team_list)
|
||||
|
||||
if week_start is not None:
|
||||
all_sched = all_sched.where(Schedule.week >= week_start)
|
||||
|
||||
if week_end is not None:
|
||||
all_sched = all_sched.where(Schedule.week <= week_end)
|
||||
|
||||
all_sched = all_sched.order_by(Schedule.id)
|
||||
|
||||
return_sched = {
|
||||
'count': all_sched.count(),
|
||||
'schedules': [model_to_dict(x, recurse=not short_output) for x in all_sched]
|
||||
}
|
||||
db.close()
|
||||
return return_sched
|
||||
|
||||
|
||||
@router.get('/{schedule_id}')
|
||||
async def get_one_schedule(schedule_id: int):
|
||||
this_sched = Schedule.get_or_none(Schedule.id == schedule_id)
|
||||
if this_sched is not None:
|
||||
r_sched = model_to_dict(this_sched)
|
||||
else:
|
||||
r_sched = None
|
||||
db.close()
|
||||
return r_sched
|
||||
|
||||
|
||||
@router.patch('/{schedule_id}')
|
||||
async def patch_schedule(
|
||||
schedule_id: int, week: list = Query(default=None), awayteam_id: Optional[int] = None,
|
||||
hometeam_id: Optional[int] = None, gamecount: Optional[int] = None, season: Optional[int] = None,
|
||||
token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_schedule - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_sched = Schedule.get_or_none(Schedule.id == schedule_id)
|
||||
if this_sched is None:
|
||||
raise HTTPException(status_code=404, detail=f'Schedule ID {schedule_id} not found')
|
||||
|
||||
if week is not None:
|
||||
this_sched.week = week
|
||||
|
||||
if awayteam_id is not None:
|
||||
this_sched.awayteam_id = awayteam_id
|
||||
|
||||
if hometeam_id is not None:
|
||||
this_sched.hometeam_id = hometeam_id
|
||||
|
||||
if gamecount is not None:
|
||||
this_sched.gamecount = gamecount
|
||||
|
||||
if season is not None:
|
||||
this_sched.season = season
|
||||
|
||||
if this_sched.save() == 1:
|
||||
r_sched = model_to_dict(this_sched)
|
||||
db.close()
|
||||
return r_sched
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to patch schedule {schedule_id}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_schedules(sched_list: ScheduleList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_schedules - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_sched = []
|
||||
for x in sched_list.schedules:
|
||||
if Team.get_or_none(Team.id == x.awayteam_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.awayteam_id} not found')
|
||||
if Team.get_or_none(Team.id == x.hometeam_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.hometeam_id} not found')
|
||||
|
||||
new_sched.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_sched, 15):
|
||||
Schedule.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_sched)} schedules'
|
||||
|
||||
|
||||
@router.delete('/{schedule_id}')
|
||||
async def delete_schedule(schedule_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'delete_schedule - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_sched = Schedule.get_or_none(Schedule.id == schedule_id)
|
||||
if this_sched is None:
|
||||
raise HTTPException(status_code=404, detail=f'Schedule ID {schedule_id} not found')
|
||||
|
||||
count = this_sched.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
return f'Schedule {this_sched} has been deleted'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Schedule {this_sched} could not be deleted')
|
||||
90
app/routers_v3/standings.py
Normal file
90
app/routers_v3/standings.py
Normal file
@ -0,0 +1,90 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Standings, Team, Division, model_to_dict, chunked, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/standings',
|
||||
tags=['standings']
|
||||
)
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_standings(
|
||||
season: int, team_abbrev: list = Query(default=None), league_abbrev: Optional[str] = None,
|
||||
division_abbrev: Optional[str] = None, short_output: Optional[bool] = False):
|
||||
standings = Standings.select_season(season)
|
||||
|
||||
# if standings.count() == 0:
|
||||
# db.close()
|
||||
# raise HTTPException(status_code=404, detail=f'No output for season {season}')
|
||||
|
||||
if team_abbrev is not None:
|
||||
t_query = Team.select().where(fn.Lower(Team.abbrev) << [x.lower() for x in team_abbrev])
|
||||
standings = standings.where(Standings.team << t_query)
|
||||
|
||||
if league_abbrev is not None:
|
||||
l_query = Division.select().where(fn.Lower(Division.league_abbrev) == league_abbrev.lower())
|
||||
standings = standings.where(Standings.team.division << l_query)
|
||||
|
||||
if division_abbrev is not None:
|
||||
d_query = Division.select().where(fn.Lower(Division.division_abbrev) == division_abbrev.lower())
|
||||
standings = standings.where(Standings.team.division << d_query)
|
||||
|
||||
def win_pct(this_team_stan):
|
||||
if this_team_stan.wins + this_team_stan.losses == 0:
|
||||
return 0
|
||||
else:
|
||||
return (this_team_stan.wins / (this_team_stan.wins + this_team_stan.losses)) + \
|
||||
(this_team_stan.run_diff * .000001)
|
||||
|
||||
div_teams = [x for x in standings]
|
||||
div_teams.sort(key=lambda team: win_pct(team), reverse=True)
|
||||
|
||||
return_standings = {
|
||||
'count': len(div_teams),
|
||||
'standings': [model_to_dict(x, recurse=not short_output) for x in div_teams]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return return_standings
|
||||
|
||||
|
||||
@router.patch('/{stan_id}')
|
||||
async def patch_standings(
|
||||
stan_id, wins: Optional[int] = None, losses: Optional[int] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_standings - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
try:
|
||||
this_stan = Standings.get_by_id(stan_id)
|
||||
except Exception as e:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'No team found with id {stan_id}')
|
||||
|
||||
if wins:
|
||||
this_stan.wins = wins
|
||||
if losses:
|
||||
this_stan.losses = losses
|
||||
|
||||
this_stan.save()
|
||||
db.close()
|
||||
|
||||
return model_to_dict(this_stan)
|
||||
|
||||
|
||||
@router.post('/s{season}/recalculate')
|
||||
async def recalculate_standings(season: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'recalculate_standings - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
code = Standings.recalculate(season)
|
||||
db.close()
|
||||
if code == 69:
|
||||
HTTPException(status_code=500, detail=f'Error recreating Standings rows')
|
||||
raise HTTPException(status_code=200, detail=f'Just recalculated standings for season {season}')
|
||||
249
app/routers_v3/teams.py
Normal file
249
app/routers_v3/teams.py
Normal file
@ -0,0 +1,249 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional, Literal
|
||||
import copy
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Team, Manager, Division, model_to_dict, chunked, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/teams',
|
||||
tags=['teams']
|
||||
)
|
||||
|
||||
|
||||
class TeamModel(pydantic.BaseModel):
|
||||
abbrev: str
|
||||
sname: str
|
||||
lname: str
|
||||
gmid: Optional[int] = None
|
||||
gmid2: Optional[int] = None
|
||||
manager1_id: Optional[int] = None
|
||||
manager2_id: Optional[int] = None
|
||||
division_id: Optional[int] = None
|
||||
stadium: Optional[str] = None
|
||||
thumbnail: Optional[str] = None
|
||||
color: Optional[str] = None
|
||||
dice_color: Optional[str] = None
|
||||
season: int
|
||||
|
||||
|
||||
class TeamList(pydantic.BaseModel):
|
||||
teams: List[TeamModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_teams(
|
||||
season: Optional[int] = None, owner_id: list = Query(default=None), manager_id: list = Query(default=None),
|
||||
team_abbrev: list = Query(default=None), active_only: Optional[bool] = False,
|
||||
short_output: Optional[bool] = False):
|
||||
if season is not None:
|
||||
all_teams = Team.select_season(season)
|
||||
else:
|
||||
all_teams = Team.select()
|
||||
|
||||
if manager_id is not None:
|
||||
managers = Manager.select().where(Manager.id << manager_id)
|
||||
all_teams = all_teams.where(
|
||||
(Team.manager1_id << managers) | (Team.manager2_id << managers)
|
||||
)
|
||||
if owner_id:
|
||||
all_teams = all_teams.where((Team.gmid << owner_id) | (Team.gmid2 << owner_id))
|
||||
if team_abbrev is not None:
|
||||
team_list = [x.lower() for x in team_abbrev]
|
||||
all_teams = all_teams.where(fn.lower(Team.abbrev) << team_list)
|
||||
if active_only:
|
||||
all_teams = all_teams.where(
|
||||
~(Team.abbrev.endswith('IL')) & ~(Team.abbrev.endswith('MiL'))
|
||||
)
|
||||
|
||||
return_teams = {
|
||||
'count': all_teams.count(),
|
||||
'teams': [model_to_dict(x, recurse=not short_output) for x in all_teams]
|
||||
}
|
||||
db.close()
|
||||
return return_teams
|
||||
|
||||
|
||||
@router.get('/{team_id}')
|
||||
async def get_one_team(team_id: int):
|
||||
this_team = Team.get_or_none(Team.id == team_id)
|
||||
if this_team:
|
||||
r_team = model_to_dict(this_team)
|
||||
else:
|
||||
r_team = None
|
||||
db.close()
|
||||
return r_team
|
||||
|
||||
|
||||
@router.get('/{team_id}/roster/{which}')
|
||||
async def get_team_roster(team_id: int, which: Literal['current', 'next'], sort: Optional[str] = None):
|
||||
try:
|
||||
this_team = Team.get_by_id(team_id)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {team_id} not found')
|
||||
|
||||
if which == 'current':
|
||||
full_roster = this_team.get_this_week()
|
||||
else:
|
||||
full_roster = this_team.get_next_week()
|
||||
|
||||
active_players = copy.deepcopy(full_roster['active']['players'])
|
||||
sil_players = copy.deepcopy(full_roster['shortil']['players'])
|
||||
lil_players = copy.deepcopy(full_roster['longil']['players'])
|
||||
full_roster['active']['players'] = []
|
||||
full_roster['shortil']['players'] = []
|
||||
full_roster['longil']['players'] = []
|
||||
|
||||
for player in active_players:
|
||||
full_roster['active']['players'].append(model_to_dict(player))
|
||||
for player in sil_players:
|
||||
full_roster['shortil']['players'].append(model_to_dict(player))
|
||||
for player in lil_players:
|
||||
full_roster['longil']['players'].append(model_to_dict(player))
|
||||
|
||||
if sort:
|
||||
if sort == 'wara-desc':
|
||||
full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True)
|
||||
full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True)
|
||||
full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True)
|
||||
|
||||
db.close()
|
||||
return full_roster
|
||||
|
||||
|
||||
@router.patch('/{team_id}')
|
||||
async def patch_team(
|
||||
team_id: int, manager1_id: Optional[int] = None, manager2_id: Optional[int] = None, gmid: Optional[int] = None,
|
||||
gmid2: Optional[int] = None, mascot: Optional[str] = None, stadium: Optional[str] = None,
|
||||
thumbnail: Optional[str] = None, color: Optional[str] = None, abbrev: Optional[str] = None,
|
||||
sname: Optional[str] = None, lname: Optional[str] = None, dice_color: Optional[str] = None,
|
||||
division_id: Optional[int] = None, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_team - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_team = Team.get_or_none(Team.id == team_id)
|
||||
if not this_team:
|
||||
return None
|
||||
|
||||
if abbrev is not None:
|
||||
this_team.abbrev = abbrev
|
||||
if manager1_id is not None:
|
||||
if manager1_id == 0:
|
||||
this_team.manager1 = None
|
||||
else:
|
||||
this_manager = Manager.get_or_none(Manager.id == manager1_id)
|
||||
if not this_manager:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Manager ID {manager1_id} not found')
|
||||
this_team.manager1 = this_manager
|
||||
if manager2_id is not None:
|
||||
if manager2_id == 0:
|
||||
this_team.manager2 = None
|
||||
else:
|
||||
this_manager = Manager.get_or_none(Manager.id == manager2_id)
|
||||
if not this_manager:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Manager ID {manager2_id} not found')
|
||||
this_team.manager2 = this_manager
|
||||
if gmid is not None:
|
||||
this_team.gmid = gmid
|
||||
if gmid2 is not None:
|
||||
if gmid2 == 0:
|
||||
this_team.gmid2 = None
|
||||
else:
|
||||
this_team.gmid2 = gmid2
|
||||
if mascot is not None:
|
||||
if mascot == 'False':
|
||||
this_team.mascot = None
|
||||
else:
|
||||
this_team.mascot = mascot
|
||||
if stadium is not None:
|
||||
this_team.stadium = stadium
|
||||
if thumbnail is not None:
|
||||
this_team.thumbnail = thumbnail
|
||||
if color is not None:
|
||||
this_team.color = color
|
||||
if dice_color is not None:
|
||||
this_team.dice_color = dice_color
|
||||
if sname is not None:
|
||||
this_team.sname = sname
|
||||
if lname is not None:
|
||||
this_team.lname = lname
|
||||
if division_id is not None:
|
||||
if division_id == 0:
|
||||
this_team.division = None
|
||||
else:
|
||||
this_division = Division.get_or_none(Division.id == division_id)
|
||||
if not this_division:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Division ID {division_id} not found')
|
||||
this_team.division = this_division
|
||||
|
||||
if this_team.save():
|
||||
r_team = model_to_dict(this_team)
|
||||
db.close()
|
||||
return r_team
|
||||
else:
|
||||
db.close()
|
||||
raise HTTPException(status_code=500, detail=f'Unable to patch team {team_id}')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_team(team_list: TeamList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_team - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
new_teams = []
|
||||
for team in team_list.teams:
|
||||
dupe_team = Team.get_or_none(Team.season == team.season, Team.abbrev == team.abbrev)
|
||||
if dupe_team:
|
||||
db.close()
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f'Team Abbrev {team.abbrev} already in use in Season {team.season}'
|
||||
)
|
||||
|
||||
if team.manager1_id and not Manager.get_or_none(Manager.id == team.manager1_id):
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Manager ID {team.manager1_id} not found')
|
||||
|
||||
if team.manager2_id and not Manager.get_or_none(Manager.id == team.manager2_id):
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Manager ID {team.manager2_id} not found')
|
||||
|
||||
if team.division_id and not Division.get_or_none(Division.id == team.division_id):
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Division ID {team.division_id} not found')
|
||||
|
||||
new_teams.append(team.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(new_teams, 15):
|
||||
Team.insert_many(batch).on_conflict_replace().execute()
|
||||
db.close()
|
||||
|
||||
return f'Inserted {len(new_teams)} teams'
|
||||
|
||||
|
||||
@router.get('/{team_id}')
|
||||
async def delete_team(team_id: int, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'delete_team - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
this_team = Team.get_or_none(Team.id == team_id)
|
||||
if not this_team:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {team_id} not found')
|
||||
|
||||
count = this_team.delete_instance()
|
||||
db.close()
|
||||
|
||||
if count == 1:
|
||||
return f'Team {team_id} has been deleted'
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail=f'Team {team_id} could not be deleted')
|
||||
|
||||
153
app/routers_v3/transactions.py
Normal file
153
app/routers_v3/transactions.py
Normal file
@ -0,0 +1,153 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from typing import List, Optional
|
||||
from pandas import DataFrame
|
||||
import logging
|
||||
import pydantic
|
||||
|
||||
from ..db_engine import db, Transaction, Team, Player, model_to_dict, chunked, fn
|
||||
from ..dependencies import oauth2_scheme, valid_token
|
||||
|
||||
router = APIRouter(
|
||||
prefix='/api/v3/transactions',
|
||||
tags=['transactions']
|
||||
)
|
||||
|
||||
|
||||
class TransactionModel(pydantic.BaseModel):
|
||||
week: int
|
||||
player_id: int
|
||||
oldteam_id: int
|
||||
newteam_id: int
|
||||
season: int
|
||||
moveid: str
|
||||
cancelled: Optional[bool] = False
|
||||
frozen: Optional[bool] = False
|
||||
|
||||
|
||||
class TransactionList(pydantic.BaseModel):
|
||||
count: int
|
||||
moves: List[TransactionModel]
|
||||
|
||||
|
||||
@router.get('')
|
||||
async def get_transactions(
|
||||
season, team_abbrev: list = Query(default=None), week_start: Optional[int] = 0,
|
||||
week_end: Optional[int] = None, cancelled: Optional[bool] = None, frozen: Optional[bool] = None,
|
||||
player_name: list = Query(default=None), player_id: list = Query(default=None), move_id: Optional[str] = None,
|
||||
is_trade: Optional[bool] = None, short_output: Optional[bool] = False):
|
||||
if season:
|
||||
transactions = Transaction.select_season(season)
|
||||
else:
|
||||
transactions = Transaction.select()
|
||||
|
||||
if team_abbrev is not None:
|
||||
t_list = [x.upper() for x in team_abbrev]
|
||||
these_teams = Team.select().where((Team.abbrev << t_list))
|
||||
transactions = transactions.where(
|
||||
(Transaction.newteam << these_teams) | (Transaction.oldteam << these_teams)
|
||||
)
|
||||
if week_start is not None:
|
||||
transactions = transactions.where(Transaction.week >= week_start)
|
||||
if week_end is not None:
|
||||
transactions = transactions.where(Transaction.week <= week_end)
|
||||
if move_id:
|
||||
transactions = transactions.where(Transaction.moveid == move_id)
|
||||
if player_id or player_name:
|
||||
if player_id:
|
||||
p_list = Player.select().where(Player.id << player_id)
|
||||
transactions = transactions.where(Transaction.player << p_list)
|
||||
else:
|
||||
p_list = [x.lower() for x in player_name]
|
||||
these_players = Player.select().where(fn.Lower(Player.name) << p_list)
|
||||
transactions = transactions.where(Transaction.player << these_players)
|
||||
|
||||
if cancelled:
|
||||
transactions = transactions.where(Transaction.cancelled == 1)
|
||||
else:
|
||||
transactions = transactions.where(Transaction.cancelled == 0)
|
||||
|
||||
if frozen:
|
||||
transactions = transactions.where(Transaction.frozen == 1)
|
||||
else:
|
||||
transactions = transactions.where(Transaction.frozen == 0)
|
||||
|
||||
if is_trade is not None:
|
||||
raise HTTPException(status_code=501, detail='The is_trade parameter is not implemented, yet')
|
||||
|
||||
transactions = transactions.order_by(-Transaction.week, Transaction.moveid)
|
||||
|
||||
return_trans = {
|
||||
'count': transactions.count(),
|
||||
'transactions': [model_to_dict(x, recurse=not short_output) for x in transactions]
|
||||
}
|
||||
|
||||
db.close()
|
||||
return return_trans
|
||||
|
||||
|
||||
@router.patch('/{move_id}')
|
||||
async def patch_transactions(
|
||||
move_id, token: str = Depends(oauth2_scheme), frozen: Optional[bool] = None, cancelled: Optional[bool] = None):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'patch_transactions - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
these_moves = Transaction.select().where(Transaction.moveid == move_id)
|
||||
if these_moves.count() == 0:
|
||||
db.close()
|
||||
raise HTTPException(status_code=404, detail=f'Move ID {move_id} not found')
|
||||
|
||||
if frozen is not None:
|
||||
for x in these_moves:
|
||||
x.frozen = frozen
|
||||
x.save()
|
||||
if cancelled is not None:
|
||||
for x in these_moves:
|
||||
x.cancelled = cancelled
|
||||
x.save()
|
||||
|
||||
db.close()
|
||||
raise HTTPException(status_code=200, detail=f'Updated {these_moves.count()} transactions')
|
||||
|
||||
|
||||
@router.post('')
|
||||
async def post_transactions(moves: TransactionList, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'post_transactions - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
all_moves = []
|
||||
|
||||
for x in moves.moves:
|
||||
if Team.get_or_none(Team.id == x.oldteam_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.oldteam_id} not found')
|
||||
if Team.get_or_none(Team.id == x.newteam_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Team ID {x.newteam_id} not found')
|
||||
if Player.get_or_none(Player.id == x.player_id) is None:
|
||||
raise HTTPException(status_code=404, detail=f'Player ID {x.player_id} not found')
|
||||
|
||||
all_moves.append(x.dict())
|
||||
|
||||
with db.atomic():
|
||||
for batch in chunked(all_moves, 15):
|
||||
Transaction.insert_many(batch).on_conflict_replace().execute()
|
||||
|
||||
db.close()
|
||||
raise HTTPException(status_code=200, detail=f'{len(all_moves)} transactions have been added')
|
||||
|
||||
|
||||
@router.delete('/{move_id}')
|
||||
async def delete_transactions(move_id, token: str = Depends(oauth2_scheme)):
|
||||
if not valid_token(token):
|
||||
logging.warning(f'delete_transactions - Bad Token: {token}')
|
||||
raise HTTPException(status_code=401, detail='Unauthorized')
|
||||
|
||||
delete_query = Transaction.delete().where(Transaction.moveid == move_id)
|
||||
|
||||
count = delete_query.execute()
|
||||
db.close()
|
||||
if count > 0:
|
||||
raise HTTPException(status_code=200, detail=f'Removed {count} transactions')
|
||||
else:
|
||||
raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; '
|
||||
f'I did not delete any records')
|
||||
2
main.py
2
main.py
@ -1539,7 +1539,7 @@ async def v1_players_patch(
|
||||
raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')
|
||||
if name:
|
||||
this_player.name = name
|
||||
if wara:
|
||||
if wara is not None:
|
||||
this_player.wara = wara
|
||||
if image:
|
||||
this_player.image = image
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
fastapi==0.61.1
|
||||
uvicorn==0.12.2
|
||||
fastapi
|
||||
uvicorn
|
||||
peewee==3.13.3
|
||||
python-multipart
|
||||
pandas
|
||||
|
||||
Loading…
Reference in New Issue
Block a user