hockey-blast-common-lib 0.1.65__tar.gz
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-0.1.65/MANIFEST.in +2 -0
- hockey_blast_common_lib-0.1.65/PKG-INFO +11 -0
- hockey_blast_common_lib-0.1.65/README.md +1 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/__init__.py +0 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/aggregate_all_stats.py +34 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/aggregate_goalie_stats.py +455 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/aggregate_h2h_stats.py +262 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/aggregate_human_stats.py +763 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/aggregate_referee_stats.py +445 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/aggregate_s2s_stats.py +203 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/aggregate_scorekeeper_stats.py +367 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/aggregate_skater_stats.py +807 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/assign_skater_skill.py +79 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/db_connection.py +99 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/dump_sample_db.sh +28 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/embedding_utils.py +309 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/h2h_models.py +201 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz +0 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/models.py +573 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/options.py +59 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/progress_utils.py +113 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/restore_sample_db.sh +44 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/skills_in_divisions.py +266 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/skills_propagation.py +502 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/stats_models.py +924 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/stats_utils.py +9 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/utils.py +215 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib/wsgi.py +24 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib.egg-info/PKG-INFO +11 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib.egg-info/SOURCES.txt +34 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib.egg-info/dependency_links.txt +1 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib.egg-info/requires.txt +4 -0
- hockey_blast_common_lib-0.1.65/hockey_blast_common_lib.egg-info/top_level.txt +1 -0
- hockey_blast_common_lib-0.1.65/pyproject.toml +13 -0
- hockey_blast_common_lib-0.1.65/setup.cfg +4 -0
- hockey_blast_common_lib-0.1.65/setup.py +18 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: hockey-blast-common-lib
|
|
3
|
+
Version: 0.1.65
|
|
4
|
+
Summary: Common library for shared functionality and DB models
|
|
5
|
+
Author: Pavel Kletskov
|
|
6
|
+
Author-email: kletskov@gmail.com
|
|
7
|
+
Requires-Python: >=3.7
|
|
8
|
+
Requires-Dist: setuptools
|
|
9
|
+
Requires-Dist: Flask-SQLAlchemy
|
|
10
|
+
Requires-Dist: SQLAlchemy
|
|
11
|
+
Requires-Dist: requests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Library for DB models and some common utils to use hockey-blast frontend and backend
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# Add the package directory to the Python path
|
|
5
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
6
|
+
|
|
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
|
+
from hockey_blast_common_lib.aggregate_referee_stats import run_aggregate_referee_stats
|
|
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
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
print("Running aggregate_skater_stats...", flush=True)
|
|
17
|
+
run_aggregate_skater_stats()
|
|
18
|
+
print("Finished running aggregate_skater_stats\n")
|
|
19
|
+
|
|
20
|
+
print("Running aggregate_goalie_stats...", flush=True)
|
|
21
|
+
run_aggregate_goalie_stats()
|
|
22
|
+
print("Finished running aggregate_goalie_stats\n")
|
|
23
|
+
|
|
24
|
+
print("Running aggregate_referee_stats...", flush=True)
|
|
25
|
+
run_aggregate_referee_stats()
|
|
26
|
+
print("Finished running aggregate_referee_stats\n")
|
|
27
|
+
|
|
28
|
+
print("Running aggregate_scorekeeper_stats...", flush=True)
|
|
29
|
+
run_aggregate_scorekeeper_stats()
|
|
30
|
+
print("Finished running aggregate_scorekeeper_stats\n")
|
|
31
|
+
|
|
32
|
+
print("Running aggregate_human_stats...", flush=True)
|
|
33
|
+
run_aggregate_human_stats()
|
|
34
|
+
print("Finished running aggregate_human_stats\n")
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# Add the package directory to the Python path
|
|
5
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
6
|
+
|
|
7
|
+
from datetime import datetime, timedelta
|
|
8
|
+
|
|
9
|
+
import sqlalchemy
|
|
10
|
+
from sqlalchemy.sql import func
|
|
11
|
+
|
|
12
|
+
from hockey_blast_common_lib.db_connection import create_session
|
|
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
|
+
)
|
|
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
|
+
)
|
|
42
|
+
|
|
43
|
+
# Import status constants for game filtering
|
|
44
|
+
FINAL_STATUS = "Final"
|
|
45
|
+
FINAL_SO_STATUS = "Final(SO)"
|
|
46
|
+
FORFEIT_STATUS = "FORFEIT"
|
|
47
|
+
NOEVENTS_STATUS = "NOEVENTS"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def aggregate_goalie_stats(
|
|
51
|
+
session,
|
|
52
|
+
aggregation_type,
|
|
53
|
+
aggregation_id,
|
|
54
|
+
debug_human_id=None,
|
|
55
|
+
aggregation_window=None,
|
|
56
|
+
):
|
|
57
|
+
human_ids_to_filter = get_non_human_ids(session)
|
|
58
|
+
|
|
59
|
+
# Get the name of the aggregation, for debug purposes
|
|
60
|
+
if aggregation_type == "org":
|
|
61
|
+
if aggregation_id == ALL_ORGS_ID:
|
|
62
|
+
aggregation_name = "All Orgs"
|
|
63
|
+
filter_condition = sqlalchemy.true() # No filter for organization
|
|
64
|
+
else:
|
|
65
|
+
aggregation_name = (
|
|
66
|
+
session.query(Organization)
|
|
67
|
+
.filter(Organization.id == aggregation_id)
|
|
68
|
+
.first()
|
|
69
|
+
.organization_name
|
|
70
|
+
)
|
|
71
|
+
filter_condition = Game.org_id == aggregation_id
|
|
72
|
+
print(
|
|
73
|
+
f"Aggregating goalie stats for {aggregation_name} with window {aggregation_window}..."
|
|
74
|
+
)
|
|
75
|
+
if aggregation_window == "Daily":
|
|
76
|
+
StatsModel = OrgStatsDailyGoalie
|
|
77
|
+
elif aggregation_window == "Weekly":
|
|
78
|
+
StatsModel = OrgStatsWeeklyGoalie
|
|
79
|
+
else:
|
|
80
|
+
StatsModel = OrgStatsGoalie
|
|
81
|
+
min_games = MIN_GAMES_FOR_ORG_STATS
|
|
82
|
+
elif aggregation_type == "division":
|
|
83
|
+
if aggregation_window == "Daily":
|
|
84
|
+
StatsModel = DivisionStatsDailyGoalie
|
|
85
|
+
elif aggregation_window == "Weekly":
|
|
86
|
+
StatsModel = DivisionStatsWeeklyGoalie
|
|
87
|
+
else:
|
|
88
|
+
StatsModel = DivisionStatsGoalie
|
|
89
|
+
min_games = MIN_GAMES_FOR_DIVISION_STATS
|
|
90
|
+
filter_condition = Game.division_id == aggregation_id
|
|
91
|
+
elif aggregation_type == "level":
|
|
92
|
+
StatsModel = LevelStatsGoalie
|
|
93
|
+
min_games = MIN_GAMES_FOR_LEVEL_STATS
|
|
94
|
+
filter_condition = Division.level_id == aggregation_id
|
|
95
|
+
# Add filter to only include games for the last 5 years
|
|
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
|
+
)
|
|
103
|
+
filter_condition = filter_condition & level_window_filter
|
|
104
|
+
else:
|
|
105
|
+
raise ValueError("Invalid aggregation type")
|
|
106
|
+
|
|
107
|
+
# Delete existing items from the stats table
|
|
108
|
+
session.query(StatsModel).filter(
|
|
109
|
+
StatsModel.aggregation_id == aggregation_id
|
|
110
|
+
).delete()
|
|
111
|
+
session.commit()
|
|
112
|
+
|
|
113
|
+
# Apply aggregation window filter
|
|
114
|
+
if aggregation_window:
|
|
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
|
+
)
|
|
120
|
+
start_datetime = get_start_datetime(last_game_datetime_str, aggregation_window)
|
|
121
|
+
if start_datetime:
|
|
122
|
+
game_window_filter = func.cast(
|
|
123
|
+
func.concat(Game.date, " ", Game.time), sqlalchemy.types.TIMESTAMP
|
|
124
|
+
).between(start_datetime, last_game_datetime_str)
|
|
125
|
+
filter_condition = filter_condition & game_window_filter
|
|
126
|
+
else:
|
|
127
|
+
# print(f"Warning: No valid start datetime for aggregation window '{aggregation_window}' for {aggregation_name}. No games will be included.")
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
# Aggregate games played, goals allowed, and shots faced for each goalie using GoalieSaves table
|
|
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
|
+
)
|
|
156
|
+
|
|
157
|
+
# Filter for specific human_id if provided
|
|
158
|
+
if debug_human_id:
|
|
159
|
+
query = query.filter(GoalieSaves.goalie_id == debug_human_id)
|
|
160
|
+
|
|
161
|
+
goalie_stats = query.group_by(GoalieSaves.goalie_id).all()
|
|
162
|
+
|
|
163
|
+
# Combine the results
|
|
164
|
+
stats_dict = {}
|
|
165
|
+
for stat in goalie_stats:
|
|
166
|
+
if stat.human_id in human_ids_to_filter:
|
|
167
|
+
continue
|
|
168
|
+
key = (aggregation_id, stat.human_id)
|
|
169
|
+
if key not in stats_dict:
|
|
170
|
+
stats_dict[key] = {
|
|
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,
|
|
181
|
+
}
|
|
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)
|
|
192
|
+
|
|
193
|
+
# Filter out entries with games_played less than min_games
|
|
194
|
+
stats_dict = {
|
|
195
|
+
key: value
|
|
196
|
+
for key, value in stats_dict.items()
|
|
197
|
+
if value["games_played"] >= min_games
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# Calculate per game stats (using games_with_stats as denominator for accuracy)
|
|
201
|
+
for key, stat in stats_dict.items():
|
|
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
|
+
)
|
|
211
|
+
|
|
212
|
+
# Ensure all keys have valid human_id values
|
|
213
|
+
stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
|
|
214
|
+
|
|
215
|
+
# Populate first_game_id and last_game_id
|
|
216
|
+
for key, stat in stats_dict.items():
|
|
217
|
+
all_game_ids = stat["game_ids"]
|
|
218
|
+
if all_game_ids:
|
|
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
|
|
233
|
+
|
|
234
|
+
# Calculate total_in_rank
|
|
235
|
+
total_in_rank = len(stats_dict)
|
|
236
|
+
|
|
237
|
+
# Assign ranks within each level
|
|
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")
|
|
245
|
+
|
|
246
|
+
# Debug output for specific human
|
|
247
|
+
if debug_human_id:
|
|
248
|
+
if any(key[1] == debug_human_id for key in stats_dict):
|
|
249
|
+
human = session.query(Human).filter(Human.id == debug_human_id).first()
|
|
250
|
+
human_name = f"{human.first_name} {human.last_name}" if human else "Unknown"
|
|
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
|
+
)
|
|
254
|
+
for key, stat in stats_dict.items():
|
|
255
|
+
if key[1] == debug_human_id:
|
|
256
|
+
for k, v in stat.items():
|
|
257
|
+
print(f"{k}: {v}")
|
|
258
|
+
|
|
259
|
+
# Insert aggregated stats into the appropriate table with progress output
|
|
260
|
+
batch_size = 1000
|
|
261
|
+
for i, (key, stat) in enumerate(stats_dict.items(), 1):
|
|
262
|
+
aggregation_id, human_id = key
|
|
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
|
+
)
|
|
273
|
+
goalie_stat = StatsModel(
|
|
274
|
+
aggregation_id=aggregation_id,
|
|
275
|
+
human_id=human_id,
|
|
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"],
|
|
289
|
+
goals_allowed_per_game=goals_allowed_per_game,
|
|
290
|
+
save_percentage=save_percentage,
|
|
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"],
|
|
296
|
+
total_in_rank=total_in_rank,
|
|
297
|
+
first_game_id=stat["first_game_id"],
|
|
298
|
+
last_game_id=stat["last_game_id"],
|
|
299
|
+
)
|
|
300
|
+
session.add(goalie_stat)
|
|
301
|
+
# Commit in batches
|
|
302
|
+
if i % batch_size == 0:
|
|
303
|
+
session.commit()
|
|
304
|
+
session.commit()
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def run_aggregate_goalie_stats():
|
|
308
|
+
session = create_session("boss")
|
|
309
|
+
human_id_to_debug = None
|
|
310
|
+
|
|
311
|
+
# Get all org_id present in the Organization table
|
|
312
|
+
org_ids = session.query(Organization.id).all()
|
|
313
|
+
org_ids = [org_id[0] for org_id in org_ids]
|
|
314
|
+
|
|
315
|
+
for org_id in org_ids:
|
|
316
|
+
division_ids = get_all_division_ids_for_org(session, org_id)
|
|
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
|
+
|
|
324
|
+
if human_id_to_debug is None and division_ids:
|
|
325
|
+
# Process divisions with progress tracking
|
|
326
|
+
progress = create_progress_tracker(
|
|
327
|
+
len(division_ids),
|
|
328
|
+
f"Processing {len(division_ids)} divisions for {org_name}",
|
|
329
|
+
)
|
|
330
|
+
for i, division_id in enumerate(division_ids):
|
|
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
|
+
)
|
|
351
|
+
progress.update(i + 1)
|
|
352
|
+
else:
|
|
353
|
+
# Debug mode or no divisions - process without progress tracking
|
|
354
|
+
for division_id in division_ids:
|
|
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
|
+
)
|
|
375
|
+
|
|
376
|
+
# Process org-level stats with progress tracking
|
|
377
|
+
if human_id_to_debug is None:
|
|
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
|
+
)
|
|
387
|
+
org_progress.update(1)
|
|
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
|
+
)
|
|
395
|
+
org_progress.update(2)
|
|
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
|
+
)
|
|
403
|
+
org_progress.update(3)
|
|
404
|
+
else:
|
|
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
|
+
|
|
426
|
+
# Aggregate by level
|
|
427
|
+
level_ids = session.query(Division.level_id).distinct().all()
|
|
428
|
+
level_ids = [level_id[0] for level_id in level_ids if level_id[0] is not None]
|
|
429
|
+
|
|
430
|
+
if human_id_to_debug is None and level_ids:
|
|
431
|
+
# Process levels with progress tracking
|
|
432
|
+
level_progress = create_progress_tracker(
|
|
433
|
+
len(level_ids), f"Processing {len(level_ids)} skill levels"
|
|
434
|
+
)
|
|
435
|
+
for i, level_id in enumerate(level_ids):
|
|
436
|
+
aggregate_goalie_stats(
|
|
437
|
+
session,
|
|
438
|
+
aggregation_type="level",
|
|
439
|
+
aggregation_id=level_id,
|
|
440
|
+
debug_human_id=human_id_to_debug,
|
|
441
|
+
)
|
|
442
|
+
level_progress.update(i + 1)
|
|
443
|
+
else:
|
|
444
|
+
# Debug mode or no levels - process without progress tracking
|
|
445
|
+
for level_id in level_ids:
|
|
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
|
+
|
|
453
|
+
|
|
454
|
+
if __name__ == "__main__":
|
|
455
|
+
run_aggregate_goalie_stats()
|