wsba-hockey 1.1.0__py3-none-any.whl → 1.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. wsba_hockey/api/api/index.py +129 -0
  2. wsba_hockey/data_pipelines.py +71 -8
  3. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/app.py +6 -5
  4. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/app.py +101 -0
  5. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/plot.py +71 -0
  6. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/rink_plot.py +245 -0
  7. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/app.py +1 -1
  8. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/plot.py +2 -0
  9. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/rink_plot.py +1 -1
  10. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/app.py +3 -3
  11. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/plot.py +2 -0
  12. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/rink_plot.py +1 -1
  13. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/app.py +44 -28
  14. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/plot.py +12 -3
  15. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/rink_plot.py +1 -1
  16. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/app.py +1 -1
  17. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/plot.py +5 -4
  18. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/rink_plot.py +1 -1
  19. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/app.py +103 -0
  20. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/plot.py +95 -0
  21. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/rink_plot.py +245 -0
  22. wsba_hockey/flask/app.py +77 -0
  23. wsba_hockey/tools/plotting.py +1 -0
  24. wsba_hockey/tools/scraping.py +6 -2
  25. wsba_hockey/tools/xg_model.py +1 -1
  26. wsba_hockey/workspace.py +28 -12
  27. wsba_hockey/wsba_main.py +10 -17
  28. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.2.dist-info}/METADATA +1 -1
  29. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.2.dist-info}/RECORD +32 -24
  30. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.2.dist-info}/WHEEL +0 -0
  31. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.2.dist-info}/licenses/LICENSE +0 -0
  32. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,245 @@
