Skip to content

fix(north-east-derbyshire)!: parse the published area calendars - #2280

Closed
robbrad wants to merge 1 commit into
masterfrom
fix/ne-derbyshire-html-calendars
Closed

robbrad wants to merge 1 commit into
masterfrom
fix/ne-derbyshire-html-calendars

Conversation

@robbrad

@robbrad robbrad commented Oct 15, 2026

Copy link
Copy Markdown
Owner

⚠️ Breaking config change — existing users must reconfigure

North East Derbyshire no longer takes a UPRN or a postcode, and no longer uses
Selenium. It now takes the user's collection area and normal collection weekday in
the house number field, e.g. North, Monday or Clay Cross, Friday.

Existing Home Assistant users will have to reconfigure the integration after the release
that carries this (the config flow is built from input.json, which INPUT_JSON_URL pins
to a release tag, so nothing changes for them until then). CLI users must swap
-u <uprn> -p <postcode> for -n "<area>, <weekday>".

Why

https://myselfservice.ne-derbyshire.gov.uk/service/Check_your_Bin_Day no longer offers a
per-address lookup — it redirects to the council homepage — so the Selenium scraper could
only ever time out waiting for the postcode form. Verified live from a UK residential
connection on 2026-10-15. This is the long-standing failure tracked in #1881.

The council publishes the same information as plain HTML on its main site instead:
https://www.ne-derbyshire.gov.uk/bins-and-recycling/bin-collection-dates returns 200 to a
plain requests GET with get_scraper_user_agent() (it is Cloudflare-fronted but does not
challenge a plain GET). It carries two area calendars, "Calendar A - The North" and
"Calendar B - The South", each a list of date ranges and the bins collected during that
range:

Monday 30th November to Friday 4th December 2026: Green Bin, Burgundy Bin and Food Caddy collections

Why the inputs had to change, and why they go in house_number

A week-long range tells you which bins go out that week but not which day your bin goes
out, so the scraper needs two things from the user: the area and their normal collection
weekday. collect_data.py only plumbs through a fixed set of arguments, so the options were:

  1. Both in the existing free-text house_number field (chosen). OrkneyIslandsCouncil
    already uses house_number for a non-address value, so there is precedent, no change to
    collect_data.py, and no change to the HA config flow — the field the user already sees
    simply carries a different kind of value, described in wiki_note. The scraper validates
    it and raises a message listing the accepted areas and weekdays.
  2. house_number for the weekday, postcode for the real postcode, deriving the area.
    Checked and rejected: the page contains no postcode-to-settlement mapping. The only
    postcode anywhere on it is the council's own office address (S42 6NG), and there is no
    other published mapping from postcode to Calendar A/B.
  3. A new argument in collect_data.py plus the HA config flow. More correct in the
    abstract, but it touches the integration, and INPUT_JSON_URL is pinned to a release tag,
    so it would reach users no sooner than option 1 while adding a field to every council's
    flow. Not needed, since option 1 works.

What the parser handles

