feat: game importance factor in hype scoring
Some checks failed
CI / Lint (push) Failing after 6s
CI / Test (push) Has been skipped
CI / Build & Push (push) Has been skipped

Adds calculate_game_importance() that boosts Priority for high-stakes
regular-season matchups based on season progress (sharp ramp after game
55), playoff bubble proximity (wildcard rank ~17-19 = max relevance),
and divisional/conference rivalry (1.4x/1.2x multipliers). Max bonus
150 pts applied to both LIVE and PRE games; playoff and FINAL games
are unaffected. Extends standings schema with division, conference,
games_played, and wildcard_sequence fields fetched from the NHL API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 18:39:55 -04:00
parent 8945b99782
commit 47a8c34215
5 changed files with 311 additions and 9 deletions

View File

@@ -52,7 +52,7 @@ def parse_games(scoreboard_data):
"Intermission": game["clock"]["inIntermission"]
if game_state == "LIVE"
else "N/A",
"Priority": calculate_game_priority(game) + get_comeback_bonus(game),
"Priority": calculate_game_priority(game) + get_comeback_bonus(game) + calculate_game_importance(game),
"Start Time": get_start_time(game),
"Home Record": format_record(game["homeTeam"]["record"])
if game["gameState"] in ["PRE", "FUT"]
@@ -290,20 +290,94 @@ def get_team_standings(team_name):
cursor = conn.cursor()
cursor.execute(
"""
SELECT league_sequence, league_l10_sequence
SELECT league_sequence, league_l10_sequence,
division_abbrev, conference_abbrev,
games_played, wildcard_sequence
FROM standings
WHERE team_common_name = ?
""",
""",
(team_name,),
)
result = cursor.fetchone()
conn.close()
if result:
return {
"league_sequence": result[0],
"league_l10_sequence": result[1],
"division_abbrev": result[2],
"conference_abbrev": result[3],
"games_played": result[4],
"wildcard_sequence": result[5],
}
return {
"league_sequence": result[0] if result else 0,
"league_l10_sequence": result[1] if result else 0,
"league_sequence": 0,
"league_l10_sequence": 0,
"division_abbrev": None,
"conference_abbrev": None,
"games_played": 0,
"wildcard_sequence": 32,
}
def calculate_game_importance(game):
# Playoff games already have elevated priorities; don't double-count
if game.get("gameType", 2) != 2:
return 0
# FINAL/OFF games must sort below LIVE and PRE games
if game["gameState"] in ("FINAL", "OFF"):
return 0
home_st = get_team_standings(game["homeTeam"]["name"]["default"])
away_st = get_team_standings(game["awayTeam"]["name"]["default"])
# Season weight — near-zero before game 30, sharp ramp 55-70, max at 82
avg_gp = (home_st["games_played"] + away_st["games_played"]) / 2
if avg_gp <= 30:
season_weight = 0.05
else:
t = (avg_gp - 30) / (82 - 30)
season_weight = min(t ** 1.8, 1.0)
# Playoff relevance — peaks for bubble teams (wildcard rank ~17-19)
best_wc = min(home_st["wildcard_sequence"] or 32, away_st["wildcard_sequence"] or 32)
if best_wc <= 12:
playoff_relevance = 0.60
elif best_wc <= 16:
playoff_relevance = 0.85
elif best_wc <= 19:
playoff_relevance = 1.00
elif best_wc <= 23:
playoff_relevance = 0.65
else:
playoff_relevance = 0.15
# Division/conference rivalry multiplier
home_div = home_st["division_abbrev"]
away_div = away_st["division_abbrev"]
home_conf = home_st["conference_abbrev"]
away_conf = away_st["conference_abbrev"]
if home_div and away_div and home_div == away_div:
rivalry_multiplier = 1.4
elif home_conf and away_conf and home_conf == away_conf:
rivalry_multiplier = 1.2
else:
rivalry_multiplier = 1.0
raw = season_weight * playoff_relevance * rivalry_multiplier
importance = int((raw / 1.4) * 150)
logger.debug(
"importance components — season_weight: %.3f, playoff_relevance: %.2f, "
"rivalry: %.1f, importance: %s",
season_weight,
playoff_relevance,
rivalry_multiplier,
importance,
)
return max(0, min(importance, 150))
def utc_to_eastern(utc_time):
utc_datetime = datetime.strptime(utc_time, "%Y-%m-%dT%H:%M:%SZ")
eastern_datetime = utc_datetime.replace(tzinfo=timezone.utc).astimezone(EASTERN)