hockey-blast-common-lib 0.1.53__py3-none-any.whl → 0.1.55__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.
@@ -26,7 +26,7 @@ def calculate_skater_skill_value(session, level_stats):
26
26
 
27
27
  # Apply skill adjustment: range from 0.8 to 1.2 of level base skill
28
28
  # Since lower skill_value is better: Best player gets 0.8x (closer to better levels), worst gets 1.2x
29
- skill_adjustment = 1.2 - 0.4 * ppg_skill_factor
29
+ skill_adjustment = 1.3 - 0.2 * ppg_skill_factor
30
30
  skill_value = level_skill_value * skill_adjustment
31
31
 
32
32
  # Take the minimum skill value across all levels the player has played in (lower is better)
@@ -43,15 +43,26 @@ def assign_skater_skill_values():
43
43
  # Create progress tracker
44
44
  progress = create_progress_tracker(total_humans, "Assigning skater skill values")
45
45
 
46
+ batch_size = 1000
47
+ updates_count = 0
48
+
46
49
  for i, human in enumerate(humans):
47
50
  level_stats = session.query(LevelStatsSkater).filter(LevelStatsSkater.human_id == human.id).all()
48
51
  if level_stats:
49
52
  skater_skill_value = calculate_skater_skill_value(session, level_stats)
50
53
  human.skater_skill_value = skater_skill_value
54
+ updates_count += 1
55
+
56
+ # Commit in batches
57
+ if updates_count % batch_size == 0:
51
58
  session.commit()
52
59
 
53
60
  progress.update(i + 1)
54
61
 
62
+ # Commit any remaining updates
63
+ if updates_count % batch_size != 0:
64
+ session.commit()
65
+
55
66
  print("Skater skill values have been assigned to all humans.")
56
67
 
57
68
  if __name__ == "__main__":
@@ -52,6 +52,8 @@ class Game(db.Model):
52
52
  home_period_1_score = db.Column(db.Integer)
53
53
  home_period_2_score = db.Column(db.Integer)
54
54
  home_period_3_score = db.Column(db.Integer)
55
+ home_ot_score = db.Column(db.Integer, default=0)
56
+ visitor_ot_score = db.Column(db.Integer, default=0)
55
57
  game_type = db.Column(db.String(50))
56
58
  went_to_ot = db.Column(db.Boolean, default=False)
57
59
  home_period_1_shots = db.Column(db.Integer)
@@ -92,6 +94,7 @@ class Goal(db.Model):
92
94
  goal_scorer_id = db.Column(db.Integer, db.ForeignKey('humans.id'))
93
95
  assist_1_id = db.Column(db.Integer, db.ForeignKey('humans.id'))
94
96
  assist_2_id = db.Column(db.Integer, db.ForeignKey('humans.id'))
97
+ goalie_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=True) # Goalie who allowed the goal (can be "Empty Net" special human)
95
98
  special_condition = db.Column(db.String(50)) # e.g., PP (power play), SH (short-handed)
96
99
  sequence_number = db.Column(db.Integer)
97
100
  __table_args__ = (
@@ -322,7 +325,11 @@ class Shootout(db.Model):
322
325
  class Team(db.Model):
323
326
  __tablename__ = 'teams'
324
327
  id = db.Column(db.Integer, primary_key=True)
325
- name = db.Column(db.String(100), unique=True, nullable=False)
328
+ name = db.Column(db.String(100), nullable=False)
329
+ org_id = db.Column(db.Integer, db.ForeignKey('organizations.id'), nullable=False)
330
+ __table_args__ = (
331
+ db.UniqueConstraint('org_id', 'name', name='_org_team_name_uc'),
332
+ )
326
333
 
327
334
  class TeamDivision(db.Model):
328
335
  __tablename__ = 'teams_divisions'
@@ -353,6 +360,31 @@ class RequestLog(db.Model):
353
360
  cgi_params = db.Column(db.String, nullable=True)
354
361
  response_time_ms = db.Column(db.Float, nullable=True) # Response time in milliseconds
355
362
 
363
+ class GoalieSaves(db.Model):
364
+ __tablename__ = 'goalie_saves'
365
+ id = db.Column(db.Integer, primary_key=True)
366
+ game_id = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=False)
367
+ goalie_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=False)
368
+ saves_count = db.Column(db.Integer, nullable=False, default=0)
369
+ shots_against = db.Column(db.Integer, nullable=False, default=0)
370
+ goals_allowed = db.Column(db.Integer, nullable=False, default=0)
371
+ __table_args__ = (
372
+ db.UniqueConstraint('game_id', 'goalie_id', name='_game_goalie_saves_uc'),
373
+ )
374
+
375
+ class ScorekeeperSaveQuality(db.Model):
376
+ __tablename__ = 'scorekeeper_save_quality'
377
+ id = db.Column(db.Integer, primary_key=True)
378
+ game_id = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=False)
379
+ scorekeeper_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=False)
380
+ total_saves_recorded = db.Column(db.Integer, nullable=False, default=0)
381
+ max_saves_per_5sec = db.Column(db.Integer, nullable=False, default=0) # Highest saves in any 5-second window
382
+ max_saves_per_20sec = db.Column(db.Integer, nullable=False, default=0) # Highest saves in any 20-second window
383
+ saves_timestamps = db.Column(db.JSON, nullable=True) # JSONB with home_saves/away_saves arrays of decisecond timestamps
384
+ __table_args__ = (
385
+ db.UniqueConstraint('game_id', 'scorekeeper_id', name='_game_scorekeeper_quality_uc'),
386
+ )
387
+
356
388
  # # MANUAL AMENDS HAPPEN HERE :)