1
+
2
+ import numpy as np
3
+ import plotly.graph_objects as go
4
+ import io
5
+ import base64
6
+ import requests as rs
7
+ from PIL import Image
8
+
9
+ def rink(setting = "full", vertical = False):
10
+ '''
11
+ Function to plot rink in Plotly. Takes 2 arguments :
12
+
13
+ setting : full (default) for full ice, offense positive half of the ice, ozone positive quarter of ice, defense for negative half of the ice, dzone for negative quarter of the ice, and neutral for the neutral zone
14
+ vertical : True if you want a vertical rink, False (default) is for an horizontal rink
15
+
16
+ '''
17
+
18
+ def faceoff_circle(x, y, outer=True):
19
+ segments = []
20
+ theta = np.linspace(0, 2*np.pi, 300)
21
+ if outer:
22
+ # Outer circle
23
+ x_outer = x + 15*np.cos(theta)
24
+ y_outer = y + 15*np.sin(theta)
25
+ outer_circle = go.Scatter(x=x_outer, y=y_outer, mode='lines', line=dict(width=2, color='red'), showlegend=False, hoverinfo='skip')
26
+
27
+ segments.append(outer_circle)
28
+
29
+ # Inner circle
30
+ x_inner = x + np.cos(theta)
31
+ y_inner = y + np.sin(theta)
32
+ inner_circle = go.Scatter(x=x_inner, y=y_inner, mode='lines', fill='toself', fillcolor='rgba(255, 0, 0, 0.43)', line=dict(color='rgba(255, 0, 0, 1)', width=2), showlegend=False, hoverinfo='skip')
33
+
34
+ segments.append(inner_circle)
35
+
36
+ return segments #segments
37
+
38
+ fig = go.Figure()
39
+
40
+ if vertical :
41
+ setting_dict = {
42
+ "full" : [-101, 101],
43
+ "offense" : [0, 101],
44
+ "ozone" : [25, 101],
45
+ "defense" : [-101, 0],
46
+ "dzone" : [-101, -25],
47
+ "neutral" : [-25,25]
48
+ }
49
+ fig.update_layout(xaxis=dict(range=[-42.6, 42.6], showgrid=False, zeroline=False, showticklabels=False, constrain="domain"), yaxis=dict(range=setting_dict[setting], showgrid=False, zeroline=False, showticklabels=False, constrain="domain"),
50
+ showlegend=False, autosize=True, template="plotly_white")
51
+ fig.update_yaxes(
52
+ scaleanchor="x",
53
+ scaleratio=1,
54
+ )
55
+ def goal_crease(flip=1):
56
+ x_seq = np.linspace(-4, 4, 100)
57
+ x_goal = np.concatenate(([-4], x_seq, [4]))
58
+ y_goal = flip * np.concatenate(([89], 83 + x_seq**2/4**2*1.5, [89]))
59
+ goal_crease = go.Scatter(x=x_goal, y=y_goal, fill='toself', fillcolor='rgba(173, 216, 230, 0.3)', line=dict(color='red'))
60
+ return goal_crease
61
+
62
+ # Outer circle
63
+ theta = np.linspace(0, 2*np.pi, 300)
64
+ x_outer = 15 * np.cos(theta)
65
+ y_outer = 15 * np.sin(theta)
66
+ fig.add_trace(go.Scatter(x=x_outer, y=y_outer, mode='lines', line=dict(color='royalblue', width=2), showlegend=False, hoverinfo='skip'))
67
+ # Inner circle
68
+ theta2 = np.linspace(np.pi/2, 3*np.pi/2, 300)
69
+ x_inner = 42.5 + 10 * np.cos(theta2)
70
+ y_inner = 10 * np.sin(theta2)
71
+ fig.add_trace(go.Scatter(x=x_inner, y=y_inner, mode='lines', line=dict(color='red', width=2), showlegend=False, hoverinfo='skip'))
72
+ # Rink boundaries
73
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-42.5, y0=25, x1=42.5, y1=26, line=dict(color='royalblue', width=1), fillcolor='royalblue', opacity=1)
74
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-42.5, y0=-25, x1=42.5, y1=-26, line=dict(color='royalblue', width=1), fillcolor='royalblue', opacity=1)
75
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-42.5, y0=-0.5, x1=42.5, y1=0.5, line=dict(color='red', width=2), fillcolor='red')
76
+
77
+ # Goal crease
78
+ fig.add_trace(goal_crease())
79
+ fig.add_trace(goal_crease(-1))
80
+ # Goal lines
81
+ goal_line_extreme = 42.5 - 28 + np.sqrt(28**2 - (28-11)**2)
82
+ fig.add_shape(type='line', xref='x', yref='y', x0=-goal_line_extreme, y0=89, x1=goal_line_extreme, y1=89, line=dict(color='red', width=2))
83
+ fig.add_shape(type='line', xref='x', yref='y', x0=-goal_line_extreme, y0=-89, x1=goal_line_extreme, y1=-89, line=dict(color='red', width=2))
84
+
85
+ # Faceoff circles
86
+ fig.add_traces(faceoff_circle(-22, 69))
87
+ fig.add_traces(faceoff_circle(22, 69))
88
+ fig.add_traces(faceoff_circle(-22, -69))
89
+ fig.add_traces(faceoff_circle(22, -69))
90
+ fig.add_traces(faceoff_circle(-22, -20, False))
91
+ fig.add_traces(faceoff_circle(22, -20, False))
92
+ fig.add_traces(faceoff_circle(-22, 20, False))
93
+ fig.add_traces(faceoff_circle(22, 20, False))
94
+
95
+ # Sidelines
96
+ theta_lines = np.linspace(0, np.pi/2, 20)
97
+ x_lines1 = np.concatenate(([-42.5], -42.5 + 28 - 28*np.cos(theta_lines), 42.5 - 28 + 28*np.cos(np.flip(theta_lines))))
98
+ y_lines1 = np.concatenate(([15], 72 + 28*np.sin(theta_lines), 72 + 28*np.sin(np.flip(theta_lines))))
99
+ x_lines2 = np.concatenate(([-42.5], -42.5 + 28 - 28*np.cos(theta_lines), 42.5 - 28 + 28*np.cos(np.flip(theta_lines))))
100
+ y_lines2 = np.concatenate(([15], -72 - 28*np.sin(theta_lines), -72 - 28*np.sin(np.flip(theta_lines))))
101
+ fig.add_trace(go.Scatter(x=x_lines1, y=y_lines1, mode='lines', line=dict(color='white', width=2), showlegend=False, hoverinfo='skip'))
102
+ fig.add_trace(go.Scatter(x=x_lines2, y=y_lines2, mode='lines', line=dict(color='white', width=2), showlegend=False, hoverinfo='skip'))
103
+ fig.add_shape(type='line', xref='x', yref='y', x0=42.5, y0=-72.5, x1=42.5, y1=72.5, line=dict(color='white', width=2))
104
+ fig.add_shape(type='line', xref='x', yref='y', x0=-42.5, y0=-72.5, x1=-42.5, y1=72.5, line=dict(color='white', width=2))
105
+
106
+ # Add goals
107
+ goal_width = 6 # feet
108
+ goal_depth = 4 # feet
109
+
110
+ # Top goal
111
+ fig.add_shape(
112
+ type="rect",
113
+ xref="x",
114
+ yref="y",
115
+ x0=-goal_width / 2,
116
+ y0=89,
117
+ x1=goal_width / 2,
118
+ y1=89 + goal_depth,
119
+ line=dict(color="red", width=2),
120
+ )
121
+ # Bottom goal
122
+ fig.add_shape(
123
+ type="rect",
124
+ xref="x",
125
+ yref="y",
126
+ x0=-goal_width / 2,
127
+ y0=-89 - goal_depth,
128
+ x1=goal_width / 2,
129
+ y1=-89,
130
+ line=dict(color="red", width=2),
131
+ )
132
+
133
+ else :
134
+ setting_dict = {
135
+ "full" : [-101, 101],
136
+ "offense" : [0, 101],
137
+ "ozone" : [25, 101],
138
+ "defense" : [-101, 0],
139
+ "dzone" : [-101, -25]
140
+ }
141
+ fig.update_layout(xaxis=dict(range=setting_dict[setting], showgrid=False, zeroline=False, showticklabels=False), yaxis=dict(range=[-42.6, 42.6], showgrid=False, zeroline=False, showticklabels=False, constrain="domain"),
142
+ showlegend=True, autosize =True, template="plotly_white")
143
+ fig.update_yaxes(
144
+ scaleanchor="x",
145
+ scaleratio=1,
146
+ )
147
+ def goal_crease(flip=1):
148
+ y_seq = np.linspace(-4, 4, 100)
149
+ y_goal = np.concatenate(([-4], y_seq, [4]))
150
+ x_goal = flip * np.concatenate(([89], 83 + y_seq**2/4**2*1.5, [89]))
151
+ goal_crease = go.Scatter(x=x_goal, y=y_goal, fill='toself', fillcolor='rgba(173, 216, 230, 0.3)', line=dict(color='red'), showlegend=False, hoverinfo='skip')
152
+ return goal_crease
153
+
154
+ # Outer circle
155
+ theta = np.linspace(0, 2 * np.pi, 300)
156
+ x_outer = 15 * np.sin(theta)
157
+ y_outer = 15 * np.cos(theta)
158
+ fig.add_trace(go.Scatter(x=x_outer, y=y_outer, mode='lines', line=dict(color='royalblue', width=2), showlegend=False, hoverinfo='skip'))
159
+ # Inner circle
160
+ theta2 = np.linspace(3 * np.pi / 2, np.pi / 2, 300) # Update theta2 to rotate the plot by 180 degrees
161
+ x_inner = 10 * np.sin(theta2) # Update x_inner to rotate the plot by 180 degrees
162
+ y_inner = -42.5 - 10 * np.cos(theta2) # Update y_inner to rotate the plot by 180 degrees
163
+ fig.add_trace(go.Scatter(x=x_inner, y=y_inner, mode='lines', line=dict(color='red', width=2), showlegend=False, hoverinfo='skip'))
164
+
165
+ # Rink boundaries
166
+ fig.add_shape(type='rect', xref='x', yref='y', x0=25, y0=-42.5, x1=26, y1=42.5, line=dict(color='royalblue', width=1), fillcolor='royalblue', opacity=1)
167
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-25, y0=-42.5, x1=-26, y1=42.5, line=dict(color='royalblue', width=1), fillcolor='royalblue', opacity=1)
168
+ fig.add_shape(type='rect', xref='x', yref='y', x0=-0.5, y0=-42.5, x1=0.5, y1=42.5, line=dict(color='red', width=2), fillcolor='red')
169
+ # Goal crease
170
+ fig.add_trace(goal_crease())
171
+ fig.add_trace(goal_crease(-1))
172
+ # Goal lines
173
+ goal_line_extreme = 42.5 - 28 + np.sqrt(28 ** 2 - (28 - 11) ** 2)
174
+ fig.add_shape(type='line', xref='x', yref='y', x0=89, y0=-goal_line_extreme, x1=89, y1=goal_line_extreme, line=dict(color='red', width=2))
175
+ fig.add_shape(type='line', xref='x', yref='y', x0=-89, y0=-goal_line_extreme, x1=-89, y1=goal_line_extreme, line=dict(color='red', width=2))
176
+ # Faceoff circles
177
+ fig.add_traces(faceoff_circle(-69, -22))
178
+ fig.add_traces(faceoff_circle(-69, 22))
179
+ fig.add_traces(faceoff_circle(69, -22))
180
+ fig.add_traces(faceoff_circle(69, 22))
181
+ fig.add_traces(faceoff_circle(-20, -22, False))
182
+ fig.add_traces(faceoff_circle(-20, 22, False))
183
+ fig.add_traces(faceoff_circle(20, -22, False))
184
+ fig.add_traces(faceoff_circle(20, 22, False))
185
+
186
+ # Sidelines
187
+ theta_lines = np.linspace(0, np.pi / 2, 20)
188
+ x_lines1 = np.concatenate(([15], 72 + 28 * np.sin(theta_lines), 72 + 28 * np.sin(np.flip(theta_lines))))
189
+ y_lines1 = np.concatenate(([-42.5], -42.5 + 28 - 28 * np.cos(theta_lines), 42.5 - 28 + 28 * np.cos(np.flip(theta_lines))))
190
+ x_lines2 = np.concatenate(([15], -72 - 28 * np.sin(theta_lines), -72 - 28 * np.sin(np.flip(theta_lines))))
191
+ y_lines2 = np.concatenate(([-42.5], -42.5 + 28 - 28 * np.cos(theta_lines), 42.5 - 28 + 28 * np.cos(np.flip(theta_lines))))
192
+ fig.add_trace(go.Scatter(x=x_lines1, y=y_lines1, mode='lines', line=dict(color='white', width=2), showlegend=False, hoverinfo='skip'))
193
+ fig.add_trace(go.Scatter(x=x_lines2, y=y_lines2, mode='lines', line=dict(color='white', width=2), showlegend=False, hoverinfo='skip'))
194
+ fig.add_shape(type='line', xref='x', yref='y', x0=-72.5, y0=-42.5, x1=72.5, y1=-42.5, line=dict(color='white', width=2))
195
+ fig.add_shape(type='line', xref='x', yref='y', x0=-72.5, y0=42.5, x1=72.5, y1=42.5, line=dict(color='white', width=2))
196
+
197
+ # Add goals
198
+ goal_width = 6 # feet
199
+ goal_depth = 4 # feet
200
+
201
+ # Right goal
202
+ fig.add_shape(
203
+ type="rect",
204
+ xref="x",
205
+ yref="y",
206
+ x0=89,
207
+ y0=-goal_width / 2,
208
+ x1=89 + goal_depth,
209
+ y1=goal_width / 2,
210
+ line=dict(color="red", width=2),
211
+ )
212
+ # Left goal
213
+ fig.add_shape(
214
+ type="rect",
215
+ xref="x",
216
+ yref="y",
217
+ x0=-89 - goal_depth,
218
+ y0=-goal_width / 2,
219
+ x1=-89,
220
+ y1=goal_width / 2,
221
+ line=dict(color="red", width=2),
222
+ )
223
+
224
+ # Add logo
225
+ logo = Image.open(rs.get('https://f005.backblazeb2.com/file/weakside-breakout/utils/wsba.png',stream=True).raw)
226
+
227
+ fig.add_layout_image(
228
+ dict(
229
+ source=logo,
230
+ xref="x",
231
+ yref="y",
232
+ x=-12,
233
+ y=12,
234
+ sizex=24,
235
+ sizey=24,
236
+ sizing="stretch",
237
+ opacity=1)
238
+ )
239
+
240
+ #Set background to transparent
241
+ fig.update_layout(
242
+ paper_bgcolor="rgba(0,0,0,0)",
243
+ plot_bgcolor="rgba(0,0,0,0)"
244
+ )
245
+ return fig
@@ -44,7 +44,7 @@ def server(input, output, session):
44
44
  #Determine which season to load based on the input
