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,22 +1,46 @@
1
- import sys, os
1
+ import os
2
+ import sys
2
3
 
3
4
  # Add the package directory to the Python path
4
5
  sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
5
6
 
6
- from datetime import datetime, timedelta
7
+
7
8
  import sqlalchemy
9
+ from sqlalchemy import and_, case, func
10
+ from sqlalchemy.sql import case, func
8
11
 
9
- from hockey_blast_common_lib.models import Game, Goal, Penalty, GameRoster, Organization, Division, Human, Level
10
- from hockey_blast_common_lib.stats_models import OrgStatsSkater, DivisionStatsSkater, OrgStatsWeeklySkater, OrgStatsDailySkater, DivisionStatsWeeklySkater, DivisionStatsDailySkater, LevelStatsSkater
11
12
  from hockey_blast_common_lib.db_connection import create_session
12
- from sqlalchemy.sql import func, case
13
- from hockey_blast_common_lib.options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, MIN_GAMES_FOR_LEVEL_STATS
14
- from hockey_blast_common_lib.utils import get_org_id_from_alias, get_non_human_ids, get_division_ids_for_last_season_in_all_leagues, get_all_division_ids_for_org
15
- from hockey_blast_common_lib.utils import get_start_datetime
16
- from sqlalchemy import func, case, and_
17
- from collections import defaultdict
18
- from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
13
+ from hockey_blast_common_lib.models import (
14
+ Division,
15
+ Game,
16
+ GameRoster,
17
+ Goal,
18
+ Human,
19
+ Level,
20
+ Organization,
21
+ Penalty,
22
+ )
23
+ from hockey_blast_common_lib.options import (
24
+ MIN_GAMES_FOR_DIVISION_STATS,
25
+ MIN_GAMES_FOR_LEVEL_STATS,
26
+ MIN_GAMES_FOR_ORG_STATS,
27
+ )
19
28
  from hockey_blast_common_lib.progress_utils import create_progress_tracker
29
+ from hockey_blast_common_lib.stats_models import (
30
+ DivisionStatsDailySkater,
31
+ DivisionStatsSkater,
32
+ DivisionStatsWeeklySkater,
33
+ LevelStatsSkater,
34
+ OrgStatsDailySkater,
35
+ OrgStatsSkater,
36
+ OrgStatsWeeklySkater,
37
+ )
38
+ from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
39
+ from hockey_blast_common_lib.utils import (
40
+ get_all_division_ids_for_org,
41
+ get_non_human_ids,
42
+ get_start_datetime,
43
+ )
20
44
 
21
45
  # Import status constants for game filtering
22
46
  FINAL_STATUS = "Final"
@@ -24,94 +48,134 @@ FINAL_SO_STATUS = "Final(SO)"
24
48
  FORFEIT_STATUS = "FORFEIT"
25
49
  NOEVENTS_STATUS = "NOEVENTS"
26
50
 
51
+
27
52
  def calculate_current_point_streak(session, human_id, filter_condition):
28
53
  """
29
54
  Calculate the current point streak for a player.
30
55
  A point streak is consecutive games (from the most recent game backward) where the player had at least one point.
31
56
  Returns a tuple: (streak_length, average_points_during_streak)
57
+
58
+ Optimized to use CASE statements for conditional aggregation in a single query.
32
59
  """
33
- # Get all games for this player ordered by date/time descending (most recent first)
34
- games = session.query(Game).join(GameRoster, Game.id == GameRoster.game_id).filter(
35
- GameRoster.human_id == human_id,
36
- ~GameRoster.role.ilike('g'), # Exclude goalie games
37
- filter_condition,
38
- Game.status.like('Final%') # Only final games
39
- ).order_by(Game.date.desc(), Game.time.desc()).all()
40
-
41
- if not games:
60
+ # Get all games with their point totals in ONE query using CASE for conditional counting
61
+ game_points = (
62
+ session.query(
63
+ Game.id,
64
+ Game.date,
65
+ Game.time,
66
+ func.sum(case((Goal.goal_scorer_id == human_id, 1), else_=0)).label(
67
+ "goals"
68
+ ),
69
+ func.sum(
70
+ case(
71
+ (
72
+ (Goal.assist_1_id == human_id) | (Goal.assist_2_id == human_id),
73
+ 1,
74
+ ),
75
+ else_=0,
76
+ )
77
+ ).label("assists"),
78
+ )
79
+ .join(GameRoster, Game.id == GameRoster.game_id)
80
+ .outerjoin(Goal, Game.id == Goal.game_id)
81
+ .filter(
82
+ GameRoster.human_id == human_id,
83
+ ~GameRoster.role.ilike("g"), # Exclude goalie games
84
+ filter_condition,
85
+ (Game.status.like("Final%"))
86
+ | (Game.status == "NOEVENTS"), # Include Final and NOEVENTS games
87
+ )
88
+ .group_by(Game.id, Game.date, Game.time)
89
+ .order_by(Game.date.desc(), Game.time.desc())
90
+ .all()
91
+ )
92
+
93
+ if not game_points:
42
94
  return 0, 0.0
43
-
95
+
44
96
  current_streak = 0
45
97
  total_points_in_streak = 0
46
-
47
- for game in games:
48
- # Check if the player had any points in this game
49
- goals = session.query(Goal).filter(
50
- Goal.game_id == game.id,
51
- Goal.goal_scorer_id == human_id
52
- ).count()
53
-
54
- assists = session.query(Goal).filter(
55
- Goal.game_id == game.id,
56
- ((Goal.assist_1_id == human_id) | (Goal.assist_2_id == human_id))
57
- ).count()
58
-
59
- total_points = goals + assists
60
-
98
+
99
+ # Iterate through games from most recent to oldest
100
+ for game in game_points:
101
+ total_points = (game.goals or 0) + (game.assists or 0)
102
+
61
103
  if total_points > 0:
62
104
  current_streak += 1
63
105
  total_points_in_streak += total_points
64
106
  else:
65
107
  # Streak is broken, stop counting
