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