nesticot commited on
Commit
bfe8aa2
·
verified ·
1 Parent(s): 158a459

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +216 -221
app.py CHANGED
@@ -1,222 +1,217 @@
1
- import polars as pl
2
- import numpy as np
3
- import pandas as pd
4
- import api_scraper
5
- scrape = api_scraper.MLB_Scrape()
6
- import requests
7
- import joblib
8
- from matplotlib.gridspec import GridSpec
9
- from shiny import App, reactive, ui, render
10
- from shiny.ui import h2, tags
11
- import matplotlib.pyplot as plt
12
- import matplotlib.gridspec as gridspec
13
- import seaborn as sns
14
- from shiny import App, reactive, ui, render
15
- from shiny.ui import h2, tags
16
-
17
-
18
- from shiny import App, ui, render
19
-
20
-
21
- # Import the MLB_Scrape class from the module
22
- from api_scraper import MLB_Scrape
23
-
24
- # Initialize the scraper
25
- scraper = MLB_Scrape()
26
-
27
-
28
- # Call the get_teams method
29
- teams = scraper.get_teams()
30
- print(teams)
31
-
32
- df_player = pl.concat([
33
- scraper.get_players(sport_id=1,season=2025,game_type=['R']),
34
- scraper.get_players(sport_id=11,season=2025,game_type=['R']),
35
- scraper.get_players(sport_id=12,season=2025,game_type=['R']),
36
- scraper.get_players(sport_id=13,season=2025,game_type=['R']),
37
- scraper.get_players(sport_id=14,season=2025,game_type=['R']),
38
- scraper.get_players(sport_id=22,season=2025,game_type=['R'])
39
- ]).unique(subset=['player_id'])
40
-
41
-
42
-
43
- teams_mlb = teams.filter(pl.col("league_id").is_in([103,104])).sort("abbreviation")
44
- teams_dict = dict(zip(teams_mlb['team_id'],teams_mlb['abbreviation']))
45
-
46
- teams_name_dict = dict(zip(teams_mlb['team_id'],teams_mlb['franchise']))
47
-
48
- app_ui = ui.page_sidebar(
49
- ui.sidebar(
50
- ui.input_select(
51
- "team_id",
52
- "Select Team",
53
- choices=teams_dict
54
- ),
55
- ui.input_switch("nri_only", "NRI Only"),
56
- ui.div(
57
- ui.div({"style": "font-size:1.2em;"}, ui.markdown("Legend")),
58
- ui.div(
59
- style="display: inline-block; width: 20px; height: 20px; background-color: #b7e1cd; margin-right: 10px;"
60
- ),
61
- ui.span("NRI", style="vertical-align: top;"),
62
- style="padding: 10px;"
63
- ),
64
-
65
- ),
66
- ui.card(
67
- ui.div({"style": "font-size:2em;"}, ui.output_text("card_title")),
68
-
69
- ui.output_table("team_stats")
70
- )
71
- )
72
-
73
- def server(input, output, session):
74
-
75
- @render.text
76
- def card_title():
77
- if input.nri_only():
78
- return f"{teams_name_dict[int(input.team_id())]} — Spring Training Roster Non-Roster Invitees"
79
- else:
80
- return f"{teams_name_dict[int(input.team_id())]} — Spring Training Roster"
81
-
82
- @render.table
83
- def team_stats():
84
- # Get the selected team's data
85
- i = int(input.team_id())
86
-
87
- url = f'https://statsapi.mlb.com/api/v1/teams/{i}/roster/40man?season=2025'
88
- data = requests.get(url).json()
89
- # Normalize the roster data
90
- roster_df = pd.json_normalize(data['roster'])
91
- roster_df['nri'] = False
92
- roster_df['status.code'] = ''
93
- roster_df = roster_df.fillna('')
94
-
95
-
96
- url = f'https://statsapi.mlb.com/api/v1/teams/{i}/roster/nonRosterInvitees?season=2025'
97
- data = requests.get(url).json()
98
- # Normalize the roster data
99
- nri_roster_df = pd.json_normalize(data['roster'])
100
- nri_roster_df['nri'] = True
101
- nri_roster_df['parentTeamId'] = i
102
- nri_roster_df = nri_roster_df.fillna('')
103
-
104
- df_output = pd.concat([roster_df,nri_roster_df]).sort_values(by=['position.code','status.code']).reset_index(drop=True)
105
- if input.nri_only():
106
- df_output = df_output[df_output['status.code'] == 'NRI']
107
-
108
-
109
- df_output = df_output.merge(df_player.to_pandas(),left_on='person.id',right_on='player_id',how='left')
110
-
111
- conditions = [
112
- (df_output['position.abbreviation'] == 'P') & (~df_output.duplicated(subset=['position.abbreviation'], keep='first')),
113
- (df_output['position.abbreviation'] == 'C') & (~df_output.duplicated(subset=['position.abbreviation'], keep='first')),
114
- (df_output['position.abbreviation'] == 'LF') & (~df_output.duplicated(subset=['position.abbreviation'], keep='first'))
115
- ]
116
-
117
- choices = ['Pitchers', 'Infielders', 'Outfielders']
118
-
119
- df_output['position_group'] = np.select(conditions, choices, default='')
120
-
121
-
122
-
123
- df_output['team'] = df_output['parentTeamId'].map(teams_dict)
124
- df_output.loc[df_output['position.abbreviation'] == 'P', 'position.abbreviation'] = df_output['pitchHand'] + 'H' + df_output['position.abbreviation']
125
- df_output['bat_throw'] = df_output['batSide'] + '/' + df_output['pitchHand']
126
- df_output_small = df_output[['position_group','person.id', 'person.fullName',
127
- 'position.abbreviation','team', 'status.code', 'age','weight', 'height', 'bat_throw']]
128
- df_output_small['age'] = df_output_small['age'].replace('', np.nan).astype(int)
129
- df_output_small['weight'] = df_output_small['weight'].replace('', np.nan).astype(int)
130
-
131
- # # Insert blank rows with position group indicated
132
- # blank_rows = []
133
- # for idx, row in df_output_small.iterrows():
134
- # if row['position_group']:
135
- # blank_row = pd.Series([''] * len(df_output_small.columns), index=df_output_small.columns)
136
- # blank_row['position_group'] = row['position_group']
137
- # blank_rows.append((idx, blank_row))
138
-
139
- # for idx, blank_row in reversed(blank_rows):
140
- # df_output_small = pd.concat([df_output_small.iloc[:idx], pd.DataFrame([blank_row]), df_output_small.iloc[idx:]]).reset_index(drop=True)
141
- # df_output_small.loc[(df_output_small['position_group'] != '') & (df_output_small['person.fullName'] != ''), 'position_group'] = ''
142
-
143
- def highlight_nri(val):
144
- color = 'yellow' if val else ''
145
- return f'background-color: {color}'
146
- # Function to alternate row colors
147
- def highlight_alternate_rows(x):
148
- return ['background-color: #ebebeb' if i % 2 == 0 else '' for i in range(len(x))]
149
-
150
- #
151
-
152
-
153
- df_output_small.columns = ['Group','Player ID', 'Name', 'Pos','Team', 'Status','Age','Weight', 'Height', 'B/T']
154
-
155
-
156
-
157
- style_df = (df_output_small.style.set_precision(1)
158
- .set_properties(**{'border': '3 px'}, overwrite=False)
159
- .set_table_styles([{
160
- 'selector': 'caption',
161
- 'props': [
162
- ('color', ''),
163
- ('fontname', 'Century Gothic'),
164
- ('font-size', '16px'),
165
- ('font-style', 'italic'),
166
- ('font-weight', ''),
167
- ('text-align', 'centre'),
168
- ]
169
- }, {
170
- 'selector': 'th',
171
- 'props': [('font-size', '16px'), ('text-align', 'center'), ('Height', 'px'), ('color', 'black'), ('border', '1px black solid !important')]
172
- }, {
173
- 'selector': 'td',
174
- 'props': [('text-align', 'center'), ('font-size', '16px'), ('color', 'black')]
175
- }], overwrite=False)
176
- .set_properties(**{'background-color': 'White', 'index': 'White', 'min-width': '72px'}, overwrite=False)
177
- .set_table_styles([{'selector': 'th:first-child', 'props': [('background-color', 'white')]}], overwrite=False)
178
- .set_table_styles([{'selector': 'tr:first-child', 'props': [('background-color', 'white')]}], overwrite=False)
179
- .set_table_styles([{'selector': 'tr', 'props': [('line-height', '20px')]}], overwrite=False)
180
- .set_properties(**{'Height': '8px'}, **{'text-align': 'center'}, overwrite=False)
181
- .hide_index()
182
- .set_properties(**{'border': '1px black solid'})
183
- .set_table_styles([{'selector': 'thead th:nth-child(1)', 'props': [('min-width', '150px')]}], overwrite=False)
184
- .set_table_styles([{'selector': 'thead th:nth-child(2)', 'props': [('min-width', '150px')]}], overwrite=False)
185
- .set_table_styles([{'selector': 'thead th:nth-child(3)', 'props': [('min-width', '250px')]}], overwrite=False)
186
- .set_table_styles([{'selector': 'thead th', 'props': [('height', '30px')]}], overwrite=False)
187
- .apply(highlight_alternate_rows, axis=0, subset=df_output_small.columns[1:])
188
- .applymap(lambda x: 'background-color: #bdbdbd' if x != '' else '', subset=['Group'])
189
- .applymap(lambda x: 'background-color: #bdbdbd' if x else '', subset=['Group'])
190
- # .apply(lambda x: ['background-color: #bdbdbd' if x['Group'] != '' else '' for _ in x], axis=1)
191
- .set_properties(
192
- **{'background-color':'#bdbdbd'}, # Apply only right border
193
- subset=df_output_small.columns[0] # Only affects column 1
194
- )
195
- .set_properties(
196
- **{'border-top': 'none', 'border-bottom': 'none'},
197
- subset=df_output_small.columns[0] # Apply only to column 1
198
- )
199
-
200
- # .format({'Age': '{:.0f}', 'Weight': '{:.0f}'})
201
- )
202
- def highlight_nri(s):
203
- return ['background-color: #b7e1cd' if s.name != 'Status' and s['Status'] == 'NRI' else '' for _ in s]
204
-
205
- # style_df = style_df.style.apply(highlight_nri, axis=1, subset=style_df.columns[1:])
206
-
207
- if not input.nri_only():
208
- style_df = style_df.apply(highlight_nri, axis=1, subset=df_output_small.columns[1:])
209
-
210
- def add_top_border(s):
211
- return ['border-top: 3px solid black' if s['Group'] != '' else '' for _ in s]
212
-
213
- styled_df = style_df.apply(add_top_border, axis=1)
214
-
215
- def add_bottom_border(s):
216
- return ['border-bottom: 3px solid black' if s.name == len(df_output_small) - 1 else '' for _ in s]
217
-
218
- styled_df = style_df.apply(add_bottom_border, axis=1)
219
-
220
-
221
- return style_df
222
  app = App(app_ui, server)
 
