hockey-blast-common-lib 0.1.60__tar.gz → 0.1.62__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.60 → hockey_blast_common_lib-0.1.62}/PKG-INFO +1 -1
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/aggregate_goalie_stats.py +51 -25
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/aggregate_human_stats.py +18 -18
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/aggregate_referee_stats.py +65 -27
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/aggregate_scorekeeper_stats.py +37 -13
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/aggregate_skater_stats.py +56 -30
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/options.py +0 -11
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/progress_utils.py +23 -9
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/stats_models.py +20 -4
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/utils.py +18 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib.egg-info/PKG-INFO +1 -1
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/setup.py +1 -1
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/MANIFEST.in +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/README.md +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/__init__.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/aggregate_all_stats.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/aggregate_h2h_stats.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/aggregate_s2s_stats.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/assign_skater_skill.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/db_connection.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/dump_sample_db.sh +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/h2h_models.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/models.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/restore_sample_db.sh +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/skills_in_divisions.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/skills_propagation.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/stats_utils.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/wsgi.py +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib.egg-info/SOURCES.txt +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib.egg-info/dependency_links.txt +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib.egg-info/requires.txt +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib.egg-info/top_level.txt +0 -0
- {hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/setup.cfg +0 -0
@@ -9,15 +9,21 @@ import sqlalchemy
|
|
9
9
|
from hockey_blast_common_lib.models import Game, Organization, Division, Human, GoalieSaves
|
10
10
|
from hockey_blast_common_lib.stats_models import OrgStatsGoalie, DivisionStatsGoalie, OrgStatsWeeklyGoalie, OrgStatsDailyGoalie, DivisionStatsWeeklyGoalie, DivisionStatsDailyGoalie, LevelStatsGoalie
|
11
11
|
from hockey_blast_common_lib.db_connection import create_session
|
12
|
-
from sqlalchemy.sql import func
|
13
|
-
from hockey_blast_common_lib.options import
|
14
|
-
from hockey_blast_common_lib.utils import
|
12
|
+
from sqlalchemy.sql import func, case
|
13
|
+
from hockey_blast_common_lib.options import MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, MIN_GAMES_FOR_LEVEL_STATS
|
14
|
+
from hockey_blast_common_lib.utils import get_non_human_ids, get_all_division_ids_for_org, get_start_datetime
|
15
15
|
from hockey_blast_common_lib.utils import assign_ranks
|
16
16
|
from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
|
17
17
|
from hockey_blast_common_lib.progress_utils import create_progress_tracker
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
# Import status constants for game filtering
|
20
|
+
FINAL_STATUS = "Final"
|
21
|
+
FINAL_SO_STATUS = "Final(SO)"
|
22
|
+
FORFEIT_STATUS = "FORFEIT"
|
23
|
+
NOEVENTS_STATUS = "NOEVENTS"
|
24
|
+
|
25
|
+
def aggregate_goalie_stats(session, aggregation_type, aggregation_id, debug_human_id=None, aggregation_window=None):
|
26
|
+
human_ids_to_filter = get_non_human_ids(session)
|
21
27
|
|
22
28
|
# Get the name of the aggregation, for debug purposes
|
23
29
|
if aggregation_type == 'org':
|
@@ -72,9 +78,19 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
72
78
|
|
73
79
|
|
74
80
|
# Aggregate games played, goals allowed, and shots faced for each goalie using GoalieSaves table
|
81
|
+
# games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
82
|
+
# games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
|
75
83
|
query = session.query(
|
76
84
|
GoalieSaves.goalie_id.label('human_id'),
|
77
|
-
func.count(GoalieSaves.game_id).label('games_played'),
|
85
|
+
func.count(GoalieSaves.game_id).label('games_played'), # DEPRECATED - will be replaced by games_participated
|
86
|
+
func.sum(case(
|
87
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
|
88
|
+
else_=0
|
89
|
+
)).label('games_participated'),
|
90
|
+
func.sum(case(
|
91
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
|
92
|
+
else_=0
|
93
|
+
)).label('games_with_stats'),
|
78
94
|
func.sum(GoalieSaves.goals_allowed).label('goals_allowed'),
|
79
95
|
func.sum(GoalieSaves.shots_against).label('shots_faced'),
|
80
96
|
func.array_agg(GoalieSaves.game_id).label('game_ids')
|
@@ -94,7 +110,9 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
94
110
|
key = (aggregation_id, stat.human_id)
|
95
111
|
if key not in stats_dict:
|
96
112
|
stats_dict[key] = {
|
97
|
-
'games_played': 0,
|
113
|
+
'games_played': 0, # DEPRECATED - for backward compatibility
|
114
|
+
'games_participated': 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
115
|
+
'games_with_stats': 0, # Games with full stats: FINAL, FINAL_SO only
|
98
116
|
'goals_allowed': 0,
|
99
117
|
'shots_faced': 0,
|
100
118
|
'goals_allowed_per_game': 0.0,
|
@@ -104,6 +122,8 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
104
122
|
'last_game_id': None
|
105
123
|
}
|
106
124
|
stats_dict[key]['games_played'] += stat.games_played
|
125
|
+
stats_dict[key]['games_participated'] += stat.games_participated
|
126
|
+
stats_dict[key]['games_with_stats'] += stat.games_with_stats
|
107
127
|
stats_dict[key]['goals_allowed'] += stat.goals_allowed if stat.goals_allowed is not None else 0
|
108
128
|
stats_dict[key]['shots_faced'] += stat.shots_faced if stat.shots_faced is not None else 0
|
109
129
|
stats_dict[key]['game_ids'].extend(stat.game_ids)
|
@@ -111,10 +131,10 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
111
131
|
# Filter out entries with games_played less than min_games
|
112
132
|
stats_dict = {key: value for key, value in stats_dict.items() if value['games_played'] >= min_games}
|
113
133
|
|
114
|
-
# Calculate per game stats
|
134
|
+
# Calculate per game stats (using games_with_stats as denominator for accuracy)
|
115
135
|
for key, stat in stats_dict.items():
|
116
|
-
if stat['
|
117
|
-
stat['goals_allowed_per_game'] = stat['goals_allowed'] / stat['
|
136
|
+
if stat['games_with_stats'] > 0:
|
137
|
+
stat['goals_allowed_per_game'] = stat['goals_allowed'] / stat['games_with_stats']
|
118
138
|
stat['save_percentage'] = (stat['shots_faced'] - stat['goals_allowed']) / stat['shots_faced'] if stat['shots_faced'] > 0 else 0.0
|
119
139
|
|
120
140
|
# Ensure all keys have valid human_id values
|
@@ -134,6 +154,8 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
134
154
|
|
135
155
|
# Assign ranks within each level
|
136
156
|
assign_ranks(stats_dict, 'games_played')
|
157
|
+
assign_ranks(stats_dict, 'games_participated') # Rank by total participation
|
158
|
+
assign_ranks(stats_dict, 'games_with_stats') # Rank by games with full stats
|
137
159
|
assign_ranks(stats_dict, 'goals_allowed', reverse_rank=True)
|
138
160
|
assign_ranks(stats_dict, 'shots_faced')
|
139
161
|
assign_ranks(stats_dict, 'goals_allowed_per_game', reverse_rank=True)
|
@@ -159,7 +181,11 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
159
181
|
goalie_stat = StatsModel(
|
160
182
|
aggregation_id=aggregation_id,
|
161
183
|
human_id=human_id,
|
162
|
-
games_played=stat['games_played'],
|
184
|
+
games_played=stat['games_played'], # DEPRECATED - for backward compatibility
|
185
|
+
games_participated=stat['games_participated'], # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
186
|
+
games_participated_rank=stat['games_participated_rank'],
|
187
|
+
games_with_stats=stat['games_with_stats'], # Games with full stats: FINAL, FINAL_SO only
|
188
|
+
games_with_stats_rank=stat['games_with_stats_rank'],
|
163
189
|
goals_allowed=stat['goals_allowed'],
|
164
190
|
shots_faced=stat['shots_faced'],
|
165
191
|
goals_allowed_per_game=goals_allowed_per_game,
|
@@ -195,30 +221,30 @@ def run_aggregate_goalie_stats():
|
|
195
221
|
# Process divisions with progress tracking
|
196
222
|
progress = create_progress_tracker(len(division_ids), f"Processing {len(division_ids)} divisions for {org_name}")
|
197
223
|
for i, division_id in enumerate(division_ids):
|
198
|
-
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id,
|
199
|
-
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id,
|
200
|
-
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id,
|
224
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug)
|
225
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
226
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
201
227
|
progress.update(i + 1)
|
202
228
|
else:
|
203
229
|
# Debug mode or no divisions - process without progress tracking
|
204
230
|
for division_id in division_ids:
|
205
|
-
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id,
|
206
|
-
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id,
|
207
|
-
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id,
|
231
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug)
|
232
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
233
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
208
234
|
|
209
235
|
# Process org-level stats with progress tracking
|
210
236
|
if human_id_to_debug is None:
|
211
237
|
org_progress = create_progress_tracker(3, f"Processing org-level stats for {org_name}")
|
212
|
-
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id,
|
238
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug)
|
213
239
|
org_progress.update(1)
|
214
|
-
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id,
|
240
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
215
241
|
org_progress.update(2)
|
216
|
-
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id,
|
242
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
217
243
|
org_progress.update(3)
|
218
244
|
else:
|
219
|
-
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id,
|
220
|
-
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id,
|
221
|
-
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id,
|
245
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug)
|
246
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
247
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
222
248
|
|
223
249
|
# Aggregate by level
|
224
250
|
level_ids = session.query(Division.level_id).distinct().all()
|
@@ -228,12 +254,12 @@ def run_aggregate_goalie_stats():
|
|
228
254
|
# Process levels with progress tracking
|
229
255
|
level_progress = create_progress_tracker(len(level_ids), f"Processing {len(level_ids)} skill levels")
|
230
256
|
for i, level_id in enumerate(level_ids):
|
231
|
-
aggregate_goalie_stats(session, aggregation_type='level', aggregation_id=level_id,
|
257
|
+
aggregate_goalie_stats(session, aggregation_type='level', aggregation_id=level_id, debug_human_id=human_id_to_debug)
|
232
258
|
level_progress.update(i + 1)
|
233
259
|
else:
|
234
260
|
# Debug mode or no levels - process without progress tracking
|
235
261
|
for level_id in level_ids:
|
236
|
-
aggregate_goalie_stats(session, aggregation_type='level', aggregation_id=level_id,
|
262
|
+
aggregate_goalie_stats(session, aggregation_type='level', aggregation_id=level_id, debug_human_id=human_id_to_debug)
|
237
263
|
|
238
264
|
if __name__ == "__main__":
|
239
265
|
run_aggregate_goalie_stats()
|
@@ -10,15 +10,15 @@ from hockey_blast_common_lib.models import Game, GameRoster, Organization, Divis
|
|
10
10
|
from hockey_blast_common_lib.stats_models import OrgStatsHuman, DivisionStatsHuman, OrgStatsDailyHuman, OrgStatsWeeklyHuman, DivisionStatsDailyHuman, DivisionStatsWeeklyHuman, LevelStatsHuman
|
11
11
|
from hockey_blast_common_lib.db_connection import create_session
|
12
12
|
from sqlalchemy.sql import func, case
|
13
|
-
from hockey_blast_common_lib.options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, MIN_GAMES_FOR_LEVEL_STATS
|
14
|
-
from hockey_blast_common_lib.utils import get_fake_human_for_stats, get_org_id_from_alias,
|
13
|
+
from hockey_blast_common_lib.options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, MIN_GAMES_FOR_LEVEL_STATS
|
14
|
+
from hockey_blast_common_lib.utils import get_fake_human_for_stats, get_org_id_from_alias, get_non_human_ids, get_division_ids_for_last_season_in_all_leagues, get_all_division_ids_for_org
|
15
15
|
from hockey_blast_common_lib.utils import assign_ranks
|
16
16
|
from hockey_blast_common_lib.utils import get_start_datetime
|
17
17
|
from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
|
18
18
|
from hockey_blast_common_lib.progress_utils import create_progress_tracker
|
19
19
|
|
20
|
-
def aggregate_human_stats(session, aggregation_type, aggregation_id,
|
21
|
-
human_ids_to_filter =
|
20
|
+
def aggregate_human_stats(session, aggregation_type, aggregation_id, human_id_filter=None, aggregation_window=None):
|
21
|
+
human_ids_to_filter = get_non_human_ids(session)
|
22
22
|
|
23
23
|
if aggregation_type == 'org':
|
24
24
|
if aggregation_id == ALL_ORGS_ID:
|
@@ -434,30 +434,30 @@ def run_aggregate_human_stats():
|
|
434
434
|
# Process divisions with progress tracking
|
435
435
|
progress = create_progress_tracker(len(division_ids), f"Processing {len(division_ids)} divisions for {org_name}")
|
436
436
|
for i, division_id in enumerate(division_ids):
|
437
|
-
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id,
|
438
|
-
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id,
|
439
|
-
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id,
|
437
|
+
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, human_id_filter=human_id_to_debug)
|
438
|
+
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, human_id_filter=human_id_to_debug, aggregation_window='Weekly')
|
439
|
+
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, human_id_filter=human_id_to_debug, aggregation_window='Daily')
|
440
440
|
progress.update(i + 1)
|
441
441
|
else:
|
442
442
|
# Debug mode or no divisions - process without progress tracking
|
443
443
|
for division_id in division_ids:
|
444
|
-
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id,
|
445
|
-
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id,
|
446
|
-
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id,
|
444
|
+
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, human_id_filter=human_id_to_debug)
|
445
|
+
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, human_id_filter=human_id_to_debug, aggregation_window='Weekly')
|
446
|
+
aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, human_id_filter=human_id_to_debug, aggregation_window='Daily')
|
447
447
|
|
448
448
|
# Process org-level stats with progress tracking
|
449
449
|
if human_id_to_debug is None:
|
450
450
|
org_progress = create_progress_tracker(3, f"Processing org-level stats for {org_name}")
|
451
|
-
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id,
|
451
|
+
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, human_id_filter=human_id_to_debug)
|
452
452
|
org_progress.update(1)
|
453
|
-
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id,
|
453
|
+
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, human_id_filter=human_id_to_debug, aggregation_window='Weekly')
|
454
454
|
org_progress.update(2)
|
455
|
-
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id,
|
455
|
+
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, human_id_filter=human_id_to_debug, aggregation_window='Daily')
|
456
456
|
org_progress.update(3)
|
457
457
|
else:
|
458
|
-
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id,
|
459
|
-
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id,
|
460
|
-
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id,
|
458
|
+
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, human_id_filter=human_id_to_debug)
|
459
|
+
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, human_id_filter=human_id_to_debug, aggregation_window='Weekly')
|
460
|
+
aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, human_id_filter=human_id_to_debug, aggregation_window='Daily')
|
461
461
|
|
462
462
|
# Aggregate by level
|
463
463
|
level_ids = session.query(Division.level_id).distinct().all()
|
@@ -467,12 +467,12 @@ def run_aggregate_human_stats():
|
|
467
467
|
# Process levels with progress tracking
|
468
468
|
level_progress = create_progress_tracker(len(level_ids), f"Processing {len(level_ids)} skill levels")
|
469
469
|
for i, level_id in enumerate(level_ids):
|
470
|
-
aggregate_human_stats(session, aggregation_type='level', aggregation_id=level_id,
|
470
|
+
aggregate_human_stats(session, aggregation_type='level', aggregation_id=level_id, human_id_filter=human_id_to_debug)
|
471
471
|
level_progress.update(i + 1)
|
472
472
|
else:
|
473
473
|
# Debug mode or no levels - process without progress tracking
|
474
474
|
for level_id in level_ids:
|
475
|
-
aggregate_human_stats(session, aggregation_type='level', aggregation_id=level_id,
|
475
|
+
aggregate_human_stats(session, aggregation_type='level', aggregation_id=level_id, human_id_filter=human_id_to_debug)
|
476
476
|
|
477
477
|
if __name__ == "__main__":
|
478
478
|
run_aggregate_human_stats()
|
@@ -10,15 +10,21 @@ from hockey_blast_common_lib.models import Game, Penalty, Organization, Division
|
|
10
10
|
from hockey_blast_common_lib.stats_models import OrgStatsReferee, DivisionStatsReferee, OrgStatsWeeklyReferee, OrgStatsDailyReferee, DivisionStatsWeeklyReferee, DivisionStatsDailyReferee, LevelStatsReferee
|
11
11
|
from hockey_blast_common_lib.db_connection import create_session
|
12
12
|
from sqlalchemy.sql import func, case
|
13
|
-
from hockey_blast_common_lib.options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, MIN_GAMES_FOR_LEVEL_STATS
|
14
|
-
from hockey_blast_common_lib.utils import get_org_id_from_alias,
|
13
|
+
from hockey_blast_common_lib.options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, MIN_GAMES_FOR_LEVEL_STATS
|
14
|
+
from hockey_blast_common_lib.utils import get_org_id_from_alias, get_non_human_ids, get_division_ids_for_last_season_in_all_leagues, get_all_division_ids_for_org
|
15
15
|
from hockey_blast_common_lib.utils import assign_ranks
|
16
16
|
from hockey_blast_common_lib.utils import get_start_datetime
|
17
17
|
from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
|
18
18
|
from hockey_blast_common_lib.progress_utils import create_progress_tracker
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
# Import status constants for game filtering
|
21
|
+
FINAL_STATUS = "Final"
|
22
|
+
FINAL_SO_STATUS = "Final(SO)"
|
23
|
+
FORFEIT_STATUS = "FORFEIT"
|
24
|
+
NOEVENTS_STATUS = "NOEVENTS"
|
25
|
+
|
26
|
+
def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregation_window=None):
|
27
|
+
human_ids_to_filter = get_non_human_ids(session)
|
22
28
|
|
23
29
|
if aggregation_type == 'org':
|
24
30
|
if aggregation_id == ALL_ORGS_ID:
|
@@ -72,15 +78,33 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
72
78
|
|
73
79
|
filter_condition = filter_condition & (Division.id == Game.division_id)
|
74
80
|
# Aggregate games reffed for each referee
|
81
|
+
# games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
82
|
+
# games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
|
75
83
|
games_reffed_stats = session.query(
|
76
84
|
Game.referee_1_id.label('human_id'),
|
77
|
-
func.count(Game.id).label('games_reffed'),
|
85
|
+
func.count(Game.id).label('games_reffed'), # DEPRECATED - will be replaced by games_participated
|
86
|
+
func.sum(case(
|
87
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
|
88
|
+
else_=0
|
89
|
+
)).label('games_participated'),
|
90
|
+
func.sum(case(
|
91
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
|
92
|
+
else_=0
|
93
|
+
)).label('games_with_stats'),
|
78
94
|
func.array_agg(Game.id).label('game_ids')
|
79
95
|
).filter(filter_condition).group_by(Game.referee_1_id).all()
|
80
96
|
|
81
97
|
games_reffed_stats_2 = session.query(
|
82
98
|
Game.referee_2_id.label('human_id'),
|
83
|
-
func.count(Game.id).label('games_reffed'),
|
99
|
+
func.count(Game.id).label('games_reffed'), # DEPRECATED - will be replaced by games_participated
|
100
|
+
func.sum(case(
|
101
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
|
102
|
+
else_=0
|
103
|
+
)).label('games_participated'),
|
104
|
+
func.sum(case(
|
105
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
|
106
|
+
else_=0
|
107
|
+
)).label('games_with_stats'),
|
84
108
|
func.array_agg(Game.id).label('game_ids')
|
85
109
|
).filter(filter_condition).group_by(Game.referee_2_id).all()
|
86
110
|
|
@@ -101,7 +125,9 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
101
125
|
key = (aggregation_id, stat.human_id)
|
102
126
|
if key not in stats_dict:
|
103
127
|
stats_dict[key] = {
|
104
|
-
'games_reffed': 0,
|
128
|
+
'games_reffed': 0, # DEPRECATED - for backward compatibility
|
129
|
+
'games_participated': 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
130
|
+
'games_with_stats': 0, # Games with full stats: FINAL, FINAL_SO only
|
105
131
|
'penalties_given': 0,
|
106
132
|
'gm_given': 0,
|
107
133
|
'penalties_per_game': 0.0,
|
@@ -111,6 +137,8 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
111
137
|
'last_game_id': None
|
112
138
|
}
|
113
139
|
stats_dict[key]['games_reffed'] += stat.games_reffed
|
140
|
+
stats_dict[key]['games_participated'] += stat.games_participated
|
141
|
+
stats_dict[key]['games_with_stats'] += stat.games_with_stats
|
114
142
|
stats_dict[key]['game_ids'].extend(stat.game_ids)
|
115
143
|
|
116
144
|
for stat in games_reffed_stats_2:
|
@@ -119,7 +147,9 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
119
147
|
key = (aggregation_id, stat.human_id)
|
120
148
|
if key not in stats_dict:
|
121
149
|
stats_dict[key] = {
|
122
|
-
'games_reffed': 0,
|
150
|
+
'games_reffed': 0, # DEPRECATED - for backward compatibility
|
151
|
+
'games_participated': 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
152
|
+
'games_with_stats': 0, # Games with full stats: FINAL, FINAL_SO only
|
123
153
|
'penalties_given': 0,
|
124
154
|
'gm_given': 0,
|
125
155
|
'penalties_per_game': 0.0,
|
@@ -129,6 +159,8 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
129
159
|
'last_game_id': None
|
130
160
|
}
|
131
161
|
stats_dict[key]['games_reffed'] += stat.games_reffed
|
162
|
+
stats_dict[key]['games_participated'] += stat.games_participated
|
163
|
+
stats_dict[key]['games_with_stats'] += stat.games_with_stats
|
132
164
|
stats_dict[key]['game_ids'].extend(stat.game_ids)
|
133
165
|
|
134
166
|
# Filter out entries with games_reffed less than min_games
|
@@ -149,11 +181,11 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
149
181
|
stats_dict[key]['gm_given'] += stat.gm_given / 2
|
150
182
|
stats_dict[key]['game_ids'].append(stat.game_id)
|
151
183
|
|
152
|
-
# Calculate per game stats
|
184
|
+
# Calculate per game stats (using games_with_stats as denominator for accuracy)
|
153
185
|
for key, stat in stats_dict.items():
|
154
|
-
if stat['
|
155
|
-
stat['penalties_per_game'] = stat['penalties_given'] / stat['
|
156
|
-
stat['gm_per_game'] = stat['gm_given'] / stat['
|
186
|
+
if stat['games_with_stats'] > 0:
|
187
|
+
stat['penalties_per_game'] = stat['penalties_given'] / stat['games_with_stats']
|
188
|
+
stat['gm_per_game'] = stat['gm_given'] / stat['games_with_stats']
|
157
189
|
|
158
190
|
# Ensure all keys have valid human_id values
|
159
191
|
stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
|
@@ -172,6 +204,8 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
172
204
|
|
173
205
|
# Assign ranks
|
174
206
|
assign_ranks(stats_dict, 'games_reffed')
|
207
|
+
assign_ranks(stats_dict, 'games_participated') # Rank by total participation
|
208
|
+
assign_ranks(stats_dict, 'games_with_stats') # Rank by games with full stats
|
175
209
|
assign_ranks(stats_dict, 'penalties_given')
|
176
210
|
assign_ranks(stats_dict, 'penalties_per_game')
|
177
211
|
assign_ranks(stats_dict, 'gm_given')
|
@@ -185,7 +219,11 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
185
219
|
referee_stat = StatsModel(
|
186
220
|
aggregation_id=aggregation_id,
|
187
221
|
human_id=human_id,
|
188
|
-
games_reffed=stat['games_reffed'],
|
222
|
+
games_reffed=stat['games_reffed'], # DEPRECATED - for backward compatibility
|
223
|
+
games_participated=stat['games_participated'], # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
224
|
+
games_participated_rank=stat['games_participated_rank'],
|
225
|
+
games_with_stats=stat['games_with_stats'], # Games with full stats: FINAL, FINAL_SO only
|
226
|
+
games_with_stats_rank=stat['games_with_stats_rank'],
|
189
227
|
penalties_given=stat['penalties_given'],
|
190
228
|
penalties_per_game=stat['penalties_per_game'],
|
191
229
|
gm_given=stat['gm_given'],
|
@@ -221,30 +259,30 @@ def run_aggregate_referee_stats():
|
|
221
259
|
# Process divisions with progress tracking
|
222
260
|
progress = create_progress_tracker(len(division_ids), f"Processing {len(division_ids)} divisions for {org_name}")
|
223
261
|
for i, division_id in enumerate(division_ids):
|
224
|
-
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id
|
225
|
-
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id,
|
226
|
-
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id,
|
262
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id)
|
263
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, aggregation_window='Weekly')
|
264
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, aggregation_window='Daily')
|
227
265
|
progress.update(i + 1)
|
228
266
|
else:
|
229
267
|
# Debug mode or no divisions - process without progress tracking
|
230
268
|
for division_id in division_ids:
|
231
|
-
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id
|
232
|
-
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id,
|
233
|
-
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id,
|
269
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id)
|
270
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, aggregation_window='Weekly')
|
271
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, aggregation_window='Daily')
|
234
272
|
|
235
273
|
# Process org-level stats with progress tracking
|
236
274
|
if human_id_to_debug is None:
|
237
275
|
org_progress = create_progress_tracker(3, f"Processing org-level stats for {org_name}")
|
238
|
-
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id
|
276
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id)
|
239
277
|
org_progress.update(1)
|
240
|
-
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id,
|
278
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Weekly')
|
241
279
|
org_progress.update(2)
|
242
|
-
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id,
|
280
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Daily')
|
243
281
|
org_progress.update(3)
|
244
282
|
else:
|
245
|
-
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id
|
246
|
-
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id,
|
247
|
-
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id,
|
283
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id)
|
284
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Weekly')
|
285
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Daily')
|
248
286
|
|
249
287
|
# Aggregate by level
|
250
288
|
level_ids = session.query(Division.level_id).distinct().all()
|
@@ -254,12 +292,12 @@ def run_aggregate_referee_stats():
|
|
254
292
|
# Process levels with progress tracking
|
255
293
|
level_progress = create_progress_tracker(len(level_ids), f"Processing {len(level_ids)} skill levels")
|
256
294
|
for i, level_id in enumerate(level_ids):
|
257
|
-
aggregate_referee_stats(session, aggregation_type='level', aggregation_id=level_id
|
295
|
+
aggregate_referee_stats(session, aggregation_type='level', aggregation_id=level_id)
|
258
296
|
level_progress.update(i + 1)
|
259
297
|
else:
|
260
298
|
# Debug mode or no levels - process without progress tracking
|
261
299
|
for level_id in level_ids:
|
262
|
-
aggregate_referee_stats(session, aggregation_type='level', aggregation_id=level_id
|
300
|
+
aggregate_referee_stats(session, aggregation_type='level', aggregation_id=level_id)
|
263
301
|
|
264
302
|
if __name__ == "__main__":
|
265
303
|
run_aggregate_referee_stats()
|
@@ -10,13 +10,19 @@ from hockey_blast_common_lib.models import Game, ScorekeeperSaveQuality
|
|
10
10
|
from hockey_blast_common_lib.stats_models import OrgStatsScorekeeper, OrgStatsWeeklyScorekeeper, OrgStatsDailyScorekeeper
|
11
11
|
from hockey_blast_common_lib.db_connection import create_session
|
12
12
|
from sqlalchemy.sql import func, case
|
13
|
-
from hockey_blast_common_lib.options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS
|
14
|
-
from hockey_blast_common_lib.utils import get_org_id_from_alias,
|
13
|
+
from hockey_blast_common_lib.options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS
|
14
|
+
from hockey_blast_common_lib.utils import get_org_id_from_alias, get_non_human_ids
|
15
15
|
from hockey_blast_common_lib.utils import assign_ranks
|
16
16
|
from hockey_blast_common_lib.utils import get_start_datetime
|
17
17
|
from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
|
18
18
|
from hockey_blast_common_lib.progress_utils import create_progress_tracker
|
19
19
|
|
20
|
+
# Import status constants for game filtering
|
21
|
+
FINAL_STATUS = "Final"
|
22
|
+
FINAL_SO_STATUS = "Final(SO)"
|
23
|
+
FORFEIT_STATUS = "FORFEIT"
|
24
|
+
NOEVENTS_STATUS = "NOEVENTS"
|
25
|
+
|
20
26
|
def calculate_quality_score(avg_max_saves_5sec, avg_max_saves_20sec, peak_max_saves_5sec, peak_max_saves_20sec):
|
21
27
|
"""
|
22
28
|
Calculate a quality score based on excessive clicking patterns.
|
@@ -51,13 +57,13 @@ def calculate_quality_score(avg_max_saves_5sec, avg_max_saves_20sec, peak_max_sa
|
|
51
57
|
|
52
58
|
return round(score, 2)
|
53
59
|
|
54
|
-
def aggregate_scorekeeper_stats(session, aggregation_type, aggregation_id,
|
60
|
+
def aggregate_scorekeeper_stats(session, aggregation_type, aggregation_id, aggregation_window=None):
|
55
61
|
# Only process scorekeeper stats for ALL_ORGS_ID - skip individual organizations
|
56
62
|
# This prevents redundant processing when upstream logic calls with all organization IDs
|
57
63
|
if aggregation_type == 'org' and aggregation_id != ALL_ORGS_ID:
|
58
64
|
return # Do nothing for individual organization IDs
|
59
65
|
|
60
|
-
human_ids_to_filter =
|
66
|
+
human_ids_to_filter = get_non_human_ids(session)
|
61
67
|
|
62
68
|
if aggregation_type == 'org':
|
63
69
|
aggregation_name = "All Orgs"
|
@@ -89,9 +95,19 @@ def aggregate_scorekeeper_stats(session, aggregation_type, aggregation_id, names
|
|
89
95
|
|
90
96
|
|
91
97
|
# Aggregate scorekeeper quality data for each human
|
98
|
+
# games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
99
|
+
# games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
|
92
100
|
scorekeeper_quality_stats = session.query(
|
93
101
|
ScorekeeperSaveQuality.scorekeeper_id.label('human_id'),
|
94
|
-
func.count(ScorekeeperSaveQuality.game_id).label('games_recorded'),
|
102
|
+
func.count(ScorekeeperSaveQuality.game_id).label('games_recorded'), # DEPRECATED - will be replaced by games_participated
|
103
|
+
func.sum(case(
|
104
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
|
105
|
+
else_=0
|
106
|
+
)).label('games_participated'),
|
107
|
+
func.sum(case(
|
108
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
|
109
|
+
else_=0
|
110
|
+
)).label('games_with_stats'),
|
95
111
|
func.sum(ScorekeeperSaveQuality.total_saves_recorded).label('total_saves_recorded'),
|
96
112
|
func.avg(ScorekeeperSaveQuality.total_saves_recorded).label('avg_saves_per_game'),
|
97
113
|
func.avg(ScorekeeperSaveQuality.max_saves_per_5sec).label('avg_max_saves_per_5sec'),
|
@@ -120,7 +136,9 @@ def aggregate_scorekeeper_stats(session, aggregation_type, aggregation_id, names
|
|
120
136
|
)
|
121
137
|
|
122
138
|
stats_dict[key] = {
|
123
|
-
'games_recorded': stat.games_recorded,
|
139
|
+
'games_recorded': stat.games_recorded, # DEPRECATED - for backward compatibility
|
140
|
+
'games_participated': stat.games_participated, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
141
|
+
'games_with_stats': stat.games_with_stats, # Games with full stats: FINAL, FINAL_SO only
|
124
142
|
'sog_given': stat.total_saves_recorded, # Legacy field name mapping
|
125
143
|
'sog_per_game': stat.avg_saves_per_game or 0.0, # Legacy field name mapping
|
126
144
|
'total_saves_recorded': stat.total_saves_recorded,
|
@@ -152,6 +170,8 @@ def aggregate_scorekeeper_stats(session, aggregation_type, aggregation_id, names
|
|
152
170
|
|
153
171
|
# Assign ranks - note: for quality metrics, lower values are better (reverse_rank=True for avg and peak clicking)
|
154
172
|
assign_ranks(stats_dict, 'games_recorded')
|
173
|
+
assign_ranks(stats_dict, 'games_participated') # Rank by total participation
|
174
|
+
assign_ranks(stats_dict, 'games_with_stats') # Rank by games with full stats
|
155
175
|
assign_ranks(stats_dict, 'sog_given') # Legacy field
|
156
176
|
assign_ranks(stats_dict, 'sog_per_game') # Legacy field
|
157
177
|
assign_ranks(stats_dict, 'total_saves_recorded')
|
@@ -169,7 +189,11 @@ def aggregate_scorekeeper_stats(session, aggregation_type, aggregation_id, names
|
|
169
189
|
scorekeeper_stat = StatsModel(
|
170
190
|
aggregation_id=aggregation_id,
|
171
191
|
human_id=human_id,
|
172
|
-
games_recorded=stat['games_recorded'],
|
192
|
+
games_recorded=stat['games_recorded'], # DEPRECATED - for backward compatibility
|
193
|
+
games_participated=stat['games_participated'], # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
194
|
+
games_participated_rank=stat['games_participated_rank'],
|
195
|
+
games_with_stats=stat['games_with_stats'], # Games with full stats: FINAL, FINAL_SO only
|
196
|
+
games_with_stats_rank=stat['games_with_stats_rank'],
|
173
197
|
sog_given=stat['sog_given'], # Legacy field mapping
|
174
198
|
sog_per_game=stat['sog_per_game'], # Legacy field mapping
|
175
199
|
total_saves_recorded=stat['total_saves_recorded'],
|
@@ -216,16 +240,16 @@ def run_aggregate_scorekeeper_stats():
|
|
216
240
|
if human_id_to_debug is None:
|
217
241
|
org_name = "All Organizations" if org_id == ALL_ORGS_ID else session.query(Organization.organization_name).filter(Organization.id == org_id).scalar() or f"org_id {org_id}"
|
218
242
|
org_progress = create_progress_tracker(3, f"Processing scorekeeper stats for {org_name}")
|
219
|
-
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id
|
243
|
+
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id)
|
220
244
|
org_progress.update(1)
|
221
|
-
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id,
|
245
|
+
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Weekly')
|
222
246
|
org_progress.update(2)
|
223
|
-
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id,
|
247
|
+
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Daily')
|
224
248
|
org_progress.update(3)
|
225
249
|
else:
|
226
|
-
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id
|
227
|
-
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id,
|
228
|
-
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id,
|
250
|
+
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id)
|
251
|
+
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Weekly')
|
252
|
+
aggregate_scorekeeper_stats(session, aggregation_type='org', aggregation_id=org_id, aggregation_window='Daily')
|
229
253
|
|
230
254
|
if __name__ == "__main__":
|
231
255
|
run_aggregate_scorekeeper_stats()
|
@@ -10,14 +10,20 @@ from hockey_blast_common_lib.models import Game, Goal, Penalty, GameRoster, Orga
|
|
10
10
|
from hockey_blast_common_lib.stats_models import OrgStatsSkater, DivisionStatsSkater, OrgStatsWeeklySkater, OrgStatsDailySkater, DivisionStatsWeeklySkater, DivisionStatsDailySkater, LevelStatsSkater
|
11
11
|
from hockey_blast_common_lib.db_connection import create_session
|
12
12
|
from sqlalchemy.sql import func, case
|
13
|
-
from hockey_blast_common_lib.options import
|
14
|
-
from hockey_blast_common_lib.utils import get_org_id_from_alias,
|
13
|
+
from hockey_blast_common_lib.options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, MIN_GAMES_FOR_LEVEL_STATS
|
14
|
+
from hockey_blast_common_lib.utils import get_org_id_from_alias, get_non_human_ids, get_division_ids_for_last_season_in_all_leagues, get_all_division_ids_for_org
|
15
15
|
from hockey_blast_common_lib.utils import get_start_datetime
|
16
16
|
from sqlalchemy import func, case, and_
|
17
17
|
from collections import defaultdict
|
18
18
|
from hockey_blast_common_lib.stats_utils import ALL_ORGS_ID
|
19
19
|
from hockey_blast_common_lib.progress_utils import create_progress_tracker
|
20
20
|
|
21
|
+
# Import status constants for game filtering
|
22
|
+
FINAL_STATUS = "Final"
|
23
|
+
FINAL_SO_STATUS = "Final(SO)"
|
24
|
+
FORFEIT_STATUS = "FORFEIT"
|
25
|
+
NOEVENTS_STATUS = "NOEVENTS"
|
26
|
+
|
21
27
|
def calculate_current_point_streak(session, human_id, filter_condition):
|
22
28
|
"""
|
23
29
|
Calculate the current point streak for a player.
|
@@ -64,8 +70,8 @@ def calculate_current_point_streak(session, human_id, filter_condition):
|
|
64
70
|
|
65
71
|
return current_streak, avg_points_during_streak
|
66
72
|
|
67
|
-
def aggregate_skater_stats(session, aggregation_type, aggregation_id,
|
68
|
-
human_ids_to_filter =
|
73
|
+
def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_human_id=None, aggregation_window=None):
|
74
|
+
human_ids_to_filter = get_non_human_ids(session)
|
69
75
|
|
70
76
|
# Get the name of the aggregation, for debug purposes
|
71
77
|
if aggregation_type == 'org':
|
@@ -138,16 +144,26 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
138
144
|
# human_filter = [GameRoster.human_id == debug_human_id]
|
139
145
|
|
140
146
|
# Aggregate games played for each human in each division, excluding goalies
|
147
|
+
# games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
148
|
+
# games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
|
141
149
|
games_played_query = session.query(
|
142
150
|
GameRoster.human_id,
|
143
|
-
func.count(Game.id).label('games_played'),
|
151
|
+
func.count(Game.id).label('games_played'), # DEPRECATED - will be replaced by games_participated
|
152
|
+
func.sum(case(
|
153
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
|
154
|
+
else_=0
|
155
|
+
)).label('games_participated'),
|
156
|
+
func.sum(case(
|
157
|
+
(Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
|
158
|
+
else_=0
|
159
|
+
)).label('games_with_stats'),
|
144
160
|
func.array_agg(Game.id).label('game_ids')
|
145
161
|
).join(Game, Game.id == GameRoster.game_id)
|
146
|
-
|
162
|
+
|
147
163
|
# Only join Division if not level aggregation (since we filter on Game.division_id directly for levels)
|
148
164
|
if aggregation_type != 'level':
|
149
165
|
games_played_query = games_played_query.join(Division, Game.division_id == Division.id)
|
150
|
-
|
166
|
+
|
151
167
|
games_played_stats = games_played_query.filter(filter_condition, ~GameRoster.role.ilike('g'), *human_filter).group_by(GameRoster.human_id).all()
|
152
168
|
|
153
169
|
# Aggregate goals for each human in each division, excluding goalies
|
@@ -206,7 +222,9 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
206
222
|
key = (aggregation_id, stat.human_id)
|
207
223
|
if key not in stats_dict:
|
208
224
|
stats_dict[key] = {
|
209
|
-
'games_played': 0,
|
225
|
+
'games_played': 0, # DEPRECATED - for backward compatibility
|
226
|
+
'games_participated': 0, # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
227
|
+
'games_with_stats': 0, # Games with full stats: FINAL, FINAL_SO only
|
210
228
|
'goals': 0,
|
211
229
|
'assists': 0,
|
212
230
|
'penalties': 0,
|
@@ -224,6 +242,8 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
224
242
|
'last_game_id': None
|
225
243
|
}
|
226
244
|
stats_dict[key]['games_played'] += stat.games_played
|
245
|
+
stats_dict[key]['games_participated'] += stat.games_participated
|
246
|
+
stats_dict[key]['games_with_stats'] += stat.games_with_stats
|
227
247
|
stats_dict[key]['game_ids'].extend(stat.game_ids)
|
228
248
|
|
229
249
|
# Filter out entries with games_played less than min_games
|
@@ -253,14 +273,14 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
253
273
|
stats_dict[key]['penalties'] += stat.penalties
|
254
274
|
stats_dict[key]['gm_penalties'] += stat.gm_penalties # Update GM penalties
|
255
275
|
|
256
|
-
# Calculate per game stats
|
276
|
+
# Calculate per game stats (using games_with_stats as denominator for accuracy)
|
257
277
|
for key, stat in stats_dict.items():
|
258
|
-
if stat['
|
259
|
-
stat['goals_per_game'] = stat['goals'] / stat['
|
260
|
-
stat['points_per_game'] = stat['points'] / stat['
|
261
|
-
stat['assists_per_game'] = stat['assists'] / stat['
|
262
|
-
stat['penalties_per_game'] = stat['penalties'] / stat['
|
263
|
-
stat['gm_penalties_per_game'] = stat['gm_penalties'] / stat['
|
278
|
+
if stat['games_with_stats'] > 0:
|
279
|
+
stat['goals_per_game'] = stat['goals'] / stat['games_with_stats']
|
280
|
+
stat['points_per_game'] = stat['points'] / stat['games_with_stats']
|
281
|
+
stat['assists_per_game'] = stat['assists'] / stat['games_with_stats']
|
282
|
+
stat['penalties_per_game'] = stat['penalties'] / stat['games_with_stats']
|
283
|
+
stat['gm_penalties_per_game'] = stat['gm_penalties'] / stat['games_with_stats'] # Calculate GM penalties per game
|
264
284
|
|
265
285
|
# Ensure all keys have valid human_id values
|
266
286
|
stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
|
@@ -292,6 +312,8 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
292
312
|
stats_dict[key][f'{field}_rank'] = rank
|
293
313
|
|
294
314
|
assign_ranks(stats_dict, 'games_played')
|
315
|
+
assign_ranks(stats_dict, 'games_participated') # Rank by total participation
|
316
|
+
assign_ranks(stats_dict, 'games_with_stats') # Rank by games with full stats
|
295
317
|
assign_ranks(stats_dict, 'goals')
|
296
318
|
assign_ranks(stats_dict, 'assists')
|
297
319
|
assign_ranks(stats_dict, 'points')
|
@@ -330,7 +352,11 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
330
352
|
skater_stat = StatsModel(
|
331
353
|
aggregation_id=aggregation_id,
|
332
354
|
human_id=human_id,
|
333
|
-
games_played=stat['games_played'],
|
355
|
+
games_played=stat['games_played'], # DEPRECATED - for backward compatibility
|
356
|
+
games_participated=stat['games_participated'], # Total games: FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
357
|
+
games_participated_rank=stat['games_participated_rank'],
|
358
|
+
games_with_stats=stat['games_with_stats'], # Games with full stats: FINAL, FINAL_SO only
|
359
|
+
games_with_stats_rank=stat['games_with_stats_rank'],
|
334
360
|
goals=stat['goals'],
|
335
361
|
assists=stat['assists'],
|
336
362
|
points=stat['goals'] + stat['assists'],
|
@@ -382,30 +408,30 @@ def run_aggregate_skater_stats():
|
|
382
408
|
# Process divisions with progress tracking
|
383
409
|
progress = create_progress_tracker(len(division_ids), f"Processing {len(division_ids)} divisions for {org_name}")
|
384
410
|
for i, division_id in enumerate(division_ids):
|
385
|
-
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id,
|
386
|
-
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id,
|
387
|
-
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id,
|
411
|
+
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug)
|
412
|
+
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
413
|
+
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
388
414
|
progress.update(i + 1)
|
389
415
|
else:
|
390
416
|
# Debug mode or no divisions - process without progress tracking
|
391
417
|
for division_id in division_ids:
|
392
|
-
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id,
|
393
|
-
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id,
|
394
|
-
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id,
|
418
|
+
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug)
|
419
|
+
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
420
|
+
aggregate_skater_stats(session, aggregation_type='division', aggregation_id=division_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
395
421
|
|
396
422
|
# Process org-level stats with progress tracking
|
397
423
|
if human_id_to_debug is None:
|
398
424
|
org_progress = create_progress_tracker(3, f"Processing org-level stats for {org_name}")
|
399
|
-
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id,
|
425
|
+
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug)
|
400
426
|
org_progress.update(1)
|
401
|
-
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id,
|
427
|
+
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
402
428
|
org_progress.update(2)
|
403
|
-
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id,
|
429
|
+
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
404
430
|
org_progress.update(3)
|
405
431
|
else:
|
406
|
-
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id,
|
407
|
-
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id,
|
408
|
-
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id,
|
432
|
+
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug)
|
433
|
+
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
434
|
+
aggregate_skater_stats(session, aggregation_type='org', aggregation_id=org_id, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
409
435
|
|
410
436
|
# Aggregate by level
|
411
437
|
level_ids = session.query(Division.level_id).distinct().all()
|
@@ -415,12 +441,12 @@ def run_aggregate_skater_stats():
|
|
415
441
|
# Process levels with progress tracking
|
416
442
|
level_progress = create_progress_tracker(len(level_ids), f"Processing {len(level_ids)} skill levels")
|
417
443
|
for i, level_id in enumerate(level_ids):
|
418
|
-
aggregate_skater_stats(session, aggregation_type='level', aggregation_id=level_id,
|
444
|
+
aggregate_skater_stats(session, aggregation_type='level', aggregation_id=level_id, debug_human_id=human_id_to_debug)
|
419
445
|
level_progress.update(i + 1)
|
420
446
|
else:
|
421
447
|
# Debug mode or no levels - process without progress tracking
|
422
448
|
for level_id in level_ids:
|
423
|
-
aggregate_skater_stats(session, aggregation_type='level', aggregation_id=level_id,
|
449
|
+
aggregate_skater_stats(session, aggregation_type='level', aggregation_id=level_id, debug_human_id=human_id_to_debug)
|
424
450
|
|
425
451
|
if __name__ == "__main__":
|
426
452
|
run_aggregate_skater_stats()
|
Binary file
|
{hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/options.py
RENAMED
@@ -9,17 +9,6 @@ MIN_GAMES_FOR_LEVEL_STATS = 1
|
|
9
9
|
|
10
10
|
orgs = {'caha', 'sharksice', 'tvice'}
|
11
11
|
|
12
|
-
not_human_names = [
|
13
|
-
("Home", None, None),
|
14
|
-
("Away", None, None),
|
15
|
-
(None, "Unknown", None),
|
16
|
-
("Not", None , None),
|
17
|
-
(None , None, "Goalie"),
|
18
|
-
("Unassigned",None , None),
|
19
|
-
# Critical addition - primary cause of 93.4% of roster insertion errors
|
20
|
-
("Not", "Signed", "In")
|
21
|
-
]
|
22
|
-
|
23
12
|
def parse_args():
|
24
13
|
parser = argparse.ArgumentParser(description="Process data for a specific organization.")
|
25
14
|
parser.add_argument("org", choices=orgs, help="The organization to process (e.g., 'caha', 'sharksice', 'tvice').")
|
@@ -4,26 +4,34 @@ from datetime import datetime, timedelta
|
|
4
4
|
class ProgressTracker:
|
5
5
|
"""
|
6
6
|
Reusable progress tracker with ETA calculation for stats aggregation processes.
|
7
|
+
Supports optional custom counters for tracking additional metrics.
|
7
8
|
"""
|
8
|
-
|
9
|
-
def __init__(self, total_items, description="Processing"):
|
9
|
+
|
10
|
+
def __init__(self, total_items, description="Processing", custom_counters=None):
|
10
11
|
self.total_items = total_items
|
11
12
|
self.description = description
|
12
13
|
self.start_time = time.time()
|
13
14
|
self.processed_items = 0
|
14
15
|
self.last_update_time = self.start_time
|
16
|
+
self.custom_counters = custom_counters if custom_counters else {}
|
15
17
|
|
16
|
-
def update(self, processed_count=None):
|
18
|
+
def update(self, processed_count=None, **kwargs):
|
17
19
|
"""
|
18
20
|
Update progress. If processed_count is None, increment by 1.
|
21
|
+
kwargs can be used to update custom counters.
|
19
22
|
"""
|
20
23
|
if processed_count is not None:
|
21
24
|
self.processed_items = processed_count
|
22
25
|
else:
|
23
26
|
self.processed_items += 1
|
24
|
-
|
27
|
+
|
28
|
+
# Update custom counters
|
29
|
+
for key, value in kwargs.items():
|
30
|
+
if key in self.custom_counters:
|
31
|
+
self.custom_counters[key] = value
|
32
|
+
|
25
33
|
current_time = time.time()
|
26
|
-
|
34
|
+
|
27
35
|
# Only update display if at least 0.1 seconds have passed (to avoid spamming)
|
28
36
|
if current_time - self.last_update_time >= 0.1 or self.processed_items == self.total_items:
|
29
37
|
self._display_progress()
|
@@ -52,10 +60,15 @@ class ProgressTracker:
|
|
52
60
|
eta_str = self._format_time(eta_seconds)
|
53
61
|
|
54
62
|
elapsed_str = self._format_time(elapsed_time)
|
55
|
-
|
63
|
+
|
56
64
|
progress_msg = f"\r{self.description}: {self.processed_items}/{self.total_items} ({percentage:.1f}%) | "
|
57
65
|
progress_msg += f"Elapsed: {elapsed_str} | ETA: {eta_str}"
|
58
|
-
|
66
|
+
|
67
|
+
# Add custom counters to the display
|
68
|
+
if self.custom_counters:
|
69
|
+
counter_parts = [f"{key}: {value}" for key, value in self.custom_counters.items()]
|
70
|
+
progress_msg += " | " + ", ".join(counter_parts)
|
71
|
+
|
59
72
|
print(progress_msg, end="", flush=True)
|
60
73
|
|
61
74
|
# Add newline when complete
|
@@ -84,8 +97,9 @@ class ProgressTracker:
|
|
84
97
|
self.processed_items = self.total_items
|
85
98
|
self._display_progress()
|
86
99
|
|
87
|
-
def create_progress_tracker(total_items, description="Processing"):
|
100
|
+
def create_progress_tracker(total_items, description="Processing", custom_counters=None):
|
88
101
|
"""
|
89
102
|
Factory function to create a progress tracker.
|
103
|
+
custom_counters: dict of counter_name: initial_value for additional metrics to track
|
90
104
|
"""
|
91
|
-
return ProgressTracker(total_items, description)
|
105
|
+
return ProgressTracker(total_items, description, custom_counters)
|
@@ -52,8 +52,12 @@ class BaseStatsSkater(db.Model):
|
|
52
52
|
__abstract__ = True
|
53
53
|
id = db.Column(db.Integer, primary_key=True)
|
54
54
|
human_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=False)
|
55
|
-
games_played = db.Column(db.Integer, default=0)
|
55
|
+
games_played = db.Column(db.Integer, default=0) # DEPRECATED - use games_participated instead
|
56
56
|
games_played_rank = db.Column(db.Integer, default=0)
|
57
|
+
games_participated = db.Column(db.Integer, default=0) # Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
58
|
+
games_participated_rank = db.Column(db.Integer, default=0)
|
59
|
+
games_with_stats = db.Column(db.Integer, default=0) # Count only FINAL, FINAL_SO
|
60
|
+
games_with_stats_rank = db.Column(db.Integer, default=0)
|
57
61
|
goals = db.Column(db.Integer, default=0)
|
58
62
|
goals_rank = db.Column(db.Integer, default=0)
|
59
63
|
assists = db.Column(db.Integer, default=0)
|
@@ -104,8 +108,12 @@ class BaseStatsGoalie(db.Model):
|
|
104
108
|
__abstract__ = True
|
105
109
|
id = db.Column(db.Integer, primary_key=True)
|
106
110
|
human_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=False)
|
107
|
-
games_played = db.Column(db.Integer, default=0)
|
111
|
+
games_played = db.Column(db.Integer, default=0) # DEPRECATED - use games_participated instead
|
108
112
|
games_played_rank = db.Column(db.Integer, default=0)
|
113
|
+
games_participated = db.Column(db.Integer, default=0) # Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
114
|
+
games_participated_rank = db.Column(db.Integer, default=0)
|
115
|
+
games_with_stats = db.Column(db.Integer, default=0) # Count only FINAL, FINAL_SO
|
116
|
+
games_with_stats_rank = db.Column(db.Integer, default=0)
|
109
117
|
goals_allowed = db.Column(db.Integer, default=0)
|
110
118
|
goals_allowed_rank = db.Column(db.Integer, default=0)
|
111
119
|
goals_allowed_per_game = db.Column(db.Float, default=0.0)
|
@@ -137,8 +145,12 @@ class BaseStatsReferee(db.Model):
|
|
137
145
|
__abstract__ = True
|
138
146
|
id = db.Column(db.Integer, primary_key=True)
|
139
147
|
human_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=False)
|
140
|
-
games_reffed = db.Column(db.Integer, default=0)
|
148
|
+
games_reffed = db.Column(db.Integer, default=0) # DEPRECATED - use games_participated instead
|
141
149
|
games_reffed_rank = db.Column(db.Integer, default=0)
|
150
|
+
games_participated = db.Column(db.Integer, default=0) # Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
151
|
+
games_participated_rank = db.Column(db.Integer, default=0)
|
152
|
+
games_with_stats = db.Column(db.Integer, default=0) # Count only FINAL, FINAL_SO (for per-game averages)
|
153
|
+
games_with_stats_rank = db.Column(db.Integer, default=0)
|
142
154
|
penalties_given = db.Column(db.Integer, default=0)
|
143
155
|
penalties_given_rank = db.Column(db.Integer, default=0)
|
144
156
|
penalties_per_game = db.Column(db.Float, default=0.0)
|
@@ -170,8 +182,12 @@ class BaseStatsScorekeeper(db.Model):
|
|
170
182
|
__abstract__ = True
|
171
183
|
id = db.Column(db.Integer, primary_key=True)
|
172
184
|
human_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=False)
|
173
|
-
games_recorded = db.Column(db.Integer, default=0)
|
185
|
+
games_recorded = db.Column(db.Integer, default=0) # DEPRECATED - use games_participated instead
|
174
186
|
games_recorded_rank = db.Column(db.Integer, default=0)
|
187
|
+
games_participated = db.Column(db.Integer, default=0) # Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
|
188
|
+
games_participated_rank = db.Column(db.Integer, default=0)
|
189
|
+
games_with_stats = db.Column(db.Integer, default=0) # Count only FINAL, FINAL_SO (for per-game averages)
|
190
|
+
games_with_stats_rank = db.Column(db.Integer, default=0)
|
175
191
|
sog_given = db.Column(db.Integer, default=0)
|
176
192
|
sog_given_rank = db.Column(db.Integer, default=0)
|
177
193
|
sog_per_game = db.Column(db.Float, default=0.0)
|
{hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/utils.py
RENAMED
@@ -52,6 +52,24 @@ def get_human_ids_by_names(session, names):
|
|
52
52
|
human_ids.update([result.id for result in results])
|
53
53
|
return human_ids
|
54
54
|
|
55
|
+
def get_non_human_ids(session):
|
56
|
+
"""Get IDs of non-human entities (placeholder names, test accounts, etc.)
|
57
|
+
|
58
|
+
Returns set of human_ids that should be filtered out from statistics.
|
59
|
+
Filters out placeholder names like "Home", "Away", "Unknown", etc.
|
60
|
+
"""
|
61
|
+
not_human_names = [
|
62
|
+
("Home", None, None),
|
63
|
+
("Away", None, None),
|
64
|
+
(None, "Unknown", None),
|
65
|
+
("Not", None, None),
|
66
|
+
(None, None, "Goalie"),
|
67
|
+
("Unassigned", None, None),
|
68
|
+
("Not", "Signed", "In"),
|
69
|
+
("Incognito", None, None)
|
70
|
+
]
|
71
|
+
return get_human_ids_by_names(session, not_human_names)
|
72
|
+
|
55
73
|
def get_division_ids_for_last_season_in_all_leagues(session, org_id):
|
56
74
|
# # TODO = remove tmp hack
|
57
75
|
# return get_all_division_ids_for_org(session, org_id)
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
2
2
|
|
3
3
|
setup(
|
4
4
|
name='hockey-blast-common-lib', # The name of your package
|
5
|
-
version='0.1.
|
5
|
+
version='0.1.62',
|
6
6
|
description='Common library for shared functionality and DB models',
|
7
7
|
author='Pavel Kletskov',
|
8
8
|
author_email='kletskov@gmail.com',
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/models.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{hockey_blast_common_lib-0.1.60 → hockey_blast_common_lib-0.1.62}/hockey_blast_common_lib/wsgi.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|