hockey-blast-common-lib 0.1.67__tar.gz → 0.1.68__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 (41) hide show
  1. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/PKG-INFO +1 -1
  2. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_skater_stats.py +130 -26
  3. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz +0 -0
  4. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib.egg-info/PKG-INFO +1 -1
  5. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/setup.py +1 -1
  6. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/MANIFEST.in +0 -0
  7. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/README.md +0 -0
  8. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/__init__.py +0 -0
  9. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_all_stats.py +0 -0
  10. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_game_stats_all.py +0 -0
  11. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_game_stats_goalie.py +0 -0
  12. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_game_stats_skater.py +0 -0
  13. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_goalie_stats.py +0 -0
  14. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_h2h_stats.py +0 -0
  15. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_human_stats.py +0 -0
  16. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_referee_stats.py +0 -0
  17. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_s2s_stats.py +0 -0
  18. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_scorekeeper_stats.py +0 -0
  19. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_team_goalie_stats.py +0 -0
  20. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/aggregate_team_skater_stats.py +0 -0
  21. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/assign_skater_skill.py +0 -0
  22. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/db_connection.py +0 -0
  23. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/dump_sample_db.sh +0 -0
  24. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/embedding_utils.py +0 -0
  25. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/h2h_models.py +0 -0
  26. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/models.py +0 -0
  27. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/options.py +0 -0
  28. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/progress_utils.py +0 -0
  29. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/restore_sample_db.sh +0 -0
  30. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/skills_in_divisions.py +0 -0
  31. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/skills_propagation.py +0 -0
  32. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/stats_models.py +0 -0
  33. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/stats_utils.py +0 -0
  34. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/utils.py +0 -0
  35. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib/wsgi.py +0 -0
  36. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib.egg-info/SOURCES.txt +0 -0
  37. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib.egg-info/dependency_links.txt +0 -0
  38. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib.egg-info/requires.txt +0 -0
  39. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/hockey_blast_common_lib.egg-info/top_level.txt +0 -0
  40. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/pyproject.toml +0 -0
  41. {hockey_blast_common_lib-0.1.67 → hockey_blast_common_lib-0.1.68}/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.67
3
+ Version: 0.1.68
4
4
  Summary: Common library for shared functionality and DB models
5
5
  Author: Pavel Kletskov
6
6
  Author-email: kletskov@gmail.com
@@ -118,6 +118,118 @@ def calculate_current_point_streak(session, human_id, filter_condition):
118
118
  return current_streak, avg_points_during_streak
119
119
 
120
120
 
121
+ def calculate_all_point_streaks_batch(session, human_ids, filter_condition):
122
+ """
123
+ Calculate point streaks for ALL players in one batch query.
124
+ This is MUCH faster than calling calculate_current_point_streak() for each player.
125
+
126
+ Args:
127
+ session: Database session
128
+ human_ids: List of all human_ids to calculate streaks for (e.g., 150 players in a division)
129
+ filter_condition: SQLAlchemy filter condition (org/division/level + time window)
130
+
131
+ Returns:
132
+ Dict[human_id] -> (streak_length, avg_points_per_game)
133
+
134
+ Performance:
135
+ - Old: N queries (one per player)
136
+ - New: 1 query for all players
137
+ - Speedup: ~150x for typical division
138
+ """
139
+ if not human_ids:
140
+ return {}
141
+
142
+ # Single query for ALL players at once
143
+ game_points = (
144
+ session.query(
145
+ GameRoster.human_id,
146
+ Game.id.label("game_id"),
147
+ Game.date,
148
+ Game.time,
149
+ func.sum(
150
+ case((Goal.goal_scorer_id == GameRoster.human_id, 1), else_=0)
151
+ ).label("goals"),
152
+ func.sum(
153
+ case(
154
+ (
155
+ (Goal.assist_1_id == GameRoster.human_id) |
156
+ (Goal.assist_2_id == GameRoster.human_id),
157
+ 1
158
+ ),
159
+ else_=0
160
+ )
161
+ ).label("assists"),
162
+ )
163
+ .join(Game, GameRoster.game_id == Game.id)
164
+ .outerjoin(Goal, Game.id == Goal.game_id)
165
+ .filter(
166
+ GameRoster.human_id.in_(human_ids), # ← ALL players at once!
167
+ ~GameRoster.role.ilike("g"), # Exclude goalie games
168
+ filter_condition,
169
+ (Game.status.like("Final%")) | (Game.status == "NOEVENTS"),
170
+ )
171
+ .group_by(GameRoster.human_id, Game.id, Game.date, Game.time)
172
+ .order_by(
173
+ GameRoster.human_id, # Group by player first
174
+ Game.date.desc(), # Then most recent games first
175
+ Game.time.desc()
176
+ )
177
+ .all()
178
+ )
179
+
180
+ # Process results in Python to calculate streaks
181
+ streaks_by_human = {}
182
+ processed_humans = set() # Track which humans we've finished processing
183
+ current_human = None
184
+ current_streak = 0
185
+ total_points = 0
186
+
187
+ for row in game_points:
188
+ # Skip if we've already processed this human (streak already broken)
189
+ if row.human_id in processed_humans:
190
+ continue
191
+
192
+ # New player?
193
+ if row.human_id != current_human:
194
+ # Save previous player's streak (if any)
195
+ if current_human is not None:
196
+ avg = total_points / current_streak if current_streak > 0 else 0.0
197
+ streaks_by_human[current_human] = (current_streak, avg)
198
+ processed_humans.add(current_human)
199
+
200
+ # Reset for new player
201
+ current_human = row.human_id
202
+ current_streak = 0
203
+ total_points = 0
204
+
205
+ # Calculate points for this game
206
+ points = (row.goals or 0) + (row.assists or 0)
207
+
208
+ if points > 0:
209
+ # Streak continues
210
+ current_streak += 1
211
+ total_points += points
212
+ else:
213
+ # Streak broken - save and mark as processed
214
+ avg = total_points / current_streak if current_streak > 0 else 0.0
215
+ streaks_by_human[current_human] = (current_streak, avg)
216
+ processed_humans.add(current_human)
217
+ # Reset current_human so we skip remaining games for this player
218
+ current_human = None
219
+
220
+ # Don't forget the last player (if still in a streak)
221
+ if current_human is not None and current_human not in processed_humans:
222
+ avg = total_points / current_streak if current_streak > 0 else 0.0
223
+ streaks_by_human[current_human] = (current_streak, avg)
224
+
225
+ # Ensure all requested human_ids have a result (even if 0,0)
226
+ for human_id in human_ids:
227
+ if human_id not in streaks_by_human:
228
+ streaks_by_human[human_id] = (0, 0.0)
229
+
230
+ return streaks_by_human
231
+
232
+
121
233
  def insert_percentile_markers_skater(
122
234
  session, stats_dict, aggregation_id, total_in_rank, StatsModel, aggregation_window
123
235
  ):
