From 54b04855994b732d74086c31818b8d18d8515bad Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Wed, 22 Mar 2023 15:56:10 -0500 Subject: [PATCH] /standings, /teams, /transactions added --- app/main.py | 5 +- app/routers_v3/players.py | 8 +- app/routers_v3/results.py | 40 ++++--- app/routers_v3/schedules.py | 21 ++-- app/routers_v3/standings.py | 102 +++++++++++++++++ app/routers_v3/teams.py | 200 +++++++++++++++++++++++++++++++++ app/routers_v3/transactions.py | 176 +++++++++++++++++++++++++++++ 7 files changed, 524 insertions(+), 28 deletions(-) create mode 100644 app/routers_v3/standings.py create mode 100644 app/routers_v3/teams.py create mode 100644 app/routers_v3/transactions.py diff --git a/app/main.py b/app/main.py index 1bcea59..3e1af89 100644 --- a/app/main.py +++ b/app/main.py @@ -1,6 +1,6 @@ from fastapi import Depends, FastAPI -from routers_v3 import current, players, results, schedules +from routers_v3 import current, players, results, schedules, standings, teams, transactions app = FastAPI( responses={404: {'description': 'Not found'}} @@ -11,6 +11,9 @@ app.include_router(current.router) app.include_router(players.router) app.include_router(results.router) app.include_router(schedules.router) +app.include_router(teams.router) +app.include_router(transactions.router) +app.include_router(standings.router) # @app.get("/api") diff --git a/app/routers_v3/players.py b/app/routers_v3/players.py index 433a968..ed77a82 100644 --- a/app/routers_v3/players.py +++ b/app/routers_v3/players.py @@ -44,7 +44,7 @@ class PlayerList(pydantic.BaseModel): @router.get('') def get_players( season: Optional[int], team_id: list = Query(default=None), pos: list = Query(default=None), - is_injured: Optional[bool] = None, sort: Optional[str] = None): + is_injured: Optional[bool] = None, short_output: Optional[bool] = False, sort: Optional[str] = None): logging.info(f'team_id: {team_id}') all_players = Player.select_season(season) @@ -74,17 +74,17 @@ def get_players( return_players = { 'count': all_players.count(), - 'players': [model_to_dict(x) for x in all_players] + 'players': [model_to_dict(x, recurse=not short_output) for x in all_players] } db.close() return return_players @router.get('/{player_id}') -def get_one_player(player_id: int): +def get_one_player(player_id: int, short_output: Optional[bool] = False): this_player = Player.get_or_none(Player.id == player_id) if this_player: - r_player = model_to_dict(this_player) + r_player = model_to_dict(this_player, recurse=not short_output) else: r_player = None db.close() diff --git a/app/routers_v3/results.py b/app/routers_v3/results.py index 818d4bc..fcae40e 100644 --- a/app/routers_v3/results.py +++ b/app/routers_v3/results.py @@ -31,7 +31,8 @@ class ResultList(pydantic.BaseModel): def get_results( season: int, team_abbrev: list = Query(default=None), week_start: list = Query(default=None), week_end: list = Query(default=None), game_num: list = Query(default=None), - away_abbrev: list = Query(default=None), home_abbrev: 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: @@ -65,17 +66,17 @@ def get_results( return_results = { 'count': all_results.count(), - 'results': [model_to_dict(x) for x in all_results] + 'results': [model_to_dict(x, recurse=not short_output) for x in all_results] } db.close() return return_results @router.get('/{result_id}') -def get_one_result(result_id: int): +def get_one_result(result_id: int, short_output: Optional[bool] = False): this_result = Result.get_or_none(Result.id == result_id) if this_result is not None: - r_result = model_to_dict(this_result) + r_result = model_to_dict(this_result, recurse=not short_output) else: r_result = None db.close() @@ -86,10 +87,15 @@ def get_one_result(result_id: int): 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): + home_score: Optional[int] = None, season: Optional[int] = None, scorecard_url: Optional[str] = None, + token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'patch_player - Bad Token: {token}') + raise HTTPException(status_code=401, detail='Unauthorized') + this_result = Result.get_or_none(Result.id == result_id) if this_result is None: - raise KeyError(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 @@ -121,17 +127,21 @@ def patch_result( return r_result else: db.close() - raise DatabaseError(f'Unable to patch result {result_id}') + raise HTTPException(status_code=500, detail=f'Unable to patch result {result_id}') @router.post('') -def post_results(result_list: ResultList): +def post_results(result_list: ResultList, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'patch_player - Bad Token: {token}') + raise HTTPException(status_code=401, detail='Unauthorized') + new_results = [] for x in result_list.results: if Team.get_or_none(Team.id == x.awayteam_id) is None: - raise KeyError(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 KeyError(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()) @@ -144,11 +154,15 @@ def post_results(result_list: ResultList): @router.delete('/{result_id}') -def delete_result(result_id: int): +def delete_result(result_id: int, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'delete_result - Bad Token: {token}') + raise HTTPException(status_code=401, detail='Unauthorized') + this_result = Result.get_or_none(Result.id == result_id) if not this_result: db.close() - raise KeyError(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() @@ -156,6 +170,6 @@ def delete_result(result_id: int): if count == 1: return f'Result {result_id} has been deleted' else: - raise DatabaseError(f'Result {result_id} could not be deleted') + raise HTTPException(status_code=500, detail=f'Result {result_id} could not be deleted') diff --git a/app/routers_v3/schedules.py b/app/routers_v3/schedules.py index 44d7b0f..db3cc8a 100644 --- a/app/routers_v3/schedules.py +++ b/app/routers_v3/schedules.py @@ -27,7 +27,8 @@ class ScheduleList(pydantic.BaseModel): @router.get('') 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): + home_abbrev: list = Query(default=None), week_start: Optional[int] = None, week_end: Optional[int] = None, + short_output: Optional[bool] = False): all_sched = Schedule.select_season(season) if team_abbrev is not None: @@ -60,17 +61,17 @@ def get_schedules( return_sched = { 'count': all_sched.count(), - 'schedules': [model_to_dict(x) for x in all_sched] + 'schedules': [model_to_dict(x, recurse=not short_output) for x in all_sched] } db.close() return return_sched @router.get('/{schedule_id}') -def get_one_schedule(schedule_id: int): +def get_one_schedule(schedule_id: int, short_output: Optional[bool] = False): this_sched = Schedule.get_or_none(Schedule.id == schedule_id) if this_sched is not None: - r_sched = model_to_dict(this_sched) + r_sched = model_to_dict(this_sched, recurse=not short_output) else: r_sched = None db.close() @@ -83,7 +84,7 @@ def patch_schedule( hometeam_id: Optional[int] = None, gamecount: Optional[int] = None, season: Optional[int] = None): this_sched = Schedule.get_or_none(Schedule.id == schedule_id) if this_sched is None: - raise KeyError(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 @@ -106,7 +107,7 @@ def patch_schedule( return r_sched else: db.close() - raise DatabaseError(f'Unable to patch schedule {schedule_id}') + raise HTTPException(status_code=500, detail=f'Unable to patch schedule {schedule_id}') @router.post('') @@ -114,9 +115,9 @@ def post_schedules(sched_list: ScheduleList): new_sched = [] for x in sched_list.schedules: if Team.get_or_none(Team.id == x.awayteam_id) is None: - raise KeyError(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 KeyError(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()) @@ -132,7 +133,7 @@ def post_schedules(sched_list: ScheduleList): def delete_schedule(schedule_id: int): this_sched = Schedule.get_or_none(Schedule.id == schedule_id) if this_sched is None: - raise KeyError(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() @@ -140,4 +141,4 @@ def delete_schedule(schedule_id: int): if count == 1: return f'Schedule {this_sched} has been deleted' else: - raise DatabaseError(f'Schedule {this_sched} could not be deleted') + raise HTTPException(status_code=500, detail=f'Schedule {this_sched} could not be deleted') diff --git a/app/routers_v3/standings.py b/app/routers_v3/standings.py new file mode 100644 index 0000000..87a265f --- /dev/null +++ b/app/routers_v3/standings.py @@ -0,0 +1,102 @@ +from fastapi import APIRouter, Depends, HTTPException, Query +from typing import List, Optional +import logging +import pydantic + +from db_engine import db, Standings, Team, Division, model_to_dict, chunked, fn +from dependencies import oauth2_scheme, valid_token, logging + +router = APIRouter( + prefix='/api/v3/standings', + tags=['standings'] +) + + +@router.get('') +async def v1_standings( + season: int, team_abbrev: Optional[str] = None, league_abbrev: Optional[str] = None, + division_abbrev: Optional[str] = None, short_output: Optional[bool] = False): + standings = Standings.select_season(season) + + if standings.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for season {season}') + + if team_abbrev: + this_team = Team.get_season(team_abbrev, season) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + standings = standings.where(Standings.team == this_team) + + if league_abbrev: + these_divisions = Division.select().where(fn.Lower(Division.league_abbrev) == league_abbrev.lower()) + if these_divisions.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'No output for league {league_abbrev}') + + standings = standings.where(Standings.team.division << these_divisions) + + if division_abbrev: + this_division = Division.select().where(fn.Lower(Division.division_abbrev) == division_abbrev.lower()) + if not this_division: + db.close() + raise HTTPException(status_code=404, detail=f'No output for division {division_abbrev}') + + standings = standings.where(Standings.team.division << this_division) + + def win_pct(this_team_stan): + if this_team_stan.wins + this_team_stan.losses == 0: + return 0 + else: + return (this_team_stan.wins / (this_team_stan.wins + this_team_stan.losses)) + \ + (this_team_stan.run_diff * .000001) + + div_teams = [team_stan for team_stan in standings] + div_teams.sort(key=lambda team: win_pct(team), reverse=True) + + return_standings = { + 'count': len(div_teams), + 'standings': [model_to_dict(x, recurse=not short_output) for x in div_teams] + } + + db.close() + return return_standings + + +@router.patch('/api/v1/standings/{stan_id}') +async def patch_standings( + stan_id, wins: Optional[int] = None, losses: Optional[int] = None, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'patch_player - Bad Token: {token}') + raise HTTPException(status_code=401, detail='Unauthorized') + + try: + this_stan = Standings.get_by_id(stan_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'No team found with id {stan_id}') + + if wins: + this_stan.wins = wins + if losses: + this_stan.losses = losses + + this_stan.save() + db.close() + + return model_to_dict(this_stan) + + +@router.post('/api/v1/standings/s{season}/recalculate') +async def recalculate_standings(season: int, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'recalculate_standings - Bad Token: {token}') + raise HTTPException(status_code=401, detail='Unauthorized') + + code = Standings.recalculate(season) + db.close() + if code == 69: + HTTPException(status_code=500, detail=f'Error recreating Standings rows') + raise HTTPException(status_code=200, detail=f'Just recalculated standings for season {season}') diff --git a/app/routers_v3/teams.py b/app/routers_v3/teams.py new file mode 100644 index 0000000..6bce3f6 --- /dev/null +++ b/app/routers_v3/teams.py @@ -0,0 +1,200 @@ +from fastapi import APIRouter, Depends, HTTPException, Query +from typing import List, Optional +import logging +import pydantic + +from db_engine import db, Team, Manager, Division, model_to_dict, chunked +from dependencies import oauth2_scheme, valid_token, logging + +router = APIRouter( + prefix='/api/v3/teams', + tags=['teams'] +) + + +class TeamModel(pydantic.BaseModel): + abbrev: str + sname: str + lname: str + gmid: Optional[int] = None + gmid2: Optional[int] = None + manager1_id: Optional[int] = None + manager2_id: Optional[int] = None + division_id: Optional[int] = None + stadium: Optional[str] = None + thumbnail: Optional[str] = None + color: Optional[str] = None + dice_color: Optional[str] = None + season: int + + +class TeamList(pydantic.BaseModel): + teams: List[TeamModel] + + +@router.get('') +def get_teams( + season: Optional[int] = None, owner_id: Optional[int] = None, manager_id: Optional[int] = None, + abbrev: Optional[str] = None, active_only: Optional[bool] = False, short_output: Optional[bool] = False): + if season is not None: + all_teams = Team.select_season(season) + else: + all_teams = Team.select() + + if manager_id is not None: + all_teams = all_teams.where( + (Team.manager1_id == manager_id) | (Team.manager2_id == manager_id) + ) + + if owner_id: + all_teams = all_teams.where((Team.gmid == owner_id) | (Team.gmid2 == owner_id)) + + if active_only: + all_teams = all_teams.where( + ~(Team.abbrev.endswith('IL')) & ~(Team.abbrev.endswith('MiL')) + ) + + if abbrev is not None: + all_teams = all_teams.where(Team.abbrev == abbrev) + + return_teams = { + 'count': all_teams.count(), + 'teams': [model_to_dict(x, recurse=not short_output) for x in all_teams] + } + db.close() + return return_teams + + +@router.get('/{team_id}') +def get_one_team(team_id: int, short_output: Optional[bool] = False): + this_team = Team.get_or_none(Team.id == team_id) + if this_team: + r_team = model_to_dict(this_team, recurse=not short_output) + else: + r_team = None + db.close() + return r_team + + +@router.get('/{team_id}') +def patch_team( + team_id: int, manager1_id: Optional[int] = None, manager2_id: Optional[int] = None, gmid: Optional[int] = None, + gmid2: Optional[int] = None, mascot: Optional[str] = None, stadium: Optional[str] = None, + thumbnail: Optional[str] = None, color: Optional[str] = None, abbrev: Optional[str] = None, + sname: Optional[str] = None, lname: Optional[str] = None, dice_color: Optional[str] = None, + division_id: Optional[int] = None): + this_team = Team.get_or_none(Team.id == team_id) + if not this_team: + return None + + if abbrev is not None: + this_team.abbrev = abbrev + if manager1_id is not None: + if manager1_id == 0: + this_team.manager1 = None + else: + this_manager = Manager.get_or_none(Manager.id == manager1_id) + if not this_manager: + db.close() + raise HTTPException(status_code=404, detail=f'Manager ID {manager1_id} not found') + this_team.manager1 = this_manager + if manager2_id is not None: + if manager2_id == 0: + this_team.manager2 = None + else: + this_manager = Manager.get_or_none(Manager.id == manager2_id) + if not this_manager: + db.close() + raise HTTPException(status_code=404, detail=f'Manager ID {manager2_id} not found') + this_team.manager2 = this_manager + if gmid is not None: + this_team.gmid = gmid + if gmid2 is not None: + if gmid2 == 0: + this_team.gmid2 = None + else: + this_team.gmid2 = gmid2 + if mascot is not None: + if mascot == 'False': + this_team.mascot = None + else: + this_team.mascot = mascot + if stadium is not None: + this_team.stadium = stadium + if thumbnail is not None: + this_team.thumbnail = thumbnail + if color is not None: + this_team.color = color + if dice_color is not None: + this_team.dice_color = dice_color + if sname is not None: + this_team.sname = sname + if lname is not None: + this_team.lname = lname + if division_id is not None: + if division_id == 0: + this_team.division = None + else: + this_division = Division.get_or_none(Division.id == division_id) + if not this_division: + db.close() + raise HTTPException(status_code=404, detail=f'Division ID {division_id} not found') + this_team.division = this_division + + if this_team.save(): + r_team = model_to_dict(this_team) + db.close() + return r_team + else: + db.close() + raise HTTPException(status_code=500, detail=f'Unable to patch team {team_id}') + + +@router.get('') +def post_team(team_list: TeamList): + new_teams = [] + for team in team_list.teams: + dupe_team = Team.get_or_none(Team.season == team.season, Team.abbrev == team.abbrev) + if dupe_team: + db.close() + raise HTTPException( + status_code=500, detail=f'Team Abbrev {team.abbrev} already in use in Season {team.season}' + ) + + if team.manager1_id and not Manager.get_or_none(Manager.id == team.manager1_id): + db.close() + raise HTTPException(status_code=404, detail=f'Manager ID {team.manager1_id} not found') + + if team.manager2_id and not Manager.get_or_none(Manager.id == team.manager2_id): + db.close() + raise HTTPException(status_code=404, detail=f'Manager ID {team.manager2_id} not found') + + if team.division_id and not Division.get_or_none(Division.id == team.division_id): + db.close() + raise HTTPException(status_code=404, detail=f'Division ID {team.division_id} not found') + + new_teams.append(team.dict()) + + with db.atomic(): + for batch in chunked(new_teams, 15): + Team.insert_many(batch).on_conflict_replace().execute() + db.close() + + return f'Inserted {len(new_teams)} teams' + + +@router.get('/{team_id}') +def delete_team(team_id: int): + this_team = Team.get_or_none(Team.id == team_id) + if not this_team: + db.close() + raise HTTPException(status_code=404, detail=f'Team ID {team_id} not found') + + count = this_team.delete_instance() + db.close() + + if count == 1: + return f'Team {team_id} has been deleted' + else: + raise HTTPException(status_code=500, detail=f'Team {team_id} could not be deleted') + diff --git a/app/routers_v3/transactions.py b/app/routers_v3/transactions.py new file mode 100644 index 0000000..9ce6584 --- /dev/null +++ b/app/routers_v3/transactions.py @@ -0,0 +1,176 @@ +from fastapi import APIRouter, Depends, HTTPException, Query, Response +from typing import List, Optional +from pandas import DataFrame +import logging +import pydantic + +from db_engine import db, Transaction, Team, Player, model_to_dict, chunked, fn +from dependencies import oauth2_scheme, valid_token, logging + +router = APIRouter( + prefix='/api/v3/transactions', + tags=['transactions'] +) + + +class TransactionModel(pydantic.BaseModel): + week: int + player_id: int + oldteam_id: int + newteam_id: int + season: int + moveid: str + cancelled: Optional[bool] = False + frozen: Optional[bool] = False + + +class TransactionList(pydantic.BaseModel): + count: int + moves: List[TransactionModel] + + +@router.get('') +async def get_transactions( + season, team_abbrev: Optional[str] = None, week_start: Optional[int] = 0, + week_end: Optional[int] = None, cancelled: Optional[bool] = None, frozen: Optional[bool] = None, + player_name: Optional[str] = None, player_id: Optional[int] = None, move_id: Optional[str] = None, + is_trade: Optional[bool] = None, short_output: Optional[bool] = False): + if season: + transactions = Transaction.select_season(season) + else: + transactions = Transaction.select() + + # if transactions.count() == 0: + # db.close() + # raise HTTPException(status_code=404, detail=f'Season {season} not found') + + if team_abbrev: + these_teams = Team.select().where( + (Team.abbrev == team_abbrev.upper()) | + (Team.abbrev == f'{team_abbrev.upper()}MiL') | (Team.abbrev == f'{team_abbrev.upper()}IL') + ) + if these_teams.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found') + + transactions = transactions.where( + (Transaction.newteam << these_teams) | (Transaction.oldteam << these_teams) + ) + + if week_start is not None: + transactions = transactions.where(Transaction.week >= week_start) + + if week_end is not None: + transactions = transactions.where(Transaction.week <= week_end) + + if move_id: + transactions = transactions.where(Transaction.moveid == move_id) + + if player_id or player_name: + if player_id: + try: + this_player = Player.get_by_id(player_id) + except Exception as e: + db.close() + raise HTTPException(status_code=404, detail=f'Player id {player_id} not found') + + transactions = transactions.where(Transaction.player == this_player) + else: + these_players = Player.select().where(fn.Lower(Player.name) == player_name.lower()) + print(f'these_players: {these_players}\nCount: {these_players.count()}') + if these_players.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Player {player_name} not found') + + transactions = transactions.where(Transaction.player << these_players) + + if cancelled: + transactions = transactions.where(Transaction.cancelled == 1) + else: + transactions = transactions.where(Transaction.cancelled == 0) + + if frozen: + transactions = transactions.where(Transaction.frozen == 1) + else: + transactions = transactions.where(Transaction.frozen == 0) + + if is_trade is not None: + raise HTTPException(status_code=501, detail='The is_trade parameter is not implemented, yet') + + transactions = transactions.order_by(-Transaction.week, Transaction.moveid) + + return_trans = { + 'count': transactions.count(), + 'transactions': [model_to_dict(x, recurse=not short_output) for x in transactions] + } + + db.close() + return return_trans + + +@router.patch('/{move_id}') +async def patch_transactions( + move_id, token: str = Depends(oauth2_scheme), frozen: Optional[bool] = None, cancelled: Optional[bool] = None): + if not valid_token(token): + logging.warning(f'patch_transactions - Bad Token: {token}') + raise HTTPException(status_code=401, detail='Unauthorized') + + these_moves = Transaction.select().where(Transaction.moveid == move_id) + if these_moves.count() == 0: + db.close() + raise HTTPException(status_code=404, detail=f'Move ID {move_id} not found') + + if frozen is not None: + for x in these_moves: + x.frozen = frozen + x.save() + if cancelled is not None: + for x in these_moves: + x.cancelled = cancelled + x.save() + + db.close() + raise HTTPException(status_code=200, detail=f'Updated {these_moves.count()} transactions') + + +@router.post('') +async def post_transactions(moves: TransactionList, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'post_transactions - Bad Token: {token}') + raise HTTPException(status_code=401, detail='Unauthorized') + + all_moves = [] + + for x in moves.moves: + if Team.get_or_none(Team.id == x.oldteam_id) is None: + raise HTTPException(status_code=404, detail=f'Team ID {x.oldteam_id} not found') + if Team.get_or_none(Team.id == x.newteam_id) is None: + raise HTTPException(status_code=404, detail=f'Team ID {x.newteam_id} not found') + if Player.get_or_none(Player.id == x.player_id): + raise HTTPException(status_code=404, detail=f'Player ID {x.player_id} not found') + + all_moves.append(x.dict()) + + with db.atomic(): + for batch in chunked(all_moves, 15): + Transaction.insert_many(batch).on_conflict_replace().execute() + + db.close() + raise HTTPException(status_code=200, detail=f'{len(all_moves)} transactions have been added') + + +@router.delete('/{move_id}') +async def delete_transactions(move_id, token: str = Depends(oauth2_scheme)): + if not valid_token(token): + logging.warning(f'delete_transactions - Bad Token: {token}') + raise HTTPException(status_code=401, detail='Unauthorized') + + delete_query = Transaction.delete().where(Transaction.moveid == move_id) + + count = delete_query.execute() + db.close() + if count > 0: + raise HTTPException(status_code=200, detail=f'Removed {count} transactions') + else: + raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; ' + f'I did not delete any records')