style: apply ruff format and fix lint issues in playoff modules
- Rename single-letter `l` loop variables in bracket_view to satisfy E741 - Drop unused `json` import from test_playoff_cache (F401/F811) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+7
-7
@@ -34,13 +34,13 @@ def build_bracket_view(year, bracket_payload, fetched_at=None):
|
||||
def slot(letter):
|
||||
return _matchup(year, letter, series_by_letter.get(letter))
|
||||
|
||||
east_r1 = [slot(l) for l in EAST_R1]
|
||||
west_r1 = [slot(l) for l in WEST_R1]
|
||||
east_r2 = [slot(l) for l in EAST_R2]
|
||||
west_r2 = [slot(l) for l in WEST_R2]
|
||||
east_cf = [slot(l) for l in EAST_CF]
|
||||
west_cf = [slot(l) for l in WEST_CF]
|
||||
cup = [slot(l) for l in CUP_FINAL]
|
||||
east_r1 = [slot(ltr) for ltr in EAST_R1]
|
||||
west_r1 = [slot(ltr) for ltr in WEST_R1]
|
||||
east_r2 = [slot(ltr) for ltr in EAST_R2]
|
||||
west_r2 = [slot(ltr) for ltr in WEST_R2]
|
||||
east_cf = [slot(ltr) for ltr in EAST_CF]
|
||||
west_cf = [slot(ltr) for ltr in WEST_CF]
|
||||
cup = [slot(ltr) for ltr in CUP_FINAL]
|
||||
|
||||
return {
|
||||
"year": year,
|
||||
|
||||
+5
-3
@@ -30,9 +30,11 @@ def series_id(game):
|
||||
return None
|
||||
start = game.get("startTimeUTC") or ""
|
||||
try:
|
||||
year = datetime.fromisoformat(start.replace("Z", "+00:00")).astimezone(
|
||||
EASTERN
|
||||
).year
|
||||
year = (
|
||||
datetime.fromisoformat(start.replace("Z", "+00:00"))
|
||||
.astimezone(EASTERN)
|
||||
.year
|
||||
)
|
||||
except (ValueError, AttributeError):
|
||||
year = datetime.now(EASTERN).year
|
||||
return f"{year}-{letter.upper()}"
|
||||
|
||||
+10
-4
@@ -21,8 +21,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
EASTERN = ZoneInfo("America/New_York")
|
||||
|
||||
BRACKET_TTL = 3600 # refresh at this cadence via scheduler
|
||||
SERIES_TTL = 300 # lazy cache for per-series schedule fetches
|
||||
BRACKET_TTL = 3600 # refresh at this cadence via scheduler
|
||||
SERIES_TTL = 300 # lazy cache for per-series schedule fetches
|
||||
MAX_STALE_SECONDS = 86400 # 24h
|
||||
|
||||
SERIES_ID_RE = re.compile(r"^(20\d{2})-([A-P])$")
|
||||
@@ -78,6 +78,7 @@ def _get(cache_key):
|
||||
|
||||
# ── Bracket ────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def bracket_key(year):
|
||||
return f"bracket:{year}"
|
||||
|
||||
@@ -109,6 +110,7 @@ def get_bracket(year=None):
|
||||
|
||||
# ── Per-series schedule ────────────────────────────────────────────
|
||||
|
||||
|
||||
def series_key(season, letter):
|
||||
return f"series:{season}:{letter.upper()}"
|
||||
|
||||
@@ -140,7 +142,9 @@ def fetch_series(series_id):
|
||||
if time.time() - fetched < SERIES_TTL:
|
||||
return payload
|
||||
|
||||
url = f"https://api-web.nhle.com/v1/schedule/playoff-series/{season}/{letter.lower()}"
|
||||
url = (
|
||||
f"https://api-web.nhle.com/v1/schedule/playoff-series/{season}/{letter.lower()}"
|
||||
)
|
||||
try:
|
||||
resp = requests.get(url, timeout=10)
|
||||
resp.raise_for_status()
|
||||
@@ -189,7 +193,9 @@ def record_start_date_if_missing(scoreboard_games, now=None):
|
||||
|
||||
now = now or datetime.now(EASTERN)
|
||||
today = now.date().isoformat()
|
||||
_put(META_KEY, {"first_date": today, "recorded_at_utc": now.astimezone().isoformat()})
|
||||
_put(
|
||||
META_KEY, {"first_date": today, "recorded_at_utc": now.astimezone().isoformat()}
|
||||
)
|
||||
return today
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
from app.bracket_view import build_bracket_view
|
||||
|
||||
|
||||
def _series(letter, top_abbrev, top_id, top_wins, bot_abbrev, bot_id, bot_wins,
|
||||
rnd=1, winning_id=None, top_seed="D1", bot_seed="WC1"):
|
||||
def _series(
|
||||
letter,
|
||||
top_abbrev,
|
||||
top_id,
|
||||
top_wins,
|
||||
bot_abbrev,
|
||||
bot_id,
|
||||
bot_wins,
|
||||
rnd=1,
|
||||
winning_id=None,
|
||||
top_seed="D1",
|
||||
bot_seed="WC1",
|
||||
):
|
||||
return {
|
||||
"seriesLetter": letter,
|
||||
"playoffRound": rnd,
|
||||
|
||||
+3
-9
@@ -756,9 +756,7 @@ class TestPlayoffEnrichment:
|
||||
}
|
||||
|
||||
def test_regular_season_game_has_empty_playoff_fields(self, mocker):
|
||||
mocker.patch(
|
||||
"app.games.get_team_standings", return_value=self._FULL_STANDINGS
|
||||
)
|
||||
mocker.patch("app.games.get_team_standings", return_value=self._FULL_STANDINGS)
|
||||
result = parse_games({"games": [make_game()]})
|
||||
g = result[0]
|
||||
assert g["Is Playoff"] is False
|
||||
@@ -791,9 +789,7 @@ class TestPlayoffEnrichment:
|
||||
assert "GAME 7" in result[0]["Series Badges"]
|
||||
|
||||
def test_pinned_game_sorts_first(self, mocker):
|
||||
mocker.patch(
|
||||
"app.games.get_team_standings", return_value=self._FULL_STANDINGS
|
||||
)
|
||||
mocker.patch("app.games.get_team_standings", return_value=self._FULL_STANDINGS)
|
||||
# Low-priority Game 7 PRE should sort above a high-priority non-playoff LIVE
|
||||
g7_pre = make_playoff_game(
|
||||
top_wins=3,
|
||||
@@ -835,9 +831,7 @@ class TestPlayoffEnrichment:
|
||||
assert result[0]["OT Label"] == "OT"
|
||||
|
||||
def test_regular_season_ot_not_flagged_as_playoff_ot(self, mocker):
|
||||
mocker.patch(
|
||||
"app.games.get_team_standings", return_value=self._FULL_STANDINGS
|
||||
)
|
||||
mocker.patch("app.games.get_team_standings", return_value=self._FULL_STANDINGS)
|
||||
game = make_game(
|
||||
game_state="LIVE", period=4, seconds_remaining=180, game_type=2
|
||||
)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
@@ -29,6 +28,7 @@ class _Resp:
|
||||
def raise_for_status(self):
|
||||
if self.status_code >= 400:
|
||||
import requests
|
||||
|
||||
raise requests.HTTPError(f"HTTP {self.status_code}")
|
||||
|
||||
|
||||
@@ -65,8 +65,10 @@ class TestBracket:
|
||||
|
||||
def test_refresh_failure_returns_none(self, tmp_db, monkeypatch):
|
||||
import requests
|
||||
|
||||
def raiser(*a, **kw):
|
||||
raise requests.ConnectionError("boom")
|
||||
|
||||
monkeypatch.setattr("app.playoff_cache.requests.get", raiser)
|
||||
assert playoff_cache.refresh_bracket(2026) is None
|
||||
|
||||
@@ -94,12 +96,14 @@ class TestFetchSeries:
|
||||
|
||||
def should_not_be_called(*a, **kw):
|
||||
raise AssertionError("network should not be called within TTL")
|
||||
|
||||
monkeypatch.setattr("app.playoff_cache.requests.get", should_not_be_called)
|
||||
|
||||
assert playoff_cache.fetch_series("2026-A") == payload_cached
|
||||
|
||||
def test_stale_cache_falls_back_on_failure(self, tmp_db, monkeypatch):
|
||||
import requests
|
||||
|
||||
key = playoff_cache.series_key("20252026", "A")
|
||||
playoff_cache._put(key, {"from": "stale"})
|
||||
|
||||
@@ -114,12 +118,14 @@ class TestFetchSeries:
|
||||
|
||||
def raiser(*a, **kw):
|
||||
raise requests.ConnectionError("network gone")
|
||||
|
||||
monkeypatch.setattr("app.playoff_cache.requests.get", raiser)
|
||||
|
||||
assert playoff_cache.fetch_series("2026-A") == {"from": "stale"}
|
||||
|
||||
def test_ancient_stale_cache_returns_none(self, tmp_db, monkeypatch):
|
||||
import requests
|
||||
|
||||
key = playoff_cache.series_key("20252026", "A")
|
||||
playoff_cache._put(key, {"from": "ancient"})
|
||||
|
||||
@@ -146,9 +152,7 @@ class TestRecordStartDate:
|
||||
|
||||
def test_records_on_first_playoff_sighting(self, tmp_db):
|
||||
now = datetime(2026, 4, 18, 20, 0, tzinfo=EASTERN)
|
||||
result = playoff_cache.record_start_date_if_missing(
|
||||
[{"gameType": 3}], now=now
|
||||
)
|
||||
result = playoff_cache.record_start_date_if_missing([{"gameType": 3}], now=now)
|
||||
assert result == "2026-04-18"
|
||||
assert playoff_cache.get_playoff_start_date().isoformat() == "2026-04-18"
|
||||
|
||||
|
||||
@@ -233,7 +233,10 @@ class TestTodayMeta:
|
||||
assert meta["round_label"] is None
|
||||
|
||||
def test_playoff_games_on_mode(self):
|
||||
games = [make_playoff_game(series_letter="A"), make_playoff_game(series_letter="B")]
|
||||
games = [
|
||||
make_playoff_game(series_letter="A"),
|
||||
make_playoff_game(series_letter="B"),
|
||||
]
|
||||
meta = today_meta(games)
|
||||
assert meta["playoff_mode"] is True
|
||||
assert meta["series_active"] == 2
|
||||
|
||||
+15
-2
@@ -188,8 +188,16 @@ class TestSeriesDetailRoute:
|
||||
"startTimeUTC": "2026-04-18T23:00:00Z",
|
||||
"gameState": "OFF",
|
||||
"periodDescriptor": {"number": 3, "periodType": "REG"},
|
||||
"awayTeam": {"abbrev": "OTT", "score": 2, "commonName": {"default": "Senators"}},
|
||||
"homeTeam": {"abbrev": "TOR", "score": 6, "commonName": {"default": "Maple Leafs"}},
|
||||
"awayTeam": {
|
||||
"abbrev": "OTT",
|
||||
"score": 2,
|
||||
"commonName": {"default": "Senators"},
|
||||
},
|
||||
"homeTeam": {
|
||||
"abbrev": "TOR",
|
||||
"score": 6,
|
||||
"commonName": {"default": "Maple Leafs"},
|
||||
},
|
||||
"gameOutcome": {"lastPeriodType": "REG"},
|
||||
},
|
||||
{
|
||||
@@ -212,12 +220,14 @@ class TestSeriesDetailRoute:
|
||||
|
||||
def test_valid_format_but_missing_cache_404(self, flask_client, monkeypatch):
|
||||
import app.routes as routes
|
||||
|
||||
monkeypatch.setattr(routes, "fetch_series", lambda sid: None)
|
||||
response = flask_client.get("/series/2026-A")
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_renders_with_cached_payload(self, flask_client, monkeypatch):
|
||||
import app.routes as routes
|
||||
|
||||
monkeypatch.setattr(routes, "fetch_series", lambda sid: self._SAMPLE_PAYLOAD)
|
||||
|
||||
response = flask_client.get("/series/2026-A")
|
||||
@@ -265,6 +275,7 @@ class TestBracketRoute:
|
||||
|
||||
def test_bracket_renders_with_cache(self, flask_client, monkeypatch):
|
||||
import app.routes as routes
|
||||
|
||||
monkeypatch.setattr(routes, "get_bracket", lambda y: (self._BRACKET, 123))
|
||||
monkeypatch.setattr(
|
||||
routes,
|
||||
@@ -283,6 +294,7 @@ class TestBracketRoute:
|
||||
self, flask_client, monkeypatch
|
||||
):
|
||||
import app.routes as routes
|
||||
|
||||
monkeypatch.setattr(routes, "get_bracket", lambda y: (None, None))
|
||||
called = {"n": 0}
|
||||
|
||||
@@ -298,6 +310,7 @@ class TestBracketRoute:
|
||||
|
||||
def test_bracket_returns_404_when_no_data(self, flask_client, monkeypatch):
|
||||
import app.routes as routes
|
||||
|
||||
monkeypatch.setattr(routes, "get_bracket", lambda y: (None, None))
|
||||
monkeypatch.setattr(routes, "refresh_bracket", lambda year=None: None)
|
||||
response = flask_client.get("/bracket")
|
||||
|
||||
Reference in New Issue
Block a user