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

@@ -1,6 +1,7 @@
import app.games
from tests.conftest import make_game
from app.games import (
calculate_game_importance,
calculate_game_priority,
convert_game_state,
format_record,
@@ -413,3 +414,123 @@ class TestGetComebackBonus:
app.games._score_cache[("Maple Leafs", "Bruins")] = (1, 3)
game = make_game(home_score=2, away_score=3, period=4)
assert get_comeback_bonus(game) == 100
class TestCalculateGameImportance:
def _standings(
self,
league_seq=10,
l10_seq=10,
div="ATL",
conf="E",
gp=65,
wc=18,
):
return {
"league_sequence": league_seq,
"league_l10_sequence": l10_seq,
"division_abbrev": div,
"conference_abbrev": conf,
"games_played": gp,
"wildcard_sequence": wc,
}
def test_returns_zero_for_playoff_game(self):
game = make_game(game_type=3)
assert calculate_game_importance(game) == 0
def test_returns_zero_for_final_game(self):
game = make_game(game_state="OFF")
assert calculate_game_importance(game) == 0
def test_near_zero_early_in_season(self, mocker):
mocker.patch(
"app.games.get_team_standings",
return_value=self._standings(gp=10, wc=18),
)
game = make_game(game_state="FUT")
assert calculate_game_importance(game) <= 10
def test_max_bonus_late_season_bubble_division_game(self, mocker):
mocker.patch(
"app.games.get_team_standings",
return_value=self._standings(gp=82, wc=18, div="ATL", conf="E"),
)
game = make_game(game_state="FUT")
assert calculate_game_importance(game) == 150
def test_same_division_beats_same_conference(self, mocker):
home_st = self._standings(gp=70, wc=18, div="ATL", conf="E")
away_st_same_div = self._standings(gp=70, wc=18, div="ATL", conf="E")
away_st_diff_div = self._standings(gp=70, wc=18, div="MET", conf="E")
mocker.patch(
"app.games.get_team_standings",
side_effect=[home_st, away_st_same_div],
)
result_div = calculate_game_importance(make_game(game_state="FUT"))
mocker.patch(
"app.games.get_team_standings",
side_effect=[home_st, away_st_diff_div],
)
result_conf = calculate_game_importance(make_game(game_state="FUT"))
assert result_div > result_conf
def test_same_conference_beats_different_conference(self, mocker):
home_st = self._standings(gp=70, wc=18, div="ATL", conf="E")
away_same_conf = self._standings(gp=70, wc=18, div="MET", conf="E")
away_diff_conf = self._standings(gp=70, wc=18, div="PAC", conf="W")
mocker.patch(
"app.games.get_team_standings",
side_effect=[home_st, away_same_conf],
)
result_same = calculate_game_importance(make_game(game_state="FUT"))
mocker.patch(
"app.games.get_team_standings",
side_effect=[home_st, away_diff_conf],
)
result_diff = calculate_game_importance(make_game(game_state="FUT"))
assert result_same > result_diff
def test_bubble_teams_beat_safely_in_teams(self, mocker):
mocker.patch(
"app.games.get_team_standings",
return_value=self._standings(gp=70, wc=18),
)
result_bubble = calculate_game_importance(make_game(game_state="FUT"))
mocker.patch(
"app.games.get_team_standings",
return_value=self._standings(gp=70, wc=5),
)
result_safe = calculate_game_importance(make_game(game_state="FUT"))
assert result_bubble > result_safe
def test_eliminated_teams_have_lowest_relevance(self, mocker):
mocker.patch(
"app.games.get_team_standings",
return_value=self._standings(gp=70, wc=30),
)
assert calculate_game_importance(make_game(game_state="FUT")) < 30
def test_result_is_non_negative_int(self, mocker):
mocker.patch(
"app.games.get_team_standings",
return_value=self._standings(gp=0, wc=32),
)
result = calculate_game_importance(make_game(game_state="FUT"))
assert isinstance(result, int)
assert result >= 0
def test_result_never_exceeds_150(self, mocker):
mocker.patch(
"app.games.get_team_standings",
return_value=self._standings(gp=82, wc=18, div="ATL", conf="E"),
)
assert calculate_game_importance(make_game(game_state="FUT")) <= 150