hockey-blast-common-lib 0.1.62__tar.gz → 0.1.63__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 (34) hide show
  1. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/PKG-INFO +1 -1
  2. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/aggregate_goalie_stats.py +8 -12
  3. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/aggregate_human_stats.py +3 -3
  4. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/aggregate_referee_stats.py +11 -20
  5. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/aggregate_scorekeeper_stats.py +7 -10
  6. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/aggregate_skater_stats.py +81 -49
  7. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz +0 -0
  8. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/models.py +1 -0
  9. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib.egg-info/PKG-INFO +1 -1
  10. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/setup.py +1 -1
  11. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/MANIFEST.in +0 -0
  12. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/README.md +0 -0
  13. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/__init__.py +0 -0
  14. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/aggregate_all_stats.py +0 -0
  15. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/aggregate_h2h_stats.py +0 -0
  16. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/aggregate_s2s_stats.py +0 -0
  17. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/assign_skater_skill.py +0 -0
  18. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/db_connection.py +0 -0
  19. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/dump_sample_db.sh +0 -0
  20. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/h2h_models.py +0 -0
  21. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/options.py +0 -0
  22. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/progress_utils.py +0 -0
  23. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/restore_sample_db.sh +0 -0
  24. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/skills_in_divisions.py +0 -0
  25. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/skills_propagation.py +0 -0
  26. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/stats_models.py +0 -0
  27. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/stats_utils.py +0 -0
  28. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/utils.py +0 -0
  29. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib/wsgi.py +0 -0
  30. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib.egg-info/SOURCES.txt +0 -0
  31. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib.egg-info/dependency_links.txt +0 -0
  32. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib.egg-info/requires.txt +0 -0
  33. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/hockey_blast_common_lib.egg-info/top_level.txt +0 -0
  34. {hockey_blast_common_lib-0.1.62 → hockey_blast_common_lib-0.1.63}/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.62
3
+ Version: 0.1.63
4
4
  Summary: Common library for shared functionality and DB models
5
5
  Author: Pavel Kletskov
6
6
  Author-email: kletskov@gmail.com
@@ -78,23 +78,19 @@ def aggregate_goalie_stats(session, aggregation_type, aggregation_id, debug_huma
78
78
 
79
79
 
80
80
  # Aggregate games played, goals allowed, and shots faced for each goalie using GoalieSaves table
81
- # games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
82
- # games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
81
+ # Filter games by status upfront for performance (avoid CASE statements)
82
+ # Only count games with these statuses: FINAL, FINAL_SO, FORFEIT, NOEVENTS
83
83
  query = session.query(
84
84
  GoalieSaves.goalie_id.label('human_id'),
85
- func.count(GoalieSaves.game_id).label('games_played'), # DEPRECATED - will be replaced by games_participated
86
- func.sum(case(
87
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
88
- else_=0
89
- )).label('games_participated'),
90
- func.sum(case(
91
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
92
- else_=0
93
- )).label('games_with_stats'),
85
+ func.count(GoalieSaves.game_id).label('games_played'),
86
+ func.count(GoalieSaves.game_id).label('games_participated'), # Same as games_played after filtering
87
+ func.count(GoalieSaves.game_id).label('games_with_stats'), # Same as games_played after filtering
94
88
  func.sum(GoalieSaves.goals_allowed).label('goals_allowed'),
95
89
  func.sum(GoalieSaves.shots_against).label('shots_faced'),
96
90
  func.array_agg(GoalieSaves.game_id).label('game_ids')
97
- ).join(Game, GoalieSaves.game_id == Game.id).join(Division, Game.division_id == Division.id).filter(filter_condition)
91
+ ).join(Game, GoalieSaves.game_id == Game.id).filter(
92
+ Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS])
93
+ ).join(Division, Game.division_id == Division.id).filter(filter_condition)
98
94
 
99
95
  # Filter for specific human_id if provided
100
96
  if debug_human_id:
@@ -61,7 +61,7 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, human_id_fi
61
61
 