66
108
  break
67
-
109
+
68
110
  # Calculate average points during streak
69
- avg_points_during_streak = total_points_in_streak / current_streak if current_streak > 0 else 0.0
70
-
111
+ avg_points_during_streak = (
112
+ total_points_in_streak / current_streak if current_streak > 0 else 0.0
113
+ )
114
+
71
115
  return current_streak, avg_points_during_streak
72
116
 
73
- def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_human_id=None, aggregation_window=None):
117
+
118
+ def aggregate_skater_stats(
119
+ session,
120
+ aggregation_type,
121
+ aggregation_id,
122
+ debug_human_id=None,
123
+ aggregation_window=None,
124
+ ):
74
125
  human_ids_to_filter = get_non_human_ids(session)
75
126
 
76
127
  # Get the name of the aggregation, for debug purposes
77
- if aggregation_type == 'org':
128
+ if aggregation_type == "org":
78
129
  if aggregation_id == ALL_ORGS_ID:
79
130
  aggregation_name = "All Orgs"
80
131
  filter_condition = sqlalchemy.true() # No filter for organization
81
132
  else:
82
- aggregation_name = session.query(Organization).filter(Organization.id == aggregation_id).first().organization_name
133
+ aggregation_name = (
134
+ session.query(Organization)
135
+ .filter(Organization.id == aggregation_id)
136
+ .first()
137
+ .organization_name
138
+ )
83
139
  filter_condition = Game.org_id == aggregation_id
84
- print(f"Aggregating skater stats for {aggregation_name} with window {aggregation_window}...")
140
+ print(
141
+ f"Aggregating skater stats for {aggregation_name} with window {aggregation_window}..."
142
+ )
85
143
 
86
- elif aggregation_type == 'division':
87
- aggregation_name = session.query(Division).filter(Division.id == aggregation_id).first().level
88
- elif aggregation_type == 'level':
89
- aggregation_name = session.query(Level).filter(Level.id == aggregation_id).first().level_name
144
+ elif aggregation_type == "division":
145
+ aggregation_name = (
146
+ session.query(Division).filter(Division.id == aggregation_id).first().level
147
+ )
148
+ elif aggregation_type == "level":
149
+ aggregation_name = (
150
+ session.query(Level).filter(Level.id == aggregation_id).first().level_name
151
+ )
90
152
  else:
91
153
  aggregation_name = "Unknown"
92
154
 
93
- if aggregation_type == 'org':
94
- if aggregation_window == 'Daily':
155
+ if aggregation_type == "org":
156
+ if aggregation_window == "Daily":
95
157
  StatsModel = OrgStatsDailySkater
96
- elif aggregation_window == 'Weekly':
158
+ elif aggregation_window == "Weekly":
97
159
  StatsModel = OrgStatsWeeklySkater
98
160
  else:
99
161
  StatsModel = OrgStatsSkater
100
162
  min_games = MIN_GAMES_FOR_ORG_STATS
101
- elif aggregation_type == 'division':
102
- if aggregation_window == 'Daily':
163
+ elif aggregation_type == "division":
164
+ if aggregation_window == "Daily":
103
165
  StatsModel = DivisionStatsDailySkater
104
- elif aggregation_window == 'Weekly':
166
+ elif aggregation_window == "Weekly":
105
167
  StatsModel = DivisionStatsWeeklySkater
106
168
  else:
107
169
  StatsModel = DivisionStatsSkater
108
170
  min_games = MIN_GAMES_FOR_DIVISION_STATS
109
171
  filter_condition = Game.division_id == aggregation_id
110
- elif aggregation_type == 'level':
172
+ elif aggregation_type == "level":
111
173
  StatsModel = LevelStatsSkater
112
174
  min_games = MIN_GAMES_FOR_LEVEL_STATS
113
175
  # Get division IDs for this level to avoid cartesian product
114
- division_ids = session.query(Division.id).filter(Division.level_id == aggregation_id).all()
176
+ division_ids = (
177
+ session.query(Division.id).filter(Division.level_id == aggregation_id).all()
178
+ )
115
179
  division_ids = [div_id[0] for div_id in division_ids]
116
180
  if not division_ids:
117
181
  return # No divisions for this level
@@ -124,95 +188,186 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
124
188
  raise ValueError("Invalid aggregation type")
125
189
 
126
190
  # Delete existing items from the stats table
127
- session.query(StatsModel).filter(StatsModel.aggregation_id == aggregation_id).delete()
191
+ session.query(StatsModel).filter(
192
+ StatsModel.aggregation_id == aggregation_id
193
+ ).delete()
128
194
  session.commit()
129
195
 
130
196
  # Apply aggregation window filter
131
197
  if aggregation_window:
132
- last_game_datetime_str = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, Game.status.like('Final%')).scalar()
198
+ last_game_datetime_str = (
199
+ session.query(func.max(func.concat(Game.date, " ", Game.time)))
200
+ .filter(
201
+ filter_condition,
202
+ (Game.status.like("Final%")) | (Game.status == "NOEVENTS"),
203
+ )
204
+ .scalar()
205
+ )
133
206
  start_datetime = get_start_datetime(last_game_datetime_str, aggregation_window)
134
207
  if start_datetime:
135
- game_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP).between(start_datetime, last_game_datetime_str)
208
+ game_window_filter = func.cast(
209
+ func.concat(Game.date, " ", Game.time), sqlalchemy.types.TIMESTAMP
210
+ ).between(start_datetime, last_game_datetime_str)
136
211
  filter_condition = filter_condition & game_window_filter
137
212
  else:
138
- #print(f"Warning: No valid start datetime for aggregation window '{aggregation_window}' for {aggregation_name}. No games will be included.")
213
+ # print(f"Warning: No valid start datetime for aggregation window '{aggregation_window}' for {aggregation_name}. No games will be included.")
139
214
  return
140
-
215
+
141
216
  # Filter for specific human_id if provided
142
217
  human_filter = []
143
218
  # if debug_human_id:
