hockey-blast-common-lib 0.1.62__py3-none-any.whl → 0.1.64__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 +303 -113
  3. hockey_blast_common_lib/aggregate_h2h_stats.py +64 -33
  4. hockey_blast_common_lib/aggregate_human_stats.py +566 -281
  5. hockey_blast_common_lib/aggregate_referee_stats.py +287 -145
  6. hockey_blast_common_lib/aggregate_s2s_stats.py +85 -25
  7. hockey_blast_common_lib/aggregate_scorekeeper_stats.py +231 -119
  8. hockey_blast_common_lib/aggregate_skater_stats.py +595 -240
  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 -149
  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 +89 -25
  22. hockey_blast_common_lib/wsgi.py +7 -5
  23. {hockey_blast_common_lib-0.1.62.dist-info → hockey_blast_common_lib-0.1.64.dist-info}/METADATA +1 -1
  24. hockey_blast_common_lib-0.1.64.dist-info/RECORD +29 -0
  25. hockey_blast_common_lib-0.1.62.dist-info/RECORD +0 -28
  26. {hockey_blast_common_lib-0.1.62.dist-info → hockey_blast_common_lib-0.1.64.dist-info}/WHEEL +0 -0
  27. {hockey_blast_common_lib-0.1.62.dist-info → hockey_blast_common_lib-0.1.64.dist-info}/top_level.txt +0 -0
@@ -1,48 +1,66 @@
1
- import sys, os
1
+ import os
2
+ import sys
2
3
  from datetime import datetime
3
4
 
4
5
  # Add the package directory to the Python path
5
6
  sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
6
7
 
7
- from hockey_blast_common_lib.models import Game, GameRoster, Goal, Penalty
8
- from hockey_blast_common_lib.h2h_models import H2HStats, H2HStatsMeta
8
+ from sqlalchemy import types
9
+ from sqlalchemy.sql import func
10
+
9
11
  from hockey_blast_common_lib.db_connection import create_session
12
+ from hockey_blast_common_lib.h2h_models import H2HStats, H2HStatsMeta
13
+ from hockey_blast_common_lib.models import Game, GameRoster, Goal, Penalty
10
14
  from hockey_blast_common_lib.progress_utils import create_progress_tracker
11
- from sqlalchemy.sql import func
12
- from sqlalchemy import types
13
15
 
14
16
  # Max games to process (set to None to process all)
15
17
  MAX_GAMES_TO_PROCESS = None # Set to None to process all games
16
18
 
19
+
17
20
  def aggregate_h2h_stats():
18
21
  session = create_session("boss")
19
- meta = None #session.query(H2HStatsMeta).order_by(H2HStatsMeta.id.desc()).first()
22
+ meta = None # session.query(H2HStatsMeta).order_by(H2HStatsMeta.id.desc()).first()
20
23
  h2h_stats_dict = {} # (h1, h2) -> H2HStats instance
21
- if meta is None or meta.last_run_timestamp is None or meta.last_processed_game_id is None:
24
+ if (
25
+ meta is None
26
+ or meta.last_run_timestamp is None
27
+ or meta.last_processed_game_id is None
28
+ ):
22
29
  # Full run: delete all existing stats and process all games
23
30
  session.query(H2HStats).delete()
24
31
  session.commit()
25
32
  games_query = session.query(Game).order_by(Game.date, Game.time, Game.id)
26
- print("No previous run found, deleted all existing H2H stats, processing all games...")
33
+ print(
34
+ "No previous run found, deleted all existing H2H stats, processing all games..."
35
+ )
27
36
  else:
28
37
  # Incremental: only process games after last processed
29
38
  # Load all existing stats into memory
30
39
  for stat in session.query(H2HStats).all():
31
40
  h2h_stats_dict[(stat.human1_id, stat.human2_id)] = stat
32
- last_game = session.query(Game).filter(Game.id == meta.last_processed_game_id).first()
41
+ last_game = (
42
+ session.query(Game).filter(Game.id == meta.last_processed_game_id).first()
43
+ )
33
44
  if last_game:
