From 89aebd441de69d4c0a6ff64892561107d797d399 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sun, 24 Sep 2023 18:59:32 -0500 Subject: [PATCH] Phase 1 card images --- app/card_creation.py | 41 +++++++++++++++ app/main.py | 5 ++ app/routers_v2/battingcardratings.py | 31 +++++++---- app/routers_v2/players.py | 79 ++++++++++++++++++++++++++-- requirements.txt | 2 + 5 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 app/card_creation.py diff --git a/app/card_creation.py b/app/card_creation.py new file mode 100644 index 0000000..7f0f8fe --- /dev/null +++ b/app/card_creation.py @@ -0,0 +1,41 @@ +import pandas as pd + +chance_df = pd.DataFrame( + { + 'd20-1': [0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.25, 0.2, 0.15, 0.1, 0.05], + 'd20-2': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1], + 'd20-3': [0.15, 0.3, 0.45, 0.6, 0.75, 0.9, 0.75, 0.6, 0.45, 0.3, 0.15], + 'd20-4': [0.2, 0.4, 0.6, 0.8, 1, 1.2, 1, 0.8, 0.6, 0.4, 0.2], + 'd20-5': [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.25, 1, 0.75, 0.5, 0.25], + 'd20-6': [0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 1.5, 1.2, 0.9, 0.6, 0.3], + 'd20-7': [0.35, 0.7, 1.05, 1.4, 1.75, 2.1, 1.75, 1.4, 1.05, 0.7, 0.35], + 'd20-8': [0.4, 0.8, 1.2, 1.6, 2, 2.4, 2, 1.6, 1.2, 0.8, 0.4], + 'd20-9': [0.45, 0.9, 1.35, 1.8, 2.25, 2.7, 2.25, 1.8, 1.35, 0.9, 0.45], + 'd20-10': [0.5, 1, 1.5, 2, 2.5, 3, 2.5, 2, 1.5, 1, 0.5], + 'd20-11': [0.55, 1.1, 1.65, 2.2, 2.75, 3.3, 2.75, 2.2, 1.65, 1.1, 0.55], + 'd20-12': [0.6, 1.2, 1.8, 2.4, 3, 3.6, 3, 2.4, 1.8, 1.2, 0.6], + 'd20-13': [0.65, 1.3, 1.95, 2.6, 3.25, 3.9, 3.25, 2.6, 1.95, 1.3, 0.65], + 'd20-14': [0.7, 1.4, 2.1, 2.8, 3.5, 4.2, 3.5, 2.8, 2.1, 1.4, 0.7], + 'd20-15': [0.75, 1.5, 2.25, 3, 3.75, 4.5, 3.75, 3, 2.25, 1.5, 0.75], + 'd20-16': [0.8, 1.6, 2.4, 3.2, 4, 4.8, 4, 3.2, 2.4, 1.6, 0.8], + 'd20-17': [0.85, 1.7, 2.55, 3.4, 4.25, 5.1, 4.25, 3.4, 2.55, 1.7, 0.85], + 'd20-18': [0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 4.5, 3.6, 2.7, 1.8, 0.9], + 'd20-19': [0.95, 1.9, 2.85, 3.8, 4.75, 5.7, 4.75, 3.8, 2.85, 1.9, 0.95], + 'd20-20': [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1] + }, + index=[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +) + + +def get_batter_card_html(request, player, batting_card, ratings_vl, ratings_vr): + """ + create header_data + create column data + return data to be templated + """ + return f'

{player.p_name}