144
219
  # human_filter = [GameRoster.human_id == debug_human_id]
145
220
 
146
221
  # Aggregate games played for each human in each division, excluding goalies
147
- # games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
148
- # games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
149
- games_played_query = session.query(
150
- GameRoster.human_id,
151
- func.count(Game.id).label('games_played'), # DEPRECATED - will be replaced by games_participated
152
- func.sum(case(
153
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
154
- else_=0
155
- )).label('games_participated'),
156
- func.sum(case(
157
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
158
- else_=0
159
- )).label('games_with_stats'),
160
- func.array_agg(Game.id).label('game_ids')
161
- ).join(Game, Game.id == GameRoster.game_id)
222
+ # Filter games by status upfront for performance (avoid CASE statements on 200K+ rows)
223
+ # Only count games with these statuses: FINAL, FINAL_SO, FORFEIT, NOEVENTS
224
+ games_played_query = (
225
+ session.query(
226
+ GameRoster.human_id,
227
+ func.count(Game.id).label("games_played"),
228
+ func.count(Game.id).label(
229
+ "games_participated"
230
+ ), # Same as games_played after filtering
231
+ func.count(Game.id).label(
232
+ "games_with_stats"
233
+ ), # Same as games_played after filtering
234
+ func.array_agg(Game.id).label("game_ids"),
235
+ )
236
+ .join(Game, Game.id == GameRoster.game_id)
237
+ .filter(
238
+ Game.status.in_(
239
+ [FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]
240
+ )
241
+ )
242
+ )
162
243
 
163
244
  # Only join Division if not level aggregation (since we filter on Game.division_id directly for levels)
164
- if aggregation_type != 'level':
165
- games_played_query = games_played_query.join(Division, Game.division_id == Division.id)
245
+ if aggregation_type != "level":
246
+ games_played_query = games_played_query.join(
247
+ Division, Game.division_id == Division.id
248
+ )
166
249
 
167
- games_played_stats = games_played_query.filter(filter_condition, ~GameRoster.role.ilike('g'), *human_filter).group_by(GameRoster.human_id).all()
250
+ games_played_stats = (
251
+ games_played_query.filter(
252
+ filter_condition, ~GameRoster.role.ilike("g"), *human_filter
253
+ )
254
+ .group_by(GameRoster.human_id)
255
+ .all()
256
+ )
168
257
 
169
258
  # Aggregate goals for each human in each division, excluding goalies
170
- goals_query = session.query(
171
- Goal.goal_scorer_id.label('human_id'),
172
- func.count(Goal.id).label('goals'),
173
- func.array_agg(Goal.game_id).label('goal_game_ids')
174
- ).join(Game, Game.id == Goal.game_id).join(GameRoster, and_(Game.id == GameRoster.game_id, Goal.goal_scorer_id == GameRoster.human_id))
175
-
176
- if aggregation_type != 'level':
259
+ goals_query = (
260
+ session.query(
261
+ Goal.goal_scorer_id.label("human_id"),
262
+ func.count(Goal.id).label("goals"),
263
+ func.array_agg(Goal.game_id).label("goal_game_ids"),
264
+ )
265
+ .join(Game, Game.id == Goal.game_id)
266
+ .join(
267
+ GameRoster,
268
+ and_(
269
+ Game.id == GameRoster.game_id,
270
+ Goal.goal_scorer_id == GameRoster.human_id,
271
+ ),
272
+ )
273
+ )
274
+
275
+ if aggregation_type != "level":
177
276
  goals_query = goals_query.join(Division, Game.division_id == Division.id)
178
-
179
- goals_stats = goals_query.filter(filter_condition, ~GameRoster.role.ilike('g'), *human_filter).group_by(Goal.goal_scorer_id).all()
277
+
278
+ goals_stats = (
279
+ goals_query.filter(filter_condition, ~GameRoster.role.ilike("g"), *human_filter)
280
+ .group_by(Goal.goal_scorer_id)
281
+ .all()
282
+ )
180
283
 
181
284
  # Aggregate assists for each human in each division, excluding goalies
182
- assists_query = session.query(
183
- Goal.assist_1_id.label('human_id'),
184
- func.count(Goal.id).label('assists'),
185
- func.array_agg(Goal.game_id).label('assist_game_ids')
186
- ).join(Game, Game.id == Goal.game_id).join(GameRoster, and_(Game.id == GameRoster.game_id, Goal.assist_1_id == GameRoster.human_id))
187
-
188
- if aggregation_type != 'level':
285
+ assists_query = (
286
+ session.query(
287
+ Goal.assist_1_id.label("human_id"),
288
+ func.count(Goal.id).label("assists"),
289
+ func.array_agg(Goal.game_id).label("assist_game_ids"),
290
+ )
291
+ .join(Game, Game.id == Goal.game_id)
292
+ .join(
293
+ GameRoster,
294
+ and_(
295
+ Game.id == GameRoster.game_id, Goal.assist_1_id == GameRoster.human_id
296
+ ),
297
+ )
298
+ )
299
+
300
+ if aggregation_type != "level":
189
301
  assists_query = assists_query.join(Division, Game.division_id == Division.id)
