Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
97 changes: 97 additions & 0 deletions uk_bin_collection/tests/test_vale_of_white_horse_council.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""Unit tests for the Vale of White Horse fortnightly roll-forward.

The council page publishes the current fortnight's pair of collections rather
than the next occurrence of each bin, so the scraper rolls a past date forward
by 14 days - except into a week with a bank holiday, when the council collects
on a different day and only the page knows which.
"""

from datetime import datetime as real_datetime
from types import SimpleNamespace
from unittest.mock import patch

from uk_bin_collection.uk_bin_collection.councils.ValeofWhiteHorseCouncil import (
CouncilClass,
)

MODULE_PATH = "uk_bin_collection.uk_bin_collection.councils.ValeofWhiteHorseCouncil"

PAGE = """
<html><body>
<div class="bintxt">It's rubbish week</div>
<div class="binextra"><span>{rubbish}</span><span>grey bin, small electrical items and food bin</span></div>
<div class="bintxt">It's recycling week</div>
<div class="binextra"><span>{recycling}</span><span>food bin, green bin, textiles and garden waste bin</span></div>
</body></html>
"""


def parse_fixture(rubbish: str, recycling: str, now: real_datetime) -> dict:
"""Run the parser against a page showing the given two panels, on `now`."""

class FrozenDatetime(real_datetime):
@classmethod
def now(cls, tz=None):
return now

response = SimpleNamespace(text=PAGE.format(rubbish=rubbish, recycling=recycling))
with patch(f"{MODULE_PATH}.requests.Session") as session, patch(
f"{MODULE_PATH}.datetime", FrozenDatetime
):
session.return_value.get.return_value = response
return CouncilClass().parse_data("", uprn="100120000000")


def test_a_bin_collected_earlier_this_fortnight_rolls_forward_14_days():
result = parse_fixture(
"Monday 3 August -", "Monday 10 August -", real_datetime(2026, 8, 8)
)

assert result == {
"bins": [
{"type": "Recycling", "collectionDate": "10/08/2026"},
{"type": "Rubbish", "collectionDate": "17/08/2026"},
]
}


def test_no_roll_forward_into_a_bank_holiday_week():
# 31 August 2026 is the late summer bank holiday, so 17 + 14 would be a
# day the council does not collect: the bin is left out until the page
# shows the real date.
result = parse_fixture(
"Monday 17 August -", "Monday 24 August -", real_datetime(2026, 8, 22)
)

assert result == {"bins": [{"type": "Recycling", "collectionDate": "24/08/2026"}]}


def test_a_rescheduled_date_on_the_page_is_returned_as_is():
result = parse_fixture(
"Tuesday 1 September -", "Monday 7 September -", real_datetime(2026, 8, 30)
)

assert result["bins"] == [
{"type": "Rubbish", "collectionDate": "01/09/2026"},
{"type": "Recycling", "collectionDate": "07/09/2026"},
]


def test_roll_forward_crosses_the_year_boundary():
result = parse_fixture(
"Monday 21 December -", "Monday 28 December -", real_datetime(2026, 12, 23)
)

assert result["bins"] == [
{"type": "Recycling", "collectionDate": "28/12/2026"},
{"type": "Rubbish", "collectionDate": "04/01/2027"},
]


def test_no_roll_forward_into_christmas_week():
# 14 + 14 = 28 December 2026, the observed Boxing Day holiday.
result = parse_fixture(
"Monday 14 December -", "Monday 21 December -", real_datetime(2026, 12, 19)
)

assert result["bins"] == [{"type": "Recycling", "collectionDate": "21/12/2026"}]
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass


def week_has_bank_holiday(collection_date) -> bool:
"""True if an English bank holiday falls Monday to Friday of that week.

A bank holiday shifts every collection in its week, not just the day
itself, so the whole working week is checked.
"""
monday = collection_date - timedelta(days=collection_date.weekday())
return any(
is_holiday(
datetime.combine(monday + timedelta(days=offset), datetime.min.time())
)
for offset in range(5)
)


# import the wonderful Beautiful Soup and the URL grabber
class CouncilClass(AbstractGetBinDataClass):
"""
Expand Down Expand Up @@ -97,8 +112,17 @@ def parse_data(self, page: str, **kwargs) -> dict:
# fortnight parses to a date already in the past - roll it
# forward by the fortnightly cycle (day-based, so it naturally
# crosses a year boundary too) until it's genuinely upcoming.
while bin_date < today:
bin_date += timedelta(days=14)
#
# Unless that lands in a week with a bank holiday: the council
# collects on a different day whenever there is one, and only
# the page knows which, so an inferred date would be wrong.
# Leave the bin out until the page catches up rather than
# publish a guess.
if bin_date < today:
while bin_date < today:
bin_date += timedelta(days=14)
if week_has_bank_holiday(bin_date):
continue
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Build data dict for each entry
dict_data = {
Expand Down