45
45
  season = query['season'][0]
46
46
  #Load appropriate dataframe
47
- df = pd.read_parquet(f'https://f005.backblazeb2.com/file/weakside-breakout/pbp/{season}.parquet')
47
+ df = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season}.parquet')
48
48
 
49
49
  #Prepare dataframe for plotting based on URL parameters
50
50
  df = df.loc[(df['season'].astype(str).isin(query['season']))&(df['season_type'].astype(str).isin(query['season_type']))].replace({np.nan: None})
@@ -13,6 +13,7 @@ def heatmap(df,skater,team,events,strengths,onice):
13
13
  df['event_team_abbr_2'] = np.where(df['home_team_abbr']==df['event_team_abbr'],df['away_team_abbr'],df['home_team_abbr'])
14
14
  df['strength_state_2'] = df['strength_state'].str[::-1]
15
15
 
16
+ df = df.fillna(0)
16
17
  df = df.loc[(df['event_type'].isin(events))&(df['x_adj'].notna())&(df['y_adj'].notna())]
17
18
  if onice == 'for':
18
19
  df['x'] = abs(df['x_adj'])
@@ -38,6 +39,7 @@ def heatmap(df,skater,team,events,strengths,onice):
38
39
  df['onice_against'] = np.where(df['away_team_abbr']==df['event_team_abbr'],df['home_on_ice'],df['away_on_ice'])
