hockey-blast-common-lib 0.1.63__py3-none-any.whl → 0.1.65__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 (27) hide show
  1. hockey_blast_common_lib/aggregate_all_stats.py +7 -4
  2. hockey_blast_common_lib/aggregate_goalie_stats.py +301 -107
  3. hockey_blast_common_lib/aggregate_h2h_stats.py +64 -33
  4. hockey_blast_common_lib/aggregate_human_stats.py +565 -280
  5. hockey_blast_common_lib/aggregate_referee_stats.py +286 -135
  6. hockey_blast_common_lib/aggregate_s2s_stats.py +85 -25
  7. hockey_blast_common_lib/aggregate_scorekeeper_stats.py +228 -113
  8. hockey_blast_common_lib/aggregate_skater_stats.py +561 -238
  9. hockey_blast_common_lib/assign_skater_skill.py +21 -11
  10. hockey_blast_common_lib/db_connection.py +59 -8
  11. hockey_blast_common_lib/embedding_utils.py +309 -0
  12. hockey_blast_common_lib/h2h_models.py +150 -56
  13. hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz +0 -0
  14. hockey_blast_common_lib/models.py +305 -150
  15. hockey_blast_common_lib/options.py +30 -15
  16. hockey_blast_common_lib/progress_utils.py +21 -13
  17. hockey_blast_common_lib/skills_in_divisions.py +170 -33
  18. hockey_blast_common_lib/skills_propagation.py +164 -70
  19. hockey_blast_common_lib/stats_models.py +489 -245
  20. hockey_blast_common_lib/stats_utils.py +6 -3
  21. hockey_blast_common_lib/utils.py +91 -25
  22. hockey_blast_common_lib/wsgi.py +7 -5
  23. {hockey_blast_common_lib-0.1.63.dist-info → hockey_blast_common_lib-0.1.65.dist-info}/METADATA +1 -1
  24. hockey_blast_common_lib-0.1.65.dist-info/RECORD +29 -0
  25. hockey_blast_common_lib-0.1.63.dist-info/RECORD +0 -28
  26. {hockey_blast_common_lib-0.1.63.dist-info → hockey_blast_common_lib-0.1.65.dist-info}/WHEEL +0 -0
  27. {hockey_blast_common_lib-0.1.63.dist-info → hockey_blast_common_lib-0.1.65.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  import argparse
2
+
2
3
  from .models import Human
3
4
 
4
5
  MAX_HUMAN_SEARCH_RESULTS = 25
@@ -7,38 +8,52 @@ MIN_GAMES_FOR_ORG_STATS = 1
7
8
  MIN_GAMES_FOR_DIVISION_STATS = 1
8
9
  MIN_GAMES_FOR_LEVEL_STATS = 1
9
10
 
10
- orgs = {'caha', 'sharksice', 'tvice'}
11
+ orgs = {"caha", "sharksice", "tvice"}
12
+
11
13
 
12
14
  def parse_args():
13
- parser = argparse.ArgumentParser(description="Process data for a specific organization.")
14
- parser.add_argument("org", choices=orgs, help="The organization to process (e.g., 'caha', 'sharksice', 'tvice').")
15
- parser.add_argument("--reprocess", action="store_true", help="Reprocess existing data.")
16
- parser.add_argument("--pre_process", action="store_true", help="Pre-Process existing data.")
15
+ parser = argparse.ArgumentParser(
16
+ description="Process data for a specific organization."
17
+ )
18
+ parser.add_argument(
19
+ "org",
20
+ choices=orgs,
21
+ help="The organization to process (e.g., 'caha', 'sharksice', 'tvice').",
22
+ )
23
+ parser.add_argument(
24
+ "--reprocess", action="store_true", help="Reprocess existing data."
25
+ )
26
+ parser.add_argument(
27
+ "--pre_process", action="store_true", help="Pre-Process existing data."
28
+ )
17
29
  return parser.parse_args()
18
30
 
31
+
19
32
  def get_or_create_empty_net_human(session, game_date):
20
33
  """Get or create the special 'Empty Net' human record for tracking pulled goalies.
21
-
34
+
22
35
  Args:
23
36
  session: Database session
24
37
  game_date: Date of the game (used for first_date/last_date if creating)
25
-
38
+
26
39
  Returns:
27
40
  int: The human_id of the Empty Net special record
28
41
  """
29
- empty_net_human = session.query(Human).filter_by(
30
- first_name="Empty", middle_name="", last_name="Net"
31
- ).first()
32
-
42
+ empty_net_human = (
43
+ session.query(Human)
44
+ .filter_by(first_name="Empty", middle_name="", last_name="Net")
45
+ .first()
46
+ )
47
+
33
48
  if not empty_net_human:
34
49
  empty_net_human = Human(
35
- first_name="Empty",
36
- middle_name="",
50
+ first_name="Empty",
51
+ middle_name="",
37
52
  last_name="Net",
38
53
  first_date=game_date,
39
- last_date=game_date
54
+ last_date=game_date,
40
55
  )
41
56
  session.add(empty_net_human)
42
57
  session.commit()
43
-
58
+
44
59
  return empty_net_human.id
@@ -1,5 +1,5 @@
1
1
  import time
2
- from datetime import datetime, timedelta
2
+
3
3
 
4
4
  class ProgressTracker:
5
5
  """
@@ -14,7 +14,7 @@ class ProgressTracker:
14
14
  self.processed_items = 0
15
15
  self.last_update_time = self.start_time
16
16
  self.custom_counters = custom_counters if custom_counters else {}
17
-
17
+
18
18
  def update(self, processed_count=None, **kwargs):
19
19
  """
20
20
  Update progress. If processed_count is None, increment by 1.
@@ -33,23 +33,26 @@ class ProgressTracker:
33
33
  current_time = time.time()
34
34
 
35
35
  # Only update display if at least 0.1 seconds have passed (to avoid spamming)
36
- if current_time - self.last_update_time >= 0.1 or self.processed_items == self.total_items:
36
+ if (
37
+ current_time - self.last_update_time >= 0.1
38
+ or self.processed_items == self.total_items
39
+ ):
37
40
  self._display_progress()
38
41
  self.last_update_time = current_time
39
-
42
+
40
43
  def _display_progress(self):
41
44
  """
42
45
  Display progress with percentage, ETA, and elapsed time.
43
46
  """
44
47
  current_time = time.time()
45
48
  elapsed_time = current_time - self.start_time
46
-
49
+
47
50
  if self.processed_items == 0:
48
51
  percentage = 0.0
49
52
  eta_str = "calculating..."
50
53
  else:
51
54
  percentage = (self.processed_items / self.total_items) * 100
52
-
55
+
53
56
  # Calculate ETA
54
57
  if self.processed_items == self.total_items:
55
58
  eta_str = "completed!"
@@ -58,7 +61,7 @@ class ProgressTracker:
58
61
  remaining_items = self.total_items - self.processed_items
59
62
  eta_seconds = avg_time_per_item * remaining_items
60
63
  eta_str = self._format_time(eta_seconds)
61
-
64
+
62
65
  elapsed_str = self._format_time(elapsed_time)
63
66
 
64
67
  progress_msg = f"\r{self.description}: {self.processed_items}/{self.total_items} ({percentage:.1f}%) | "
@@ -66,15 +69,17 @@ class ProgressTracker:
66
69
 
67
70
  # Add custom counters to the display
68
71
  if self.custom_counters:
69
- counter_parts = [f"{key}: {value}" for key, value in self.custom_counters.items()]
72
+ counter_parts = [
73
+ f"{key}: {value}" for key, value in self.custom_counters.items()
74
+ ]
70
75
  progress_msg += " | " + ", ".join(counter_parts)
71
76
 
72
77
  print(progress_msg, end="", flush=True)
73
-
78
+
74
79
  # Add newline when complete
75
80
  if self.processed_items == self.total_items:
76
81
  print() # Newline to finish the progress line
77
-
82
+
78
83
  def _format_time(self, seconds):
79
84
  """
80
85
  Format seconds into a human-readable string.
@@ -89,7 +94,7 @@ class ProgressTracker:
89
94
  hours = int(seconds // 3600)
90
95
  minutes = int((seconds % 3600) // 60)
91
96
  return f"{hours}h {minutes}m"
92
-
97
+
93
98
  def finish(self):
94
99
  """
95
100
  Mark progress as complete and add final newline.
@@ -97,9 +102,12 @@ class ProgressTracker:
97
102
  self.processed_items = self.total_items
98
103
  self._display_progress()
99
104
 
100
- def create_progress_tracker(total_items, description="Processing", custom_counters=None):
105
+
106
+ def create_progress_tracker(
107
+ total_items, description="Processing", custom_counters=None
108
+ ):
101
109
  """
102
110
  Factory function to create a progress tracker.
103
111
  custom_counters: dict of counter_name: initial_value for additional metrics to track
104
112
  """
105
- return ProgressTracker(total_items, description, custom_counters)
113
+ return ProgressTracker(total_items, description, custom_counters)
@@ -1,20 +1,31 @@
1
- import sys
2
1
  import os
2
+ import sys
3
3
  from collections import defaultdict
4
4
 
5
5
  # Add the project root directory to the Python path
6
6
  sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
7
7
 
8
- from hockey_blast_common_lib.models import Game, Division, Level, Season, League
9
8
  from hockey_blast_common_lib.db_connection import create_session
9
+ from hockey_blast_common_lib.models import Division, Game, League, Level, Season
10
10
  from hockey_blast_common_lib.utils import get_fake_level
11
11
 
12
+
12
13
  def analyze_levels(org):
13
14
  session = create_session(org)
14
15
 
15
16
  # Query to get games and their divisions for season_number 33 and 35 with league_number 1
16
- games_season_33 = session.query(Game.home_team_id, Game.visitor_team_id, Division.level).join(Division, Game.division_id == Division.id).filter(Division.season_number == 33, Division.league_number == 1).all()
17
- games_season_35 = session.query(Game.home_team_id, Game.visitor_team_id, Division.level).join(Division, Game.division_id == Division.id).filter(Division.season_number == 35, Division.league_number == 1).all()
17
+ games_season_33 = (
18
+ session.query(Game.home_team_id, Game.visitor_team_id, Division.level)
19
+ .join(Division, Game.division_id == Division.id)
20
+ .filter(Division.season_number == 33, Division.league_number == 1)
21
+ .all()
22
+ )
23
+ games_season_35 = (
24
+ session.query(Game.home_team_id, Game.visitor_team_id, Division.level)
25
+ .join(Division, Game.division_id == Division.id)
26
+ .filter(Division.season_number == 35, Division.league_number == 1)
27
+ .all()
28
+ )
18
29
 
19
30
  # Dictionary to store levels for each team by season
20
31
  team_levels_season_33 = defaultdict(set)
@@ -43,32 +54,131 @@ def analyze_levels(org):
43
54
  for new_level in sorted(level_connections.keys()):
44
55
  connections = level_connections[new_level]
45
56
  connections_list = sorted(connections.items(), key=lambda x: x[0])
46
- connections_str = ", ".join([f"{old_level}: {count}" for old_level, count in connections_list])
57
+ connections_str = ", ".join(
58
+ [f"{old_level}: {count}" for old_level, count in connections_list]
59
+ )
47
60
  print(f"{new_level}: {connections_str}")
48
61
 
49
62
  session.close()
50
63
 
64
+
51
65
  def fill_seed_levels():
52
66
  session = create_session("boss")
53
67
 
54
68
  # List of Skill objects based on the provided comments
55
69
  levels = [
56
- Level(is_seed=True, org_id=1, skill_value=10.0, level_name='Adult Division 1', level_alternative_name='Senior A'),
57
- Level(is_seed=True, org_id=1, skill_value=20.0, level_name='Adult Division 2', level_alternative_name='Senior B'),
58
- Level(is_seed=True, org_id=1, skill_value=30.0, level_name='Adult Division 3A', level_alternative_name='Senior BB'),
59
- Level(is_seed=True, org_id=1, skill_value=35.0, level_name='Adult Division 3B', level_alternative_name='Senior C'),
60
- Level(is_seed=True, org_id=1, skill_value=40.0, level_name='Adult Division 4A', level_alternative_name='Senior CC'),
61
- Level(is_seed=True, org_id=1, skill_value=45.0, level_name='Adult Division 4B', level_alternative_name='Senior CCC,Senior CCCC'),
62
- Level(is_seed=True, org_id=1, skill_value=50.0, level_name='Adult Division 5A', level_alternative_name='Senior D,Senior DD'),
63
- Level(is_seed=True, org_id=1, skill_value=55.0, level_name='Adult Division 5B', level_alternative_name='Senior DDD'),
64
- Level(is_seed=True, org_id=1, skill_value=60.0, level_name='Adult Division 6A', level_alternative_name='Senior DDDD'),
65
- Level(is_seed=True, org_id=1, skill_value=65.0, level_name='Adult Division 6B', level_alternative_name='Senior DDDDD'),
66
- Level(is_seed=True, org_id=1, skill_value=70.0, level_name='Adult Division 7A', level_alternative_name='Senior E'),
67
- Level(is_seed=True, org_id=1, skill_value=75.0, level_name='Adult Division 7B', level_alternative_name='Senior EE'),
68
- Level(is_seed=True, org_id=1, skill_value=80.0, level_name='Adult Division 8', level_alternative_name='Senior EEE'),
69
- Level(is_seed=True, org_id=1, skill_value=80.0, level_name='Adult Division 8A', level_alternative_name='Senior EEE'),
70
- Level(is_seed=True, org_id=1, skill_value=85.0, level_name='Adult Division 8B', level_alternative_name='Senior EEEE'),
71
- Level(is_seed=True, org_id=1, skill_value=90.0, level_name='Adult Division 9', level_alternative_name='Senior EEEEE')
70
+ Level(
71
+ is_seed=True,
72
+ org_id=1,
73
+ skill_value=10.0,
74
+ level_name="Adult Division 1",
75
+ level_alternative_name="Senior A",
76
+ ),
77
+ Level(
78
+ is_seed=True,
79
+ org_id=1,
80
+ skill_value=20.0,
81
+ level_name="Adult Division 2",
82
+ level_alternative_name="Senior B",
83
+ ),
84
+ Level(
85
+ is_seed=True,
86
+ org_id=1,
87
+ skill_value=30.0,
88
+ level_name="Adult Division 3A",
89
+ level_alternative_name="Senior BB",
90
+ ),
91
+ Level(
92
+ is_seed=True,
93
+ org_id=1,
94
+ skill_value=35.0,
95
+ level_name="Adult Division 3B",
96
+ level_alternative_name="Senior C",
97
+ ),
98
+ Level(
99
+ is_seed=True,
100
+ org_id=1,
101
+ skill_value=40.0,
102
+ level_name="Adult Division 4A",
103
+ level_alternative_name="Senior CC",
104
+ ),
105
+ Level(
106
+ is_seed=True,
107
+ org_id=1,
108
+ skill_value=45.0,
109
+ level_name="Adult Division 4B",
110
+ level_alternative_name="Senior CCC,Senior CCCC",
111
+ ),
112
+ Level(
113
+ is_seed=True,
114
+ org_id=1,
115
+ skill_value=50.0,
116
+ level_name="Adult Division 5A",
117
+ level_alternative_name="Senior D,Senior DD",
118
+ ),
119
+ Level(
120
+ is_seed=True,
121
+ org_id=1,
122
+ skill_value=55.0,
123
+ level_name="Adult Division 5B",
124
+ level_alternative_name="Senior DDD",
125
+ ),
126
+ Level(
127
+ is_seed=True,
128
+ org_id=1,
129
+ skill_value=60.0,
130
+ level_name="Adult Division 6A",
131
+ level_alternative_name="Senior DDDD",
132
+ ),
133
+ Level(
134
+ is_seed=True,
135
+ org_id=1,
136
+ skill_value=65.0,
137
+ level_name="Adult Division 6B",
138
+ level_alternative_name="Senior DDDDD",
139
+ ),
140
+ Level(
141
+ is_seed=True,
142
+ org_id=1,
143
+ skill_value=70.0,
144
+ level_name="Adult Division 7A",
145
+ level_alternative_name="Senior E",
146
+ ),
147
+ Level(
148
+ is_seed=True,
149
+ org_id=1,
150
+ skill_value=75.0,
151
+ level_name="Adult Division 7B",
152
+ level_alternative_name="Senior EE",
153
+ ),
154
+ Level(
155
+ is_seed=True,
156
+ org_id=1,
157
+ skill_value=80.0,
158
+ level_name="Adult Division 8",
159
+ level_alternative_name="Senior EEE",
160
+ ),
161
+ Level(
162
+ is_seed=True,
163
+ org_id=1,
164
+ skill_value=80.0,
165
+ level_name="Adult Division 8A",
166
+ level_alternative_name="Senior EEE",
167
+ ),
168
+ Level(
169
+ is_seed=True,
170
+ org_id=1,
171
+ skill_value=85.0,
172
+ level_name="Adult Division 8B",
173
+ level_alternative_name="Senior EEEE",
174
+ ),
175
+ Level(
176
+ is_seed=True,
177
+ org_id=1,
178
+ skill_value=90.0,
179
+ level_name="Adult Division 9",
180
+ level_alternative_name="Senior EEEEE",
181
+ ),
72
182
  ]
73
183
 
74
184
  for skill in levels:
@@ -77,6 +187,7 @@ def fill_seed_levels():
77
187
 
78
188
  print("Seed skills have been populated into the database.")
79
189
 
190
+
80
191
  def assign_fake_level_to_divisions(session, fake_level):
81
192
  # Assign the special fake Skill to every existing Division
82
193
  divisions = session.query(Division).all()
@@ -85,45 +196,71 @@ def assign_fake_level_to_divisions(session, fake_level):
85
196
  session.commit()
86
197
  print("Assigned special fake Skill to all Division records.")
87
198
 
199
+
88
200
  def delete_all_levels():
89
201
  session = create_session("boss")
90
202
  fake_level = get_fake_level(session)
91
203
  assign_fake_level_to_divisions(session, fake_level)
92
204
  # Delete all Skill records except the fake skill
93
- session.query(Level).filter(Level.id != fake_level.id).delete(synchronize_session=False)
205
+ session.query(Level).filter(Level.id != fake_level.id).delete(
206
+ synchronize_session=False
207
+ )
94
208
  session.commit()
95
209
  print("All Skill records except the fake skill have been deleted.")
96
210
 
211
+
97
212
  def populate_season_ids():
98
213
  session = create_session("boss")
99
214
  divisions = session.query(Division).all()
100
215
  for division in divisions:
101
216
  # Find the Season record that matches the season_number
102
- season = session.query(Season).filter_by(season_number=division.season_number, org_id=division.org_id, league_number=division.league_number).first()
217
+ season = (
218
+ session.query(Season)
219
+ .filter_by(
220
+ season_number=division.season_number,
221
+ org_id=division.org_id,
222
+ league_number=division.league_number,
223
+ )
224
+ .first()
225
+ )
103
226
  if season:
104
227
  division.season_id = season.id
105
- print(f"Assigned season_id {season.id} for Division with season_number {division.season_number}")
228
+ print(
229
+ f"Assigned season_id {season.id} for Division with season_number {division.season_number}"
230
+ )
106
231
  else:
107
- print(f"Season not found for Division with season_number {division.season_number}")
232
+ print(
233
+ f"Season not found for Division with season_number {division.season_number}"
234
+ )
108
235
  session.commit()
109
236
  print("Season IDs have been populated into the Division table.")
110
237
 
238
+
111
239
  def populate_league_ids():
112
240
  session = create_session("boss")
113
241
  seasons = session.query(Season).all()
114
242
  for season in seasons:
115
243
  # Find the League record that matches the league_number and org_id
116
- league = session.query(League).filter_by(league_number=season.league_number, org_id=season.org_id).first()
244
+ league = (
245
+ session.query(League)
246
+ .filter_by(league_number=season.league_number, org_id=season.org_id)
247
+ .first()
248
+ )
117
249
  if league:
118
250
  season.league_id = league.id
119
- print(f"Assigned league_id {league.id} for Season with league_number {season.league_number}")
251
+ print(
252
+ f"Assigned league_id {league.id} for Season with league_number {season.league_number}"
253
+ )
120
254
  else:
121
- print(f"League not found for Season with league_number {season.league_number}")
255
+ print(
256
+ f"League not found for Season with league_number {season.league_number}"
257
+ )
122
258
  session.commit()
123
259
  print("League IDs have been populated into the Season table.")
124
260
 
125
- #if __name__ == "__main__":
126
- # delete_all_levels()
127
- #fill_seed_levels()
128
- #populate_season_ids() # Call the function to populate season_ids
129
- #populate_league_ids() # Call the new function to populate league_ids
261
+
262
+ # if __name__ == "__main__":
263
+ # delete_all_levels()
264
+ # fill_seed_levels()
265
+ # populate_season_ids() # Call the function to populate season_ids
266
+ # populate_league_ids() # Call the new function to populate league_ids