357
389
  # from db_connection import create_session
358
390
  # session = create_session("sharksice")
@@ -1,4 +1,5 @@
1
1
  import argparse
2
+ from .models import Human
2
3
 
3
4
  MAX_HUMAN_SEARCH_RESULTS = 25
4
5
  MAX_TEAM_SEARCH_RESULTS = 25
@@ -23,3 +24,30 @@ def parse_args():
23
24
  parser.add_argument("--reprocess", action="store_true", help="Reprocess existing data.")
24
25
  parser.add_argument("--pre_process", action="store_true", help="Pre-Process existing data.")
25
26
  return parser.parse_args()
27
+
28
+ def get_or_create_empty_net_human(session, game_date):
29
+ """Get or create the special 'Empty Net' human record for tracking pulled goalies.
30
+
31
+ Args:
32
+ session: Database session
33
+ game_date: Date of the game (used for first_date/last_date if creating)
34
+
35
+ Returns:
36
+ int: The human_id of the Empty Net special record
37
+ """
38
+ empty_net_human = session.query(Human).filter_by(
39
+ first_name="Empty", middle_name="", last_name="Net"
40
+ ).first()
41
+
42
+ if not empty_net_human:
43
+ empty_net_human = Human(
44
+ first_name="Empty",
45
+ middle_name="",
46
+ last_name="Net",
47
+ first_date=game_date,
48
+ last_date=game_date
49
+ )
50
+ session.add(empty_net_human)
51
+ session.commit()
52
+
53
+ return empty_net_human.id
@@ -11,6 +11,7 @@ from hockey_blast_common_lib.stats_models import LevelsGraphEdge, LevelStatsSkat
11
11
  from hockey_blast_common_lib.db_connection import create_session
12
12
  from hockey_blast_common_lib.progress_utils import create_progress_tracker
13
13
  from sqlalchemy import func
14
+ from sqlalchemy.exc import IntegrityError
14
15
 
15
16
  import numpy as np
16
17
 
@@ -93,9 +94,22 @@ def reset_skill_values_in_divisions():
93
94
  skill_propagation_sequence=-1
94
95
  )
95
96
  session.add(new_level)
96
- session.commit()
97
- division.level_id = new_level.id
98
- print(f"Created new Level for Division {division.level}")
97
+ try:
98
+ session.commit()
99
+ division.level_id = new_level.id
100
+ print(f"Created new Level for Division {division.level}")
101
+ except IntegrityError:
102
+ session.rollback()
103
+ # Another process created this level, query for it
104
+ existing_level = session.query(Level).filter(
105
+ Level.org_id == division.org_id,
106
+ Level.level_name == division.level
107
+ ).first()
108
+ if existing_level:
109
+ division.level_id = existing_level.id
110
+ print(f"Race condition resolved - using existing Level for Division {division.level}")
111
+ else:
112
+ raise RuntimeError(f"Unable to create or find level: {division.level} for org_id: {division.org_id}")
99
113
 
100
114
  # Commit the changes to the Division
101
115
  session.commit()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hockey-blast-common-lib
3
- Version: 0.1.53
3
+ Version: 0.1.55
4
4
  Summary: Common library for shared functionality and DB models
5
5
  Author: Pavel Kletskov
6
6
  Author-email: kletskov@gmail.com
@@ -6,22 +6,22 @@ hockey_blast_common_lib/aggregate_human_stats.py,sha256=ku42TAjUIj49822noM8fEeB8
6
6
  hockey_blast_common_lib/aggregate_referee_stats.py,sha256=mUcTVQH9K4kwmIfgfGsnh_3AqX6Ia3RjfukkYuQas3I,13938
7
7
  hockey_blast_common_lib/aggregate_s2s_stats.py,sha256=urYN0Q06twwLO-XWGlSMVAVOTVR_D2AWdmoGsxIYHXE,6737