1
+ import polars as pl
2
+ import numpy as np
3
+ import pandas as pd
4
+ import api_scraper
5
+ scrape = api_scraper.MLB_Scrape()
6
+ import requests
7
+ from shiny import App, reactive, ui, render
8
+ from shiny.ui import h2, tags
9
+ from shiny import App, reactive, ui, render
10
+ from shiny.ui import h2, tags
11
+
12
+
13
+ from shiny import App, ui, render
14
+
15
+
16
+ # Import the MLB_Scrape class from the module
17
+ from api_scraper import MLB_Scrape
18
+
19
+ # Initialize the scraper
20
+ scraper = MLB_Scrape()
21
+
22
+
23
+ # Call the get_teams method
24
+ teams = scraper.get_teams()
25
+ print(teams)
26
+
27
+ df_player = pl.concat([
28
+ scraper.get_players(sport_id=1,season=2025,game_type=['R']),
29
+ scraper.get_players(sport_id=11,season=2025,game_type=['R']),
30
+ scraper.get_players(sport_id=12,season=2025,game_type=['R']),
31
+ scraper.get_players(sport_id=13,season=2025,game_type=['R']),
32
+ scraper.get_players(sport_id=14,season=2025,game_type=['R']),
33
+ scraper.get_players(sport_id=22,season=2025,game_type=['R'])
34
+ ]).unique(subset=['player_id'])
35
+
36
+
37
+
38
+ teams_mlb = teams.filter(pl.col("league_id").is_in([103,104])).sort("abbreviation")
39
+ teams_dict = dict(zip(teams_mlb['team_id'],teams_mlb['abbreviation']))
40
+
41
+ teams_name_dict = dict(zip(teams_mlb['team_id'],teams_mlb['franchise']))
42
+
43
+ app_ui = ui.page_sidebar(
44
+ ui.sidebar(
45
+ ui.input_select(
46
+ "team_id",
47
+ "Select Team",
48
+ choices=teams_dict
49
+ ),
50
+ ui.input_switch("nri_only", "NRI Only"),
51
+ ui.div(
52
+ ui.div({"style": "font-size:1.2em;"}, ui.markdown("Legend")),
53
+ ui.div(
54
+ style="display: inline-block; width: 20px; height: 20px; background-color: #b7e1cd; margin-right: 10px;"
55
+ ),
56
+ ui.span("NRI", style="vertical-align: top;"),
57
+ style="padding: 10px;"
58
+ ),
59
+
60
+ ),
61
+ ui.card(
62
+ ui.div({"style": "font-size:2em;"}, ui.output_text("card_title")),
63
+
64
+ ui.output_table("team_stats")
65
+ )
66
+ )
67
+
68
+ def server(input, output, session):
69
+
70
+ @render.text
71
+ def card_title():
72
+ if input.nri_only():
73
+ return f"{teams_name_dict[int(input.team_id())]} Spring Training Roster Non-Roster Invitees"
74
+ else:
75
+ return f"{teams_name_dict[int(input.team_id())]} — Spring Training Roster"
76
+
77
+ @render.table
78
+ def team_stats():
79
+ # Get the selected team's data
80
+ i = int(input.team_id())
81
+
82
+ url = f'https://statsapi.mlb.com/api/v1/teams/{i}/roster/40man?season=2025'
83
+ data = requests.get(url).json()
84
+ # Normalize the roster data
85
+ roster_df = pd.json_normalize(data['roster'])
86
+ roster_df['nri'] = False
87
+ roster_df['status.code'] = ''
88
+ roster_df = roster_df.fillna('')
89
+
90
+
91
+ url = f'https://statsapi.mlb.com/api/v1/teams/{i}/roster/nonRosterInvitees?season=2025'
92
+ data = requests.get(url).json()
93
+ # Normalize the roster data
94
+ nri_roster_df = pd.json_normalize(data['roster'])
95
+ nri_roster_df['nri'] = True
96
+ nri_roster_df['parentTeamId'] = i
97
+ nri_roster_df = nri_roster_df.fillna('')
98
+
99
+ df_output = pd.concat([roster_df,nri_roster_df]).sort_values(by=['position.code','status.code']).reset_index(drop=True)
100
+ if input.nri_only():
101
+ df_output = df_output[df_output['status.code'] == 'NRI']
102
+
103
+
104
+ df_output = df_output.merge(df_player.to_pandas(),left_on='person.id',right_on='player_id',how='left')
105
+
106
+ conditions = [
107
+ (df_output['position.abbreviation'] == 'P') & (~df_output.duplicated(subset=['position.abbreviation'], keep='first')),
108
+ (df_output['position.abbreviation'] == 'C') & (~df_output.duplicated(subset=['position.abbreviation'], keep='first')),
109
+ (df_output['position.abbreviation'] == 'LF') & (~df_output.duplicated(subset=['position.abbreviation'], keep='first'))
110
+ ]
111
+
112
+ choices = ['Pitchers', 'Infielders', 'Outfielders']
113
+
114
+ df_output['position_group'] = np.select(conditions, choices, default='')
115
+
116
+
117
+
118
+ df_output['team'] = df_output['parentTeamId'].map(teams_dict)
119
+ df_output.loc[df_output['position.abbreviation'] == 'P', 'position.abbreviation'] = df_output['pitchHand'] + 'H' + df_output['position.abbreviation']
120
+ df_output['bat_throw'] = df_output['batSide'] + '/' + df_output['pitchHand']
121
+ df_output_small = df_output[['position_group','person.id', 'person.fullName',
122
+ 'position.abbreviation','team', 'status.code', 'age','weight', 'height', 'bat_throw']]
123
+ df_output_small['age'] = df_output_small['age'].replace('', np.nan).astype(int)
124
+ df_output_small['weight'] = df_output_small['weight'].replace('', np.nan).astype(int)
125
+
126
+ # # Insert blank rows with position group indicated
127
+ # blank_rows = []
128
+ # for idx, row in df_output_small.iterrows():
129
+ # if row['position_group']:
130
+ # blank_row = pd.Series([''] * len(df_output_small.columns), index=df_output_small.columns)
131
+ # blank_row['position_group'] = row['position_group']
132
+ # blank_rows.append((idx, blank_row))
133
+
134
+ # for idx, blank_row in reversed(blank_rows):
135
+ # df_output_small = pd.concat([df_output_small.iloc[:idx], pd.DataFrame([blank_row]), df_output_small.iloc[idx:]]).reset_index(drop=True)
136
+ # df_output_small.loc[(df_output_small['position_group'] != '') & (df_output_small['person.fullName'] != ''), 'position_group'] = ''
137
+
138
+ def highlight_nri(val):
139
+ color = 'yellow' if val else ''
140
+ return f'background-color: {color}'
141
+ # Function to alternate row colors
142
+ def highlight_alternate_rows(x):
143
+ return ['background-color: #ebebeb' if i % 2 == 0 else '' for i in range(len(x))]
144
+
145
+ #
146
+
147
+
148
+ df_output_small.columns = ['Group','Player ID', 'Name', 'Pos','Team', 'Status','Age','Weight', 'Height', 'B/T']
149
+
150
+
151
+
152
+ style_df = (df_output_small.style.set_precision(1)
153
+ .set_properties(**{'border': '3 px'}, overwrite=False)
154
+ .set_table_styles([{
155
+ 'selector': 'caption',
156
+ 'props': [
157
+ ('color', ''),
158
+ ('fontname', 'Century Gothic'),
159
+ ('font-size', '16px'),
160
+ ('font-style', 'italic'),
161
+ ('font-weight', ''),
162
+ ('text-align', 'centre'),
163
+ ]
164
+ }, {
165
+ 'selector': 'th',
166
+ 'props': [('font-size', '16px'), ('text-align', 'center'), ('Height', 'px'), ('color', 'black'), ('border', '1px black solid !important')]
167
+ }, {
168
+ 'selector': 'td',
169
+ 'props': [('text-align', 'center'), ('font-size', '16px'), ('color', 'black')]
170
+ }], overwrite=False)
171
+ .set_properties(**{'background-color': 'White', 'index': 'White', 'min-width': '72px'}, overwrite=False)
172
+ .set_table_styles([{'selector': 'th:first-child', 'props': [('background-color', 'white')]}], overwrite=False)
173
+ .set_table_styles([{'selector': 'tr:first-child', 'props': [('background-color', 'white')]}], overwrite=False)
174
+ .set_table_styles([{'selector': 'tr', 'props': [('line-height', '20px')]}], overwrite=False)
175
+ .set_properties(**{'Height': '8px'}, **{'text-align': 'center'}, overwrite=False)
176
+ .hide_index()
177
+ .set_properties(**{'border': '1px black solid'})
178
+ .set_table_styles([{'selector': 'thead th:nth-child(1)', 'props': [('min-width', '150px')]}], overwrite=False)
179
+ .set_table_styles([{'selector': 'thead th:nth-child(2)', 'props': [('min-width', '150px')]}], overwrite=False)
180
+ .set_table_styles([{'selector': 'thead th:nth-child(3)', 'props': [('min-width', '250px')]}], overwrite=False)
181
+ .set_table_styles([{'selector': 'thead th', 'props': [('height', '30px')]}], overwrite=False)
182
+ .apply(highlight_alternate_rows, axis=0, subset=df_output_small.columns[1:])
183
+ .applymap(lambda x: 'background-color: #bdbdbd' if x != '' else '', subset=['Group'])
184
+ .applymap(lambda x: 'background-color: #bdbdbd' if x else '', subset=['Group'])
185
+ # .apply(lambda x: ['background-color: #bdbdbd' if x['Group'] != '' else '' for _ in x], axis=1)
186
+ .set_properties(
187
+ **{'background-color':'#bdbdbd'}, # Apply only right border
188
+ subset=df_output_small.columns[0] # Only affects column 1
189
+ )
190
+ .set_properties(
191
+ **{'border-top': 'none', 'border-bottom': 'none'},
192
+ subset=df_output_small.columns[0] # Apply only to column 1
193
+ )
194
+
195
+ # .format({'Age': '{:.0f}', 'Weight': '{:.0f}'})
196
+ )
197
+ def highlight_nri(s):
198
+ return ['background-color: #b7e1cd' if s.name != 'Status' and s['Status'] == 'NRI' else '' for _ in s]
199
+
200
+ # style_df = style_df.style.apply(highlight_nri, axis=1, subset=style_df.columns[1:])
201
+
202
+ if not input.nri_only():
203
+ style_df = style_df.apply(highlight_nri, axis=1, subset=df_output_small.columns[1:])
204
+
205
+ def add_top_border(s):
206
+ return ['border-top: 3px solid black' if s['Group'] != '' else '' for _ in s]
207
+
208
+ styled_df = style_df.apply(add_top_border, axis=1)
209
+
210
+ def add_bottom_border(s):
211
+ return ['border-bottom: 3px solid black' if s.name == len(df_output_small) - 1 else '' for _ in s]
212
+
213
+ styled_df = style_df.apply(add_bottom_border, axis=1)
214
+
215
+
216
+ return style_df
 
 
 
 
 
217
  app = App(app_ui, server)