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, PRIVATE_IN_SCHEMA, handle_db_errors, ) logger = logging.getLogger("discord_app") 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("") @handle_db_errors async def get_transactions( season, team_abbrev: list = Query(default=None), week_start: Optional[int] = 0, week_end: Optional[int] = None, cancelled: Optional[bool] = None, frozen: Optional[bool] = None, player_name: list = Query(default=None), player_id: list = Query(default=None), move_id: Optional[str] = None, is_trade: Optional[bool] = None, short_output: Optional[bool] = False, ): if season: transactions = Transaction.select_season(season) else: transactions = Transaction.select() if team_abbrev is not None: t_list = [x.upper() for x in team_abbrev] these_teams = Team.select().where(fn.UPPER(Team.abbrev) << t_list) transactions = transactions.where( (Transaction.newteam << these_teams) | (Transaction.oldteam << these_teams) ) if week_start is not None: transactions = transactions.where(Transaction.week >= week_start) if week_end is not None: transactions = transactions.where(Transaction.week <= week_end) if move_id: transactions = transactions.where(Transaction.moveid == move_id) if player_id or player_name: if player_id: p_list = Player.select().where(Player.id << player_id) transactions = transactions.where(Transaction.player << p_list) else: p_list = [x.lower() for x in player_name] these_players = Player.select().where(fn.Lower(Player.name) << p_list) transactions = transactions.where(Transaction.player << these_players) if cancelled: transactions = transactions.where(Transaction.cancelled == 1) else: transactions = transactions.where(Transaction.cancelled == 0) if frozen: transactions = transactions.where(Transaction.frozen == 1) else: transactions = transactions.where(Transaction.frozen == 0) if is_trade is not None: raise HTTPException( status_code=501, detail="The is_trade parameter is not implemented, yet" ) transactions = transactions.order_by(-Transaction.week, Transaction.moveid) return_trans = { "count": transactions.count(), "transactions": [ model_to_dict(x, recurse=not short_output) for x in transactions ], } db.close() return return_trans @router.patch("/{move_id}", include_in_schema=PRIVATE_IN_SCHEMA) @handle_db_errors async def patch_transactions( move_id, token: str = Depends(oauth2_scheme), frozen: Optional[bool] = None, cancelled: Optional[bool] = None, ): if not valid_token(token): logger.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() return f"Updated {these_moves.count()} transactions" @router.post("/", include_in_schema=PRIVATE_IN_SCHEMA) @handle_db_errors async def post_transactions( moves: TransactionList, token: str = Depends(oauth2_scheme) ): if not valid_token(token): logger.warning(f"post_transactions - Bad Token: {token}") raise HTTPException(status_code=401, detail="Unauthorized") all_moves = [] all_team_ids = list( set(x.oldteam_id for x in moves.moves) | set(x.newteam_id for x in moves.moves) ) all_player_ids = list(set(x.player_id for x in moves.moves)) found_team_ids = set( t.id for t in Team.select(Team.id).where(Team.id << all_team_ids) ) found_player_ids = set( p.id for p in Player.select(Player.id).where(Player.id << all_player_ids) ) for x in moves.moves: if x.oldteam_id not in found_team_ids: raise HTTPException( status_code=404, detail=f"Team ID {x.oldteam_id} not found" ) if x.newteam_id not in found_team_ids: raise HTTPException( status_code=404, detail=f"Team ID {x.newteam_id} not found" ) if x.player_id not in found_player_ids: 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_ignore().execute() db.close() return f"{len(all_moves)} transactions have been added" @router.delete("/{move_id}", include_in_schema=PRIVATE_IN_SCHEMA) @handle_db_errors async def delete_transactions(move_id, token: str = Depends(oauth2_scheme)): if not valid_token(token): logger.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: return 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", )