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.
- hockey_blast_common_lib/aggregate_all_stats.py +7 -4
- hockey_blast_common_lib/aggregate_goalie_stats.py +301 -107
- hockey_blast_common_lib/aggregate_h2h_stats.py +64 -33
- hockey_blast_common_lib/aggregate_human_stats.py +565 -280
- hockey_blast_common_lib/aggregate_referee_stats.py +286 -135
- hockey_blast_common_lib/aggregate_s2s_stats.py +85 -25
- hockey_blast_common_lib/aggregate_scorekeeper_stats.py +228 -113
- hockey_blast_common_lib/aggregate_skater_stats.py +561 -238
- hockey_blast_common_lib/assign_skater_skill.py +21 -11
- hockey_blast_common_lib/db_connection.py +59 -8
- hockey_blast_common_lib/embedding_utils.py +309 -0
- hockey_blast_common_lib/h2h_models.py +150 -56
- hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz +0 -0
- hockey_blast_common_lib/models.py +305 -150
- hockey_blast_common_lib/options.py +30 -15
- hockey_blast_common_lib/progress_utils.py +21 -13
- hockey_blast_common_lib/skills_in_divisions.py +170 -33
- hockey_blast_common_lib/skills_propagation.py +164 -70
- hockey_blast_common_lib/stats_models.py +489 -245
- hockey_blast_common_lib/stats_utils.py +6 -3
- hockey_blast_common_lib/utils.py +91 -25
- hockey_blast_common_lib/wsgi.py +7 -5
- {hockey_blast_common_lib-0.1.63.dist-info → hockey_blast_common_lib-0.1.65.dist-info}/METADATA +1 -1
- hockey_blast_common_lib-0.1.65.dist-info/RECORD +29 -0
- hockey_blast_common_lib-0.1.63.dist-info/RECORD +0 -28
- {hockey_blast_common_lib-0.1.63.dist-info → hockey_blast_common_lib-0.1.65.dist-info}/WHEEL +0 -0
- {hockey_blast_common_lib-0.1.63.dist-info → hockey_blast_common_lib-0.1.65.dist-info}/top_level.txt +0 -0
|
@@ -1,22 +1,46 @@
|
|
|
1
|
-
import
|
|
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
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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,6 +48,7 @@ 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.
|
|
@@ -33,29 +58,37 @@ def calculate_current_point_streak(session, human_id, filter_condition):
|
|
|
33
58
|
Optimized to use CASE statements for conditional aggregation in a single query.
|
|
34
59
|
"""
|
|
35
60
|
# Get all games with their point totals in ONE query using CASE for conditional counting
|
|
36
|
-
game_points =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
+
)
|
|
59
92
|
|
|
60
93
|
if not game_points:
|
|
61
94
|
return 0, 0.0
|
|
@@ -75,52 +108,74 @@ def calculate_current_point_streak(session, human_id, filter_condition):
|
|
|
75
108
|
break
|
|
76
109
|
|
|
77
110
|
# Calculate average points during streak
|
|
78
|
-
avg_points_during_streak =
|
|
111
|
+
avg_points_during_streak = (
|
|
112
|
+
total_points_in_streak / current_streak if current_streak > 0 else 0.0
|
|
113
|
+
)
|
|
79
114
|
|
|
80
115
|
return current_streak, avg_points_during_streak
|
|
81
116
|
|
|
82
|
-
|
|
117
|
+
|
|
118
|
+
def aggregate_skater_stats(
|
|
119
|
+
session,
|
|
120
|
+
aggregation_type,
|
|
121
|
+
aggregation_id,
|
|
122
|
+
debug_human_id=None,
|
|
123
|
+
aggregation_window=None,
|
|
124
|
+
):
|
|
83
125
|
human_ids_to_filter = get_non_human_ids(session)
|
|
84
126
|
|
|
85
127
|
# Get the name of the aggregation, for debug purposes
|
|
86
|
-
if aggregation_type ==
|
|
128
|
+
if aggregation_type == "org":
|
|
87
129
|
if aggregation_id == ALL_ORGS_ID:
|
|
88
130
|
aggregation_name = "All Orgs"
|
|
89
131
|
filter_condition = sqlalchemy.true() # No filter for organization
|
|
90
132
|
else:
|
|
91
|
-
aggregation_name =
|
|
133
|
+
aggregation_name = (
|
|
134
|
+
session.query(Organization)
|
|
135
|
+
.filter(Organization.id == aggregation_id)
|
|
136
|
+
.first()
|
|
137
|
+
.organization_name
|
|
138
|
+
)
|
|
92
139
|
filter_condition = Game.org_id == aggregation_id
|
|
93
|
-
print(
|
|
140
|
+
print(
|
|
141
|
+
f"Aggregating skater stats for {aggregation_name} with window {aggregation_window}..."
|
|
142
|
+
)
|
|
94
143
|
|
|
95
|
-
elif aggregation_type ==
|
|
96
|
-
aggregation_name =
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
)
|
|
99
152
|
else:
|
|
100
153
|
aggregation_name = "Unknown"
|
|
101
154
|
|
|
102
|
-
if aggregation_type ==
|
|
103
|
-
if aggregation_window ==
|
|
155
|
+
if aggregation_type == "org":
|
|
156
|
+
if aggregation_window == "Daily":
|
|
104
157
|
StatsModel = OrgStatsDailySkater
|
|
105
|
-
elif aggregation_window ==
|
|
158
|
+
elif aggregation_window == "Weekly":
|
|
106
159
|
StatsModel = OrgStatsWeeklySkater
|
|
107
160
|
else:
|
|
108
161
|
StatsModel = OrgStatsSkater
|
|
109
162
|
min_games = MIN_GAMES_FOR_ORG_STATS
|
|
110
|
-
elif aggregation_type ==
|
|
111
|
-
if aggregation_window ==
|
|
163
|
+
elif aggregation_type == "division":
|
|
164
|
+
if aggregation_window == "Daily":
|
|
112
165
|
StatsModel = DivisionStatsDailySkater
|
|
113
|
-
elif aggregation_window ==
|
|
166
|
+
elif aggregation_window == "Weekly":
|
|
114
167
|
StatsModel = DivisionStatsWeeklySkater
|
|
115
168
|
else:
|
|
116
169
|
StatsModel = DivisionStatsSkater
|
|
117
170
|
min_games = MIN_GAMES_FOR_DIVISION_STATS
|
|
118
171
|
filter_condition = Game.division_id == aggregation_id
|
|
119
|
-
elif aggregation_type ==
|
|
172
|
+
elif aggregation_type == "level":
|
|
120
173
|
StatsModel = LevelStatsSkater
|
|
121
174
|
min_games = MIN_GAMES_FOR_LEVEL_STATS
|
|
122
175
|
# Get division IDs for this level to avoid cartesian product
|
|
123
|
-
division_ids =
|
|
176
|
+
division_ids = (
|
|
177
|
+
session.query(Division.id).filter(Division.level_id == aggregation_id).all()
|
|
178
|
+
)
|
|
124
179
|
division_ids = [div_id[0] for div_id in division_ids]
|
|
125
180
|
if not division_ids:
|
|
126
181
|
return # No divisions for this level
|
|
@@ -133,20 +188,31 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
|
|
|
133
188
|
raise ValueError("Invalid aggregation type")
|
|
134
189
|
|
|
135
190
|
# Delete existing items from the stats table
|
|
136
|
-
session.query(StatsModel).filter(
|
|
191
|
+
session.query(StatsModel).filter(
|
|
192
|
+
StatsModel.aggregation_id == aggregation_id
|
|
193
|
+
).delete()
|
|
137
194
|
session.commit()
|
|
138
195
|
|
|
139
196
|
# Apply aggregation window filter
|
|
140
197
|
if aggregation_window:
|
|
141
|
-
last_game_datetime_str =
|
|
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
|
+
)
|
|
142
206
|
start_datetime = get_start_datetime(last_game_datetime_str, aggregation_window)
|
|
143
207
|
if start_datetime:
|
|
144
|
-
game_window_filter = func.cast(
|
|
208
|
+
game_window_filter = func.cast(
|
|
209
|
+
func.concat(Game.date, " ", Game.time), sqlalchemy.types.TIMESTAMP
|
|
210
|
+
).between(start_datetime, last_game_datetime_str)
|
|
145
211
|
filter_condition = filter_condition & game_window_filter
|
|
146
212
|
else:
|
|
147
|
-
#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.")
|
|
148
214
|
return
|
|
149
|
-
|
|
215
|
+
|
|
150
216
|
# Filter for specific human_id if provided
|
|
151
217
|
human_filter = []
|
|
152
218
|
# if debug_human_id:
|
|
@@ -155,69 +221,153 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
|
|
|
155
221
|
# Aggregate games played for each human in each division, excluding goalies
|
|
156
222
|
# Filter games by status upfront for performance (avoid CASE statements on 200K+ rows)
|
|
157
223
|
# Only count games with these statuses: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
|
158
|
-
games_played_query =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
)
|
|
166
242
|
)
|
|
167
243
|
|
|
168
244
|
# Only join Division if not level aggregation (since we filter on Game.division_id directly for levels)
|
|
169
|
-
if aggregation_type !=
|
|
170
|
-
games_played_query = games_played_query.join(
|
|
245
|
+
if aggregation_type != "level":
|
|
246
|
+
games_played_query = games_played_query.join(
|
|
247
|
+
Division, Game.division_id == Division.id
|
|
248
|
+
)
|
|
171
249
|
|
|
172
|
-
games_played_stats =
|
|
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
|
+
)
|
|
173
257
|
|
|
174
258
|
# Aggregate goals for each human in each division, excluding goalies
|
|
175
|
-
goals_query =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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":
|
|
182
276
|
goals_query = goals_query.join(Division, Game.division_id == Division.id)
|
|
183
|
-
|
|
184
|
-
goals_stats =
|
|
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
|
+
)
|
|
185
283
|
|
|
186
284
|
# Aggregate assists for each human in each division, excluding goalies
|
|
187
|
-
assists_query =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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":
|
|
194
301
|
assists_query = assists_query.join(Division, Game.division_id == Division.id)
|
|
195
|
-
|
|
196
|
-
assists_stats =
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
+
)
|
|
208
338
|
|
|
209
339
|
# Aggregate penalties for each human in each division, excluding goalies
|
|
210
|
-
penalties_query =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
+
)
|
|
221
371
|
|
|
222
372
|
# Combine the results
|
|
223
373
|
stats_dict = {}
|
|
@@ -227,65 +377,71 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
|
|
|
227
377
|
key = (aggregation_id, stat.human_id)
|
|
228
378
|
if key not in stats_dict:
|
|
229
379
|
stats_dict[key] = {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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,
|
|
248
398
|
}
|
|
249
|
-
stats_dict[key][
|
|
250
|
-
stats_dict[key][
|
|
251
|
-
stats_dict[key][
|
|
252
|
-
stats_dict[key][
|
|
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)
|
|
253
403
|
|
|
254
404
|
# Filter out entries with games_played less than min_games
|
|
255
|
-
stats_dict = {
|
|
405
|
+
stats_dict = {
|
|
406
|
+
key: value
|
|
407
|
+
for key, value in stats_dict.items()
|
|
408
|
+
if value["games_played"] >= min_games
|
|
409
|
+
}
|
|
256
410
|
|
|
257
411
|
for stat in goals_stats:
|
|
258
412
|
key = (aggregation_id, stat.human_id)
|
|
259
413
|
if key in stats_dict:
|
|
260
|
-
stats_dict[key][
|
|
261
|
-
stats_dict[key][
|
|
414
|
+
stats_dict[key]["goals"] += stat.goals
|
|
415
|
+
stats_dict[key]["points"] += stat.goals # Update points
|
|
262
416
|
|
|
263
417
|
for stat in assists_stats:
|
|
264
418
|
key = (aggregation_id, stat.human_id)
|
|
265
419
|
if key in stats_dict:
|
|
266
|
-
stats_dict[key][
|
|
267
|
-
stats_dict[key][
|
|
420
|
+
stats_dict[key]["assists"] += stat.assists
|
|
421
|
+
stats_dict[key]["points"] += stat.assists # Update points
|
|
268
422
|
|
|
269
423
|
for stat in assists_stats_2:
|
|
270
424
|
key = (aggregation_id, stat.human_id)
|
|
271
425
|
if key in stats_dict:
|
|
272
|
-
stats_dict[key][
|
|
273
|
-
stats_dict[key][
|
|
426
|
+
stats_dict[key]["assists"] += stat.assists
|
|
427
|
+
stats_dict[key]["points"] += stat.assists # Update points
|
|
274
428
|
|
|
275
429
|
for stat in penalties_stats:
|
|
276
430
|
key = (aggregation_id, stat.human_id)
|
|
277
431
|
if key in stats_dict:
|
|
278
|
-
stats_dict[key][
|
|
279
|
-
stats_dict[key][
|
|
432
|
+
stats_dict[key]["penalties"] += stat.penalties
|
|
433
|
+
stats_dict[key]["gm_penalties"] += stat.gm_penalties # Update GM penalties
|
|
280
434
|
|
|
281
435
|
# Calculate per game stats (using games_with_stats as denominator for accuracy)
|
|
282
436
|
for key, stat in stats_dict.items():
|
|
283
|
-
if stat[
|
|
284
|
-
stat[
|
|
285
|
-
stat[
|
|
286
|
-
stat[
|
|
287
|
-
stat[
|
|
288
|
-
stat[
|
|
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
|
|
289
445
|
|
|
290
446
|
# Ensure all keys have valid human_id values
|
|
291
447
|
stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
|
|
@@ -293,79 +449,126 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
|
|
|
293
449
|
# Populate first_game_id and last_game_id
|
|
294
450
|
# Only show progress for "All Orgs" with no window (all-time stats) - the slowest case
|
|
295
451
|
total_players = len(stats_dict)
|
|
296
|
-
if
|
|
297
|
-
|
|
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
|
+
)
|
|
298
461
|
for idx, (key, stat) in enumerate(stats_dict.items()):
|
|
299
|
-
all_game_ids = stat[
|
|
462
|
+
all_game_ids = stat["game_ids"]
|
|
300
463
|
if all_game_ids:
|
|
301
|
-
first_game =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
|
306
481
|
progress.update(idx + 1)
|
|
307
482
|
else:
|
|
308
483
|
# No progress tracking for all other cases
|
|
309
484
|
for key, stat in stats_dict.items():
|
|
310
|
-
all_game_ids = stat[
|
|
485
|
+
all_game_ids = stat["game_ids"]
|
|
311
486
|
if all_game_ids:
|
|
312
|
-
first_game =
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
|
316
501
|
|
|
317
502
|
# Calculate current point streak (only for all-time stats)
|
|
318
503
|
if aggregation_window is None:
|
|
319
504
|
total_players = len(stats_dict)
|
|
320
505
|
# Show progress for All Orgs - this is the slowest part
|
|
321
|
-
if
|
|
322
|
-
|
|
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
|
+
)
|
|
323
514
|
for idx, (key, stat) in enumerate(stats_dict.items()):
|
|
324
515
|
agg_id, human_id = key
|
|
325
|
-
streak_length, avg_points = calculate_current_point_streak(
|
|
326
|
-
|
|
327
|
-
|
|
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
|
|
328
521
|
if (idx + 1) % 100 == 0 or (idx + 1) == total_players:
|
|
329
522
|
progress.update(idx + 1)
|
|
330
523
|
else:
|
|
331
524
|
for key, stat in stats_dict.items():
|
|
332
525
|
agg_id, human_id = key
|
|
333
|
-
streak_length, avg_points = calculate_current_point_streak(
|
|
334
|
-
|
|
335
|
-
|
|
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
|
|
336
531
|
|
|
337
532
|
# Calculate total_in_rank
|
|
338
533
|
total_in_rank = len(stats_dict)
|
|
339
534
|
|
|
340
535
|
# Assign ranks within each level
|
|
341
536
|
def assign_ranks(stats_dict, field):
|
|
342
|
-
sorted_stats = sorted(
|
|
537
|
+
sorted_stats = sorted(
|
|
538
|
+
stats_dict.items(), key=lambda x: x[1][field], reverse=True
|
|
539
|
+
)
|
|
343
540
|
for rank, (key, stat) in enumerate(sorted_stats, start=1):
|
|
344
|
-
stats_dict[key][f
|
|
345
|
-
|
|
346
|
-
assign_ranks(stats_dict,
|
|
347
|
-
assign_ranks(stats_dict,
|
|
348
|
-
assign_ranks(stats_dict,
|
|
349
|
-
assign_ranks(stats_dict,
|
|
350
|
-
assign_ranks(stats_dict,
|
|
351
|
-
assign_ranks(stats_dict,
|
|
352
|
-
assign_ranks(stats_dict,
|
|
353
|
-
assign_ranks(stats_dict,
|
|
354
|
-
assign_ranks(stats_dict,
|
|
355
|
-
assign_ranks(stats_dict,
|
|
356
|
-
assign_ranks(stats_dict,
|
|
357
|
-
assign_ranks(stats_dict,
|
|
358
|
-
assign_ranks(
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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")
|
|
362
563
|
|
|
363
564
|
# Debug output for specific human
|
|
364
565
|
if debug_human_id:
|
|
365
566
|
if any(key[1] == debug_human_id for key in stats_dict):
|
|
366
567
|
human = session.query(Human).filter(Human.id == debug_human_id).first()
|
|
367
568
|
human_name = f"{human.first_name} {human.last_name}" if human else "Unknown"
|
|
368
|
-
print(
|
|
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
|
+
)
|
|
369
572
|
for key, stat in stats_dict.items():
|
|
370
573
|
if key[1] == debug_human_id:
|
|
371
574
|
for k, v in stat.items():
|
|
@@ -376,47 +579,75 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
|
|
|
376
579
|
batch_size = 1000
|
|
377
580
|
for i, (key, stat) in enumerate(stats_dict.items(), 1):
|
|
378
581
|
aggregation_id, human_id = key
|
|
379
|
-
goals_per_game =
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
|
384
603
|
skater_stat = StatsModel(
|
|
385
604
|
aggregation_id=aggregation_id,
|
|
386
605
|
human_id=human_id,
|
|
387
|
-
games_played=stat[
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
|
397
622
|
goals_per_game=goals_per_game,
|
|
398
623
|
points_per_game=points_per_game,
|
|
399
624
|
assists_per_game=assists_per_game,
|
|
400
625
|
penalties_per_game=penalties_per_game,
|
|
401
626
|
gm_penalties_per_game=gm_penalties_per_game, # Include GM penalties per game
|
|
402
|
-
games_played_rank=stat[
|
|
403
|
-
goals_rank=stat[
|
|
404
|
-
assists_rank=stat[
|
|
405
|
-
points_rank=stat[
|
|
406
|
-
penalties_rank=stat[
|
|
407
|
-
gm_penalties_rank=stat[
|
|
408
|
-
goals_per_game_rank=stat[
|
|
409
|
-
points_per_game_rank=stat[
|
|
410
|
-
assists_per_game_rank=stat[
|
|
411
|
-
penalties_per_game_rank=stat[
|
|
412
|
-
gm_penalties_per_game_rank=stat[
|
|
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
|
|
413
640
|
total_in_rank=total_in_rank,
|
|
414
|
-
current_point_streak=stat.get(
|
|
415
|
-
current_point_streak_rank=stat.get(
|
|
416
|
-
current_point_streak_avg_points=stat.get(
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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"],
|
|
420
651
|
)
|
|
421
652
|
session.add(skater_stat)
|
|
422
653
|
# Commit in batches
|
|
@@ -424,6 +655,7 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
|
|
|
424
655
|
session.commit()
|
|
425
656
|
session.commit()
|
|
426
657
|
|
|
658
|
+
|
|
427
659
|
def run_aggregate_skater_stats():
|
|
428
660
|
session = create_session("boss")
|
|
429
661
|
human_id_to_debug = None
|
|
@@ -434,51 +666,142 @@ def run_aggregate_skater_stats():
|
|
|
434
666
|
|
|
435
667
|
for org_id in org_ids:
|
|
436
668
|
division_ids = get_all_division_ids_for_org(session, org_id)
|
|
437
|
-
org_name =
|
|
438
|
-
|
|
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
|
+
|
|
439
676
|
if human_id_to_debug is None and division_ids:
|
|
440
677
|
# Process divisions with progress tracking
|
|
441
|
-
progress = create_progress_tracker(
|
|
678
|
+
progress = create_progress_tracker(
|
|
679
|
+
len(division_ids),
|
|
680
|
+
f"Processing {len(division_ids)} divisions for {org_name}",
|
|
681
|
+
)
|
|
442
682
|
for i, division_id in enumerate(division_ids):
|
|
443
|
-
aggregate_skater_stats(
|
|
444
|
-
|
|
445
|
-
|
|
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
|
+
)
|
|
446
703
|
progress.update(i + 1)
|
|
447
704
|
else:
|
|
448
705
|
# Debug mode or no divisions - process without progress tracking
|
|
449
706
|
for division_id in division_ids:
|
|
450
|
-
aggregate_skater_stats(
|
|
451
|
-
|
|
452
|
-
|
|
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
|
+
)
|
|
453
727
|
|
|
454
728
|
# Process org-level stats with progress tracking
|
|
455
729
|
if human_id_to_debug is None:
|
|
456
|
-
org_progress = create_progress_tracker(
|
|
457
|
-
|
|
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
|
+
)
|
|
458
739
|
org_progress.update(1)
|
|
459
|
-
aggregate_skater_stats(
|
|
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
|
+
)
|
|
460
747
|
org_progress.update(2)
|
|
461
|
-
aggregate_skater_stats(
|
|
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
|
+
)
|
|
462
755
|
org_progress.update(3)
|
|
463
756
|
else:
|
|
464
|
-
aggregate_skater_stats(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
+
|
|
468
778
|
# Aggregate by level
|
|
469
779
|
level_ids = session.query(Division.level_id).distinct().all()
|
|
470
780
|
level_ids = [level_id[0] for level_id in level_ids if level_id[0] is not None]
|
|
471
|
-
|
|
781
|
+
|
|
472
782
|
if human_id_to_debug is None and level_ids:
|
|
473
783
|
# Process levels with progress tracking
|
|
474
|
-
level_progress = create_progress_tracker(
|
|
784
|
+
level_progress = create_progress_tracker(
|
|
785
|
+
len(level_ids), f"Processing {len(level_ids)} skill levels"
|
|
786
|
+
)
|
|
475
787
|
for i, level_id in enumerate(level_ids):
|
|
476
|
-
aggregate_skater_stats(
|
|
788
|
+
aggregate_skater_stats(
|
|
789
|
+
session,
|
|
790
|
+
aggregation_type="level",
|
|
791
|
+
aggregation_id=level_id,
|
|
792
|
+
debug_human_id=human_id_to_debug,
|
|
793
|
+
)
|
|
477
794
|
level_progress.update(i + 1)
|
|
478
795
|
else:
|
|
479
796
|
# Debug mode or no levels - process without progress tracking
|
|
480
797
|
for level_id in level_ids:
|
|
481
|
-
aggregate_skater_stats(
|
|
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
|
+
|
|
482
805
|
|
|
483
806
|
if __name__ == "__main__":
|
|
484
807
|
run_aggregate_skater_stats()
|