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,21 +1,38 @@
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
7
 
7
- from datetime import datetime, timedelta
8
+
8
9
  import sqlalchemy
9
- from hockey_blast_common_lib.models import Game, Penalty, Organization, Division
10
- from hockey_blast_common_lib.stats_models import OrgStatsReferee, DivisionStatsReferee, OrgStatsWeeklyReferee, OrgStatsDailyReferee, DivisionStatsWeeklyReferee, DivisionStatsDailyReferee, LevelStatsReferee
10
+ from sqlalchemy.sql import case, func
11
+
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 assign_ranks
16
- from hockey_blast_common_lib.utils import get_start_datetime
17
- from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
13
+ from hockey_blast_common_lib.models import Division, Game, Organization, Penalty
14
+ from hockey_blast_common_lib.options import (
15
+ MIN_GAMES_FOR_DIVISION_STATS,
16
+ MIN_GAMES_FOR_LEVEL_STATS,
17
+ MIN_GAMES_FOR_ORG_STATS,
18
+ )
18
19
  from hockey_blast_common_lib.progress_utils import create_progress_tracker
20
+ from hockey_blast_common_lib.stats_models import (
21
+ DivisionStatsDailyReferee,
22
+ DivisionStatsReferee,
23
+ DivisionStatsWeeklyReferee,
24
+ LevelStatsReferee,
25
+ OrgStatsDailyReferee,
26
+ OrgStatsReferee,
27
+ OrgStatsWeeklyReferee,
28
+ )
29
+ from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
30
+ from hockey_blast_common_lib.utils import (
31
+ assign_ranks,
32
+ get_all_division_ids_for_org,
33
+ get_non_human_ids,
34
+ get_start_datetime,
35
+ )
19
36
 
20
37
  # Import status constants for game filtering
21
38
  FINAL_STATUS = "Final"
@@ -23,34 +40,44 @@ FINAL_SO_STATUS = "Final(SO)"
23
40
  FORFEIT_STATUS = "FORFEIT"
24
41
  NOEVENTS_STATUS = "NOEVENTS"
25
42
 
26
- def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregation_window=None):
43
+
44
+ def aggregate_referee_stats(
45
+ session, aggregation_type, aggregation_id, aggregation_window=None
46
+ ):
27
47
  human_ids_to_filter = get_non_human_ids(session)
28
48
 
29
- if aggregation_type == 'org':
49
+ if aggregation_type == "org":
30
50
  if aggregation_id == ALL_ORGS_ID:
31
51
  aggregation_name = "All Orgs"
32
52
  filter_condition = sqlalchemy.true() # No filter for organization
33
53
  else:
34
- aggregation_name = session.query(Organization).filter(Organization.id == aggregation_id).first().organization_name
54
+ aggregation_name = (
55
+ session.query(Organization)
56
+ .filter(Organization.id == aggregation_id)
57
+ .first()
58
+ .organization_name
59
+ )
35
60
  filter_condition = Game.org_id == aggregation_id
36
- print(f"Aggregating referee stats for {aggregation_name} with window {aggregation_window}...")
37
- if aggregation_window == 'Daily':
61
+ print(
62
+ f"Aggregating referee stats for {aggregation_name} with window {aggregation_window}..."
63
+ )
64
+ if aggregation_window == "Daily":
38
65
  StatsModel = OrgStatsDailyReferee
39
- elif aggregation_window == 'Weekly':
66
+ elif aggregation_window == "Weekly":
40
67
  StatsModel = OrgStatsWeeklyReferee
41
68
  else:
42
69
  StatsModel = OrgStatsReferee
43
70
  min_games = MIN_GAMES_FOR_ORG_STATS
44
- elif aggregation_type == 'division':
45
- if aggregation_window == 'Daily':
71
+ elif aggregation_type == "division":
72
+ if aggregation_window == "Daily":
46
73
  StatsModel = DivisionStatsDailyReferee
47
- elif aggregation_window == 'Weekly':
74
+ elif aggregation_window == "Weekly":
48
75
  StatsModel = DivisionStatsWeeklyReferee
49
76
  else:
50
77
  StatsModel = DivisionStatsReferee
51
78
  min_games = MIN_GAMES_FOR_DIVISION_STATS
52
79
  filter_condition = Game.division_id == aggregation_id
53
- elif aggregation_type == 'level':
80
+ elif aggregation_type == "level":
54
81
  StatsModel = LevelStatsReferee