39
40
 
40
41
  df['strength_state'] = np.where(df['strength_state'].isin(['5v5','5v4','4v5']),df['strength_state'],'Other')
42
+ df['strength_state_2'] = np.where(df['strength_state_2'].isin(['5v5','5v4','4v5']),df['strength_state_2'],'Other')
41
43
 
42
44
  if strengths != 'all':
43
45
  if onice == 'against':
@@ -222,7 +222,7 @@ def rink(setting = "full", vertical = False):
222
222
  )
223
223
 
224
224
  # Add logo
225
- logo = Image.open(rs.get('https://f005.backblazeb2.com/file/weakside-breakout/utils/wsba.png',stream=True).raw)
225
+ logo = Image.open(rs.get('https://weakside-breakout.s3.us-east-2.amazonaws.com/utils/wsba.png',stream=True).raw)
226
226
 
227
227
  fig.add_layout_image(
228
228
  dict(
@@ -50,12 +50,12 @@ def server(input, output, session):
50
50
  season_2 = query['season_2'][0]
51
51
  #Load appropriate dataframes
52
52
  if season_1 == season_2:
53
- df_1 = pd.read_parquet(f'https://f005.backblazeb2.com/file/weakside-breakout/pbp/{season_1}.parquet')
53
+ df_1 = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season_1}.parquet')
54
54
  df_2 = df_1
55
55
 
56
56
  else:
57
- df_1 = pd.read_parquet(f'https://f005.backblazeb2.com/file/weakside-breakout/pbp/{season_1}.parquet')
58
- df_2 = pd.read_parquet(f'https://f005.backblazeb2.com/file/weakside-breakout/pbp/{season_2}.parquet')
57
+ df_1 = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season_1}.parquet')
58
+ df_2 = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season_2}.parquet')
59
59
 
