hockey-blast-common-lib 0.1.6__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.
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/PKG-INFO +1 -1
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/aggregate_goalie_stats.py +161 -0
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/aggregate_human_stats.py +291 -0
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/aggregate_referee_stats.py +229 -0
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/aggregate_skater_stats.py +323 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/db_connection.py +5 -17
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/models.py +353 -0
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/options.py +22 -0
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/stats_models.py +563 -0
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/utils.py +65 -0
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib/wsgi.py +16 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/PKG-INFO +1 -1
- hockey_blast_common_lib-0.1.8/hockey_blast_common_lib.egg-info/SOURCES.txt +19 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/setup.py +1 -1
- hockey_blast_common_lib-0.1.6/hockey_blast_common_lib/models.py +0 -880
- hockey_blast_common_lib-0.1.6/hockey_blast_common_lib.egg-info/SOURCES.txt +0 -11
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/README.md +0 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/__init__.py +0 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib/db_utils.py +0 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/dependency_links.txt +0 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/requires.txt +0 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/hockey_blast_common_lib.egg-info/top_level.txt +0 -0
- {hockey_blast_common_lib-0.1.6 → hockey_blast_common_lib-0.1.8}/setup.cfg +0 -0
@@ -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')
|