55
82
  min_games = MIN_GAMES_FOR_LEVEL_STATS
56
83
  filter_condition = Division.level_id == aggregation_id
@@ -62,18 +89,26 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregati
62
89
  raise ValueError("Invalid aggregation type")
63
90
 
64
91
  # Delete existing items from the stats table
65
- session.query(StatsModel).filter(StatsModel.aggregation_id == aggregation_id).delete()
92
+ session.query(StatsModel).filter(
93
+ StatsModel.aggregation_id == aggregation_id
94
+ ).delete()
66
95
  session.commit()
67
96
 
68
97
  # Apply aggregation window filter
69
98
  if aggregation_window:
70
- last_game_datetime_str = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, Game.status.like('Final%')).scalar()
99
+ last_game_datetime_str = (
100
+ session.query(func.max(func.concat(Game.date, " ", Game.time)))
101
+ .filter(filter_condition, Game.status.like("Final%"))
102
+ .scalar()
103
+ )
71
104
  start_datetime = get_start_datetime(last_game_datetime_str, aggregation_window)
72
105
  if start_datetime:
73
- game_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP).between(start_datetime, last_game_datetime_str)
106
+ game_window_filter = func.cast(
107
+ func.concat(Game.date, " ", Game.time), sqlalchemy.types.TIMESTAMP
108
+ ).between(start_datetime, last_game_datetime_str)
74
109
  filter_condition = filter_condition & game_window_filter
75
110
  else:
76
- #print(f"Warning: No valid start datetime for aggregation window '{aggregation_window}' for {aggregation_name}. No games will be included.")
111
+ # print(f"Warning: No valid start datetime for aggregation window '{aggregation_window}' for {aggregation_name}. No games will be included.")
77
112
  return
78
113
 
79
114
  filter_condition = filter_condition & (Division.id == Game.division_id)
@@ -81,32 +116,60 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregati
81
116
  # games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
82
117
  # games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
83
118
  # Filter by game status upfront for performance
84
- status_filter = Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS])
85
-
86
- games_reffed_stats = session.query(
87
- Game.referee_1_id.label('human_id'),
88
- func.count(Game.id).label('games_reffed'),
89
- func.count(Game.id).label('games_participated'), # Same as games_reffed after filtering
90
- func.count(Game.id).label('games_with_stats'), # Same as games_reffed after filtering
91
- func.array_agg(Game.id).label('game_ids')
92
- ).filter(filter_condition, status_filter).group_by(Game.referee_1_id).all()
93
-
94
- games_reffed_stats_2 = session.query(
95
- Game.referee_2_id.label('human_id'),
96
- func.count(Game.id).label('games_reffed'),
97
- func.count(Game.id).label('games_participated'), # Same as games_reffed after filtering
98
- func.count(Game.id).label('games_with_stats'), # Same as games_reffed after filtering
99
- func.array_agg(Game.id).label('game_ids')
100
- ).filter(filter_condition, status_filter).group_by(Game.referee_2_id).all()
119
+ status_filter = Game.status.in_(
120
+ [FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]
121
+ )
122
+
123
+ games_reffed_stats = (
124
+ session.query(
125
+ Game.referee_1_id.label("human_id"),
126
+ func.count(Game.id).label("games_reffed"),
127
+ func.count(Game.id).label(
128
+ "games_participated"
129
+ ), # Same as games_reffed after filtering
130
+ func.count(Game.id).label(
131
+ "games_with_stats"
132
+ ), # Same as games_reffed after filtering
133
+ func.array_agg(Game.id).label("game_ids"),
134
+ )
135
+ .filter(filter_condition, status_filter)
136
+ .group_by(Game.referee_1_id)
137
+ .all()
138
+ )
139
+
140
+ games_reffed_stats_2 = (
141
+ session.query(
142
+ Game.referee_2_id.label("human_id"),
143
+ func.count(Game.id).label("games_reffed"),
144
+ func.count(Game.id).label(
145
+ "games_participated"
146
+ ), # Same as games_reffed after filtering
147
+ func.count(Game.id).label(
148
+ "games_with_stats"
149
+ ), # Same as games_reffed after filtering
150
+ func.array_agg(Game.id).label("game_ids"),
151
+ )
152
+ .filter(filter_condition, status_filter)
153
+ .group_by(Game.referee_2_id)
154
+ .all()
155
+ )
101
156
 
102
157
  # Aggregate penalties given for each referee