@@ -605,34 +717,26 @@ def aggregate_skater_stats(
605
717
  stat["last_game_id"] = last_game.id if last_game else None
606
718
 
607
719
  # Calculate current point streak (only for all-time stats)
720
+ # OPTIMIZED: Use batch calculation instead of N+1 queries
608
721
  if aggregation_window is None:
609
722
  total_players = len(stats_dict)
610
- # Show progress for All Orgs - this is the slowest part
611
- if (
612
- aggregation_type == "org"
613
- and aggregation_id == ALL_ORGS_ID
614
- and total_players > 1000
615
- ):
616
- progress = create_progress_tracker(
617
- total_players, f"Calculating point streaks for {total_players} players"
618
- )
619
- for idx, (key, stat) in enumerate(stats_dict.items()):
620
- agg_id, human_id = key
621
- streak_length, avg_points = calculate_current_point_streak(
622
- session, human_id, filter_condition
623
- )
624
- stat["current_point_streak"] = streak_length
625
- stat["current_point_streak_avg_points"] = avg_points
626
- if (idx + 1) % 100 == 0 or (idx + 1) == total_players:
627
- progress.update(idx + 1)
628
- else:
629
- for key, stat in stats_dict.items():
630
- agg_id, human_id = key
631
- streak_length, avg_points = calculate_current_point_streak(
632
- session, human_id, filter_condition
633
- )
634
- stat["current_point_streak"] = streak_length
635
- stat["current_point_streak_avg_points"] = avg_points
723
+
724
+ # Extract all human_ids from stats_dict
725
+ all_human_ids = [key[1] for key in stats_dict.keys()]
726
+
727
+ # Calculate all point streaks in ONE batch query (instead of N queries)
728
+ print(f"Calculating point streaks for {total_players} players using batch query...")
729
+ all_streaks = calculate_all_point_streaks_batch(
730
+ session, all_human_ids, filter_condition
731
+ )
732
+ print(f"✓ Point streaks calculated for {len(all_streaks)} players")
733
+
734
+ # Assign streak values to stats_dict
735
+ for key, stat in stats_dict.items():
736
+ agg_id, human_id = key
737
+ streak_length, avg_points = all_streaks.get(human_id, (0, 0.0))
738
+ stat["current_point_streak"] = streak_length
739
+ stat["current_point_streak_avg_points"] = avg_points
636
740
 
637
741
  # Calculate total_in_rank
638
742
  total_in_rank = len(stats_dict)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hockey-blast-common-lib
3
- Version: 0.1.67
3
+ Version: 0.1.68
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 find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="hockey-blast-common-lib", # The name of your package
5
- version="0.1.67",
5
+ version="0.1.68",
6
6
  description="Common library for shared functionality and DB models",
7
7
  author="Pavel Kletskov",
8
8
  author_email="kletskov@gmail.com",