34
45
  last_dt = datetime.combine(last_game.date, last_game.time)
35
- games_query = session.query(Game).filter(
36
- func.cast(func.concat(Game.date, ' ', Game.time), types.TIMESTAMP()) > last_dt
37
- ).order_by(Game.date, Game.time, Game.id)
38
- print(f"Resuming from game after id {meta.last_processed_game_id} ({last_dt})...")
46
+ games_query = (
47
+ session.query(Game)
48
+ .filter(
49
+ func.cast(func.concat(Game.date, " ", Game.time), types.TIMESTAMP())
50
+ > last_dt
51
+ )
52
+ .order_by(Game.date, Game.time, Game.id)
53
+ )
54
+ print(
55
+ f"Resuming from game after id {meta.last_processed_game_id} ({last_dt})..."
56
+ )
39
57
  else:
40
58
  games_query = session.query(Game).order_by(Game.date, Game.time, Game.id)
41
59
  print("Previous game id not found, processing all games...")
42
60
 
43
61
  total_games = games_query.count()
44
62
  print(f"Total games to process: {total_games}")
45
-
63
+
46
64
  # Create progress tracker
47
65
  progress = create_progress_tracker(total_games, "Processing H2H stats")
48
66
  processed = 0
@@ -64,21 +82,21 @@ def aggregate_h2h_stats():
64
82
  # Add goalies from Game table (home/visitor)
65
83
  if game.home_goalie_id:
66
84
  all_humans.add(game.home_goalie_id)
67
- human_roles.setdefault(game.home_goalie_id, set()).add('G')
85
+ human_roles.setdefault(game.home_goalie_id, set()).add("G")
68
86
  if game.visitor_goalie_id:
69
87
  all_humans.add(game.visitor_goalie_id)
70
- human_roles.setdefault(game.visitor_goalie_id, set()).add('G')
88
+ human_roles.setdefault(game.visitor_goalie_id, set()).add("G")
71
89
  # Add referees from Game table (NOT from roster!)
72
90
  if game.referee_1_id:
73
91
  all_humans.add(game.referee_1_id)
74
- human_roles.setdefault(game.referee_1_id, set()).add('R')
92
+ human_roles.setdefault(game.referee_1_id, set()).add("R")
75
93
  if game.referee_2_id:
76
94
  all_humans.add(game.referee_2_id)
77
- human_roles.setdefault(game.referee_2_id, set()).add('R')
95
+ human_roles.setdefault(game.referee_2_id, set()).add("R")
78
96
  # --- Build all pairs of humans in this game ---
79
97
  all_humans = list(all_humans)
80
98
  for i in range(len(all_humans)):
81
- for j in range(i+1, len(all_humans)):
99
+ for j in range(i + 1, len(all_humans)):
82
100
  h1, h2 = sorted([all_humans[i], all_humans[j]])
83
101
  key = (h1, h2)
84
102
  h2h = h2h_stats_dict.get(key)
@@ -128,7 +146,7 @@ def aggregate_h2h_stats():
128
146
  h1_shootout_attempts_vs_h2_goalie=0,
129
147
  h1_shootout_goals_vs_h2_goalie=0,
130
148
  h2_shootout_attempts_vs_h1_goalie=0,
131
- h2_shootout_goals_vs_h1_goalie=0
149
+ h2_shootout_goals_vs_h1_goalie=0,
132
150
  )
133
151
  h2h_stats_dict[key] = h2h
134
152
  # Update first/last game ids
@@ -163,34 +181,40 @@ def aggregate_h2h_stats():
163
181
  if _is_tie(game):
164
182
  h2h.games_tied_against += 1
165
183
  # --- Role-specific stats ---
166
- if 'G' in h1_roles:
184
+ if "G" in h1_roles:
167
185
  h2h.games_h1_goalie += 1
168
- if 'G' in h2_roles:
186
+ if "G" in h2_roles:
169
187
  h2h.games_h2_goalie += 1
170
- if 'R' in h1_roles:
188
+ if "R" in h1_roles:
171
189
  h2h.games_h1_ref += 1