' + + +def get_batter_card_data(player, batting_card, ratings_vl, ratings_vr) -> dict: + + return {} diff --git a/app/main.py b/app/main.py index f187f3b..423c32a 100644 --- a/app/main.py +++ b/app/main.py @@ -1,4 +1,7 @@ +import os + from fastapi import FastAPI +from fastapi.templating import Jinja2Templates from .routers_v2 import ( current, teams, rarity, cardsets, players, packtypes, packs, cards, events, results, rewards, @@ -9,6 +12,8 @@ app = FastAPI( responses={404: {'description': 'Not found'}} ) +# templates = Jinja2Templates(directory=os.path.dirname(os.path.abspath(__file__))) + app.include_router(current.router) app.include_router(teams.router) app.include_router(rarity.router) diff --git a/app/routers_v2/battingcardratings.py b/app/routers_v2/battingcardratings.py index 987825f..d7f25ff 100644 --- a/app/routers_v2/battingcardratings.py +++ b/app/routers_v2/battingcardratings.py @@ -1,10 +1,10 @@ -from fastapi import APIRouter, Depends, HTTPException, Query +from fastapi import APIRouter, Depends, HTTPException, Query, Response 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 ..db_engine import db, BattingCardRatings, model_to_dict, chunked, BattingCard, Player, query_to_csv from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( @@ -74,7 +74,7 @@ class BattingCardRatingsModel(pydantic.BaseModel): values['flyout_bq'] + values['flyout_lf_b'] + values['flyout_rf_b'] + values['groundout_a'] + values['groundout_b'] + values['groundout_c']) - if total_chances != 108: + if round(total_chances) != 108: raise ValueError("Must have exactly 108 chances on the card") return values @@ -86,7 +86,8 @@ class RatingsList(pydantic.BaseModel): @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)): + short_output: bool = False, csv: bool = False, cardset_id: list = Query(default=None), + token: str = Depends(oauth2_scheme)): if not valid_token(token): logging.warning(f'Bad Token: {token}') db.close() @@ -100,13 +101,23 @@ async def get_card_ratings( 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]) + all_ratings = all_ratings.where(BattingCardRatings.vs_hand == vs_hand[-1]) + if cardset_id is not None: + set_players = Player.select(Player.player_id).where(Player.cardset_id << cardset_id) + set_cards = BattingCard.select(BattingCard.id).where(BattingCard.player << set_players) + all_ratings = all_ratings.where(BattingCardRatings.battingcard << set_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 + if csv: + return_val = query_to_csv(all_ratings) + db.close() + return Response(content=return_val, media_type='text/csv') + + else: + 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}') diff --git a/app/routers_v2/players.py b/app/routers_v2/players.py index d2f00ff..3f39c43 100644 --- a/app/routers_v2/players.py +++ b/app/routers_v2/players.py @@ -1,10 +1,17 @@ -from fastapi import APIRouter, Depends, HTTPException, Response, Query +import os.path + +from fastapi import APIRouter, Depends, HTTPException, Request, Response, Query +from fastapi.responses import FileResponse +from fastapi.templating import Jinja2Templates +from html2image import Html2Image from typing import Optional, List import logging import pydantic from pandas import DataFrame -from ..db_engine import db, Player, model_to_dict, fn, chunked, Paperdex, Cardset, Rarity +from ..card_creation import get_batter_card_html, get_batter_card_data +from ..db_engine import db, Player, model_to_dict, fn, chunked, Paperdex, Cardset, Rarity, BattingCard, \ + BattingCardRatings from ..dependencies import oauth2_scheme, valid_token, LOG_DATA logging.basicConfig( @@ -19,6 +26,9 @@ router = APIRouter( ) +templates = Jinja2Templates(directory="storage/templates") + + class PlayerPydantic(pydantic.BaseModel): player_id: int = None p_name: str @@ -60,7 +70,7 @@ async def get_players( has_vanity_card: Optional[bool] = None, strat_code: Optional[str] = None, bbref_id: Optional[str] = None, fangr_id: Optional[str] = None, inc_dex: Optional[bool] = True, in_desc: Optional[str] = None, flat: Optional[bool] = False, sort_by: Optional[str] = False, cardset_id_exclude: list = Query(default=None), - limit: Optional[int] = None, csv: Optional[bool] = None): + limit: Optional[int] = None, csv: Optional[bool] = None, short_output: Optional[bool] = False): all_players = Player.select() if all_players.count() == 0: db.close() @@ -158,7 +168,7 @@ async def get_players( return_val = {'count': len(final_players), 'players': []} for x in final_players: - this_record = model_to_dict(x, recurse=not flat) + this_record = model_to_dict(x, recurse=not (flat or short_output)) if inc_dex: this_dex = Paperdex.select().where(Paperdex.player == x) @@ -319,6 +329,67 @@ async def get_one_player(player_id, csv: Optional[bool] = False): return return_val +@router.get('/{player_id}/batting-card') +async def get_player_card( + request: Request, player_id: int, variant: int = 0, d: str = None, html: Optional[bool] = False): + if os.path.isfile(f'storage/cards/{player_id}-{d}-v{variant}.png') and html is False: + db.close() + return FileResponse( + path=f'storage/cards/{player_id}-{d}-v{variant}.png', + media_type='image/png' + ) + + try: + this_player = Player.get_by_id(player_id) + except Exception: + db.close() + raise HTTPException(status_code=404, detail=f'No player found with id {player_id}') + + this_bc = BattingCard.get_or_none(BattingCard.player == this_player, BattingCard.variant == variant) + if this_bc is None: + raise HTTPException(status_code=404, detail=f'Batting card not found for id {player_id}, variant {variant}') + + rating_vl = BattingCardRatings.get_or_none( + BattingCardRatings.battingcard == this_bc, BattingCardRatings.vs_hand == 'L') + rating_vr = BattingCardRatings.get_or_none( + BattingCardRatings.battingcard == this_bc, BattingCardRatings.vs_hand == 'R') + if None in [rating_vr, rating_vl]: + raise HTTPException(status_code=404, detail=f'Ratings not found for batting card {this_bc.id}') + + hti = Html2Image( + browser='chromium', + size=(1200, 600), + output_path=f'storage/cards', + custom_flags=['--no-sandbox', '--disable-remote-debugging', '--headless', '--disable-gpu', + '--disable-software-rasterizer', '--disable-dev-shm-usage'] + ) + card_data = { + 'player': this_player, + 'card_type': 'batter', + 'results_vl_one': 'Big Dongs', + 'results_vl_two': 'Lesser Dongs', + 'results_vl_three': 'Sad Dongs', + 'results_vr_one': 'Light Dongs', + 'results_vr_two': 'Hefty Dongs', + 'results_vr_three': 'Obese Dongs', + 'request': request + } + html_response = templates.TemplateResponse("player_card.html", card_data) + + if html: + db.close() + return html_response + + logging.debug(f'body:\n{html_response.body.decode("UTF-8")}') + x = hti.screenshot( + html_str=str(html_response.body.decode("UTF-8")), + save_as=f'{player_id}-{d}-v{variant}.png' + ) + + db.close() + return FileResponse(path=x[0], media_type='image/png') + + @router.patch('/{player_id}') async def v1_players_patch( player_id, name: Optional[str] = None, image: Optional[str] = None, image2: Optional[str] = None, diff --git a/requirements.txt b/requirements.txt index e6c901c..9d19235 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,5 @@ pygsheets pybaseball python-multipart requests +html2image +jinja2