62
62
  # Apply aggregation window filter
63
63
  if aggregation_window:
64
- last_game_datetime_str = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, Game.status.like('Final%')).scalar()
64
+ last_game_datetime_str = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, (Game.status.like('Final%')) | (Game.status == 'NOEVENTS')).scalar()
65
65
  start_datetime = get_start_datetime(last_game_datetime_str, aggregation_window)
66
66
  if start_datetime:
67
67
  game_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP).between(start_datetime, last_game_datetime_str)
@@ -75,8 +75,8 @@ def aggregate_human_stats(session, aggregation_type, aggregation_id, human_id_fi
75
75
  if human_id_filter:
76
76
  human_filter = [GameRoster.human_id == human_id_filter]
77
77
 
78
- # Filter games by status
79
- game_status_filter = Game.status.like('Final%')
78
+ # Filter games by status - include both Final and NOEVENTS games
79
+ game_status_filter = (Game.status.like('Final%')) | (Game.status == 'NOEVENTS')
80
80
 
81
81
  # Aggregate skater games played
82
82
  skater_stats = session.query(
@@ -80,33 +80,24 @@ def aggregate_referee_stats(session, aggregation_type, aggregation_id, aggregati
80
80
  # Aggregate games reffed for each referee
81
81
  # games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
82
82
  # games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
83
+ # Filter by game status upfront for performance
84
+ status_filter = Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS])
85
+
83
86
  games_reffed_stats = session.query(
84
87
  Game.referee_1_id.label('human_id'),
85
- func.count(Game.id).label('games_reffed'), # DEPRECATED - will be replaced by games_participated
86
- func.sum(case(
87
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
88
- else_=0
89
- )).label('games_participated'),
90
- func.sum(case(
91
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
92
- else_=0
93
- )).label('games_with_stats'),
88
+ func.count(Game.id).label('games_reffed'),
89
+ func.count(Game.id).label('games_participated'), # Same as games_reffed after filtering
90
+ func.count(Game.id).label('games_with_stats'), # Same as games_reffed after filtering
94
91
  func.array_agg(Game.id).label('game_ids')
95
- ).filter(filter_condition).group_by(Game.referee_1_id).all()
92
+ ).filter(filter_condition, status_filter).group_by(Game.referee_1_id).all()
96
93
 
97
94
  games_reffed_stats_2 = session.query(
98
95
  Game.referee_2_id.label('human_id'),
99
- func.count(Game.id).label('games_reffed'), # DEPRECATED - will be replaced by games_participated
100
- func.sum(case(
101
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
102
- else_=0
103
- )).label('games_participated'),
104
- func.sum(case(
105
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
106
- else_=0
107
- )).label('games_with_stats'),
96
+ func.count(Game.id).label('games_reffed'),
97
+ func.count(Game.id).label('games_participated'), # Same as games_reffed after filtering
98
+ func.count(Game.id).label('games_with_stats'), # Same as games_reffed after filtering
108
99
  func.array_agg(Game.id).label('game_ids')
109
- ).filter(filter_condition).group_by(Game.referee_2_id).all()
100
+ ).filter(filter_condition, status_filter).group_by(Game.referee_2_id).all()
110
101
 
111
102
  # Aggregate penalties given for each referee