190
-
191
- assists_stats = assists_query.filter(filter_condition, ~GameRoster.role.ilike('g'), *human_filter).group_by(Goal.assist_1_id).all()
192
-
193
- assists_query_2 = session.query(
194
- Goal.assist_2_id.label('human_id'),
195
- func.count(Goal.id).label('assists'),
196
- func.array_agg(Goal.game_id).label('assist_2_game_ids')
197
- ).join(Game, Game.id == Goal.game_id).join(GameRoster, and_(Game.id == GameRoster.game_id, Goal.assist_2_id == GameRoster.human_id))
198
-
199
- if aggregation_type != 'level':
200
- assists_query_2 = assists_query_2.join(Division, Game.division_id == Division.id)
201
-
202
- assists_stats_2 = assists_query_2.filter(filter_condition, ~GameRoster.role.ilike('g'), *human_filter).group_by(Goal.assist_2_id).all()
302
+
303
+ assists_stats = (
304
+ assists_query.filter(
305
+ filter_condition, ~GameRoster.role.ilike("g"), *human_filter
306
+ )
307
+ .group_by(Goal.assist_1_id)
308
+ .all()
309
+ )
310
+
311
+ assists_query_2 = (
312
+ session.query(
313
+ Goal.assist_2_id.label("human_id"),
314
+ func.count(Goal.id).label("assists"),
315
+ func.array_agg(Goal.game_id).label("assist_2_game_ids"),
316
+ )
317
+ .join(Game, Game.id == Goal.game_id)
318
+ .join(
319
+ GameRoster,
320
+ and_(
321
+ Game.id == GameRoster.game_id, Goal.assist_2_id == GameRoster.human_id
322
+ ),
323
+ )
324
+ )
325
+
326
+ if aggregation_type != "level":
327
+ assists_query_2 = assists_query_2.join(
328
+ Division, Game.division_id == Division.id
329
+ )
330
+
331
+ assists_stats_2 = (
332
+ assists_query_2.filter(
333
+ filter_condition, ~GameRoster.role.ilike("g"), *human_filter
334
+ )
335
+ .group_by(Goal.assist_2_id)
336
+ .all()
337
+ )
203
338
 
204
339
  # Aggregate penalties for each human in each division, excluding goalies
205
- penalties_query = session.query(
206
- Penalty.penalized_player_id.label('human_id'),
207
- func.count(Penalty.id).label('penalties'),
208
- func.sum(case((Penalty.penalty_minutes == 'GM', 1), else_=0)).label('gm_penalties'), # New aggregation for GM penalties
209
- func.array_agg(Penalty.game_id).label('penalty_game_ids')
210
- ).join(Game, Game.id == Penalty.game_id).join(GameRoster, and_(Game.id == GameRoster.game_id, Penalty.penalized_player_id == GameRoster.human_id))
211
-
212
- if aggregation_type != 'level':
213
- penalties_query = penalties_query.join(Division, Game.division_id == Division.id)
214
-
215
- penalties_stats = penalties_query.filter(filter_condition, ~GameRoster.role.ilike('g'), *human_filter).group_by(Penalty.penalized_player_id).all()
340
+ penalties_query = (
341
+ session.query(
342
+ Penalty.penalized_player_id.label("human_id"),
343
+ func.count(Penalty.id).label("penalties"),
344
+ func.sum(case((Penalty.penalty_minutes == "GM", 1), else_=0)).label(
345
+ "gm_penalties"
346
+ ), # New aggregation for GM penalties
347
+ func.array_agg(Penalty.game_id).label("penalty_game_ids"),
348
+ )
349
+ .join(Game, Game.id == Penalty.game_id)
350
+ .join(
351
+ GameRoster,
352
+ and_(
353
+ Game.id == GameRoster.game_id,
354
+ Penalty.penalized_player_id == GameRoster.human_id,
355
+ ),
356
+ )
357
+ )
358
+
359
+ if aggregation_type != "level":
360
+ penalties_query = penalties_query.join(
361
+ Division, Game.division_id == Division.id
362
+ )
363
+
364
+ penalties_stats = (
365
+ penalties_query.filter(
366
+ filter_condition, ~GameRoster.role.ilike("g"), *human_filter
367
+ )
368
+ .group_by(Penalty.penalized_player_id)
369
+ .all()
370
+ )
216
371
 
217
372
  # Combine the results
218
373
  stats_dict = {}
@@ -222,118 +377,198 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
222
377
  key = (aggregation_id, stat.human_id)
223
378
  if key not in stats_dict:
224
379
  stats_dict[key] = {
225
- 'games_played': 0, # DEPRECATED - for backward compatibility
226
- 'games_participated': 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
227
- 'games_with_stats': 0, # Games with full stats: FINAL, FINAL_SO only
228
- 'goals': 0,
229
- 'assists': 0,
230
- 'penalties': 0,
231
- 'gm_penalties': 0, # Initialize GM penalties
232
- 'points': 0, # Initialize points
233
- 'goals_per_game': 0.0,
234
- 'points_per_game': 0.0,
235
- 'assists_per_game': 0.0,
236
- 'penalties_per_game': 0.0,
237
- 'gm_penalties_per_game': 0.0, # Initialize GM penalties per game
238
- 'current_point_streak': 0, # Initialize current point streak
239
- 'current_point_streak_avg_points': 0.0, # Initialize current point streak average points
240
- 'game_ids': [],
241
- 'first_game_id': None,
242
- 'last_game_id': None
380
+ "games_played": 0, # DEPRECATED - for backward compatibility
381
+ "games_participated": 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
382
+ "games_with_stats": 0, # Games with full stats: FINAL, FINAL_SO only
383
+ "goals": 0,
384
+ "assists": 0,
385
+ "penalties": 0,
386
+ "gm_penalties": 0, # Initialize GM penalties
387
+ "points": 0, # Initialize points
388
+ "goals_per_game": 0.0,
389
+ "points_per_game": 0.0,
390
+ "assists_per_game": 0.0,
391
+ "penalties_per_game": 0.0,
392
+ "gm_penalties_per_game": 0.0, # Initialize GM penalties per game
393
+ "current_point_streak": 0, # Initialize current point streak
394
+ "current_point_streak_avg_points": 0.0, # Initialize current point streak average points
395
+ "game_ids": [],
396
+ "first_game_id": None,
397
+ "last_game_id": None,
243
398
  }
244
- stats_dict[key]['games_played'] += stat.games_played
245
- stats_dict[key]['games_participated'] += stat.games_participated
246
- stats_dict[key]['games_with_stats'] += stat.games_with_stats
247
- stats_dict[key]['game_ids'].extend(stat.game_ids)
399
+ stats_dict[key]["games_played"] += stat.games_played
400
+ stats_dict[key]["games_participated"] += stat.games_participated
401
+ stats_dict[key]["games_with_stats"] += stat.games_with_stats
402
+ stats_dict[key]["game_ids"].extend(stat.game_ids)
248
403
 