103
- penalties_given_stats = session.query(
104
- Game.id.label('game_id'),
105
- Game.referee_1_id,
106
- Game.referee_2_id,
107
- func.count(Penalty.id).label('penalties_given'),
108
- func.sum(case((func.lower(Penalty.penalty_minutes) == 'gm', 1), else_=0)).label('gm_given')
109
- ).join(Game, Game.id == Penalty.game_id).filter(filter_condition).group_by(Game.id, Game.referee_1_id, Game.referee_2_id).all()
158
+ penalties_given_stats = (
159
+ session.query(
160
+ Game.id.label("game_id"),
161
+ Game.referee_1_id,
162
+ Game.referee_2_id,
163
+ func.count(Penalty.id).label("penalties_given"),
164
+ func.sum(
165
+ case((func.lower(Penalty.penalty_minutes) == "gm", 1), else_=0)
166
+ ).label("gm_given"),
167
+ )
168
+ .join(Game, Game.id == Penalty.game_id)
169
+ .filter(filter_condition)
170
+ .group_by(Game.id, Game.referee_1_id, Game.referee_2_id)
171
+ .all()
172
+ )
110
173
 
111
174
  # Combine the results
112
175
  stats_dict = {}
@@ -116,21 +179,21 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregati
116
179
  key = (aggregation_id, stat.human_id)
117
180
  if key not in stats_dict:
118
181
  stats_dict[key] = {
119
- 'games_reffed': 0, # DEPRECATED - for backward compatibility
120
- 'games_participated': 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
121
- 'games_with_stats': 0, # Games with full stats: FINAL, FINAL_SO only
122
- 'penalties_given': 0,
123
- 'gm_given': 0,
124
- 'penalties_per_game': 0.0,
125
- 'gm_per_game': 0.0,
126
- 'game_ids': [],
127
- 'first_game_id': None,
128
- 'last_game_id': None
182
+ "games_reffed": 0, # DEPRECATED - for backward compatibility
183
+ "games_participated": 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
184
+ "games_with_stats": 0, # Games with full stats: FINAL, FINAL_SO only
185
+ "penalties_given": 0,
186
+ "gm_given": 0,
187
+ "penalties_per_game": 0.0,
188
+ "gm_per_game": 0.0,
189
+ "game_ids": [],
190
+ "first_game_id": None,
191
+ "last_game_id": None,
129
192
  }
130
- stats_dict[key]['games_reffed'] += stat.games_reffed
131
- stats_dict[key]['games_participated'] += stat.games_participated
132
- stats_dict[key]['games_with_stats'] += stat.games_with_stats
133
- stats_dict[key]['game_ids'].extend(stat.game_ids)
193
+ stats_dict[key]["games_reffed"] += stat.games_reffed
194
+ stats_dict[key]["games_participated"] += stat.games_participated
195
+ stats_dict[key]["games_with_stats"] += stat.games_with_stats
196
+ stats_dict[key]["game_ids"].extend(stat.game_ids)
134
197
 
135
198
  for stat in games_reffed_stats_2:
136
199
  if stat.human_id in human_ids_to_filter:
@@ -138,69 +201,85 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregati
138
201
  key = (aggregation_id, stat.human_id)
139
202
  if key not in stats_dict:
140
203
  stats_dict[key] = {
141
- 'games_reffed': 0, # DEPRECATED - for backward compatibility
142
- 'games_participated': 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
143
- 'games_with_stats': 0, # Games with full stats: FINAL, FINAL_SO only
144
- 'penalties_given': 0,
145
- 'gm_given': 0,
146
- 'penalties_per_game': 0.0,
147
- 'gm_per_game': 0.0,
148
- 'game_ids': [],
149
- 'first_game_id': None,
150
- 'last_game_id': None
204
+ "games_reffed": 0, # DEPRECATED - for backward compatibility
205
+ "games_participated": 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
206
+ "games_with_stats": 0, # Games with full stats: FINAL, FINAL_SO only
207
+ "penalties_given": 0,
208
+ "gm_given": 0,
209
+ "penalties_per_game": 0.0,
210
+ "gm_per_game": 0.0,
211
+ "game_ids": [],
212
+ "first_game_id": None,
213
+ "last_game_id": None,
151
214
  }