112
103
  penalties_given_stats = session.query(
@@ -97,17 +97,12 @@ def aggregate_scorekeeper_stats(session, aggregation_type, aggregation_id, aggre
97
97
  # Aggregate scorekeeper quality data for each human
98
98
  # games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
99
99
  # games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
100
+ # Filter by game status upfront for performance
100
101
  scorekeeper_quality_stats = session.query(
101
102
  ScorekeeperSaveQuality.scorekeeper_id.label('human_id'),
102
- func.count(ScorekeeperSaveQuality.game_id).label('games_recorded'), # DEPRECATED - will be replaced by games_participated
103
- func.sum(case(
104
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
105
- else_=0
106
- )).label('games_participated'),
107
- func.sum(case(
108
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
109
- else_=0
110
- )).label('games_with_stats'),
103
+ func.count(ScorekeeperSaveQuality.game_id).label('games_recorded'),
104
+ func.count(ScorekeeperSaveQuality.game_id).label('games_participated'), # Same as games_recorded after filtering
105
+ func.count(ScorekeeperSaveQuality.game_id).label('games_with_stats'), # Same as games_recorded after filtering
111
106
  func.sum(ScorekeeperSaveQuality.total_saves_recorded).label('total_saves_recorded'),
112
107
  func.avg(ScorekeeperSaveQuality.total_saves_recorded).label('avg_saves_per_game'),
113
108
  func.avg(ScorekeeperSaveQuality.max_saves_per_5sec).label('avg_max_saves_per_5sec'),
@@ -115,7 +110,9 @@ def aggregate_scorekeeper_stats(session, aggregation_type, aggregation_id, aggre
115
110
  func.max(ScorekeeperSaveQuality.max_saves_per_5sec).label('peak_max_saves_per_5sec'),
116
111
  func.max(ScorekeeperSaveQuality.max_saves_per_20sec).label('peak_max_saves_per_20sec'),
117
112
  func.array_agg(ScorekeeperSaveQuality.game_id).label('game_ids')
118
- ).join(Game, Game.id == ScorekeeperSaveQuality.game_id)
113
+ ).join(Game, Game.id == ScorekeeperSaveQuality.game_id).filter(
114
+ Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS])
115
+ )
119
116
 
120
117
 
121
118
  scorekeeper_quality_stats = scorekeeper_quality_stats.filter(filter_condition).group_by(ScorekeeperSaveQuality.scorekeeper_id).all()
@@ -29,45 +29,54 @@ def calculate_current_point_streak(session, human_id, filter_condition):
29
29
  Calculate the current point streak for a player.
30
30
  A point streak is consecutive games (from the most recent game backward) where the player had at least one point.
31
31
  Returns a tuple: (streak_length, average_points_during_streak)