60
60
  events = ['missed-shot','shot-on-goal','goal']
61
61
  team_xg = {}
@@ -12,6 +12,7 @@ def wsba_rink(setting='full', vertical=False):
12
12
  def heatmap_prep(df,team,events,strengths,onice,flip=False):
13
13
  df['event_team_abbr_2'] = np.where(df['home_team_abbr']==df['event_team_abbr'],df['away_team_abbr'],df['home_team_abbr'])
14
14
 
15
+ df = df.fillna(0)
15
16
  df = df.loc[(df['event_type'].isin(events))&(df['x_adj'].notna())&(df['y_adj'].notna())]
16
17
  if flip:
17
18
  if onice == 'for':
@@ -55,6 +56,7 @@ def heatmap_prep(df,team,events,strengths,onice,flip=False):
55
56
  df['onice_against'] = np.where(df['away_team_abbr']==df['event_team_abbr'],df['home_on_ice'],df['away_on_ice'])
56
57
 
57
58
  df['strength_state'] = np.where(df['strength_state'].isin(['5v5','5v4','4v5']),df['strength_state'],'Other')
59
+ df['strength_state_2'] = np.where(df['strength_state_2'].isin(['5v5','5v4','4v5']),df['strength_state_2'],'Other')
58
60
 
59
61
  if strengths != 'all':
