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.
- hockey_blast_common_lib/aggregate_all_stats.py +7 -4
- hockey_blast_common_lib/aggregate_goalie_stats.py +303 -113
- hockey_blast_common_lib/aggregate_h2h_stats.py +64 -33
- hockey_blast_common_lib/aggregate_human_stats.py +566 -281
- hockey_blast_common_lib/aggregate_referee_stats.py +287 -145
- hockey_blast_common_lib/aggregate_s2s_stats.py +85 -25
- hockey_blast_common_lib/aggregate_scorekeeper_stats.py +231 -119
- hockey_blast_common_lib/aggregate_skater_stats.py +595 -240
- 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 -149
- 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 +89 -25
- hockey_blast_common_lib/wsgi.py +7 -5
- {hockey_blast_common_lib-0.1.62.dist-info → hockey_blast_common_lib-0.1.64.dist-info}/METADATA +1 -1
- hockey_blast_common_lib-0.1.64.dist-info/RECORD +29 -0
- hockey_blast_common_lib-0.1.62.dist-info/RECORD +0 -28
- {hockey_blast_common_lib-0.1.62.dist-info → hockey_blast_common_lib-0.1.64.dist-info}/WHEEL +0 -0
- {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
|
|
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,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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 =
|
|
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
|
-
|
|
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 ==
|
|
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 =
|
|
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(
|
|
140
|
+
print(
|
|
141
|
+
f"Aggregating skater stats for {aggregation_name} with window {aggregation_window}..."
|
|
142
|
+
)
|
|
85
143
|
|
|
86
|
-
elif aggregation_type ==
|
|
87
|
-
aggregation_name =
|
|
88
|
-
|
|
89
|
-
|
|
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 ==
|
|
94
|
-
if aggregation_window ==
|
|
155
|
+
if aggregation_type == "org":
|
|
156
|
+
if aggregation_window == "Daily":
|
|
95
157
|
StatsModel = OrgStatsDailySkater
|
|
96
|
-
elif aggregation_window ==
|
|
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 ==
|
|
102
|
-
if aggregation_window ==
|
|
163
|
+
elif aggregation_type == "division":
|
|
164
|
+
if aggregation_window == "Daily":
|
|
103
165
|
StatsModel = DivisionStatsDailySkater
|
|
104
|
-
elif aggregation_window ==
|
|
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 ==
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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
|
-
#
|
|
148
|
-
#
|
|
149
|
-
games_played_query =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
(Game.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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 !=
|
|
165
|
-
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
|
+
)
|
|
166
249
|
|
|
167
|
-
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
|
+
)
|
|
168
257
|
|
|
169
258
|
# Aggregate goals for each human in each division, excluding goalies
|
|
170
|
-
goals_query =
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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 =
|
|
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 =
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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 =
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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 =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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][
|
|
245
|
-
stats_dict[key][
|
|
246
|
-
stats_dict[key][
|
|
247
|
-
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)
|
|
248
403
|
|
|
249
404
|
# Filter out entries with games_played less than min_games
|
|
250
|
-
stats_dict = {
|
|
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][
|
|
256
|
-
stats_dict[key][
|
|
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][
|
|
262
|
-
stats_dict[key][
|
|
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][
|
|
268
|
-
stats_dict[key][
|
|
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][
|
|
274
|
-
stats_dict[key][
|
|
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[
|
|
279
|
-
stat[
|
|
280
|
-
stat[
|
|
281
|
-
stat[
|
|
282
|
-
stat[
|
|
283
|
-
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
|
|
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
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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(
|
|
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
|
|
313
|
-
|
|
314
|
-
assign_ranks(stats_dict,
|
|
315
|
-
assign_ranks(stats_dict,
|
|
316
|
-
assign_ranks(stats_dict,
|
|
317
|
-
assign_ranks(stats_dict,
|
|
318
|
-
assign_ranks(stats_dict,
|
|
319
|
-
assign_ranks(stats_dict,
|
|
320
|
-
assign_ranks(stats_dict,
|
|
321
|
-
assign_ranks(stats_dict,
|
|
322
|
-
assign_ranks(stats_dict,
|
|
323
|
-
assign_ranks(stats_dict,
|
|
324
|
-
assign_ranks(stats_dict,
|
|
325
|
-
assign_ranks(stats_dict,
|
|
326
|
-
assign_ranks(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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(
|
|
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 =
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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[
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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[
|
|
371
|
-
goals_rank=stat[
|
|
372
|
-
assists_rank=stat[
|
|
373
|
-
points_rank=stat[
|
|
374
|
-
penalties_rank=stat[
|
|
375
|
-
gm_penalties_rank=stat[
|
|
376
|
-
goals_per_game_rank=stat[
|
|
377
|
-
points_per_game_rank=stat[
|
|
378
|
-
assists_per_game_rank=stat[
|
|
379
|
-
penalties_per_game_rank=stat[
|
|
380
|
-
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
|
|
381
640
|
total_in_rank=total_in_rank,
|
|
382
|
-
current_point_streak=stat.get(
|
|
383
|
-
current_point_streak_rank=stat.get(
|
|
384
|
-
current_point_streak_avg_points=stat.get(
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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 =
|
|
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(
|
|
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(
|
|
412
|
-
|
|
413
|
-
|
|
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(
|
|
419
|
-
|
|
420
|
-
|
|
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(
|
|
425
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
433
|
-
|
|
434
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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()
|