hockey-blast-common-lib 0.1.7__tar.gz → 0.1.8__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.
Files changed (21) hide show
  1. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/PKG-INFO +1 -1
  2. hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/aggregate_goalie_stats.py +161 -0
  3. hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/aggregate_human_stats.py +291 -0
  4. hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/aggregate_referee_stats.py +229 -0
  5. hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/aggregate_skater_stats.py +323 -0
  6. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/db_connection.py +9 -0
  7. hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/options.py +22 -0
  8. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/stats_models.py +32 -1
  9. hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/utils.py +65 -0
  10. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/PKG-INFO +1 -1
  11. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/SOURCES.txt +6 -0
  12. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/setup.py +1 -1
  13. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/README.md +0 -0
  14. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/__init__.py +0 -0
  15. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/db_utils.py +0 -0
  16. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/models.py +0 -0
  17. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/wsgi.py +0 -0
  18. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/dependency_links.txt +0 -0
  19. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/requires.txt +0 -0
  20. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/top_level.txt +0 -0
  21. {hockey_blast_common_lib-0.1.7 → hockey_blast_common_lib-0.1.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hockey-blast-common-lib
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Common library for shared functionality and DB models
5
5
  Author: Pavel Kletskov
6
6
  Author-email: kletskov@gmail.com
@@ -0,0 +1,161 @@
1
+ import sys, os
2
+ from datetime import datetime, timedelta
3
+ import sqlalchemy
4
+ from options import not_human_names
5
+ from models import Game, GameRoster
6
+ from stats_models import OrgStatsGoalie, DivisionStatsGoalie, OrgStatsWeeklyGoalie, OrgStatsDailyGoalie, DivisionStatsWeeklyGoalie, DivisionStatsDailyGoalie
7
+ from db_connection import create_session
8
+ from sqlalchemy.sql import func, case
9
+ from options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS
10
+ from 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
11
+
12
+ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, names_to_filter_out, aggregation_window=None):
13
+ human_ids_to_filter = get_human_ids_by_names(session, names_to_filter_out)
14
+
15
+ if aggregation_type == 'org':
16
+ if aggregation_window == 'Daily':
17
+ StatsModel = OrgStatsDailyGoalie
18
+ elif aggregation_window == 'Weekly':
19
+ StatsModel = OrgStatsWeeklyGoalie
20
+ else:
21
+ StatsModel = OrgStatsGoalie
22
+ min_games = MIN_GAMES_FOR_ORG_STATS
23
+ filter_condition = Game.org_id == aggregation_id
24
+ elif aggregation_type == 'division':
25
+ if aggregation_window == 'Daily':
26
+ StatsModel = DivisionStatsDailyGoalie
27
+ elif aggregation_window == 'Weekly':
28
+ StatsModel = DivisionStatsWeeklyGoalie
29
+ else:
30
+ StatsModel = DivisionStatsGoalie
31
+ min_games = MIN_GAMES_FOR_DIVISION_STATS
32
+ filter_condition = Game.division_id == aggregation_id
33
+ else:
34
+ raise ValueError("Invalid aggregation type")
35
+
36
+ # Apply aggregation window filter
37
+ if aggregation_window:
38
+ last_game_datetime = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, Game.status.like('Final%')).scalar()
39
+ if last_game_datetime:
40
+ last_game_datetime = datetime.strptime(last_game_datetime, '%Y-%m-%d %H:%M:%S')
41
+ if aggregation_window == 'Daily':
42
+ start_datetime = last_game_datetime - timedelta(days=1)
43
+ elif aggregation_window == 'Weekly':
44
+ start_datetime = last_game_datetime - timedelta(weeks=1)
45
+ else:
46
+ start_datetime = None
47
+ if start_datetime:
48
+ game_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP).between(start_datetime, last_game_datetime)
49
+ filter_condition = filter_condition & game_window_filter
50
+
51
+ # Delete existing items from the stats table
52
+ session.query(StatsModel).filter(StatsModel.aggregation_id == aggregation_id).delete()
53
+ session.commit()
54
+
55
+ # Aggregate games played, goals allowed, and shots faced for each goalie
56
+ goalie_stats = session.query(
57
+ GameRoster.human_id,
58
+ func.count(Game.id).label('games_played'),
59
+ func.sum(case((GameRoster.team_id == Game.home_team_id, Game.visitor_final_score), else_=Game.home_final_score)).label('goals_allowed'),
60
+ 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'),
61
+ func.array_agg(Game.id).label('game_ids')
62
+ ).join(Game, GameRoster.game_id == Game.id).filter(filter_condition, GameRoster.role == 'G').group_by(GameRoster.human_id).all()
63
+
64
+ # Combine the results
65
+ stats_dict = {}
66
+ for stat in goalie_stats:
67
+ if stat.human_id in human_ids_to_filter:
68
+ continue
69
+ key = (aggregation_id, stat.human_id)
70
+ stats_dict[key] = {
71
+ 'games_played': stat.games_played,
72
+ 'goals_allowed': stat.goals_allowed if stat.goals_allowed is not None else 0,
73
+ 'shots_faced': stat.shots_faced if stat.shots_faced is not None else 0,
74
+ 'goals_allowed_per_game': 0.0,
75
+ 'save_percentage': 0.0,
76
+ 'game_ids': stat.game_ids,
77
+ 'first_game_id': None,
78
+ 'last_game_id': None
79
+ }
80
+
81
+ # Calculate per game stats
82
+ for key, stat in stats_dict.items():
83
+ if stat['games_played'] > 0:
84
+ stat['goals_allowed_per_game'] = stat['goals_allowed'] / stat['games_played']
85
+ stat['save_percentage'] = (stat['shots_faced'] - stat['goals_allowed']) / stat['shots_faced'] if stat['shots_faced'] > 0 else 0.0
86
+
87
+ # Ensure all keys have valid human_id values
88
+ stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
89
+
90
+ # Populate first_game_id and last_game_id
91
+ for key, stat in stats_dict.items():
92
+ all_game_ids = stat['game_ids']
93
+ if all_game_ids:
94
+ first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
95
+ last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
96
+ stat['first_game_id'] = first_game.id if first_game else None
97
+ stat['last_game_id'] = last_game.id if last_game else None
98
+
99
+ # Calculate total_in_rank
100
+ total_in_rank = len(stats_dict)
101
+
102
+ # Assign ranks
103
+ def assign_ranks(stats_dict, field):
104
+ sorted_stats = sorted(stats_dict.items(), key=lambda x: x[1][field], reverse=True)
105
+ for rank, (key, stat) in enumerate(sorted_stats, start=1):
106
+ stats_dict[key][f'{field}_rank'] = rank
107
+
108
+ assign_ranks(stats_dict, 'games_played')
109
+ assign_ranks(stats_dict, 'goals_allowed')
110
+ assign_ranks(stats_dict, 'goals_allowed_per_game')
111
+ assign_ranks(stats_dict, 'shots_faced')
112
+ assign_ranks(stats_dict, 'save_percentage')
113
+
114
+ # Insert aggregated stats into the appropriate table with progress output
115
+ total_items = len(stats_dict)
116
+ batch_size = 1000
117
+ for i, (key, stat) in enumerate(stats_dict.items(), 1):
118
+ aggregation_id, human_id = key
119
+ if stat['games_played'] < min_games:
120
+ continue
121
+ goalie_stat = StatsModel(
122
+ aggregation_id=aggregation_id,
123
+ human_id=human_id,
124
+ games_played=stat['games_played'],
125
+ goals_allowed=stat['goals_allowed'],
126
+ goals_allowed_per_game=stat['goals_allowed_per_game'],
127
+ shots_faced=stat['shots_faced'],
128
+ save_percentage=stat['save_percentage'],
129
+ games_played_rank=stat['games_played_rank'],
130
+ goals_allowed_rank=stat['goals_allowed_rank'],
131
+ goals_allowed_per_game_rank=stat['goals_allowed_per_game_rank'],
132
+ shots_faced_rank=stat['shots_faced_rank'],
133
+ save_percentage_rank=stat['save_percentage_rank'],
134
+ total_in_rank=total_in_rank,
135
+ first_game_id=stat['first_game_id'],
136
+ last_game_id=stat['last_game_id']
137
+ )
138
+ session.add(goalie_stat)
139
+ # Commit in batches
140
+ if i % batch_size == 0:
141
+ session.commit()
142
+ print(f"\r{i}/{total_items} ({(i/total_items)*100:.2f}%)", end="")
143
+ session.commit()
144
+ print(f"\r{total_items}/{total_items} (100.00%)")
145
+ print("\nDone.")
146
+
147
+ # Example usage
148
+ if __name__ == "__main__":
149
+ args = parse_args()
150
+ org_alias = args.org
151
+ session = create_session("boss")
152
+ org_id = get_org_id_from_alias(session, org_alias)
153
+ division_ids = get_division_ids_for_last_season_in_all_leagues(session, org_id)
154
+ print(f"Aggregating goalie stats for {len(division_ids)} divisions in {org_alias}...")
155
+ for division_id in division_ids:
156
+ aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names)
157
+ aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, aggregation_window='Daily')
158
+ aggregate_goalie_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, aggregation_window='Weekly')
159
+ aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names)
160
+ aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, aggregation_window='Daily')
161
+ aggregate_goalie_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, aggregation_window='Weekly')
@@ -0,0 +1,291 @@
1
+ import sys, os
2
+ from datetime import datetime, timedelta
3
+ import sqlalchemy
4
+ from models import Game, GameRoster
5
+ from stats_models import OrgStatsHuman, DivisionStatsHuman, OrgStatsDailyHuman, OrgStatsWeeklyHuman, DivisionStatsDailyHuman, DivisionStatsWeeklyHuman
6
+ from db_connection import create_session
7
+ from sqlalchemy.sql import func, case
8
+ from options import parse_args, MIN_GAMES_FOR_ORG_STATS, MIN_GAMES_FOR_DIVISION_STATS, not_human_names
9
+ from utils import get_org_id_from_alias, get_human_ids_by_names, get_division_ids_for_last_season_in_all_leagues
10
+ from db_utils import get_fake_human_for_stats
11
+
12
+ def aggregate_human_stats(session, aggregation_type, aggregation_id, names_to_filter_out, human_id_filter=None, aggregation_window=None):
13
+ human_ids_to_filter = get_human_ids_by_names(session, names_to_filter_out)
14
+
15
+ if aggregation_type == 'org':
16
+ if aggregation_window == 'Daily':
17
+ StatsModel = OrgStatsDailyHuman
18
+ elif aggregation_window == 'Weekly':
19
+ StatsModel = OrgStatsWeeklyHuman
20
+ else:
21
+ StatsModel = OrgStatsHuman
22
+ min_games = MIN_GAMES_FOR_ORG_STATS
23
+ filter_condition = Game.org_id == aggregation_id
24
+ elif aggregation_type == 'division':
25
+ if aggregation_window == 'Daily':
26
+ StatsModel = DivisionStatsDailyHuman
27
+ elif aggregation_window == 'Weekly':
28
+ StatsModel = DivisionStatsWeeklyHuman
29
+ else:
30
+ StatsModel = DivisionStatsHuman
31
+ min_games = MIN_GAMES_FOR_DIVISION_STATS
32
+ filter_condition = Game.division_id == aggregation_id
33
+ else:
34
+ raise ValueError("Invalid aggregation type")
35
+
36
+ # Delete existing items from the stats table
37
+ session.query(StatsModel).filter(StatsModel.aggregation_id == aggregation_id).delete()
38
+ session.commit()
39
+
40
+ # Filter for specific human_id if provided
41
+ human_filter = []
42
+ if human_id_filter:
43
+ human_filter = [GameRoster.human_id == human_id_filter]
44
+
45
+ # Filter games by status
46
+ game_status_filter = Game.status.like('Final%')
47
+
48
+ # Apply aggregation window filter
49
+ if aggregation_window:
50
+ last_game_datetime = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, game_status_filter).scalar()
51
+ if last_game_datetime:
52
+ last_game_datetime = datetime.strptime(last_game_datetime, '%Y-%m-%d %H:%M:%S')
53
+ if aggregation_window == 'Daily':
54
+ start_datetime = last_game_datetime - timedelta(days=1)
55
+ elif aggregation_window == 'Weekly':
56
+ start_datetime = last_game_datetime - timedelta(weeks=1)
57
+ else:
58
+ start_datetime = None
59
+ if start_datetime:
60
+ game_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP).between(start_datetime, last_game_datetime)
61
+ filter_condition = filter_condition & game_window_filter
62
+
63
+ # Aggregate games played for each human in each role
64
+ human_stats = session.query(
65
+ GameRoster.human_id,
66
+ func.count(func.distinct(case((GameRoster.role != 'G', Game.id), else_=None))).label('games_skater'),
67
+ func.count(func.distinct(case((GameRoster.role == 'G', Game.id), else_=None))).label('games_goalie'),
68
+ func.array_agg(func.distinct(Game.id)).label('game_ids')
69
+ ).join(Game, GameRoster.game_id == Game.id).filter(filter_condition, game_status_filter, *human_filter).group_by(GameRoster.human_id).all()
70
+
71
+ # Aggregate referee and scorekeeper games from Game table
72
+ referee_stats = session.query(
73
+ Game.referee_1_id.label('human_id'),
74
+ func.count(func.distinct(Game.id)).label('games_referee'),
75
+ func.array_agg(func.distinct(Game.id)).label('referee_game_ids')
76
+ ).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.referee_1_id).all()
77
+
78
+ referee_stats_2 = session.query(
79
+ Game.referee_2_id.label('human_id'),
80
+ func.count(func.distinct(Game.id)).label('games_referee'),
81
+ func.array_agg(func.distinct(Game.id)).label('referee_game_ids')
82
+ ).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.referee_2_id).all()
83
+
84
+ scorekeeper_stats = session.query(
85
+ Game.scorekeeper_id.label('human_id'),
86
+ func.count(func.distinct(Game.id)).label('games_scorekeeper'),
87
+ func.array_agg(func.distinct(Game.id)).label('scorekeeper_game_ids')
88
+ ).filter(filter_condition, game_status_filter, *human_filter).group_by(Game.scorekeeper_id).all()
89
+
90
+ # Combine the results
91
+ stats_dict = {}
92
+ for stat in human_stats:
93
+ if stat.human_id in human_ids_to_filter:
94
+ continue
95
+ key = (aggregation_id, stat.human_id)
96
+ stats_dict[key] = {
97
+ 'games_total': stat.games_skater + stat.games_goalie,
98
+ 'games_skater': stat.games_skater,
99
+ 'games_goalie': stat.games_goalie,
100
+ 'games_referee': 0,
101
+ 'games_scorekeeper': 0,
102
+ 'game_ids': stat.game_ids,
103
+ 'referee_game_ids': [],
104
+ 'scorekeeper_game_ids': []
105
+ }
106
+
107
+ for stat in referee_stats:
108
+ if stat.human_id in human_ids_to_filter:
109
+ continue
110
+ key = (aggregation_id, stat.human_id)
111
+ if key not in stats_dict:
112
+ stats_dict[key] = {
113
+ 'games_total': stat.games_referee,
114
+ 'games_skater': 0,
115
+ 'games_goalie': 0,
116
+ 'games_referee': stat.games_referee,
117
+ 'games_scorekeeper': 0,
118
+ 'game_ids': [],
119
+ 'referee_game_ids': stat.referee_game_ids,
120
+ 'scorekeeper_game_ids': []
121
+ }
122
+ else:
123
+ stats_dict[key]['games_referee'] += stat.games_referee
124
+ stats_dict[key]['games_total'] += stat.games_referee
125
+ stats_dict[key]['referee_game_ids'] += stat.referee_game_ids
126
+
127
+ for stat in referee_stats_2:
128
+ if stat.human_id in human_ids_to_filter:
129
+ continue
130
+ key = (aggregation_id, stat.human_id)
131
+ if key not in stats_dict:
132
+ stats_dict[key] = {
133
+ 'games_total': stat.games_referee,
134
+ 'games_skater': 0,
135
+ 'games_goalie': 0,
136
+ 'games_referee': stat.games_referee,
137
+ 'games_scorekeeper': 0,
138
+ 'game_ids': [],
139
+ 'referee_game_ids': stat.referee_game_ids,
140
+ 'scorekeeper_game_ids': []
141
+ }
142
+ else:
143
+ stats_dict[key]['games_referee'] += stat.games_referee
144
+ stats_dict[key]['games_total'] += stat.games_referee
145
+ stats_dict[key]['referee_game_ids'] += stat.referee_game_ids
146
+
147
+ for stat in scorekeeper_stats:
148
+ if stat.human_id in human_ids_to_filter:
149
+ continue
150
+ key = (aggregation_id, stat.human_id)
151
+ if key not in stats_dict:
152
+ stats_dict[key] = {
153
+ 'games_total': stat.games_scorekeeper,
154
+ 'games_skater': 0,
155
+ 'games_goalie': 0,
156
+ 'games_referee': 0,
157
+ 'games_scorekeeper': stat.games_scorekeeper,
158
+ 'game_ids': [],
159
+ 'referee_game_ids': [],
160
+ 'scorekeeper_game_ids': stat.scorekeeper_game_ids
161
+ }
162
+ else:
163
+ stats_dict[key]['games_scorekeeper'] += stat.games_scorekeeper
164
+ stats_dict[key]['games_total'] += stat.games_scorekeeper
165
+ stats_dict[key]['scorekeeper_game_ids'] += stat.scorekeeper_game_ids
166
+
167
+ # Ensure all keys have valid human_id values
168
+ stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
169
+
170
+ # Calculate total_in_rank
171
+ total_in_rank = len(stats_dict)
172
+
173
+ # Assign ranks
174
+ def assign_ranks(stats_dict, field):
175
+ sorted_stats = sorted(stats_dict.items(), key=lambda x: x[1][field], reverse=True)
176
+ for rank, (key, stat) in enumerate(sorted_stats, start=1):
177
+ stats_dict[key][f'{field}_rank'] = rank
178
+
179
+ assign_ranks(stats_dict, 'games_total')
180
+ assign_ranks(stats_dict, 'games_skater')
181
+ assign_ranks(stats_dict, 'games_goalie')
182
+ assign_ranks(stats_dict, 'games_referee')
183
+ assign_ranks(stats_dict, 'games_scorekeeper')
184
+
185
+ # Populate first_game_id and last_game_id
186
+ for key, stat in stats_dict.items():
187
+ all_game_ids = stat['game_ids'] + stat['referee_game_ids'] + stat['scorekeeper_game_ids']
188
+ if all_game_ids:
189
+ first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
190
+ last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
191
+ stat['first_game_id'] = first_game.id if first_game else None
192
+ stat['last_game_id'] = last_game.id if last_game else None
193
+
194
+ # Insert aggregated stats into the appropriate table with progress output
195
+ total_items = len(stats_dict)
196
+ batch_size = 1000
197
+ for i, (key, stat) in enumerate(stats_dict.items(), 1):
198
+ aggregation_id, human_id = key
199
+ if human_id_filter and human_id != human_id_filter:
200
+ continue
201
+ if stat['games_total'] < min_games:
202
+ continue
203
+
204
+ human_stat = StatsModel(
205
+ aggregation_id=aggregation_id,
206
+ human_id=human_id,
207
+ games_total=stat['games_total'],
208
+ games_total_rank=stat['games_total_rank'],
209
+ games_skater=stat['games_skater'],
210
+ games_skater_rank=stat['games_skater_rank'],
211
+ games_goalie=stat['games_goalie'],
212
+ games_goalie_rank=stat['games_goalie_rank'],
213
+ games_referee=stat['games_referee'],
214
+ games_referee_rank=stat['games_referee_rank'],
215
+ games_scorekeeper=stat['games_scorekeeper'],
216
+ games_scorekeeper_rank=stat['games_scorekeeper_rank'],
217
+ total_in_rank=total_in_rank,
218
+ first_game_id=stat['first_game_id'],
219
+ last_game_id=stat['last_game_id']
220
+ )
221
+ session.add(human_stat)
222
+ # Commit in batches
223
+ if i % batch_size == 0:
224
+ session.commit()
225
+ print(f"\r{i}/{total_items} ({(i/total_items)*100:.2f}%)", end="")
226
+ session.commit()
227
+
228
+ # Fetch fake human ID for overall stats
229
+ fake_human_id = get_fake_human_for_stats(session)
230
+
231
+ # Calculate overall stats
232
+ overall_stats = {
233
+ 'games_total': sum(stat['games_total'] for stat in stats_dict.values()),
234
+ 'games_skater': sum(stat['games_skater'] for stat in stats_dict.values()),
235
+ 'games_goalie': sum(stat['games_goalie'] for stat in stats_dict.values()),
236
+ 'games_referee': sum(stat['games_referee'] for stat in stats_dict.values()),
237
+ 'games_scorekeeper': sum(stat['games_scorekeeper'] for stat in stats_dict.values()),
238
+ 'total_in_rank': total_in_rank,
239
+ 'first_game_id': None,
240
+ 'last_game_id': None
241
+ }
242
+
243
+ # Populate first_game_id and last_game_id for overall stats
244
+ all_game_ids = [game_id for stat in stats_dict.values() for game_id in stat['game_ids'] + stat['referee_game_ids'] + stat['scorekeeper_game_ids']]
245
+ if all_game_ids:
246
+ first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
247
+ last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
248
+ overall_stats['first_game_id'] = first_game.id if first_game else None
249
+ overall_stats['last_game_id'] = last_game.id if last_game else None
250
+
251
+ # Insert overall stats for the fake human
252
+ overall_human_stat = StatsModel(
253
+ aggregation_id=aggregation_id,
254
+ human_id=fake_human_id,
255
+ games_total=overall_stats['games_total'],
256
+ games_total_rank=0, # Overall stats do not need a rank
257
+ games_skater=overall_stats['games_skater'],
258
+ games_skater_rank=0, # Overall stats do not need a rank
259
+ games_goalie=overall_stats['games_goalie'],
260
+ games_goalie_rank=0, # Overall stats do not need a rank
261
+ games_referee=overall_stats['games_referee'],
262
+ games_referee_rank=0, # Overall stats do not need a rank
263
+ games_scorekeeper=overall_stats['games_scorekeeper'],
264
+ games_scorekeeper_rank=0, # Overall stats do not need a rank
265
+ total_in_rank=overall_stats['total_in_rank'],
266
+ first_game_id=overall_stats['first_game_id'],
267
+ last_game_id=overall_stats['last_game_id']
268
+ )
269
+ session.add(overall_human_stat)
270
+ session.commit()
271
+
272
+ print(f"\r{total_items}/{total_items} (100.00%)")
273
+ print("\nDone.")
274
+
275
+ # Example usage
276
+ if __name__ == "__main__":
277
+ args = parse_args()
278
+ org_alias=args.org
279
+ session = create_session("boss")
280
+ org_id = get_org_id_from_alias(session, org_alias)
281
+
282
+ division_ids = get_division_ids_for_last_season_in_all_leagues(session, org_id)
283
+ print(f"Aggregating human stats for {len(division_ids)} divisions in {org_alias}...")
284
+ for division_id in division_ids:
285
+ aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, human_id_filter=None)
286
+ aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, human_id_filter=None, aggregation_window='Daily')
287
+ aggregate_human_stats(session, aggregation_type='division', aggregation_id=division_id, names_to_filter_out=not_human_names, human_id_filter=None, aggregation_window='Weekly')
288
+
289
+ aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, human_id_filter=None)
290
+ aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, human_id_filter=None, aggregation_window='Daily')
291
+ aggregate_human_stats(session, aggregation_type='org', aggregation_id=org_id, names_to_filter_out=not_human_names, human_id_filter=None, aggregation_window='Weekly')