172
- if 'R' in h2_roles:
190
+ if "R" in h2_roles:
173
191
  h2h.games_h2_ref += 1
174
- if 'R' in h1_roles and 'R' in h2_roles:
192
+ if "R" in h1_roles and "R" in h2_roles:
175
193
  h2h.games_both_referees += 1
176
194
  # --- Goals, assists, penalties ---
177
195
  # Goals
178
196
  goals = session.query(Goal).filter(Goal.game_id == game.id).all()
179
197
  for goal in goals:
180
- if goal.goal_scorer_id == h1 and (goal.assist_1_id == h2 or goal.assist_2_id == h2):
198
+ if goal.goal_scorer_id == h1 and (
199
+ goal.assist_1_id == h2 or goal.assist_2_id == h2
200
+ ):
181
201
  h2h.goals_h1_when_together += 1
182
- if goal.goal_scorer_id == h2 and (goal.assist_1_id == h1 or goal.assist_2_id == h1):
202
+ if goal.goal_scorer_id == h2 and (
203
+ goal.assist_1_id == h1 or goal.assist_2_id == h1
204
+ ):
183
205
  h2h.goals_h2_when_together += 1
184
206
  # Penalties
185
- penalties = session.query(Penalty).filter(Penalty.game_id == game.id).all()
207
+ penalties = (
208
+ session.query(Penalty).filter(Penalty.game_id == game.id).all()
209
+ )
186
210
  for pen in penalties:
187
211
  if pen.penalized_player_id == h1:
188
212
  h2h.penalties_h1_when_together += 1
189
- if pen.penalty_minutes and 'GM' in pen.penalty_minutes:
213
+ if pen.penalty_minutes and "GM" in pen.penalty_minutes:
190
214
  h2h.gm_penalties_h1_when_together += 1
191
215
  if pen.penalized_player_id == h2:
192
216
  h2h.penalties_h2_when_together += 1
193
- if pen.penalty_minutes and 'GM' in pen.penalty_minutes:
217
+ if pen.penalty_minutes and "GM" in pen.penalty_minutes:
194
218
  h2h.gm_penalties_h2_when_together += 1
195
219
  # --- TODO: Add more detailed logic for goalie/skater, referee/player, shootouts, etc. ---
196
220
  latest_game_id = game.id
@@ -202,13 +226,13 @@ def aggregate_h2h_stats():
202
226
  session.commit()
203
227
  # Save/update meta
204
228
  meta = H2HStatsMeta(
205
- last_run_timestamp=datetime.utcnow(),
206
- last_processed_game_id=latest_game_id
229
+ last_run_timestamp=datetime.utcnow(), last_processed_game_id=latest_game_id
207
230
  )
208
231
  session.add(meta)
209
232
  session.commit()
210
233
  print("H2H aggregation complete.")
211
234
 
235
+
212
236
  # --- Helper functions for win/loss/tie ---
213
237
  def _is_win(game, team_id):
214
238
  if team_id == game.home_team_id:
@@ -217,6 +241,7 @@ def _is_win(game, team_id):
217
241
  return (game.visitor_final_score or 0) > (game.home_final_score or 0)
218
242
  return False
219
243
 
244
+
220
245
  def _is_loss(game, team_id):
221
246
  if team_id == game.home_team_id:
222
247
  return (game.home_final_score or 0) < (game.visitor_final_score or 0)
@@ -224,8 +249,14 @@ def _is_loss(game, team_id):
224
249
  return (game.visitor_final_score or 0) < (game.home_final_score or 0)
225
250
  return False
226
251
 
252
+
227
253
  def _is_tie(game):
228
- return (game.home_final_score is not None and game.visitor_final_score is not None and game.home_final_score == game.visitor_final_score)
254
+ return (
255
+ game.home_final_score is not None
256
+ and game.visitor_final_score is not None
257
+ and game.home_final_score == game.visitor_final_score
258
+ )
259
+
229
260
 
230
261
  if __name__ == "__main__":
231
262
  aggregate_h2h_stats()