hockey-blast-common-lib 0.1.54__py3-none-any.whl → 0.1.56__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__ = (
@@ -101,14 +104,18 @@ class Goal(db.Model):
101
104
  class Human(db.Model):
102
105
  __tablename__ = 'humans'
103
106
  id = db.Column(db.Integer, primary_key=True)
104
- first_name = db.Column(db.String(100))
105
- middle_name = db.Column(db.String(100))
106
- last_name = db.Column(db.String(100))
107
+ # All name components now declared non-nullable at the ORM level. Ensure data cleanup
108
+ # (convert existing NULLs to '') BEFORE applying a DB migration that enforces NOT NULL.
109
+ # middle_name and suffix may be logically "empty" but must not be NULL; use '' for absence.
110
+ first_name = db.Column(db.String(100), nullable=False, default='')
111
+ middle_name = db.Column(db.String(100), nullable=False, default='')
112
+ last_name = db.Column(db.String(100), nullable=False, default='')
113
+ suffix = db.Column(db.String(100), nullable=False, default='')
107
114
  first_date = db.Column(db.Date)
108
115
  last_date = db.Column(db.Date)
109
116
  skater_skill_value = db.Column(db.Float, nullable=True)
110
117
  __table_args__ = (
111
- db.UniqueConstraint('first_name', 'middle_name', 'last_name', name='_human_name_uc'),
118
+ db.UniqueConstraint('first_name', 'middle_name', 'last_name', 'suffix', name='_human_name_uc'),
112
119
  )
113
120
 
114
121
  class HumanAlias(db.Model):
@@ -118,10 +125,11 @@ class HumanAlias(db.Model):
118
125
  first_name = db.Column(db.String(100))
119
126
  middle_name = db.Column(db.String(100))
120
127
  last_name = db.Column(db.String(100))
128
+ suffix = db.Column(db.String(100))
121
129
  first_date = db.Column(db.Date)
122
130
  last_date = db.Column(db.Date)
123
131
  __table_args__ = (
124
- db.UniqueConstraint('human_id', 'first_name', 'middle_name', 'last_name', name='_human_alias_uc'),
132
+ db.UniqueConstraint('human_id', 'first_name', 'middle_name', 'last_name', 'suffix', name='_human_alias_uc'),
125
133
  )
126
134
 
127
135
  class HumanInTTS(db.Model):
@@ -357,6 +365,31 @@ class RequestLog(db.Model):
357
365
  cgi_params = db.Column(db.String, nullable=True)
358
366
  response_time_ms = db.Column(db.Float, nullable=True) # Response time in milliseconds
359
367
 
368
+ class GoalieSaves(db.Model):
369
+ __tablename__ = 'goalie_saves'
370
+ id = db.Column(db.Integer, primary_key=True)
371
+ game_id = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=False)
372
+ goalie_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=False)
373
+ saves_count = db.Column(db.Integer, nullable=False, default=0)
374
+ shots_against = db.Column(db.Integer, nullable=False, default=0)
375
+ goals_allowed = db.Column(db.Integer, nullable=False, default=0)
376
+ __table_args__ = (
377
+ db.UniqueConstraint('game_id', 'goalie_id', name='_game_goalie_saves_uc'),
378
+ )
379
+
380
+ class ScorekeeperSaveQuality(db.Model):
381
+ __tablename__ = 'scorekeeper_save_quality'
382
+ id = db.Column(db.Integer, primary_key=True)
383
+ game_id = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=False)
384
+ scorekeeper_id = db.Column(db.Integer, db.ForeignKey('humans.id'), nullable=False)
385
+ total_saves_recorded = db.Column(db.Integer, nullable=False, default=0)
386
+ max_saves_per_5sec = db.Column(db.Integer, nullable=False, default=0) # Highest saves in any 5-second window
387
+ max_saves_per_20sec = db.Column(db.Integer, nullable=False, default=0) # Highest saves in any 20-second window
388
+ saves_timestamps = db.Column(db.JSON, nullable=True) # JSONB with home_saves/away_saves arrays of decisecond timestamps
389
+ __table_args__ = (
390
+ db.UniqueConstraint('game_id', 'scorekeeper_id', name='_game_scorekeeper_quality_uc'),
391
+ )
392
+
360
393
  # # MANUAL AMENDS HAPPEN HERE :)