249
404
  # Filter out entries with games_played less than min_games
250
- stats_dict = {key: value for key, value in stats_dict.items() if value['games_played'] >= min_games}
405
+ stats_dict = {
406
+ key: value
407
+ for key, value in stats_dict.items()
408
+ if value["games_played"] >= min_games
409
+ }
251
410
 
252
411
  for stat in goals_stats:
253
412
  key = (aggregation_id, stat.human_id)
254
413
  if key in stats_dict:
255
- stats_dict[key]['goals'] += stat.goals
256
- stats_dict[key]['points'] += stat.goals # Update points
414
+ stats_dict[key]["goals"] += stat.goals
415
+ stats_dict[key]["points"] += stat.goals # Update points
257
416
 
258
417
  for stat in assists_stats:
259
418
  key = (aggregation_id, stat.human_id)
260
419
  if key in stats_dict:
261
- stats_dict[key]['assists'] += stat.assists
262
- stats_dict[key]['points'] += stat.assists # Update points
420
+ stats_dict[key]["assists"] += stat.assists
421
+ stats_dict[key]["points"] += stat.assists # Update points
263
422
 
264
423
  for stat in assists_stats_2:
265
424
  key = (aggregation_id, stat.human_id)
266
425
  if key in stats_dict:
267
- stats_dict[key]['assists'] += stat.assists
268
- stats_dict[key]['points'] += stat.assists # Update points
426
+ stats_dict[key]["assists"] += stat.assists
427
+ stats_dict[key]["points"] += stat.assists # Update points
269
428
 
270
429
  for stat in penalties_stats:
271
430
  key = (aggregation_id, stat.human_id)
272
431
  if key in stats_dict:
273
- stats_dict[key]['penalties'] += stat.penalties
274
- stats_dict[key]['gm_penalties'] += stat.gm_penalties # Update GM penalties
432
+ stats_dict[key]["penalties"] += stat.penalties
433
+ stats_dict[key]["gm_penalties"] += stat.gm_penalties # Update GM penalties
275
434
 
276
435
  # Calculate per game stats (using games_with_stats as denominator for accuracy)
277
436
  for key, stat in stats_dict.items():
278
- if stat['games_with_stats'] > 0:
279
- stat['goals_per_game'] = stat['goals'] / stat['games_with_stats']
280
- stat['points_per_game'] = stat['points'] / stat['games_with_stats']
281
- stat['assists_per_game'] = stat['assists'] / stat['games_with_stats']
282
- stat['penalties_per_game'] = stat['penalties'] / stat['games_with_stats']
283
- stat['gm_penalties_per_game'] = stat['gm_penalties'] / stat['games_with_stats'] # Calculate GM penalties per game
437
+ if stat["games_with_stats"] > 0:
438
+ stat["goals_per_game"] = stat["goals"] / stat["games_with_stats"]
439
+ stat["points_per_game"] = stat["points"] / stat["games_with_stats"]
440
+ stat["assists_per_game"] = stat["assists"] / stat["games_with_stats"]
441
+ stat["penalties_per_game"] = stat["penalties"] / stat["games_with_stats"]
442
+ stat["gm_penalties_per_game"] = (
443
+ stat["gm_penalties"] / stat["games_with_stats"]
444
+ ) # Calculate GM penalties per game
284
445
 
285
446
  # Ensure all keys have valid human_id values
286
447
  stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
287
448
 
288
449
  # Populate first_game_id and last_game_id
289
- for key, stat in stats_dict.items():
290
- all_game_ids = stat['game_ids']
291
- if all_game_ids:
292
- first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
293
- last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
294
- stat['first_game_id'] = first_game.id if first_game else None
295
- stat['last_game_id'] = last_game.id if last_game else None
450
+ # Only show progress for "All Orgs" with no window (all-time stats) - the slowest case
451
+ total_players = len(stats_dict)
452
+ if (
453
+ aggregation_type == "org"
454
+ and aggregation_id == ALL_ORGS_ID
455
+ and aggregation_window is None
456
+ and total_players > 1000
457
+ ):
458
+ progress = create_progress_tracker(
459
+ total_players, f"Processing {total_players} players for {aggregation_name}"
460
+ )
461
+ for idx, (key, stat) in enumerate(stats_dict.items()):
462
+ all_game_ids = stat["game_ids"]
463
+ if all_game_ids:
464
+ first_game = (
465
+ session.query(Game)
466
+ .filter(Game.id.in_(all_game_ids))
467
+ .order_by(Game.date, Game.time)
468
+ .first()
469
+ )
470
+ last_game = (
471
+ session.query(Game)
472
+ .filter(Game.id.in_(all_game_ids))
473
+ .order_by(Game.date.desc(), Game.time.desc())
474
+ .first()
475
+ )
476
+ stat["first_game_id"] = first_game.id if first_game else None
477
+ stat["last_game_id"] = last_game.id if last_game else None
478
+ if (idx + 1) % 100 == 0 or (
479
+ idx + 1
480
+ ) == total_players: # Update every 100 players
481
+ progress.update(idx + 1)
482
+ else:
483
+ # No progress tracking for all other cases
484
+ for key, stat in stats_dict.items():
485
+ all_game_ids = stat["game_ids"]
486
+ if all_game_ids:
487
+ first_game = (
488
+ session.query(Game)
489
+ .filter(Game.id.in_(all_game_ids))
490
+ .order_by(Game.date, Game.time)
491
+ .first()
492
+ )
493
+ last_game = (
494
+ session.query(Game)
495
+ .filter(Game.id.in_(all_game_ids))
496
+ .order_by(Game.date.desc(), Game.time.desc())
497
+ .first()
498
+ )
499
+ stat["first_game_id"] = first_game.id if first_game else None
500
+ stat["last_game_id"] = last_game.id if last_game else None
296
501
 
297
502
  # Calculate current point streak (only for all-time stats)
298
503
  if aggregation_window is None:
