Repository navigation
fix: November council fixes from 9 contributor PRs (Gedling, Canterbury, Newcastle, Charnwood, Lancaster, East Herts, East Suffolk, Torfaen, East Hampshire) - #2343
Merged
Conversation
Contributor
|
Warning Review limit reachedYou'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 24 minutes. View limit details
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. |
❌ 2 Tests Failed:
View the top 1 failed test(s) by shortest run time
View the full list of 1 ❄️ flaky test(s)
To view more test analytics, go to the Test Analytics Dashboard |
robbrad
force-pushed
the
claude/november-release
branch
from
November 14, 2026 06:34
794ee32 to
c9092c4
Compare
robbrad
force-pushed
the
claude/november-release
branch
from
November 14, 2026 06:35
c9092c4 to
5cecd3f
Compare
Lancaster's portal lists "Recycling Red Collection Service" and "Recycling Yellow Collection Service", but prefix matching against "Recycling" collapsed both into a single Recycling bin. Match the specific streams first, keeping "Recycling" as the fallback for the unspecified-stream wording.
East Herts retired AchieveForms lookup 683d9ff0e299d. It still answers
HTTP 200, but with {"error": "User Package Not Found"}, so the scraper
crashed with KeyError: 'integration'. The "When are my Bin Collection
days?" form now calls lookup 6accfd6441228. The form and process URIs,
the posted field (Collection Days / inputUPRN) and the response path
(integration.transformed.rows_data["0"]) are unchanged.
- Use the new lookup id.
- Raise a ValueError naming the council's message when the lookup
returns an error body, so the next form change is obvious.
- Strip the whitespace the council now wraps around service names.
- Sort bins by collection date.
…a browser Gedling now publishes bin days through a Netcall Liberty Create portal (waste.digital.gedling.gov.uk). The gbcbincalendars.co.uk API the scraper used reads the old calendars: it takes 30-45 seconds, a bare postcode always fails, and many addresses fail with "Failed to get yearly calendar page". Search the portal by postcode, choose the address that starts with the house number or name, and read its next four weeks of collections. Plain requests, about 1.5 seconds. Takes postcode and house_number, as the current scraper does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Instead of searching a malformed URL, which would surface as a misleading "no addresses for postcode" error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new endpoint is called without the old outage handling, so a 403 would surface as a bare HTTPError. Say what it usually means (a network block or a council-side outage, not a bad UPRN). Adds tests for both response shapes, the bin-type mapping and ordering, the 403 message and the no-data errors, which the original change did not include. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
…kets The bracket-stripping change replaced the " - standard bin" rule, so any row still using that wording would regress. Both are handled now, in a small function so each wording is covered by a test, and a bracket in the middle of a name no longer leaves a double space. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
…ther home
The address search is a fuzzy prefix match, and the previous code fell back to
the first parcel when nothing matched, which silently returned a different
household's days. The contributor's change makes the match whole-word and
postcode-constrained; these tests lock that in ("1" must not match 10, 11,
1A or a flat in No. 12; an unknown number or wrong postcode is refused).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
If two addresses at a postcode started the same way the first was chosen silently, which returns another household's collection days. It now raises and lists the matches. A page that parses to no dated collections also raises instead of returning an empty list (which surfaced downstream as an opaque "[] should be non-empty"). Tests added for both. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
async_get_events compared collection_date <= end_date.date(), so a collection on the day a midnight-bounded range ends was returned. Asking for tomorrow's events (tomorrow 00:00 to the day after 00:00) returned the day after tomorrow's collection, and asking for today's events returned tomorrow's, so dashboards built on calendar.get_events showed bin days one day early. Compare the all-day event's [start, start + 1 day) span against the requested range with an exclusive end instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… passes The contributor's tests use naive datetimes. Home Assistant localises both dates (dt_util.as_local) before calling async_get_events, so in summer a "tomorrow" range starts at 23:00 UTC the day before. These tests use that real shape (Europe/London, BST) and assert that today's, tomorrow's and the day after's collections each land in exactly one of the two ranges. They fail on the old inclusive-end code and pass with the fix. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
…olumns Follow-up to the layout fix (#2310, which found the council had moved from col-md-4 to col-md-3 and dropped the year from dates; this is why #2334 reports a blank result). - Address matching could return another household. The fallback matched the house number anywhere in the label, and with no house number the first address for the postcode was taken. It now matches only at the start of the label, raises when more than one address starts the same way, and accepts a missing house number only for a postcode with a single address. - Matching every col-* div also matched outer wrapper columns, which pair their first heading with the first date they contain. If the first service has no date, that is the next service's date under the wrong name. Only the innermost columns are read now, and repeated (service, date) pairs dropped. - The page parsing and address matching are pure functions with 25 tests built from the layouts in the PR, using fixed "today" values so none expire. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
…ad of returning nothing The council's error paths built Exception(...) objects without raising them, so an unknown or ambiguous street fell through to an empty result (or an UnboundLocalError on the street page). Raise real errors, prefer an exact street-name match, and list the candidates when more than one street matches. Targets #2334 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
…not one containing it
The dropdown was matched with `paon in option.text`, so "40" selected the
first label containing 40 anywhere ("140 ...", "Flat 40") or nothing at all,
after which the page timed out waiting for collections. Match whole-word at the
start of the label and raise on no match or on ambiguity.
Targets #2305
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
…ybook Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
…address The contributor's input.json entry and test fixtures used a residential address. Use Gedling Civic Centre (NG5 6LU) in input.json and a fictional address on the same postcode in the unit-test fixtures. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4
robbrad
force-pushed
the
claude/november-release
branch
from
November 14, 2026 06:39
5cecd3f to
a1c2617
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please merge with a merge commit or rebase, not a squash
This branch carries 15 commits authored by the original contributors, cherry-picked without
-nso each keeps their authorship. Squashing would re-author all of them to whoever merges (the problem described in the playbook after #2276). My follow-up commits are separate and labelled. Every commit title is conventional (fix:/test:/docs:), so the release triggers either way.Included from contributor PRs
LAD24CD(E07000085)HeadlessChrome) and addsLAD24CDparcels[0]requestsMy follow-up commits
I read every diff and added what I'd have asked the contributors for:
HTTPError; 6 tests (PR had none)." - standard bin"rule, so old wording would regress. Both handled in a testable function; 7 tests.[]; 2 tests.col-*columns are read (the outer wrapper repeated its first service); dates with/without a year; raises if no bins found. 25 tests, mutation-checked.Targets #2334): fiveException(...)objects were built and never raised, so an unknown street returned an empty result and a failed street page crashed withUnboundLocalError. They now raise; an exact street name wins over longer ones containing it; ambiguity lists the candidates. 7 tests, all fail on the old code.Targets #2305): the dropdown was matched withpaon in option.text, so "40" selected "140 ..." / "Flat 40" or nothing, and the page then timed out. Now start-anchored, whole-word, raises on none/ambiguous. 9 tests added; the existing test's mock dropdown had no "select an address" prompt, so I gave it one like the real page.ISSUE_RESOLUTION_PROGRESS.mdand a lessons section in the playbook.Heads-up for the maintainer
input.jsongetsNG5 6NQ/11(Gedling Road, Arnold). I haven't changed it because I can't verify a replacement offline. Decision for you.Not included, with reasons
match_addresswhose third pattern matches the number anywhere in the label ("1" returns "Flat 1, 12 Main Street"). Several still require a UPRN in the HA form, and some test data looks residential. They need one shared, tested helper.wiki/Councils.md, reads the PDF by hardcoded RGB colours, matches by substring.Test plan
make unit-testson Python 3.14 with Home Assistant at the latest head: 630 passed; the 7 failures are the 3 sandbox-only network tests plus Orkney and Broxbourne ×3, all fixed by test: stop the Orkney and Broxbourne tests failing on the calendar and the network #2341.lint.yml.🤖 Generated with Claude Code
https://claude.ai/code/session_015MaCS29izthZp2dwtvLwf4