We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Bug]: Dudley Council, UK - Not Loading
### I Have A Problem With:

A specific source

### What's Your Problem

Now the Xmas alternative dates have been removed from the council website it's no longer loading my bin dates into my calendar and showing the below error messages in the HA log file

### Source (if relevant)

dudley_gov_uk

### Logs

```Shell
2024-01-10 01:19:15.591 ERROR (SyncWorker_4) [waste_collection_schedule.source_shell] fetch failed for source Dudley Metropolitan Borough Council:
Traceback (most recent call last):
  File "/config/custom_components/waste_collection_schedule/waste_collection_schedule/source_shell.py", line 134, in fetch
    entries = self._source.fetch()
              ^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py", line 107, in fetch
    xmas_map = self.get_xmas_map(footer_panel)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py", line 71, in get_xmas_map
    footer_panel.find("table").find("tr"),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'find'

```


### Relevant Configuration

_No response_

### Checklist Source Error

- [x] Use the example parameters for your source (often available in the documentation) (don't forget to restart Home Assistant after changing the configuration)
- [X] Checked that the website of your service provider is still working
- [x] Tested my attributes on the service provider website (if possible)
- [X] I have tested with the latest version of the integration (master) (for HACS in the 3 dot menu of the integration click on "Redownload" and choose master as version)

### Checklist Sensor Error

- [X] Checked in the Home Assistant Calendar tab if the event names match the types names (if types argument is used)

### Required

- [X] I have searched past (closed AND opened) issues to see if this bug has already been reported, and it hasn't been.
- [X] I understand that people give their precious time for free, and thus I've done my very best to make this problem as easy as possible to investigate.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py`
Content:
```
1 import re
2 from datetime import date, datetime, timedelta
3 
4 import requests
5 from bs4 import BeautifulSoup
6 from waste_collection_schedule import Collection  # type: ignore[attr-defined]
7 
8 TITLE = "Dudley Metropolitan Borough Council"
9 DESCRIPTION = "Source for Dudley Metropolitan Borough Council, UK."
10 URL = "https://dudley.gov.uk"
11 TEST_CASES = {
12     "Test_001": {"uprn": "90090715"},
13     "Test_002": {"uprn": 90104555},
14     "Test_003": {"uprn": "90164803"},
15     "Test_004": {"uprn": 90092621},
16 }
17 ICON_MAP = {"RECYCLING": "mdi:recycle", "GARDEN": "mdi:leaf", "REFUSE": "mdi:trash-can"}
18 REGEX = {
19     "DATES": r"(\d+ \w{3})",
20     "DAYS": r"every: (Monday|Tuesday|Wednesday|Thursday|Friday)",
21 }
22 DAYS = {
23     "Monday": 0,
24     "Tuesday": 1,
25     "Wednesday": 2,
26     "Thursday": 3,
27     "Friday": 4,
28     "Saturday": 5,
29     "Sunday": 6,
30 }
31 
32 
33 class Source:
34     def __init__(self, uprn: str | int):
35         self._uprn = str(uprn)
36 
37     def check_date(self, d: str, t: datetime, y: int):
38         """
39         Get date, append year, and increment year if date is >1 month in the past.
40 
41         This tries to deal year-end dates when the YEAR is missing
42         """
43         d += " " + str(y)
44         try:
45             date = datetime.strptime(d, "%d %b %Y")
46         except ValueError:
47             date = datetime.strptime(d, "%A %d %b %Y")
48         if (date - t) < timedelta(days=-31):
49             date = date.replace(year=date.year + 1)
50         return date.date()
51 
52     def append_entries(self, d: datetime, w: str, e: list) -> list:
53         e.append(
54             Collection(
55                 date=d,
56                 t=w,
57                 icon=ICON_MAP.get(w.upper()),
58             )
59         )
60         return e
61 
62     def get_xmas_map(self, footer_panel) -> dict[date, date]:
63         if not (
64             footer_panel
65             and footer_panel.find("table")
66             and footer_panel.find("table").find("tr")
67         ):
68             print(
69                 footer_panel,
70                 footer_panel.find("table"),
71                 footer_panel.find("table").find("tr"),
72             )
73             return {}
74         xmas_map: dict = {}
75         today = datetime.now()
76         yr = int(today.year)
77         for tr in footer_panel.find("table").findAll("tr")[1:]:
78             try:
79                 moved, moved_to = tr.findAll("td")
80                 moved = self.check_date(moved.text, today, yr)
81                 moved_to = self.check_date(moved_to.text, today, yr)
82                 xmas_map[moved] = moved_to
83             except Exception as e:
84                 print(e)
85                 continue
86         return xmas_map
87 
88     def fetch(self):
89         today = datetime.now()
90         today = today.replace(hour=0, minute=0, second=0, microsecond=0)
91         yr = int(today.year)
92 
93         s = requests.Session()
94         r = s.get(
95             f"https://maps.dudley.gov.uk/?action=SetAddress&UniqueId={self._uprn}"
96         )
97         soup = BeautifulSoup(r.text, "html.parser")
98 
99         panel = soup.find("div", {"aria-label": "Refuse and Recycling Collection"})
100         panel_data = panel.find("div", {"class": "atPanelData"})
101         waste_data = panel_data.text.split("Next")[
102             1:
103         ]  # remove first element it just contains general info
104 
105         # get table of holiday moved dates (only around xmas)
106         footer_panel = panel.find("div", {"class": "atPanelFooter"})
107         xmas_map = self.get_xmas_map(footer_panel)
108 
109         entries = []
110         # Deal with Recycling and Garden collections
111         for item in waste_data:
112             text = item.replace("\r\n", "").strip()
113             if "recycling" in text:
114                 dates = re.findall(REGEX["DATES"], text)
115                 for dt in dates:
116                     dt = self.check_date(dt, today, yr)
117                     dt = xmas_map.get(dt, dt)
118                     self.append_entries(dt, "Recycling", entries)
119             elif "garden" in text:
120                 dates = re.findall(REGEX["DATES"], text)
121                 for dt in dates:
122                     dt = self.check_date(dt, today, yr)
123                     dt = xmas_map.get(dt, dt)
124                     self.append_entries(dt, "Garden", entries)
125 
126         # Refuse collections only have a DAY not a date, so work out dates for the next few collections
127         refuse_day = re.findall(REGEX["DAYS"], panel_data.text)[0]
128         refuse_date = today + timedelta((int(DAYS[refuse_day]) - today.weekday()) % 7)
129         for i in range(0, 4):
130             temp_date = (refuse_date + timedelta(days=7 * i)).date()
131             temp_date = xmas_map.get(temp_date, temp_date)
132             self.append_entries(temp_date, "Refuse", entries)
133 
134         return entries
135
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py b/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py
--- a/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py
+++ b/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py
@@ -65,11 +65,6 @@
             and footer_panel.find("table")
             and footer_panel.find("table").find("tr")
         ):
-            print(
-                footer_panel,
-                footer_panel.find("table"),
-                footer_panel.find("table").find("tr"),
-            )
             return {}
         xmas_map: dict = {}
         today = datetime.now()

{"golden_diff": "diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py b/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py\n--- a/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py\n+++ b/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py\n@@ -65,11 +65,6 @@\n             and footer_panel.find(\"table\")\n             and footer_panel.find(\"table\").find(\"tr\")\n         ):\n-            print(\n-                footer_panel,\n-                footer_panel.find(\"table\"),\n-                footer_panel.find(\"table\").find(\"tr\"),\n-            )\n             return {}\n         xmas_map: dict = {}\n         today = datetime.now()\n", "issue": "[Bug]: Dudley Council, UK - Not Loading\n### I Have A Problem With:\r\n\r\nA specific source\r\n\r\n### What's Your Problem\r\n\r\nNow the Xmas alternative dates have been removed from the council website it's no longer loading my bin dates into my calendar and showing the below error messages in the HA log file\r\n\r\n### Source (if relevant)\r\n\r\ndudley_gov_uk\r\n\r\n### Logs\r\n\r\n```Shell\r\n2024-01-10 01:19:15.591 ERROR (SyncWorker_4) [waste_collection_schedule.source_shell] fetch failed for source Dudley Metropolitan Borough Council:\r\nTraceback (most recent call last):\r\n  File \"/config/custom_components/waste_collection_schedule/waste_collection_schedule/source_shell.py\", line 134, in fetch\r\n    entries = self._source.fetch()\r\n              ^^^^^^^^^^^^^^^^^^^^\r\n  File \"/config/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py\", line 107, in fetch\r\n    xmas_map = self.get_xmas_map(footer_panel)\r\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File \"/config/custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py\", line 71, in get_xmas_map\r\n    footer_panel.find(\"table\").find(\"tr\"),\r\n    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\nAttributeError: 'NoneType' object has no attribute 'find'\r\n\r\n```\r\n\r\n\r\n### Relevant Configuration\r\n\r\n_No response_\r\n\r\n### Checklist Source Error\r\n\r\n- [x] Use the example parameters for your source (often available in the documentation) (don't forget to restart Home Assistant after changing the configuration)\r\n- [X] Checked that the website of your service provider is still working\r\n- [x] Tested my attributes on the service provider website (if possible)\r\n- [X] I have tested with the latest version of the integration (master) (for HACS in the 3 dot menu of the integration click on \"Redownload\" and choose master as version)\r\n\r\n### Checklist Sensor Error\r\n\r\n- [X] Checked in the Home Assistant Calendar tab if the event names match the types names (if types argument is used)\r\n\r\n### Required\r\n\r\n- [X] I have searched past (closed AND opened) issues to see if this bug has already been reported, and it hasn't been.\r\n- [X] I understand that people give their precious time for free, and thus I've done my very best to make this problem as easy as possible to investigate.\n", "before_files": [{"content": "import re\nfrom datetime import date, datetime, timedelta\n\nimport requests\nfrom bs4 import BeautifulSoup\nfrom waste_collection_schedule import Collection  # type: ignore[attr-defined]\n\nTITLE = \"Dudley Metropolitan Borough Council\"\nDESCRIPTION = \"Source for Dudley Metropolitan Borough Council, UK.\"\nURL = \"https://dudley.gov.uk\"\nTEST_CASES = {\n    \"Test_001\": {\"uprn\": \"90090715\"},\n    \"Test_002\": {\"uprn\": 90104555},\n    \"Test_003\": {\"uprn\": \"90164803\"},\n    \"Test_004\": {\"uprn\": 90092621},\n}\nICON_MAP = {\"RECYCLING\": \"mdi:recycle\", \"GARDEN\": \"mdi:leaf\", \"REFUSE\": \"mdi:trash-can\"}\nREGEX = {\n    \"DATES\": r\"(\\d+ \\w{3})\",\n    \"DAYS\": r\"every: (Monday|Tuesday|Wednesday|Thursday|Friday)\",\n}\nDAYS = {\n    \"Monday\": 0,\n    \"Tuesday\": 1,\n    \"Wednesday\": 2,\n    \"Thursday\": 3,\n    \"Friday\": 4,\n    \"Saturday\": 5,\n    \"Sunday\": 6,\n}\n\n\nclass Source:\n    def __init__(self, uprn: str | int):\n        self._uprn = str(uprn)\n\n    def check_date(self, d: str, t: datetime, y: int):\n        \"\"\"\n        Get date, append year, and increment year if date is >1 month in the past.\n\n        This tries to deal year-end dates when the YEAR is missing\n        \"\"\"\n        d += \" \" + str(y)\n        try:\n            date = datetime.strptime(d, \"%d %b %Y\")\n        except ValueError:\n            date = datetime.strptime(d, \"%A %d %b %Y\")\n        if (date - t) < timedelta(days=-31):\n            date = date.replace(year=date.year + 1)\n        return date.date()\n\n    def append_entries(self, d: datetime, w: str, e: list) -> list:\n        e.append(\n            Collection(\n                date=d,\n                t=w,\n                icon=ICON_MAP.get(w.upper()),\n            )\n        )\n        return e\n\n    def get_xmas_map(self, footer_panel) -> dict[date, date]:\n        if not (\n            footer_panel\n            and footer_panel.find(\"table\")\n            and footer_panel.find(\"table\").find(\"tr\")\n        ):\n            print(\n                footer_panel,\n                footer_panel.find(\"table\"),\n                footer_panel.find(\"table\").find(\"tr\"),\n            )\n            return {}\n        xmas_map: dict = {}\n        today = datetime.now()\n        yr = int(today.year)\n        for tr in footer_panel.find(\"table\").findAll(\"tr\")[1:]:\n            try:\n                moved, moved_to = tr.findAll(\"td\")\n                moved = self.check_date(moved.text, today, yr)\n                moved_to = self.check_date(moved_to.text, today, yr)\n                xmas_map[moved] = moved_to\n            except Exception as e:\n                print(e)\n                continue\n        return xmas_map\n\n    def fetch(self):\n        today = datetime.now()\n        today = today.replace(hour=0, minute=0, second=0, microsecond=0)\n        yr = int(today.year)\n\n        s = requests.Session()\n        r = s.get(\n            f\"https://maps.dudley.gov.uk/?action=SetAddress&UniqueId={self._uprn}\"\n        )\n        soup = BeautifulSoup(r.text, \"html.parser\")\n\n        panel = soup.find(\"div\", {\"aria-label\": \"Refuse and Recycling Collection\"})\n        panel_data = panel.find(\"div\", {\"class\": \"atPanelData\"})\n        waste_data = panel_data.text.split(\"Next\")[\n            1:\n        ]  # remove first element it just contains general info\n\n        # get table of holiday moved dates (only around xmas)\n        footer_panel = panel.find(\"div\", {\"class\": \"atPanelFooter\"})\n        xmas_map = self.get_xmas_map(footer_panel)\n\n        entries = []\n        # Deal with Recycling and Garden collections\n        for item in waste_data:\n            text = item.replace(\"\\r\\n\", \"\").strip()\n            if \"recycling\" in text:\n                dates = re.findall(REGEX[\"DATES\"], text)\n                for dt in dates:\n                    dt = self.check_date(dt, today, yr)\n                    dt = xmas_map.get(dt, dt)\n                    self.append_entries(dt, \"Recycling\", entries)\n            elif \"garden\" in text:\n                dates = re.findall(REGEX[\"DATES\"], text)\n                for dt in dates:\n                    dt = self.check_date(dt, today, yr)\n                    dt = xmas_map.get(dt, dt)\n                    self.append_entries(dt, \"Garden\", entries)\n\n        # Refuse collections only have a DAY not a date, so work out dates for the next few collections\n        refuse_day = re.findall(REGEX[\"DAYS\"], panel_data.text)[0]\n        refuse_date = today + timedelta((int(DAYS[refuse_day]) - today.weekday()) % 7)\n        for i in range(0, 4):\n            temp_date = (refuse_date + timedelta(days=7 * i)).date()\n            temp_date = xmas_map.get(temp_date, temp_date)\n            self.append_entries(temp_date, \"Refuse\", entries)\n\n        return entries\n", "path": "custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py"}], "after_files": [{"content": "import re\nfrom datetime import date, datetime, timedelta\n\nimport requests\nfrom bs4 import BeautifulSoup\nfrom waste_collection_schedule import Collection  # type: ignore[attr-defined]\n\nTITLE = \"Dudley Metropolitan Borough Council\"\nDESCRIPTION = \"Source for Dudley Metropolitan Borough Council, UK.\"\nURL = \"https://dudley.gov.uk\"\nTEST_CASES = {\n    \"Test_001\": {\"uprn\": \"90090715\"},\n    \"Test_002\": {\"uprn\": 90104555},\n    \"Test_003\": {\"uprn\": \"90164803\"},\n    \"Test_004\": {\"uprn\": 90092621},\n}\nICON_MAP = {\"RECYCLING\": \"mdi:recycle\", \"GARDEN\": \"mdi:leaf\", \"REFUSE\": \"mdi:trash-can\"}\nREGEX = {\n    \"DATES\": r\"(\\d+ \\w{3})\",\n    \"DAYS\": r\"every: (Monday|Tuesday|Wednesday|Thursday|Friday)\",\n}\nDAYS = {\n    \"Monday\": 0,\n    \"Tuesday\": 1,\n    \"Wednesday\": 2,\n    \"Thursday\": 3,\n    \"Friday\": 4,\n    \"Saturday\": 5,\n    \"Sunday\": 6,\n}\n\n\nclass Source:\n    def __init__(self, uprn: str | int):\n        self._uprn = str(uprn)\n\n    def check_date(self, d: str, t: datetime, y: int):\n        \"\"\"\n        Get date, append year, and increment year if date is >1 month in the past.\n\n        This tries to deal year-end dates when the YEAR is missing\n        \"\"\"\n        d += \" \" + str(y)\n        try:\n            date = datetime.strptime(d, \"%d %b %Y\")\n        except ValueError:\n            date = datetime.strptime(d, \"%A %d %b %Y\")\n        if (date - t) < timedelta(days=-31):\n            date = date.replace(year=date.year + 1)\n        return date.date()\n\n    def append_entries(self, d: datetime, w: str, e: list) -> list:\n        e.append(\n            Collection(\n                date=d,\n                t=w,\n                icon=ICON_MAP.get(w.upper()),\n            )\n        )\n        return e\n\n    def get_xmas_map(self, footer_panel) -> dict[date, date]:\n        if not (\n            footer_panel\n            and footer_panel.find(\"table\")\n            and footer_panel.find(\"table\").find(\"tr\")\n        ):\n            return {}\n        xmas_map: dict = {}\n        today = datetime.now()\n        yr = int(today.year)\n        for tr in footer_panel.find(\"table\").findAll(\"tr\")[1:]:\n            try:\n                moved, moved_to = tr.findAll(\"td\")\n                moved = self.check_date(moved.text, today, yr)\n                moved_to = self.check_date(moved_to.text, today, yr)\n                xmas_map[moved] = moved_to\n            except Exception as e:\n                print(e)\n                continue\n        return xmas_map\n\n    def fetch(self):\n        today = datetime.now()\n        today = today.replace(hour=0, minute=0, second=0, microsecond=0)\n        yr = int(today.year)\n\n        s = requests.Session()\n        r = s.get(\n            f\"https://maps.dudley.gov.uk/?action=SetAddress&UniqueId={self._uprn}\"\n        )\n        soup = BeautifulSoup(r.text, \"html.parser\")\n\n        panel = soup.find(\"div\", {\"aria-label\": \"Refuse and Recycling Collection\"})\n        panel_data = panel.find(\"div\", {\"class\": \"atPanelData\"})\n        waste_data = panel_data.text.split(\"Next\")[\n            1:\n        ]  # remove first element it just contains general info\n\n        # get table of holiday moved dates (only around xmas)\n        footer_panel = panel.find(\"div\", {\"class\": \"atPanelFooter\"})\n        xmas_map = self.get_xmas_map(footer_panel)\n\n        entries = []\n        # Deal with Recycling and Garden collections\n        for item in waste_data:\n            text = item.replace(\"\\r\\n\", \"\").strip()\n            if \"recycling\" in text:\n                dates = re.findall(REGEX[\"DATES\"], text)\n                for dt in dates:\n                    dt = self.check_date(dt, today, yr)\n                    dt = xmas_map.get(dt, dt)\n                    self.append_entries(dt, \"Recycling\", entries)\n            elif \"garden\" in text:\n                dates = re.findall(REGEX[\"DATES\"], text)\n                for dt in dates:\n                    dt = self.check_date(dt, today, yr)\n                    dt = xmas_map.get(dt, dt)\n                    self.append_entries(dt, \"Garden\", entries)\n\n        # Refuse collections only have a DAY not a date, so work out dates for the next few collections\n        refuse_day = re.findall(REGEX[\"DAYS\"], panel_data.text)[0]\n        refuse_date = today + timedelta((int(DAYS[refuse_day]) - today.weekday()) % 7)\n        for i in range(0, 4):\n            temp_date = (refuse_date + timedelta(days=7 * i)).date()\n            temp_date = xmas_map.get(temp_date, temp_date)\n            self.append_entries(temp_date, \"Refuse\", entries)\n\n        return entries\n", "path": "custom_components/waste_collection_schedule/waste_collection_schedule/source/dudley_gov_uk.py"}]}