299
- for key, stat in stats_dict.items():
300
- aggregation_id, human_id = key
301
- streak_length, avg_points = calculate_current_point_streak(session, human_id, filter_condition)
302
- stat['current_point_streak'] = streak_length
303
- stat['current_point_streak_avg_points'] = avg_points
504
+ total_players = len(stats_dict)
505
+ # Show progress for All Orgs - this is the slowest part
506
+ if (
507
+ aggregation_type == "org"
508
+ and aggregation_id == ALL_ORGS_ID
509
+ and total_players > 1000
510
+ ):
511
+ progress = create_progress_tracker(
512
+ total_players, f"Calculating point streaks for {total_players} players"
513
+ )
514
+ for idx, (key, stat) in enumerate(stats_dict.items()):
515
+ agg_id, human_id = key
516
+ streak_length, avg_points = calculate_current_point_streak(
517
+ session, human_id, filter_condition
518
+ )
519
+ stat["current_point_streak"] = streak_length
520
+ stat["current_point_streak_avg_points"] = avg_points
521
+ if (idx + 1) % 100 == 0 or (idx + 1) == total_players:
522
+ progress.update(idx + 1)
523
+ else:
524
+ for key, stat in stats_dict.items():
525
+ agg_id, human_id = key
526
+ streak_length, avg_points = calculate_current_point_streak(
527
+ session, human_id, filter_condition
528
+ )
529
+ stat["current_point_streak"] = streak_length
530
+ stat["current_point_streak_avg_points"] = avg_points
304
531
 
305
532
  # Calculate total_in_rank
306
533
  total_in_rank = len(stats_dict)
307
534
 
308
535
  # Assign ranks within each level
309
536
  def assign_ranks(stats_dict, field):
310
- sorted_stats = sorted(stats_dict.items(), key=lambda x: x[1][field], reverse=True)
537
+ sorted_stats = sorted(
538
+ stats_dict.items(), key=lambda x: x[1][field], reverse=True
539
+ )
311
540
  for rank, (key, stat) in enumerate(sorted_stats, start=1):
312
- stats_dict[key][f'{field}_rank'] = rank
313
-
314
- assign_ranks(stats_dict, 'games_played')
315
- assign_ranks(stats_dict, 'games_participated') # Rank by total participation
316
- assign_ranks(stats_dict, 'games_with_stats') # Rank by games with full stats
317
- assign_ranks(stats_dict, 'goals')
318
- assign_ranks(stats_dict, 'assists')
319
- assign_ranks(stats_dict, 'points')
320
- assign_ranks(stats_dict, 'penalties')
321
- assign_ranks(stats_dict, 'gm_penalties') # Assign ranks for GM penalties
322
- assign_ranks(stats_dict, 'goals_per_game')
323
- assign_ranks(stats_dict, 'points_per_game')
324
- assign_ranks(stats_dict, 'assists_per_game')
325
- assign_ranks(stats_dict, 'penalties_per_game')
326
- assign_ranks(stats_dict, 'gm_penalties_per_game') # Assign ranks for GM penalties per game
327
- if aggregation_window is None: # Only assign current_point_streak ranks for all-time stats
328
- assign_ranks(stats_dict, 'current_point_streak')
329
- assign_ranks(stats_dict, 'current_point_streak_avg_points')
541
+ stats_dict[key][f"{field}_rank"] = rank
542
+
543
+ assign_ranks(stats_dict, "games_played")
544
+ assign_ranks(stats_dict, "games_participated") # Rank by total participation
545
+ assign_ranks(stats_dict, "games_with_stats") # Rank by games with full stats
546
+ assign_ranks(stats_dict, "goals")
547
+ assign_ranks(stats_dict, "assists")
548
+ assign_ranks(stats_dict, "points")
549
+ assign_ranks(stats_dict, "penalties")
550
+ assign_ranks(stats_dict, "gm_penalties") # Assign ranks for GM penalties
551
+ assign_ranks(stats_dict, "goals_per_game")
552
+ assign_ranks(stats_dict, "points_per_game")
553
+ assign_ranks(stats_dict, "assists_per_game")
554
+ assign_ranks(stats_dict, "penalties_per_game")
555
+ assign_ranks(
556
+ stats_dict, "gm_penalties_per_game"
557
+ ) # Assign ranks for GM penalties per game
558
+ if (
559
+ aggregation_window is None
560
+ ): # Only assign current_point_streak ranks for all-time stats
561
+ assign_ranks(stats_dict, "current_point_streak")
562
+ assign_ranks(stats_dict, "current_point_streak_avg_points")
330
563
 
331
564
  # Debug output for specific human
332
565
  if debug_human_id:
333
566
  if any(key[1] == debug_human_id for key in stats_dict):
334
567
  human = session.query(Human).filter(Human.id == debug_human_id).first()
335
568
  human_name = f"{human.first_name} {human.last_name}" if human else "Unknown"
336
- print(f"For Human {debug_human_id} ({human_name}) for {aggregation_type} {aggregation_id} ({aggregation_name}) , total_in_rank {total_in_rank} and window {aggregation_window}:")
569
+ print(
570
+ f"For Human {debug_human_id} ({human_name}) for {aggregation_type} {aggregation_id} ({aggregation_name}) , total_in_rank {total_in_rank} and window {aggregation_window}:"
571
+ )
337
572
  for key, stat in stats_dict.items():
338
573
  if key[1] == debug_human_id:
339
574
  for k, v in stat.items():
@@ -344,47 +579,75 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
344
579
  batch_size = 1000
345
580
  for i, (key, stat) in enumerate(stats_dict.items(), 1):
346
581
  aggregation_id, human_id = key