60
62
  df = df.loc[((df['strength_state'].isin(strengths)))]
@@ -222,7 +222,7 @@ def rink(setting = "full", vertical = False):
222
222
  )
223
223
 
224
224
  # Add logo
225
- logo = Image.open(rs.get('https://f005.backblazeb2.com/file/weakside-breakout/utils/wsba.png',stream=True).raw)
225
+ logo = Image.open(rs.get('https://weakside-breakout.s3.us-east-2.amazonaws.com/utils/wsba.png',stream=True).raw)
226
226
 
227
227
  fig.add_layout_image(
228
228
  dict(
@@ -109,7 +109,7 @@ def server(input, output, session):
109
109
  query = reactive.Value(None)
110
110
 
111
111
  def schedule():
112
- schedule = pd.read_csv('https://f005.backblazeb2.com/file/weakside-breakout/info/schedule.csv')
112
+ schedule = pd.read_csv('https://weakside-breakout.s3.us-east-2.amazonaws.com/info/schedule.csv')
113
113
 
114
114
  return schedule.loc[schedule['gameState'].isin(['OFF','FINAL'])]
115
115
 
@@ -126,7 +126,8 @@ def server(input, output, session):
126
126
  'event_type':['missed-shot,shot-on-goal,goal'],
127
127
  'strength_state':['all'],
128
128
  'filters':['false'],
129
- 'table':['false']
129
+ 'table':['false'],
130
+ 'title':['true']
130
131
  }
131
132
 
132
133
  for key in defaults.keys():
@@ -223,7 +224,7 @@ def server(input, output, session):
223
224
  front_year = int(query['game_id'][0][0:4])
224
225
  season = f'{front_year}{front_year+1}'
225
226
  #Load appropriate dataframe
226
- df = pd.read_parquet(f'https://f005.backblazeb2.com/file/weakside-breakout/pbp/{season}.parquet')
227
+ df = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season}.parquet')
227
228
 
228
229
  #Prepare dataframe for plotting based on URL parameters
229
230
  game_data = df.loc[df['game_id'].astype(str).isin(query['game_id'])].replace({np.nan: None})
@@ -255,7 +256,7 @@ def server(input, output, session):
255
256
  color='Team',
256
257
  color_discrete_map=colors,
257
258
  hover_name='Description',
