Skip to content

fix: #2153 Lincoln stale anchor date + Flintshire schedule-note cleanup - #2166

Merged
robbrad merged 3 commits into
masterfrom
fix/lincoln-stale-anchor-date
Jul 14, 2026
Merged

robbrad merged 3 commits into
masterfrom
fix/lincoln-stale-anchor-date

Conversation

@robbrad

@robbrad robbrad commented Jul 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

Root cause of #2153 ("Lincoln city no longer works", all sensors unavailable, log shows "No bin data found... No previous data to fall back to").

The council's API returns a "next date" per bin type that can lag behind reality by weeks. Verified live right now: refusenextdate is 2026-06-03 - over 6 weeks in the past - and identical across every single address in the test postcode, confirming this is a staleness issue on the council's own backend, not anything address-specific (rules out a bad UPRN/postcode on the reporter's end).

The scraper only ever projected a small fixed window of occurrences forward from that anchor ([0, 7, 14, 21, 28] days for weekly, [0, 14, 28] for fortnightly). Once the anchor is stale by more than that window, every generated date - including the last one - is still in the past. HA's coordinator filters out all past dates, leaving nothing, which is exactly the reported "No bin data found, no previous data to fall back to" symptom.

Fix: roll the anchor date forward by the bin type's own frequency (weekly/fortnightly) until it's genuinely upcoming, then generate the occurrence window from there. A stale anchor no longer produces an entirely-past result.

Also folds in #2167 (FlintshireCountyCouncil)

Found while re-investigating #2118 (Brown Bin / Garden Waste). The Brown Bin extraction itself was already working correctly (unrelated to this fix), but along the way found a genuine, separate bug: some collection weeks include a schedule-notice row (*New Round* - Tuesday AHP) in the same table column as the actual bin type, which was getting appended to the bins list as if it were a real collection. These notice rows are consistently prefixed with *, so skip them.

Test plan

  • Verified live: previously all-past Lincoln dates (03/06–01/07) now correctly resolve to 15/07 onward
  • Verified live: Flintshire junk *New Round* entries gone, Brown Bin and everything else still correctly present
  • poetry run pytest uk_bin_collection/tests/step_defs/ -k "LincolnCouncil or FlintshireCountyCouncil" - both passed
  • poetry run pytest uk_bin_collection/tests custom_components/uk_bin_collection/tests --ignore=uk_bin_collection/tests/step_defs/ - 221 passed
  • black --check clean

Fixes #2153

Summary by CodeRabbit

  • Bug Fixes
    • Improved Lincoln bin collection date calculations to ensure the next genuinely upcoming collection is shown for weekly and fortnightly schedules.
    • Updated future schedule generation for weekly and fortnightly services.
    • Adjusted Lincoln/Flintshire parsing so only valid bin types contribute to upcoming collection results, while preserving existing address matching behavior.

The council's API returns a "next date" field for each bin type that
can lag behind reality by weeks (confirmed live: refusenextdate was
2026-06-03, over 6 weeks in the past, identical across every address
in the postcode - a staleness issue on their end, not address-specific).
The scraper only ever projected a small fixed window of occurrences
forward from that anchor, so when the anchor itself was stale enough,
every generated date - including the last one in the window - was
still in the past. HA's coordinator then filtered out all of them,
producing "No bin data found" with nothing to fall back to (the
reported symptom).

Now rolls the anchor date forward by the collection's own frequency
(weekly/fortnightly) until it's genuinely upcoming before generating
the occurrence window, so a stale anchor no longer produces an
entirely-past result. Verified live: previously all-past dates
(03/06-01/07) now correctly resolve to 15/07 onward.
@coderabbitai

coderabbitai Bot commented Jul 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Lincoln council parsing now rolls collection dates forward using recognized weekly or fortnightly frequencies, skips invalid past dates, and limits generated occurrences. Flintshire parsing ignores non-bin schedule notices, while Lincoln property matching formatting remains behaviorally unchanged.

Changes

Council parsing

Layer / File(s) Summary
Upcoming collection scheduling
uk_bin_collection/uk_bin_collection/councils/LincolnCouncil.py
Frequency labels map to day steps; past weekly and fortnightly dates roll forward, while unrecognized frequencies skip past dates and use only the current offset.
Flintshire schedule notice filtering
uk_bin_collection/uk_bin_collection/councils/FlintshireCountyCouncil.py
Schedule entries whose bin types start with * are skipped instead of being added to the bins list.
PAON matching readability
uk_bin_collection/uk_bin_collection/councils/LincolnCouncil.py
The existing matching condition and ValueError construction are reformatted without changing matching behavior or error content.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main Lincoln stale-date fix and also mentions the Flintshire schedule-note cleanup.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/lincoln-stale-anchor-date

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.

@robbrad robbrad mentioned this pull request Jul 13, 2026
4 tasks done

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@uk_bin_collection/uk_bin_collection/councils/LincolnCouncil.py`:
- Around line 90-107: Update the frequency handling in the BIN_TYPES loop to
distinguish missing/empty frequency values from unrecognized non-empty values.
Preserve the existing one-off behavior only when the frequency is absent, but
raise an explicit error for any non-empty value not present in FREQ_DAYS instead
of entering the else branch; retain the existing roll-forward logic for
recognized frequencies.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ee034516-fc1e-49b2-88fb-6b8174b33cbb

📥 Commits

Reviewing files that changed from the base of the PR and between a102aca and ac38a87.

📒 Files selected for processing (1)
  • uk_bin_collection/uk_bin_collection/councils/LincolnCouncil.py

Comment on lines 90 to +107
for key, bin_type, freq in BIN_TYPES:
if not data[key]:
continue
offsets = [0]
if data[freq] == "fortnightly":
offsets.extend(list(range(14, 30, 14)))
elif data[freq] == "weekly":
offsets.extend(list(range(7, 30, 7)))
date = datetime.strptime(data[key], "%Y-%m-%d").date()
step = FREQ_DAYS.get(data[freq])
if step:
# The council's "next date" field can lag behind reality
# by weeks - roll it forward by the collection frequency
# until it's genuinely upcoming, rather than trusting it
# as always being the next one.
while date < today:
date += timedelta(days=step)
occurrences = 3 if step == 14 else 5
offsets = [step * n for n in range(occurrences)]
else:
if date < today:
continue
offsets = [0]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Unrecognized/empty freq values silently skip the roll-forward fix.

step = FREQ_DAYS.get(data[freq]) returns None for any frequency string other than exactly "weekly"/"fortnightly" (typo, casing mismatch, empty string, or a new frequency category added by the council). In that case the code falls into the else branch and treats the anchor date as a one-off, silently reproducing this PR's original bug (stale past dates causing unavailable sensors) for that bin type instead of surfacing the unexpected value.

Based on learnings, bin collection parsing should prefer explicit failures over silent defaults when encountering unexpected formats, so format regressions are caught early.

🐛 Suggested fix: raise on unrecognized non-empty frequency values
                 date = datetime.strptime(data[key], "%Y-%m-%d").date()
-                step = FREQ_DAYS.get(data[freq])
+                freq_value = data[freq]
+                if freq_value and freq_value not in FREQ_DAYS:
+                    raise ValueError(
+                        f"Unrecognized collection frequency '{freq_value}' for {bin_type}"
+                    )
+                step = FREQ_DAYS.get(freq_value)
                 if step:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for key, bin_type, freq in BIN_TYPES:
if not data[key]:
continue
offsets = [0]
if data[freq] == "fortnightly":
offsets.extend(list(range(14, 30, 14)))
elif data[freq] == "weekly":
offsets.extend(list(range(7, 30, 7)))
date = datetime.strptime(data[key], "%Y-%m-%d").date()
step = FREQ_DAYS.get(data[freq])
if step:
# The council's "next date" field can lag behind reality
# by weeks - roll it forward by the collection frequency
# until it's genuinely upcoming, rather than trusting it
# as always being the next one.
while date < today:
date += timedelta(days=step)
occurrences = 3 if step == 14 else 5
offsets = [step * n for n in range(occurrences)]
else:
if date < today:
continue
offsets = [0]
for key, bin_type, freq in BIN_TYPES:
if not data[key]:
continue
date = datetime.strptime(data[key], "%Y-%m-%d").date()
freq_value = data[freq]
if freq_value and freq_value not in FREQ_DAYS:
raise ValueError(
f"Unrecognized collection frequency '{freq_value}' for {bin_type}"
)
step = FREQ_DAYS.get(freq_value)
if step:
# The council's "next date" field can lag behind reality
# by weeks - roll it forward by the collection frequency
# until it's genuinely upcoming, rather than trusting it
# as always being the next one.
while date < today:
date += timedelta(days=step)
occurrences = 3 if step == 14 else 5
offsets = [step * n for n in range(occurrences)]
else:
if date < today:
continue
offsets = [0]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@uk_bin_collection/uk_bin_collection/councils/LincolnCouncil.py` around lines
90 - 107, Update the frequency handling in the BIN_TYPES loop to distinguish
missing/empty frequency values from unrecognized non-empty values. Preserve the
existing one-off behavior only when the frequency is absent, but raise an
explicit error for any non-empty value not present in FREQ_DAYS instead of
entering the else branch; retain the existing roll-forward logic for recognized
frequencies.

Source: Learnings

Found while re-investigating #2118: some collection weeks include a
row like "*New Round* - Tuesday AHP" in the same column as the actual
bin type, which was getting appended to the bins list as if it were a
collection. These notice rows are consistently prefixed with "*", so
skip them rather than treating them as a bin type. Verified live -
"Brown Bin" entries (the subject of #2118) are still correctly
extracted; the junk entries are gone.
@codecov

codecov Bot commented Jul 13, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.89%. Comparing base (a102aca) to head (95edac1).
⚠️ Report is 5 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2166   +/-   ##
=======================================
  Coverage   86.89%   86.89%           
=======================================
  Files           9        9           
  Lines        1160     1160           
=======================================
  Hits         1008     1008           
  Misses        152      152           

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

@robbrad robbrad changed the title fix: #2153 LincolnCouncil - roll forward a stale anchor date fix: #2153 Lincoln stale anchor date + Flintshire schedule-note cleanup Jul 14, 2026
@robbrad
robbrad merged commit c1c775a into master Jul 14, 2026
11 of 13 checks passed
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.

Lincoln city no longer works

1 participant