Download app.py from TJStatsApps/pitching_summary_complete: direct link, hf CLI and curl.
- Browser
- Download file 34.6 kB
-
https://huggingface.co/spaces/TJStatsApps/pitching_summary_complete/resolve/main/app.py
- Command line
-
hf download hf://spaces/TJStatsApps/pitching_summary_complete/app.py
-
curl -L -o app.py https://huggingface.co/spaces/TJStatsApps/pitching_summary_complete/resolve/main/app.py
34.6 kB
| import polars as pl | |
| import numpy as np | |
| import pandas as pd | |
| import api_scraper | |
| scrape = api_scraper.MLB_Scrape() | |
| from functions import df_update | |
| from functions import pitch_summary_functions | |
| update = df_update.df_update() | |
| from stuff_model import feature_engineering as fe | |
| from stuff_model import stuff_apply | |
| import requests | |
| import joblib | |
| from matplotlib.gridspec import GridSpec | |
| from shiny import App, reactive, ui, render | |
| from shiny.ui import h2, tags | |
| import matplotlib.pyplot as plt | |
| import matplotlib.gridspec as gridspec | |
| import seaborn as sns | |
| from functions.pitch_summary_functions import * | |
| from shiny import App, reactive, ui, render | |
| from shiny.ui import h2, tags | |
| colour_palette = ['#FFB000','#648FFF','#785EF0', | |
| '#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED'] | |
| year_list = [2017,2018,2019,2020,2021,2022,2023,2024,2025,2026] | |
| level_dict = {'1':'MLB', | |
| '11':'AAA', | |
| # '12':'AA', | |
| #'13':'A+', | |
| '14':'A', | |
| '16':'ROK', | |
| '17':'AFL', | |
| '22':'College', | |
| '21':'Prospects', | |
| '51':'International' } | |
| function_dict={ | |
| 'velocity_kdes':'Velocity Distributions', | |
| 'break_plot':'Pitch Movement', | |
| 'tj_stuff_roling':'Rolling tjStuff+ by Pitch', | |
| 'tj_stuff_roling_game':'Rolling tjStuff+ by Game', | |
| 'location_plot_lhb':'Locations vs LHB', | |
| 'location_plot_rhb':'Locations vs RHB', | |
| 'pitch_usage':'Pitch Usage', | |
| } | |
| split_dict = {'all':'All', | |
| 'left':'LHH', | |
| 'right':'RHH'} | |
| split_dict_hand = {'all':['L','R'], | |
| 'left':['L'], | |
| 'right':['R']} | |
| type_dict = {'R':'Regular Season', | |
| 'S':'Spring', | |
| 'P':'Playoffs' } | |
| # List of MLB teams and their corresponding ESPN logo URLs | |
| mlb_teams = [ | |
| {"team": "AZ", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/ari.png&h=500&w=500"}, | |
| {"team": "ATH", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/oak.png&h=500&w=500"}, | |
| {"team": "ATL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/atl.png&h=500&w=500"}, | |
| {"team": "BAL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/bal.png&h=500&w=500"}, | |
| {"team": "BOS", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/bos.png&h=500&w=500"}, | |
| {"team": "CHC", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/chc.png&h=500&w=500"}, | |
| {"team": "CWS", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/chw.png&h=500&w=500"}, | |
| {"team": "CIN", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/cin.png&h=500&w=500"}, | |
| {"team": "CLE", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/cle.png&h=500&w=500"}, | |
| {"team": "COL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/col.png&h=500&w=500"}, | |
| {"team": "DET", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/det.png&h=500&w=500"}, | |
| {"team": "HOU", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/hou.png&h=500&w=500"}, | |
| {"team": "KC", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/kc.png&h=500&w=500"}, | |
| {"team": "LAA", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/laa.png&h=500&w=500"}, | |
| {"team": "LAD", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/lad.png&h=500&w=500"}, | |
| {"team": "MIA", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/mia.png&h=500&w=500"}, | |
| {"team": "MIL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/mil.png&h=500&w=500"}, | |
| {"team": "MIN", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/min.png&h=500&w=500"}, | |
| {"team": "NYM", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/nym.png&h=500&w=500"}, | |
| {"team": "NYY", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/nyy.png&h=500&w=500"}, | |
| {"team": "PHI", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/phi.png&h=500&w=500"}, | |
| {"team": "PIT", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/pit.png&h=500&w=500"}, | |
| {"team": "SD", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sd.png&h=500&w=500"}, | |
| {"team": "SF", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sf.png&h=500&w=500"}, | |
| {"team": "SEA", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sea.png&h=500&w=500"}, | |
| {"team": "STL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/stl.png&h=500&w=500"}, | |
| {"team": "TB", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tb.png&h=500&w=500"}, | |
| {"team": "TEX", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tex.png&h=500&w=500"}, | |
| {"team": "TOR", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tor.png&h=500&w=500"}, | |
| {"team": "WSH", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/wsh.png&h=500&w=500"}, | |
| {"team": "ZZZ", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/leagues/500/mlb.png&w=500&h=500"} | |
| ] | |
| df_image = pd.DataFrame(mlb_teams) | |
| image_dict = df_image.set_index('team')['logo_url'].to_dict() | |
| image_dict_flip = df_image.set_index('logo_url')['team'].to_dict() | |
| import requests | |
| import os | |
| CAMPAIGN_ID = os.getenv("CAMPAIGN_ID") | |
| ACCESS_TOKEN = os.getenv("ACCESS_TOKEN") | |
| BACKUP_PW = os.getenv("BACKUP_PW") | |
| ADMIN_PW = os.getenv("ADMIN_PW") | |
| url = f"https://www.patreon.com/api/oauth2/v2/campaigns/{CAMPAIGN_ID}/members" | |
| headers = { | |
| "Authorization": f"Bearer {ACCESS_TOKEN}" | |
| } | |
| # Simple parameters, requesting the member's email and currently entitled tiers | |
| params = { | |
| "fields[member]": "full_name,email", # Request the member's email | |
| "include": "currently_entitled_tiers", # Include the currently entitled tiers | |
| "page[size]": 10000 # Fetch up to 1000 patrons per request | |
| } | |
| response = requests.get(url, headers=headers, params=params) | |
| VALID_PASSWORDS = [] | |
| if response.status_code == 200: | |
| data = response.json() | |
| for patron in data['data']: | |
| try: | |
| tiers = patron['relationships']['currently_entitled_tiers']['data'] | |
| if any(tier['id'] == '9078921' for tier in tiers): | |
| full_name = patron['attributes']['email'] | |
| VALID_PASSWORDS.append(full_name) | |
| except KeyError: | |
| continue | |
| VALID_PASSWORDS.append(BACKUP_PW) | |
| VALID_PASSWORDS.append(ADMIN_PW) | |
| from shiny import App, reactive, ui, render | |
| from shiny.ui import h2, tags | |
| from datetime import datetime | |
| def is_valid_date(date_str): | |
| try: | |
| datetime.strptime(date_str, "%Y-%m-%d") # Attempt to parse the date | |
| return True | |
| except ValueError: | |
| return False # If parsing fails, it's not in the correct format | |
| # Define the login UI | |
| login_ui = ui.page_fluid( | |
| ui.card( | |
| ui.h2([ | |
| "TJStats Pitching Summary App ", | |
| ui.tags.a("(@TJStats)", href="https://twitter.com/TJStats", target="_blank") | |
| ]), | |
| ui.p( | |
| "This App is available to Superstar Patrons. Please enter your Patreon email address in the box below. If you're having trouble, please refer to the ", | |
| ui.tags.a("Patreon post", href="https://www.patreon.com/posts/116064432", target="_blank"), | |
| "." | |
| ), | |
| ui.input_password("password", "Enter Patreon Email (or Password from Link):", width="50%"), | |
| ui.tags.input( | |
| type="checkbox", | |
| id="authenticated", | |
| value=False, | |
| disabled=True | |
| ), | |
| ui.input_action_button("login", "Login", class_="btn-primary"), | |
| ui.output_text("login_message"), | |
| ) | |
| ) | |
| # Define the UI layout for the app | |
| main_ui = ui.page_sidebar( | |
| ui.sidebar( | |
| # Row for selecting season and level | |
| ui.row( | |
| ui.column(4, ui.input_select('year_input', 'Select Season', year_list, selected=2026)), | |
| ui.column(4, ui.input_select('level_input', 'Select Level', level_dict)), | |
| ui.column(4, ui.input_select('type_input', 'Select Type', type_dict,selected='R')) | |
| ), | |
| # Row for the action button to get player list | |
| ui.row(ui.input_action_button("player_button", "Get Player List", class_="btn-primary")), | |
| # Row for selecting the player | |
| ui.row(ui.column(12, ui.output_ui('player_select_ui', 'Select Player'))), | |
| # Row for selecting the date range | |
| ui.row(ui.column(12, ui.output_ui('date_id', 'Select Date'))), | |
| # Rows for selecting plots and split options | |
| ui.row( | |
| ui.column(4, ui.input_select('plot_id_1', 'Plot Left', function_dict, multiple=False, selected='velocity_kdes')), | |
| ui.column(4, ui.input_select('plot_id_2', 'Plot Middle', function_dict, multiple=False, selected='break_plot')), | |
| ui.column(4, ui.input_select('plot_id_3', 'Plot Right', function_dict, multiple=False, selected='pitch_usage')) | |
| ), | |
| ui.row( | |
| ui.column(6, ui.input_select('split_id', 'Select Split', split_dict, multiple=False)), | |
| ui.column(6, ui.input_numeric('rolling_window', 'Rolling Window (for tjStuff+ Plot)', min=1, value=50)) | |
| ), | |
| ui.row( | |
| ui.column(6, ui.input_switch("switch", "Custom Team?", False)), | |
| ui.column(6, ui.input_select('logo_select', 'Select Custom Logo', image_dict_flip, multiple=False)) | |
| ), | |
| # Row for the action button to generate plot | |
| ui.row(ui.input_action_button("generate_plot", "Generate Plot", class_="btn-primary")), | |
| width="400px" # Added this parameter to control sidebar width | |
| ), | |
| # Main content area with tabs (placed directly in page_sidebar) | |
| ui.navset_tab( | |
| ui.nav_panel("Pitching Summary", | |
| ui.output_text("status"), | |
| ui.output_plot('plot', width='2100px', height='2100px') | |
| ), | |
| ui.nav_panel("Game Summary", | |
| ui.output_text("status2"), | |
| ui.output_plot('game_plot', width='2100px', height='2100px') | |
| ), | |
| ui.nav_panel("Table Range", | |
| ui.output_data_frame("grid")), | |
| ui.nav_panel("Table Game", | |
| ui.output_data_frame("grid_game")), | |
| id="tabset" | |
| ) | |
| ) | |
| # Combined UI with conditional panel | |
| app_ui = ui.page_fluid( | |
| ui.tags.head( | |
| ui.tags.script(src="script.js") | |
| ), | |
| ui.panel_conditional( | |
| "!input.authenticated", | |
| login_ui | |
| ), | |
| ui.panel_conditional( | |
| "input.authenticated", | |
| main_ui | |
| ) | |
| ) | |
| def server(input, output, session): | |
| def check_password(): | |
| if input.password() in VALID_PASSWORDS: | |
| ui.update_checkbox("authenticated", value=True) | |
| ui.update_text("login_message", value="") | |
| else: | |
| ui.update_text("login_message", value="Invalid password!") | |
| ui.update_text("password", value="") | |
| def login_message(): | |
| return "" | |
| def cached_data(): | |
| year_input = int(input.year_input()) | |
| sport_id = int(input.level_input()) | |
| player_input = int(input.pitcher_id()) | |
| start_date = str(input.date_id()[0]) | |
| end_date = str(input.date_id()[1]) | |
| game_list = scrape.get_player_games_list(sport_id = sport_id, | |
| season = year_input, | |
| player_id = player_input, | |
| start_date = start_date, | |
| end_date = end_date, | |
| game_type = [input.type_input()]) | |
| # if input.tabset() == 'Game Summary': | |
| # print(year_input, sport_id, player_input, 'yup') | |
| # print(input.date_id()) | |
| # game_list = [input.date_id()] | |
| data_list = scrape.get_data(game_list_input = game_list[:]) | |
| try: | |
| df = (stuff_apply.stuff_apply(fe.feature_engineering(update.update(scrape.get_data_df(data_list = data_list).filter( | |
| (pl.col("pitcher_id") == player_input)& | |
| (pl.col("is_pitch") == True)& | |
| (pl.col("start_speed") >= 50)& | |
| (pl.col('batter_hand').is_in(split_dict_hand[input.split_id()])) | |
| )))).with_columns( | |
| pl.col('pitch_type').count().over('pitch_type').alias('pitch_count') | |
| )) | |
| return df | |
| except TypeError: | |
| print("NONE") | |
| return None | |
| def cached_data_daily(): | |
| year_input = int(input.year_input()) | |
| sport_id = int(input.level_input()) | |
| player_input = int(input.pitcher_id()) | |
| # start_date = str(input.date_id()[0]) | |
| # end_date = str(input.date_id()[1]) | |
| game_list = [int(input.date_id())] | |
| print(game_list) | |
| # if input.tabset() == 'Game Summary': | |
| # print(year_input, sport_id, player_input, 'yup') | |
| # print(input.date_id()) | |
| # game_list = | |
| data_list = scrape.get_data(game_list_input = game_list[:]) | |
| try: | |
| df = (stuff_apply.stuff_apply(fe.feature_engineering(update.update(scrape.get_data_df(data_list = data_list).filter( | |
| (pl.col("pitcher_id") == player_input)& | |
| (pl.col("is_pitch") == True)& | |
| (pl.col("start_speed") >= 50)& | |
| (pl.col('batter_hand').is_in(split_dict_hand[input.split_id()])) | |
| )))).with_columns( | |
| pl.col('pitch_type').count().over('pitch_type').alias('pitch_count') | |
| )) | |
| return df | |
| except TypeError: | |
| print("NONE") | |
| return None | |
| def player_select_ui(): | |
| # Get the list of pitchers for the selected level and season | |
| df_pitcher_info = scrape.get_players(sport_id=int(input.level_input()), season=int(input.year_input()), game_type = [input.type_input()]).filter( | |
| (pl.col("position").is_in(['P','TWP']))| | |
| (pl.col("player_id").is_in([686846,806823])) | |
| ).sort("name") | |
| # Create a dictionary of pitcher IDs and names | |
| pitcher_dict = dict(zip(df_pitcher_info['player_id'], df_pitcher_info['name'])) | |
| # Return a select input for choosing a pitcher | |
| return ui.input_select("pitcher_id", "Select Pitcher", pitcher_dict, selectize=True) | |
| def date_id(): | |
| if input.tabset() == 'Pitching Summary' or input.tabset() == 'Table Range': | |
| # Create a date range input for selecting the date range within the selected year | |
| return ui.input_date_range("date_id", "Select Date Range", | |
| start=f"{int(input.year_input())}-01-01", | |
| end=f"{int(input.year_input())}-12-31", | |
| min=f"{int(input.year_input())}-01-01", | |
| max=f"{int(input.year_input())}-12-31") | |
| if input.tabset() == 'Game Summary' or input.tabset() == 'Table Game': | |
| year_input = int(input.year_input()) | |
| sport_id = int(input.level_input()) | |
| player_input = int(input.pitcher_id()) | |
| print('game summary') | |
| # start_date = str(input.date_id()[0]) | |
| # end_date = str(input.date_id()[1]) | |
| game_list = scrape.get_player_games_list(player_id = player_input, | |
| season = year_input, | |
| sport_id=sport_id, | |
| game_type=[input.type_input()], | |
| pitching = True) | |
| schedule_df = scrape.get_schedule(year_input=[year_input], | |
| sport_id= [sport_id], | |
| game_type = [input.type_input()]) | |
| player_schedule_df = schedule_df.filter(pl.col('game_id').is_in(game_list)).to_pandas().sort_values('date') | |
| player_schedule_df['def'] = player_schedule_df['date'].astype(str) + ' - ' + player_schedule_df['away'] + ' @ ' + player_schedule_df['home'] + ' ' | |
| game_dict = dict(zip(player_schedule_df['game_id'], player_schedule_df['def'])) | |
| # print(game_dict) | |
| return ui.input_select("date_id", "Select Game", game_dict) | |
| def status(): | |
| # Only show status when generating | |
| if input.generate == 0: | |
| return "" | |
| return "" | |
| def plot(): | |
| # Show progress/loading notification | |
| with ui.Progress(min=0, max=1) as p: | |
| p.set(message="Generating plot", detail="This may take a while...") | |
| p.set(0.3, "Gathering data...") | |
| year_input = int(input.year_input()) | |
| sport_id = int(input.level_input()) | |
| player_input = int(input.pitcher_id()) | |
| start_date = str(input.date_id()[0]) | |
| end_date = str(input.date_id()[1]) | |
| if not is_valid_date(start_date): | |
| fig = plt.figure(figsize=(26,26)) | |
| fig.text(x=0.1,y=0.9,s='Select Date Range and Generate Plot',fontsize=36,ha='left') | |
| return fig | |
| print(year_input, sport_id, player_input, start_date, end_date) | |
| df = cached_data() | |
| if df is None: | |
| fig = plt.figure(figsize=(26,26)) | |
| fig.text(x=0.1,y=0.9,s='No Statcast Data For This Pitcher',fontsize=36,ha='left') | |
| return fig | |
| df = df.clone() | |
| p.set(0.6, "Creating plot...") | |
| #plt.rcParams["figure.figsize"] = [10,10] | |
| fig = plt.figure(figsize=(26,26)) | |
| plt.rcParams.update({'figure.autolayout': True}) | |
| fig.set_facecolor('white') | |
| sns.set_theme(style="whitegrid", palette=colour_palette) | |
| print('this is the one plot') | |
| gs = gridspec.GridSpec(6, 8, | |
| height_ratios=[6,20,12,36,36,6], | |
| width_ratios=[4,18,18,18,18,18,18,4]) | |
| gs.update(hspace=0.2, wspace=0.5) | |
| # Define the positions of each subplot in the grid | |
| ax_headshot = fig.add_subplot(gs[1,1:3]) | |
| ax_bio = fig.add_subplot(gs[1,3:5]) | |
| ax_logo = fig.add_subplot(gs[1,5:7]) | |
| ax_season_table = fig.add_subplot(gs[2,1:7]) | |
| ax_plot_1 = fig.add_subplot(gs[3,1:3]) | |
| ax_plot_2 = fig.add_subplot(gs[3,3:5]) | |
| ax_plot_3 = fig.add_subplot(gs[3,5:7]) | |
| ax_table = fig.add_subplot(gs[4,1:7]) | |
| ax_footer = fig.add_subplot(gs[-1,1:7]) | |
| ax_header = fig.add_subplot(gs[0,1:7]) | |
| ax_left = fig.add_subplot(gs[:,0]) | |
| ax_right = fig.add_subplot(gs[:,-1]) | |
| # Hide axes for footer, header, left, and right | |
| ax_footer.axis('off') | |
| ax_header.axis('off') | |
| ax_left.axis('off') | |
| ax_right.axis('off') | |
| sns.set_theme(style="whitegrid", palette=colour_palette) | |
| fig.set_facecolor('white') | |
| df_teams = scrape.get_teams() | |
| player_headshot(player_input=player_input, ax=ax_headshot,sport_id=sport_id,season=year_input) | |
| player_bio(pitcher_id=player_input, ax=ax_bio,sport_id=sport_id,year_input=year_input) | |
| if input.switch(): | |
| # Get the logo URL from the image dictionary using the team abbreviation | |
| logo_url = input.logo_select() | |
| # Send a GET request to the logo URL | |
| response = requests.get(logo_url) | |
| # Open the image from the response content | |
| img = Image.open(BytesIO(response.content)) | |
| # Display the image on the axis | |
| ax_logo.set_xlim(0, 1.3) | |
| ax_logo.set_ylim(0, 1) | |
| ax_logo.imshow(img, extent=[0.3, 1.3, 0, 1], origin='upper') | |
| # Turn off the axis | |
| ax_logo.axis('off') | |
| else: | |
| plot_logo(pitcher_id=player_input, ax=ax_logo, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input)) | |
| stat_summary_table(df=df, | |
| ax=ax_season_table, | |
| player_input=player_input, | |
| split=input.split_id(), | |
| sport_id=sport_id, | |
| game_type=[input.type_input()], | |
| start_date_input= str(input.date_id()[0]), | |
| end_date_input=str(input.date_id()[1])) | |
| # break_plot(df=df_plot,ax=ax2) | |
| for x,y,z in zip([input.plot_id_1(),input.plot_id_2(),input.plot_id_3()],[ax_plot_1,ax_plot_2,ax_plot_3],[1,3,5]): | |
| if x == 'velocity_kdes': | |
| velocity_kdes(df, | |
| ax=y, | |
| gs=gs, | |
| gs_x=[3,4], | |
| gs_y=[z,z+2], | |
| fig=fig) | |
| if x == 'tj_stuff_roling': | |
| tj_stuff_roling(df=df, | |
| window=int(input.rolling_window()), | |
| ax=y) | |
| if x == 'tj_stuff_roling_game': | |
| tj_stuff_roling_game(df=df, | |
| window=int(input.rolling_window()), | |
| ax=y) | |
| if x == 'break_plot': | |
| break_plot(df = df,ax=y) | |
| if x == 'location_plot_lhb': | |
| location_plot(df = df,ax=y,hand='L') | |
| if x == 'location_plot_rhb': | |
| location_plot(df = df,ax=y,hand='R') | |
| if x == 'pitch_usage': | |
| pitch_usage(df = df,ax=y) | |
| summary_table(df=df, | |
| ax=ax_table) | |
| plot_footer(ax_footer) | |
| # ax_watermark = fig.add_subplot(gs[1:-1,1:-1],zorder=-1) | |
| # # Hide axes ticks and labels | |
| # ax_watermark.set_xticks([]) | |
| # ax_watermark.set_yticks([]) | |
| # ax_watermark.set_frame_on(False) # Optional: Hide border | |
| # img = Image.open('tj stats circle-01_new.jpg') | |
| # # Display the image | |
| # ax_watermark.imshow(img, extent=[0, 1, 0, 1], origin='upper',zorder=-1, alpha=0.1) | |
| ax_watermark2 = fig.add_subplot(gs[-2:,1:4],zorder=1) | |
| ax_watermark2.set_xlim(0,1) | |
| ax_watermark2.set_ylim(0,1) | |
| # Hide axes ticks and labels | |
| ax_watermark2.set_xticks([]) | |
| ax_watermark2.set_yticks([]) | |
| ax_watermark2.set_frame_on(False) # Optional: Hide border | |
| # Open the image | |
| img = Image.open('tj stats circle-01_new.jpg') | |
| # Get the original size | |
| width, height = img.size | |
| # Calculate the new size (50% larger) | |
| new_width = int(width * 0.5) | |
| new_height = int(height * 0.5) | |
| # Resize the image | |
| img_resized = img.resize((new_width, new_height)) | |
| # Display the image | |
| ax_watermark2.imshow(img, extent=[0.26, 0.46, 0.0,0.2], origin='upper',zorder=-1, alpha=1) | |
| fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01) | |
| def game_plot(): | |
| # Show progress/loading notification | |
| with ui.Progress(min=0, max=1) as p: | |
| print(input.date_id(),'TEST') | |
| if isinstance(input.date_id(), tuple): | |
| fig = plt.figure(figsize=(26,26)) | |
| fig.text(x=0.1,y=0.9,s='Select Game and Generate Plot',fontsize=36,ha='left') | |
| return fig | |
| p.set(message="Generating plot", detail="This may take a while...") | |
| p.set(0.3, "Gathering data...") | |
| year_input = int(input.year_input()) | |
| sport_id = int(input.level_input()) | |
| player_input = int(input.pitcher_id()) | |
| # print(input.game_id()) | |
| # print(year_input, sport_id, player_input) | |
| # print(year_input, sport_id, player_input, start_date, end_date) | |
| df = cached_data_daily() | |
| # start_date = str(df['game_date'][0]) | |
| # end_date = str(df['game_date'][0]) | |
| if df is None: | |
| fig = plt.figure(figsize=(26,26)) | |
| fig.text(x=0.1,y=0.9,s='No Statcast Data For This Pitcher',fontsize=36,ha='left') | |
| return fig | |
| df = df.clone() | |
| p.set(0.6, "Creating plot...") | |
| #plt.rcParams["figure.figsize"] = [10,10] | |
| fig = plt.figure(figsize=(26,26)) | |
| plt.rcParams.update({'figure.autolayout': True}) | |
| fig.set_facecolor('white') | |
| sns.set_theme(style="whitegrid", palette=colour_palette) | |
| print('this is the one plot') | |
| gs = gridspec.GridSpec(6, 8, | |
| height_ratios=[6,20,12,36,36,6], | |
| width_ratios=[4,18,18,18,18,18,18,4]) | |
| gs.update(hspace=0.2, wspace=0.5) | |
| # Define the positions of each subplot in the grid | |
| ax_headshot = fig.add_subplot(gs[1,1:3]) | |
| ax_bio = fig.add_subplot(gs[1,3:5]) | |
| ax_logo = fig.add_subplot(gs[1,5:7]) | |
| ax_season_table = fig.add_subplot(gs[2,1:7]) | |
| ax_plot_1 = fig.add_subplot(gs[3,1:3]) | |
| ax_plot_2 = fig.add_subplot(gs[3,3:5]) | |
| ax_plot_3 = fig.add_subplot(gs[3,5:7]) | |
| ax_table = fig.add_subplot(gs[4,1:7]) | |
| ax_footer = fig.add_subplot(gs[-1,1:7]) | |
| ax_header = fig.add_subplot(gs[0,1:7]) | |
| ax_left = fig.add_subplot(gs[:,0]) | |
| ax_right = fig.add_subplot(gs[:,-1]) | |
| # Hide axes for footer, header, left, and right | |
| ax_footer.axis('off') | |
| ax_header.axis('off') | |
| ax_left.axis('off') | |
| ax_right.axis('off') | |
| sns.set_theme(style="whitegrid", palette=colour_palette) | |
| fig.set_facecolor('white') | |
| df_teams = scrape.get_teams() | |
| player_headshot(player_input=player_input, ax=ax_headshot,sport_id=sport_id,season=year_input) | |
| player_bio(pitcher_id=player_input, ax=ax_bio,sport_id=sport_id,year_input=year_input) | |
| if input.switch(): | |
| # Get the logo URL from the image dictionary using the team abbreviation | |
| logo_url = input.logo_select() | |
| # Send a GET request to the logo URL | |
| response = requests.get(logo_url) | |
| # Open the image from the response content | |
| img = Image.open(BytesIO(response.content)) | |
| # Display the image on the axis | |
| ax_logo.set_xlim(0, 1.3) | |
| ax_logo.set_ylim(0, 1) | |
| ax_logo.imshow(img, extent=[0.3, 1.3, 0, 1], origin='upper') | |
| # Turn off the axis | |
| ax_logo.axis('off') | |
| else: | |
| plot_logo(pitcher_id=player_input, ax=ax_logo, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input)) | |
| stat_summary_table(df=df, | |
| ax=ax_season_table, | |
| player_input=player_input, | |
| split=input.split_id(), | |
| sport_id=sport_id, | |
| game_type=[input.type_input()], | |
| start_date_input=None, | |
| end_date_input=None) | |
| # break_plot(df=df_plot,ax=ax2) | |
| for x,y,z in zip([input.plot_id_1(),input.plot_id_2(),input.plot_id_3()],[ax_plot_1,ax_plot_2,ax_plot_3],[1,3,5]): | |
| if x == 'velocity_kdes': | |
| velocity_kdes(df, | |
| ax=y, | |
| gs=gs, | |
| gs_x=[3,4], | |
| gs_y=[z,z+2], | |
| fig=fig) | |
| if x == 'tj_stuff_roling': | |
| tj_stuff_roling(df=df, | |
| window=int(input.rolling_window()), | |
| ax=y) | |
| if x == 'tj_stuff_roling_game': | |
| tj_stuff_roling_game(df=df, | |
| window=int(input.rolling_window()), | |
| ax=y) | |
| if x == 'break_plot': | |
| break_plot(df = df,ax=y) | |
| if x == 'location_plot_lhb': | |
| location_plot(df = df,ax=y,hand='L') | |
| if x == 'location_plot_rhb': | |
| location_plot(df = df,ax=y,hand='R') | |
| if x == 'pitch_usage': | |
| pitch_usage(df = df,ax=y) | |
| summary_table(df=df, | |
| ax=ax_table) | |
| plot_footer(ax_footer) | |
| ax_watermark = fig.add_subplot(gs[1:-1,1:-1],zorder=-1) | |
| # Hide axes ticks and labels | |
| ax_watermark.set_xticks([]) | |
| ax_watermark.set_yticks([]) | |
| ax_watermark.set_frame_on(False) # Optional: Hide border | |
| img = Image.open('tj stats circle-01_new.jpg') | |
| # Display the image | |
| ax_watermark.imshow(img, extent=[0, 1, 0, 1], origin='upper',zorder=-1, alpha=0.1) | |
| ax_watermark2 = fig.add_subplot(gs[-2:,1:4],zorder=1) | |
| ax_watermark2.set_xlim(0,1) | |
| ax_watermark2.set_ylim(0,1) | |
| # Hide axes ticks and labels | |
| ax_watermark2.set_xticks([]) | |
| ax_watermark2.set_yticks([]) | |
| ax_watermark2.set_frame_on(False) # Optional: Hide border | |
| # Open the image | |
| img = Image.open('tj stats circle-01_new.jpg') | |
| # Get the original size | |
| width, height = img.size | |
| # Calculate the new size (50% larger) | |
| new_width = int(width * 0.5) | |
| new_height = int(height * 0.5) | |
| # Resize the image | |
| img_resized = img.resize((new_width, new_height)) | |
| # Display the image | |
| ax_watermark2.imshow(img, extent=[0.26, 0.46, 0.0,0.2], origin='upper',zorder=-1, alpha=1) | |
| fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01) | |
| def grid(): | |
| start_date = str(input.date_id()[0]) | |
| if not is_valid_date(start_date): | |
| return pd.DataFrame({"Message": ["Select range to generate table"]}) | |
| df = cached_data() | |
| df = df.clone() | |
| features_table = ['start_speed', | |
| 'spin_rate', | |
| 'extension', | |
| 'ivb', | |
| 'hb', | |
| 'x0', | |
| 'z0'] | |
| selection = ['game_id','pitcher_id','pitcher_name','batter_id','batter_name','pitcher_hand', | |
| 'batter_hand','balls','strikes','play_code','event_type','pitch_type','vaa','haa']+features_table+['tj_stuff_plus','pitch_grade'] | |
| return render.DataGrid( | |
| df.select(selection).to_pandas().round(1), | |
| row_selection_mode='multiple', | |
| height='700px', | |
| width='fit-content', | |
| filters=True, | |
| ) | |
| def grid_game(): | |
| if isinstance(input.date_id(), tuple): | |
| return pd.DataFrame({"Message": ["Select game to generate table"]}) | |
| df = cached_data_daily() | |
| df = df.clone() | |
| features_table = ['start_speed', | |
| 'spin_rate', | |
| 'extension', | |
| 'ivb', | |
| 'hb', | |
| 'x0', | |
| 'z0'] | |
| selection = ['game_id','pitcher_id','pitcher_name','batter_id','batter_name','pitcher_hand', | |
| 'batter_hand','balls','strikes','play_code','event_type','pitch_type','vaa','haa']+features_table+['tj_stuff_plus','pitch_grade'] | |
| return render.DataGrid( | |
| df.select(selection).to_pandas().round(1), | |
| row_selection_mode='multiple', | |
| height='700px', | |
| width='fit-content', | |
| filters=True, | |
| ) | |
| app = App(app_ui, server) |