The page's markup is irregular, and all of this is covered by the unit tests:

  • North dates are in <li> elements, South dates in <p> elements.
  • Month headings appear as <h4>August 2026</h4> and as <p>December '26</p> / <p>March 27</p>.
  • Ordinal suffixes (1st, 2nd, 3rd, 21st, 30th).
  • Entries whose start date carries neither month nor year (Monday 3rd to Friday 7th August),
    taking both from the end date and the month heading.
  • Ranges crossing the year boundary (Wednesday 30th December 2026 to Friday 1st January 2027).
  • Ranges from one day to twelve (Monday 4th January to Friday 15th January 2027 is two
    weeks and yields two collections per weekday).
  • No collections weeks, which are skipped.
  • Bank-holiday weeks, where the range starts on a Tuesday and the council shifts every
    household a day later ("when there is a Bank Holiday Monday, the collection day is normally
    one day later than usual"): a Tuesday 30th March to Saturday 3rd April 2027 range collects
    a Monday household on the Tuesday and a Friday household on the Saturday.
  • The council's B lack Bin typo, since bin names are matched with non-letters removed.
  • Weeks listed twice across two monthly sections, deduplicated.

Settlement lists are scraped from the "Which calendar do I use?" section rather than
hardcoded, and an explicit North/South always wins. Sutton and Temple Normanton appear on
both lists (their smaller 'rural' route properties are on Calendar A and the rest on
Calendar B), so passing either name alone raises an error asking for an explicit area.

Known limitation, stated in wiki_note: the Christmas catch-up collections
(Tuesday 29th December 2026: ... if your bin should have been emptied on the 25th of December) are not attributed to the households they apply to, so that one collection is
under-reported rather than mis-reported.

Test plan

  • uk_bin_collection/tests/test_north_east_derbyshire.py — new, 20 offline tests driving the
    parser from a fixture reproducing the live markup shapes captured on 2026-10-15. Covers a
    North address and a South one, settlement lookup, a two-day range, a twelve-day range, the
    year boundary, a bank-holiday week, ordinal parsing, the typo, past-date filtering,
    No collections weeks, a settlement on both calendars, an unknown area, an unreadable
    weekday, an exhausted calendar, and the HTTP fetch with the right User-Agent. All pass.
  • poetry run pytest uk_bin_collection/tests custom_components/uk_bin_collection/tests --ignore=uk_bin_collection/tests/step_defs/ -q
    → 359 passed, no failures.
  • Live integration test, run from a UK residential connection:
    poetry run pytest uk_bin_collection/tests/step_defs/ -k NorthEastDerbyshire --local_browser True -q
    → 1 passed in 2.4s (one plain GET, no Selenium).
  • Live CLI end to end:
    collect_data.py NorthEastDerbyshireDistrictCouncil <url> -n "Clay Cross, Friday" -s
    → valid JSON, 142 entries starting 16/10/2026, cross-checked against the published South
    calendar including the bank-holiday weeks and the December/January boundary.
  • Offline parity check and python -c "import json; json.load(...)" on input.json → clean.
  • web_driver consistency: the entry has no web_driver and the class no longer calls
    create_webdriver. (The CI script itself reads from the GitHub API and was rate-limited
    locally, so this was checked with the offline equivalent.)
  • poetry run black on both changed files → unchanged.

input.json is updated in the same commit: uprn, postcode and web_driver removed,
house_number set to North, Monday, url pointed at the calendar page with
skip_get_url, and wiki_note rewritten to say exactly what to enter and that existing
users must reconfigure.

Targets #1881 — left open rather than auto-closed so the reporter can confirm against their
own address and calendar.

🤖 Generated with Claude Code

The council withdrew the per-address lookup from its self-service portal
(#1881): Check_your_Bin_Day redirects to the homepage, so the Selenium
uprn + postcode scraper could only ever time out.

The district's collection calendars are published as plain HTML on the
main council site instead, as two area calendars ("Calendar A - The
North" and "Calendar B - The South"), each a list of date ranges and the
bins collected during that range. Parse those with a plain requests GET.

BREAKING CHANGE: a week-long range says which bins go out that week but
not on which day, so the scraper now needs the user's area and their
normal collection weekday rather than a uprn and postcode. Both are
passed in the existing house_number field, e.g. "North, Monday" or
"Clay Cross, Friday", following the OrkneyIslandsCouncil precedent for a
non-address value in that field; nothing in collect_data.py or the Home
Assistant config flow needs new plumbing. Existing users must
reconfigure. input.json drops uprn, postcode and web_driver in the same
commit.

The parser handles the page's real irregularities: ordinal suffixes,
entries whose start date carries neither month nor year, the month
heading formats ("August 2026", "December '26"), ranges from one to
twelve days, ranges crossing the year boundary, "No collections" weeks,
and bank-holiday weeks, where the council shifts every household a day
later. Settlement lists are scraped rather than hardcoded, and Sutton
and Temple Normanton, which appear on both calendars, raise an error
asking for an explicit area.

Targets #1881

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

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 43 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6ca2f13f-f64f-4749-8c98-09a691f80a7e
📥 Commits

Reviewing files that changed from the base of the PR and between d62b4f1 and b8ff309.

📒 Files selected for processing: 3
  • uk_bin_collection/tests/input.json
  • uk_bin_collection/tests/test_north_east_derbyshire.py
  • uk_bin_collection/uk_bin_collection/councils/NorthEastDerbyshireDistrictCouncil.py
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.27451% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.07%. Comparing base (85a38d3) to head (b8ff309).
⚠️ Report is 7 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ion/councils/NorthEastDerbyshireDistrictCouncil.py 86.27% 21 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2280      +/-   ##
==========================================
+ Coverage   80.97%   82.07%   +1.09%     
==========================================
  Files          16       34      +18     
  Lines        1619     2616     +997     
==========================================
+ Hits         1311     2147     +836     
- Misses        308      469     +161     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@robbrad

robbrad commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

Cherry-picked into #2277 unchanged (commit 911b6b49), so closing this one.

Nothing wrong with the work — bump.yml runs on every push to master, so merging this separately would have cut its own release. #2277 now carries all of this pass's council fixes as a single merge and a single release, and its body states that the squash title must be fix!: so your breaking change still drives the version bump and reaches the changelog.

Re-verified on the consolidated branch: the live test and the 20 offline tests all pass, parity is clean, and input.json is consistent with the new area+weekday inputs.

@robbrad robbrad closed this Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant