Added battingcards and ratings

This commit is contained in:
Cal Corum 2023-09-15 22:38:15 -05:00
parent 88b723d9f2
commit 22cc01d200
5 changed files with 260 additions and 50 deletions

View File

@ -28,9 +28,6 @@ logging.basicConfig(
def model_csv_headers(this_obj, exclude=None) -> List:
if this_obj is None:
return ['None']
data = model_to_dict(this_obj, recurse=False, exclude=exclude)
return [x for x in data.keys()]
@ -41,9 +38,47 @@ def model_to_csv(this_obj, exclude=None) -> List:
def query_to_csv(all_items: ModelSelect, exclude=None):
data_list = [model_csv_headers(all_items[0], exclude=exclude)]
for x in all_items:
data_list.append(model_to_csv(x, exclude=exclude))
if all_items.count() == 0:
data_list = [['No data found']]
else:
data_list = [model_csv_headers(all_items[0], exclude=exclude)]
for x in all_items:
data_list.append(model_to_csv(x, exclude=exclude))
return DataFrame(data_list).to_csv(header=False, index=False)
def complex_data_to_csv(complex_data: List):
if len(complex_data) == 0:
data_list = [['No data found']]
else:
data_list = [[x for x in complex_data[0].keys()]]
for line in complex_data:
logging.info(f'line: {line}')
this_row = []
for key in line:
logging.info(f'key: {key}')
if line[key] is None:
this_row.append('')
elif isinstance(line[key], dict):
if 'name' in line[key]:
this_row.append(line[key]['name'])
elif 'abbrev' in line[key]:
this_row.append(line[key]['abbrev'])
else:
this_row.append(line[key]['id'])
elif isinstance(line[key], int) and line[key] > 100000000:
this_row.append(f"'{line[key]}")
elif isinstance(line[key], str) and ',' in line[key]:
this_row.append(line[key].replace(",", "-_-"))
else:
this_row.append(line[key])
data_list.append(this_row)
return DataFrame(data_list).to_csv(header=False, index=False)
@ -526,7 +561,7 @@ BattingCard.add_index(BattingCard.player, BattingCard.variant)
class BattingCardRatings(BaseModel):
battingcard = ForeignKeyField(BattingCard)
vs_hand = FloatField()
vs_hand = CharField(default='R')
homerun = FloatField()
bp_homerun = FloatField()
triple = FloatField()
@ -554,8 +589,12 @@ class BattingCardRatings(BaseModel):
slg = FloatField(null=True)
BattingCardRatings.add_index(BattingCardRatings.battingcard, BattingCardRatings.vs_hand)
class PitchingCard(BaseModel):
player = ForeignKeyField(Player)
variant = IntegerField()
balk = IntegerField()
wild_pitch = IntegerField(null=True)
hold = CharField()
@ -565,9 +604,12 @@ class PitchingCard(BaseModel):
batting = CharField(null=True)
PitchingCard.add_index(PitchingCard.player, PitchingCard.variant)
class PitchingCardRatings(BaseModel):
pitchingcard = ForeignKeyField(PitchingCard)
vs_hand = CharField()
vs_hand = CharField(default='R')
homerun = FloatField()
bp_homerun = FloatField()
triple = FloatField()

View File

@ -1,7 +1,9 @@
from fastapi import FastAPI
from.routers_v2 import current, teams, rarity, cardsets, players, packtypes, packs, cards, events, results, rewards, \
batstats, pitstats, notifications, paperdex, gamerewards, gauntletrewards, gauntletruns, battingcard
from.routers_v2 import (
current, teams, rarity, cardsets, players, packtypes, packs, cards, events, results, rewards,
batstats, pitstats, notifications, paperdex, gamerewards, gauntletrewards, gauntletruns, battingcards,
battingcardratings)
app = FastAPI(
responses={404: {'description': 'Not found'}}
@ -25,4 +27,5 @@ app.include_router(paperdex.router)
app.include_router(gamerewards.router)
app.include_router(gauntletrewards.router)
app.include_router(gauntletruns.router)
app.include_router(battingcard.router)
app.include_router(battingcards.router)
app.include_router(battingcardratings.router)

View File

@ -0,0 +1,182 @@
from fastapi import APIRouter, Depends, HTTPException, Query
from typing import Literal, Optional, List
import logging
import pydantic
from pydantic import validator, root_validator
from ..db_engine import db, BattingCardRatings, model_to_dict, chunked, BattingCard
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/battingcardratings',
tags=['battingcardratings']
)
class BattingCardRatingsModel(pydantic.BaseModel):
battingcard_id: int
vs_hand: Literal['R', 'L', 'vR', 'vL']
homerun: float = 0.0
bp_homerun: float = 0.0
triple: float = 0.0
double_three: float = 0.0
double_two: float = 0.0
double_pull: float = 0.0
single_two: float = 0.0
single_one: float = 0.0
single_center: float = 0.0
bp_single: float = 0.0
hbp: float = 0.0
walk: float = 0.0
strikeout: float = 0.0
lineout: float = 0.0
popout: float = 0.0
flyout_a: float = 0.0
flyout_bq: float = 0.0
flyout_lf_b: float = 0.0
flyout_rf_b: float = 0.0
groundout_a: float = 0.0
groundout_b: float = 0.0
groundout_c: float = 0.0
avg: float = 0.0
obp: float = 0.0
slg: float = 0.0
@validator("avg", always=True)
def avg_validator(cls, v, values, **kwargs):
return (values['homerun'] + values['bp_homerun'] / 2 + values['triple'] + values['double_three'] +
values['double_two'] + values['double_pull'] + values['single_two'] + values['single_one'] +
values['single_center'] + values['bp_single'] / 2) / 108
@validator("obp", always=True)
def obp_validator(cls, v, values, **kwargs):
return ((values['hbp'] + values['walk']) / 108) + values['avg']
@validator("slg", always=True)
def slg_validator(cls, v, values, **kwargs):
return (values['homerun'] * 4 + values['bp_homerun'] * 2 + values['triple'] * 3 + values['double_three'] * 2 +
values['double_two'] * 2 + values['double_pull'] * 2 + values['single_two'] + values['single_one'] +
values['single_center'] + values['bp_single'] / 2) / 108
@root_validator
def validate_chance_total(cls, values):
total_chances = (
values['homerun'] + values['bp_homerun'] + values['triple'] + values['double_three'] +
values['double_two'] + values['double_pull'] + values['single_two'] + values['single_one'] +
values['single_center'] + values['bp_single'] + values['hbp'] + values['walk'] +
values['strikeout'] + values['lineout'] + values['popout'] + values['flyout_a'] +
values['flyout_bq'] + values['flyout_lf_b'] + values['flyout_rf_b'] + values['groundout_a'] +
values['groundout_b'] + values['groundout_c'])
if total_chances != 108:
raise ValueError("Must have exactly 108 chances on the card")
return values
class RatingsList(pydantic.BaseModel):
ratings: List[BattingCardRatingsModel]
@router.get('')
async def get_card_ratings(
battingcard_id: list = Query(default=None), vs_hand: Literal['R', 'L', 'vR', 'vL'] = None,
short_output: bool = False, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'Bad Token: {token}')
db.close()
raise HTTPException(
status_code=401,
detail='You are not authorized to pull card ratings.'
)
all_ratings = BattingCardRatings.select()
if battingcard_id is not None:
all_ratings = all_ratings.where(BattingCardRatings.battingcard_id << battingcard_id)
if vs_hand is not None:
all_ratings = all_ratings.where(BattingCardRatings.vs_hand << vs_hand[-1])
return_val = {'count': all_ratings.count(), 'ratings': [
model_to_dict(x, recurse=not short_output) for x in all_ratings
]}
db.close()
return return_val
@router.get('/{ratings_id}')
async def get_one_rating(ratings_id: int):
this_rating = BattingCardRatings.get_or_none(BattingCardRatings.id == ratings_id)
if this_rating is None:
db.close()
raise HTTPException(status_code=404, detail=f'BattingCardRating id {ratings_id} not found')
r_data = model_to_dict(this_rating)
db.close()
return r_data
@router.get('')
async def get_player_ratings(player_id: int, variant: list = Query(default=None), short_output: bool = False):
all_cards = BattingCard.select().where(BattingCard.player_id == player_id).order_by(BattingCard.variant)
if variant is not None:
all_cards = all_cards.where(BattingCard.variant << variant)
all_ratings = BattingCardRatings.select().where(BattingCardRatings.battingcard << all_cards)
return_val = {'count': all_ratings.count(), 'ratings': [
model_to_dict(x, recurse=not short_output) for x in all_ratings
]}
db.close()
return return_val
@router.put('')
async def put_ratings(ratings: RatingsList, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'Bad Token: {token}')
db.close()
raise HTTPException(
status_code=401,
detail='You are not authorized to post card ratings.'
)
new_ratings = [x.dict() for x in ratings.ratings]
with db.atomic():
for batch in chunked(new_ratings, 30):
BattingCardRatings.insert_many(batch).on_conflict_replace().execute()
db.close()
return f'Inserted {len(new_ratings)} batting ratings'
@router.delete('/{ratings_id}')
async def put_one_rating(
ratings_id: int, this_rating: BattingCardRatingsModel, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f'Bad Token: {token}')
db.close()
raise HTTPException(
status_code=401,
detail='You are not authorized to post card ratings.'
)
this_rating = BattingCardRatings.get_or_none(BattingCardRatings.id == ratings_id)
if this_rating is None:
db.close()
raise HTTPException(status_code=404, detail=f'BattingCardRating id {ratings_id} not found')
count = this_rating.delete_instance()
db.close()
if count == 1:
return f'Rating {this_rating} has been deleted'
else:
raise HTTPException(status_code=500, detail=f'Rating {this_rating} could not be deleted')

View File

@ -1,10 +1,9 @@
from fastapi import APIRouter, Depends, HTTPException, Response, Query
from fastapi import APIRouter, Depends, HTTPException, Query
from typing import Literal, Optional, List
import logging
import pydantic
from pandas import DataFrame
from ..db_engine import db, BattingCard, model_to_dict, fn, chunked
from ..db_engine import db, BattingCard, model_to_dict, chunked, Player
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
logging.basicConfig(
@ -14,13 +13,14 @@ logging.basicConfig(
)
router = APIRouter(
prefix='/api/v2/battingcard',
tags=['battingcard']
prefix='/api/v2/battingcards',
tags=['battingcards']
)
class BattingCardModel(pydantic.BaseModel):
player_id: int
variant: int = 0
steal_low: int = 3
steal_high: int = 20
steal_auto: bool = False
@ -29,7 +29,7 @@ class BattingCardModel(pydantic.BaseModel):
hit_and_run: str = 'C'
running: int = 10
offense_col: int
hand: Literal['R', 'L', 'S']
hand: Literal['R', 'L', 'S'] = 'R'
class BattingCardList(pydantic.BaseModel):
@ -37,13 +37,17 @@ class BattingCardList(pydantic.BaseModel):
@router.get('')
async def get_batting_cards(player_id: list = Query(default=None), short_output: bool = False):
async def get_batting_cards(
player_id: list = Query(default=None), cardset_id: list = Query(default=None), short_output: bool = False):
all_cards = BattingCard.select()
if player_id is not None:
all_cards = all_cards.where(BattingCard.player_id << player_id)
if cardset_id is not None:
all_players = Player.select().where(Player.cardset_id << cardset_id)
all_cards = all_cards.where(BattingCard.player << all_players)
return_val = {'count': all_cards.count(), 'cards': [
{model_to_dict(x, recurse=not short_output)} for x in all_cards
model_to_dict(x, recurse=not short_output) for x in all_cards
]}
db.close()
return return_val
@ -68,7 +72,7 @@ async def get_player_cards(player_id: int, variant: list = Query(default=None),
all_cards = all_cards.where(BattingCard.variant << variant)
return_val = {'count': all_cards.count(), 'cards': [
{model_to_dict(x, recurse=not short_output) for x in all_cards}
model_to_dict(x, recurse=not short_output) for x in all_cards
]}
db.close()
return return_val

View File

@ -6,7 +6,7 @@ import pydantic
from pandas import DataFrame
from ..db_engine import db, Team, model_to_dict, fn, Pack, Card, Player, Paperdex, Notification, PackType, \
Rarity, Current, query_to_csv
Rarity, Current, query_to_csv, complex_data_to_csv
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, int_timestamp
logging.basicConfig(
@ -116,22 +116,7 @@ async def get_teams(
if limit is not None:
all_teams = all_teams.limit(limit)
# if all_teams.count() == 0:
# db.close()
# raise HTTPException(status_code=404, detail=f'No teams found')
if csv:
# data_list = [[
# 'id', 'abbrev', 'sname', 'lname', 'gmid', 'gmname', 'wallet', 'gsheet', 'team_value',
# 'collection_value', 'logo', 'color', 'season', 'ranking'
# ]]
# for line in all_teams:
# data_list.append(
# [
# line.id, line.abbrev, line.sname, line.lname, line.gmid, line.gmname, line.wallet, line.gsheet,
# line.team_value, line.collection_value, line.logo, f'\'{line.color}', line.season, line.ranking
# ]
# )
return_val = query_to_csv(all_teams, exclude=[Team.career])
db.close()
return Response(content=return_val, media_type='text/csv')
@ -153,23 +138,17 @@ async def get_one_team(team_id, csv: Optional[bool] = False):
db.close()
raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')
p_query = Pack.select().where((Pack.team == this_team) & (Pack.open_time.is_null(True)))
if csv:
team_packs = Pack.select().where((Pack.team == this_team) & (Pack.open_time.is_null(True)))
data_list = [
['id', 'abbrev', 'sname', 'lname', 'gmid', 'gmname', 'wallet', 'ranking', 'gsheet', 'sealed_packs',
'collection_value', 'logo', 'color', 'season'],
[this_team.id, this_team.abbrev, this_team.sname, this_team.lname, this_team.gmid, this_team.gmname,
this_team.wallet, this_team.ranking, this_team.gsheet, team_packs.count(), this_team.collection_value,
this_team.logo, this_team.color, this_team.season]
]
return_val = DataFrame(data_list).to_csv(header=False, index=False)
db.close()
return Response(content=return_val, media_type='text/csv')
data = model_to_dict(this_team)
data['sealed_packs'] = p_query.count()
return_val = complex_data_to_csv([data])
else:
return_val = model_to_dict(this_team)
db.close()
return return_val
return_val['sealed_packs'] = [model_to_dict(x) for x in p_query]
db.close()
return return_val
@router.get('/{team_id}/buy/players')