258
- hover_data=['Event Num.', 'Period', 'Time (in seconds)',
259
+ hover_data=['Event Num.', 'Period', 'Time (in Period)',
259
260
  'Strength',
260
261
  'Away Score', 'Home Score', 'x', 'y',
261
262
  'Event Distance from Attacking Net',
@@ -265,28 +266,44 @@ def server(input, output, session):
265
266
  for trace in plot.data:
266
267
  rink.add_trace(trace)
267
268
 
268
- return rink.update_layout(
269
- title=dict(
270
- text=game_title,
271
- x=0.5, y=0.94,
272
- xanchor='center',
273
- yanchor='top',
274
- font=dict(color='white')
275
- ),
276
-
277
- legend=dict(
278
- orientation='h',
279
- x=0.49,
280
- y=-0.04,
281
- xanchor='center',
282
- yanchor='bottom',
283
- font=dict(color='white')
284
- ),
269
+ if active_params()['title'][0]=='false':
270
+ return rink.update_layout(
271
+ legend=dict(
272
+ orientation='h',
273
+ x=0.49,
274
+ y=-0.04,
275
+ xanchor='center',
276
+ yanchor='bottom',
277
+ font=dict(color='white')
278
+ ),
285
279
 
286
- hoverlabel=dict(
287
- font_size=10
280
+ hoverlabel=dict(
281
+ font_size=10
282
+ )
283
+ )
284
+ else:
285
+ return rink.update_layout(
286
+ title=dict(
287
+ text=game_title,
288
+ x=0.5, y=0.94,
289
+ xanchor='center',
290
+ yanchor='top',
291
+ font=dict(color='white')
292
+ ),
293
+
294
+ legend=dict(
295
+ orientation='h',
296
+ x=0.49,
297
+ y=-0.04,
298
+ xanchor='center',
299
+ yanchor='bottom',
300
+ font=dict(color='white')
301
+ ),
302
+
303
+ hoverlabel=dict(
304
+ font_size=10
305
+ )
288
306
  )
289
- )
290
307
 
291
308
  @output
292
309
  @render.ui
@@ -322,10 +339,9 @@ def server(input, output, session):
322
339
  if input.metric_select()=='Timelines':
323
340
  return None
324
341
  else:
325
- df = game_df.get().copy()[['event_num','period','seconds_elapsed','strength_state','event_type','Description','event_team_abbr','event_player_1_name','shot_type','zone_code','x','y','away_score','home_score','xG']].rename(columns={
342
+ df = game_df.get().copy()[['event_num','period','Time (in Period)','strength_state','event_type','Description','event_team_abbr','event_player_1_name','shot_type','zone_code','x','y','away_score','home_score','xG']].rename(columns={
326
343
  'event_num':'#',
327
344
  'period':'Period',
328
- 'seconds_elapsed':'Seconds',
329
345
  'strength_state':'Strength State',
330
346
  'event_type':'Event',
331
347
  'event_team_abbr':'Team',
@@ -352,7 +368,7 @@ def server(input, output, session):
352
368
  data = wsba_plt.timelines(game_df.get().copy())
353
369
  colors = wsba_plt.colors(data)
354
370
  timelines = px.line(data,
355
- x='Time (in seconds)',
371
+ x='Time (in Period)',
356
372
  y=input.timeline_select(),
357
373
  color='Team',
358
374
  color_discrete_map=colors,
@@ -368,7 +384,7 @@ def server(input, output, session):
368
384
  paper_bgcolor="rgba(0,0,0,0)",
369
385
  plot_bgcolor="rgba(0,0,0,0)",
370
386
  font_color='white',
371
- xaxis=dict(title=dict(text='Time (in seconds)'),showgrid=False),
387
+ xaxis=dict(title=dict(text='Time (in Period)'),showgrid=False),
372
388
  yaxis=dict(title=dict(text=input.timeline_select()),showgrid=False),
373
389
 
374
390
  legend=dict(
@@ -23,7 +23,7 @@ def colors(df):
23
23
  away_abbr = list(df['away_team_abbr'])[0]
24
24
  home_abbr = list(df['home_team_abbr'])[0]
25
25
  season = list(df['season'])[0]
26
- team_data = pd.read_csv('https://f005.backblazeb2.com/file/weakside-breakout/info/nhl_teaminfo.csv')
26
+ team_data = pd.read_csv('https://weakside-breakout.s3.us-east-2.amazonaws.com/info/nhl_teaminfo.csv')
27
27
 
28
28
  team_info ={
29
29
  away_abbr: list(team_data.loc[team_data['WSBA']==f'{away_abbr}{season}','Primary Color'])[0],
@@ -32,13 +32,21 @@ def colors(df):
32
32
 
33
33
  return team_info
34
34
 
35
+ def convert_time(seconds,period):
36
+ period_seconds = seconds - ((period-1)*1200)
37
+ minutes = int(period_seconds/60)
38
+ seconds = int(((period_seconds/60)-minutes)*60)
39
+
40
+ return f'{minutes}:{seconds:02}'
41
+
35
42
  def prep(df,events,strengths):
36
43
  df = df.loc[(df['event_type'].isin(events))]
37
-
44
+
45
+ df['strength_state'] = np.where(df['strength_state'].isin(['5v5','5v4','4v5']),df['strength_state'],'Other')
38
46
  if 'all' not in strengths:
39
47
  df = df.loc[((df['strength_state'].isin(strengths)))]
40
48
 
41
- df['xG'] = df['xG'].fillna(0)
49
+ df = df.fillna(0)
42
50
  df['size'] = np.where(df['xG']<=0,40,df['xG']*400)
43
51
 
44
52
  df['marker'] = df['event_type'].replace(event_markers)
@@ -48,6 +56,7 @@ def prep(df,events,strengths):
48
56
  df['Event Num.'] = df['event_num']
49
57
  df['Period'] = df['period']
50
58
  df['Time (in seconds)'] = df['seconds_elapsed']
59
+ df['Time (in Period)'] = df.apply(lambda x: convert_time(x['Time (in seconds)'],x['Period']), axis=1)
51
60
  df['Strength'] = df['strength_state']
52
61
  df['Away Score'] = df['away_score']
53
62
  df['Home Score'] = df['home_score']
@@ -222,7 +222,7 @@ def rink(setting = "full", vertical = False):
222
222
  )
223
223
 
224
224
  # Add logo
225
- logo = Image.open(rs.get('https://f005.backblazeb2.com/file/weakside-breakout/utils/wsba.png',stream=True).raw)
225
+ logo = Image.open(rs.get('https://weakside-breakout.s3.us-east-2.amazonaws.com/utils/wsba.png',stream=True).raw)
226
226
 
227
227
  fig.add_layout_image(
228
228
  dict(
@@ -46,7 +46,7 @@ def server(input, output, session):
46
46
  #Determine which season to load based on the input
47
47
  season = query['season'][0]
48
48
  #Load appropriate dataframe
49
- df = pd.read_parquet(f'https://f005.backblazeb2.com/file/weakside-breakout/pbp/{season}.parquet')
49
+ df = pd.read_parquet(f'https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season}.parquet')
50
50
 
51
51
  #Prepare dataframe for plotting based on URL parameters
52
52
  df = df.loc[(df['event_player_1_id'].astype(str).str.replace('.0','').isin(query['skater']))&(df['season'].astype(str).isin(query['season']))&(df['event_team_abbr'].astype(str).isin(query['team']))&(df['season_type'].astype(str).isin(query['season_type']))].replace({np.nan: None})
@@ -20,9 +20,9 @@ def wsba_rink(setting='full', vertical=False):
20
20
  return rink_plot.rink(setting=setting, vertical=vertical)
21
21
 
22
22
  def colors(df):
23
- team = list(df['away_team_abbr'])[0]
23
+ team = list(df['event_team_abbr'])[0]
24
24
  season = list(df['season'])[0]
25
- team_data = pd.read_csv('https://f005.backblazeb2.com/file/weakside-breakout/info/nhl_teaminfo.csv')
25
+ team_data = pd.read_csv('https://weakside-breakout.s3.us-east-2.amazonaws.com/info/nhl_teaminfo.csv')
26
26
 
27
27
  team_info ={
28
28
  team: list(team_data.loc[team_data['WSBA']==f'{team}{season}','Primary Color'])[0],
@@ -37,7 +37,7 @@ def prep(df,events,strengths):
37
37
  if strengths != 'all':
38
38
  df = df.loc[((df['strength_state'].isin(strengths)))]
39
39
 
40
- df['xG'] = df['xG'].fillna(0)
40
+ df = df.fillna(0)
41
41
  df['size'] = np.where(df['xG']<=0,40,df['xG']*400)
42
42
 
43
43
  df['marker'] = df['event_type'].replace(event_markers)
@@ -50,9 +50,10 @@ def prep(df,events,strengths):
50
50
  df['Strength'] = df['strength_state']
51
51
  df['Away Score'] = df['away_score']
52
52
  df['Home Score'] = df['home_score']
53
- df['x'] = np.where(df['x_adj']<0,-df['y_adj'],df['y_adj'])
53
+ df['x'] = np.where(df['x_adj']<0,df['y_adj'],-df['y_adj'])
54
54
  df['y'] = abs(df['x_adj'])
55
55
  df['Event Distance from Attacking Net'] = df['event_distance']
56
56
  df['Event Angle to Attacking Net'] = df['event_angle']
57
57
  df['xG'] = df['xG']*100
58
+
58
59
  return df
@@ -222,7 +222,7 @@ def rink(setting = "full", vertical = False):
222
222
  )
223
223
 
224
224
  # Add logo
225
- logo = Image.open(rs.get('https://f005.backblazeb2.com/file/weakside-breakout/utils/wsba.png',stream=True).raw)
225
+ logo = Image.open(rs.get('https://weakside-breakout.s3.us-east-2.amazonaws.com/utils/wsba.png',stream=True).raw)
226
226
 
227
227
  fig.add_layout_image(
228
228
  dict(