32
+
33
+ Optimized to use CASE statements for conditional aggregation in a single query.
32
34
  """
33
- # Get all games for this player ordered by date/time descending (most recent first)
34
- games = session.query(Game).join(GameRoster, Game.id == GameRoster.game_id).filter(
35
+ # Get all games with their point totals in ONE query using CASE for conditional counting
36
+ game_points = session.query(
37
+ Game.id,
38
+ Game.date,
39
+ Game.time,
40
+ func.sum(case((Goal.goal_scorer_id == human_id, 1), else_=0)).label('goals'),
41
+ func.sum(case(
42
+ ((Goal.assist_1_id == human_id) | (Goal.assist_2_id == human_id), 1),
43
+ else_=0
44
+ )).label('assists')
45
+ ).join(
46
+ GameRoster, Game.id == GameRoster.game_id
47
+ ).outerjoin(
48
+ Goal, Game.id == Goal.game_id
49
+ ).filter(
35
50
  GameRoster.human_id == human_id,
36
51
  ~GameRoster.role.ilike('g'), # Exclude goalie games
37
52
  filter_condition,
38
- Game.status.like('Final%') # Only final games
39
- ).order_by(Game.date.desc(), Game.time.desc()).all()
40
-
41
- if not games:
53
+ (Game.status.like('Final%')) | (Game.status == 'NOEVENTS') # Include Final and NOEVENTS games
54
+ ).group_by(
55
+ Game.id, Game.date, Game.time
56
+ ).order_by(
57
+ Game.date.desc(), Game.time.desc()
58
+ ).all()
59
+
60
+ if not game_points:
42
61
  return 0, 0.0
43
-
62
+
44
63
  current_streak = 0
45
64
  total_points_in_streak = 0
46
-
47
- for game in games:
48
- # Check if the player had any points in this game
49
- goals = session.query(Goal).filter(
50
- Goal.game_id == game.id,
51
- Goal.goal_scorer_id == human_id
52
- ).count()
53
-
54
- assists = session.query(Goal).filter(
55
- Goal.game_id == game.id,
56
- ((Goal.assist_1_id == human_id) | (Goal.assist_2_id == human_id))
57
- ).count()
58
-
59
- total_points = goals + assists
60
-
65
+
66
+ # Iterate through games from most recent to oldest
67
+ for game in game_points:
68
+ total_points = (game.goals or 0) + (game.assists or 0)
69
+
61
70
  if total_points > 0:
62
71
  current_streak += 1
63
72
  total_points_in_streak += total_points
64
73
  else:
65
74
  # Streak is broken, stop counting
66
75
  break
67
-
76
+
68
77
  # Calculate average points during streak
69
78
  avg_points_during_streak = total_points_in_streak / current_streak if current_streak > 0 else 0.0
70
-
79
+
71
80
  return current_streak, avg_points_during_streak
72
81
 
73
82
  def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_human_id=None, aggregation_window=None):
@@ -129,7 +138,7 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
129
138
 
130
139
  # Apply aggregation window filter
131
140
  if aggregation_window:
132
- last_game_datetime_str = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, Game.status.like('Final%')).scalar()
141
+ last_game_datetime_str = session.query(func.max(func.concat(Game.date, ' ', Game.time))).filter(filter_condition, (Game.status.like('Final%')) | (Game.status == 'NOEVENTS')).scalar()
133
142
  start_datetime = get_start_datetime(last_game_datetime_str, aggregation_window)
134
143
  if start_datetime:
135
144
  game_window_filter = func.cast(func.concat(Game.date, ' ', Game.time), sqlalchemy.types.TIMESTAMP).between(start_datetime, last_game_datetime_str)
@@ -144,21 +153,17 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
144
153
  # human_filter = [GameRoster.human_id == debug_human_id]
145
154
 
146
155
  # Aggregate games played for each human in each division, excluding goalies
147
- # games_participated: Count FINAL, FINAL_SO, FORFEIT, NOEVENTS
148
- # games_with_stats: Count only FINAL, FINAL_SO (for per-game averages)
156
+ # Filter games by status upfront for performance (avoid CASE statements on 200K+ rows)
157
+ # Only count games with these statuses: FINAL, FINAL_SO, FORFEIT, NOEVENTS
149
158
  games_played_query = session.query(
150
159
  GameRoster.human_id,
151
- func.count(Game.id).label('games_played'), # DEPRECATED - will be replaced by games_participated
152
- func.sum(case(
153
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS]), 1),
154
- else_=0
155
- )).label('games_participated'),
156
- func.sum(case(
157
- (Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS]), 1),
158
- else_=0
159
- )).label('games_with_stats'),
160
+ func.count(Game.id).label('games_played'),
161
+ func.count(Game.id).label('games_participated'), # Same as games_played after filtering
162
+ func.count(Game.id).label('games_with_stats'), # Same as games_played after filtering
160
163
  func.array_agg(Game.id).label('game_ids')
161
- ).join(Game, Game.id == GameRoster.game_id)
164
+ ).join(Game, Game.id == GameRoster.game_id).filter(
165
+ Game.status.in_([FINAL_STATUS, FINAL_SO_STATUS, FORFEIT_STATUS, NOEVENTS_STATUS])
166
+ )
162
167
 
163
168
  # Only join Division if not level aggregation (since we filter on Game.division_id directly for levels)
164
169
  if aggregation_type != 'level':
@@ -286,21 +291,48 @@ def aggregate_skater_stats(session, aggregation_type, aggregation_id, debug_huma
286
291
  stats_dict = {key: value for key, value in stats_dict.items() if key[1] is not None}
287
292
 
288
293
  # Populate first_game_id and last_game_id
289
- for key, stat in stats_dict.items():
290
- all_game_ids = stat['game_ids']
291
- if all_game_ids:
292
- first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
293
- last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
294
- stat['first_game_id'] = first_game.id if first_game else None
295
- stat['last_game_id'] = last_game.id if last_game else None
294
+ # Only show progress for "All Orgs" with no window (all-time stats) - the slowest case
295
+ total_players = len(stats_dict)
296
+ if aggregation_type == 'org' and aggregation_id == ALL_ORGS_ID and aggregation_window is None and total_players > 1000:
297
+ progress = create_progress_tracker(total_players, f"Processing {total_players} players for {aggregation_name}")
298
+ for idx, (key, stat) in enumerate(stats_dict.items()):
299
+ all_game_ids = stat['game_ids']
300
+ if all_game_ids:
301
+ first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
302
+ last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
303
+ stat['first_game_id'] = first_game.id if first_game else None
304
+ stat['last_game_id'] = last_game.id if last_game else None
305
+ if (idx + 1) % 100 == 0 or (idx + 1) == total_players: # Update every 100 players
306
+ progress.update(idx + 1)
307
+ else:
308
+ # No progress tracking for all other cases
309
+ for key, stat in stats_dict.items():
310
+ all_game_ids = stat['game_ids']
311
+ if all_game_ids:
312
+ first_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date, Game.time).first()
313
+ last_game = session.query(Game).filter(Game.id.in_(all_game_ids)).order_by(Game.date.desc(), Game.time.desc()).first()
314
+ stat['first_game_id'] = first_game.id if first_game else None
315
+ stat['last_game_id'] = last_game.id if last_game else None
296
316
 
297
317
  # Calculate current point streak (only for all-time stats)
298
318
  if aggregation_window is None:
299
- for key, stat in stats_dict.items():
300
- aggregation_id, human_id = key
301
- streak_length, avg_points = calculate_current_point_streak(session, human_id, filter_condition)
302
- stat['current_point_streak'] = streak_length
303
- stat['current_point_streak_avg_points'] = avg_points
319
+ total_players = len(stats_dict)
320
+ # Show progress for All Orgs - this is the slowest part
321
+ if aggregation_type == 'org' and aggregation_id == ALL_ORGS_ID and total_players > 1000:
322
+ progress = create_progress_tracker(total_players, f"Calculating point streaks for {total_players} players")
323
+ for idx, (key, stat) in enumerate(stats_dict.items()):
324
+ agg_id, human_id = key
325
+ streak_length, avg_points = calculate_current_point_streak(session, human_id, filter_condition)
326
+ stat['current_point_streak'] = streak_length
327
+ stat['current_point_streak_avg_points'] = avg_points
328
+ if (idx + 1) % 100 == 0 or (idx + 1) == total_players:
329
+ progress.update(idx + 1)
330
+ else:
331
+ for key, stat in stats_dict.items():
332
+ agg_id, human_id = key
333
+ streak_length, avg_points = calculate_current_point_streak(session, human_id, filter_condition)
334
+ stat['current_point_streak'] = streak_length
335
+ stat['current_point_streak_avg_points'] = avg_points
304
336
 
305
337
  # Calculate total_in_rank
306
338
  total_in_rank = len(stats_dict)
@@ -99,6 +99,7 @@ class Goal(db.Model):
99
99
  sequence_number = db.Column(db.Integer)
100
100
  __table_args__ = (
101
101
  db.UniqueConstraint('game_id', 'scoring_team_id', 'sequence_number', name='_goal_team_sequence_uc'),
102
+ db.UniqueConstraint('game_id', 'period', 'time', 'goal_scorer_id', 'scoring_team_id', name='uq_goals_no_duplicates'),
102
103
  )
103
104
 
104
105
  class Human(db.Model):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hockey-blast-common-lib
3
- Version: 0.1.62
3
+ Version: 0.1.63
4
4
  Summary: Common library for shared functionality and DB models
5
5
  Author: Pavel Kletskov
6
6
  Author-email: kletskov@gmail.com
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='hockey-blast-common-lib', # The name of your package
5
- version='0.1.62',
5
+ version='0.1.63',
6
6
  description='Common library for shared functionality and DB models',
7
7
  author='Pavel Kletskov',
8
8
  author_email='kletskov@gmail.com',