wsba-hockey 1.1.0__py3-none-any.whl → 1.1.1__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 (44) hide show
  1. wsba_hockey/api/api/index.py +129 -0
  2. wsba_hockey/api/api/main.py +4 -0
  3. wsba_hockey/api/api/tools/__init__.py +0 -0
  4. wsba_hockey/api/api/tools/agg.py +374 -0
  5. wsba_hockey/api/api/tools/archive/old_scraping.py +1104 -0
  6. wsba_hockey/api/api/tools/plotting.py +144 -0
  7. wsba_hockey/api/api/tools/scraping.py +1000 -0
  8. wsba_hockey/api/api/tools/utils/__init__.py +1 -0
  9. wsba_hockey/api/api/tools/utils/config.py +14 -0
  10. wsba_hockey/api/api/tools/utils/save_pages.py +133 -0
  11. wsba_hockey/api/api/tools/utils/shared.py +450 -0
  12. wsba_hockey/api/api/tools/xg_model.py +455 -0
  13. wsba_hockey/api/api/wsba_main.py +1213 -0
  14. wsba_hockey/data_pipelines.py +71 -8
  15. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/app.py +6 -5
  16. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/app.py +101 -0
  17. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/plot.py +71 -0
  18. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/rink_plot.py +245 -0
  19. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/app.py +1 -1
  20. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/plot.py +2 -0
  21. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/rink_plot.py +1 -1
  22. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/app.py +3 -3
  23. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/plot.py +2 -0
  24. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/rink_plot.py +1 -1
  25. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/app.py +44 -28
  26. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/plot.py +12 -3
  27. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/rink_plot.py +1 -1
  28. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/app.py +1 -1
  29. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/plot.py +5 -4
  30. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/rink_plot.py +1 -1
  31. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/app.py +103 -0
  32. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/plot.py +95 -0
  33. wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/rink_plot.py +245 -0
  34. wsba_hockey/flask/app.py +77 -0
  35. wsba_hockey/tools/plotting.py +2 -1
  36. wsba_hockey/tools/scraping.py +7 -3
  37. wsba_hockey/tools/xg_model.py +3 -3
  38. wsba_hockey/workspace.py +28 -12
  39. wsba_hockey/wsba_main.py +10 -17
  40. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.1.dist-info}/METADATA +1 -1
  41. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.1.dist-info}/RECORD +44 -24
  42. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.1.dist-info}/WHEEL +0 -0
  43. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.1.dist-info}/licenses/LICENSE +0 -0
  44. {wsba_hockey-1.1.0.dist-info → wsba_hockey-1.1.1.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://weakside-breakout.s3.us-east-2.amazonaws.com/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
@@ -0,0 +1,77 @@
1
+ from flask import Flask, render_template, request, redirect
2
+ from flask_sqlalchemy import SQLAlchemy
3
+ import pandas as pd
4
+
5
+ app = Flask(__name__)
6
+
7
+ #Globals
8
+ seasons = [
9
+ '20102011',
10
+ '20112012',
11
+ '20122013',
12
+ '20132014',
13
+ '20142015',
14
+ '20152016',
15
+ '20162017',
16
+ '20172018',
17
+ '20182019',
18
+ '20192020',
19
+ '20202021',
20
+ '20212022',
21
+ '20222023',
22
+ '20232024',
23
+ '20242025'
24
+ ]
25
+
26
+ #Generate pages
27
+ @app.route("/")
28
+ def index():
29
+ return render_template("index.html")
30
+
31
+ @app.route("/about/about")
32
+ def about():
33
+ return render_template("about/about.html")
34
+
35
+ @app.route("/about/glossary")
36
+ def glossary():
37
+ return render_template("about/glossary.html")
38
+
39
+ @app.route("/about/goal_impact")
40
+ def goal_impact():
41
+ return render_template("about/goal_impact.html")
42
+
43
+ @app.route("/about/resources")
44
+ def resources():
45
+ return render_template("about/resources.html")
46
+
47
+ @app.route("/about/xg_model")
48
+ def xg_model():
49
+ return render_template("about/xg_model.html")
50
+
51
+ @app.route("/games/schedule")
52
+ def schedule():
53
+ return render_template("games/schedule.html")
54
+
55
+ @app.route("/games/game_metrics")
56
+ def pbp_viewer():
57
+ return render_template("games/game_metrics.html")
58
+
59
+ @app.route("/players/skater_stats", methods=["GET", "POST"])
60
+ def skater_stats():
61
+ filters = {}
62
+ for filter in ['season','span','strength','position','display','type','min_age','min_toi']:
63
+ print(request.args.get(filter))
64
+ filters.update({filter:request.args.get(filter)})
65
+
66
+ return render_template("players/skater_stats.html")
67
+
68
+ @app.route("/players/goalie_stats")
69
+ def goalie_stats():
70
+ return render_template("players/goalie_stats.html")
71
+
72
+ @app.route("/players/team_stats")
73
+ def team_stats():
74
+ return render_template("players/team_stats.html")
75
+
76
+ if __name__ == "__main__":
77
+ app.run()
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import matplotlib.pyplot as plt
2
3
  import numpy as np
3
4
  import pandas as pd
@@ -5,7 +6,7 @@ from hockey_rink import NHLRink
5
6
  from hockey_rink import CircularImage
6
7
  from scipy.interpolate import griddata
7
8
  from scipy.ndimage import gaussian_filter
8
- from wsba_hockey.tools.xg_model import *
9
+ from tools.xg_model import *
9
10
 
10
11
  ### PLOTTING FUNCTIONS ###
11
12
  # Provided in this file are basic plotting functions for the WSBA Hockey Python package. #
@@ -6,7 +6,7 @@ import pandas as pd
6
6
  import requests as rs
7
7
  import json as json_lib
8
8
  from bs4 import BeautifulSoup
9
- from wsba_hockey.tools.utils.shared import *
9
+ from tools.utils.shared import *
10
10
  warnings.filterwarnings('ignore')
11
11
 
12
12
  ### SCRAPING FUNCTIONS ###
@@ -28,7 +28,7 @@ def get_col():
28
28
  return [
29
29
  'season','season_type','game_id','game_date',"start_time","venue","venue_location",
30
30
  'away_team_abbr','home_team_abbr','event_num','period','period_type',
31
- 'seconds_elapsed',"strength_state","strength_state_venue","home_team_defending_side",
31
+ 'seconds_elapsed','period_time','game_time',"strength_state","strength_state_venue","home_team_defending_side",
32
32
  "event_type_code","event_type","description","event_reason",
33
33
  "penalty_type","penalty_duration","penalty_attribution",
34
34
  "event_team_abbr","event_team_venue",
@@ -984,7 +984,11 @@ def combine_data(info,sources):
984
984
  df[f'{venue}_corsi'] = ((df['event_team_venue']==venue)&(df['event_type'].isin(['blocked-shot','missed-shot','shot-on-goal','goal']))).cumsum()
985
985
  df[f'{venue}_fenwick'] = ((df['event_team_venue']==venue)&(df['event_type'].isin(['missed-shot','shot-on-goal','goal']))).cumsum()
986
986
  df[f'{venue}_penalties'] = ((df['event_team_venue']==venue)&(df['event_type']=='penalty')).cumsum()
987
-
987
+
988
+ #Add time adjustments
989
+ df['period_time'] = np.trunc((df['seconds_elapsed']-((df['period']-1)*1200))/60).astype(str).str.replace('.0','')+":"+(df['seconds_elapsed'] % 60).astype(str).str.pad(2,'left','0')
990
+ df['game_time'] = np.trunc(df['seconds_elapsed']/60).astype(str).str.replace('.0','')+":"+(df['seconds_elapsed'] % 60).astype(str).str.pad(2,'left','0')
991
+
988
992
  #Forward fill as necessary
989
993
  cols = ['period_type','home_team_defending_side','away_coach','home_coach']
990
994
  for col in cols:
@@ -4,8 +4,8 @@ import pandas as pd
4
4
  import numpy as np
5
5
  import xgboost as xgb
6
6
  import scipy.sparse as sp
7
- import wsba_hockey.wsba_main as wsba
8
- import wsba_hockey.tools.scraping as scraping
7
+ import wsba_main as wsba
8
+ import tools.scraping as scraping
9
9
  import matplotlib.pyplot as plt
10
10
  from sklearn.calibration import calibration_curve
11
11
  from sklearn.metrics import roc_curve, auc
@@ -118,7 +118,7 @@ def fix_players(pbp):
118
118
 
119
119
  def prep_xG_data(data):
120
120
  #Prep data for xG training and calculation
121
- data = fix_players(data)
121
+ #data = fix_players(data)
122
122
 
123
123
  #Informal groupby
124
124
  data = data.sort_values(by=['season','game_id','period','seconds_elapsed','event_num'])
wsba_hockey/workspace.py CHANGED
@@ -6,30 +6,46 @@ import numpy as np
6
6
 
7
7
  season_load = wsba.repo_load_seasons()
8
8
 
9
- select = season_load[3:18]
10
-
11
- #data.pbp(select)
12
- #data.pbp_db(select)
9
+ select = season_load[9:17]
13
10
 
14
11
  #pbp = data.load_pbp_db(select)
15
12
 
16
13
  #wsba.wsba_xG(pbp,hypertune=True,train=True,train_runs=30,cv_runs=30)
14
+ #select = season_load[3:18]
17
15
  #for season in select:
18
16
  # wsba.nhl_apply_xG(data.load_pbp([season])).to_parquet(f'pbp/parquet/nhl_pbp_{season}.parquet',index=False)
19
17
  #data.pbp_db(select)
20
- test = pd.read_parquet('backblaze_pbp/20242025.parquet')
21
- test.loc[(test['event_goalie_id']==8476945)].to_csv('Hellebuyck.csv',index=False)
22
18
 
23
- mp = pd.read_csv('shots_2024.csv')
24
- mp.loc[(mp['goalieIdForShot']==8476945)].to_csv('mfreally.csv',index=False)
19
+ #test = pd.read_parquet('aws_pbp/20242025.parquet')
25
20
  #wsba.roc_auc_curve(test,'tools/xg_model/wsba_xg.joblib')
26
21
  #wsba.feature_importance('tools/xg_model/wsba_xg.joblib')
27
22
  #wsba.reliability(test,'tools/xg_model/wsba_xg.joblib')
28
23
 
29
- ## UPLOAD TO BACKBLAZE ##
30
-
31
- #data.stats(['goalie'],select)
32
- #data.game_log(['goalie'],select)
24
+ #data.build_stats(['skater','team','goalie'],select)
25
+ #data.game_log(['skater','goalie'],select)
26
+ #data.fix_names(['skater','goalie'],select)
33
27
 
28
+ ## DATA EXPORT ##
34
29
  #data.push_to_sheet(select,['skaters','team','info'])
35
30
 
31
+ wsba.nhl_scrape_game(['2024020008'],remove=[]).to_csv('wtfwhy.csv',index=False)
32
+
33
+ pbp = pd.read_parquet('pbp/parquet/nhl_pbp_20242025.parquet')
34
+ helle = pbp.loc[pbp['event_goalie_id']==8476945,
35
+ ['game_id','period','seconds_elapsed',
36
+ 'strength_state','event_type','description',
37
+ 'event_goalie_id','x','y','xG']]
38
+ mp = pd.read_csv('shots_2024.csv')
39
+ goalie = mp.loc[mp['goalieIdForShot']==8476945,
40
+ ['game_id','period','time','event','goalieIdForShot',
41
+ 'xCord','yCord','xGoal']].replace({
42
+ 'SHOT':'shot-on-goal',
43
+ 'MISS':'missed-shot',
44
+ 'GOAL':'goal'
45
+ })
46
+
47
+ helle.to_csv('hellebuyck.csv',index=False)
48
+ helle['game_id'] = helle['game_id'].astype(str)
49
+ goalie['game_id'] = ('20240'+goalie['game_id'].astype(str))
50
+ pd.merge(helle,goalie,how='left',left_on=['game_id','period','seconds_elapsed','event_type','x','y'],right_on=['game_id','period','time','event','xCord','yCord']).to_csv('test.csv',index=False)
51
+
wsba_hockey/wsba_main.py CHANGED
@@ -475,16 +475,19 @@ def nhl_scrape_player_data(player_ids):
475
475
  api = f'https://api-web.nhle.com/v1/player/{player_id}/landing'
476
476
 
477
477
  data = pd.json_normalize(rs.get(api).json())
478
-
479
478
  #Add name column
480
479
  data['fullName'] = (data['firstName.default'] + " " + data['lastName.default']).str.upper()
481
480
 
482
481
  #Append
483
482
  infos.append(data)
484
483
 
485
- df = pd.concat(infos)
486
- #Return: player data
487
- return df
484
+ if infos:
485
+ df = pd.concat(infos)
486
+
487
+ #Return: player data
488
+ return df
489
+ else:
490
+ return pd.DataFrame()
488
491
 
489
492
  def nhl_scrape_draft_rankings(arg = 'now', category = ''):
490
493
  #Given url argument for timeframe and prospect category, return draft rankings
@@ -885,18 +888,8 @@ def nhl_calculate_stats(pbp,type,season_types,game_strength,split_game=False,ros
885
888
  except KeyError:
886
889
  pbp = wsba_xG(pbp)
887
890
 
888
- #Filter by season types, remove shootouts, remove shots with no coordinates, and remove shots on empty nets
889
- pbp_noshot = pbp.loc[(pbp['season_type'].isin(season_types)) & ~(pbp['event_type'].isin(fenwick_events))]
890
-
891
- #Include everything when strengths is set to 'all'
892
- if game_strength == 'all':
893
- mask = ((pbp['event_type'].isin(fenwick_events)) & (pbp['empty_net']<1))
894
- else:
895
- mask = ((pbp['event_type'].isin(fenwick_events)) & (pbp['empty_net']<1) & (pbp['x'].notna()) & (pbp['y'].notna()))
896
-
897
- pbp_shot = pbp.loc[(pbp['season_type'].isin(season_types)) & mask]
898
-
899
- pbp = pd.concat([pbp_shot,pbp_noshot])
891
+ #Apply season_type filter
892
+ pbp = pbp.loc[(pbp['season_type'].isin(season_types))]
900
893
 
901
894
  #Convert all columns with player ids to float in order to avoid merging errors
902
895
  for col in get_col():
@@ -1210,7 +1203,7 @@ def repo_load_pbp(seasons = []):
1210
1203
 
1211
1204
  #Add parquet to total
1212
1205
  print(f'Loading play-by-play from the following seasons: {seasons}...')
1213
- dfs = [pd.read_parquet(f"https://f005.backblazeb2.com/file/weakside-breakout/pbp/{season}.parquet") for season in seasons]
1206
+ dfs = [pd.read_parquet(f"https://weakside-breakout.s3.us-east-2.amazonaws.com/pbp/{season}.parquet") for season in seasons]
1214
1207
 
1215
1208
  return pd.concat(dfs)
1216
1209
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wsba_hockey
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: WeakSide Breakout's complete Python package of access to hockey data, primairly including the scraping of National Hockey League schedule, play-by-play, and shifts information.
5
5
  Author-email: Owen Singh <owenbksingh@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/owensingh38/wsba_hockey/
@@ -1,7 +1,20 @@
1
1
  wsba_hockey/__init__.py,sha256=yfr8z5PA503iaIQv30ngancwT_WnsuK-tZETKlHcI0M,377
2
- wsba_hockey/data_pipelines.py,sha256=tqAnUL8xf-K2jYG1zeuau5GzRlgTiaHkXwER481Jkb4,7952
3
- wsba_hockey/workspace.py,sha256=B--BrXrY5BL3AXQYnsy4pHQGv2LG_82VtqZT0ANab30,1088
4
- wsba_hockey/wsba_main.py,sha256=NPZvcL3VTi3pMq2jd9rlgyfVgXetrwSpRJehbvldWr8,54247
2
+ wsba_hockey/data_pipelines.py,sha256=wjyxw1ikv4N3kmfTuHxdHj7_W5bz0wAM-DIgxvJVg2s,10852
3
+ wsba_hockey/workspace.py,sha256=ECScWNqkmx8SDoxTtZYZtM1K1_oX-wQSXXsb_1Bjca4,2014
4
+ wsba_hockey/wsba_main.py,sha256=c8KEXBpjHHEadMMStMRXsNYqbvtoYdZbmOl8_FBmHQg,53761
5
+ wsba_hockey/api/api/index.py,sha256=EgGd5B1382b5OMFkDvKdiodByIaDQY6-mYm58774Y0Y,5924
6
+ wsba_hockey/api/api/main.py,sha256=QaObP7kSFr-zjJL549sSWv7LtqHq5mBqvpyjjs2nVn8,87
7
+ wsba_hockey/api/api/wsba_main.py,sha256=zrQFsy-AqW1GENm-gOm9VCZNrK4TAINHRadbPi156k8,53757
8
+ wsba_hockey/api/api/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ wsba_hockey/api/api/tools/agg.py,sha256=SYZDykUQFygDfRaazlBMKcfs_1eYP7GTJNpUnNKHH-8,21409
10
+ wsba_hockey/api/api/tools/plotting.py,sha256=3x59nyqMV6pIoL548m17dfDbc39ZkMTj4EjEdJVG4uo,6017
11
+ wsba_hockey/api/api/tools/scraping.py,sha256=i6Br0XCwq4EL1Nq9z9sTzISGQ6kPZ5EYnASVJMD9lyk,45645
12
+ wsba_hockey/api/api/tools/xg_model.py,sha256=nOr_2RBijLgPmJ0TTs4wbSsORYmRqWCKRjLKDm7sAhI,18342
13
+ wsba_hockey/api/api/tools/archive/old_scraping.py,sha256=hEjMI1RtfeZnf0RBiJFI38oXkLZ3WofeH5xqcF4pzgM,49585
14
+ wsba_hockey/api/api/tools/utils/__init__.py,sha256=vccXhOtzARoR99fmEWU1OEI3qCIdQ9Z42AlRA_BUhrs,114
15
+ wsba_hockey/api/api/tools/utils/config.py,sha256=D3Uk05-YTyrhfReMTTLfNI3HN_rON2uo_CDE9oER3Lg,351
16
+ wsba_hockey/api/api/tools/utils/save_pages.py,sha256=CsyL_0n-b-4pJoUauwU3HpnCO6n69-RlBMJQBd_qGDc,4979
17
+ wsba_hockey/api/api/tools/utils/shared.py,sha256=dH_JwZfia5fib8rksy5sW-mBp0pluBPvw37Vdr8Kap0,14211
5
18
  wsba_hockey/evidence/weakside-breakout/node_modules/duckdb/vendor.py,sha256=lmu0TB0rIYkAuV9-csFJgW-1hJojso_-EZpEoorUUKM,4949
6
19
  wsba_hockey/evidence/weakside-breakout/node_modules/flatted/python/flatted.py,sha256=ke8FuEflns-WlphCcQ9CC0qJqWqX3zEEuak74o6rgE8,3879
7
20
  wsba_hockey/evidence/weakside-breakout/node_modules/flatted/python/test.py,sha256=uTOn6HJd7KeY_PTRvvufv60dmvON3KWp3nnqACj8IlA,2129
@@ -104,32 +117,39 @@ wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gy
104
117
  wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/pretty_sln.py,sha256=b_Fxm-SXUCPL3Tix4EyNwZNmQ-zkeRIFFmuL0R5wFhw,5482
105
118
  wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/gyp/tools/pretty_vcproj.py,sha256=AwQrxK1F-jhjsbbT35XQjrvWNbc3IBFaKXoJogqMh_o,10633
106
119
  wsba_hockey/evidence/weakside-breakout/node_modules/sqlite3/node_modules/node-gyp/test/fixtures/test-charmap.py,sha256=5raXzaQnO2eJnrlFtlDtWftryhZX7Fj0amFW3hdSnhE,547
107
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/app.py,sha256=ay8NzjfC_2bZ38tOlXKDNpF35_KPGsl6Adx13ls6yWg,12619
120
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/app.py,sha256=P3vDaWyuij-DXlwk6XJ0aiAjrfs-NHi5hmngk-c9r8U,12704
108
121
  wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/game_stats/name_fix.py,sha256=v7IN4JWrudeFuIsBdjLLlHsr9wU65jYi9-34pI_ZpoM,1488
109
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/app.py,sha256=53jaUv8LOerKHWD-0d-EoxogEb83YFVm6KiRdbSLlVA,4254
110
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/plot.py,sha256=nTVk41u8l6M3czVeyJ-3edmiIAzXY-By7meGfl5C_kE,4505
111
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/rink_plot.py,sha256=bvU7enxqxGwo2QThNabSo1qB2vltS_0HjF95ZOYZ98I,11993
112
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/app.py,sha256=IKMn92hgEodX58op8lz_mtUWXevQ9OE0EHOlA0sDZ3c,6133
113
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/plot.py,sha256=nMAoLutqQeMYXy1HfwNI1joHcn7FNTL-v9TRMnGpfbg,3792
114
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/rink_plot.py,sha256=bvU7enxqxGwo2QThNabSo1qB2vltS_0HjF95ZOYZ98I,11993
115
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/app.py,sha256=nEveNuGE4Qk-K_PkQzPE0QbNAaA60cxvUPEQzNDdewQ,13189
116
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/plot.py,sha256=8glQwxuRSmFrpMqSi_6JDOpKsX53L0-IQNXUiufROyE,2355
117
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/rink_plot.py,sha256=bvU7enxqxGwo2QThNabSo1qB2vltS_0HjF95ZOYZ98I,11993
118
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/app.py,sha256=wNArrigZpFVDLE331MiQ_GS0qSLdhmim52WanLNFnJU,4307
119
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/plot.py,sha256=xDHLM_jcXSi5NBcQ7tWeF9yzcjNiOrHYD7C8sBfynFo,1860
120
- wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/rink_plot.py,sha256=bvU7enxqxGwo2QThNabSo1qB2vltS_0HjF95ZOYZ98I,11993
122
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/app.py,sha256=f5uwSvNKbtud91e7B17zC0HbPnYIvUgnShhdJZCOtkE,3820
123
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/plot.py,sha256=GFp2FalFg2P3kGR88mZdJY_6tJblvEl5ZLtyuKJU-Oo,3149
124
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/goalie/rink_plot.py,sha256=bvU7enxqxGwo2QThNabSo1qB2vltS_0HjF95ZOYZ98I,11993
125
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/app.py,sha256=XDoZ6-uTgT1vCAOjJ8KjW3r7T0eNDfShGScDxY95HyU,4255
126
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/plot.py,sha256=QERvb9I_OJPhrz84Omsf9Mq6JjaECark_ql1KfWha-I,4648
127
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/heatmaps/rink_plot.py,sha256=RB_csrnTxlFR4OyFEhZXbHDSR3dP-KgME0xGBR2JE-4,11994
128
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/app.py,sha256=JFWhfsyMhCnvLVyIfYzgZE2xT8_mnsOVWVXNi2EF9rM,6136
129
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/plot.py,sha256=t3XRT88L0lh5jE-PSSboLZZ-Yg_u8L4gzJh-egWiRcY,3935
130
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/matchups/rink_plot.py,sha256=RB_csrnTxlFR4OyFEhZXbHDSR3dP-KgME0xGBR2JE-4,11994
131
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/app.py,sha256=oH_Su2Z9iCfwsgW2aINzzxlo1fCq6dnEkTMbngjw9vg,13811
132
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/plot.py,sha256=zfUTSic2M1AfXufvLFDTxxvpqQXw0RzwwQTGxALjjSw,2779
133
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/pbp/rink_plot.py,sha256=RB_csrnTxlFR4OyFEhZXbHDSR3dP-KgME0xGBR2JE-4,11994
134
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/app.py,sha256=fNGWRHgqZr2G_QgACzh1l93ob-ugik1F9Wnq-sowWKU,4308
135
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/plot.py,sha256=qLxANHYsdPCevTGDbvDDzx4R3B8Mqm1XAs4XuuEWbfk,1852
136
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/skater/rink_plot.py,sha256=RB_csrnTxlFR4OyFEhZXbHDSR3dP-KgME0xGBR2JE-4,11994
137
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/app.py,sha256=qqTs9uFnTS5JD5ibCKFQZOI2lFBKIlcyuf17BUV1USQ,3912
138
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/plot.py,sha256=rTIqkSIPrh818xDxm3iOTkRat0SAgdi_vKuIFFPEJkI,4559
139
+ wsba_hockey/evidence/weakside-breakout/wsba_nhl_apps/wsba_nhl_apps/team_heatmaps/rink_plot.py,sha256=RB_csrnTxlFR4OyFEhZXbHDSR3dP-KgME0xGBR2JE-4,11994
140
+ wsba_hockey/flask/app.py,sha256=J51iA65h9xyJfLgdH0h2sVSbfIR7xgGd2Oy8bJsmpAk,1873
121
141
  wsba_hockey/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
142
  wsba_hockey/tools/agg.py,sha256=SYZDykUQFygDfRaazlBMKcfs_1eYP7GTJNpUnNKHH-8,21409
123
- wsba_hockey/tools/plotting.py,sha256=5yo8_c3_aZEjyDD-BJmsYLNhm5WWATO1mzi23zJLh5Y,6018
124
- wsba_hockey/tools/scraping.py,sha256=rmypleknqfS9gU-JuB5VQn5yDVwnNastoDL3YDeUCVk,45261
125
- wsba_hockey/tools/xg_model.py,sha256=nOr_2RBijLgPmJ0TTs4wbSsORYmRqWCKRjLKDm7sAhI,18342
143
+ wsba_hockey/tools/plotting.py,sha256=3x59nyqMV6pIoL548m17dfDbc39ZkMTj4EjEdJVG4uo,6017
144
+ wsba_hockey/tools/scraping.py,sha256=i6Br0XCwq4EL1Nq9z9sTzISGQ6kPZ5EYnASVJMD9lyk,45645
145
+ wsba_hockey/tools/xg_model.py,sha256=t9ETZ8H8VlOiniVscuPTs8b0iXGJS_Ds63FJ1nehlYM,18319
126
146
  wsba_hockey/tools/archive/old_scraping.py,sha256=hEjMI1RtfeZnf0RBiJFI38oXkLZ3WofeH5xqcF4pzgM,49585
127
147
  wsba_hockey/tools/utils/__init__.py,sha256=vccXhOtzARoR99fmEWU1OEI3qCIdQ9Z42AlRA_BUhrs,114
128
148
  wsba_hockey/tools/utils/config.py,sha256=D3Uk05-YTyrhfReMTTLfNI3HN_rON2uo_CDE9oER3Lg,351
129
149
  wsba_hockey/tools/utils/save_pages.py,sha256=CsyL_0n-b-4pJoUauwU3HpnCO6n69-RlBMJQBd_qGDc,4979
130
150
  wsba_hockey/tools/utils/shared.py,sha256=dH_JwZfia5fib8rksy5sW-mBp0pluBPvw37Vdr8Kap0,14211
131
- wsba_hockey-1.1.0.dist-info/licenses/LICENSE,sha256=Nr_Um1Pd5FQJTWWgm7maZArdtYMbDhzXYSwyJIZDGik,1114
132
- wsba_hockey-1.1.0.dist-info/METADATA,sha256=SzFmMpNRLdSaYZZySLr9-etSjC0zIf2Qs8jpRT-anTg,3542
133
- wsba_hockey-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
134
- wsba_hockey-1.1.0.dist-info/top_level.txt,sha256=acU7s3x-RZC1zGiqCOmO0g267iqCg34lzIfdmYxxGmQ,12
135
- wsba_hockey-1.1.0.dist-info/RECORD,,
151
+ wsba_hockey-1.1.1.dist-info/licenses/LICENSE,sha256=Nr_Um1Pd5FQJTWWgm7maZArdtYMbDhzXYSwyJIZDGik,1114
152
+ wsba_hockey-1.1.1.dist-info/METADATA,sha256=Xb5o3LMbRGCzB1soAZWpFQNIhk_0GnG9e9EQXqqFB-Y,3542
153
+ wsba_hockey-1.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
154
+ wsba_hockey-1.1.1.dist-info/top_level.txt,sha256=acU7s3x-RZC1zGiqCOmO0g267iqCg34lzIfdmYxxGmQ,12
155
+ wsba_hockey-1.1.1.dist-info/RECORD,,