hockey-blast-common-lib 0.1.32__py3-none-any.whl → 0.1.33__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_goalie_stats.py +89 -34
- hockey_blast_common_lib/aggregate_human_stats.py +229 -70
- hockey_blast_common_lib/aggregate_referee_stats.py +55 -48
- hockey_blast_common_lib/skills_in_divisions.py +1 -1
- hockey_blast_common_lib/stats_models.py +53 -2
- hockey_blast_common_lib/stats_utils.py +4 -0
- {hockey_blast_common_lib-0.1.32.dist-info → hockey_blast_common_lib-0.1.33.dist-info}/METADATA +1 -1
- {hockey_blast_common_lib-0.1.32.dist-info → hockey_blast_common_lib-0.1.33.dist-info}/RECORD +10 -9
- {hockey_blast_common_lib-0.1.32.dist-info → hockey_blast_common_lib-0.1.33.dist-info}/WHEEL +0 -0
- {hockey_blast_common_lib-0.1.32.dist-info → hockey_blast_common_lib-0.1.33.dist-info}/top_level.txt +0 -0
@@ -5,16 +5,31 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
5
5
|
|
6
6
|
from datetime import datetime, timedelta
|
7
7
|
import sqlalchemy
|
8
|
-
|
9
|
-
from hockey_blast_common_lib.
|
8
|
+
|
9
|
+
from hockey_blast_common_lib.models import Game, Goal, Penalty, GameRoster, Organization, Division, Human, Level
|
10
|
+
from hockey_blast_common_lib.stats_models import OrgStatsGoalie, DivisionStatsGoalie, OrgStatsWeeklyGoalie, OrgStatsDailyGoalie, DivisionStatsWeeklyGoalie, DivisionStatsDailyGoalie, LevelStatsGoalie
|
10
11
|
from hockey_blast_common_lib.db_connection import create_session
|
11
12
|
from sqlalchemy.sql import func, case
|
12
|
-
from hockey_blast_common_lib.options import not_human_names, parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS
|
13
|
+
from hockey_blast_common_lib.options import not_human_names, parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, MIN_GAMES_FOR_LEVEL_STATS
|
13
14
|
from hockey_blast_common_lib.utils import get_org_id_from_alias, get_human_ids_by_names, get_division_ids_for_last_season_in_all_leagues, get_all_division_ids_for_org
|
15
|
+
from hockey_blast_common_lib.stats_utils import assign_ranks
|
16
|
+
from sqlalchemy import func, case, and_
|
17
|
+
from collections import defaultdict
|
14
18
|
|
15
|
-
def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_filter_out, aggregation_window=None):
|
19
|
+
def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_filter_out, debug_human_id=None, aggregation_window=None):
|
16
20
|
human_ids_to_filter = get_human_ids_by_names(session, names_to_filter_out)
|
17
21
|
|
22
|
+
# Get the name of the aggregation, for debug purposes
|
23
|
+
if aggregation_type == 'org':
|
24
|
+
aggregation_name = session.query(Organization).filter(Organization.id == aggregation_id).first().organization_name
|
25
|
+
print(f"Aggregating goalie stats for {aggregation_name} with window {aggregation_window}...")
|
26
|
+
elif aggregation_type == 'division':
|
27
|
+
aggregation_name = session.query(Division).filter(Division.id == aggregation_id).first().level
|
28
|
+
elif aggregation_type == 'level':
|
29
|
+
aggregation_name = session.query(Level).filter(Level.id == aggregation_id).first().level_name
|
30
|
+
else:
|
31
|
+
aggregation_name = "Unknown"
|
32
|
+
|
18
33
|
if aggregation_type == 'org':
|
19
34
|
if aggregation_window == 'Daily':
|
20
35
|
StatsModel = OrgStatsDailyGoalie
|
@@ -33,6 +48,14 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
33
48
|
StatsModel = DivisionStatsGoalie
|
34
49
|
min_games = MIN_GAMES_FOR_DIVISION_STATS
|
35
50
|
filter_condition = Game.division_id == aggregation_id
|
51
|
+
elif aggregation_type == 'level':
|
52
|
+
StatsModel = LevelStatsGoalie
|
53
|
+
min_games = MIN_GAMES_FOR_LEVEL_STATS
|
54
|
+
filter_condition = Division.level_id == aggregation_id
|
55
|
+
# Add filter to only include games for the last 5 years
|
56
|
+
five_years_ago = datetime.now() - timedelta(days=5*365)
|
57
|
+
level_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP) >= five_years_ago
|
58
|
+
filter_condition = filter_condition & level_window_filter
|
36
59
|
else:
|
37
60
|
raise ValueError("Invalid aggregation type")
|
38
61
|
|
@@ -55,6 +78,11 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
55
78
|
session.query(StatsModel).filter(StatsModel.aggregation_id == aggregation_id).delete()
|
56
79
|
session.commit()
|
57
80
|
|
81
|
+
# Filter for specific human_id if provided
|
82
|
+
human_filter = []
|
83
|
+
# if debug_human_id:
|
84
|
+
# human_filter = [GameRoster.human_id == debug_human_id]
|
85
|
+
|
58
86
|
# Aggregate games played, goals allowed, and shots faced for each goalie
|
59
87
|
goalie_stats = session.query(
|
60
88
|
GameRoster.human_id,
|
@@ -62,7 +90,7 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
62
90
|
func.sum(case((GameRoster.team_id == Game.home_team_id, Game.visitor_final_score), else_=Game.home_final_score)).label('goals_allowed'),
|
63
91
|
func.sum(case((GameRoster.team_id == Game.home_team_id, Game.visitor_period_1_shots + Game.visitor_period_2_shots + Game.visitor_period_3_shots + Game.visitor_ot_shots + Game.visitor_so_shots), else_=Game.home_period_1_shots + Game.home_period_2_shots + Game.home_period_3_shots + Game.home_ot_shots + Game.home_so_shots)).label('shots_faced'),
|
64
92
|
func.array_agg(Game.id).label('game_ids')
|
65
|
-
).join(Game, GameRoster.game_id == Game.id).filter(filter_condition, GameRoster.role
|
93
|
+
).join(Game, GameRoster.game_id == Game.id).join(Division, Game.division_id == Division.id).filter(filter_condition, GameRoster.role.ilike('g')).group_by(GameRoster.human_id).all()
|
66
94
|
|
67
95
|
# Combine the results
|
68
96
|
stats_dict = {}
|
@@ -70,6 +98,8 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
70
98
|
if stat.human_id in human_ids_to_filter:
|
71
99
|
continue
|
72
100
|
key = (aggregation_id, stat.human_id)
|
101
|
+
if stat.games_played < min_games:
|
102
|
+
continue
|
73
103
|
stats_dict[key] = {
|
74
104
|
'games_played': stat.games_played,
|
75
105
|
'goals_allowed': stat.goals_allowed if stat.goals_allowed is not None else 0,
|
@@ -102,37 +132,43 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
102
132
|
# Calculate total_in_rank
|
103
133
|
total_in_rank = len(stats_dict)
|
104
134
|
|
105
|
-
# Assign ranks
|
106
|
-
def assign_ranks(stats_dict, field):
|
107
|
-
sorted_stats = sorted(stats_dict.items(), key=lambda x: x[1][field], reverse=True)
|
108
|
-
for rank, (key, stat) in enumerate(sorted_stats, start=1):
|
109
|
-
stats_dict[key][f'{field}_rank'] = rank
|
110
|
-
|
135
|
+
# Assign ranks within each level
|
111
136
|
assign_ranks(stats_dict, 'games_played')
|
112
|
-
assign_ranks(stats_dict, 'goals_allowed')
|
113
|
-
assign_ranks(stats_dict, 'goals_allowed_per_game')
|
137
|
+
assign_ranks(stats_dict, 'goals_allowed', reverse_rank=True)
|
114
138
|
assign_ranks(stats_dict, 'shots_faced')
|
139
|
+
assign_ranks(stats_dict, 'goals_allowed_per_game', reverse_rank=True)
|
115
140
|
assign_ranks(stats_dict, 'save_percentage')
|
116
141
|
|
142
|
+
# Debug output for specific human
|
143
|
+
if debug_human_id:
|
144
|
+
if any(key[1] == debug_human_id for key in stats_dict):
|
145
|
+
human = session.query(Human).filter(Human.id == debug_human_id).first()
|
146
|
+
human_name = f"{human.first_name} {human.last_name}" if human else "Unknown"
|
147
|
+
print(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}:")
|
148
|
+
for key, stat in stats_dict.items():
|
149
|
+
if key[1] == debug_human_id:
|
150
|
+
for k, v in stat.items():
|
151
|
+
print(f"{k}: {v}")
|
152
|
+
|
117
153
|
# Insert aggregated stats into the appropriate table with progress output
|
118
154
|
total_items = len(stats_dict)
|
119
155
|
batch_size = 1000
|
120
156
|
for i, (key, stat) in enumerate(stats_dict.items(), 1):
|
121
157
|
aggregation_id, human_id = key
|
122
|
-
if stat['games_played']
|
123
|
-
|
158
|
+
goals_allowed_per_game = stat['goals_allowed'] / stat['games_played'] if stat['games_played'] > 0 else 0.0
|
159
|
+
save_percentage = (stat['shots_faced'] - stat['goals_allowed']) / stat['shots_faced'] if stat['shots_faced'] > 0 else 0.0
|
124
160
|
goalie_stat = StatsModel(
|
125
161
|
aggregation_id=aggregation_id,
|
126
162
|
human_id=human_id,
|
127
163
|
games_played=stat['games_played'],
|
128
164
|
goals_allowed=stat['goals_allowed'],
|
129
|
-
goals_allowed_per_game=stat['goals_allowed_per_game'],
|
130
165
|
shots_faced=stat['shots_faced'],
|
131
|
-
|
166
|
+
goals_allowed_per_game=goals_allowed_per_game,
|
167
|
+
save_percentage=save_percentage,
|
132
168
|
games_played_rank=stat['games_played_rank'],
|
133
169
|
goals_allowed_rank=stat['goals_allowed_rank'],
|
134
|
-
goals_allowed_per_game_rank=stat['goals_allowed_per_game_rank'],
|
135
170
|
shots_faced_rank=stat['shots_faced_rank'],
|
171
|
+
goals_allowed_per_game_rank=stat['goals_allowed_per_game_rank'],
|
136
172
|
save_percentage_rank=stat['save_percentage_rank'],
|
137
173
|
total_in_rank=total_in_rank,
|
138
174
|
first_game_id=stat['first_game_id'],
|
@@ -142,23 +178,42 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_f
|
|
142
178
|
# Commit in batches
|
143
179
|
if i % batch_size == 0:
|
144
180
|
session.commit()
|
145
|
-
print(f"\r{i}/{total_items} ({(i/total_items)*100:.2f}%)", end="")
|
146
181
|
session.commit()
|
147
|
-
print(f"\r{total_items}/{total_items} (100.00%)")
|
148
|
-
print("\nDone.")
|
149
182
|
|
150
|
-
# Example usage
|
151
183
|
if __name__ == "__main__":
|
152
|
-
args = parse_args()
|
153
|
-
org_alias = args.org
|
154
184
|
session = create_session("boss")
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
185
|
+
human_id_to_debug = None
|
186
|
+
|
187
|
+
# Get all org_id present in the Organization table
|
188
|
+
org_ids = session.query(Organization.id).all()
|
189
|
+
org_ids = [org_id[0] for org_id in org_ids]
|
190
|
+
|
191
|
+
for org_id in org_ids:
|
192
|
+
division_ids = get_all_division_ids_for_org(session, org_id)
|
193
|
+
print(f"Aggregating goalie stats for {len(division_ids)} divisions in org_id {org_id}...")
|
194
|
+
total_divisions = len(division_ids)
|
195
|
+
processed_divisions = 0
|
196
|
+
for division_id in division_ids:
|
197
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, debug_human_id=human_id_to_debug)
|
198
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
199
|
+
aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
200
|
+
processed_divisions += 1
|
201
|
+
if human_id_to_debug is None:
|
202
|
+
print(f"\rProcessed {processed_divisions}/{total_divisions} divisions ({(processed_divisions/total_divisions)*100:.2f}%)", end="")
|
203
|
+
|
204
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, debug_human_id=human_id_to_debug)
|
205
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, debug_human_id=human_id_to_debug, aggregation_window='Weekly')
|
206
|
+
aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, debug_human_id=human_id_to_debug, aggregation_window='Daily')
|
207
|
+
|
208
|
+
# Aggregate by level
|
209
|
+
level_ids = session.query(Division.level_id).distinct().all()
|
210
|
+
level_ids = [level_id[0] for level_id in level_ids]
|
211
|
+
total_levels = len(level_ids)
|
212
|
+
processed_levels = 0
|
213
|
+
for level_id in level_ids:
|
214
|
+
if level_id is None:
|
215
|
+
continue
|
216
|
+
if human_id_to_debug is None:
|
217
|
+
print(f"\rProcessed {processed_levels}/{total_levels} levels ({(processed_levels/total_levels)*100:.2f}%)", end="")
|
218
|
+
processed_levels += 1
|
219
|
+
aggregate_goalie_stats(session, aggregation_type='level', aggregation_id=level_id, names_to_filter_out=not_human_names, debug_human_id=human_id_to_debug)
|
@@ -6,17 +6,20 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
6
6
|
|
7
7
|
from datetime import datetime, timedelta
|
8
8
|
import sqlalchemy
|
9
|
-
from hockey_blast_common_lib.models import Game, GameRoster
|
10
|
-
from hockey_blast_common_lib.stats_models import OrgStatsHuman, DivisionStatsHuman, OrgStatsDailyHuman, OrgStatsWeeklyHuman, DivisionStatsDailyHuman, DivisionStatsWeeklyHuman
|
9
|
+
from hockey_blast_common_lib.models import Game, GameRoster, Organization, Division
|
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, not_human_names
|
14
|
-
from hockey_blast_common_lib.utils import get_fake_human_for_stats, get_org_id_from_alias, get_human_ids_by_names, get_division_ids_for_last_season_in_all_leagues
|
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, not_human_names
|
14
|
+
from hockey_blast_common_lib.utils import get_fake_human_for_stats, get_org_id_from_alias, get_human_ids_by_names, get_division_ids_for_last_season_in_all_leagues, get_all_division_ids_for_org
|
15
|
+
from hockey_blast_common_lib.stats_utils import assign_ranks
|
15
16
|
|
16
17
|
def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_filter_out, human_id_filter=None, aggregation_window=None):
|
17
18
|
human_ids_to_filter = get_human_ids_by_names(session, names_to_filter_out)
|
18
19
|
|
19
20
|
if aggregation_type == 'org':
|
21
|
+
aggregation_name = session.query(Organization).filter(Organization.id == aggregation_id).first().organization_name
|
22
|
+
print(f"Aggregating goalie stats for {aggregation_name} with window {aggregation_window}...")
|
20
23
|
if aggregation_window == 'Daily':
|
21
24
|
StatsModel = OrgStatsDailyHuman
|
22
25
|
elif aggregation_window == 'Weekly':
|
@@ -34,24 +37,20 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
34
37
|
StatsModel = DivisionStatsHuman
|
35
38
|
min_games = MIN_GAMES_FOR_DIVISION_STATS
|
36
39
|
filter_condition = Game.division_id == aggregation_id
|
40
|
+
elif aggregation_type == 'level':
|
41
|
+
StatsModel = LevelStatsHuman
|
42
|
+
min_games = MIN_GAMES_FOR_LEVEL_STATS
|
43
|
+
filter_condition = Division.level_id == aggregation_id
|
44
|
+
# Add filter to only include games for the last 5 years
|
45
|
+
five_years_ago = datetime.now() - timedelta(days=5*365)
|
46
|
+
level_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP) >= five_years_ago
|
47
|
+
filter_condition = filter_condition & level_window_filter
|
37
48
|
else:
|
38
49
|
raise ValueError("Invalid aggregation type")
|
39
50
|
|
40
|
-
# Delete existing items from the stats table
|
41
|
-
session.query(StatsModel).filter(StatsModel.aggregation_id == aggregation_id).delete()
|
42
|
-
session.commit()
|
43
|
-
|
44
|
-
# Filter for specific human_id if provided
|
45
|
-
human_filter = []
|
46
|
-
if human_id_filter:
|
47
|
-
human_filter = [GameRoster.human_id == human_id_filter]
|
48
|
-
|
49
|
-
# Filter games by status
|
50
|
-
game_status_filter = Game.status.like('Final%')
|
51
|
-
|
52
51
|
# Apply aggregation window filter
|
53
52
|
if aggregation_window:
|
54
|
-
last_game_datetime = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition,
|
53
|
+
last_game_datetime = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, Game.status.like('Final%')).scalar()
|
55
54
|
if last_game_datetime:
|
56
55
|
last_game_datetime = datetime.strptime(last_game_datetime, '%Y-%m-%d %H:%M:%S')
|
57
56
|
if aggregation_window == 'Daily':
|
@@ -64,50 +63,106 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
64
63
|
game_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP).between(start_datetime, last_game_datetime)
|
65
64
|
filter_condition = filter_condition & game_window_filter
|
66
65
|
|
67
|
-
#
|
68
|
-
|
66
|
+
# Delete existing items from the stats table
|
67
|
+
session.query(StatsModel).filter(StatsModel.aggregation_id == aggregation_id).delete()
|
68
|
+
session.commit()
|
69
|
+
|
70
|
+
# Filter for specific human_id if provided
|
71
|
+
human_filter = []
|
72
|
+
if human_id_filter:
|
73
|
+
human_filter = [GameRoster.human_id == human_id_filter]
|
74
|
+
|
75
|
+
# Filter games by status
|
76
|
+
game_status_filter = Game.status.like('Final%')
|
77
|
+
|
78
|
+
# Aggregate skater games played
|
79
|
+
skater_stats = session.query(
|
80
|
+
GameRoster.human_id,
|
81
|
+
func.count(func.distinct(Game.id)).label('games_skater'),
|
82
|
+
func.array_agg(func.distinct(Game.id)).label('skater_game_ids')
|
83
|
+
).join(Game, GameRoster.game_id == Game.id).join(Division, Game.division_id == Division.id).filter(filter_condition, game_status_filter, ~GameRoster.role.ilike('G'), *human_filter).group_by(GameRoster.human_id).all()
|
84
|
+
|
85
|
+
# Aggregate goalie games played
|
86
|
+
goalie_stats = session.query(
|
69
87
|
GameRoster.human_id,
|
70
|
-
func.count(func.distinct(
|
71
|
-
func.
|
72
|
-
|
73
|
-
).join(Game, GameRoster.game_id == Game.id).filter(filter_condition, game_status_filter, *human_filter).group_by(GameRoster.human_id).all()
|
88
|
+
func.count(func.distinct(Game.id)).label('games_goalie'),
|
89
|
+
func.array_agg(func.distinct(Game.id)).label('goalie_game_ids')
|
90
|
+
).join(Game, GameRoster.game_id == Game.id).join(Division, Game.division_id == Division.id).filter(filter_condition, game_status_filter, GameRoster.role.ilike('G'), *human_filter).group_by(GameRoster.human_id).all()
|
74
91
|
|
75
92
|
# Aggregate referee and scorekeeper games from Game table
|
76
93
|
referee_stats = session.query(
|
77
94
|
Game.referee_1_id.label('human_id'),
|
78
95
|
func.count(func.distinct(Game.id)).label('games_referee'),
|
79
96
|
func.array_agg(func.distinct(Game.id)).label('referee_game_ids')
|
80
|
-
).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.referee_1_id).all()
|
97
|
+
).join(Division, Game.division_id == Division.id).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.referee_1_id).all()
|
81
98
|
|
82
99
|
referee_stats_2 = session.query(
|
83
100
|
Game.referee_2_id.label('human_id'),
|
84
101
|
func.count(func.distinct(Game.id)).label('games_referee'),
|
85
102
|
func.array_agg(func.distinct(Game.id)).label('referee_game_ids')
|
86
|
-
).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.referee_2_id).all()
|
103
|
+
).join(Division, Game.division_id == Division.id).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.referee_2_id).all()
|
87
104
|
|
88
105
|
scorekeeper_stats = session.query(
|
89
106
|
Game.scorekeeper_id.label('human_id'),
|
90
107
|
func.count(func.distinct(Game.id)).label('games_scorekeeper'),
|
91
108
|
func.array_agg(func.distinct(Game.id)).label('scorekeeper_game_ids')
|
92
|
-
).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.scorekeeper_id).all()
|
109
|
+
).join(Division, Game.division_id == Division.id).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.scorekeeper_id).all()
|
93
110
|
|
94
111
|
# Combine the results
|
95
112
|
stats_dict = {}
|
96
|
-
for stat in
|
113
|
+
for stat in skater_stats:
|
97
114
|
if stat.human_id in human_ids_to_filter:
|
98
115
|
continue
|
99
116
|
key = (aggregation_id, stat.human_id)
|
100
117
|
stats_dict[key] = {
|
101
|
-
'games_total': stat.games_skater
|
118
|
+
'games_total': stat.games_skater,
|
102
119
|
'games_skater': stat.games_skater,
|
103
|
-
'games_goalie':
|
120
|
+
'games_goalie': 0,
|
104
121
|
'games_referee': 0,
|
105
122
|
'games_scorekeeper': 0,
|
106
|
-
'
|
123
|
+
'skater_game_ids': stat.skater_game_ids,
|
124
|
+
'goalie_game_ids': [],
|
107
125
|
'referee_game_ids': [],
|
108
|
-
'scorekeeper_game_ids': []
|
126
|
+
'scorekeeper_game_ids': [],
|
127
|
+
'first_game_id_skater': None,
|
128
|
+
'last_game_id_skater': None,
|
129
|
+
'first_game_id_goalie': None,
|
130
|
+
'last_game_id_goalie': None,
|
131
|
+
'first_game_id_referee': None,
|
132
|
+
'last_game_id_referee': None,
|
133
|
+
'first_game_id_scorekeeper': None,
|
134
|
+
'last_game_id_scorekeeper': None
|
109
135
|
}
|
110
136
|
|
137
|
+
for stat in goalie_stats:
|
138
|
+
if stat.human_id in human_ids_to_filter:
|
139
|
+
continue
|
140
|
+
key = (aggregation_id, stat.human_id)
|
141
|
+
if key not in stats_dict:
|
142
|
+
stats_dict[key] = {
|
143
|
+
'games_total': stat.games_goalie,
|
144
|
+
'games_skater': 0,
|
145
|
+
'games_goalie': stat.games_goalie,
|
146
|
+
'games_referee': 0,
|
147
|
+
'games_scorekeeper': 0,
|
148
|
+
'skater_game_ids': [],
|
149
|
+
'goalie_game_ids': stat.goalie_game_ids,
|
150
|
+
'referee_game_ids': [],
|
151
|
+
'scorekeeper_game_ids': [],
|
152
|
+
'first_game_id_skater': None,
|
153
|
+
'last_game_id_skater': None,
|
154
|
+
'first_game_id_goalie': None,
|
155
|
+
'last_game_id_goalie': None,
|
156
|
+
'first_game_id_referee': None,
|
157
|
+
'last_game_id_referee': None,
|
158
|
+
'first_game_id_scorekeeper': None,
|
159
|
+
'last_game_id_scorekeeper': None
|
160
|
+
}
|
161
|
+
else:
|
162
|
+
stats_dict[key]['games_goalie'] += stat.games_goalie
|
163
|
+
stats_dict[key]['games_total'] += stat.games_goalie
|
164
|
+
stats_dict[key]['goalie_game_ids'] += stat.goalie_game_ids
|
165
|
+
|
111
166
|
for stat in referee_stats:
|
112
167
|
if stat.human_id in human_ids_to_filter:
|
113
168
|
continue
|
@@ -119,9 +174,18 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
119
174
|
'games_goalie': 0,
|
120
175
|
'games_referee': stat.games_referee,
|
121
176
|
'games_scorekeeper': 0,
|
122
|
-
'
|
177
|
+
'skater_game_ids': [],
|
178
|
+
'goalie_game_ids': [],
|
123
179
|
'referee_game_ids': stat.referee_game_ids,
|
124
|
-
'scorekeeper_game_ids': []
|
180
|
+
'scorekeeper_game_ids': [],
|
181
|
+
'first_game_id_skater': None,
|
182
|
+
'last_game_id_skater': None,
|
183
|
+
'first_game_id_goalie': None,
|
184
|
+
'last_game_id_goalie': None,
|
185
|
+
'first_game_id_referee': None,
|
186
|
+
'last_game_id_referee': None,
|
187
|
+
'first_game_id_scorekeeper': None,
|
188
|
+
'last_game_id_scorekeeper': None
|
125
189
|
}
|
126
190
|
else:
|
127
191
|
stats_dict[key]['games_referee'] += stat.games_referee
|
@@ -139,9 +203,18 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
139
203
|
'games_goalie': 0,
|
140
204
|
'games_referee': stat.games_referee,
|
141
205
|
'games_scorekeeper': 0,
|
142
|
-
'
|
206
|
+
'skater_game_ids': [],
|
207
|
+
'goalie_game_ids': [],
|
143
208
|
'referee_game_ids': stat.referee_game_ids,
|
144
|
-
'scorekeeper_game_ids': []
|
209
|
+
'scorekeeper_game_ids': [],
|
210
|
+
'first_game_id_skater': None,
|
211
|
+
'last_game_id_skater': None,
|
212
|
+
'first_game_id_goalie': None,
|
213
|
+
'last_game_id_goalie': None,
|
214
|
+
'first_game_id_referee': None,
|
215
|
+
'last_game_id_referee': None,
|
216
|
+
'first_game_id_scorekeeper': None,
|
217
|
+
'last_game_id_scorekeeper': None
|
145
218
|
}
|
146
219
|
else:
|
147
220
|
stats_dict[key]['games_referee'] += stat.games_referee
|
@@ -159,9 +232,18 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
159
232
|
'games_goalie': 0,
|
160
233
|
'games_referee': 0,
|
161
234
|
'games_scorekeeper': stat.games_scorekeeper,
|
162
|
-
'
|
235
|
+
'skater_game_ids': [],
|
236
|
+
'goalie_game_ids': [],
|
163
237
|
'referee_game_ids': [],
|
164
|
-
'scorekeeper_game_ids': stat.scorekeeper_game_ids
|
238
|
+
'scorekeeper_game_ids': stat.scorekeeper_game_ids,
|
239
|
+
'first_game_id_skater': None,
|
240
|
+
'last_game_id_skater': None,
|
241
|
+
'first_game_id_goalie': None,
|
242
|
+
'last_game_id_goalie': None,
|
243
|
+
'first_game_id_referee': None,
|
244
|
+
'last_game_id_referee': None,
|
245
|
+
'first_game_id_scorekeeper': None,
|
246
|
+
'last_game_id_scorekeeper': None
|
165
247
|
}
|
166
248
|
else:
|
167
249
|
stats_dict[key]['games_scorekeeper'] += stat.games_scorekeeper
|
@@ -174,36 +256,61 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
174
256
|
# Calculate total_in_rank
|
175
257
|
total_in_rank = len(stats_dict)
|
176
258
|
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
259
|
+
# Calculate number of items in rank per role
|
260
|
+
skaters_in_rank = len([stat for stat in stats_dict.values() if stat['games_skater'] > 0])
|
261
|
+
goalies_in_rank = len([stat for stat in stats_dict.values() if stat['games_goalie'] > 0])
|
262
|
+
referees_in_rank = len([stat for stat in stats_dict.values() if stat['games_referee'] > 0])
|
263
|
+
scorekeepers_in_rank = len([stat for stat in stats_dict.values() if stat['games_scorekeeper'] > 0])
|
264
|
+
|
265
|
+
# Filter out humans with less than min_games
|
266
|
+
stats_dict = {key: value for key, value in stats_dict.items() if value['games_total'] >= min_games}
|
182
267
|
|
268
|
+
# Assign ranks
|
183
269
|
assign_ranks(stats_dict, 'games_total')
|
184
270
|
assign_ranks(stats_dict, 'games_skater')
|
185
271
|
assign_ranks(stats_dict, 'games_goalie')
|
186
272
|
assign_ranks(stats_dict, 'games_referee')
|
187
273
|
assign_ranks(stats_dict, 'games_scorekeeper')
|
188
274
|
|
189
|
-
# Populate first_game_id and last_game_id
|
275
|
+
# Populate first_game_id and last_game_id for each role
|
190
276
|
for key, stat in stats_dict.items():
|
191
|
-
all_game_ids = stat['
|
277
|
+
all_game_ids = stat['skater_game_ids'] + stat['goalie_game_ids'] + stat['referee_game_ids'] + stat['scorekeeper_game_ids']
|
192
278
|
if all_game_ids:
|
193
279
|
first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
|
194
280
|
last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
|
195
281
|
stat['first_game_id'] = first_game.id if first_game else None
|
196
282
|
stat['last_game_id'] = last_game.id if last_game else None
|
197
283
|
|
284
|
+
if stat['skater_game_ids']:
|
285
|
+
first_game_skater = session.query(Game).filter(Game.id.in_(stat['skater_game_ids'])).order_by(Game.date, Game.time).first()
|
286
|
+
last_game_skater = session.query(Game).filter(Game.id.in_(stat['skater_game_ids'])).order_by(Game.date.desc(), Game.time.desc()).first()
|
287
|
+
stat['first_game_id_skater'] = first_game_skater.id if first_game_skater else None
|
288
|
+
stat['last_game_id_skater'] = last_game_skater.id if last_game_skater else None
|
289
|
+
|
290
|
+
if stat['goalie_game_ids']:
|
291
|
+
first_game_goalie = session.query(Game).filter(Game.id.in_(stat['goalie_game_ids'])).order_by(Game.date, Game.time).first()
|
292
|
+
last_game_goalie = session.query(Game).filter(Game.id.in_(stat['goalie_game_ids'])).order_by(Game.date.desc(), Game.time.desc()).first()
|
293
|
+
stat['first_game_id_goalie'] = first_game_goalie.id if first_game_goalie else None
|
294
|
+
stat['last_game_id_goalie'] = last_game_goalie.id if last_game_goalie else None
|
295
|
+
|
296
|
+
if stat['referee_game_ids']:
|
297
|
+
first_game_referee = session.query(Game).filter(Game.id.in_(stat['referee_game_ids'])).order_by(Game.date, Game.time).first()
|
298
|
+
last_game_referee = session.query(Game).filter(Game.id.in_(stat['referee_game_ids'])).order_by(Game.date.desc(), Game.time.desc()).first()
|
299
|
+
stat['first_game_id_referee'] = first_game_referee.id if first_game_referee else None
|
300
|
+
stat['last_game_id_referee'] = last_game_referee.id if last_game_referee else None
|
301
|
+
|
302
|
+
if stat['scorekeeper_game_ids']:
|
303
|
+
first_game_scorekeeper = session.query(Game).filter(Game.id.in_(stat['scorekeeper_game_ids'])).order_by(Game.date, Game.time).first()
|
304
|
+
last_game_scorekeeper = session.query(Game).filter(Game.id.in_(stat['scorekeeper_game_ids'])).order_by(Game.date.desc(), Game.time.desc()).first()
|
305
|
+
stat['first_game_id_scorekeeper'] = first_game_scorekeeper.id if first_game_scorekeeper else None
|
306
|
+
stat['last_game_id_scorekeeper'] = last_game_scorekeeper.id if last_game_scorekeeper else None
|
307
|
+
|
198
308
|
# Insert aggregated stats into the appropriate table with progress output
|
199
|
-
total_items = len(stats_dict)
|
200
309
|
batch_size = 1000
|
201
310
|
for i, (key, stat) in enumerate(stats_dict.items(), 1):
|
202
311
|
aggregation_id, human_id = key
|
203
312
|
if human_id_filter and human_id != human_id_filter:
|
204
313
|
continue
|
205
|
-
if stat['games_total'] < min_games:
|
206
|
-
continue
|
207
314
|
|
208
315
|
human_stat = StatsModel(
|
209
316
|
aggregation_id=aggregation_id,
|
@@ -219,14 +326,25 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
219
326
|
games_scorekeeper=stat['games_scorekeeper'],
|
220
327
|
games_scorekeeper_rank=stat['games_scorekeeper_rank'],
|
221
328
|
total_in_rank=total_in_rank,
|
329
|
+
skaters_in_rank=skaters_in_rank,
|
330
|
+
goalies_in_rank=goalies_in_rank,
|
331
|
+
referees_in_rank=referees_in_rank,
|
332
|
+
scorekeepers_in_rank=scorekeepers_in_rank,
|
222
333
|
first_game_id=stat['first_game_id'],
|
223
|
-
last_game_id=stat['last_game_id']
|
334
|
+
last_game_id=stat['last_game_id'],
|
335
|
+
first_game_id_skater=stat['first_game_id_skater'],
|
336
|
+
last_game_id_skater=stat['last_game_id_skater'],
|
337
|
+
first_game_id_goalie=stat['first_game_id_goalie'],
|
338
|
+
last_game_id_goalie=stat['last_game_id_goalie'],
|
339
|
+
first_game_id_referee=stat['first_game_id_referee'],
|
340
|
+
last_game_id_referee=stat['last_game_id_referee'],
|
341
|
+
first_game_id_scorekeeper=stat['first_game_id_scorekeeper'],
|
342
|
+
last_game_id_scorekeeper=stat['last_game_id_scorekeeper']
|
224
343
|
)
|
225
344
|
session.add(human_stat)
|
226
345
|
# Commit in batches
|
227
346
|
if i % batch_size == 0:
|
228
347
|
session.commit()
|
229
|
-
print(f"\r{i}/{total_items} ({(i/total_items)*100:.2f}%)", end="")
|
230
348
|
session.commit()
|
231
349
|
|
232
350
|
# Fetch fake human ID for overall stats
|
@@ -240,12 +358,24 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
240
358
|
'games_referee': sum(stat['games_referee'] for stat in stats_dict.values()),
|
241
359
|
'games_scorekeeper': sum(stat['games_scorekeeper'] for stat in stats_dict.values()),
|
242
360
|
'total_in_rank': total_in_rank,
|
361
|
+
'skaters_in_rank': skaters_in_rank,
|
362
|
+
'goalies_in_rank': goalies_in_rank,
|
363
|
+
'referees_in_rank': referees_in_rank,
|
364
|
+
'scorekeepers_in_rank': scorekeepers_in_rank,
|
243
365
|
'first_game_id': None,
|
244
|
-
'last_game_id': None
|
366
|
+
'last_game_id': None,
|
367
|
+
'first_game_id_skater': None,
|
368
|
+
'last_game_id_skater': None,
|
369
|
+
'first_game_id_goalie': None,
|
370
|
+
'last_game_id_goalie': None,
|
371
|
+
'first_game_id_referee': None,
|
372
|
+
'last_game_id_referee': None,
|
373
|
+
'first_game_id_scorekeeper': None,
|
374
|
+
'last_game_id_scorekeeper': None
|
245
375
|
}
|
246
376
|
|
247
377
|
# Populate first_game_id and last_game_id for overall stats
|
248
|
-
all_game_ids = [game_id for stat in stats_dict.values() for game_id in stat['
|
378
|
+
all_game_ids = [game_id for stat in stats_dict.values() for game_id in stat['skater_game_ids'] + stat['goalie_game_ids'] + stat['referee_game_ids'] + stat['scorekeeper_game_ids']]
|
249
379
|
if all_game_ids:
|
250
380
|
first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
|
251
381
|
last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
|
@@ -267,29 +397,58 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_fi
|
|
267
397
|
games_scorekeeper=overall_stats['games_scorekeeper'],
|
268
398
|
games_scorekeeper_rank=0, # Overall stats do not need a rank
|
269
399
|
total_in_rank=overall_stats['total_in_rank'],
|
400
|
+
skaters_in_rank=overall_stats['skaters_in_rank'],
|
401
|
+
goalies_in_rank=overall_stats['goalies_in_rank'],
|
402
|
+
referees_in_rank=overall_stats['referees_in_rank'],
|
403
|
+
scorekeepers_in_rank=overall_stats['scorekeepers_in_rank'],
|
270
404
|
first_game_id=overall_stats['first_game_id'],
|
271
|
-
last_game_id=overall_stats['last_game_id']
|
405
|
+
last_game_id=overall_stats['last_game_id'],
|
406
|
+
first_game_id_skater=overall_stats['first_game_id_skater'],
|
407
|
+
last_game_id_skater=overall_stats['last_game_id_skater'],
|
408
|
+
first_game_id_goalie=overall_stats['first_game_id_goalie'],
|
409
|
+
last_game_id_goalie=overall_stats['last_game_id_goalie'],
|
410
|
+
first_game_id_referee=overall_stats['first_game_id_referee'],
|
411
|
+
last_game_id_referee=overall_stats['last_game_id_referee'],
|
412
|
+
first_game_id_scorekeeper=overall_stats['first_game_id_scorekeeper'],
|
413
|
+
last_game_id_scorekeeper=overall_stats['last_game_id_scorekeeper']
|
272
414
|
)
|
273
415
|
session.add(overall_human_stat)
|
274
416
|
session.commit()
|
275
417
|
|
276
|
-
print(f"\r{total_items}/{total_items} (100.00%)")
|
277
|
-
print("\nDone.")
|
278
|
-
|
279
|
-
# Example usage
|
280
418
|
if __name__ == "__main__":
|
281
|
-
args = parse_args()
|
282
|
-
org_alias=args.org
|
283
419
|
session = create_session("boss")
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
for
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
420
|
+
human_id_to_debug = None
|
421
|
+
|
422
|
+
# Get all org_id present in the Organization table
|
423
|
+
# org_ids = session.query(Organization.id).all()
|
424
|
+
# org_ids = [org_id[0] for org_id in org_ids]
|
425
|
+
|
426
|
+
# for org_id in org_ids:
|
427
|
+
# division_ids = get_all_division_ids_for_org(session, org_id)
|
428
|
+
# print(f"Aggregating human stats for {len(division_ids)} divisions in org_id {org_id}...")
|
429
|
+
# total_divisions = len(division_ids)
|
430
|
+
# processed_divisions = 0
|
431
|
+
# for division_id in division_ids:
|
432
|
+
# aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, human_id_filter=human_id_to_debug)
|
433
|
+
# aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, human_id_filter=human_id_to_debug, aggregation_window='Weekly')
|
434
|
+
# aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, human_id_filter=human_id_to_debug, aggregation_window='Daily')
|
435
|
+
# processed_divisions += 1
|
436
|
+
# if human_id_to_debug is None:
|
437
|
+
# print(f"\rProcessed {processed_divisions}/{total_divisions} divisions ({(processed_divisions/total_divisions)*100:.2f}%)", end="")
|
438
|
+
# print("")
|
439
|
+
# aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, human_id_filter=human_id_to_debug)
|
440
|
+
# aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, human_id_filter=human_id_to_debug, aggregation_window='Weekly')
|
441
|
+
# aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, human_id_filter=human_id_to_debug, aggregation_window='Daily')
|
442
|
+
|
443
|
+
# Aggregate by level
|
444
|
+
level_ids = session.query(Division.level_id).distinct().all()
|
445
|
+
level_ids = [level_id[0] for level_id in level_ids]
|
446
|
+
total_levels = len(level_ids)
|
447
|
+
processed_levels = 0
|
448
|
+
for level_id in level_ids:
|
449
|
+
if level_id is None:
|
450
|
+
continue
|
451
|
+
if human_id_to_debug is None:
|
452
|
+
print(f"\rProcessed {processed_levels}/{total_levels} levels ({(processed_levels/total_levels)*100:.2f}%)", end="")
|
453
|
+
processed_levels += 1
|
454
|
+
aggregate_human_stats(session, aggregation_type='level', aggregation_id=level_id, names_to_filter_out=not_human_names, human_id_filter=human_id_to_debug)
|
@@ -6,17 +6,20 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
6
6
|
|
7
7
|
from datetime import datetime, timedelta
|
8
8
|
import sqlalchemy
|
9
|
-
from hockey_blast_common_lib.models import Game, Penalty
|
10
|
-
from hockey_blast_common_lib.stats_models import OrgStatsReferee, DivisionStatsReferee,OrgStatsWeeklyReferee, OrgStatsDailyReferee, DivisionStatsWeeklyReferee, DivisionStatsDailyReferee
|
9
|
+
from hockey_blast_common_lib.models import Game, Penalty, Organization, Division
|
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, not_human_names
|
14
|
-
from hockey_blast_common_lib.utils import get_org_id_from_alias, get_human_ids_by_names, get_division_ids_for_last_season_in_all_leagues
|
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, not_human_names
|
14
|
+
from hockey_blast_common_lib.utils import get_org_id_from_alias, get_human_ids_by_names, get_division_ids_for_last_season_in_all_leagues, get_all_division_ids_for_org
|
15
|
+
from hockey_blast_common_lib.stats_utils import assign_ranks
|
15
16
|
|
16
17
|
def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_filter_out, aggregation_window=None):
|
17
18
|
human_ids_to_filter = get_human_ids_by_names(session, names_to_filter_out)
|
18
19
|
|
19
20
|
if aggregation_type == 'org':
|
21
|
+
aggregation_name = session.query(Organization).filter(Organization.id == aggregation_id).first().organization_name
|
22
|
+
print(f"Aggregating referee stats for {aggregation_name} with window {aggregation_window}...")
|
20
23
|
if aggregation_window == 'Daily':
|
21
24
|
StatsModel = OrgStatsDailyReferee
|
22
25
|
elif aggregation_window == 'Weekly':
|
@@ -34,6 +37,14 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
34
37
|
StatsModel = DivisionStatsReferee
|
35
38
|
min_games = MIN_GAMES_FOR_DIVISION_STATS
|
36
39
|
filter_condition = Game.division_id == aggregation_id
|
40
|
+
elif aggregation_type == 'level':
|
41
|
+
StatsModel = LevelStatsReferee
|
42
|
+
min_games = MIN_GAMES_FOR_LEVEL_STATS
|
43
|
+
filter_condition = Division.level_id == aggregation_id
|
44
|
+
# Add filter to only include games for the last 5 years
|
45
|
+
five_years_ago = datetime.now() - timedelta(days=5*365)
|
46
|
+
level_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP) >= five_years_ago
|
47
|
+
filter_condition = filter_condition & level_window_filter
|
37
48
|
else:
|
38
49
|
raise ValueError("Invalid aggregation type")
|
39
50
|
|
@@ -117,36 +128,14 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
117
128
|
for stat in penalties_given_stats:
|
118
129
|
if stat.referee_1_id and stat.referee_1_id not in human_ids_to_filter:
|
119
130
|
key = (aggregation_id, stat.referee_1_id)
|
120
|
-
if key
|
121
|
-
stats_dict[key] = {
|
122
|
-
'games_reffed': 0,
|
123
|
-
'penalties_given': stat.penalties_given / 2,
|
124
|
-
'gm_given': stat.gm_given / 2,
|
125
|
-
'penalties_per_game': 0.0,
|
126
|
-
'gm_per_game': 0.0,
|
127
|
-
'game_ids': [stat.game_id],
|
128
|
-
'first_game_id': None,
|
129
|
-
'last_game_id': None
|
130
|
-
}
|
131
|
-
else:
|
131
|
+
if key in stats_dict:
|
132
132
|
stats_dict[key]['penalties_given'] += stat.penalties_given / 2
|
133
133
|
stats_dict[key]['gm_given'] += stat.gm_given / 2
|
134
134
|
stats_dict[key]['game_ids'].append(stat.game_id)
|
135
135
|
|
136
136
|
if stat.referee_2_id and stat.referee_2_id not in human_ids_to_filter:
|
137
137
|
key = (aggregation_id, stat.referee_2_id)
|
138
|
-
if key
|
139
|
-
stats_dict[key] = {
|
140
|
-
'games_reffed': 0,
|
141
|
-
'penalties_given': stat.penalties_given / 2,
|
142
|
-
'gm_given': stat.gm_given / 2,
|
143
|
-
'penalties_per_game': 0.0,
|
144
|
-
'gm_per_game': 0.0,
|
145
|
-
'game_ids': [stat.game_id],
|
146
|
-
'first_game_id': None,
|
147
|
-
'last_game_id': None
|
148
|
-
}
|
149
|
-
else:
|
138
|
+
if key in stats_dict:
|
150
139
|
stats_dict[key]['penalties_given'] += stat.penalties_given / 2
|
151
140
|
stats_dict[key]['gm_given'] += stat.gm_given / 2
|
152
141
|
stats_dict[key]['game_ids'].append(stat.game_id)
|
@@ -160,6 +149,9 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
160
149
|
# Ensure all keys have valid human_id values
|
161
150
|
stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
|
162
151
|
|
152
|
+
# Filter out referees with less than min_games
|
153
|
+
stats_dict = {key: value for key, value in stats_dict.items() if value['games_reffed'] >= min_games}
|
154
|
+
|
163
155
|
# Populate first_game_id and last_game_id
|
164
156
|
for key, stat in stats_dict.items():
|
165
157
|
all_game_ids = stat['game_ids']
|
@@ -173,11 +165,6 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
173
165
|
total_in_rank = len(stats_dict)
|
174
166
|
|
175
167
|
# Assign ranks
|
176
|
-
def assign_ranks(stats_dict, field):
|
177
|
-
sorted_stats = sorted(stats_dict.items(), key=lambda x: x[1][field], reverse=True)
|
178
|
-
for rank, (key, stat) in enumerate(sorted_stats, start=1):
|
179
|
-
stats_dict[key][f'{field}_rank'] = rank
|
180
|
-
|
181
168
|
assign_ranks(stats_dict, 'games_reffed')
|
182
169
|
assign_ranks(stats_dict, 'penalties_given')
|
183
170
|
assign_ranks(stats_dict, 'penalties_per_game')
|
@@ -189,8 +176,6 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
189
176
|
batch_size = 1000
|
190
177
|
for i, (key, stat) in enumerate(stats_dict.items(), 1):
|
191
178
|
aggregation_id, human_id = key
|
192
|
-
if stat['games_reffed'] < min_games:
|
193
|
-
continue
|
194
179
|
referee_stat = StatsModel(
|
195
180
|
aggregation_id=aggregation_id,
|
196
181
|
human_id=human_id,
|
@@ -217,18 +202,40 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, names_to_
|
|
217
202
|
print(f"\r{total_items}/{total_items} (100.00%)")
|
218
203
|
print("\nDone.")
|
219
204
|
|
220
|
-
# Example usage
|
221
205
|
if __name__ == "__main__":
|
222
|
-
args = parse_args()
|
223
|
-
org_alias = args.org
|
224
206
|
session = create_session("boss")
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
207
|
+
human_id_to_debug = None
|
208
|
+
|
209
|
+
# Get all org_id present in the Organization table
|
210
|
+
org_ids = session.query(Organization.id).all()
|
211
|
+
org_ids = [org_id[0] for org_id in org_ids]
|
212
|
+
|
213
|
+
for org_id in org_ids:
|
214
|
+
division_ids = get_all_division_ids_for_org(session, org_id)
|
215
|
+
print(f"Aggregating referee stats for {len(division_ids)} divisions in org_id {org_id}...")
|
216
|
+
total_divisions = len(division_ids)
|
217
|
+
processed_divisions = 0
|
218
|
+
for division_id in division_ids:
|
219
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names)
|
220
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, aggregation_window='Weekly')
|
221
|
+
aggregate_referee_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, aggregation_window='Daily')
|
222
|
+
processed_divisions += 1
|
223
|
+
if human_id_to_debug is None:
|
224
|
+
print(f"\rProcessed {processed_divisions}/{total_divisions} divisions ({(processed_divisions/total_divisions)*100:.2f}%)", end="")
|
225
|
+
|
226
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names)
|
227
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, aggregation_window='Weekly')
|
228
|
+
aggregate_referee_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, aggregation_window='Daily')
|
229
|
+
|
230
|
+
# Aggregate by level
|
231
|
+
level_ids = session.query(Division.level_id).distinct().all()
|
232
|
+
level_ids = [level_id[0] for level_id in level_ids]
|
233
|
+
total_levels = len(level_ids)
|
234
|
+
processed_levels = 0
|
235
|
+
for level_id in level_ids:
|
236
|
+
if level_id is None:
|
237
|
+
continue
|
238
|
+
if human_id_to_debug is None:
|
239
|
+
print(f"\rProcessed {processed_levels}/{total_levels} levels ({(processed_levels/total_levels)*100:.2f}%)", end="")
|
240
|
+
processed_levels += 1
|
241
|
+
aggregate_referee_stats(session, aggregation_type='level', aggregation_id=level_id, names_to_filter_out=not_human_names)
|
@@ -137,7 +137,7 @@ def populate_league_ids():
|
|
137
137
|
session.commit()
|
138
138
|
print("League IDs have been populated into the Season table.")
|
139
139
|
|
140
|
-
if __name__ == "__main__":
|
140
|
+
#if __name__ == "__main__":
|
141
141
|
# delete_all_skills()
|
142
142
|
#fill_seed_skills()
|
143
143
|
#populate_season_ids() # Call the function to populate season_ids
|
@@ -17,8 +17,20 @@ class BaseStatsHuman(db.Model):
|
|
17
17
|
games_goalie = db.Column(db.Integer, default=0)
|
18
18
|
games_goalie_rank = db.Column(db.Integer, default=0)
|
19
19
|
total_in_rank = db.Column(db.Integer, default=0)
|
20
|
-
|
21
|
-
|
20
|
+
skaters_in_rank = db.Column(db.Integer, default=0)
|
21
|
+
goalies_in_rank = db.Column(db.Integer, default=0)
|
22
|
+
referees_in_rank = db.Column(db.Integer, default=0)
|
23
|
+
scorekeepers_in_rank = db.Column(db.Integer, default=0)
|
24
|
+
first_game_id = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
25
|
+
last_game_id = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
26
|
+
first_game_id_skater = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
27
|
+
last_game_id_skater = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
28
|
+
first_game_id_goalie = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
29
|
+
last_game_id_goalie = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
30
|
+
first_game_id_referee = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
31
|
+
last_game_id_referee = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
32
|
+
first_game_id_scorekeeper = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
33
|
+
last_game_id_scorekeeper = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=True)
|
22
34
|
|
23
35
|
@declared_attr
|
24
36
|
def __table_args__(cls):
|
@@ -196,6 +208,19 @@ class DivisionStatsHuman(BaseStatsHuman):
|
|
196
208
|
def get_aggregation_column(cls):
|
197
209
|
return 'division_id'
|
198
210
|
|
211
|
+
class LevelStatsHuman(BaseStatsHuman):
|
212
|
+
__tablename__ = 'level_stats_human'
|
213
|
+
level_id = db.Column(db.Integer, db.ForeignKey('levels.id'), nullable=False)
|
214
|
+
aggregation_id = synonym('level_id')
|
215
|
+
|
216
|
+
@declared_attr
|
217
|
+
def aggregation_type(cls):
|
218
|
+
return 'level'
|
219
|
+
|
220
|
+
@classmethod
|
221
|
+
def get_aggregation_column(cls):
|
222
|
+
return 'level_id'
|
223
|
+
|
199
224
|
class OrgStatsSkater(BaseStatsSkater):
|
200
225
|
__tablename__ = 'org_stats_skater'
|
201
226
|
org_id = db.Column(db.Integer, db.ForeignKey('organizations.id'), nullable=False)
|
@@ -262,6 +287,19 @@ class DivisionStatsGoalie(BaseStatsGoalie):
|
|
262
287
|
def get_aggregation_column(cls):
|
263
288
|
return 'division_id'
|
264
289
|
|
290
|
+
class LevelStatsGoalie(BaseStatsGoalie):
|
291
|
+
__tablename__ = 'level_stats_goalie'
|
292
|
+
level_id = db.Column(db.Integer, db.ForeignKey('levels.id'), nullable=False)
|
293
|
+
aggregation_id = synonym('level_id')
|
294
|
+
|
295
|
+
@declared_attr
|
296
|
+
def aggregation_type(cls):
|
297
|
+
return 'level'
|
298
|
+
|
299
|
+
@classmethod
|
300
|
+
def get_aggregation_column(cls):
|
301
|
+
return 'level_id'
|
302
|
+
|
265
303
|
|
266
304
|
class OrgStatsReferee(BaseStatsReferee):
|
267
305
|
__tablename__ = 'org_stats_referee'
|
@@ -289,6 +327,19 @@ class DivisionStatsReferee(BaseStatsReferee):
|
|
289
327
|
def get_aggregation_column(cls):
|
290
328
|
return 'division_id'
|
291
329
|
|
330
|
+
class LevelStatsReferee(BaseStatsReferee):
|
331
|
+
__tablename__ = 'level_stats_referee'
|
332
|
+
level_id = db.Column(db.Integer, db.ForeignKey('levels.id'), nullable=False)
|
333
|
+
aggregation_id = synonym('level_id')
|
334
|
+
|
335
|
+
@declared_attr
|
336
|
+
def aggregation_type(cls):
|
337
|
+
return 'level'
|
338
|
+
|
339
|
+
@classmethod
|
340
|
+
def get_aggregation_column(cls):
|
341
|
+
return 'level_id'
|
342
|
+
|
292
343
|
|
293
344
|
class OrgStatsScorekeeper(BaseStatsScorekeeper):
|
294
345
|
__tablename__ = 'org_stats_scorekeeper'
|
{hockey_blast_common_lib-0.1.32.dist-info → hockey_blast_common_lib-0.1.33.dist-info}/RECORD
RENAMED
@@ -1,7 +1,7 @@
|
|
1
1
|
hockey_blast_common_lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
hockey_blast_common_lib/aggregate_goalie_stats.py,sha256=
|
3
|
-
hockey_blast_common_lib/aggregate_human_stats.py,sha256=
|
4
|
-
hockey_blast_common_lib/aggregate_referee_stats.py,sha256=
|
2
|
+
hockey_blast_common_lib/aggregate_goalie_stats.py,sha256=GRlKfZcfQWLHfeVZjHSG4uc9RNVttJaOC9_-VncqDP8,12190
|
3
|
+
hockey_blast_common_lib/aggregate_human_stats.py,sha256=BlG-TDPEJyUsx3mYxEt50pVgCftCr1r9L0u5q9Zf4BU,23958
|
4
|
+
hockey_blast_common_lib/aggregate_referee_stats.py,sha256=BmvEiewtwT5z4GMrmhNKIPWDxPtFHLMuUZ_UNucu0YQ,11910
|
5
5
|
hockey_blast_common_lib/aggregate_skater_stats.py,sha256=jkBD5u-gJc1DTDIEuxM_qymKsrWtLagFKeEn__2rFgU,16009
|
6
6
|
hockey_blast_common_lib/assign_skater_skill.py,sha256=p-0fbodGpM8BCjKHDpxNb7BH2FcIlBsJwON844KNrUY,1817
|
7
7
|
hockey_blast_common_lib/db_connection.py,sha256=HvPxDvOj7j5H85RfslGvHVNevfg7mKCd0syJ6NX21mU,1890
|
@@ -10,12 +10,13 @@ hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz,sha256=fO5SsdPB6XYptPL
|
|
10
10
|
hockey_blast_common_lib/models.py,sha256=ebRnnvDOVNDfqAp8CA8u7uk3LCOfI3iUwOpHgzoBy0U,15984
|
11
11
|
hockey_blast_common_lib/options.py,sha256=6na8fo-5A2RBPpd_h-7dsqetOLSLoNEJg1QMYgl4jNs,792
|
12
12
|
hockey_blast_common_lib/restore_sample_db.sh,sha256=u2zKazC6vNMULkpYzI64nlneCWaGUtDHPBAU-gWgRbw,1861
|
13
|
-
hockey_blast_common_lib/skills_in_divisions.py,sha256=
|
13
|
+
hockey_blast_common_lib/skills_in_divisions.py,sha256=RR-x-D7V_lQX--2a2GHEYHtATtIOj2ACpvcEUDzVgkY,7487
|
14
14
|
hockey_blast_common_lib/skills_propagation.py,sha256=x6yy7fJ6IX3YiHqiP_v7-p_S2Expb8JJ-mWuajEFBdY,16388
|
15
|
-
hockey_blast_common_lib/stats_models.py,sha256=
|
15
|
+
hockey_blast_common_lib/stats_models.py,sha256=qvkt-XRFb4ZW7yBj7vltedzUS_YwWagm_efMRcsioSA,25120
|
16
|
+
hockey_blast_common_lib/stats_utils.py,sha256=Uv7xv9Eph2g7kQFOpTJujMm8P-UB42IDbAw5WkjJA0g,267
|
16
17
|
hockey_blast_common_lib/utils.py,sha256=odDJWCK0BgbResXeoUzxbVChjaxcXr168ZxbrAw3L_8,3752
|
17
18
|
hockey_blast_common_lib/wsgi.py,sha256=7LGUzioigviJp-EUhSEaQcd4jBae0mxbkyBscQfZhlc,730
|
18
|
-
hockey_blast_common_lib-0.1.
|
19
|
-
hockey_blast_common_lib-0.1.
|
20
|
-
hockey_blast_common_lib-0.1.
|
21
|
-
hockey_blast_common_lib-0.1.
|
19
|
+
hockey_blast_common_lib-0.1.33.dist-info/METADATA,sha256=AqXHHG_rYxzkm7oBWB7W1Ew94nm51nTCN1YCetrOX6Q,318
|
20
|
+
hockey_blast_common_lib-0.1.33.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
21
|
+
hockey_blast_common_lib-0.1.33.dist-info/top_level.txt,sha256=wIR4LIkE40npoA2QlOdfCYlgFeGbsHR8Z6r0h46Vtgc,24
|
22
|
+
hockey_blast_common_lib-0.1.33.dist-info/RECORD,,
|
File without changes
|
{hockey_blast_common_lib-0.1.32.dist-info → hockey_blast_common_lib-0.1.33.dist-info}/top_level.txt
RENAMED
File without changes
|