Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions soccerdata/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,12 +460,30 @@ def seasons(self) -> list[str]:
def seasons(self, seasons: str | int | Iterable[str | int] | None) -> None:
if seasons is None:
logger.info("No seasons provided. Will retrieve data for the last 5 seasons.")
year = datetime.now(tz=timezone.utc).year
seasons = [f"{y - 1}-{y}" for y in range(year, year - 6, -1)]
seasons = self._default_seasons(datetime.now(tz=timezone.utc))
if isinstance(seasons, (str, int)):
seasons = [seasons]
self._season_ids = [self._season_code.parse(s) for s in seasons]

def _default_seasons(self, now: datetime) -> list[str]:
"""Return the last 5 seasons, ending with the current one.

The current season is derived from the season calendar, not from the
calendar year. For a multi-year league (e.g. Aug-May), the current
season ends in the next calendar year during the second half of the
year, so the end year is shifted by one during that period. For a
single-year league, the season id equals the start year of the
generated range, so the end year is one ahead of the calendar year.
"""
end_year = now.year
if (
self._season_code == SeasonCode.SINGLE_YEAR
or self._season_code == SeasonCode.MULTI_YEAR
and now.month >= 8
):
end_year += 1
return [f"{y - 1}-{y}" for y in range(end_year, end_year - 5, -1)]


class BaseRequestsReader(BaseReader):
"""Base class for readers that use the Python requests module."""
Expand Down
44 changes: 44 additions & 0 deletions tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,50 @@ def test_is_complete_undefined_league(mocker):
reader._is_complete("FAKE-Dummy League", "1920")


def test_default_seasons(mocker):
"""It should default to the last 5 seasons ending with the current season."""
reader = BaseRequestsReader(no_store=True)
october = datetime(2024, 10, 30, 12, 0, tzinfo=timezone.utc)
march = datetime(2024, 3, 30, 12, 0, tzinfo=timezone.utc)

# multi-year leagues: the current season ends next year after August
mocker.patch.object(
BaseRequestsReader,
"_season_code",
new_callable=mocker.PropertyMock,
return_value=SeasonCode.MULTI_YEAR,
)
assert [SeasonCode.MULTI_YEAR.parse(s) for s in reader._default_seasons(october)] == [
"2425",
"2324",
"2223",
"2122",
"2021",
]
assert [SeasonCode.MULTI_YEAR.parse(s) for s in reader._default_seasons(march)] == [
"2324",
"2223",
"2122",
"2021",
"1920",
]

# single-year leagues: seasons are single calendar years
mocker.patch.object(
BaseRequestsReader,
"_season_code",
new_callable=mocker.PropertyMock,
return_value=SeasonCode.SINGLE_YEAR,
)
assert [SeasonCode.SINGLE_YEAR.parse(s) for s in reader._default_seasons(october)] == [
"2024",
"2023",
"2022",
"2021",
"2020",
]


# Season codes
def test_season_pattern1a():
assert SeasonCode.MULTI_YEAR.parse("9495") == "9495"
Expand Down
Loading