152
- stats_dict[key]['games_reffed'] += stat.games_reffed
153
- stats_dict[key]['games_participated'] += stat.games_participated
154
- stats_dict[key]['games_with_stats'] += stat.games_with_stats
155
- stats_dict[key]['game_ids'].extend(stat.game_ids)
215
+ stats_dict[key]["games_reffed"] += stat.games_reffed
216
+ stats_dict[key]["games_participated"] += stat.games_participated
217
+ stats_dict[key]["games_with_stats"] += stat.games_with_stats
218
+ stats_dict[key]["game_ids"].extend(stat.game_ids)
156
219
 
157
220
  # Filter out entries with games_reffed less than min_games
158
- stats_dict = {key: value for key, value in stats_dict.items() if value['games_reffed'] >= min_games}
221
+ stats_dict = {
222
+ key: value
223
+ for key, value in stats_dict.items()
224
+ if value["games_reffed"] >= min_games
225
+ }
159
226
 
160
227
  for stat in penalties_given_stats:
161
228
  if stat.referee_1_id and stat.referee_1_id not in human_ids_to_filter:
162
229
  key = (aggregation_id, stat.referee_1_id)
163
230
  if key in stats_dict:
164
- stats_dict[key]['penalties_given'] += stat.penalties_given / 2
165
- stats_dict[key]['gm_given'] += stat.gm_given / 2
166
- stats_dict[key]['game_ids'].append(stat.game_id)
231
+ stats_dict[key]["penalties_given"] += stat.penalties_given / 2
232
+ stats_dict[key]["gm_given"] += stat.gm_given / 2
233
+ stats_dict[key]["game_ids"].append(stat.game_id)
167
234
 
168
235
  if stat.referee_2_id and stat.referee_2_id not in human_ids_to_filter:
169
236
  key = (aggregation_id, stat.referee_2_id)
170
237
  if key in stats_dict:
171
- stats_dict[key]['penalties_given'] += stat.penalties_given / 2
172
- stats_dict[key]['gm_given'] += stat.gm_given / 2
173
- stats_dict[key]['game_ids'].append(stat.game_id)
238
+ stats_dict[key]["penalties_given"] += stat.penalties_given / 2
239
+ stats_dict[key]["gm_given"] += stat.gm_given / 2
240
+ stats_dict[key]["game_ids"].append(stat.game_id)
174
241
 
175
242
  # Calculate per game stats (using games_with_stats as denominator for accuracy)
176
243
  for key, stat in stats_dict.items():
177
- if stat['games_with_stats'] > 0:
178
- stat['penalties_per_game'] = stat['penalties_given'] / stat['games_with_stats']
179
- stat['gm_per_game'] = stat['gm_given'] / stat['games_with_stats']
244
+ if stat["games_with_stats"] > 0:
245
+ stat["penalties_per_game"] = (
246
+ stat["penalties_given"] / stat["games_with_stats"]
247
+ )
248
+ stat["gm_per_game"] = stat["gm_given"] / stat["games_with_stats"]
180
249
 
181
250
  # Ensure all keys have valid human_id values
182
251
  stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
183
252
 
184
253
  # Populate first_game_id and last_game_id
185
254
  for key, stat in stats_dict.items():
186
- all_game_ids = stat['game_ids']
255
+ all_game_ids = stat["game_ids"]
187
256
  if all_game_ids:
188
- first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
189
- last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
190
- stat['first_game_id'] = first_game.id if first_game else None
191
- stat['last_game_id'] = last_game.id if last_game else None
257
+ first_game = (
258
+ session.query(Game)
259
+ .filter(Game.id.in_(all_game_ids))
260
+ .order_by(Game.date, Game.time)
261
+ .first()
262
+ )
263
+ last_game = (
264
+ session.query(Game)
265
+ .filter(Game.id.in_(all_game_ids))
266
+ .order_by(Game.date.desc(), Game.time.desc())
267
+ .first()
268
+ )
269
+ stat["first_game_id"] = first_game.id if first_game else None
270
+ stat["last_game_id"] = last_game.id if last_game else None
192
271
 
193
272
  # Calculate total_in_rank
194
273
  total_in_rank = len(stats_dict)
195
274
 
196
275
  # Assign ranks
