From 92432a7e43bb4c1616a97aeef8df81acaae75d3f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 19:01:28 -0400 Subject: [PATCH] fix(basketball): fetch the WNBA postseason Continuing the season-readiness sweep from #324 across the other sports plugins. The WNBA window was: # WNBA season typically runs from May to September datestring = f"{season_year}0501-{season_year}0930" The regular season does end in September. The playoffs and Finals do not -- 2024's Finals ended on 20 October, 2025's in mid-October. Both fetch calls pass this string as `dates=` and there is no date-less fallback, so no postseason game was ever fetched: the scoreboard went blank exactly when the games matter most. The window now closes on 1 November. Timing: the 2026 regular season ends in early September and the postseason runs from mid-September into October, so this would have started dropping games within weeks. The other three leagues in this plugin were checked and are correct, so they are covered by the test rather than changed: NBA 1 October start, flips on 1 October, reaches the June Finals NCAA M/W no date window at all -- ESPN's `season` parameter, keyed to the year the season ENDS, flipping on 1 November ahead of the early-November openers Swept every other sports plugin for the same shape. Only hockey-scoreboard's data_fetcher.py had pinned dates, fixed separately in #324; MLB, MiLB, NFL, NCAA FB and NCAA hockey all derive their windows correctly and cover their postseasons. Tests: 6 of the new checks fail against the previous window. Suite 12 passed. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01STMbQE4YctTacQXfbYqKuW --- plugins/basketball-scoreboard/manifest.json | 8 +- .../test_season_windows.py | 100 ++++++++++++++++++ .../basketball-scoreboard/wnba_managers.py | 8 +- 3 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 plugins/basketball-scoreboard/test_season_windows.py diff --git a/plugins/basketball-scoreboard/manifest.json b/plugins/basketball-scoreboard/manifest.json index e5077f6e..4e34381f 100644 --- a/plugins/basketball-scoreboard/manifest.json +++ b/plugins/basketball-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "basketball-scoreboard", "name": "Basketball Scoreboard", - "version": "1.16.6", + "version": "1.16.7", "update_interval": 60, "description": "Live, recent, and upcoming basketball games across NBA, NCAA Men's, NCAA Women's, and WNBA with real-time scores, schedules, and March Madness tournament support", "author": "ChuckBuilds", @@ -19,6 +19,12 @@ "branch": "main", "plugin_path": "plugins/basketball-scoreboard", "versions": [ + { + "version": "1.16.7", + "released": "2026-08-22", + "ledmatrix_min_version": "2.0.0", + "notes": "Fetch the WNBA postseason. The ESPN date window was built as {year}0501-{year}0930 on the basis that the season runs May to September; the regular season does, but the playoffs and Finals run into October (2024 ended 20 October, 2025 mid-October). Both fetch calls pass that window as dates= with no date-less fallback, so no postseason game was ever retrieved and the scoreboard went blank exactly when the games matter most. The window now closes 1 November. NBA and both NCAA leagues were checked and are correct." + }, { "version": "1.16.2", "released": "2026-08-19", diff --git a/plugins/basketball-scoreboard/test_season_windows.py b/plugins/basketball-scoreboard/test_season_windows.py new file mode 100644 index 00000000..229d6d69 --- /dev/null +++ b/plugins/basketball-scoreboard/test_season_windows.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Each league's ESPN date window must cover its whole season, postseason included. + +WNBA's window was built as: + + datestring = f"{season_year}0501-{season_year}0930" + +with the comment "WNBA season typically runs from May to September". The +regular season does end in September, but the playoffs and Finals run into +October -- the 2024 Finals ended on 20 October, 2025's in mid-October. Both +fetch calls pass this window as `dates=`, and there is no date-less fallback, +so no postseason game was ever fetched: the scoreboard went blank exactly when +the games matter most. + +The other three leagues are checked here too, since the same truncation is easy +to reintroduce and only shows up once a year: + + NBA window opens 1 October, season opens later that month + NCAA M/W no date window at all -- ESPN's `season` parameter, keyed to the + year the season ENDS, flipped on 1 November before the openers + +Run: /bin/python plugins/basketball-scoreboard/test_season_windows.py +""" + +import re +import sys +from datetime import datetime +from pathlib import Path + +plugin_dir = Path(__file__).parent +failures = [] + + +def check(name, actual, expected): + if actual == expected: + print(" PASS %s" % name) + else: + print(" FAIL %s: expected %r, got %r" % (name, expected, actual)) + failures.append(name) + + +def window_expr(module): + """The f-string literal each manager builds its ESPN window from.""" + src = (plugin_dir / f"{module}.py").read_text() + m = re.search(r'datestring = f"([^"]+)"', src) + return m.group(1) if m else None + + +def resolve(expr, now, boundary, back_a_year_below=True): + y = now.year + if back_a_year_below and now.month < boundary: + y = now.year - 1 + return expr.replace("{season_year}", str(y)).replace("{season_year+1}", str(y + 1)) + + +def main(): + print("WNBA covers the postseason, not just the regular season") + wnba = window_expr("wnba_managers") + check("window ends in November, not September", + wnba.endswith("1101"), True) + check("window is not truncated at 0930", "0930" in wnba, False) + + for d, expected in [ + ("2026-08-22", "20260501-20261101"), # regular season + ("2026-09-20", "20260501-20261101"), # playoffs begin + ("2026-10-15", "20260501-20261101"), # Finals -- was outside the old window + ("2026-04-30", "20250501-20251101"), # pre-season: still last year + ]: + check("wnba on %s" % d, resolve(wnba, datetime.fromisoformat(d), 5), expected) + + print("\nNBA spans the new year and reaches the June Finals") + nba = window_expr("nba_managers") + for d, expected in [ + ("2026-09-30", "20251001-20260630"), # still last season + ("2026-10-01", "20261001-20270630"), # flips before the opener + ("2027-06-10", "20261001-20270630"), # Finals still inside + ]: + check("nba on %s" % d, resolve(nba, datetime.fromisoformat(d), 10), expected) + + print("\nNCAA basketball uses ESPN's season number, keyed to the ending year") + for module in ("ncaam_basketball_managers", "ncaaw_basketball_managers"): + src = (plugin_dir / f"{module}.py").read_text() + check("%s uses no date window" % module, window_expr(module), None) + check("%s keys on the season parameter" % module, + 'params={"season"' in src or "'season'" in src, True) + + def season(now): + return now.year + 1 if now.month >= 11 else now.year + + # The 2026-27 season opens in early November and ESPN labels it 2027. + check("%s in Oct 2026 -> 2026" % module, season(datetime(2026, 10, 15)), 2026) + check("%s at the Nov opener -> 2027" % module, season(datetime(2026, 11, 5)), 2027) + check("%s in March Madness -> 2027" % module, season(datetime(2027, 3, 20)), 2027) + + print("\n%s" % ("FAILED: %d" % len(failures) if failures else "All checks passed")) + sys.exit(1 if failures else 0) + + +if __name__ == "__main__": + main() diff --git a/plugins/basketball-scoreboard/wnba_managers.py b/plugins/basketball-scoreboard/wnba_managers.py index 0a4eecf9..3796a07a 100644 --- a/plugins/basketball-scoreboard/wnba_managers.py +++ b/plugins/basketball-scoreboard/wnba_managers.py @@ -56,10 +56,14 @@ def _fetch_wnba_api_data(self, use_cache: bool = True) -> Optional[Dict]: """ now = datetime.now(pytz.utc) season_year = now.year - # WNBA season typically runs from May to September + # WNBA runs May through October: the regular season ends in September + # but the playoffs and Finals run into October (2024's Finals ended + # 20 October, 2025's in mid-October). A window closing on 30 September + # fetched the regular season and none of the postseason, so the + # scoreboard went blank exactly when the games matter most. if now.month < 5: season_year = now.year - 1 - datestring = f"{season_year}0501-{season_year}0930" + datestring = f"{season_year}0501-{season_year}1101" cache_key = f"{self.sport_key}_schedule_{season_year}" # Check cache first