nesticot's picture
Update app.py
dd3c175 verified
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
from shiny import req
colour_palette = ['#FFB000','#648FFF','#785EF0',
'#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
year_list = [2017,2018,2019,2020,2021,2022,2023,2024,2025]
level_dict = {'1':'MLB',
'11':'AAA',
'12':'AA',
'13':'A+',
'14':'A',
'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',
}
split_dict = {'all':'All',
'left':'LHH',
'right':'RHH'}
split_dict_title = {'all':'',
'left':' - vs LHH',
'right':' - vs 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()
from shiny import App, reactive, ui, render
from shiny.ui import h2, tags
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 datetime import datetime, timedelta
from shiny import App, reactive, ui, render
from shiny.ui import h2, tags
# Define the login UI
login_ui = ui.page_fluid(
ui.card(
ui.h2([
"TJStats Pitching Summary Compare 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/119993776", target="_blank"),
"."
),
ui.input_password("password", "Enter Patreon Email (or Password from Link):", width="25%"),
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.markdown("### Select Pitcher 1")),
ui.row(
ui.column(4, ui.input_select('year_input_1', 'Season', year_list, selected=2025)),
ui.column(4, ui.input_select('level_input_1', 'Level', level_dict)),
ui.column(4, ui.input_select('type_input_1', 'Type', type_dict,selected='R'))
),
# Row for the action button to get player list
ui.row(ui.input_action_button("player_button_1", "Get Player List", class_="btn-primary"),
),
# Row for selecting the player
ui.row(ui.column(8, ui.output_ui('player_select_ui_1', 'Select Player')),
ui.column(4, ui.input_select('split_id_1', 'Select Split', split_dict, multiple=False))
),
# Row for selecting the date range
ui.row(ui.column(8, ui.output_ui('date_id_1', 'Select Date')),
ui.column(4, ui.input_select('select_date_time1', 'Quick Time',['Season','Last 10 Days','Last 30 Days'])),
),
ui.row(
ui.column(6, ui.input_switch("switch_1", "Custom Team?", False)),
ui.column(6, ui.input_select('logo_select_1', 'Select Custom Logo', image_dict_flip, multiple=False))
),
# Row for selecting season and level
ui.row(ui.markdown("### Select Pitcher 2")),
ui.row(
ui.column(4, ui.input_select('year_input_2', 'Season', year_list, selected=2025)),
ui.column(4, ui.input_select('level_input_2', 'Level', level_dict)),
ui.column(4, ui.input_select('type_input_2', 'Type', type_dict,selected='R'))
),
# Row for the action button to get player list
ui.row(ui.input_action_button("player_button_2", "Get Player List", class_="btn-primary"),
),
# Row for selecting the player
ui.row(ui.column(8, ui.output_ui('player_select_ui_2', 'Select Player')),
ui.column(4, ui.input_select('split_id_2', 'Select Split', split_dict, multiple=False))
),
# Row for selecting the date range
ui.row(ui.column(8, ui.output_ui('date_id_2', 'Select Date')),
ui.column(4, ui.input_select('select_date_time2', 'Quick Time',['Season','Last 10 Days','Last 30 Days'])),
),
ui.row(
ui.column(6, ui.input_switch("switch_2", "Custom Team?", False)),
ui.column(6, ui.input_select('logo_select_2', '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("Full Compare",
ui.output_text("status"),
ui.output_plot('plot', width='2100px', height='2100px')),
ui.nav_panel("Break Plots",
ui.output_text("status_break"),
ui.output_plot('plot_break', width='2100px', height='1300px')),
ui.nav_panel("Tables",
ui.output_text("status_table"),
ui.output_plot('plot_table', width='2100px', height='1300px')),
id="tabset_id",
),
)
# 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):
@reactive.Effect
@reactive.event(input.login)
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="")
@output
@render.text
def login_message():
return ""
@reactive.calc
@reactive.event(input.pitcher_id_1, input.date_id_1,input.split_id_1)
def cached_data_1():
year_input = int(input.year_input_1())
sport_id = int(input.level_input_1())
player_input = int(input.pitcher_id_1())
start_date = str(input.date_id_1()[0])
end_date = str(input.date_id_1()[1])
# Simulate an expensive data operation
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_1()])
data_list = scrape.get_data(game_list_input = game_list[:])
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('batter_hand').is_in(split_dict_hand[input.split_id_1()]))
)))).with_columns(
pl.col('pitch_type').count().over('pitch_type').alias('pitch_count')
))
return df
def cached_data_2():
year_input = int(input.year_input_2())
sport_id = int(input.level_input_2())
player_input = int(input.pitcher_id_2())
start_date = str(input.date_id_2()[0])
end_date = str(input.date_id_2()[1])
# Simulate an expensive data operation
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_2()])
data_list = scrape.get_data(game_list_input = game_list[:])
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('batter_hand').is_in(split_dict_hand[input.split_id_2()]))
)))).with_columns(
pl.col('pitch_type').count().over('pitch_type').alias('pitch_count')
))
return df
@render.ui
@reactive.event(input.player_button_1, input.year_input_1, input.level_input_1, input.type_input_1,ignore_none=False)
def player_select_ui_1():
# Get the list of pitchers for the selected level and season
df_pitcher_info = scrape.get_players(sport_id=int(input.level_input_1()), season=int(input.year_input_1()), game_type = [input.type_input_1()]).filter(
pl.col("position").is_in(['P','TWP'])).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_1", "Select Pitcher", pitcher_dict, selectize=True)
@render.ui
@reactive.event(input.player_button_2, input.year_input_2, input.level_input_2, input.type_input_2,ignore_none=False)
def player_select_ui_2():
# Get the list of pitchers for the selected level and season
df_pitcher_info = scrape.get_players(sport_id=int(input.level_input_2()), season=int(input.year_input_2()), game_type = [input.type_input_2()]).filter(
pl.col("position").is_in(['P','TWP'])).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_2", "Select Pitcher", pitcher_dict, selectize=True)
@render.ui
@reactive.event(input.player_button_1, input.year_input_1, input.select_date_time1, ignore_none=False)
def date_id_1():
if input.select_date_time1() == 'Season':
# Create a date range input for selecting the date range within the selected year
return ui.input_date_range("date_id_1", "Select Date Range",
start=f"{int(input.year_input_1())}-01-01",
end=f"{int(input.year_input_1())}-12-31",
min=f"{int(input.year_input_1())}-01-01",
max=f"{int(input.year_input_1())}-12-31")
elif input.select_date_time1() == 'Last 10 Days':
today = datetime.today().date()
last_10_days = today - timedelta(days=10)
return ui.input_date_range("date_id_1", "Select Date Range",
start=last_10_days.strftime("%Y-%m-%d"),
end=today.strftime("%Y-%m-%d"),
min=last_10_days.strftime("%Y-%m-%d"),
max=today.strftime("%Y-%m-%d"))
elif input.select_date_time1() == 'Last 30 Days':
today = datetime.today().date()
last_10_days = today - timedelta(days=30)
return ui.input_date_range("date_id_1", "Select Date Range",
start=last_10_days.strftime("%Y-%m-%d"),
end=today.strftime("%Y-%m-%d"),
min=last_10_days.strftime("%Y-%m-%d"),
max=today.strftime("%Y-%m-%d"))
@render.ui
@reactive.event(input.player_button_2, input.year_input_2, input.select_date_time2, ignore_none=False)
def date_id_2():
# Create a date range input for selecting the date range within the selected year
if input.select_date_time2() == 'Season':
# Create a date range input for selecting the date range within the selected year
return ui.input_date_range("date_id_2", "Select Date Range",
start=f"{int(input.year_input_2())}-01-01",
end=f"{int(input.year_input_2())}-12-31",
min=f"{int(input.year_input_2())}-01-01",
max=f"{int(input.year_input_2())}-12-31")
elif input.select_date_time2() == 'Last 10 Days':
today = datetime.today().date()
last_10_days = today - timedelta(days=10)
return ui.input_date_range("date_id_2", "Select Date Range",
start=last_10_days.strftime("%Y-%m-%d"),
end=today.strftime("%Y-%m-%d"),
min=last_10_days.strftime("%Y-%m-%d"),
max=today.strftime("%Y-%m-%d"))
elif input.select_date_time2() == 'Last 30 Days':
today = datetime.today().date()
last_10_days = today - timedelta(days=30)
return ui.input_date_range("date_id_2", "Select Date Range",
start=last_10_days.strftime("%Y-%m-%d"),
end=today.strftime("%Y-%m-%d"),
min=last_10_days.strftime("%Y-%m-%d"),
max=today.strftime("%Y-%m-%d"))
@output
@render.text
def status():
# Only show status when generating
if input.generate == 0:
return ""
return ""
@output
@render.text
def status_break():
# Only show status when generating
if input.generate == 0:
return ""
return ""
@output
@render.text
def status_table():
# Only show status when generating
if input.generate == 0:
return ""
return ""
@output
@render.plot
@reactive.event(input.generate_plot, ignore_none=False)
def plot():
req(input.generate_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_1())
sport_id = int(input.level_input_1())
player_input = int(input.pitcher_id_1())
start_date = str(input.date_id_1()[0])
end_date = str(input.date_id_1()[1])
print(year_input, sport_id, player_input, start_date, end_date)
year_input_2 = int(input.year_input_2())
sport_id_2 = int(input.level_input_2())
player_input_2 = int(input.pitcher_id_2())
start_date_2 = str(input.date_id_2()[0])
end_date_2 = str(input.date_id_2()[1])
print(year_input_2, sport_id_2, player_input_2, start_date_2, end_date_2)
df = cached_data_1()
print(df)
df_2 = cached_data_2()
print(df_2)
df = df.clone()
df_2 = df_2.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, 12,
height_ratios=[1,10,40,25,25,5],
width_ratios=[3,3,10,24,10,3,3,10,24,10,3,3])
gs.update(hspace=0.15, wspace=0.15)
ax_headshot_1 = fig.add_subplot(gs[1,2])
ax_bio_1 = fig.add_subplot(gs[1,3])
ax_logo_1 = fig.add_subplot(gs[1,4])
ax_break_1 = fig.add_subplot(gs[2,1:6])
ax_table_1 = fig.add_subplot(gs[3,1:11])
ax_headshot_2 = fig.add_subplot(gs[1,7])
ax_bio_2 = fig.add_subplot(gs[1,8])
ax_logo_2 = fig.add_subplot(gs[1,9])
ax_break_2 = fig.add_subplot(gs[2,6:11])
ax_table_2 = fig.add_subplot(gs[4,1:11])
ax_footer = fig.add_subplot(gs[-1,1:11])
ax_header = fig.add_subplot(gs[0,1:11])
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_1,sport_id=sport_id,season=year_input)
player_bio(pitcher_id=player_input, ax=ax_bio_1,sport_id=sport_id,year_input=year_input,
df=df,split=input.split_id_1(),game_type=input.type_input_1())
df_teams = scrape.get_teams()
if input.switch_1():
# Get the logo URL from the image dictionary using the team abbreviation
logo_url = input.logo_select_1()
# 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_1.set_xlim(0, 1)
ax_logo_1.set_ylim(0, 1)
ax_logo_1.imshow(img, extent=[0, 1, 0, 1], origin='upper')
# Turn off the axis
ax_logo_1.axis('off')
else:
plot_logo(pitcher_id=player_input, ax=ax_logo_1, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input))
break_plot(df = df,ax=ax_break_1)
summary_table(df=df,
ax=ax_table_1)
ax_table_1.text(x=0.5,y=0.83,s=f'{df["pitcher_name"][0]} - {year_input} - {level_dict[str(sport_id)]} - {type_dict[input.type_input_1()]}{split_dict_title[input.split_id_1()]}',
fontsize=30, ha='center', va='bottom',fontstyle='italic')
# level_dict
# split_dict
# type_dict
#####################
player_headshot(player_input=player_input_2, ax=ax_headshot_2,sport_id=sport_id_2,season=year_input_2)
player_bio(pitcher_id=player_input_2, ax=ax_bio_2,sport_id=sport_id_2,year_input=year_input_2,
df=df_2,split=input.split_id_2(),game_type=input.type_input_2())
# df_teams = scrape.get_teams()
if input.switch_2():
# Get the logo URL from the image dictionary using the team abbreviation
logo_url = input.logo_select_2()
# 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_2.set_xlim(0, 1)
ax_logo_2.set_ylim(0, 1)
ax_logo_2.imshow(img, extent=[0, 1, 0, 1], origin='upper')
# Turn off the axis
ax_logo_2.axis('off')
else:
plot_logo(pitcher_id=player_input_2, ax=ax_logo_2, df_team=df_teams,df_players=scrape.get_players(sport_id_2,year_input_2))
break_plot(df = df_2,ax=ax_break_2)
summary_table(df=df_2,
ax=ax_table_2)
ax_table_2.text(x=0.5,y=0.83,s=f'{df_2["pitcher_name"][0]} - {year_input_2} - {level_dict[str(sport_id_2)]} - {type_dict[input.type_input_2()]}{split_dict_title[input.split_id_2()]}',
fontsize=30, ha='center', va='bottom',fontstyle='italic')
#########################
plot_footer(ax_footer)
fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
# Draw a line halfway down the figure, starting from the top middle and going to the center
line = plt.Line2D([0.5, 0.5], [0.55, 0.95], transform=fig.transFigure, color="grey",alpha=0.4, linewidth=2)
fig.add_artist(line)
@output
@render.plot
@reactive.event(input.generate_plot, ignore_none=False)
def plot_break():
req(input.generate_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_1())
sport_id = int(input.level_input_1())
player_input = int(input.pitcher_id_1())
start_date = str(input.date_id_1()[0])
end_date = str(input.date_id_1()[1])
print(year_input, sport_id, player_input, start_date, end_date)
year_input_2 = int(input.year_input_2())
sport_id_2 = int(input.level_input_2())
player_input_2 = int(input.pitcher_id_2())
start_date_2 = str(input.date_id_2()[0])
end_date_2 = str(input.date_id_2()[1])
print(year_input_2, sport_id_2, player_input_2, start_date_2, end_date_2)
df = cached_data_1()
print(df)
df_2 = cached_data_2()
print(df_2)
df = df.clone()
df_2 = df_2.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(4, 12,
height_ratios=[1,10,40,10],
width_ratios=[3,3,10,24,10,3,3,10,24,10,3,3])
gs.update(hspace=0.15, wspace=0.15)
ax_headshot_1 = fig.add_subplot(gs[1,2])
ax_bio_1 = fig.add_subplot(gs[1,3])
ax_logo_1 = fig.add_subplot(gs[1,4])
ax_break_1 = fig.add_subplot(gs[2,1:6])
# ax_table_1 = fig.add_subplot(gs[3,1:11])
ax_headshot_2 = fig.add_subplot(gs[1,7])
ax_bio_2 = fig.add_subplot(gs[1,8])
ax_logo_2 = fig.add_subplot(gs[1,9])
ax_break_2 = fig.add_subplot(gs[2,6:11])
# ax_table_2 = fig.add_subplot(gs[4,1:11])
ax_footer = fig.add_subplot(gs[-1,1:11])
ax_header = fig.add_subplot(gs[0,1:11])
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_1,sport_id=sport_id,season=year_input)
player_bio(pitcher_id=player_input, ax=ax_bio_1,sport_id=sport_id,year_input=year_input,
df=df,split=input.split_id_1(),game_type=input.type_input_1())
df_teams = scrape.get_teams()
if input.switch_1():
# Get the logo URL from the image dictionary using the team abbreviation
logo_url = input.logo_select_1()
# 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_1.set_xlim(0, 1)
ax_logo_1.set_ylim(0, 1)
ax_logo_1.imshow(img, extent=[0, 1, 0, 1], origin='upper')
# Turn off the axis
ax_logo_1.axis('off')
else:
plot_logo(pitcher_id=player_input, ax=ax_logo_1, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input))
break_plot(df = df,ax=ax_break_1)
#####################
player_headshot(player_input=player_input_2, ax=ax_headshot_2,sport_id=sport_id_2,season=year_input_2)
player_bio(pitcher_id=player_input_2, ax=ax_bio_2,sport_id=sport_id_2,year_input=year_input_2,
df=df_2,split=input.split_id_2(),game_type=input.type_input_2())
# df_teams = scrape.get_teams()
if input.switch_2():
# Get the logo URL from the image dictionary using the team abbreviation
logo_url = input.logo_select_2()
# 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_2.set_xlim(0, 1)
ax_logo_2.set_ylim(0, 1)
ax_logo_2.imshow(img, extent=[0, 1, 0, 1], origin='upper')
# Turn off the axis
ax_logo_2.axis('off')
else:
plot_logo(pitcher_id=player_input_2, ax=ax_logo_2, df_team=df_teams,df_players=scrape.get_players(sport_id_2,year_input_2))
break_plot(df = df_2,ax=ax_break_2)
#########################
plot_footer_break(ax_footer)
fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
# Draw a line halfway down the figure, starting from the top middle and going to the center
line = plt.Line2D([0.5, 0.5], [0.15, 0.95], transform=fig.transFigure, color="grey",alpha=0.4, linewidth=2)
fig.add_artist(line)
print(df.schema)
print(df_2.schema)
columns_to_cast = [
"trajectory_fly_ball", "trajectory_ground_ball",
"trajectory_line_drive", "trajectory_popup",
"count", "pitch_count"
]
df = df.with_columns([pl.col(col).cast(pl.Int32) for col in columns_to_cast])
df_2 = df_2.with_columns([pl.col(col).cast(pl.Int32) for col in columns_to_cast])
common_cols = list(set(df.columns) & set(df_2.columns))
df_legend = pl.concat([df.select(common_cols), df_2.select(common_cols)], rechunk=True)
# # Create legend for pitch types
items_in_order = (df_legend.sort("pitch_count", descending=True)['pitch_type'].unique(maintain_order=True).to_numpy())
colour_pitches = [dict_colour[x] for x in items_in_order]
label = [dict_pitch[x] for x in items_in_order]
handles = [plt.scatter([], [], color=color, marker='o', s=100) for color in colour_pitches]
if len(label) > 10:
ax_footer.legend(handles, label, bbox_to_anchor=(0.2, 0.2, 0.6, 0.3), ncol=5,
fancybox=True, loc='lower center', fontsize=16, framealpha=1.0, markerscale=1.7, prop={'family': 'calibi', 'size': 16})
if len(label) > 5:
ax_footer.legend(handles, label, bbox_to_anchor=(0.2, 0.2, 0.6, 0.2), ncol=5,
fancybox=True, loc='lower center', fontsize=16, framealpha=1.0, markerscale=1.7, prop={'family': 'calibi', 'size': 16})
else:
ax_footer.legend(handles, label, bbox_to_anchor=(0.2, 0.2, 0.6, 0.14), ncol=5,
fancybox=True, loc='lower center', fontsize=16, framealpha=1.0, markerscale=1.7, prop={'family': 'calibi', 'size': 16})
ax_footer.axis('off')
@output
@render.plot
@reactive.event(input.generate_plot, ignore_none=False)
def plot_table():
req(input.generate_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_1())
sport_id = int(input.level_input_1())
player_input = int(input.pitcher_id_1())
start_date = str(input.date_id_1()[0])
end_date = str(input.date_id_1()[1])
print(year_input, sport_id, player_input, start_date, end_date)
year_input_2 = int(input.year_input_2())
sport_id_2 = int(input.level_input_2())
player_input_2 = int(input.pitcher_id_2())
start_date_2 = str(input.date_id_2()[0])
end_date_2 = str(input.date_id_2()[1])
print(year_input_2, sport_id_2, player_input_2, start_date_2, end_date_2)
df = cached_data_1()
print(df)
df_2 = cached_data_2()
print(df_2)
df = df.clone()
df_2 = df_2.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(4, 12,
height_ratios=[1,25,25,5],
width_ratios=[3,3,10,24,10,3,3,10,24,10,3,3])
gs.update(hspace=0.15, wspace=0.15)
# ax_headshot_1 = fig.add_subplot(gs[1,2])
# ax_bio_1 = fig.add_subplot(gs[1,3])
# ax_logo_1 = fig.add_subplot(gs[1,4])
# ax_break_1 = fig.add_subplot(gs[2,1:6])
ax_table_1 = fig.add_subplot(gs[1,1:11])
# ax_headshot_2 = fig.add_subplot(gs[1,7])
# ax_bio_2 = fig.add_subplot(gs[1,8])
# ax_logo_2 = fig.add_subplot(gs[1,9])
# ax_break_2 = fig.add_subplot(gs[2,6:11])
ax_table_2 = fig.add_subplot(gs[2,1:11])
ax_footer = fig.add_subplot(gs[-1,1:11])
ax_header = fig.add_subplot(gs[0,1:11])
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_1,sport_id=sport_id,season=year_input)
# player_bio(pitcher_id=player_input, ax=ax_bio_1,sport_id=sport_id,year_input=year_input,
# df=df,split=input.split_id_1(),game_type=input.type_input_1())
# df_teams = scrape.get_teams()
# if input.switch_1():
# # Get the logo URL from the image dictionary using the team abbreviation
# logo_url = input.logo_select_1()
# # 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_1.set_xlim(0, 1)
# ax_logo_1.set_ylim(0, 1)
# ax_logo_1.imshow(img, extent=[0, 1, 0, 1], origin='upper')
# # Turn off the axis
# # ax_logo_1.axis('off')
# else:
# plot_logo(pitcher_id=player_input, ax=ax_logo_1, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input))
# break_plot(df = df,ax=ax_break_1)
summary_table(df=df,
ax=ax_table_1)
ax_table_1.text(x=0.5,y=0.83,s=f'{df["pitcher_name"][0]} - {year_input} - {level_dict[str(sport_id)]} - {type_dict[input.type_input_1()]}{split_dict_title[input.split_id_1()]} - {start_date} to {end_date}',
fontsize=30, ha='center', va='bottom',fontstyle='italic')
# level_dict
# split_dict
# type_dict
#####################
# player_headshot(player_input=player_input_2, ax=ax_headshot_2,sport_id=sport_id_2,season=year_input_2)
# player_bio(pitcher_id=player_input_2, ax=ax_bio_2,sport_id=sport_id_2,year_input=year_input_2,
# df=df_2,split=input.split_id_2(),game_type=input.type_input_2())
# # df_teams = scrape.get_teams()
# if input.switch_2():
# # Get the logo URL from the image dictionary using the team abbreviation
# logo_url = input.logo_select_2()
# # 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_2.set_xlim(0, 1)
# ax_logo_2.set_ylim(0, 1)
# ax_logo_2.imshow(img, extent=[0, 1, 0, 1], origin='upper')
# # Turn off the axis
# ax_logo_2.axis('off')
# else:
# plot_logo(pitcher_id=player_input_2, ax=ax_logo_2, df_team=df_teams,df_players=scrape.get_players(sport_id_2,year_input_2))
# break_plot(df = df_2,ax=ax_break_2)
summary_table(df=df_2,
ax=ax_table_2)
ax_table_2.text(x=0.5,y=0.83,s=f'{df_2["pitcher_name"][0]} - {year_input_2} - {level_dict[str(sport_id_2)]} - {type_dict[input.type_input_2()]}{split_dict_title[input.split_id_2()]} - {start_date_2} to {end_date_2}',
fontsize=30, ha='center', va='bottom',fontstyle='italic')
#########################
plot_footer(ax_footer)
fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
# # Draw a line halfway down the figure, starting from the top middle and going to the center
# line = plt.Line2D([0.5, 0.5], [0.55, 0.95], transform=fig.transFigure, color="grey",alpha=0.4, linewidth=2)
# fig.add_artist(line)
app = App(app_ui, server)