197
- assign_ranks(stats_dict, 'games_reffed')
198
- assign_ranks(stats_dict, 'games_participated') # Rank by total participation
199
- assign_ranks(stats_dict, 'games_with_stats') # Rank by games with full stats
200
- assign_ranks(stats_dict, 'penalties_given')
201
- assign_ranks(stats_dict, 'penalties_per_game')
202
- assign_ranks(stats_dict, 'gm_given')
203
- assign_ranks(stats_dict, 'gm_per_game')
276
+ assign_ranks(stats_dict, "games_reffed")
277
+ assign_ranks(stats_dict, "games_participated") # Rank by total participation
278
+ assign_ranks(stats_dict, "games_with_stats") # Rank by games with full stats
279
+ assign_ranks(stats_dict, "penalties_given")
280
+ assign_ranks(stats_dict, "penalties_per_game")
281
+ assign_ranks(stats_dict, "gm_given")
282
+ assign_ranks(stats_dict, "gm_per_game")
204
283
 
205
284
  # Insert aggregated stats into the appropriate table with progress output
206
285
  total_items = len(stats_dict)
@@ -210,23 +289,29 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregati
210
289
  referee_stat = StatsModel(
211
290
  aggregation_id=aggregation_id,
212
291
  human_id=human_id,
213
- games_reffed=stat['games_reffed'], # DEPRECATED - for backward compatibility
214
- games_participated=stat['games_participated'], # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
215
- games_participated_rank=stat['games_participated_rank'],
216
- games_with_stats=stat['games_with_stats'], # Games with full stats: FINAL, FINAL_SO only
217
- games_with_stats_rank=stat['games_with_stats_rank'],
218
- penalties_given=stat['penalties_given'],
219
- penalties_per_game=stat['penalties_per_game'],
220
- gm_given=stat['gm_given'],
221
- gm_per_game=stat['gm_per_game'],
222
- games_reffed_rank=stat['games_reffed_rank'],
223
- penalties_given_rank=stat['penalties_given_rank'],
224
- penalties_per_game_rank=stat['penalties_per_game_rank'],
225
- gm_given_rank=stat['gm_given_rank'],
226
- gm_per_game_rank=stat['gm_per_game_rank'],
292
+ games_reffed=stat[
293
+ "games_reffed"
294
+ ], # DEPRECATED - for backward compatibility
295
+ games_participated=stat[
296
+ "games_participated"
297
+ ], # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
298
+ games_participated_rank=stat["games_participated_rank"],
299
+ games_with_stats=stat[
300
+ "games_with_stats"
301
+ ], # Games with full stats: FINAL, FINAL_SO only
302
+ games_with_stats_rank=stat["games_with_stats_rank"],
303
+ penalties_given=stat["penalties_given"],
304
+ penalties_per_game=stat["penalties_per_game"],
305
+ gm_given=stat["gm_given"],
306
+ gm_per_game=stat["gm_per_game"],
307
+ games_reffed_rank=stat["games_reffed_rank"],
308
+ penalties_given_rank=stat["penalties_given_rank"],
309
+ penalties_per_game_rank=stat["penalties_per_game_rank"],
310
+ gm_given_rank=stat["gm_given_rank"],
311
+ gm_per_game_rank=stat["gm_per_game_rank"],
227
312
  total_in_rank=total_in_rank,
228
- first_game_id=stat['first_game_id'],
229
- last_game_id=stat['last_game_id']
313
+ first_game_id=stat["first_game_id"],
314
+ last_game_id=stat["last_game_id"],
230
315
  )
231
316
  session.add(referee_stat)
232
317
  # Commit in batches
@@ -234,6 +319,7 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregati
234
319
  session.commit()
235
320
  session.commit()
236
321
 
322
+
237
323
  def run_aggregate_referee_stats():
238
324
  session = create_session("boss")
239
325
  human_id_to_debug = None
@@ -244,51 +330,116 @@ def run_aggregate_referee_stats():
244
330
 
245
331
  for org_id in org_ids:
246
332
  division_ids = get_all_division_ids_for_org(session, org_id)
247
- org_name = session.query(Organization.organization_name).filter(Organization.id == org_id).scalar() or f"org_id {org_id}"
248
-
333
+ org_name = (
334
+ session.query(Organization.organization_name)
335
+ .filter(Organization.id == org_id)
336
+ .scalar()
337
+ or f"org_id {org_id}"
338
+ )
339
+
249
340
  if human_id_to_debug is None and division_ids:
250
341
  # Process divisions with progress tracking
251
- progress = create_progress_tracker(len(division_ids), f"Processing {len(division_ids)} divisions for {org_name}")
342
+ progress = create_progress_tracker(
343
+ len(division_ids),
344
+ f"Processing {len(division_ids)} divisions for {org_name}",
345
+ )
252
346
  for i, division_id in enumerate(division_ids):
