fix(common): favourite check no longer calls a started postseason a finished season (#667)

* fix(common): favourite check no longer calls a started postseason a finished season

The day after a regular season ends, ESPN's default scoreboard still
returns that last regular-season day, while leagues[0].season has moved
to Postseason. All events were in the past, so the check logged "the
season has finished" for MLB on 2026-09-29 while the upcoming manager in
the same process was showing TB's wild-card games.

When every event is past and the league is in a later in-season phase
(regular season or postseason) than all of the returned events, draw no
conclusion. The offseason is excluded, so a genuinely finished season is
still reported as finished.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(common): favourite check reports the next matchday between soccer rounds

Between matchdays ESPN's soccer scoreboard keeps showing the last one, so
every event is in the past and in the league's current phase, which the
postseason rule does not cover; the check said the Premier League season
had finished on 2026-09-29 (last games 20 September, next 10 October).
When the league calendar is a "day" whitelist, its entries are days with
games, so a future one is used as the next fixture. MLB's day calendar is
a blacklist and is not read that way; PLL's whitelist has no future days
and is still reported as finished.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-29 10:52:38 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent a11412dabb
commit db49275075
2 changed files with 130 additions and 0 deletions
+86
View File
@@ -353,3 +353,89 @@ class ScheduleNoteUsesGameDatesTests(unittest.TestCase):
"events": [{"date": self.iso(base, 1)}],
"leagues": [{"calendar": [{"startDate": self.iso(base, 6)}]}],
}))
class ScheduleNotePostseasonTests(unittest.TestCase):
"""Past regular-season events are not a finished season once the postseason starts.
Captured from ESPN's MLB scoreboard on 2026-09-29, two days after the
regular season ended: the default scoreboard still returned the last
regular-season day (event ``season.type`` 2), while ``leagues[0].season``
had already moved to Postseason (type 3). The check told a Rays fan the
season was over while their upcoming manager listed TB's wild-card games.
"""
note = ScheduleNoteTests.note
@staticmethod
def payload(league_type, event_type, days=-2):
date = (datetime.now(timezone.utc) + timedelta(days=days)).isoformat()
return {
"events": [{"date": date, "season": {"year": 2026, "type": event_type}}],
"leagues": [{"season": {"year": 2026, "type": {"type": league_type}},
"calendar": [date]}],
}
def test_postseason_after_regular_season_events_is_not_finished(self):
self.assertIsNone(self.note(self.payload(league_type=3, event_type=2)))
def test_a_finished_postseason_is_still_reported_as_finished(self):
# AFL the week after its grand final: league and events both postseason.
note = self.note(self.payload(league_type=3, event_type=3, days=-4))
self.assertIn("season has finished", note)
def test_offseason_after_the_postseason_is_still_finished(self):
note = self.note(self.payload(league_type=4, event_type=3, days=-40))
self.assertIn("season has finished", note)
class ScheduleNoteMatchdayTests(unittest.TestCase):
"""Between soccer matchdays the next fixture is only in the calendar.
Captured from ESPN's Premier League scoreboard on 2026-09-29: the
scoreboard still showed the 20 September matchday, the league was in the
same season phase as those events, and the next games (10 October) were
only in ``leagues[0].calendar`` -- a ``"day"`` calendar with
``calendarIsWhitelist`` true, i.e. the days that have games. The check
said the season had finished.
"""
note = ScheduleNoteTests.note
@staticmethod
def payload(event_days, calendar_days, whitelist=True):
from datetime import datetime, timedelta, timezone
now = datetime.now(timezone.utc)
def iso(days):
return (now + timedelta(days=days)).strftime("%Y-%m-%dT%H:%MZ")
return {
"events": [{"date": iso(d), "season": {"type": 14308}}
for d in event_days],
"leagues": [{"season": {"type": {"type": 14308}},
"calendarType": "day",
"calendarIsWhitelist": whitelist,
"calendar": [iso(d) for d in calendar_days]}],
}
def test_gap_between_matchdays_reports_the_next_matchday(self):
from datetime import datetime, timedelta, timezone
note = self.note(self.payload([-9], [-9, 11, 12]))
self.assertIn("nothing on until", note)
self.assertIn((datetime.now(timezone.utc) + timedelta(days=11))
.strftime("%d %B %Y"), note)
def test_next_matchday_within_a_couple_of_days_says_nothing(self):
self.assertIsNone(self.note(self.payload([-2], [-2, 1])))
def test_no_matchdays_left_is_still_finished(self):
# PLL on the same day: a match-day calendar whose last day is past.
note = self.note(self.payload([-9], [-30, -9]))
self.assertIn("season has finished", note)
def test_a_blacklist_day_calendar_is_not_read_as_fixtures(self):
# MLB's day calendar has calendarIsWhitelist false: it lists days
# without games, so a future entry there is not a next fixture.
note = self.note(self.payload([-9], [11], whitelist=False))
self.assertIn("season has finished", note)