fix: standardize all collection POST routes to use trailing slash #61

Merged
cal merged 1 commits from fix/standardize-post-trailing-slashes into main 2026-03-10 00:41:22 +00:00
20 changed files with 1717 additions and 1212 deletions

View File

@ -4,15 +4,17 @@ 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, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/awards',
tags=['awards']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/awards", tags=["awards"])
class AwardModel(pydantic.BaseModel):
name: str
@ -30,13 +32,18 @@ class AwardList(pydantic.BaseModel):
awards: List[AwardModel]
@router.get('')
@router.get("")
@handle_db_errors
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,
player_name: list = Query(default=None)):
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,
player_name: list = Query(default=None),
):
all_awards = Award.select()
if name is not None:
@ -61,39 +68,47 @@ async def get_awards(
all_awards = all_awards.where(Award.player << all_players)
return_awards = {
'count': all_awards.count(),
'awards': [model_to_dict(x, recurse=not short_output) for x in all_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}')
@router.get("/{award_id}")
@handle_db_errors
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')
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}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{award_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
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)):
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):
logger.warning(f'patch_player - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
raise HTTPException(status_code=404, detail=f"Award ID {award_id} not found")
if name is not None:
this_award.name = name
@ -118,26 +133,43 @@ async def patch_award(
return r_award
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch award {award_id}')
raise HTTPException(status_code=500, detail=f"Unable to patch award {award_id}")
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_award(award_list: AwardList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'patch_player - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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.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')
raise HTTPException(
status_code=404, detail=f"Team ID {x.team_id} not found"
)
new_awards.append(x.dict())
@ -146,29 +178,27 @@ async def post_award(award_list: AwardList, token: str = Depends(oauth2_scheme))
Award.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Inserted {len(new_awards)} awards'
return f"Inserted {len(new_awards)} awards"
@router.delete('/{award_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{award_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_award(award_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'patch_player - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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'
return f"Award {award_id} has been deleted"
else:
raise HTTPException(status_code=500, detail=f'Award {award_id} could not be deleted')
raise HTTPException(
status_code=500, detail=f"Award {award_id} could not be deleted"
)

View File

@ -3,15 +3,27 @@ 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, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/battingstats',
tags=['battingstats']
from ..db_engine import (
db,
BattingStat,
Team,
Player,
Current,
model_to_dict,
chunked,
fn,
per_season_weeks,
)
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/battingstats", tags=["battingstats"])
class BatStatModel(pydantic.BaseModel):
@ -60,29 +72,37 @@ class BatStatList(pydantic.BaseModel):
stats: List[BatStatModel]
@router.get('')
@router.get("")
@handle_db_errors
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():
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']:
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': []}
return {"count": 0, "stats": []}
else:
all_stats = BattingStat.regular_season(season)
if all_stats.count() == 0:
db.close()
return {'count': 0, 'stats': []}
return {"count": 0, "stats": []}
if position is not None:
all_stats = all_stats.where(BattingStat.pos << [x.upper() for x in position])
@ -93,7 +113,9 @@ async def get_batstats(
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])
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)
@ -108,21 +130,19 @@ async def get_batstats(
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)
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':
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],
"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]
}
@ -130,50 +150,80 @@ async def get_batstats(
return return_stats
@router.get('/totals')
@router.get("/totals")
@handle_db_errors
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)):
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.')
raise HTTPException(
status_code=400,
detail=f"Only one of s_type, week_start/week_end, or week may be used.",
)
# Build SELECT fields conditionally based on group_by to match GROUP BY exactly
select_fields = []
if group_by == 'player':
if group_by == "player":
select_fields = [BattingStat.player]
elif group_by == 'team':
elif group_by == "team":
select_fields = [BattingStat.team]
elif group_by == 'playerteam':
elif group_by == "playerteam":
select_fields = [BattingStat.player, BattingStat.team]
else:
# Default case
select_fields = [BattingStat.player]
all_stats = (
BattingStat
.select(*select_fields,
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.select(
*select_fields,
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"),
)
.where(BattingStat.season == season)
.having(fn.SUM(BattingStat.pa) >= min_pa)
)
@ -185,16 +235,20 @@ async def get_totalstats(
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.'
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",
)
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
weeks["end"] = week_end
all_stats = all_stats.where(
(BattingStat.week >= weeks['start']) & (BattingStat.week <= weeks['end'])
(BattingStat.week >= weeks["start"]) & (BattingStat.week <= weeks["end"])
)
elif week is not None:
all_stats = all_stats.where(BattingStat.week << week)
@ -204,14 +258,20 @@ async def get_totalstats(
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)
(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':
if sort == "player":
all_stats = all_stats.order_by(BattingStat.player)
elif sort == 'team':
elif sort == "team":
all_stats = all_stats.order_by(BattingStat.team)
if group_by is not None:
# Use the same fields for GROUP BY as we used for SELECT
@ -227,56 +287,63 @@ async def get_totalstats(
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_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_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': []
}
return_stats = {"count": all_stats.count(), "stats": []}
for x in all_stats:
# Handle player field based on grouping with safe access
this_player = 'TOT'
if 'player' in group_by and hasattr(x, 'player'):
this_player = x.player_id if short_output else model_to_dict(x.player, recurse=False)
this_player = "TOT"
if "player" in group_by and hasattr(x, "player"):
this_player = (
x.player_id if short_output else model_to_dict(x.player, recurse=False)
)
# Handle team field based on grouping with safe access
this_team = 'TOT'
if 'team' in group_by and hasattr(x, 'team'):
this_team = x.team_id if short_output else model_to_dict(x.team, recurse=False)
this_team = "TOT"
if "team" in group_by and hasattr(x, "team"):
this_team = (
x.team_id if short_output else model_to_dict(x.team, recurse=False)
)
return_stats['stats'].append({
'player': this_player,
'team': this_team,
'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
})
return_stats["stats"].append(
{
"player": this_player,
"team": this_team,
"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,
}
)
db.close()
return return_stats
@ -287,15 +354,17 @@ async def get_totalstats(
# pass # Keep Career Stats table and recalculate after posting stats
@router.patch('/{stat_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{stat_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_batstats(stat_id: int, new_stats: BatStatModel, token: str = Depends(oauth2_scheme)):
async def patch_batstats(
stat_id: int, new_stats: BatStatModel, token: str = Depends(oauth2_scheme)
):
if not valid_token(token):
logger.warning(f'patch_batstats - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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))
@ -303,12 +372,12 @@ async def patch_batstats(stat_id: int, new_stats: BatStatModel, token: str = Dep
return r_stat
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_batstats(s_list: BatStatList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_batstats - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"post_batstats - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
all_stats = []
@ -316,9 +385,13 @@ async def post_batstats(s_list: BatStatList, token: str = Depends(oauth2_scheme)
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')
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')
raise HTTPException(
status_code=404, detail=f"Player ID {x.player_id} not found"
)
all_stats.append(BattingStat(**x.dict()))
@ -329,4 +402,4 @@ async def post_batstats(s_list: BatStatList, token: str = Depends(oauth2_scheme)
# Update career stats
db.close()
return f'Added {len(all_stats)} batting lines'
return f"Added {len(all_stats)} batting lines"

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, Current, model_to_dict
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/current',
tags=['current']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/current", tags=["current"])
class CurrentModel(pydantic.BaseModel):
week: Optional[int] = 0
@ -29,7 +31,7 @@ class CurrentModel(pydantic.BaseModel):
injury_count: Optional[int] = 0
@router.get('')
@router.get("")
@handle_db_errors
async def get_current(season: Optional[int] = None):
if season is not None:
@ -45,21 +47,33 @@ async def get_current(season: Optional[int] = None):
return None
@router.patch('/{current_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{current_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
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)):
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):
logger.warning(f'patch_current - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
raise HTTPException(
status_code=404, detail=f"Current id {current_id} not found"
)
if week is not None:
current.week = week
@ -90,15 +104,17 @@ async def patch_current(
return r_curr
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch current {current_id}')
raise HTTPException(
status_code=500, detail=f"Unable to patch current {current_id}"
)
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_current(new_current: CurrentModel, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'patch_current - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"patch_current - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_current = Current(**new_current.dict())
@ -108,17 +124,22 @@ async def post_current(new_current: CurrentModel, token: str = Depends(oauth2_sc
return r_curr
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to post season {new_current.season} current')
raise HTTPException(
status_code=500,
detail=f"Unable to post season {new_current.season} current",
)
@router.delete('/{current_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{current_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_current(current_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'patch_current - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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}'
return f"Deleted current ID {current_id}"
raise HTTPException(status_code=500, detail=f'Unable to delete current {current_id}')
raise HTTPException(
status_code=500, detail=f"Unable to delete current {current_id}"
)

File diff suppressed because it is too large Load Diff

View File

@ -4,15 +4,26 @@ import copy
import logging
import pydantic
from ..db_engine import db, Decision, StratGame, Player, model_to_dict, chunked, fn, Team
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/decisions',
tags=['decisions']
from ..db_engine import (
db,
Decision,
StratGame,
Player,
model_to_dict,
chunked,
fn,
Team,
)
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/decisions", tags=["decisions"])
class DecisionModel(pydantic.BaseModel):
@ -43,17 +54,31 @@ class DecisionReturnList(pydantic.BaseModel):
decisions: list[DecisionModel]
@router.get('')
@router.get("")
@handle_db_errors
async def get_decisions(
season: list = Query(default=None), week: list = Query(default=None), game_num: list = Query(default=None),
s_type: Literal['regular', 'post', 'all', None] = None, team_id: list = Query(default=None),
week_start: Optional[int] = None, week_end: Optional[int] = None, win: Optional[int] = None,
loss: Optional[int] = None, hold: Optional[int] = None, save: Optional[int] = None,
b_save: Optional[int] = None, irunners: list = Query(default=None), irunners_scored: list = Query(default=None),
game_id: list = Query(default=None), player_id: list = Query(default=None),
limit: Optional[int] = None, short_output: Optional[bool] = False):
all_dec = Decision.select().order_by(-Decision.season, -Decision.week, -Decision.game_num)
season: list = Query(default=None),
week: list = Query(default=None),
game_num: list = Query(default=None),
s_type: Literal["regular", "post", "all", None] = None,
team_id: list = Query(default=None),
week_start: Optional[int] = None,
week_end: Optional[int] = None,
win: Optional[int] = None,
loss: Optional[int] = None,
hold: Optional[int] = None,
save: Optional[int] = None,
b_save: Optional[int] = None,
irunners: list = Query(default=None),
irunners_scored: list = Query(default=None),
game_id: list = Query(default=None),
player_id: list = Query(default=None),
limit: Optional[int] = None,
short_output: Optional[bool] = False,
):
all_dec = Decision.select().order_by(
-Decision.season, -Decision.week, -Decision.game_num
)
if season is not None:
all_dec = all_dec.where(Decision.season << season)
@ -79,7 +104,8 @@ async def get_decisions(
if season is not None and 8 in season or s8_teams:
all_teams = Team.select().where(Team.id << team_id)
all_games = StratGame.select().where(
(StratGame.away_team << all_teams) | (StratGame.home_team << all_teams))
(StratGame.away_team << all_teams) | (StratGame.home_team << all_teams)
)
all_dec = all_dec.where(Decision.game << all_games)
else:
all_teams = Team.select().where(Team.id << team_id)
@ -115,28 +141,38 @@ async def get_decisions(
all_dec = all_dec.limit(limit)
return_dec = {
'count': all_dec.count(),
'decisions': [model_to_dict(x, recurse=not short_output) for x in all_dec]
"count": all_dec.count(),
"decisions": [model_to_dict(x, recurse=not short_output) for x in all_dec],
}
db.close()
return return_dec
@router.patch('/{decision_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{decision_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_decision(
decision_id: int, win: Optional[int] = None, loss: Optional[int] = None, hold: Optional[int] = None,
save: Optional[int] = None, b_save: Optional[int] = None, irunners: Optional[int] = None,
irunners_scored: Optional[int] = None, rest_ip: Optional[int] = None, rest_required: Optional[int] = None,
token: str = Depends(oauth2_scheme)):
decision_id: int,
win: Optional[int] = None,
loss: Optional[int] = None,
hold: Optional[int] = None,
save: Optional[int] = None,
b_save: Optional[int] = None,
irunners: Optional[int] = None,
irunners_scored: Optional[int] = None,
rest_ip: Optional[int] = None,
rest_required: Optional[int] = None,
token: str = Depends(oauth2_scheme),
):
if not valid_token(token):
logger.warning(f'patch_decision - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"patch_decision - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_dec = Decision.get_or_none(Decision.id == decision_id)
if this_dec is None:
db.close()
raise HTTPException(status_code=404, detail=f'Decision ID {decision_id} not found')
raise HTTPException(
status_code=404, detail=f"Decision ID {decision_id} not found"
)
if win is not None:
this_dec.win = win
@ -163,22 +199,28 @@ async def patch_decision(
return d_result
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch decision {decision_id}')
raise HTTPException(
status_code=500, detail=f"Unable to patch decision {decision_id}"
)
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_decisions(dec_list: DecisionList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_decisions - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"post_decisions - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
new_dec = []
for x in dec_list.decisions:
if StratGame.get_or_none(StratGame.id == x.game_id) is None:
raise HTTPException(status_code=404, detail=f'Game ID {x.game_id} not found')
raise HTTPException(
status_code=404, detail=f"Game ID {x.game_id} not found"
)
if Player.get_or_none(Player.id == x.pitcher_id) is None:
raise HTTPException(status_code=404, detail=f'Player ID {x.pitcher_id} not found')
raise HTTPException(
status_code=404, detail=f"Player ID {x.pitcher_id} not found"
)
new_dec.append(x.dict())
@ -187,49 +229,53 @@ async def post_decisions(dec_list: DecisionList, token: str = Depends(oauth2_sch
Decision.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Inserted {len(new_dec)} decisions'
return f"Inserted {len(new_dec)} decisions"
@router.delete('/{decision_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{decision_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_decision(decision_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_decision - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"delete_decision - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_dec = Decision.get_or_none(Decision.id == decision_id)
if this_dec is None:
db.close()
raise HTTPException(status_code=404, detail=f'Decision ID {decision_id} not found')
raise HTTPException(
status_code=404, detail=f"Decision ID {decision_id} not found"
)
count = this_dec.delete_instance()
db.close()
if count == 1:
return f'Decision {decision_id} has been deleted'
return f"Decision {decision_id} has been deleted"
else:
raise HTTPException(status_code=500, detail=f'Decision {decision_id} could not be deleted')
raise HTTPException(
status_code=500, detail=f"Decision {decision_id} could not be deleted"
)
@router.delete('/game/{game_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/game/{game_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_decisions_game(game_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_decisions_game - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"delete_decisions_game - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_game = StratGame.get_or_none(StratGame.id == game_id)
if not this_game:
db.close()
raise HTTPException(status_code=404, detail=f'Game ID {game_id} not found')
raise HTTPException(status_code=404, detail=f"Game ID {game_id} not found")
count = Decision.delete().where(Decision.game == this_game).execute()
db.close()
if count > 0:
return f'Deleted {count} decisions matching Game ID {game_id}'
return f"Deleted {count} decisions matching Game ID {game_id}"
else:
raise HTTPException(status_code=500, detail=f'No decisions matching Game ID {game_id} were deleted')
raise HTTPException(
status_code=500,
detail=f"No decisions matching Game ID {game_id} were deleted",
)

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, Division, Team, model_to_dict, chunked, fn
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/divisions',
tags=['divisions']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/divisions", tags=["divisions"])
class DivisionModel(pydantic.BaseModel):
division_name: str
@ -22,11 +24,15 @@ class DivisionModel(pydantic.BaseModel):
season: int = 0
@router.get('')
@router.get("")
@handle_db_errors
async def get_divisions(
season: int, div_name: Optional[str] = None, div_abbrev: Optional[str] = None, lg_name: Optional[str] = None,
lg_abbrev: Optional[str] = None):
season: int,
div_name: Optional[str] = None,
div_abbrev: Optional[str] = None,
lg_name: Optional[str] = None,
lg_abbrev: Optional[str] = None,
):
all_divisions = Division.select().where(Division.season == season)
if div_name is not None:
@ -39,39 +45,48 @@ async def get_divisions(
all_divisions = all_divisions.where(Division.league_abbrev == lg_abbrev)
return_div = {
'count': all_divisions.count(),
'divisions': [model_to_dict(x) for x in all_divisions]
"count": all_divisions.count(),
"divisions": [model_to_dict(x) for x in all_divisions],
}
db.close()
return return_div
@router.get('/{division_id}')
@router.get("/{division_id}")
@handle_db_errors
async def get_one_division(division_id: int):
this_div = Division.get_or_none(Division.id == division_id)
if this_div is None:
db.close()
raise HTTPException(status_code=404, detail=f'Division ID {division_id} not found')
raise HTTPException(
status_code=404, detail=f"Division ID {division_id} not found"
)
r_div = model_to_dict(this_div)
db.close()
return r_div
@router.patch('/{division_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{division_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_division(
division_id: int, div_name: Optional[str] = None, div_abbrev: Optional[str] = None,
lg_name: Optional[str] = None, lg_abbrev: Optional[str] = None, token: str = Depends(oauth2_scheme)):
division_id: int,
div_name: Optional[str] = None,
div_abbrev: Optional[str] = None,
lg_name: Optional[str] = None,
lg_abbrev: Optional[str] = None,
token: str = Depends(oauth2_scheme),
):
if not valid_token(token):
logger.warning(f'patch_division - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"patch_division - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_div = Division.get_or_none(Division.id == division_id)
if this_div is None:
db.close()
raise HTTPException(status_code=404, detail=f'Division ID {division_id} not found')
raise HTTPException(
status_code=404, detail=f"Division ID {division_id} not found"
)
if div_name is not None:
this_div.division_name = div_name
@ -88,15 +103,19 @@ async def patch_division(
return r_division
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch division {division_id}')
raise HTTPException(
status_code=500, detail=f"Unable to patch division {division_id}"
)
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_division(new_division: DivisionModel, token: str = Depends(oauth2_scheme)):
async def post_division(
new_division: DivisionModel, token: str = Depends(oauth2_scheme)
):
if not valid_token(token):
logger.warning(f'post_division - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"post_division - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_division = Division(**new_division.dict())
@ -106,27 +125,29 @@ async def post_division(new_division: DivisionModel, token: str = Depends(oauth2
return r_division
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to post division')
raise HTTPException(status_code=500, detail=f"Unable to post division")
@router.delete('/{division_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{division_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_division(division_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_division - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"delete_division - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_div = Division.get_or_none(Division.id == division_id)
if this_div is None:
db.close()
raise HTTPException(status_code=404, detail=f'Division ID {division_id} not found')
raise HTTPException(
status_code=404, detail=f"Division ID {division_id} not found"
)
count = this_div.delete_instance()
db.close()
if count == 1:
return f'Division {division_id} has been deleted'
return f"Division {division_id} has been deleted"
else:
raise HTTPException(status_code=500, detail=f'Division {division_id} could not be deleted')
raise HTTPException(
status_code=500, detail=f"Division {division_id} could not be deleted"
)

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, DraftList, Team, model_to_dict, chunked
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/draftlist',
tags=['draftlist']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/draftlist", tags=["draftlist"])
class DraftListModel(pydantic.BaseModel):
season: int
@ -26,13 +28,16 @@ class DraftListList(pydantic.BaseModel):
draft_list: List[DraftListModel]
@router.get('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.get("", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def get_draftlist(
season: Optional[int], team_id: list = Query(default=None), token: str = Depends(oauth2_scheme)):
season: Optional[int],
team_id: list = Query(default=None),
token: str = Depends(oauth2_scheme),
):
if not valid_token(token):
logger.warning(f'get_draftlist - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"get_draftlist - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
all_list = DraftList.select()
@ -41,47 +46,49 @@ async def get_draftlist(
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]
}
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}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.get("/team/{team_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def get_team_draftlist(team_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_draftlist - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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]
"count": this_list.count(),
"picks": [model_to_dict(x) for x in this_list],
}
db.close()
return r_list
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_draftlist(draft_list: DraftListList, token: str = Depends(oauth2_scheme)):
async def post_draftlist(
draft_list: DraftListList, token: str = Depends(oauth2_scheme)
):
if not valid_token(token):
logger.warning(f'post_draftlist - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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()
@ -93,16 +100,16 @@ async def post_draftlist(draft_list: DraftListList, token: str = Depends(oauth2_
DraftList.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Inserted {len(new_list)} list values'
return f"Inserted {len(new_list)} list values"
@router.delete('/team/{team_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/team/{team_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_draftlist(team_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_draftlist - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"delete_draftlist - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
count = DraftList.delete().where(DraftList.team_id == team_id).execute()
db.close()
return f'Deleted {count} list values'
return f"Deleted {count} list values"

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, DraftPick, Team, model_to_dict, chunked
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/draftpicks',
tags=['draftpicks']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/draftpicks", tags=["draftpicks"])
class DraftPickModel(pydantic.BaseModel):
overall: Optional[int] = None
@ -32,15 +34,26 @@ class DraftPickReturnList(pydantic.BaseModel):
picks: list[DraftPickModel]
@router.get('')
@router.get("")
@handle_db_errors
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):
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:
@ -61,16 +74,25 @@ async def get_picks(
if owner_team_id is not None:
all_picks = all_picks.where(
(DraftPick.owner_id << owner_team_id) | (DraftPick.owner_id << owner_team_id)
(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)
(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 (
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)
@ -92,40 +114,42 @@ async def get_picks(
all_picks = all_picks.limit(limit)
if sort is not None:
if sort == 'order-asc':
if sort == "order-asc":
all_picks = all_picks.order_by(DraftPick.overall)
elif sort == 'order-desc':
elif sort == "order-desc":
all_picks = all_picks.order_by(-DraftPick.overall)
return_picks = {'count': all_picks.count(), 'picks': []}
return_picks = {"count": all_picks.count(), "picks": []}
for line in all_picks:
return_picks['picks'].append(model_to_dict(line, recurse=not short_output))
return_picks["picks"].append(model_to_dict(line, recurse=not short_output))
db.close()
return return_picks
@router.get('/{pick_id}')
@router.get("/{pick_id}")
@handle_db_errors
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')
raise HTTPException(status_code=404, detail=f"Pick ID {pick_id} not found")
db.close()
return r_pick
@router.patch('/{pick_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{pick_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_pick(pick_id: int, new_pick: DraftPickModel, token: str = Depends(oauth2_scheme)):
async def patch_pick(
pick_id: int, new_pick: DraftPickModel, token: str = Depends(oauth2_scheme)
):
if not valid_token(token):
logger.warning(f'patch_pick - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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))
@ -133,21 +157,23 @@ async def patch_pick(pick_id: int, new_pick: DraftPickModel, token: str = Depend
return r_pick
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_picks(p_list: DraftPickList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_picks - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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)
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}'
detail=f"Pick # {pick.overall} already exists for season {pick.season}",
)
new_picks.append(pick.dict())
@ -157,25 +183,26 @@ async def post_picks(p_list: DraftPickList, token: str = Depends(oauth2_scheme))
DraftPick.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Inserted {len(new_picks)} picks'
return f"Inserted {len(new_picks)} picks"
@router.delete('/{pick_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{pick_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_pick(pick_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_pick - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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'
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')
raise HTTPException(
status_code=500, detail=f"Draft pick {pick_id} could not be deleted"
)

View File

@ -6,15 +6,17 @@ from pydantic import BaseModel, Field
from playhouse.shortcuts import model_to_dict
from peewee import fn
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
from ..db_engine import db, HelpCommand
logger = logging.getLogger('database_api')
logger = logging.getLogger("database_api")
router = APIRouter(
prefix='/api/v3/help_commands',
tags=['help_commands']
)
router = APIRouter(prefix="/api/v3/help_commands", tags=["help_commands"])
# Pydantic Models for API
@ -52,15 +54,16 @@ class HelpCommandStatsResponse(BaseModel):
# API Endpoints
@router.get('')
@router.get("")
@handle_db_errors
async def get_help_commands(
name: Optional[str] = None,
category: Optional[str] = None,
is_active: Optional[bool] = True,
sort: Optional[str] = 'name',
sort: Optional[str] = "name",
page: int = Query(1, ge=1),
page_size: int = Query(25, ge=1, le=100)
page_size: int = Query(25, ge=1, le=100),
):
"""Get help commands with filtering and pagination"""
try:
@ -82,16 +85,16 @@ async def get_help_commands(
# Apply sorting
sort_mapping = {
'name': HelpCommand.name,
'title': HelpCommand.title,
'category': HelpCommand.category,
'created_at': HelpCommand.created_at,
'updated_at': HelpCommand.updated_at,
'view_count': HelpCommand.view_count,
'display_order': HelpCommand.display_order
"name": HelpCommand.name,
"title": HelpCommand.title,
"category": HelpCommand.category,
"created_at": HelpCommand.created_at,
"updated_at": HelpCommand.updated_at,
"view_count": HelpCommand.view_count,
"display_order": HelpCommand.display_order,
}
if sort.startswith('-'):
if sort.startswith("-"):
sort_field = sort[1:]
order_by = sort_mapping.get(sort_field, HelpCommand.name).desc()
else:
@ -111,10 +114,10 @@ async def get_help_commands(
for help_cmd in query:
cmd_dict = model_to_dict(help_cmd)
# Convert datetime objects to ISO strings
if cmd_dict.get('created_at'):
cmd_dict['created_at'] = cmd_dict['created_at'].isoformat()
if cmd_dict.get('updated_at'):
cmd_dict['updated_at'] = cmd_dict['updated_at'].isoformat()
if cmd_dict.get("created_at"):
cmd_dict["created_at"] = cmd_dict["created_at"].isoformat()
if cmd_dict.get("updated_at"):
cmd_dict["updated_at"] = cmd_dict["updated_at"].isoformat()
try:
command_model = HelpCommandModel(**cmd_dict)
@ -129,7 +132,7 @@ async def get_help_commands(
page=page,
page_size=page_size,
total_pages=total_pages,
has_more=page < total_pages
has_more=page < total_pages,
)
except Exception as e:
@ -139,22 +142,23 @@ async def get_help_commands(
db.close()
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def create_help_command_endpoint(
command: HelpCommandModel,
token: str = Depends(oauth2_scheme)
command: HelpCommandModel, token: str = Depends(oauth2_scheme)
):
"""Create a new help command"""
if not valid_token(token):
logger.warning(f'create_help_command - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"create_help_command - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
try:
# Check if command name already exists
existing = HelpCommand.get_by_name(command.name)
if existing:
raise HTTPException(status_code=409, detail=f"Help topic '{command.name}' already exists")
raise HTTPException(
status_code=409, detail=f"Help topic '{command.name}' already exists"
)
# Create the command
help_cmd = HelpCommand.create(
@ -166,15 +170,15 @@ async def create_help_command_endpoint(
created_at=datetime.now(),
is_active=True,
view_count=0,
display_order=command.display_order
display_order=command.display_order,
)
# Return the created command
result = model_to_dict(help_cmd)
if result.get('created_at'):
result['created_at'] = result['created_at'].isoformat()
if result.get('updated_at'):
result['updated_at'] = result['updated_at'].isoformat()
if result.get("created_at"):
result["created_at"] = result["created_at"].isoformat()
if result.get("updated_at"):
result["updated_at"] = result["updated_at"].isoformat()
return result
@ -187,23 +191,23 @@ async def create_help_command_endpoint(
db.close()
@router.put('/{command_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.put("/{command_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def update_help_command_endpoint(
command_id: int,
command: HelpCommandModel,
token: str = Depends(oauth2_scheme)
command_id: int, command: HelpCommandModel, token: str = Depends(oauth2_scheme)
):
"""Update an existing help command"""
if not valid_token(token):
logger.warning(f'update_help_command - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"update_help_command - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
try:
# Get the command
help_cmd = HelpCommand.get_or_none(HelpCommand.id == command_id)
if not help_cmd:
raise HTTPException(status_code=404, detail=f"Help command {command_id} not found")
raise HTTPException(
status_code=404, detail=f"Help command {command_id} not found"
)
# Update fields
if command.title:
@ -222,10 +226,10 @@ async def update_help_command_endpoint(
# Return updated command
result = model_to_dict(help_cmd)
if result.get('created_at'):
result['created_at'] = result['created_at'].isoformat()
if result.get('updated_at'):
result['updated_at'] = result['updated_at'].isoformat()
if result.get("created_at"):
result["created_at"] = result["created_at"].isoformat()
if result.get("updated_at"):
result["updated_at"] = result["updated_at"].isoformat()
return result
@ -238,32 +242,33 @@ async def update_help_command_endpoint(
db.close()
@router.patch('/{command_id}/restore', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{command_id}/restore", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def restore_help_command_endpoint(
command_id: int,
token: str = Depends(oauth2_scheme)
command_id: int, token: str = Depends(oauth2_scheme)
):
"""Restore a soft-deleted help command"""
if not valid_token(token):
logger.warning(f'restore_help_command - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"restore_help_command - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
try:
# Get the command
help_cmd = HelpCommand.get_or_none(HelpCommand.id == command_id)
if not help_cmd:
raise HTTPException(status_code=404, detail=f"Help command {command_id} not found")
raise HTTPException(
status_code=404, detail=f"Help command {command_id} not found"
)
# Restore the command
help_cmd.restore()
# Return restored command
result = model_to_dict(help_cmd)
if result.get('created_at'):
result['created_at'] = result['created_at'].isoformat()
if result.get('updated_at'):
result['updated_at'] = result['updated_at'].isoformat()
if result.get("created_at"):
result["created_at"] = result["created_at"].isoformat()
if result.get("updated_at"):
result["updated_at"] = result["updated_at"].isoformat()
return result
@ -276,22 +281,23 @@ async def restore_help_command_endpoint(
db.close()
@router.delete('/{command_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{command_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_help_command_endpoint(
command_id: int,
token: str = Depends(oauth2_scheme)
command_id: int, token: str = Depends(oauth2_scheme)
):
"""Soft delete a help command"""
if not valid_token(token):
logger.warning(f'delete_help_command - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"delete_help_command - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
try:
# Get the command
help_cmd = HelpCommand.get_or_none(HelpCommand.id == command_id)
if not help_cmd:
raise HTTPException(status_code=404, detail=f"Help command {command_id} not found")
raise HTTPException(
status_code=404, detail=f"Help command {command_id} not found"
)
# Soft delete the command
help_cmd.soft_delete()
@ -307,44 +313,56 @@ async def delete_help_command_endpoint(
db.close()
@router.get('/stats')
@router.get("/stats")
@handle_db_errors
async def get_help_command_stats():
"""Get comprehensive statistics about help commands"""
try:
# Get basic counts
total_commands = HelpCommand.select().count()
active_commands = HelpCommand.select().where(HelpCommand.is_active == True).count()
active_commands = (
HelpCommand.select().where(HelpCommand.is_active == True).count()
)
# Get total views
total_views = HelpCommand.select(fn.SUM(HelpCommand.view_count)).where(
HelpCommand.is_active == True
).scalar() or 0
total_views = (
HelpCommand.select(fn.SUM(HelpCommand.view_count))
.where(HelpCommand.is_active == True)
.scalar()
or 0
)
# Get most viewed command
most_viewed = HelpCommand.get_most_viewed(limit=1).first()
most_viewed_command = None
if most_viewed:
most_viewed_dict = model_to_dict(most_viewed)
if most_viewed_dict.get('created_at'):
most_viewed_dict['created_at'] = most_viewed_dict['created_at'].isoformat()
if most_viewed_dict.get('updated_at'):
most_viewed_dict['updated_at'] = most_viewed_dict['updated_at'].isoformat()
if most_viewed_dict.get("created_at"):
most_viewed_dict["created_at"] = most_viewed_dict[
"created_at"
].isoformat()
if most_viewed_dict.get("updated_at"):
most_viewed_dict["updated_at"] = most_viewed_dict[
"updated_at"
].isoformat()
most_viewed_command = most_viewed_dict
# Get recent commands count (last 7 days)
week_ago = datetime.now() - timedelta(days=7)
recent_count = HelpCommand.select().where(
(HelpCommand.created_at >= week_ago) &
(HelpCommand.is_active == True)
).count()
recent_count = (
HelpCommand.select()
.where(
(HelpCommand.created_at >= week_ago) & (HelpCommand.is_active == True)
)
.count()
)
return HelpCommandStatsResponse(
total_commands=total_commands,
active_commands=active_commands,
total_views=int(total_views),
most_viewed_command=most_viewed_command,
recent_commands_count=recent_count
recent_commands_count=recent_count,
)
except Exception as e:
@ -355,24 +373,27 @@ async def get_help_command_stats():
# Special endpoints for Discord bot integration
@router.get('/by_name/{command_name}')
@router.get("/by_name/{command_name}")
@handle_db_errors
async def get_help_command_by_name_endpoint(
command_name: str,
include_inactive: bool = Query(False)
command_name: str, include_inactive: bool = Query(False)
):
"""Get a help command by name (for Discord bot)"""
try:
help_cmd = HelpCommand.get_by_name(command_name, include_inactive=include_inactive)
help_cmd = HelpCommand.get_by_name(
command_name, include_inactive=include_inactive
)
if not help_cmd:
raise HTTPException(status_code=404, detail=f"Help topic '{command_name}' not found")
raise HTTPException(
status_code=404, detail=f"Help topic '{command_name}' not found"
)
result = model_to_dict(help_cmd)
if result.get('created_at'):
result['created_at'] = result['created_at'].isoformat()
if result.get('updated_at'):
result['updated_at'] = result['updated_at'].isoformat()
if result.get("created_at"):
result["created_at"] = result["created_at"].isoformat()
if result.get("updated_at"):
result["updated_at"] = result["updated_at"].isoformat()
return result
@ -385,32 +406,31 @@ async def get_help_command_by_name_endpoint(
db.close()
@router.patch('/by_name/{command_name}/view', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/by_name/{command_name}/view", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def increment_view_count(
command_name: str,
token: str = Depends(oauth2_scheme)
):
async def increment_view_count(command_name: str, token: str = Depends(oauth2_scheme)):
"""Increment view count for a help command"""
if not valid_token(token):
logger.warning(f'increment_view_count - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"increment_view_count - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
try:
help_cmd = HelpCommand.get_by_name(command_name)
if not help_cmd:
raise HTTPException(status_code=404, detail=f"Help topic '{command_name}' not found")
raise HTTPException(
status_code=404, detail=f"Help topic '{command_name}' not found"
)
# Increment view count
help_cmd.increment_view_count()
# Return updated command
result = model_to_dict(help_cmd)
if result.get('created_at'):
result['created_at'] = result['created_at'].isoformat()
if result.get('updated_at'):
result['updated_at'] = result['updated_at'].isoformat()
if result.get("created_at"):
result["created_at"] = result["created_at"].isoformat()
if result.get("updated_at"):
result["updated_at"] = result["updated_at"].isoformat()
return result
@ -423,11 +443,10 @@ async def increment_view_count(
db.close()
@router.get('/autocomplete')
@router.get("/autocomplete")
@handle_db_errors
async def get_help_names_for_autocomplete(
q: str = Query(""),
limit: int = Query(25, ge=1, le=100)
q: str = Query(""), limit: int = Query(25, ge=1, le=100)
):
"""Get help command names for Discord autocomplete"""
try:
@ -438,11 +457,11 @@ async def get_help_names_for_autocomplete(
# Return list of dictionaries with name, title, category
return {
'results': [
"results": [
{
'name': help_cmd.name,
'title': help_cmd.title,
'category': help_cmd.category
"name": help_cmd.name,
"title": help_cmd.title,
"category": help_cmd.category,
}
for help_cmd in results
]
@ -455,7 +474,7 @@ async def get_help_names_for_autocomplete(
db.close()
@router.get('/{command_id}')
@router.get("/{command_id}")
@handle_db_errors
async def get_help_command(command_id: int):
"""Get a single help command by ID"""
@ -463,13 +482,15 @@ async def get_help_command(command_id: int):
help_cmd = HelpCommand.get_or_none(HelpCommand.id == command_id)
if not help_cmd:
raise HTTPException(status_code=404, detail=f"Help command {command_id} not found")
raise HTTPException(
status_code=404, detail=f"Help command {command_id} not found"
)
result = model_to_dict(help_cmd)
if result.get('created_at'):
result['created_at'] = result['created_at'].isoformat()
if result.get('updated_at'):
result['updated_at'] = result['updated_at'].isoformat()
if result.get("created_at"):
result["created_at"] = result["created_at"].isoformat()
if result.get("updated_at"):
result["updated_at"] = result["updated_at"].isoformat()
return result

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, Injury, Player, model_to_dict, fn
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/injuries',
tags=['injuries']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/injuries", tags=["injuries"])
class InjuryModel(pydantic.BaseModel):
season: int
@ -25,12 +27,18 @@ class InjuryModel(pydantic.BaseModel):
is_active: bool = True
@router.get('')
@router.get("")
@handle_db_errors
async def get_injuries(
season: list = Query(default=None), player_id: list = Query(default=None), min_games: int = None,
max_games: int = None, team_id: list = Query(default=None), is_active: bool = None,
short_output: bool = False, sort: Optional[str] = 'start-asc'):
season: list = Query(default=None),
player_id: list = Query(default=None),
min_games: int = None,
max_games: int = None,
team_id: list = Query(default=None),
is_active: bool = None,
short_output: bool = False,
sort: Optional[str] = "start-asc",
):
all_injuries = Injury.select()
if season is not None:
@ -47,34 +55,38 @@ async def get_injuries(
all_players = Player.select().where(Player.team_id << team_id)
all_injuries = all_injuries.where(Injury.player << all_players)
if sort == 'return-asc':
if sort == "return-asc":
all_injuries = all_injuries.order_by(Injury.end_week, Injury.end_game)
elif sort == 'return-desc':
elif sort == "return-desc":
all_injuries = all_injuries.order_by(-Injury.end_week, -Injury.end_game)
elif sort == 'start-asc':
elif sort == "start-asc":
all_injuries = all_injuries.order_by(Injury.start_week, Injury.start_game)
elif sort == 'start-desc':
elif sort == "start-desc":
all_injuries = all_injuries.order_by(-Injury.start_week, -Injury.start_game)
return_injuries = {
'count': all_injuries.count(),
'injuries': [model_to_dict(x, recurse=not short_output) for x in all_injuries]
"count": all_injuries.count(),
"injuries": [model_to_dict(x, recurse=not short_output) for x in all_injuries],
}
db.close()
return return_injuries
@router.patch('/{injury_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{injury_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_injury(injury_id: int, is_active: Optional[bool] = None, token: str = Depends(oauth2_scheme)):
async def patch_injury(
injury_id: int,
is_active: Optional[bool] = None,
token: str = Depends(oauth2_scheme),
):
if not valid_token(token):
logger.warning(f'patch_injury - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"patch_injury - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_injury = Injury.get_or_none(Injury.id == injury_id)
if this_injury is None:
db.close()
raise HTTPException(status_code=404, detail=f'Injury ID {injury_id} not found')
raise HTTPException(status_code=404, detail=f"Injury ID {injury_id} not found")
if is_active is not None:
this_injury.is_active = is_active
@ -85,15 +97,17 @@ async def patch_injury(injury_id: int, is_active: Optional[bool] = None, token:
return r_injury
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch injury {injury_id}')
raise HTTPException(
status_code=500, detail=f"Unable to patch injury {injury_id}"
)
@router.post(f'', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_injury(new_injury: InjuryModel, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_injury - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"post_injury - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_injury = Injury(**new_injury.dict())
@ -103,28 +117,27 @@ async def post_injury(new_injury: InjuryModel, token: str = Depends(oauth2_schem
return r_injury
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to post injury')
raise HTTPException(status_code=500, detail=f"Unable to post injury")
@router.delete('/{injury_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{injury_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_injury(injury_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_injury - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"delete_injury - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_injury = Injury.get_or_none(Injury.id == injury_id)
if this_injury is None:
db.close()
raise HTTPException(status_code=404, detail=f'Injury ID {injury_id} not found')
raise HTTPException(status_code=404, detail=f"Injury ID {injury_id} not found")
count = this_injury.delete_instance()
db.close()
if count == 1:
return f'Injury {injury_id} has been deleted'
return f"Injury {injury_id} has been deleted"
else:
raise HTTPException(status_code=500, detail=f'Unable to delete injury {injury_id}')
raise HTTPException(
status_code=500, detail=f"Unable to delete injury {injury_id}"
)

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, Keeper, Player, model_to_dict, chunked, fn
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/keepers',
tags=['keepers']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/keepers", tags=["keepers"])
class KeeperModel(pydantic.BaseModel):
season: int
@ -25,11 +27,14 @@ class KeeperList(pydantic.BaseModel):
keepers: List[KeeperModel]
@router.get('')
@router.get("")
@handle_db_errors
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):
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:
@ -40,25 +45,29 @@ async def get_keepers(
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]
"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}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{keeper_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
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)):
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):
logger.warning(f'patch_keeper - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
raise HTTPException(status_code=404, detail=f"Keeper ID {keeper_id} not found")
if season is not None:
this_keeper.season = season
@ -73,15 +82,17 @@ async def patch_keeper(
return r_keeper
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch keeper {keeper_id}')
raise HTTPException(
status_code=500, detail=f"Unable to patch keeper {keeper_id}"
)
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_keepers(k_list: KeeperList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_keepers - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"post_keepers - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
new_keepers = []
for keeper in k_list.keepers:
@ -92,26 +103,26 @@ async def post_keepers(k_list: KeeperList, token: str = Depends(oauth2_scheme)):
Keeper.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Inserted {len(new_keepers)} keepers'
return f"Inserted {len(new_keepers)} keepers"
@router.delete('/{keeper_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{keeper_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_keeper(keeper_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_keeper - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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'
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')
raise HTTPException(
status_code=500, detail=f"Keeper ID {keeper_id} could not be deleted"
)

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, Manager, Team, Current, model_to_dict, fn
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/managers',
tags=['managers']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/managers", tags=["managers"])
class ManagerModel(pydantic.BaseModel):
name: str
@ -21,45 +23,45 @@ class ManagerModel(pydantic.BaseModel):
bio: Optional[str] = None
@router.get('')
@router.get("")
@handle_db_errors
async def get_managers(
name: list = Query(default=None), active: Optional[bool] = None, short_output: Optional[bool] = False):
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'))
~(Team.abbrev.endswith("IL")) & ~(Team.abbrev.endswith("MiL"))
)
logger.info(f'tquery: {t_query}')
logger.info(f"tquery: {t_query}")
a_mgr = []
i_mgr = []
for x in t_query:
logger.info(f'Team: {x.abbrev} / mgr1: {x.manager1} / mgr2: {x.manager2}')
logger.info(f"Team: {x.abbrev} / mgr1: {x.manager1} / mgr2: {x.manager2}")
if x.manager1 is not None:
a_mgr.append(x.manager1)
logger.info(f'appending {x.manager1.name}')
logger.info(f"appending {x.manager1.name}")
if x.manager2 is not None:
a_mgr.append(x.manager2)
logger.info(f'appending {x.manager2.name}')
logger.info(f"appending {x.manager2.name}")
logger.info(f'a_mgr: {a_mgr}')
logger.info(f"a_mgr: {a_mgr}")
if active:
final_mgrs = [model_to_dict(y, recurse=not short_output) for y in a_mgr]
else:
logger.info(f'checking inactive')
logger.info(f"checking inactive")
for z in Manager.select():
logger.info(f'checking: {z.name}')
logger.info(f"checking: {z.name}")
if z not in a_mgr:
logger.info(f'+inactive: {z.name}')
logger.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
}
return_managers = {"count": len(final_mgrs), "managers": final_mgrs}
else:
all_managers = Manager.select()
@ -68,15 +70,17 @@ async def get_managers(
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]
"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}')
@router.get("/{manager_id}")
@handle_db_errors
async def get_one_manager(manager_id: int, short_output: Optional[bool] = False):
this_manager = Manager.get_or_none(Manager.id == manager_id)
@ -85,22 +89,29 @@ async def get_one_manager(manager_id: int, short_output: Optional[bool] = False)
db.close()
return r_manager
else:
raise HTTPException(status_code=404, detail=f'Manager {manager_id} not found')
raise HTTPException(status_code=404, detail=f"Manager {manager_id} not found")
@router.patch('/{manager_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{manager_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
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)):
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):
logger.warning(f'patch_manager - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
raise HTTPException(
status_code=404, detail=f"Manager ID {manager_id} not found"
)
if name is not None:
this_manager.name = name
@ -117,15 +128,17 @@ async def patch_manager(
return r_manager
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch manager {this_manager}')
raise HTTPException(
status_code=500, detail=f"Unable to patch manager {this_manager}"
)
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_manager(new_manager: ManagerModel, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_manager - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"post_manager - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
this_manager = Manager(**new_manager.dict())
@ -135,25 +148,31 @@ async def post_manager(new_manager: ManagerModel, token: str = Depends(oauth2_sc
return r_manager
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to post manager {this_manager.name}')
raise HTTPException(
status_code=500, detail=f"Unable to post manager {this_manager.name}"
)
@router.delete('/{manager_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{manager_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_manager(manager_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_manager - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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'
return f"Manager {manager_id} has been deleted"
else:
raise HTTPException(status_code=500, detail=f'Manager {manager_id} could not be deleted')
raise HTTPException(
status_code=500, detail=f"Manager {manager_id} could not be deleted"
)

View File

@ -6,15 +6,27 @@ 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, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/pitchingstats',
tags=['pitchingstats']
from ..db_engine import (
db,
PitchingStat,
Team,
Player,
Current,
model_to_dict,
chunked,
fn,
per_season_weeks,
)
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/pitchingstats", tags=["pitchingstats"])
class PitStatModel(pydantic.BaseModel):
@ -48,29 +60,37 @@ class PitStatList(pydantic.BaseModel):
stats: List[PitStatModel]
@router.get('')
@router.get("")
@handle_db_errors
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():
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']:
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': []}
return {"count": 0, "stats": []}
else:
all_stats = PitchingStat.regular_season(season)
if all_stats.count() == 0:
db.close()
return {'count': 0, 'stats': []}
return {"count": 0, "stats": []}
if team_abbrev is not None:
t_query = Team.select().where(Team.abbrev << [x.upper() for x in team_abbrev])
@ -79,7 +99,9 @@ async def get_pitstats(
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])
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)
@ -96,7 +118,7 @@ async def get_pitstats(
db.close()
raise HTTPException(
status_code=404,
detail=f'Start week {start} is after end week {end} - cannot pull stats'
detail=f"Start week {start} is after end week {end} - cannot pull stats",
)
all_stats = all_stats.where(
(PitchingStat.week >= start) & (PitchingStat.week <= end)
@ -105,56 +127,79 @@ async def get_pitstats(
if limit:
all_stats = all_stats.limit(limit)
if sort:
if sort == 'newest':
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]
"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')
@router.get("/totals")
@handle_db_errors
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)):
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.')
raise HTTPException(
status_code=400,
detail=f"Only one of s_type, week_start/week_end, or week may be used.",
)
# Build SELECT fields conditionally based on group_by to match GROUP BY exactly
select_fields = []
if group_by == 'player':
if group_by == "player":
select_fields = [PitchingStat.player]
elif group_by == 'team':
elif group_by == "team":
select_fields = [PitchingStat.team]
elif group_by == 'playerteam':
elif group_by == "playerteam":
select_fields = [PitchingStat.player, PitchingStat.team]
else:
# Default case
select_fields = [PitchingStat.player]
all_stats = (
PitchingStat
.select(*select_fields,
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'), fn.COUNT(PitchingStat.game).alias('sum_games'))
PitchingStat.select(
*select_fields,
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"),
fn.COUNT(PitchingStat.game).alias("sum_games"),
)
.where(PitchingStat.season == season)
.having(fn.SUM(PitchingStat.ip) >= ip_min)
)
@ -166,16 +211,20 @@ async def get_totalstats(
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.'
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",
)
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
weeks["end"] = week_end
all_stats = all_stats.where(
(PitchingStat.week >= weeks['start']) & (PitchingStat.week <= weeks['end'])
(PitchingStat.week >= weeks["start"]) & (PitchingStat.week <= weeks["end"])
)
elif week is not None:
@ -189,9 +238,9 @@ async def get_totalstats(
if not is_sp:
all_stats = all_stats.where(PitchingStat.gs == 0)
if sort is not None:
if sort == 'player':
if sort == "player":
all_stats = all_stats.order_by(PitchingStat.player)
elif sort == 'team':
elif sort == "team":
all_stats = all_stats.order_by(PitchingStat.team)
if group_by is not None:
# Use the same fields for GROUP BY as we used for SELECT
@ -200,67 +249,76 @@ async def get_totalstats(
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_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_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': []
}
return_stats = {"count": all_stats.count(), "stats": []}
for x in all_stats:
# Handle player field based on grouping with safe access
this_player = 'TOT'
if 'player' in group_by and hasattr(x, 'player'):
this_player = x.player_id if short_output else model_to_dict(x.player, recurse=False)
this_player = "TOT"
if "player" in group_by and hasattr(x, "player"):
this_player = (
x.player_id if short_output else model_to_dict(x.player, recurse=False)
)
# Handle team field based on grouping with safe access
this_team = 'TOT'
if 'team' in group_by and hasattr(x, 'team'):
this_team = x.team_id if short_output else model_to_dict(x.team, recurse=False)
this_team = "TOT"
if "team" in group_by and hasattr(x, "team"):
this_team = (
x.team_id if short_output else model_to_dict(x.team, recurse=False)
)
return_stats['stats'].append({
'player': this_player,
'team': this_team,
'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,
'games': x.sum_games,
'win': x.sum_win,
'loss': x.sum_loss,
'hold': x.sum_hold,
'sv': x.sum_sv,
'bsv': x.sum_bsv
})
return_stats["stats"].append(
{
"player": this_player,
"team": this_team,
"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,
"games": x.sum_games,
"win": x.sum_win,
"loss": x.sum_loss,
"hold": x.sum_hold,
"sv": x.sum_sv,
"bsv": x.sum_bsv,
}
)
db.close()
return return_stats
@router.patch('/{stat_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{stat_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_pitstats(stat_id: int, new_stats: PitStatModel, token: str = Depends(oauth2_scheme)):
async def patch_pitstats(
stat_id: int, new_stats: PitStatModel, token: str = Depends(oauth2_scheme)
):
if not valid_token(token):
logger.warning(f'patch_pitstats - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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))
@ -268,12 +326,12 @@ async def patch_pitstats(stat_id: int, new_stats: PitStatModel, token: str = Dep
return r_stat
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_pitstats(s_list: PitStatList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_pitstats - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.warning(f"post_pitstats - Bad Token: {token}")
raise HTTPException(status_code=401, detail="Unauthorized")
all_stats = []
@ -281,9 +339,13 @@ async def post_pitstats(s_list: PitStatList, token: str = Depends(oauth2_scheme)
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')
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')
raise HTTPException(
status_code=404, detail=f"Player ID {x.player_id} not found"
)
all_stats.append(PitchingStat(**x.dict()))
@ -292,4 +354,4 @@ async def post_pitstats(s_list: PitStatList, token: str = Depends(oauth2_scheme)
PitchingStat.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Added {len(all_stats)} batting lines'
return f"Added {len(all_stats)} batting lines"

View File

@ -24,8 +24,12 @@ async def get_players(
strat_code: list = Query(default=None),
is_injured: Optional[bool] = None,
sort: Optional[str] = None,
limit: Optional[int] = Query(default=None, ge=1, description="Maximum number of results to return"),
offset: Optional[int] = Query(default=None, ge=0, description="Number of results to skip for pagination"),
limit: Optional[int] = Query(
default=None, ge=1, description="Maximum number of results to return"
),
offset: Optional[int] = Query(
default=None, ge=0, description="Number of results to skip for pagination"
),
short_output: Optional[bool] = False,
csv: Optional[bool] = False,
):
@ -41,7 +45,7 @@ async def get_players(
limit=limit,
offset=offset,
short_output=short_output or False,
as_csv=csv or False
as_csv=csv or False,
)
if csv:
@ -54,35 +58,29 @@ async def get_players(
@cache_result(ttl=15 * 60, key_prefix="players-search")
async def search_players(
q: str = Query(..., description="Search query for player name"),
season: Optional[int] = Query(default=None, description="Season to search (0 for all)"),
season: Optional[int] = Query(
default=None, description="Season to search (0 for all)"
),
limit: int = Query(default=10, ge=1, le=50),
short_output: bool = False,
):
"""Search players by name with fuzzy matching."""
return PlayerService.search_players(
query_str=q,
season=season,
limit=limit,
short_output=short_output
query_str=q, season=season, limit=limit, short_output=short_output
)
@router.get("/{player_id}")
@handle_db_errors
@cache_result(ttl=30 * 60, key_prefix="player")
async def get_one_player(
player_id: int,
short_output: Optional[bool] = False
):
async def get_one_player(player_id: int, short_output: Optional[bool] = False):
"""Get a single player by ID."""
return PlayerService.get_player(player_id, short_output=short_output or False)
@router.put("/{player_id}")
async def put_player(
player_id: int,
new_player: dict,
token: str = Depends(oauth2_scheme)
player_id: int, new_player: dict, token: str = Depends(oauth2_scheme)
):
"""Update a player (full replacement)."""
return PlayerService.update_player(player_id, new_player, token)
@ -120,14 +118,28 @@ async def patch_player(
data = {
key: value
for key, value in {
'name': name, 'wara': wara, 'image': image, 'image2': image2,
'team_id': team_id, 'season': season,
'pos_1': pos_1, 'pos_2': pos_2, 'pos_3': pos_3, 'pos_4': pos_4,
'pos_5': pos_5, 'pos_6': pos_6, 'pos_7': pos_7, 'pos_8': pos_8,
'vanity_card': vanity_card, 'headshot': headshot,
'il_return': il_return, 'demotion_week': demotion_week,
'strat_code': strat_code, 'bbref_id': bbref_id,
'injury_rating': injury_rating, 'sbaref_id': sbaref_id,
"name": name,
"wara": wara,
"image": image,
"image2": image2,
"team_id": team_id,
"season": season,
"pos_1": pos_1,
"pos_2": pos_2,
"pos_3": pos_3,
"pos_4": pos_4,
"pos_5": pos_5,
"pos_6": pos_6,
"pos_7": pos_7,
"pos_8": pos_8,
"vanity_card": vanity_card,
"headshot": headshot,
"il_return": il_return,
"demotion_week": demotion_week,
"strat_code": strat_code,
"bbref_id": bbref_id,
"injury_rating": injury_rating,
"sbaref_id": sbaref_id,
}.items()
if value is not None
}
@ -135,19 +147,13 @@ async def patch_player(
return PlayerService.patch_player(player_id, data, token)
@router.post("")
async def post_players(
p_list: dict,
token: str = Depends(oauth2_scheme)
):
@router.post("/")
async def post_players(p_list: dict, token: str = Depends(oauth2_scheme)):
"""Create multiple players."""
return PlayerService.create_players(p_list.get("players", []), token)
@router.delete("/{player_id}")
async def delete_player(
player_id: int,
token: str = Depends(oauth2_scheme)
):
async def delete_player(player_id: int, token: str = Depends(oauth2_scheme)):
"""Delete a player."""
return PlayerService.delete_player(player_id, token)

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, Result, Team, model_to_dict, chunked
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/results',
tags=['results']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/results", tags=["results"])
class ResultModel(pydantic.BaseModel):
week: int
@ -29,13 +31,18 @@ class ResultList(pydantic.BaseModel):
results: List[ResultModel]
@router.get('')
@router.get("")
@handle_db_errors
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):
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:
@ -68,14 +75,14 @@ async def get_results(
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]
"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}')
@router.get("/{result_id}")
@handle_db_errors
async def get_one_result(result_id: int, short_output: Optional[bool] = False):
this_result = Result.get_or_none(Result.id == result_id)
@ -87,20 +94,27 @@ async def get_one_result(result_id: int, short_output: Optional[bool] = False):
return r_result
@router.patch('/{result_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{result_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
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)):
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):
logger.warning(f'patch_player - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
raise HTTPException(status_code=404, detail=f"Result ID {result_id} not found")
if week_num is not None:
this_result.week = week_num
@ -132,22 +146,28 @@ async def patch_result(
return r_result
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch result {result_id}')
raise HTTPException(
status_code=500, detail=f"Unable to patch result {result_id}"
)
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_results(result_list: ResultList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'patch_player - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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')
raise HTTPException(
status_code=404, detail=f"Team ID {x.hometeam_id} not found"
)
new_results.append(x.dict())
@ -156,27 +176,27 @@ async def post_results(result_list: ResultList, token: str = Depends(oauth2_sche
Result.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Inserted {len(new_results)} results'
return f"Inserted {len(new_results)} results"
@router.delete('/{result_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{result_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_result(result_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_result - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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'
return f"Result {result_id} has been deleted"
else:
raise HTTPException(status_code=500, detail=f'Result {result_id} could not be deleted')
raise HTTPException(
status_code=500, detail=f"Result {result_id} could not be deleted"
)

View File

@ -7,15 +7,17 @@ import logging
import pydantic
from ..db_engine import db, SbaPlayer, model_to_dict, fn, chunked, query_to_csv
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/sbaplayers',
tags=['sbaplayers']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/sbaplayers", tags=["sbaplayers"])
class SbaPlayerModel(pydantic.BaseModel):
first_name: str
@ -30,19 +32,26 @@ class PlayerList(pydantic.BaseModel):
players: List[SbaPlayerModel]
@router.get('')
@router.get("")
@handle_db_errors
async def get_players(
full_name: list = Query(default=None), first_name: list = Query(default=None),
last_name: list = Query(default=None), key_fangraphs: list = Query(default=None),
key_bbref: list = Query(default=None), key_retro: list = Query(default=None),
key_mlbam: list = Query(default=None), sort: Optional[str] = None, csv: Optional[bool] = False):
full_name: list = Query(default=None),
first_name: list = Query(default=None),
last_name: list = Query(default=None),
key_fangraphs: list = Query(default=None),
key_bbref: list = Query(default=None),
key_retro: list = Query(default=None),
key_mlbam: list = Query(default=None),
sort: Optional[str] = None,
csv: Optional[bool] = False,
):
all_players = SbaPlayer.select()
if full_name is not None:
name_list = [x.lower() for x in full_name]
all_players = all_players.where(
fn.lower(SbaPlayer.first_name) + ' ' + fn.lower(SbaPlayer.last_name) << name_list
fn.lower(SbaPlayer.first_name) + " " + fn.lower(SbaPlayer.last_name)
<< name_list
)
if first_name is not None:
name_list = [x.lower() for x in first_name]
@ -62,74 +71,85 @@ async def get_players(
all_players = all_players.where(SbaPlayer.key_mlbam << key_mlbam)
if sort is not None:
if sort in ['firstname-asc', 'fullname-asc']:
if sort in ["firstname-asc", "fullname-asc"]:
all_players = all_players.order_by(SbaPlayer.first_name)
elif sort in ['firstname-desc', 'fullname-desc']:
elif sort in ["firstname-desc", "fullname-desc"]:
all_players = all_players.order_by(-SbaPlayer.first_name)
elif sort == 'lastname-asc':
elif sort == "lastname-asc":
all_players = all_players.order_by(SbaPlayer.last_name)
elif sort == 'lastname-desc':
elif sort == "lastname-desc":
all_players = all_players.order_by(-SbaPlayer.last_name)
elif sort == 'fangraphs-asc':
elif sort == "fangraphs-asc":
all_players = all_players.order_by(SbaPlayer.key_fangraphs)
elif sort == 'fangraphs-desc':
elif sort == "fangraphs-desc":
all_players = all_players.order_by(-SbaPlayer.key_fangraphs)
elif sort == 'bbref-asc':
elif sort == "bbref-asc":
all_players = all_players.order_by(SbaPlayer.key_bbref)
elif sort == 'bbref-desc':
elif sort == "bbref-desc":
all_players = all_players.order_by(-SbaPlayer.key_bbref)
elif sort == 'retro-asc':
elif sort == "retro-asc":
all_players = all_players.order_by(SbaPlayer.key_retro)
elif sort == 'retro-desc':
elif sort == "retro-desc":
all_players = all_players.order_by(-SbaPlayer.key_retro)
elif sort == 'mlbam-asc':
elif sort == "mlbam-asc":
all_players = all_players.order_by(SbaPlayer.key_mlbam)
elif sort == 'mlbam-desc':
elif sort == "mlbam-desc":
all_players = all_players.order_by(-SbaPlayer.key_mlbam)
if csv:
return_val = query_to_csv(all_players)
db.close()
return Response(content=return_val, media_type='text/csv')
return Response(content=return_val, media_type="text/csv")
return_val = {'count': all_players.count(), 'players': [
model_to_dict(x) for x in all_players
]}
return_val = {
"count": all_players.count(),
"players": [model_to_dict(x) for x in all_players],
}
db.close()
return return_val
@router.get('/{player_id}')
@router.get("/{player_id}")
@handle_db_errors
async def get_one_player(player_id: int):
this_player = SbaPlayer.get_or_none(SbaPlayer.id == player_id)
if this_player is None:
db.close()
raise HTTPException(status_code=404, detail=f'SbaPlayer id {player_id} not found')
raise HTTPException(
status_code=404, detail=f"SbaPlayer id {player_id} not found"
)
r_data = model_to_dict(this_player)
db.close()
return r_data
@router.patch('/{player_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{player_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_player(
player_id: int, first_name: Optional[str] = None, last_name: Optional[str] = None,
key_fangraphs: Optional[str] = None, key_bbref: Optional[str] = None, key_retro: Optional[str] = None,
key_mlbam: Optional[str] = None, token: str = Depends(oauth2_scheme)):
player_id: int,
first_name: Optional[str] = None,
last_name: Optional[str] = None,
key_fangraphs: Optional[str] = None,
key_bbref: Optional[str] = None,
key_retro: Optional[str] = None,
key_mlbam: Optional[str] = None,
token: str = Depends(oauth2_scheme),
):
if not valid_token(token):
logging.warning(f'Bad Token: {token}')
logging.warning(f"Bad Token: {token}")
db.close()
raise HTTPException(
status_code=401,
detail='You are not authorized to patch mlb players. This event has been logged.'
detail="You are not authorized to patch mlb players. This event has been logged.",
)
this_player = SbaPlayer.get_or_none(SbaPlayer.id == player_id)
if this_player is None:
db.close()
raise HTTPException(status_code=404, detail=f'SbaPlayer id {player_id} not found')
raise HTTPException(
status_code=404, detail=f"SbaPlayer id {player_id} not found"
)
if first_name is not None:
this_player.first_name = first_name
@ -152,33 +172,38 @@ async def patch_player(
db.close()
raise HTTPException(
status_code=418,
detail='Well slap my ass and call me a teapot; I could not save that player'
detail="Well slap my ass and call me a teapot; I could not save that player",
)
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_players(players: PlayerList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'Bad Token: {token}')
logging.warning(f"Bad Token: {token}")
db.close()
raise HTTPException(
status_code=401,
detail='You are not authorized to post mlb players. This event has been logged.'
detail="You are not authorized to post mlb players. This event has been logged.",
)
new_players = []
for x in players.players:
dupes = SbaPlayer.select().where(
((SbaPlayer.key_fangraphs == x.key_fangraphs) & (x.key_fangraphs is not None)) | ((SbaPlayer.key_mlbam == x.key_mlbam) & (x.key_mlbam is not None)) |
((SbaPlayer.key_retro == x.key_retro) & (x.key_retro is not None)) | ((SbaPlayer.key_bbref == x.key_bbref) & (x.key_bbref is not None))
(
(SbaPlayer.key_fangraphs == x.key_fangraphs)
& (x.key_fangraphs is not None)
)
| ((SbaPlayer.key_mlbam == x.key_mlbam) & (x.key_mlbam is not None))
| ((SbaPlayer.key_retro == x.key_retro) & (x.key_retro is not None))
| ((SbaPlayer.key_bbref == x.key_bbref) & (x.key_bbref is not None))
)
if dupes.count() > 0:
logger.error(f'Found a dupe for {x}')
logger.error(f"Found a dupe for {x}")
db.close()
raise HTTPException(
status_code=400,
detail=f'{x.first_name} {x.last_name} has a key already in the database'
detail=f"{x.first_name} {x.last_name} has a key already in the database",
)
new_players.append(x.model_dump())
@ -188,32 +213,33 @@ async def post_players(players: PlayerList, token: str = Depends(oauth2_scheme))
SbaPlayer.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Inserted {len(new_players)} new MLB players'
return f"Inserted {len(new_players)} new MLB players"
@router.post('/one', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/one", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_one_player(player: SbaPlayerModel, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'Bad Token: {token}')
logging.warning(f"Bad Token: {token}")
db.close()
raise HTTPException(
status_code=401,
detail='You are not authorized to post mlb players. This event has been logged.'
detail="You are not authorized to post mlb players. This event has been logged.",
)
dupes = SbaPlayer.select().where(
(SbaPlayer.key_fangraphs == player.key_fangraphs) | (SbaPlayer.key_mlbam == player.key_mlbam) |
(SbaPlayer.key_bbref == player.key_bbref)
(SbaPlayer.key_fangraphs == player.key_fangraphs)
| (SbaPlayer.key_mlbam == player.key_mlbam)
| (SbaPlayer.key_bbref == player.key_bbref)
)
if dupes.count() > 0:
logging.info(f'POST /SbaPlayers/one - dupes found:')
logging.info(f"POST /SbaPlayers/one - dupes found:")
for x in dupes:
logging.info(f'{x}')
logging.info(f"{x}")
db.close()
raise HTTPException(
status_code=400,
detail=f'{player.first_name} {player.last_name} has a key already in the database'
detail=f"{player.first_name} {player.last_name} has a key already in the database",
)
new_player = SbaPlayer(**player.dict())
@ -226,30 +252,34 @@ async def post_one_player(player: SbaPlayerModel, token: str = Depends(oauth2_sc
db.close()
raise HTTPException(
status_code=418,
detail='Well slap my ass and call me a teapot; I could not save that player'
detail="Well slap my ass and call me a teapot; I could not save that player",
)
@router.delete('/{player_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{player_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_player(player_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'Bad Token: {token}')
logging.warning(f"Bad Token: {token}")
db.close()
raise HTTPException(
status_code=401,
detail='You are not authorized to delete mlb players. This event has been logged.'
detail="You are not authorized to delete mlb players. This event has been logged.",
)
this_player = SbaPlayer.get_or_none(SbaPlayer.id == player_id)
if this_player is None:
db.close()
raise HTTPException(status_code=404, detail=f'SbaPlayer id {player_id} not found')
raise HTTPException(
status_code=404, detail=f"SbaPlayer id {player_id} not found"
)
count = this_player.delete_instance()
db.close()
if count == 1:
return f'Player {player_id} has been deleted'
return f"Player {player_id} has been deleted"
else:
raise HTTPException(status_code=500, detail=f'Player {player_id} was not deleted')
raise HTTPException(
status_code=500, detail=f"Player {player_id} was not deleted"
)

View File

@ -4,15 +4,17 @@ import logging
import pydantic
from ..db_engine import db, Schedule, Team, model_to_dict, chunked
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors
logger = logging.getLogger('discord_app')
router = APIRouter(
prefix='/api/v3/schedules',
tags=['schedules']
from ..dependencies import (
oauth2_scheme,
valid_token,
PRIVATE_IN_SCHEMA,
handle_db_errors,
)
logger = logging.getLogger("discord_app")
router = APIRouter(prefix="/api/v3/schedules", tags=["schedules"])
class ScheduleModel(pydantic.BaseModel):
week: int
@ -26,12 +28,17 @@ class ScheduleList(pydantic.BaseModel):
schedules: List[ScheduleModel]
@router.get('')
@router.get("")
@handle_db_errors
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):
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:
@ -63,14 +70,14 @@ async def get_schedules(
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]
"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}')
@router.get("/{schedule_id}")
@handle_db_errors
async def get_one_schedule(schedule_id: int):
this_sched = Schedule.get_or_none(Schedule.id == schedule_id)
@ -82,19 +89,26 @@ async def get_one_schedule(schedule_id: int):
return r_sched
@router.patch('/{schedule_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.patch("/{schedule_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
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)):
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):
logger.warning(f'patch_schedule - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
raise HTTPException(
status_code=404, detail=f"Schedule ID {schedule_id} not found"
)
if week is not None:
this_sched.week = week
@ -117,22 +131,28 @@ async def patch_schedule(
return r_sched
else:
db.close()
raise HTTPException(status_code=500, detail=f'Unable to patch schedule {schedule_id}')
raise HTTPException(
status_code=500, detail=f"Unable to patch schedule {schedule_id}"
)
@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_schedules(sched_list: ScheduleList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'post_schedules - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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')
raise HTTPException(
status_code=404, detail=f"Team ID {x.hometeam_id} not found"
)
new_sched.append(x.dict())
@ -141,24 +161,28 @@ async def post_schedules(sched_list: ScheduleList, token: str = Depends(oauth2_s
Schedule.insert_many(batch).on_conflict_ignore().execute()
db.close()
return f'Inserted {len(new_sched)} schedules'
return f"Inserted {len(new_sched)} schedules"
@router.delete('/{schedule_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@router.delete("/{schedule_id}", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_schedule(schedule_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logger.warning(f'delete_schedule - Bad Token: {token}')
raise HTTPException(status_code=401, detail='Unauthorized')
logger.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')
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'
return f"Schedule {this_sched} has been deleted"
else:
raise HTTPException(status_code=500, detail=f'Schedule {this_sched} could not be deleted')
raise HTTPException(
status_code=500, detail=f"Schedule {this_sched} could not be deleted"
)

View File

@ -230,7 +230,7 @@ async def patch_game(
raise HTTPException(status_code=500, detail=f"Unable to patch game {game_id}")
@router.post("", include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_games(game_list: GameList, token: str = Depends(oauth2_scheme)) -> Any:
if not valid_token(token):

View File

@ -6,16 +6,21 @@ Thin HTTP layer using TeamService for business logic.
from fastapi import APIRouter, Query, Response, Depends
from typing import List, Optional, Literal
from ..dependencies import oauth2_scheme, PRIVATE_IN_SCHEMA, handle_db_errors, cache_result
from ..dependencies import (
oauth2_scheme,
PRIVATE_IN_SCHEMA,
handle_db_errors,
cache_result,
)
from ..services.base import BaseService
from ..services.team_service import TeamService
router = APIRouter(prefix='/api/v3/teams', tags=['teams'])
router = APIRouter(prefix="/api/v3/teams", tags=["teams"])
@router.get('')
@router.get("")
@handle_db_errors
@cache_result(ttl=10*60, key_prefix='teams')
@cache_result(ttl=10 * 60, key_prefix="teams")
async def get_teams(
season: Optional[int] = None,
owner_id: list = Query(default=None),
@ -23,7 +28,7 @@ async def get_teams(
team_abbrev: list = Query(default=None),
active_only: Optional[bool] = False,
short_output: Optional[bool] = False,
csv: Optional[bool] = False
csv: Optional[bool] = False,
):
"""Get teams with filtering."""
result = TeamService.get_teams(
@ -33,46 +38,41 @@ async def get_teams(
team_abbrev=team_abbrev if team_abbrev else None,
active_only=active_only or False,
short_output=short_output or False,
as_csv=csv or False
as_csv=csv or False,
)
if csv:
return Response(content=result, media_type='text/csv')
return Response(content=result, media_type="text/csv")
return result
@router.get('/{team_id}')
@router.get("/{team_id}")
@handle_db_errors
@cache_result(ttl=30*60, key_prefix='team')
@cache_result(ttl=30 * 60, key_prefix="team")
async def get_one_team(team_id: int):
"""Get a single team by ID."""
return TeamService.get_team(team_id)
@router.get('/{team_id}/roster')
@router.get("/{team_id}/roster")
@handle_db_errors
@cache_result(ttl=30*60, key_prefix='team-roster')
async def get_team_roster_default(
team_id: int,
sort: Optional[str] = None
):
@cache_result(ttl=30 * 60, key_prefix="team-roster")
async def get_team_roster_default(team_id: int, sort: Optional[str] = None):
"""Get team roster with IL lists (defaults to current season)."""
return TeamService.get_team_roster(team_id, 'current', sort=sort)
return TeamService.get_team_roster(team_id, "current", sort=sort)
@router.get('/{team_id}/roster/{which}')
@router.get("/{team_id}/roster/{which}")
@handle_db_errors
@cache_result(ttl=30*60, key_prefix='team-roster')
@cache_result(ttl=30 * 60, key_prefix="team-roster")
async def get_team_roster(
team_id: int,
which: Literal['current', 'next'],
sort: Optional[str] = None
team_id: int, which: Literal["current", "next"], sort: Optional[str] = None
):
"""Get team roster with IL lists."""
return TeamService.get_team_roster(team_id, which, sort=sort)
@router.patch('/{team_id}')
@router.patch("/{team_id}")
async def patch_team(
team_id: int,
token: str = Depends(oauth2_scheme),
@ -95,11 +95,19 @@ async def patch_team(
data = {
key: value
for key, value in {
'manager1_id': manager1_id, 'manager2_id': manager2_id,
'gmid': gmid, 'gmid2': gmid2, 'mascot': mascot,
'stadium': stadium, 'thumbnail': thumbnail, 'color': color,
'abbrev': abbrev, 'sname': sname, 'lname': lname,
'dice_color': dice_color, 'division_id': division_id,
"manager1_id": manager1_id,
"manager2_id": manager2_id,
"gmid": gmid,
"gmid2": gmid2,
"mascot": mascot,
"stadium": stadium,
"thumbnail": thumbnail,
"color": color,
"abbrev": abbrev,
"sname": sname,
"lname": lname,
"dice_color": dice_color,
"division_id": division_id,
}.items()
if value is not None
}
@ -107,19 +115,13 @@ async def patch_team(
return TeamService.update_team(team_id, data, token)
@router.post('')
async def post_teams(
team_list: dict,
token: str = Depends(oauth2_scheme)
):
@router.post("/")
async def post_teams(team_list: dict, token: str = Depends(oauth2_scheme)):
"""Create multiple teams."""
return TeamService.create_teams(team_list.get("teams", []), token)
@router.delete('/{team_id}')
async def delete_team(
team_id: int,
token: str = Depends(oauth2_scheme)
):
@router.delete("/{team_id}")
async def delete_team(team_id: int, token: str = Depends(oauth2_scheme)):
"""Delete a team."""
return TeamService.delete_team(team_id, token)

View File

@ -132,7 +132,7 @@ async def patch_transactions(
return f"Updated {these_moves.count()} transactions"
@router.post("", include_in_schema=PRIVATE_IN_SCHEMA)
@router.post("/", include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_transactions(
moves: TransactionList, token: str = Depends(oauth2_scheme)