253
- aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id)
254
- aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, aggregation_window='Weekly')
255
- aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, aggregation_window='Daily')
347
+ aggregate_referee_stats(
348
+ session, aggregation_type="division", aggregation_id=division_id
349
+ )
350
+ aggregate_referee_stats(
351
+ session,
352
+ aggregation_type="division",
353
+ aggregation_id=division_id,
354
+ aggregation_window="Weekly",
355
+ )
356
+ aggregate_referee_stats(
357
+ session,
358
+ aggregation_type="division",
359
+ aggregation_id=division_id,
360
+ aggregation_window="Daily",
361
+ )
256
362
  progress.update(i + 1)
257
363
  else:
258
364
  # Debug mode or no divisions - process without progress tracking
259
365
  for division_id in division_ids:
260
- aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id)
261
- aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, aggregation_window='Weekly')
262
- aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, aggregation_window='Daily')
366
+ aggregate_referee_stats(
367
+ session, aggregation_type="division", aggregation_id=division_id
368
+ )
369
+ aggregate_referee_stats(
370
+ session,
371
+ aggregation_type="division",
372
+ aggregation_id=division_id,
373
+ aggregation_window="Weekly",
374
+ )
375
+ aggregate_referee_stats(
376
+ session,
377
+ aggregation_type="division",
378
+ aggregation_id=division_id,
379
+ aggregation_window="Daily",
380
+ )
263
381
 
264
382
  # Process org-level stats with progress tracking
265
383
  if human_id_to_debug is None:
266
- org_progress = create_progress_tracker(3, f"Processing org-level stats for {org_name}")
267
- aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id)
384
+ org_progress = create_progress_tracker(
385
+ 3, f"Processing org-level stats for {org_name}"
386
+ )
387
+ aggregate_referee_stats(
388
+ session, aggregation_type="org", aggregation_id=org_id
389
+ )
268
390
  org_progress.update(1)
269
- aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Weekly')
391
+ aggregate_referee_stats(
392
+ session,
393
+ aggregation_type="org",
394
+ aggregation_id=org_id,
395
+ aggregation_window="Weekly",
396
+ )
270
397
  org_progress.update(2)
271
- aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Daily')
398
+ aggregate_referee_stats(
399
+ session,
400
+ aggregation_type="org",
401
+ aggregation_id=org_id,
402
+ aggregation_window="Daily",
403
+ )
272
404
  org_progress.update(3)
273
405
  else:
274
- aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id)
275
- aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Weekly')
276
- aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Daily')
277
-
406
+ aggregate_referee_stats(
407
+ session, aggregation_type="org", aggregation_id=org_id
408
+ )
409
+ aggregate_referee_stats(
410
+ session,
411
+ aggregation_type="org",
412
+ aggregation_id=org_id,
413
+ aggregation_window="Weekly",
414
+ )
415
+ aggregate_referee_stats(
416
+ session,
417
+ aggregation_type="org",
418
+ aggregation_id=org_id,
419
+ aggregation_window="Daily",
420
+ )
421
+
278
422
  # Aggregate by level
279
423
  level_ids = session.query(Division.level_id).distinct().all()
280
424
  level_ids = [level_id[0] for level_id in level_ids if level_id[0] is not None]
281
-
425
+
282
426
  if human_id_to_debug is None and level_ids:
283
427
  # Process levels with progress tracking
284
- level_progress = create_progress_tracker(len(level_ids), f"Processing {len(level_ids)} skill levels")
428
+ level_progress = create_progress_tracker(
429
+ len(level_ids), f"Processing {len(level_ids)} skill levels"
430
+ )
285
431
  for i, level_id in enumerate(level_ids):
286
- aggregate_referee_stats(session, aggregation_type='level', aggregation_id=level_id)
432
+ aggregate_referee_stats(
433
+ session, aggregation_type="level", aggregation_id=level_id
434
+ )
287
435
  level_progress.update(i + 1)
288
436
  else:
289
437
  # Debug mode or no levels - process without progress tracking
290
438
  for level_id in level_ids:
291
- aggregate_referee_stats(session, aggregation_type='level', aggregation_id=level_id)
439
+ aggregate_referee_stats(
440
+ session, aggregation_type="level", aggregation_id=level_id
441
+ )
442
+
292
443
 
293
444
  if __name__ == "__main__":
294
445
  run_aggregate_referee_stats()