347
- goals_per_game = stat['goals'] / stat['games_played'] if stat['games_played'] > 0 else 0.0
348
- points_per_game = (stat['goals'] + stat['assists']) / stat['games_played'] if stat['games_played'] > 0 else 0.0
349
- assists_per_game = stat['assists'] / stat['games_played'] if stat['games_played'] > 0 else 0.0
350
- penalties_per_game = stat['penalties'] / stat['games_played'] if stat['games_played'] > 0 else 0.0
351
- gm_penalties_per_game = stat['gm_penalties'] / stat['games_played'] if stat['games_played'] > 0 else 0.0 # Calculate GM penalties per game
582
+ goals_per_game = (
583
+ stat["goals"] / stat["games_played"] if stat["games_played"] > 0 else 0.0
584
+ )
585
+ points_per_game = (
586
+ (stat["goals"] + stat["assists"]) / stat["games_played"]
587
+ if stat["games_played"] > 0
588
+ else 0.0
589
+ )
590
+ assists_per_game = (
591
+ stat["assists"] / stat["games_played"] if stat["games_played"] > 0 else 0.0
592
+ )
593
+ penalties_per_game = (
594
+ stat["penalties"] / stat["games_played"]
595
+ if stat["games_played"] > 0
596
+ else 0.0
597
+ )
598
+ gm_penalties_per_game = (
599
+ stat["gm_penalties"] / stat["games_played"]
600
+ if stat["games_played"] > 0
601
+ else 0.0
602
+ ) # Calculate GM penalties per game
352
603
  skater_stat = StatsModel(
353
604
  aggregation_id=aggregation_id,
354
605
  human_id=human_id,
355
- games_played=stat['games_played'], # DEPRECATED - for backward compatibility
356
- games_participated=stat['games_participated'], # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
357
- games_participated_rank=stat['games_participated_rank'],
358
- games_with_stats=stat['games_with_stats'], # Games with full stats: FINAL, FINAL_SO only
359
- games_with_stats_rank=stat['games_with_stats_rank'],
360
- goals=stat['goals'],
361
- assists=stat['assists'],
362
- points=stat['goals'] + stat['assists'],
363
- penalties=stat['penalties'],
364
- gm_penalties=stat['gm_penalties'], # Include GM penalties
606
+ games_played=stat[
607
+ "games_played"
608
+ ], # DEPRECATED - for backward compatibility
609
+ games_participated=stat[
610
+ "games_participated"
611
+ ], # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
612
+ games_participated_rank=stat["games_participated_rank"],
613
+ games_with_stats=stat[
614
+ "games_with_stats"
615
+ ], # Games with full stats: FINAL, FINAL_SO only
616
+ games_with_stats_rank=stat["games_with_stats_rank"],
617
+ goals=stat["goals"],
618
+ assists=stat["assists"],
619
+ points=stat["goals"] + stat["assists"],
620
+ penalties=stat["penalties"],
621
+ gm_penalties=stat["gm_penalties"], # Include GM penalties
365
622
  goals_per_game=goals_per_game,
366
623
  points_per_game=points_per_game,
367
624
  assists_per_game=assists_per_game,
368
625
  penalties_per_game=penalties_per_game,
369
626
  gm_penalties_per_game=gm_penalties_per_game, # Include GM penalties per game
370
- games_played_rank=stat['games_played_rank'],
371
- goals_rank=stat['goals_rank'],
372
- assists_rank=stat['assists_rank'],
373
- points_rank=stat['points_rank'],
374
- penalties_rank=stat['penalties_rank'],
375
- gm_penalties_rank=stat['gm_penalties_rank'], # Include GM penalties rank
376
- goals_per_game_rank=stat['goals_per_game_rank'],
377
- points_per_game_rank=stat['points_per_game_rank'],
378
- assists_per_game_rank=stat['assists_per_game_rank'],
379
- penalties_per_game_rank=stat['penalties_per_game_rank'],
380
- gm_penalties_per_game_rank=stat['gm_penalties_per_game_rank'], # Include GM penalties per game rank
627
+ games_played_rank=stat["games_played_rank"],
628
+ goals_rank=stat["goals_rank"],
629
+ assists_rank=stat["assists_rank"],
630
+ points_rank=stat["points_rank"],
631
+ penalties_rank=stat["penalties_rank"],
632
+ gm_penalties_rank=stat["gm_penalties_rank"], # Include GM penalties rank
633
+ goals_per_game_rank=stat["goals_per_game_rank"],
634
+ points_per_game_rank=stat["points_per_game_rank"],
635
+ assists_per_game_rank=stat["assists_per_game_rank"],
636
+ penalties_per_game_rank=stat["penalties_per_game_rank"],
637
+ gm_penalties_per_game_rank=stat[
638
+ "gm_penalties_per_game_rank"
639
+ ], # Include GM penalties per game rank
381
640
  total_in_rank=total_in_rank,
382
- current_point_streak=stat.get('current_point_streak', 0),
383
- current_point_streak_rank=stat.get('current_point_streak_rank', 0),
384
- current_point_streak_avg_points=stat.get('current_point_streak_avg_points', 0.0),
385
- current_point_streak_avg_points_rank=stat.get('current_point_streak_avg_points_rank', 0),
386
- first_game_id=stat['first_game_id'],
387
- last_game_id=stat['last_game_id']
641
+ current_point_streak=stat.get("current_point_streak", 0),
642
+ current_point_streak_rank=stat.get("current_point_streak_rank", 0),
643
+ current_point_streak_avg_points=stat.get(
644
+ "current_point_streak_avg_points", 0.0
645
+ ),
646
+ current_point_streak_avg_points_rank=stat.get(
647
+ "current_point_streak_avg_points_rank", 0
648
+ ),
649
+ first_game_id=stat["first_game_id"],
650
+ last_game_id=stat["last_game_id"],
388
651
  )
389
652
  session.add(skater_stat)
390
653
  # Commit in batches
@@ -392,6 +655,7 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
392
655
  session.commit()
393
656
  session.commit()
394
657
 
658
+
395
659
  def run_aggregate_skater_stats():
396
660
  session = create_session("boss")
397
661
  human_id_to_debug = None
@@ -402,51 +666,142 @@ def run_aggregate_skater_stats():
402
666
 
403
667
  for org_id in org_ids:
404
668
  division_ids = get_all_division_ids_for_org(session, org_id)
405
- org_name = session.query(Organization.organization_name).filter(Organization.id == org_id).scalar() or f"org_id {org_id}"
406
-
669
+ org_name = (
670
+ session.query(Organization.organization_name)
671
+ .filter(Organization.id == org_id)
672
+ .scalar()
673
+ or f"org_id {org_id}"
674
+ )
675
+
407
676
  if human_id_to_debug is None and division_ids:
408
677
  # Process divisions with progress tracking
409
- progress = create_progress_tracker(len(division_ids), f"Processing {len(division_ids)} divisions for {org_name}")
678
+ progress = create_progress_tracker(
679
+ len(division_ids),
680
+ f"Processing {len(division_ids)} divisions for {org_name}",
681
+ )
410
682
  for i, division_id in enumerate(division_ids):
411
- aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug)
412
- aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
413
- aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
683
+ aggregate_skater_stats(
684
+ session,
685
+ aggregation_type="division",
686
+ aggregation_id=division_id,
687
+ debug_human_id=human_id_to_debug,
688
+ )
689
+ aggregate_skater_stats(
690
+ session,
691
+ aggregation_type="division",
692
+ aggregation_id=division_id,
693
+ debug_human_id=human_id_to_debug,
694
+ aggregation_window="Weekly",
695
+ )
696
+ aggregate_skater_stats(
697
+ session,
698
+ aggregation_type="division",
699
+ aggregation_id=division_id,
700
+ debug_human_id=human_id_to_debug,
701
+ aggregation_window="Daily",
702
+ )
414
703
  progress.update(i + 1)
415
704
  else:
416
705
  # Debug mode or no divisions - process without progress tracking
417
706
  for division_id in division_ids:
418
- aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug)
419
- aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
420
- aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
707
+ aggregate_skater_stats(
708
+ session,
709
+ aggregation_type="division",
710
+ aggregation_id=division_id,
711
+ debug_human_id=human_id_to_debug,
712
+ )
713
+ aggregate_skater_stats(
714
+ session,
715
+ aggregation_type="division",
716
+ aggregation_id=division_id,
717
+ debug_human_id=human_id_to_debug,
718
+ aggregation_window="Weekly",
719
+ )
720
+ aggregate_skater_stats(
721
+ session,
722
+ aggregation_type="division",
723
+ aggregation_id=division_id,
724
+ debug_human_id=human_id_to_debug,
725
+ aggregation_window="Daily",
726
+ )
421
727
 
422
728
  # Process org-level stats with progress tracking
423
729
  if human_id_to_debug is None:
424
- org_progress = create_progress_tracker(3, f"Processing org-level stats for {org_name}")
425
- aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug)
730
+ org_progress = create_progress_tracker(
731
+ 3, f"Processing org-level stats for {org_name}"
732
+ )
733
+ aggregate_skater_stats(
734
+ session,
735
+ aggregation_type="org",
736
+ aggregation_id=org_id,
737
+ debug_human_id=human_id_to_debug,
738
+ )
426
739
  org_progress.update(1)
427
- aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
740
+ aggregate_skater_stats(
741
+ session,
742
+ aggregation_type="org",
743
+ aggregation_id=org_id,
744
+ debug_human_id=human_id_to_debug,
745
+ aggregation_window="Weekly",
746
+ )
428
747
  org_progress.update(2)
429
- aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
748
+ aggregate_skater_stats(
749
+ session,
750
+ aggregation_type="org",
751
+ aggregation_id=org_id,
752
+ debug_human_id=human_id_to_debug,
753
+ aggregation_window="Daily",
754
+ )
430
755
  org_progress.update(3)
431
756
  else:
432
- aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug)
433
- aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
434
- aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
435
-
757
+ aggregate_skater_stats(
758
+ session,
759
+ aggregation_type="org",
760
+ aggregation_id=org_id,
761
+ debug_human_id=human_id_to_debug,
762
+ )
763
+ aggregate_skater_stats(
764
+ session,
765
+ aggregation_type="org",
766
+ aggregation_id=org_id,
767
+ debug_human_id=human_id_to_debug,
768
+ aggregation_window="Weekly",
769
+ )
770
+ aggregate_skater_stats(
771
+ session,
772
+ aggregation_type="org",
773
+ aggregation_id=org_id,
774
+ debug_human_id=human_id_to_debug,
775
+ aggregation_window="Daily",
776
+ )
777
+
436
778
  # Aggregate by level
437
779
  level_ids = session.query(Division.level_id).distinct().all()
438
780
  level_ids = [level_id[0] for level_id in level_ids if level_id[0] is not None]
439
-
781
+
440
782
  if human_id_to_debug is None and level_ids:
441
783
  # Process levels with progress tracking
442
- level_progress = create_progress_tracker(len(level_ids), f"Processing {len(level_ids)} skill levels")
784
+ level_progress = create_progress_tracker(
785
+ len(level_ids), f"Processing {len(level_ids)} skill levels"
786
+ )
443
787
  for i, level_id in enumerate(level_ids):
444
- aggregate_skater_stats(session, aggregation_type='level', aggregation_id=level_id, debug_human_id=human_id_to_debug)
788
+ aggregate_skater_stats(
789
+ session,
790
+ aggregation_type="level",
791
+ aggregation_id=level_id,
792
+ debug_human_id=human_id_to_debug,
793
+ )
445
794
  level_progress.update(i + 1)
446
795
  else:
447
796
  # Debug mode or no levels - process without progress tracking
448
797
  for level_id in level_ids:
449
- aggregate_skater_stats(session, aggregation_type='level', aggregation_id=level_id, debug_human_id=human_id_to_debug)
798
+ aggregate_skater_stats(
799
+ session,
800
+ aggregation_type="level",
801
+ aggregation_id=level_id,
802
+ debug_human_id=human_id_to_debug,
803
+ )
804
+
450
805
 
451
806
  if __name__ == "__main__":
452
807
  run_aggregate_skater_stats()