uk_bin_collection 0.151.0__py3-none-any.whl → 0.152.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. uk_bin_collection/tests/input.json +137 -66
  2. uk_bin_collection/uk_bin_collection/councils/AdurAndWorthingCouncils.py +69 -24
  3. uk_bin_collection/uk_bin_collection/councils/AngusCouncil.py +149 -0
  4. uk_bin_collection/uk_bin_collection/councils/BarkingDagenham.py +11 -2
  5. uk_bin_collection/uk_bin_collection/councils/BexleyCouncil.py +24 -47
  6. uk_bin_collection/uk_bin_collection/councils/BrightonandHoveCityCouncil.py +11 -2
  7. uk_bin_collection/uk_bin_collection/councils/BroadlandDistrictCouncil.py +21 -6
  8. uk_bin_collection/uk_bin_collection/councils/CharnwoodBoroughCouncil.py +14 -3
  9. uk_bin_collection/uk_bin_collection/councils/CheltenhamBoroughCouncil.py +12 -12
  10. uk_bin_collection/uk_bin_collection/councils/CheshireEastCouncil.py +24 -2
  11. uk_bin_collection/uk_bin_collection/councils/ChichesterDistrictCouncil.py +105 -53
  12. uk_bin_collection/uk_bin_collection/councils/ChorleyCouncil.py +4 -0
  13. uk_bin_collection/uk_bin_collection/councils/CroydonCouncil.py +114 -261
  14. uk_bin_collection/uk_bin_collection/councils/DartfordBoroughCouncil.py +13 -0
  15. uk_bin_collection/uk_bin_collection/councils/DoverDistrictCouncil.py +17 -2
  16. uk_bin_collection/uk_bin_collection/councils/EastDevonDC.py +14 -1
  17. uk_bin_collection/uk_bin_collection/councils/EastbourneBoroughCouncil.py +76 -0
  18. uk_bin_collection/uk_bin_collection/councils/EastleighBoroughCouncil.py +59 -45
  19. uk_bin_collection/uk_bin_collection/councils/EnvironmentFirst.py +2 -0
  20. uk_bin_collection/uk_bin_collection/councils/EppingForestDistrictCouncil.py +47 -15
  21. uk_bin_collection/uk_bin_collection/councils/FermanaghOmaghDistrictCouncil.py +102 -0
  22. uk_bin_collection/uk_bin_collection/councils/GlasgowCityCouncil.py +13 -1
  23. uk_bin_collection/uk_bin_collection/councils/GuildfordCouncil.py +2 -3
  24. uk_bin_collection/uk_bin_collection/councils/HerefordshireCouncil.py +13 -2
  25. uk_bin_collection/uk_bin_collection/councils/HuntingdonDistrictCouncil.py +18 -4
  26. uk_bin_collection/uk_bin_collection/councils/LewesDistrictCouncil.py +76 -0
  27. uk_bin_collection/uk_bin_collection/councils/LiverpoolCityCouncil.py +16 -4
  28. uk_bin_collection/uk_bin_collection/councils/MaidstoneBoroughCouncil.py +42 -47
  29. uk_bin_collection/uk_bin_collection/councils/NewhamCouncil.py +13 -6
  30. uk_bin_collection/uk_bin_collection/councils/NorthEastDerbyshireDistrictCouncil.py +61 -39
  31. uk_bin_collection/uk_bin_collection/councils/NorthSomersetCouncil.py +14 -9
  32. uk_bin_collection/uk_bin_collection/councils/NorthTynesideCouncil.py +2 -2
  33. uk_bin_collection/uk_bin_collection/councils/NorthumberlandCouncil.py +50 -14
  34. uk_bin_collection/uk_bin_collection/councils/SloughBoroughCouncil.py +140 -0
  35. uk_bin_collection/uk_bin_collection/councils/SouthRibbleCouncil.py +115 -65
  36. uk_bin_collection/uk_bin_collection/councils/StokeOnTrentCityCouncil.py +10 -5
  37. uk_bin_collection/uk_bin_collection/councils/TewkesburyBoroughCouncil.py +40 -0
  38. uk_bin_collection/uk_bin_collection/councils/TorbayCouncil.py +1 -3
  39. uk_bin_collection/uk_bin_collection/councils/WakefieldCityCouncil.py +3 -0
  40. {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/METADATA +1 -1
  41. {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/RECORD +44 -38
  42. {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/LICENSE +0 -0
  43. {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/WHEEL +0 -0
  44. {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/entry_points.txt +0 -0
@@ -1,12 +1,27 @@
1
- from bs4 import BeautifulSoup
2
- from datetime import datetime
3
1
  import re
2
+ from datetime import datetime
3
+
4
+ from bs4 import BeautifulSoup
5
+
4
6
  from uk_bin_collection.uk_bin_collection.common import * # Consider specific imports
5
7
  from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
6
8
 
7
9
 
8
10
  class CouncilClass(AbstractGetBinDataClass):
9
11
  def parse_data(self, page: str, **kwargs) -> dict:
12
+
13
+ try:
14
+ user_uprn = kwargs.get("uprn")
15
+ check_uprn(user_uprn)
16
+ url = f"https://collections.dover.gov.uk/property/{user_uprn}"
17
+ if not user_uprn:
18
+ # This is a fallback for if the user stored a URL in old system. Ensures backwards compatibility.
19
+ url = kwargs.get("url")
20
+ except Exception as e:
21
+ raise ValueError(f"Error getting identifier: {str(e)}")
22
+
23
+ # Make a BS4 object
24
+ page = requests.get(url)
10
25
  soup = BeautifulSoup(page.text, "html.parser")
11
26
 
12
27
  bins_data = {"bins": []}
@@ -3,7 +3,8 @@ from datetime import datetime
3
3
 
4
4
  import pandas as pd
5
5
  from bs4 import BeautifulSoup
6
- from uk_bin_collection.uk_bin_collection.common import date_format
6
+
7
+ from uk_bin_collection.uk_bin_collection.common import *
7
8
  from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
8
9
 
9
10
 
@@ -15,7 +16,19 @@ class CouncilClass(AbstractGetBinDataClass):
15
16
  """
16
17
 
17
18
  def parse_data(self, page: str, **kwargs) -> dict:
19
+
20
+ try:
21
+ user_uprn = kwargs.get("uprn")
22
+ check_uprn(user_uprn)
23
+ url = f"https://eastdevon.gov.uk/recycling-and-waste/recycling-waste-information/when-is-my-bin-collected/future-collections-calendar/?UPRN={user_uprn}"
24
+ if not user_uprn:
25
+ # This is a fallback for if the user stored a URL in old system. Ensures backwards compatibility.
26
+ url = kwargs.get("url")
27
+ except Exception as e:
28
+ raise ValueError(f"Error getting identifier: {str(e)}")
29
+
18
30
  # Make a BS4 object
31
+ page = requests.get(url)
19
32
  soup = BeautifulSoup(page.text, features="html.parser")
20
33
  soup.prettify()
21
34
 
@@ -0,0 +1,76 @@
1
+ # Lewes Borough Council uses the same script.
2
+
3
+ from bs4 import BeautifulSoup
4
+
5
+ from uk_bin_collection.uk_bin_collection.common import *
6
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
7
+
8
+
9
+ # import the wonderful Beautiful Soup and the URL grabber
10
+ class CouncilClass(AbstractGetBinDataClass):
11
+ """
12
+ Concrete classes have to implement all abstract operations of the
13
+ base class. They can also override some operations with a default
14
+ implementation.
15
+ """
16
+
17
+ def parse_data(self, page: str, **kwargs) -> dict:
18
+
19
+ try:
20
+ user_uprn = kwargs.get("uprn")
21
+ check_uprn(user_uprn)
22
+ url = f"https://environmentfirst.co.uk/house.php?uprn={user_uprn}"
23
+ if not user_uprn:
24
+ # This is a fallback for if the user stored a URL in old system. Ensures backwards compatibility.
25
+ url = kwargs.get("url")
26
+ except Exception as e:
27
+ raise ValueError(f"Error getting identifier: {str(e)}")
28
+
29
+ # Make a BS4 object
30
+ page = requests.get(url)
31
+ soup = BeautifulSoup(page.text, features="html.parser")
32
+ soup.prettify()
33
+
34
+ # Get the paragraph lines from the page
35
+ data = {"bins": []}
36
+ page_text = soup.find("div", {"class": "collect"}).find_all("p")
37
+
38
+ # Parse the correct lines (find them, remove the ordinal indicator and make them the correct format date) and
39
+ # then add them to the dictionary
40
+ rubbish_day = datetime.strptime(
41
+ remove_ordinal_indicator_from_date_string(
42
+ page_text[2].find_next("strong").text
43
+ ),
44
+ "%d %B %Y",
45
+ ).strftime(date_format)
46
+ dict_data = {
47
+ "type": "Rubbish",
48
+ "collectionDate": rubbish_day,
49
+ }
50
+ data["bins"].append(dict_data)
51
+ recycling_day = datetime.strptime(
52
+ remove_ordinal_indicator_from_date_string(
53
+ page_text[4].find_next("strong").text
54
+ ),
55
+ "%d %B %Y",
56
+ ).strftime(date_format)
57
+ dict_data = {
58
+ "type": "Recycling",
59
+ "collectionDate": recycling_day,
60
+ }
61
+ data["bins"].append(dict_data)
62
+
63
+ if len(page_text) > 5:
64
+ garden_day = datetime.strptime(
65
+ remove_ordinal_indicator_from_date_string(
66
+ page_text[6].find_next("strong").text
67
+ ),
68
+ "%d %B %Y",
69
+ ).strftime(date_format)
70
+ dict_data = {
71
+ "type": "Garden",
72
+ "collectionDate": garden_day,
73
+ }
74
+ data["bins"].append(dict_data)
75
+
76
+ return data
@@ -1,4 +1,8 @@
1
1
  from bs4 import BeautifulSoup
2
+ from selenium.webdriver.common.by import By
3
+ from selenium.webdriver.support import expected_conditions as EC
4
+ from selenium.webdriver.support.ui import Select, WebDriverWait
5
+
2
6
  from uk_bin_collection.uk_bin_collection.common import *
3
7
  from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
4
8
 
@@ -12,59 +16,69 @@ class CouncilClass(AbstractGetBinDataClass):
12
16
  """
13
17
 
14
18
  def parse_data(self, page: str, **kwargs) -> dict:
15
- uprn = kwargs.get("uprn")
16
- # Check the UPRN is valid
17
- check_uprn(uprn)
19
+ try:
20
+ uprn = kwargs.get("uprn")
21
+ # Check the UPRN is valid
22
+ check_uprn(uprn)
23
+ headless = kwargs.get("headless")
24
+ web_driver = kwargs.get("web_driver")
25
+ url = f"https://www.eastleigh.gov.uk/waste-bins-and-recycling/collection-dates/your-waste-bin-and-recycling-collections?uprn={uprn}"
26
+ driver = create_webdriver(web_driver, headless, None, __name__)
27
+ driver.get(url)
18
28
 
19
- # Request URL
20
- url = f"https://www.eastleigh.gov.uk/waste-bins-and-recycling/collection-dates/your-waste-bin-and-recycling-collections?uprn={uprn}"
29
+ wait = WebDriverWait(driver, 10)
30
+ bin_content = wait.until(
31
+ EC.presence_of_element_located((By.CLASS_NAME, "dl-horizontal"))
32
+ )
21
33
 
22
- # Make Request
23
- requests.packages.urllib3.disable_warnings()
24
- page = requests.get(url)
34
+ # Make a BS4 object from driver's page source
35
+ soup = BeautifulSoup(driver.page_source, features="html.parser")
25
36
 
26
- # Make a BS4 object
27
- soup = BeautifulSoup(page.text, features="html.parser")
28
- soup.prettify()
37
+ # Data to return
38
+ data = {"bins": []}
29
39
 
30
- # Data to return
31
- data = {"bins": []}
40
+ # Valid bin types
41
+ binTypes = [
42
+ "Household Waste Bin",
43
+ "Recycling Bin",
44
+ "Food Waste Bin",
45
+ "Glass Box and Batteries",
46
+ "Garden Waste Bin",
47
+ ]
32
48
 
33
- # Valid bin types
34
- binTypes = [
35
- "Household Waste Bin",
36
- "Recycling Bin",
37
- "Food Waste Bin",
38
- "Glass Box and Batteries",
39
- "Garden Waste Bin",
40
- ]
49
+ # Value to create dict for DL values
50
+ keys, values = [], []
41
51
 
42
- # Value to create dict for DL values
43
- keys, values = [], []
52
+ # Loop though DT and DD for DL containing bins
53
+ dl = soup.find("dl", {"class": "dl-horizontal"})
54
+ for dt in dl.find_all("dt"):
55
+ keys.append(dt.text.strip())
56
+ for dd in dl.find_all("dd"):
57
+ values.append(dd.text.strip())
44
58
 
45
- # Loop though DT and DD for DL containing bins
46
- dl = soup.find("dl", {"class": "dl-horizontal"})
47
- for dt in dl.find_all("dt"):
48
- keys.append(dt.text.strip())
49
- for dd in dl.find_all("dd"):
50
- values.append(dd.text.strip())
59
+ # Create dict for bin name and string dates
60
+ binDict = dict(zip(keys, values))
51
61
 
52
- # Create dict for bin name and string dates
53
- binDict = dict(zip(keys, values))
62
+ # Process dict for valid bin types
63
+ for bin in list(binDict):
64
+ if bin in binTypes:
65
+ if not binDict[bin].startswith("You haven't yet signed up for"):
66
+ # Convert date
67
+ date = datetime.strptime(binDict[bin], "%a, %d %b %Y")
54
68
 
55
- # Process dict for valid bin types
56
- for bin in list(binDict):
57
- if bin in binTypes:
58
- if not binDict[bin].startswith("You haven't yet signed up for"):
59
- # Convert date
60
- date = datetime.strptime(binDict[bin], "%a, %d %b %Y")
69
+ # Set bin data
70
+ dict_data = {
71
+ "type": bin,
72
+ "collectionDate": date.strftime(date_format),
73
+ }
74
+ data["bins"].append(dict_data)
61
75
 
62
- # Set bin data
63
- dict_data = {
64
- "type": bin,
65
- "collectionDate": date.strftime(date_format),
66
- }
67
- data["bins"].append(dict_data)
76
+ # Return bin data
77
+ return data
68
78
 
69
- # Return bin data
70
- return data
79
+ except Exception as e:
80
+ print(f"Error fetching/parsing data: {str(e)}")
81
+ return {"bins": [{"type": "Error", "collectionDate": "2024-01-01"}]}
82
+ finally:
83
+ if "driver" in locals():
84
+ driver.quit()
@@ -1,3 +1,5 @@
1
+ # Legacy script. Copied to Lewes and Eastbourne.
2
+
1
3
  from bs4 import BeautifulSoup
2
4
 
3
5
  from uk_bin_collection.uk_bin_collection.common import *
@@ -1,13 +1,15 @@
1
+ from datetime import datetime
2
+
1
3
  from bs4 import BeautifulSoup
2
- from uk_bin_collection.uk_bin_collection.common import *
3
- from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
4
4
  from selenium import webdriver
5
- from selenium.webdriver.common.keys import Keys
6
5
  from selenium.webdriver.common.by import By
7
- from selenium.webdriver.support.ui import WebDriverWait
6
+ from selenium.webdriver.common.keys import Keys
8
7
  from selenium.webdriver.support import expected_conditions as EC
9
- from datetime import datetime
8
+ from selenium.webdriver.support.ui import WebDriverWait
9
+
10
+ from uk_bin_collection.uk_bin_collection.common import *
10
11
  from uk_bin_collection.uk_bin_collection.common import date_format
12
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
11
13
 
12
14
 
13
15
  class CouncilClass(AbstractGetBinDataClass):
@@ -15,27 +17,57 @@ class CouncilClass(AbstractGetBinDataClass):
15
17
  postcode = kwargs.get("postcode", "")
16
18
  web_driver = kwargs.get("web_driver")
17
19
  headless = kwargs.get("headless")
18
-
19
- options = webdriver.ChromeOptions()
20
- if headless:
21
- options.add_argument("--headless")
22
- driver = create_webdriver(web_driver, headless)
20
+ data = {"bins": []}
23
21
 
24
22
  try:
25
- driver.get(
26
- f"https://eppingforestdc.maps.arcgis.com/apps/instant/lookup/index.html?appid=bfca32b46e2a47cd9c0a84f2d8cdde17&find={postcode}"
23
+ # Initialize webdriver with logging
24
+ print(f"Initializing webdriver with: {web_driver}, headless: {headless}")
25
+ driver = create_webdriver(web_driver, headless, None, __name__)
26
+
27
+ # Format and load URL
28
+ page_url = f"https://eppingforestdc.maps.arcgis.com/apps/instant/lookup/index.html?appid=bfca32b46e2a47cd9c0a84f2d8cdde17&find={postcode}"
29
+ print(f"Accessing URL: {page_url}")
30
+ driver.get(page_url)
31
+
32
+ # Wait for initial page load
33
+ wait = WebDriverWait(driver, 20) # Reduced timeout to fail faster if issues
34
+
35
+ # First wait for any loading indicators to disappear
36
+ try:
37
+ print("Waiting for loading spinner to disappear...")
38
+ wait.until(
39
+ EC.invisibility_of_element_located(
40
+ (By.CSS_SELECTOR, ".esri-widget--loader-container")
41
+ )
42
+ )
43
+ except Exception as e:
44
+ print(f"Loading spinner wait failed (may be normal): {str(e)}")
45
+
46
+ # Then wait for the content container
47
+ print("Waiting for content container...")
48
+ wait.until(
49
+ EC.presence_of_element_located(
50
+ (By.CSS_SELECTOR, ".esri-feature-content")
51
+ )
27
52
  )
28
- wait = WebDriverWait(driver, 10)
29
- WebDriverWait(driver, 10).until(
53
+
54
+ # Finally wait for actual content
55
+ print("Waiting for content to be visible...")
56
+ content = wait.until(
30
57
  EC.visibility_of_element_located(
31
58
  (By.CSS_SELECTOR, ".esri-feature-content")
32
59
  )
33
60
  )
61
+
62
+ # Check if content is actually present
63
+ if not content:
64
+ raise ValueError("Content element found but empty")
65
+
66
+ print("Content found, getting page source...")
34
67
  html_content = driver.page_source
35
68
 
36
69
  soup = BeautifulSoup(html_content, "html.parser")
37
70
  bin_info_divs = soup.select(".esri-feature-content p")
38
- data = {"bins": []}
39
71
  for div in bin_info_divs:
40
72
  if "collection day is" in div.text:
41
73
  bin_type, date_str = div.text.split(" collection day is ")
@@ -0,0 +1,102 @@
1
+ import difflib
2
+ from datetime import date, datetime
3
+
4
+ import requests
5
+ from bs4 import BeautifulSoup
6
+
7
+ from uk_bin_collection.uk_bin_collection.common import *
8
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
9
+
10
+
11
+ # import the wonderful Beautiful Soup and the URL grabber
12
+ class CouncilClass(AbstractGetBinDataClass):
13
+ """
14
+ Concrete classes have to implement all abstract operations of the
15
+ base class. They can also override some operations with a default
16
+ implementation.
17
+ """
18
+
19
+ base_url = "https://fermanaghomagh.isl-fusion.com/"
20
+
21
+ def parse_data(self, page: str, **kwargs) -> dict:
22
+ """
23
+ This function will make a request to the search endpoint with the postcode, extract the
24
+ house numbers from the responses, then retrieve the ID of the entry with the house number that matches,
25
+ to then retrieve the bin schedule.
26
+
27
+ The API here is a weird combination of HTML in json responses.
28
+ """
29
+ postcode = kwargs.get("postcode")
30
+ paon = kwargs.get("paon")
31
+
32
+ if not postcode:
33
+ raise ValueError("Must provide a postcode")
34
+
35
+ if not paon:
36
+ raise ValueError("Must provide a house number")
37
+
38
+ search_url = f"{self.base_url}/address/{postcode}"
39
+
40
+ requests.packages.urllib3.disable_warnings()
41
+ s = requests.Session()
42
+ response = s.get(search_url)
43
+ response.raise_for_status()
44
+
45
+ address_data = response.json()
46
+
47
+ address_list = address_data["html"]
48
+
49
+ soup = BeautifulSoup(address_list, features="html.parser")
50
+
51
+ address_by_id = {}
52
+
53
+ for li in soup.find_all("li"):
54
+ link = li.find_all("a")[0]
55
+ address_id = link.attrs["href"]
56
+ address = link.text
57
+
58
+ address_by_id[address_id] = address
59
+
60
+ addresses = list(address_by_id.values())
61
+
62
+ common = difflib.SequenceMatcher(
63
+ a=addresses[0], b=addresses[1]
64
+ ).find_longest_match()
65
+ extra_bit = addresses[0][common.a : common.a + common.size]
66
+
67
+ ids_by_paon = {
68
+ a.replace(extra_bit, ""): a_id.replace("/view/", "").replace("/", "")
69
+ for a_id, a in address_by_id.items()
70
+ }
71
+
72
+ property_id = ids_by_paon.get(paon)
73
+ if not property_id:
74
+ raise ValueError(
75
+ f"Invalid house number, valid values are {', '.join(ids_by_paon.keys())}"
76
+ )
77
+
78
+ today = date.today()
79
+ calendar_url = (
80
+ f"{self.base_url}/calendar/{property_id}/{today.strftime('%Y-%m-%d')}"
81
+ )
82
+ response = s.get(calendar_url)
83
+ response.raise_for_status()
84
+ calendar_data = response.json()
85
+ next_collections = calendar_data["nextCollections"]
86
+
87
+ collections = list(next_collections["collections"].values())
88
+
89
+ data = {"bins": []}
90
+
91
+ for collection in collections:
92
+ collection_date = datetime.strptime(collection["date"], "%Y-%m-%d")
93
+ bins = [c["name"] for c in collection["collections"].values()]
94
+
95
+ for bin in bins:
96
+ data["bins"].append(
97
+ {
98
+ "type": bin,
99
+ "collectionDate": collection_date.strftime(date_format),
100
+ }
101
+ )
102
+ return data
@@ -14,7 +14,19 @@ class CouncilClass(AbstractGetBinDataClass):
14
14
  """
15
15
 
16
16
  def parse_data(self, page: str, **kwargs) -> dict:
17
- # Parse the page
17
+
18
+ try:
19
+ user_uprn = kwargs.get("uprn")
20
+ check_uprn(user_uprn)
21
+ url = f"https://onlineservices.glasgow.gov.uk/forms/RefuseAndRecyclingWebApplication/CollectionsCalendar.aspx?UPRN={user_uprn}"
22
+ if not user_uprn:
23
+ # This is a fallback for if the user stored a URL in old system. Ensures backwards compatibility.
24
+ url = kwargs.get("url")
25
+ except Exception as e:
26
+ raise ValueError(f"Error getting identifier: {str(e)}")
27
+
28
+ # Make a BS4 object
29
+ page = requests.get(url, verify=False)
18
30
  soup = BeautifulSoup(page.text, features="html.parser")
19
31
  soup.prettify()
20
32
 
@@ -27,9 +27,8 @@ class CouncilClass(AbstractGetBinDataClass):
27
27
  def parse_data(self, page: str, **kwargs) -> dict:
28
28
  driver = None
29
29
  try:
30
- uprn = kwargs.get("uprn")
31
30
  postcode = kwargs.get("postcode")
32
- full_address = kwargs.get("paon")
31
+ house_number = kwargs.get("paon")
33
32
 
34
33
  url = "https://my.guildford.gov.uk/customers/s/view-bin-collections"
35
34
 
@@ -60,7 +59,7 @@ class CouncilClass(AbstractGetBinDataClass):
60
59
  EC.presence_of_element_located(
61
60
  (
62
61
  By.XPATH,
63
- f"//lightning-base-formatted-text[contains(text(), '{full_address}')]",
62
+ f"//lightning-base-formatted-text[contains(text(), '{house_number}')]",
64
63
  )
65
64
  )
66
65
  )
@@ -15,9 +15,20 @@ class CouncilClass(AbstractGetBinDataClass):
15
15
  """
16
16
 
17
17
  def parse_data(self, page: str, **kwargs) -> dict:
18
+ try:
19
+ user_uprn = kwargs.get("uprn")
20
+ check_uprn(user_uprn)
21
+ url = f"https://www.herefordshire.gov.uk/rubbish-recycling/check-bin-collection-day?blpu_uprn={user_uprn}"
22
+ if not user_uprn:
23
+ # This is a fallback for if the user stored a URL in old system. Ensures backwards compatibility.
24
+ url = kwargs.get("url")
25
+ except Exception as e:
26
+ raise ValueError(f"Error getting identifier: {str(e)}")
27
+
18
28
  # Make a BS4 object
19
- soup = BeautifulSoup(page.text, features="html.parser")
20
- soup.prettify()
29
+ page = requests.get(url)
30
+ soup = BeautifulSoup(page.text, "html.parser")
31
+ soup.prettify
21
32
 
22
33
  data = {"bins": []}
23
34
 
@@ -2,10 +2,12 @@
2
2
 
3
3
  # This script pulls (in one hit) the data from
4
4
  # Huntingdon District Council District Council Bins Data
5
+ from datetime import datetime
6
+
5
7
  from bs4 import BeautifulSoup
8
+
9
+ from uk_bin_collection.uk_bin_collection.common import *
6
10
  from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
7
- from uk_bin_collection.uk_bin_collection.common import date_format
8
- from datetime import datetime
9
11
 
10
12
 
11
13
  # import the wonderful Beautiful Soup and the URL grabber
@@ -17,9 +19,21 @@ class CouncilClass(AbstractGetBinDataClass):
17
19
  """
18
20
 
19
21
  def parse_data(self, page, **kwargs) -> None:
22
+
23
+ try:
24
+ user_uprn = kwargs.get("uprn")
25
+ check_uprn(user_uprn)
26
+ url = f"http://www.huntingdonshire.gov.uk/refuse-calendar/{user_uprn}"
27
+ if not user_uprn:
28
+ # This is a fallback for if the user stored a URL in old system. Ensures backwards compatibility.
29
+ url = kwargs.get("url")
30
+ except Exception as e:
31
+ raise ValueError(f"Error getting identifier: {str(e)}")
32
+
20
33
  # Make a BS4 object
21
- soup = BeautifulSoup(page.text, features="html.parser")
22
- soup.prettify()
34
+ page = requests.get(url)
35
+ soup = BeautifulSoup(page.text, "html.parser")
36
+ soup.prettify
23
37
 
24
38
  data = {"bins": []}
25
39
 
@@ -0,0 +1,76 @@
1
+ # Eastbourne uses the same script.
2
+
3
+ from bs4 import BeautifulSoup
4
+
5
+ from uk_bin_collection.uk_bin_collection.common import *
6
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
7
+
8
+
9
+ # import the wonderful Beautiful Soup and the URL grabber
10
+ class CouncilClass(AbstractGetBinDataClass):
11
+ """
12
+ Concrete classes have to implement all abstract operations of the
13
+ base class. They can also override some operations with a default
14
+ implementation.
15
+ """
16
+
17
+ def parse_data(self, page: str, **kwargs) -> dict:
18
+
19
+ try:
20
+ user_uprn = kwargs.get("uprn")
21
+ check_uprn(user_uprn)
22
+ url = f"https://environmentfirst.co.uk/house.php?uprn={user_uprn}"
23
+ if not user_uprn:
24
+ # This is a fallback for if the user stored a URL in old system. Ensures backwards compatibility.
25
+ url = kwargs.get("url")
26
+ except Exception as e:
27
+ raise ValueError(f"Error getting identifier: {str(e)}")
28
+
29
+ # Make a BS4 object
30
+ page = requests.get(url)
31
+ soup = BeautifulSoup(page.text, features="html.parser")
32
+ soup.prettify()
33
+
34
+ # Get the paragraph lines from the page
35
+ data = {"bins": []}
36
+ page_text = soup.find("div", {"class": "collect"}).find_all("p")
37
+
38
+ # Parse the correct lines (find them, remove the ordinal indicator and make them the correct format date) and
39
+ # then add them to the dictionary
40
+ rubbish_day = datetime.strptime(
41
+ remove_ordinal_indicator_from_date_string(
42
+ page_text[2].find_next("strong").text
43
+ ),
44
+ "%d %B %Y",
45
+ ).strftime(date_format)
46
+ dict_data = {
47
+ "type": "Rubbish",
48
+ "collectionDate": rubbish_day,
49
+ }
50
+ data["bins"].append(dict_data)
51
+ recycling_day = datetime.strptime(
52
+ remove_ordinal_indicator_from_date_string(
53
+ page_text[4].find_next("strong").text
54
+ ),
55
+ "%d %B %Y",
56
+ ).strftime(date_format)
57
+ dict_data = {
58
+ "type": "Recycling",
59
+ "collectionDate": recycling_day,
60
+ }
61
+ data["bins"].append(dict_data)
62
+
63
+ if len(page_text) > 5:
64
+ garden_day = datetime.strptime(
65
+ remove_ordinal_indicator_from_date_string(
66
+ page_text[6].find_next("strong").text
67
+ ),
68
+ "%d %B %Y",
69
+ ).strftime(date_format)
70
+ dict_data = {
71
+ "type": "Garden",
72
+ "collectionDate": garden_day,
73
+ }
74
+ data["bins"].append(dict_data)
75
+
76
+ return data