361
394
  # from db_connection import create_session
362
395
  # 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
@@ -14,7 +15,9 @@ not_human_names = [
14
15
  (None, "Unknown", None),
15
16
  ("Not", None , None),
16
17
  (None , None, "Goalie"),
17
- ("Unassigned",None , None)
18
+ ("Unassigned",None , None),
19
+ # Critical addition - primary cause of 93.4% of roster insertion errors
20
+ ("Not", "Signed", "In")
18
21
  ]
19
22
 
20
23
  def parse_args():
@@ -23,3 +26,30 @@ def parse_args():
23
26
  parser.add_argument("--reprocess", action="store_true", help="Reprocess existing data.")
24
27
  parser.add_argument("--pre_process", action="store_true", help="Pre-Process existing data.")
25
28
  return parser.parse_args()
29
+
30
+ def get_or_create_empty_net_human(session, game_date):
31
+ """Get or create the special 'Empty Net' human record for tracking pulled goalies.
32
+
33
+ Args:
34
+ session: Database session
35
+ game_date: Date of the game (used for first_date/last_date if creating)
36
+
37
+ Returns:
38
+ int: The human_id of the Empty Net special record
39
+ """
40
+ empty_net_human = session.query(Human).filter_by(
41
+ first_name="Empty", middle_name="", last_name="Net"
42
+ ).first()
43
+
44
+ if not empty_net_human:
45
+ empty_net_human = Human(
46
+ first_name="Empty",
47
+ middle_name="",
48
+ last_name="Net",
49
+ first_date=game_date,
50
+ last_date=game_date
51
+ )
52
+ session.add(empty_net_human)
53
+ session.commit()
54
+
55
+ 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.54
3
+ Version: 0.1.56
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=fzSnsPZVLF7RVGymJcXZPIragcHXnMwX-Levh9LY_ao,4648903
14
- hockey_blast_common_lib/models.py,sha256=aa5M1hF-IQ3XbDBgq_GK2pk-GcHtatH94ADEg7GAR7M,16734
15
- hockey_blast_common_lib/options.py,sha256=2L4J9rKCKr58om34259D3_s7kbPdknMSwoo6IwTNnx0,849
13
+ hockey_blast_common_lib/hockey_blast_sample_backup.sql.gz,sha256=PAgRoxxkhHCjF4FzEbjFX84hGJyNy8txMWCnF4Eiaac,4648905
14
+ hockey_blast_common_lib/models.py,sha256=JMwQE1QgNkuWuKakxuHIlbEHfk8Z7GAYktCppCWA6LE,18928
15
+ hockey_blast_common_lib/options.py,sha256=rQaLRYYcaxXrpZoXfUpmvsahC23oVGPEOzEpqtthbIQ,1794
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.54.dist-info/METADATA,sha256=ApLUd6xFjG9PecXGI42K-4eyVYLrCbgGNtbccp3EOXY,318
25
- hockey_blast_common_lib-0.1.54.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
26
- hockey_blast_common_lib-0.1.54.dist-info/top_level.txt,sha256=wIR4LIkE40npoA2QlOdfCYlgFeGbsHR8Z6r0h46Vtgc,24
27
- hockey_blast_common_lib-0.1.54.dist-info/RECORD,,
24
+ hockey_blast_common_lib-0.1.56.dist-info/METADATA,sha256=mB63Gvv1wgCt1hJAl0lk-97pqp5vgCOCQkFFk5_7SVs,318
25
+ hockey_blast_common_lib-0.1.56.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
26
+ hockey_blast_common_lib-0.1.56.dist-info/top_level.txt,sha256=wIR4LIkE40npoA2QlOdfCYlgFeGbsHR8Z6r0h46Vtgc,24
27
+ hockey_blast_common_lib-0.1.56.dist-info/RECORD,,