prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Changes to Campbelltown Council URLs and APIs have broken the ability to retrieve waste collection info (campbelltown_nsw_gov_au.py)
  Hi there,
  
  I'm absolutely useless with Python or I'd be coding a fix and lodging a pull request, but instead am posting this as an issue at the community's mercy :)
  
  The Campbelltown Council (Australia) API URLs have changed, and I believe that they're now presenting address search data a little differently too - XML instead of JSON.
  
  The overall pattern still appears to be the same:
  
  1. Retrieve the GeolocationID for a given address via the 'address_search' API
  2. Plug that GeolocationID into the 'collection' API to retrieve the waste collection info in HTML format, wrapped in JSON
  
  **URLs in existing version of Campbelltown NSW AU Waste Collection Source:**
  ```
  API_URLS = {
      "address_search": "https://www.campbelltown.nsw.gov.au/ocsvc/public/spatial/findaddress?address={}",
      "collection": "https://www.campbelltown.nsw.gov.au/ocsvc/Public/InMyNeighbourhood/WasteServices?GeoLocationId={}",
  }
  ```
  
  **URLs that work now:**
  ```
  API_URLS = {
      "address_search": "https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords={}",
      "collection": "https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid={}&ocsvclang=en-AU",
  }
  ```
  
  I'm not familiar with the old Campbelltown address search API, but looking at the code it appears that it used to return JSON data. It's now returning pure XML. 
  
  **Examples using the new API URLs:**
  Address search result: [https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords=10%20Brookfield%20Road%20Minto%20NSW%202566](https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords=10%20Brookfield%20Road%20Minto%20NSW%202566)
  
  Collection API result for the above GeolocationID: [https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid=401ee13b-b04c-4948-82e4-09d274c479a0&ocsvclang=en-AU](https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid=401ee13b-b04c-4948-82e4-09d274c479a0&ocsvclang=en-AU)
  
  --- 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/campbelltown_nsw_gov_au.py`
  Content:
  ```
  1 import datetime
  2 import json
  3 
  4 import requests
  5 from bs4 import BeautifulSoup
  6 from requests.utils import requote_uri
  7 from waste_collection_schedule import Collection
  8 
  9 TITLE = "Campbelltown City Council"
  10 DESCRIPTION = "Source for Campbelltown City Council rubbish collection."
  11 URL = "https://www.campbelltown.nsw.gov.au/"
  12 TEST_CASES = {
  13     "Minto Mall": {
  14         "post_code": "2566",
  15         "suburb": "Minto",
  16         "street_name": "Brookfield Road",
  17         "street_number": "10",
  18     },
  19     "Campbelltown Catholic Club": {
  20         "post_code": "2560",
  21         "suburb": "Campbelltown",
  22         "street_name": "Camden Road",
  23         "street_number": "20-22",
  24     },
  25     "Australia Post Ingleburn": {
  26         "post_code": "2565",
  27         "suburb": "INGLEBURN",
  28         "street_name": "Oxford Road",
  29         "street_number": "34",
  30     },
  31 }
  32 
  33 API_URLS = {
  34     "address_search": "https://www.campbelltown.nsw.gov.au/ocsvc/public/spatial/findaddress?address={}",
  35     "collection": "https://www.campbelltown.nsw.gov.au/ocsvc/Public/InMyNeighbourhood/WasteServices?GeoLocationId={}",
  36 }
  37 
  38 HEADERS = {"user-agent": "Mozilla/5.0"}
  39 
  40 ICON_MAP = {
  41     "General Waste": "trash-can",
  42     "Recycling": "mdi:recycle",
  43     "Green Waste": "mdi:leaf",
  44 }
  45 
  46 
  47 class Source:
  48     def __init__(
  49         self, post_code: str, suburb: str, street_name: str, street_number: str
  50     ):
  51         self.post_code = post_code
  52         self.suburb = suburb
  53         self.street_name = street_name
  54         self.street_number = street_number
  55 
  56     def fetch(self):
  57         locationId = 0
  58 
  59         address = "{} {} {} NSW {}".format(
  60             self.street_number, self.street_name, self.suburb, self.post_code
  61         )
  62 
  63         q = requote_uri(str(API_URLS["address_search"]).format(address))
  64 
  65         # Retrieve suburbs
  66         r = requests.get(q, headers=HEADERS)
  67 
  68         data = json.loads(r.text)
  69 
  70         # Find the ID for our suburb
  71         for item in data["locations"]:
  72             locationId = item["Id"]
  73             break
  74 
  75         if locationId == 0:
  76             return []
  77 
  78         # Retrieve the upcoming collections for our property
  79         q = requote_uri(str(API_URLS["collection"]).format(locationId))
  80 
  81         r = requests.get(q, headers=HEADERS)
  82 
  83         data = json.loads(r.text)
  84 
  85         responseContent = data["responseContent"]
  86 
  87         soup = BeautifulSoup(responseContent, "html.parser")
  88         services = soup.find_all("div", attrs={"class": "service-details"})
  89 
  90         entries = []
  91 
  92         for item in services:
  93             # test if <div> contains a valid date. If not, is is not a collection item.
  94             date_text = item.find("span")
  95             try:
  96                 date = datetime.datetime.strptime(date_text.text, "%A%d %b %Y").date()
  97 
  98             except ValueError:
  99                 continue
  100 
  101             waste_type = item.contents[0].strip()
  102 
  103             entries.append(
  104                 Collection(
  105                     date=date,
  106                     t=waste_type,
  107                     icon=ICON_MAP.get(waste_type, "mdi:trash-can"),
  108                 )
  109             )
  110 
  111         return entries
  112
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py b/custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py
  --- a/custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py
  +++ b/custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py
  @@ -31,8 +31,8 @@
   }
   
   API_URLS = {
  -    "address_search": "https://www.campbelltown.nsw.gov.au/ocsvc/public/spatial/findaddress?address={}",
  -    "collection": "https://www.campbelltown.nsw.gov.au/ocsvc/Public/InMyNeighbourhood/WasteServices?GeoLocationId={}",
  +    "address_search": "https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords={}",
  +    "collection": "https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid={}&ocsvclang=en-AU",
   }
   
   HEADERS = {"user-agent": "Mozilla/5.0"}
  @@ -43,7 +43,6 @@
       "Green Waste": "mdi:leaf",
   }
   
  -
   class Source:
       def __init__(
           self, post_code: str, suburb: str, street_name: str, street_number: str
  @@ -68,7 +67,7 @@
           data = json.loads(r.text)
   
           # Find the ID for our suburb
  -        for item in data["locations"]:
  +        for item in data["Items"]:
               locationId = item["Id"]
               break
   
  @@ -85,20 +84,28 @@
           responseContent = data["responseContent"]
   
           soup = BeautifulSoup(responseContent, "html.parser")
  -        services = soup.find_all("div", attrs={"class": "service-details"})
  +        services = soup.find_all("div", attrs={"class": "waste-services-result"})
   
           entries = []
   
           for item in services:
               # test if <div> contains a valid date. If not, is is not a collection item.
  -            date_text = item.find("span")
  +            date_text = item.find("div", attrs={"class": "next-service"})
  +            
  +            # The date format currently used on https://www.campbelltown.nsw.gov.au/Services-and-Facilities/Waste-and-Recycling/Check-my-collection-day
  +            date_format = '%a %d/%m/%Y'
  +
               try:
  -                date = datetime.datetime.strptime(date_text.text, "%A%d %b %Y").date()
  +                # Strip carriage returns and newlines out of the HTML content
  +                cleaned_date_text = date_text.text.replace('\r','').replace('\n','').strip()
  +
  +                # Parse the date
  +                date = datetime.datetime.strptime(cleaned_date_text, date_format).date()
   
               except ValueError:
                   continue
   
  -            waste_type = item.contents[0].strip()
  +            waste_type = item.find("h3").text.strip()
   
               entries.append(
                   Collection(
  
verification_info: {"golden_diff": "diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py b/custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py\n--- a/custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py\n+++ b/custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py\n@@ -31,8 +31,8 @@\n }\n \n API_URLS = {\n-    \"address_search\": \"https://www.campbelltown.nsw.gov.au/ocsvc/public/spatial/findaddress?address={}\",\n-    \"collection\": \"https://www.campbelltown.nsw.gov.au/ocsvc/Public/InMyNeighbourhood/WasteServices?GeoLocationId={}\",\n+    \"address_search\": \"https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords={}\",\n+    \"collection\": \"https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid={}&ocsvclang=en-AU\",\n }\n \n HEADERS = {\"user-agent\": \"Mozilla/5.0\"}\n@@ -43,7 +43,6 @@\n     \"Green Waste\": \"mdi:leaf\",\n }\n \n-\n class Source:\n     def __init__(\n         self, post_code: str, suburb: str, street_name: str, street_number: str\n@@ -68,7 +67,7 @@\n         data = json.loads(r.text)\n \n         # Find the ID for our suburb\n-        for item in data[\"locations\"]:\n+        for item in data[\"Items\"]:\n             locationId = item[\"Id\"]\n             break\n \n@@ -85,20 +84,28 @@\n         responseContent = data[\"responseContent\"]\n \n         soup = BeautifulSoup(responseContent, \"html.parser\")\n-        services = soup.find_all(\"div\", attrs={\"class\": \"service-details\"})\n+        services = soup.find_all(\"div\", attrs={\"class\": \"waste-services-result\"})\n \n         entries = []\n \n         for item in services:\n             # test if <div> contains a valid date. If not, is is not a collection item.\n-            date_text = item.find(\"span\")\n+            date_text = item.find(\"div\", attrs={\"class\": \"next-service\"})\n+            \n+            # The date format currently used on https://www.campbelltown.nsw.gov.au/Services-and-Facilities/Waste-and-Recycling/Check-my-collection-day\n+            date_format = '%a %d/%m/%Y'\n+\n             try:\n-                date = datetime.datetime.strptime(date_text.text, \"%A%d %b %Y\").date()\n+                # Strip carriage returns and newlines out of the HTML content\n+                cleaned_date_text = date_text.text.replace('\\r','').replace('\\n','').strip()\n+\n+                # Parse the date\n+                date = datetime.datetime.strptime(cleaned_date_text, date_format).date()\n \n             except ValueError:\n                 continue\n \n-            waste_type = item.contents[0].strip()\n+            waste_type = item.find(\"h3\").text.strip()\n \n             entries.append(\n                 Collection(\n", "issue": "Changes to Campbelltown Council URLs and APIs have broken the ability to retrieve waste collection info (campbelltown_nsw_gov_au.py)\nHi there,\r\n\r\nI'm absolutely useless with Python or I'd be coding a fix and lodging a pull request, but instead am posting this as an issue at the community's mercy :)\r\n\r\nThe Campbelltown Council (Australia) API URLs have changed, and I believe that they're now presenting address search data a little differently too - XML instead of JSON.\r\n\r\nThe overall pattern still appears to be the same:\r\n\r\n1. Retrieve the GeolocationID for a given address via the 'address_search' API\r\n2. Plug that GeolocationID into the 'collection' API to retrieve the waste collection info in HTML format, wrapped in JSON\r\n\r\n**URLs in existing version of Campbelltown NSW AU Waste Collection Source:**\r\n```\r\nAPI_URLS = {\r\n    \"address_search\": \"https://www.campbelltown.nsw.gov.au/ocsvc/public/spatial/findaddress?address={}\",\r\n    \"collection\": \"https://www.campbelltown.nsw.gov.au/ocsvc/Public/InMyNeighbourhood/WasteServices?GeoLocationId={}\",\r\n}\r\n```\r\n\r\n**URLs that work now:**\r\n```\r\nAPI_URLS = {\r\n    \"address_search\": \"https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords={}\",\r\n    \"collection\": \"https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid={}&ocsvclang=en-AU\",\r\n}\r\n```\r\n\r\nI'm not familiar with the old Campbelltown address search API, but looking at the code it appears that it used to return JSON data. It's now returning pure XML. \r\n\r\n**Examples using the new API URLs:**\r\nAddress search result: [https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords=10%20Brookfield%20Road%20Minto%20NSW%202566](https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords=10%20Brookfield%20Road%20Minto%20NSW%202566)\r\n\r\nCollection API result for the above GeolocationID: [https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid=401ee13b-b04c-4948-82e4-09d274c479a0&ocsvclang=en-AU](https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid=401ee13b-b04c-4948-82e4-09d274c479a0&ocsvclang=en-AU)\n", "before_files": [{"content": "import datetime\nimport json\n\nimport requests\nfrom bs4 import BeautifulSoup\nfrom requests.utils import requote_uri\nfrom waste_collection_schedule import Collection\n\nTITLE = \"Campbelltown City Council\"\nDESCRIPTION = \"Source for Campbelltown City Council rubbish collection.\"\nURL = \"https://www.campbelltown.nsw.gov.au/\"\nTEST_CASES = {\n    \"Minto Mall\": {\n        \"post_code\": \"2566\",\n        \"suburb\": \"Minto\",\n        \"street_name\": \"Brookfield Road\",\n        \"street_number\": \"10\",\n    },\n    \"Campbelltown Catholic Club\": {\n        \"post_code\": \"2560\",\n        \"suburb\": \"Campbelltown\",\n        \"street_name\": \"Camden Road\",\n        \"street_number\": \"20-22\",\n    },\n    \"Australia Post Ingleburn\": {\n        \"post_code\": \"2565\",\n        \"suburb\": \"INGLEBURN\",\n        \"street_name\": \"Oxford Road\",\n        \"street_number\": \"34\",\n    },\n}\n\nAPI_URLS = {\n    \"address_search\": \"https://www.campbelltown.nsw.gov.au/ocsvc/public/spatial/findaddress?address={}\",\n    \"collection\": \"https://www.campbelltown.nsw.gov.au/ocsvc/Public/InMyNeighbourhood/WasteServices?GeoLocationId={}\",\n}\n\nHEADERS = {\"user-agent\": \"Mozilla/5.0\"}\n\nICON_MAP = {\n    \"General Waste\": \"trash-can\",\n    \"Recycling\": \"mdi:recycle\",\n    \"Green Waste\": \"mdi:leaf\",\n}\n\n\nclass Source:\n    def __init__(\n        self, post_code: str, suburb: str, street_name: str, street_number: str\n    ):\n        self.post_code = post_code\n        self.suburb = suburb\n        self.street_name = street_name\n        self.street_number = street_number\n\n    def fetch(self):\n        locationId = 0\n\n        address = \"{} {} {} NSW {}\".format(\n            self.street_number, self.street_name, self.suburb, self.post_code\n        )\n\n        q = requote_uri(str(API_URLS[\"address_search\"]).format(address))\n\n        # Retrieve suburbs\n        r = requests.get(q, headers=HEADERS)\n\n        data = json.loads(r.text)\n\n        # Find the ID for our suburb\n        for item in data[\"locations\"]:\n            locationId = item[\"Id\"]\n            break\n\n        if locationId == 0:\n            return []\n\n        # Retrieve the upcoming collections for our property\n        q = requote_uri(str(API_URLS[\"collection\"]).format(locationId))\n\n        r = requests.get(q, headers=HEADERS)\n\n        data = json.loads(r.text)\n\n        responseContent = data[\"responseContent\"]\n\n        soup = BeautifulSoup(responseContent, \"html.parser\")\n        services = soup.find_all(\"div\", attrs={\"class\": \"service-details\"})\n\n        entries = []\n\n        for item in services:\n            # test if <div> contains a valid date. If not, is is not a collection item.\n            date_text = item.find(\"span\")\n            try:\n                date = datetime.datetime.strptime(date_text.text, \"%A%d %b %Y\").date()\n\n            except ValueError:\n                continue\n\n            waste_type = item.contents[0].strip()\n\n            entries.append(\n                Collection(\n                    date=date,\n                    t=waste_type,\n                    icon=ICON_MAP.get(waste_type, \"mdi:trash-can\"),\n                )\n            )\n\n        return entries\n", "path": "custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py"}], "after_files": [{"content": "import datetime\nimport json\n\nimport requests\nfrom bs4 import BeautifulSoup\nfrom requests.utils import requote_uri\nfrom waste_collection_schedule import Collection\n\nTITLE = \"Campbelltown City Council\"\nDESCRIPTION = \"Source for Campbelltown City Council rubbish collection.\"\nURL = \"https://www.campbelltown.nsw.gov.au/\"\nTEST_CASES = {\n    \"Minto Mall\": {\n        \"post_code\": \"2566\",\n        \"suburb\": \"Minto\",\n        \"street_name\": \"Brookfield Road\",\n        \"street_number\": \"10\",\n    },\n    \"Campbelltown Catholic Club\": {\n        \"post_code\": \"2560\",\n        \"suburb\": \"Campbelltown\",\n        \"street_name\": \"Camden Road\",\n        \"street_number\": \"20-22\",\n    },\n    \"Australia Post Ingleburn\": {\n        \"post_code\": \"2565\",\n        \"suburb\": \"INGLEBURN\",\n        \"street_name\": \"Oxford Road\",\n        \"street_number\": \"34\",\n    },\n}\n\nAPI_URLS = {\n    \"address_search\": \"https://www.campbelltown.nsw.gov.au/api/v1/myarea/search?keywords={}\",\n    \"collection\": \"https://www.campbelltown.nsw.gov.au/ocapi/Public/myarea/wasteservices?geolocationid={}&ocsvclang=en-AU\",\n}\n\nHEADERS = {\"user-agent\": \"Mozilla/5.0\"}\n\nICON_MAP = {\n    \"General Waste\": \"trash-can\",\n    \"Recycling\": \"mdi:recycle\",\n    \"Green Waste\": \"mdi:leaf\",\n}\n\nclass Source:\n    def __init__(\n        self, post_code: str, suburb: str, street_name: str, street_number: str\n    ):\n        self.post_code = post_code\n        self.suburb = suburb\n        self.street_name = street_name\n        self.street_number = street_number\n\n    def fetch(self):\n        locationId = 0\n\n        address = \"{} {} {} NSW {}\".format(\n            self.street_number, self.street_name, self.suburb, self.post_code\n        )\n\n        q = requote_uri(str(API_URLS[\"address_search\"]).format(address))\n\n        # Retrieve suburbs\n        r = requests.get(q, headers=HEADERS)\n\n        data = json.loads(r.text)\n\n        # Find the ID for our suburb\n        for item in data[\"Items\"]:\n            locationId = item[\"Id\"]\n            break\n\n        if locationId == 0:\n            return []\n\n        # Retrieve the upcoming collections for our property\n        q = requote_uri(str(API_URLS[\"collection\"]).format(locationId))\n\n        r = requests.get(q, headers=HEADERS)\n\n        data = json.loads(r.text)\n\n        responseContent = data[\"responseContent\"]\n\n        soup = BeautifulSoup(responseContent, \"html.parser\")\n        services = soup.find_all(\"div\", attrs={\"class\": \"waste-services-result\"})\n\n        entries = []\n\n        for item in services:\n            # test if <div> contains a valid date. If not, is is not a collection item.\n            date_text = item.find(\"div\", attrs={\"class\": \"next-service\"})\n            \n            # The date format currently used on https://www.campbelltown.nsw.gov.au/Services-and-Facilities/Waste-and-Recycling/Check-my-collection-day\n            date_format = '%a %d/%m/%Y'\n\n            try:\n                # Strip carriage returns and newlines out of the HTML content\n                cleaned_date_text = date_text.text.replace('\\r','').replace('\\n','').strip()\n\n                # Parse the date\n                date = datetime.datetime.strptime(cleaned_date_text, date_format).date()\n\n            except ValueError:\n                continue\n\n            waste_type = item.find(\"h3\").text.strip()\n\n            entries.append(\n                Collection(\n                    date=date,\n                    t=waste_type,\n                    icon=ICON_MAP.get(waste_type, \"mdi:trash-can\"),\n                )\n            )\n\n        return entries\n", "path": "custom_components/waste_collection_schedule/waste_collection_schedule/source/campbelltown_nsw_gov_au.py"}]}