8
8
  hockey_blast_common_lib/aggregate_skater_stats.py,sha256=pA_2pDOGcJyEywISg2ySG8gFCuoLWwqw6a3Gm2wHLyo,23302
9
- hockey_blast_common_lib/assign_skater_skill.py,sha256=8gAiqQm14QMFJNmdKb2jjaGyQlhzvVhXrqVvaZ84KDM,2499
9
+ hockey_blast_common_lib/assign_skater_skill.py,sha256=Asq6iRMPsCMDnvuNSd-M3s4Gee4kDocP9Eznwju_9kA,2749
10
10
  hockey_blast_common_lib/db_connection.py,sha256=HvPxDvOj7j5H85RfslGvHVNevfg7mKCd0syJ6NX21mU,1890
11
11
  hockey_blast_common_lib/dump_sample_db.sh,sha256=MY3lnzTXBoWd76-ZlZr9nWsKMEVgyRsUn-LZ2d1JWZs,810
12
12
  hockey_blast_common_lib/h2h_models.py,sha256=0st4xoJO0U6ONfx3BV03BQvHjZE31e_PqZfphAJMoSU,7968
13
- hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz,sha256=AIB10IouLiSrxfaUZUN0zFjF_TzZ2D-Rjde9_yGTUTQ,4648897
14
- hockey_blast_common_lib/models.py,sha256=PTIWyl7ygVQ_hbooqFWmnpOIKcJyyyTew_433IAMIFM,16559
15
- hockey_blast_common_lib/options.py,sha256=2L4J9rKCKr58om34259D3_s7kbPdknMSwoo6IwTNnx0,849
13
+ hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz,sha256=hWju95cPNMukwns66g0CTK3XUFShrMDN-FzRjpzqLPQ,4648902
14
+ hockey_blast_common_lib/models.py,sha256=NBF0u4wTAwxFe5UaUObFo-L9Az2jLPXbKejtcJRuGQ0,18440
15
+ hockey_blast_common_lib/options.py,sha256=alOXCYF7Jmw4Z97ae6tAsDIS0VNideV_-GKJ4-v4Pjo,1689
16
16
  hockey_blast_common_lib/progress_utils.py,sha256=H_zRFOsb2qQQpGw56wJghZ1nUe_m6zqGeR9hZ33Y1Uo,3229
17
17
  hockey_blast_common_lib/restore_sample_db.sh,sha256=7W3lzRZeu9zXIu1Bvtnaw8EHc1ulHmFM4mMh86oUQJo,2205
18
18
  hockey_blast_common_lib/skills_in_divisions.py,sha256=m-UEwMwn1KM7wOYvDstgsOEeH57M9V6yrkBoghzGYKE,7005
19
- hockey_blast_common_lib/skills_propagation.py,sha256=CYpnjcJit01-QxkvVstNx1DhUo5ljZB_-o31vGzPT-A,17668
19
+ hockey_blast_common_lib/skills_propagation.py,sha256=nUxntyK8M4xWjHpkfze8f0suaBeunxicgDCduGmNJ-A,18468
20
20
  hockey_blast_common_lib/stats_models.py,sha256=uBNQSqCMXurzS-tD13OoV5WqurYYGHMZMHk1CeA5jgI,26104
21
21
  hockey_blast_common_lib/stats_utils.py,sha256=DXsPO4jw8XsdRUN46TGF_IiBAfz3GCIVBswCGp5ELDk,284
22
22
  hockey_blast_common_lib/utils.py,sha256=PduHp6HoI4sfr5HvJfQAaz7170dy5kTFVdIfWvBR-Jg,5874
23
23
  hockey_blast_common_lib/wsgi.py,sha256=y3NxoJfWjdzX3iP7RGvDEer6zcnPyCanpqSgW1BlXgg,779
24
- hockey_blast_common_lib-0.1.53.dist-info/METADATA,sha256=gQutIQ7tPRkjSSi-AcdJwikZt900Vhja3qU-owRdurI,318
25
- hockey_blast_common_lib-0.1.53.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
26
- hockey_blast_common_lib-0.1.53.dist-info/top_level.txt,sha256=wIR4LIkE40npoA2QlOdfCYlgFeGbsHR8Z6r0h46Vtgc,24
27
- hockey_blast_common_lib-0.1.53.dist-info/RECORD,,
24
+ hockey_blast_common_lib-0.1.55.dist-info/METADATA,sha256=fl85rh700VSbNViKgFuvQFhsvJbJUq3BwYlzQeTzko8,318
25
+ hockey_blast_common_lib-0.1.55.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
26
+ hockey_blast_common_lib-0.1.55.dist-info/top_level.txt,sha256=wIR4LIkE40npoA2QlOdfCYlgFeGbsHR8Z6r0h46Vtgc,24
27
+ hockey_blast_common_lib-0.1.55.dist-info/RECORD,,