uk_bin_collection 0.92.0__py3-none-any.whl → 0.94.0__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.
@@ -42,7 +42,13 @@
42
42
  "wiki_name": "Barnsley Metropolitan Borough Council",
43
43
  "wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
44
44
  },
45
- "BasingstokeCouncil": {
45
+ "BasildonCouncil": {
46
+ "skip_get_url": true,
47
+ "uprn": "10013350430",
48
+ "url": "https://basildonportal.azurewebsites.net/api/getPropertyRefuseInformation",
49
+ "wiki_name": "Basildon Council"
50
+ },
51
+ "BasingstokeCouncil": {
46
52
  "skip_get_url": true,
47
53
  "uprn": "100060220926",
48
54
  "url": "https://www.basingstoke.gov.uk/bincollection",
@@ -109,6 +115,7 @@
109
115
  "wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search). Previously required single field that was UPRN and full address, now requires UPRN and postcode as separate fields."
110
116
  },
111
117
  "BracknellForestCouncil": {
118
+ "house_number": "57",
112
119
  "paon": "57",
113
120
  "postcode": "GU47 9BS",
114
121
  "skip_get_url": true,
@@ -155,10 +162,10 @@
155
162
  "wiki_name": "Broxtowe Borough Council"
156
163
  },
157
164
  "BuckinghamshireCouncil": {
158
- "house_number": "HUGHENDEN MANOR, MANOR ROAD, HUGHENDEN VALLEY, HIGH WYCOMBE",
159
- "postcode": "HP14 4LA",
165
+ "house_number": "2",
166
+ "postcode": "HP13 7BA",
160
167
  "skip_get_url": true,
161
- "url": "https://chiltern.gov.uk/collection-dates",
168
+ "url": "https://iapp.itouchvision.com/iappcollectionday/collection-day/?uuid=FA353FC74600CBE61BE409534D00A8EC09BDA3AC&lang=en",
162
169
  "web_driver": "http://selenium:4444",
163
170
  "wiki_name": "Buckinghamshire Council (Chiltern, South Bucks, Wycombe)",
164
171
  "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes"
@@ -286,6 +293,14 @@
286
293
  "url": "https://service.croydon.gov.uk/wasteservices/w/webpage/bin-day-enter-address",
287
294
  "wiki_name": "Croydon Council"
288
295
  },
296
+ "DacorumBoroughCouncil": {
297
+ "house_number": "13",
298
+ "postcode": "HP3 9JY",
299
+ "skip_get_url": true,
300
+ "web_driver": "http://selenium:4444",
301
+ "url": "https://webapps.dacorum.gov.uk/bincollections/",
302
+ "wiki_name": "Dacorum Borough Council"
303
+ },
289
304
  "DartfordBoroughCouncil": {
290
305
  "uprn": "010094157511",
291
306
  "url": "https://windmz.dartford.gov.uk/ufs/WS_CHECK_COLLECTIONS.eb?UPRN=010094157511",
@@ -337,8 +352,8 @@
337
352
  "wiki_name": "East Cambridgeshire Council"
338
353
  },
339
354
  "EastDevonDC": {
340
- "url": "https://eastdevon.gov.uk/recycling-and-waste/recycling-and-waste-information/when-is-my-bin-collected/future-collections-calendar/?UPRN=010090909915",
341
- "wiki_command_url_override": "https://eastdevon.gov.uk/recycling-and-waste/recycling-and-waste-information/when-is-my-bin-collected/future-collections-calendar/?UPRN=XXXXXXXX",
355
+ "url": "https://eastdevon.gov.uk/recycling-and-waste/recycling-waste-information/when-is-my-bin-collected/future-collections-calendar/?UPRN=010090909915",
356
+ "wiki_command_url_override": "https://eastdevon.gov.uk/recycling-waste/recycling-and-waste-information/when-is-my-bin-collected/future-collections-calendar/?UPRN=XXXXXXXX",
342
357
  "wiki_name": "East Devon District Council",
343
358
  "wiki_note": "Replace XXXXXXXX with UPRN."
344
359
  },
@@ -589,6 +604,12 @@
589
604
  "url": "https://www.hounslow.gov.uk/homepage/86/recycling_and_waste_collection_day_finder",
590
605
  "wiki_name": "London Borough Hounslow"
591
606
  },
607
+ "LondonBoroughLambeth": {
608
+ "skip_get_url": true,
609
+ "uprn": "100021881738",
610
+ "url": "https://wasteservice.lambeth.gov.uk/WhitespaceComms/GetServicesByUprn",
611
+ "wiki_name": "London Borough Lambeth"
612
+ },
592
613
  "LondonBoroughRedbridge": {
593
614
  "postcode": "IG2 6LQ",
594
615
  "uprn": "10023770353",
@@ -659,9 +680,9 @@
659
680
  "wiki_note": "UPRN can only be parsed with a valid postcode."
660
681
  },
661
682
  "NeathPortTalbotCouncil": {
683
+ "house_number": "2",
662
684
  "postcode": "SA13 3BA",
663
685
  "skip_get_url": true,
664
- "uprn": "10023947752",
665
686
  "url": "https://www.npt.gov.uk",
666
687
  "web_driver": "http://selenium:4444",
667
688
  "wiki_name": "Neath Port Talbot Council"
@@ -1229,6 +1250,16 @@
1229
1250
  "url": "https://ilambassadorformsprod.azurewebsites.net/wastecollectiondays/index",
1230
1251
  "wiki_name": "Wiltshire Council"
1231
1252
  },
1253
+ "WinchesterCityCouncil": {
1254
+ "house_number": "12",
1255
+ "paon": "12",
1256
+ "postcode": "SO23 7GA",
1257
+ "skip_get_url": false,
1258
+ "url": "https://iportal.itouchvision.com/icollectionday/collection-day",
1259
+ "web_driver": "http://selenium:4444",
1260
+ "wiki_name": "Winchester City Council",
1261
+ "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes"
1262
+ },
1232
1263
  "WindsorAndMaidenheadCouncil": {
1233
1264
  "web_driver": "http://selenium:4444",
1234
1265
  "uprn": "100080371082",
@@ -1244,6 +1275,13 @@
1244
1275
  "wiki_name": "Woking Borough Council/Joint Waste Solutions",
1245
1276
  "wiki_note": "Works with all collection areas that use Joint Waste Solutions. Just use the correct URL."
1246
1277
  },
1278
+ "WokinghamBoroughCouncil": {
1279
+ "house_number": "90",
1280
+ "postcode": "RG40 2HR",
1281
+ "skip_get_url": true,
1282
+ "url": "https://www.wokingham.gov.uk/rubbish-and-recycling/waste-collection/find-your-bin-collection-day",
1283
+ "wiki_name": "Wokingham Borough Council"
1284
+ },
1247
1285
  "WychavonDistrictCouncil": {
1248
1286
  "postcode": "WR3 7RU",
1249
1287
  "skip_get_url": true,
@@ -0,0 +1,81 @@
1
+ from uk_bin_collection.uk_bin_collection.common import *
2
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
3
+
4
+
5
+ # import the wonderful Beautiful Soup and the URL grabber
6
+ class CouncilClass(AbstractGetBinDataClass):
7
+ """
8
+ Concrete classes have to implement all abstract operations of the
9
+ base class. They can also override some operations with a default
10
+ implementation.
11
+ """
12
+
13
+ def parse_data(self, page: str, **kwargs) -> dict:
14
+ url_base = "https://basildonportal.azurewebsites.net/api/getPropertyRefuseInformation"
15
+
16
+ uprn = kwargs.get("uprn")
17
+ # Check the UPRN is valid
18
+ check_uprn(uprn)
19
+
20
+ payload = {
21
+ # Add your payload details here (replace this with the actual payload structure if required)
22
+ "uprn": uprn
23
+ }
24
+
25
+ # Headers for the request
26
+ headers = {
27
+ "Content-Type": "application/json"
28
+ }
29
+
30
+ response = requests.post(url_base, data=json.dumps(payload), headers=headers)
31
+
32
+ # Ensure the request was successful
33
+ if response.status_code == 200:
34
+ data = response.json()
35
+
36
+ # Initialize an empty list to store the bin collection details
37
+
38
+ bins = []
39
+
40
+ # Function to add collection details to bins list
41
+ def add_collection(service_name, collection_data):
42
+ bins.append({
43
+ "type": service_name,
44
+ "collectionDate": collection_data.get("current_collection_date")
45
+ })
46
+
47
+ # Extract refuse information
48
+ available_services = data["refuse"]["available_services"]
49
+
50
+ for service_name, service_data in available_services.items():
51
+ # Append the service name and current collection date to the "bins" list
52
+ match service_data["container"]:
53
+ case "Green Wheelie Bin":
54
+ subscription_status = service_data["subscription"]["active"] if service_data["subscription"] else False
55
+ type_descr = f"Green Wheelie Bin ({"Active" if subscription_status else "Expired"})"
56
+ case "N/A":
57
+ type_descr = service_data["name"]
58
+ case _:
59
+ type_descr = service_data["container"]
60
+
61
+
62
+ date_str = service_data.get("current_collection_date")
63
+ # Parse the date string into a datetime object
64
+ date_obj = datetime.strptime(date_str, "%Y-%m-%d")
65
+
66
+ # Convert the datetime object to the desired format
67
+ formatted_date = date_obj.strftime(date_format)
68
+
69
+ bins.append({
70
+ "type": type_descr, # Use service name from the data
71
+ "collectionDate": formatted_date
72
+ })
73
+
74
+ else:
75
+ print(f"Failed to fetch data. Status code: {response.status_code}")
76
+
77
+ data = {
78
+ "bins": bins
79
+ }
80
+
81
+ return data
@@ -1,11 +1,9 @@
1
- import time
2
- import pandas as pd
1
+ from bs4 import BeautifulSoup
3
2
  from selenium.webdriver.common.by import By
4
- from selenium.webdriver.common.keys import Keys
5
- from selenium.webdriver.support.ui import Select
6
- from io import StringIO
3
+ from selenium.webdriver.support import expected_conditions as EC
4
+ from selenium.webdriver.support.wait import WebDriverWait
7
5
 
8
- from uk_bin_collection.uk_bin_collection.common import create_webdriver
6
+ from uk_bin_collection.uk_bin_collection.common import *
9
7
  from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
10
8
 
11
9
 
@@ -16,76 +14,95 @@ class CouncilClass(AbstractGetBinDataClass):
16
14
  implementation.
17
15
  """
18
16
 
19
- def get_data(self, df: pd.DataFrame) -> dict:
20
- # Create dictionary of data to be returned
21
- data = {"bins": []}
22
-
23
- # Output collection data into dictionary
24
- for i, row in df.iterrows():
25
- dict_data = {
26
- "type": row["Collection Name"],
27
- "collectionDate": row["Next Collection Due"],
28
- }
29
-
30
- data["bins"].append(dict_data)
31
-
32
- return data
33
-
34
17
  def parse_data(self, page: str, **kwargs) -> dict:
35
18
  driver = None
36
19
  try:
37
- page = "https://chiltern.gov.uk/collection-dates"
38
-
39
- # Assign user info
40
- user_postcode = kwargs.get("postcode")
20
+ data = {"bins": []}
41
21
  user_paon = kwargs.get("paon")
22
+ user_postcode = kwargs.get("postcode")
42
23
  web_driver = kwargs.get("web_driver")
43
24
  headless = kwargs.get("headless")
25
+ check_paon(user_paon)
26
+ check_postcode(user_postcode)
44
27
 
45
28
  # Create Selenium webdriver
46
29
  driver = create_webdriver(web_driver, headless, None, __name__)
47
- driver.get(page)
30
+ driver.get(
31
+ "https://iapp.itouchvision.com/iappcollectionday/collection-day/?uuid=FA353FC74600CBE61BE409534D00A8EC09BDA3AC&lang=en"
32
+ )
48
33
 
49
- # Enter postcode in text box and wait
50
- inputElement_pc = driver.find_element(
51
- By.ID,
52
- "COPYOFECHOCOLLECTIONDATES_ADDRESSSELECTION_ADDRESSSELECTIONPOSTCODE",
34
+ # Wait for the postcode field to appear then populate it
35
+ inputElement_postcode = WebDriverWait(driver, 10).until(
36
+ EC.presence_of_element_located((By.ID, "postcodeSearch"))
53
37
  )
54
- inputElement_pc.send_keys(user_postcode)
55
- inputElement_pc.send_keys(Keys.ENTER)
38
+ inputElement_postcode.send_keys(user_postcode)
56
39
 
57
- time.sleep(4)
40
+ # Click search button
41
+ findAddress = WebDriverWait(driver, 10).until(
42
+ EC.presence_of_element_located(
43
+ (By.XPATH, '//button[@class="govuk-button mt-4"]')
44
+ )
45
+ )
46
+ findAddress.click()
47
+
48
+ # Wait for the 'Select address' dropdown to appear and select option matching the house name/number
49
+ WebDriverWait(driver, 10).until(
50
+ EC.element_to_be_clickable(
51
+ (
52
+ By.XPATH,
53
+ "//select[@id='addressSelect']//option[contains(., '"
54
+ + user_paon
55
+ + "')]",
56
+ )
57
+ )
58
+ ).click()
58
59
 
59
- # Select address from dropdown and wait
60
- inputElement_ad = Select(
61
- driver.find_element(
62
- By.ID,
63
- "COPYOFECHOCOLLECTIONDATES_ADDRESSSELECTION_ADDRESSSELECTIONADDRESS",
60
+ # Wait for the collections table to appear
61
+ WebDriverWait(driver, 10).until(
62
+ EC.presence_of_element_located(
63
+ (
64
+ By.XPATH,
65
+ '//div[@class="ant-row d-flex justify-content-between mb-4 mt-2 css-2rgkd4"]',
66
+ )
64
67
  )
65
68
  )
66
69
 
67
- inputElement_ad.select_by_visible_text(user_paon)
70
+ soup = BeautifulSoup(driver.page_source, features="html.parser")
68
71
 
69
- time.sleep(4)
72
+ recyclingcalendar = soup.find(
73
+ "div",
74
+ {
75
+ "class": "ant-row d-flex justify-content-between mb-4 mt-2 css-2rgkd4"
76
+ },
77
+ )
70
78
 
71
- # Submit address information and wait
72
- driver.find_element(
73
- By.ID, "COPYOFECHOCOLLECTIONDATES_ADDRESSSELECTION_NAV1_NEXT"
74
- ).click()
79
+ rows = recyclingcalendar.find_all(
80
+ "div",
81
+ {
82
+ "class": "ant-col ant-col-xs-12 ant-col-sm-12 ant-col-md-12 ant-col-lg-12 ant-col-xl-12 css-2rgkd4"
83
+ },
84
+ )
75
85
 
76
- time.sleep(4)
86
+ current_year = datetime.now().year
87
+ current_month = datetime.now().month
77
88
 
78
- # Read next collection information into Pandas
79
- table = driver.find_element(
80
- By.ID, "COPYOFECHOCOLLECTIONDATES_PAGE1_DATES2"
81
- ).get_attribute("outerHTML")
89
+ for row in rows:
90
+ BinType = row.find("h3").text
91
+ collectiondate = datetime.strptime(
92
+ row.find("div", {"class": "text-white fw-bold"}).text,
93
+ "%A %d %B",
94
+ )
95
+ if (current_month > 10) and (collectiondate.month < 3):
96
+ collectiondate = collectiondate.replace(year=(current_year + 1))
97
+ else:
98
+ collectiondate = collectiondate.replace(year=current_year)
82
99
 
83
- # Wrap the HTML table in a StringIO object to address the warning
84
- table_io = StringIO(table)
85
- df = pd.read_html(table_io, header=[1])[0]
100
+ dict_data = {
101
+ "type": BinType,
102
+ "collectionDate": collectiondate.strftime("%d/%m/%Y"),
103
+ }
104
+ data["bins"].append(dict_data)
86
105
 
87
- # Parse data into dict
88
- data = self.get_data(df)
89
106
  except Exception as e:
90
107
  # Here you can log the exception if needed
91
108
  print(f"An error occurred: {e}")
@@ -0,0 +1,102 @@
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.wait import WebDriverWait
5
+
6
+ from uk_bin_collection.uk_bin_collection.common import *
7
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
8
+
9
+
10
+ # import the wonderful Beautiful Soup and the URL grabber
11
+ class CouncilClass(AbstractGetBinDataClass):
12
+ """
13
+ Concrete classes have to implement all abstract operations of the
14
+ base class. They can also override some operations with a default
15
+ implementation.
16
+ """
17
+
18
+ def parse_data(self, page: str, **kwargs) -> dict:
19
+ driver = None
20
+ try:
21
+ data = {"bins": []}
22
+ user_paon = kwargs.get("paon")
23
+ user_postcode = kwargs.get("postcode")
24
+ web_driver = kwargs.get("web_driver")
25
+ headless = kwargs.get("headless")
26
+ check_paon(user_paon)
27
+ check_postcode(user_postcode)
28
+
29
+ # Create Selenium webdriver
30
+ driver = create_webdriver(web_driver, headless, None, __name__)
31
+ driver.get("https://webapps.dacorum.gov.uk/bincollections/")
32
+
33
+ # Wait for the postcode field to appear then populate it
34
+ inputElement_postcode = WebDriverWait(driver, 30).until(
35
+ EC.presence_of_element_located((By.ID, "txtBxPCode"))
36
+ )
37
+ inputElement_postcode.send_keys(user_postcode)
38
+
39
+ # Click search button
40
+ findAddress = WebDriverWait(driver, 10).until(
41
+ EC.presence_of_element_located((By.ID, "btnFindAddr"))
42
+ )
43
+ findAddress.click()
44
+
45
+ # Wait for the 'Select address' dropdown to appear and select option matching the house name/number
46
+ WebDriverWait(driver, 10).until(
47
+ EC.element_to_be_clickable(
48
+ (
49
+ By.XPATH,
50
+ "//select[@id='lstBxAddrList']//option[contains(., '"
51
+ + user_paon
52
+ + "')]",
53
+ )
54
+ )
55
+ ).click()
56
+
57
+ # Click search button
58
+ findDates = WebDriverWait(driver, 10).until(
59
+ EC.presence_of_element_located((By.ID, "MainContent_btnGetSchedules"))
60
+ )
61
+ findDates.click()
62
+
63
+ # Wait for the collections table to appear
64
+ WebDriverWait(driver, 30).until(
65
+ EC.presence_of_element_located((By.ID, "lblSelectedAddr"))
66
+ )
67
+
68
+ soup = BeautifulSoup(driver.page_source, features="html.parser")
69
+ soup.prettify()
70
+
71
+ # Get collections div
72
+ BinCollectionSchedule = soup.find("div", {"id": "MainContent_updPnl"})
73
+
74
+ NextCollections = BinCollectionSchedule.find_all(
75
+ "div", {"style": " margin:5px;"}
76
+ )
77
+
78
+ for Collection in NextCollections:
79
+ BinType = Collection.find("strong").text.strip()
80
+ if BinType:
81
+ CollectionDate = datetime.strptime(
82
+ Collection.find_all("div", {"style": "display:table-cell;"})[1]
83
+ .get_text()
84
+ .strip(),
85
+ "%a, %d %b %Y",
86
+ )
87
+ dict_data = {
88
+ "type": BinType,
89
+ "collectionDate": CollectionDate.strftime("%d/%m/%Y"),
90
+ }
91
+ data["bins"].append(dict_data)
92
+
93
+ except Exception as e:
94
+ # Here you can log the exception if needed
95
+ print(f"An error occurred: {e}")
96
+ # Optionally, re-raise the exception if you want it to propagate
97
+ raise
98
+ finally:
99
+ # This block ensures that the driver is closed regardless of an exception
100
+ if driver:
101
+ driver.quit()
102
+ return data
@@ -0,0 +1,54 @@
1
+ import requests
2
+ from requests.structures import CaseInsensitiveDict
3
+
4
+ from uk_bin_collection.uk_bin_collection.common import *
5
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
6
+
7
+
8
+ # import the wonderful Beautiful Soup and the URL grabber
9
+ class CouncilClass(AbstractGetBinDataClass):
10
+ """
11
+ Concrete classes have to implement all abstract operations of the
12
+ base class. They can also override some operations with a default
13
+ implementation.
14
+ """
15
+
16
+ def parse_data(self, page: str, **kwargs) -> dict:
17
+
18
+ user_uprn = kwargs.get("uprn")
19
+ check_uprn(user_uprn)
20
+ data = {"bins": []}
21
+
22
+ url = "https://wasteservice.lambeth.gov.uk/WhitespaceComms/GetServicesByUprn"
23
+
24
+ headers = CaseInsensitiveDict()
25
+ headers["Content-Type"] = "application/json"
26
+
27
+ body = {"uprn": user_uprn, "includeEventTypes": False, "includeFlags": True}
28
+ json_data = json.dumps(body)
29
+
30
+ res = requests.post(url, headers=headers, data=json_data)
31
+
32
+ if res.status_code != 200:
33
+ raise ConnectionRefusedError("Cannot connect to API!")
34
+
35
+ json_data = res.json()
36
+
37
+ if "SiteServices" in json_data:
38
+ SiteServices = json_data["SiteServices"]
39
+ for service in SiteServices:
40
+ if "NextCollectionDate" in service:
41
+ NextCollectionDate = service["NextCollectionDate"]
42
+ if NextCollectionDate:
43
+ Container = service["Container"]
44
+ if Container:
45
+ Bin_Type = Container["DisplayPhrase"]
46
+ dict_data = {
47
+ "type": Bin_Type,
48
+ "collectionDate": datetime.strptime(
49
+ NextCollectionDate, "%d/%m/%Y"
50
+ ).strftime(date_format),
51
+ }
52
+ data["bins"].append(dict_data)
53
+
54
+ return data
@@ -22,16 +22,16 @@ class CouncilClass(AbstractGetBinDataClass):
22
22
  driver = None
23
23
  try:
24
24
  data = {"bins": []}
25
- user_uprn = kwargs.get("uprn")
25
+ user_paon = kwargs.get("paon")
26
26
  user_postcode = kwargs.get("postcode")
27
27
  web_driver = kwargs.get("web_driver")
28
28
  headless = kwargs.get("headless")
29
- check_uprn(user_uprn)
29
+ check_paon(user_paon)
30
30
  check_postcode(user_postcode)
31
31
 
32
32
  # Create Selenium webdriver
33
33
  driver = create_webdriver(web_driver, headless, None, __name__)
34
- driver.get("https://www.npt.gov.uk/2195")
34
+ driver.get("https://beta.npt.gov.uk/bins-and-recycling/bin-day-finder/")
35
35
 
36
36
  # Accept cookies banner
37
37
  cookieAccept = WebDriverWait(driver, 10).until(
@@ -40,9 +40,13 @@ class CouncilClass(AbstractGetBinDataClass):
40
40
  cookieAccept.click()
41
41
 
42
42
  # Populate postcode field
43
- inputElement_postcode = driver.find_element(
44
- By.ID,
45
- "ContentPlaceHolderDefault_ctl13_nptLLPG2_25_addresslookup_txtTmpPostcode",
43
+ inputElement_postcode = WebDriverWait(driver, 10).until(
44
+ EC.presence_of_element_located(
45
+ (
46
+ By.ID,
47
+ "PostCode",
48
+ )
49
+ )
46
50
  )
47
51
  inputElement_postcode.send_keys(user_postcode)
48
52
 
@@ -50,67 +54,69 @@ class CouncilClass(AbstractGetBinDataClass):
50
54
  findAddress = WebDriverWait(driver, 10).until(
51
55
  EC.presence_of_element_located(
52
56
  (
53
- By.ID,
54
- "ContentPlaceHolderDefault_ctl13_nptLLPG2_25_addresslookup_btnFindAddress",
57
+ By.XPATH,
58
+ "//button[@value='Find address']",
55
59
  )
56
60
  )
57
61
  )
58
62
  findAddress.click()
59
63
 
60
- time.sleep(1)
61
-
62
64
  # Wait for the 'Select address' dropdown to appear and select option matching UPRN
63
65
  dropdown = WebDriverWait(driver, 10).until(
64
66
  EC.presence_of_element_located(
65
67
  (
66
68
  By.ID,
67
- "ContentPlaceHolderDefault_ctl13_nptLLPG2_25_addresslookup_ddlAddressLookup",
69
+ "Address",
68
70
  )
69
71
  )
70
72
  )
71
- # Create a 'Select' for it, then select the matching URPN option
72
- dropdownSelect = Select(dropdown)
73
- dropdownSelect.select_by_value(user_uprn)
74
-
75
- # Remove back to top button if exists
76
- driver.execute_script(
77
- """
78
- if (document.contains(document.querySelector(".backtotop"))) {
79
- document.querySelector(".backtotop").remove();
80
- }
81
- """
82
- )
73
+ # Wait for the 'Select address' dropdown to appear and select option matching the house name/number
74
+ WebDriverWait(driver, 10).until(
75
+ EC.presence_of_element_located(
76
+ (
77
+ By.XPATH,
78
+ "//select[@ID='Address']//option[contains(., '"
79
+ + user_paon
80
+ + "')]",
81
+ )
82
+ )
83
+ ).click()
83
84
 
84
85
  # Wait for the submit button to appear, then click it to get the collection dates
85
86
  submit = WebDriverWait(driver, 10).until(
86
87
  EC.presence_of_element_located(
87
- (By.ID, "ContentPlaceHolderDefault_ctl13_nptLLPG2_25_btnDisplay")
88
+ (
89
+ By.XPATH,
90
+ "//input[@value='Find bin day']",
91
+ )
88
92
  )
89
93
  )
90
94
  submit.click()
91
95
 
92
96
  soup = BeautifulSoup(driver.page_source, features="html.parser")
93
97
 
94
- # Get the property details
95
- property_details = soup.find(
98
+ soup = soup.find(
96
99
  "div",
97
- {
98
- "id": "ContentPlaceHolderDefault_ctl13_nptLLPG2_25_divPropertyDetails"
99
- },
100
+ {"id": "contentInner"},
100
101
  )
101
102
 
102
103
  # Get the dates
103
- for date in property_details.find_all("h2"):
104
+ for date in soup.find_all("h2"):
104
105
  if date.get_text(strip=True) != "Bank Holidays":
105
106
  bin_date = datetime.strptime(
106
- date.get_text(strip=True).replace("&nbsp", " ")
107
+ date.get_text(strip=True)
108
+ .removesuffix("(Today)")
109
+ .replace("&nbsp", " ")
107
110
  + " "
108
111
  + datetime.now().strftime("%Y"),
109
112
  "%A, %d %B %Y",
110
113
  )
111
114
  bin_types_wrapper = date.find_next_sibling("div")
112
115
  for bin_type_wrapper in bin_types_wrapper.find_all(
113
- "div", {"class": "card"}
116
+ "div",
117
+ {
118
+ "class": "card-body ps-5 ps-md-4 ps-lg-5 position-relative bg-white"
119
+ },
114
120
  ):
115
121
  if bin_date and bin_type_wrapper:
116
122
  bin_type = bin_type_wrapper.find("a").get_text(strip=True)
@@ -36,7 +36,8 @@ class CouncilClass(AbstractGetBinDataClass):
36
36
  collection_date = datetime.strptime(
37
37
  remove_ordinal_indicator_from_date_string(
38
38
  week_text[0].split(" - ")[0]
39
- ),
39
+ )
40
+ .strip(),
40
41
  "%A %d %B",
41
42
  )
42
43
  next_collection = collection_date.replace(year=datetime.now().year)
@@ -0,0 +1,114 @@
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.wait import WebDriverWait
5
+
6
+ from uk_bin_collection.uk_bin_collection.common import *
7
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
8
+
9
+
10
+ # import the wonderful Beautiful Soup and the URL grabber
11
+ class CouncilClass(AbstractGetBinDataClass):
12
+ """
13
+ Concrete classes have to implement all abstract operations of the
14
+ base class. They can also override some operations with a default
15
+ implementation.
16
+ """
17
+
18
+ def parse_data(self, page: str, **kwargs) -> dict:
19
+ driver = None
20
+ try:
21
+ data = {"bins": []}
22
+ user_paon = kwargs.get("paon")
23
+ user_postcode = kwargs.get("postcode")
24
+ web_driver = kwargs.get("web_driver")
25
+ headless = kwargs.get("headless")
26
+ check_paon(user_paon)
27
+ check_postcode(user_postcode)
28
+
29
+ # Create Selenium webdriver
30
+ driver = create_webdriver(web_driver, headless, None, __name__)
31
+ driver.get("http://www.winchester.gov.uk/bin-calendar")
32
+
33
+ # Wait for the postcode field to appear then populate it
34
+ inputElement_postcode = WebDriverWait(driver, 10).until(
35
+ EC.presence_of_element_located((By.ID, "postcodeSearch"))
36
+ )
37
+ inputElement_postcode.send_keys(user_postcode)
38
+
39
+ # Click search button
40
+ findAddress = WebDriverWait(driver, 10).until(
41
+ EC.presence_of_element_located(
42
+ (By.XPATH, '//button[@class="govuk-button mt-4"]')
43
+ )
44
+ )
45
+ findAddress.click()
46
+
47
+ # Wait for the 'Select address' dropdown to appear and select option matching the house name/number
48
+ WebDriverWait(driver, 10).until(
49
+ EC.element_to_be_clickable(
50
+ (
51
+ By.XPATH,
52
+ "//select[@id='addressSelect']//option[contains(., '"
53
+ + user_paon
54
+ + "')]",
55
+ )
56
+ )
57
+ ).click()
58
+
59
+ # Wait for the collections table to appear
60
+ WebDriverWait(driver, 10).until(
61
+ EC.presence_of_element_located(
62
+ (
63
+ By.XPATH,
64
+ '//div[@class="ant-row d-flex justify-content-between mb-4 mt-2 css-2rgkd4"]',
65
+ )
66
+ )
67
+ )
68
+
69
+ soup = BeautifulSoup(driver.page_source, features="html.parser")
70
+
71
+ recyclingcalendar = soup.find(
72
+ "div",
73
+ {
74
+ "class": "ant-row d-flex justify-content-between mb-4 mt-2 css-2rgkd4"
75
+ },
76
+ )
77
+
78
+ rows = recyclingcalendar.find_all(
79
+ "div",
80
+ {
81
+ "class": "ant-col ant-col-xs-12 ant-col-sm-12 ant-col-md-12 ant-col-lg-12 ant-col-xl-12 css-2rgkd4"
82
+ },
83
+ )
84
+
85
+ current_year = datetime.now().year
86
+ current_month = datetime.now().month
87
+
88
+ for row in rows:
89
+ BinType = row.find("h3").text
90
+ collectiondate = datetime.strptime(
91
+ row.find("div", {"class": "text-white fw-bold"}).text,
92
+ "%A %d %B",
93
+ )
94
+ if (current_month > 10) and (collectiondate.month < 3):
95
+ collectiondate = collectiondate.replace(year=(current_year + 1))
96
+ else:
97
+ collectiondate = collectiondate.replace(year=current_year)
98
+
99
+ dict_data = {
100
+ "type": BinType,
101
+ "collectionDate": collectiondate.strftime("%d/%m/%Y"),
102
+ }
103
+ data["bins"].append(dict_data)
104
+
105
+ except Exception as e:
106
+ # Here you can log the exception if needed
107
+ print(f"An error occurred: {e}")
108
+ # Optionally, re-raise the exception if you want it to propagate
109
+ raise
110
+ finally:
111
+ # This block ensures that the driver is closed regardless of an exception
112
+ if driver:
113
+ driver.quit()
114
+ return data
@@ -0,0 +1,99 @@
1
+ from selenium.webdriver.common.by import By
2
+ from selenium.webdriver.common.keys import Keys
3
+ from selenium.webdriver.support import expected_conditions as EC
4
+ from selenium.webdriver.support.wait import WebDriverWait
5
+
6
+ from uk_bin_collection.uk_bin_collection.common import *
7
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
8
+
9
+
10
+ # import the wonderful Beautiful Soup and the URL grabber
11
+ class CouncilClass(AbstractGetBinDataClass):
12
+ """
13
+ Concrete classes have to implement all abstract operations of the
14
+ base class. They can also override some operations with a default
15
+ implementation.
16
+ """
17
+
18
+ def parse_data(self, page: str, **kwargs) -> dict:
19
+ driver = None
20
+ try:
21
+ data = {"bins": []}
22
+ source_date_format = "%d/%m/%Y"
23
+ timeout = 10
24
+ user_paon = kwargs.get("paon")
25
+ user_postcode = kwargs.get("postcode")
26
+ web_driver = kwargs.get("web_driver")
27
+ headless = kwargs.get("headless")
28
+
29
+ check_paon(user_paon)
30
+ check_postcode(user_postcode)
31
+
32
+ # Create Selenium webdriver
33
+ driver = create_webdriver(web_driver, headless, None, __name__)
34
+ driver.get(
35
+ "https://www.wokingham.gov.uk/rubbish-and-recycling/waste-collection/find-your-bin-collection-day"
36
+ )
37
+
38
+ # Wait for the postcode field to appear then populate it
39
+ inputElement_postcode = WebDriverWait(driver, timeout).until(
40
+ EC.presence_of_element_located((By.ID, "edit-postcode-search-csv"))
41
+ )
42
+ inputElement_postcode.send_keys(user_postcode)
43
+
44
+ # Simulates hitting the "Enter" key to submit
45
+ inputElement_postcode.send_keys(Keys.RETURN)
46
+
47
+ # Select the exact address from the drop down box
48
+ WebDriverWait(driver, timeout).until(
49
+ EC.element_to_be_clickable(
50
+ (
51
+ By.XPATH,
52
+ ""
53
+ "//*[@id='edit-address-options-csv']//option[starts-with(normalize-space(.), '"
54
+ + user_paon
55
+ + "')]",
56
+ )
57
+ )
58
+ ).click()
59
+
60
+ # Wait for the Show collection dates button to appear, then click it to get the collection dates
61
+ inputElement_show_dates_button = WebDriverWait(driver, timeout).until(
62
+ EC.presence_of_element_located(
63
+ (By.XPATH, '//*[@id="edit-show-collection-dates-csv"]')
64
+ )
65
+ )
66
+ inputElement_show_dates_button.send_keys(Keys.RETURN)
67
+
68
+ # Wait for the collection dates elements to load
69
+ collection_date_cards = WebDriverWait(driver, timeout).until(
70
+ EC.presence_of_all_elements_located(
71
+ (By.XPATH, '//div[@class = "card__content"]')
72
+ )
73
+ )
74
+
75
+ for collection_date_card in collection_date_cards:
76
+ waste_type = collection_date_card.find_element(
77
+ By.XPATH, './/h3[@class = "heading heading--sub heading--tiny"]'
78
+ )
79
+ collection_date = collection_date_card.find_element(
80
+ By.XPATH, './/span[@class = "card__date"]'
81
+ )
82
+ dt_collection_date = datetime.strptime(
83
+ collection_date.text.split(" ")[1], source_date_format
84
+ )
85
+ dict_data = {
86
+ "type": waste_type.text,
87
+ "collectionDate": dt_collection_date.strftime(date_format),
88
+ }
89
+ data["bins"].append(dict_data)
90
+ except Exception as e:
91
+ # Here you can log the exception if needed
92
+ print(f"An error occurred: {e}")
93
+ # Optionally, re-raise the exception if you want it to propagate
94
+ raise
95
+ finally:
96
+ # This block ensures that the driver is closed regardless of an exception
97
+ if driver:
98
+ driver.quit()
99
+ return data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: uk_bin_collection
3
- Version: 0.92.0
3
+ Version: 0.94.0
4
4
  Summary: Python Lib to collect UK Bin Data
5
5
  Author: Robert Bradley
6
6
  Author-email: robbrad182@gmail.com
@@ -53,6 +53,21 @@ Most scripts make use of [Beautiful Soup 4](https://pypi.org/project/beautifulso
53
53
 
54
54
  ---
55
55
 
56
+ ## Requesting your council
57
+ > :warning: Please check that a request for your council has not already been made. You can do this by searching on the [Issues](https://github.com/robbrad/UKBinCollectionData/issues) page.
58
+
59
+ If an issue already exists, please comment on that issue to express your interest. Please do not open a new issue, as it will be closed as a duplicate.
60
+
61
+ If an issue does not already exist, please fill in a new [Council Request](https://github.com/robbrad/UKBinCollectionData/issues/new/choose) form, including as much information as possible, including:
62
+ - Name of the council
63
+ - URL to bin collections
64
+ - An example postcode and/or UPRN (whichever is relevant)
65
+ - Any further information
66
+
67
+ Please be aware that this project is run by volunteer contributors and completion depends on numerous factors - even with a request, we cannot guarantee if/when your council will get added.
68
+
69
+ ---
70
+
56
71
  ## Home Assistant Usage
57
72
 
58
73
  ### Install with HACS (recommended)
@@ -255,17 +270,6 @@ docker pull selenium/standalone-chrome docker run -d -p 4444:4444 --name seleniu
255
270
 
256
271
  ---
257
272
 
258
- ## Requesting your council
259
- To make a request for your council, first check the [Issues](https://github.com/robbrad/UKBinCollectionData/issues) page to make sure it has not already been requested. If not, please fill in a new [Council Request](https://github.com/robbrad/UKBinCollectionData/issues/new/choose) form, including as much information as possible, including:
260
- - Name of the council
261
- - URL to bin collections
262
- - An example postcode and/or UPRN (whichever is relevant)
263
- - Any further information
264
-
265
- Please be aware that this project is run by volunteer contributors and completion depends on numerous factors - even with a request, we cannot guarantee if/when your council will get a script.
266
-
267
- ---
268
-
269
273
  ## Reports
270
274
 
271
275
  - [3.11](https://robbrad.github.io/UKBinCollectionData/3.11/)
@@ -371,3 +375,4 @@ Contributions are always welcome! See ```CONTRIBUTING.md``` to get started. Plea
371
375
  <img src="https://contrib.rocks/image?repo=robbrad/UKBinCollectionData" alt="Image of contributors"/>
372
376
  </a>
373
377
 
378
+
@@ -2,7 +2,7 @@ uk_bin_collection/README.rst,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
2
2
  uk_bin_collection/tests/council_feature_input_parity.py,sha256=DO6Mk4ImYgM5ZCZ-cutwz5RoYYWZRLYx2tr6zIs_9Rc,3843
3
3
  uk_bin_collection/tests/features/environment.py,sha256=VQZjJdJI_kZn08M0j5cUgvKT4k3iTw8icJge1DGOkoA,127
4
4
  uk_bin_collection/tests/features/validate_council_outputs.feature,sha256=SJK-Vc737hrf03tssxxbeg_JIvAH-ddB8f6gU1LTbuQ,251
5
- uk_bin_collection/tests/input.json,sha256=Q3l16fwk4hmvIG6tY6SgP2aZCn-_ghBbMnGjuZCtS_c,61684
5
+ uk_bin_collection/tests/input.json,sha256=vimuJzI_t8_nzD9v1BC2vj3WbLLFpGtPhT10GjlBrAY,63198
6
6
  uk_bin_collection/tests/output.schema,sha256=ZwKQBwYyTDEM4G2hJwfLUVM-5v1vKRvRK9W9SS1sd18,1086
7
7
  uk_bin_collection/tests/step_defs/step_helpers/file_handler.py,sha256=Ygzi4V0S1MIHqbdstUlIqtRIwnynvhu4UtpweJ6-5N8,1474
8
8
  uk_bin_collection/tests/step_defs/test_validate_council.py,sha256=LrOSt_loA1Mw3vTqaO2LpaDMu7rYJy6k5Kr-EOBln7s,3424
@@ -18,6 +18,7 @@ uk_bin_collection/uk_bin_collection/councils/AylesburyValeCouncil.py,sha256=Louq
18
18
  uk_bin_collection/uk_bin_collection/councils/BCPCouncil.py,sha256=W7QBx6Mgso8RYosuXsaYo3GGNAu-tiyBSmuYxr1JSOU,1707
19
19
  uk_bin_collection/uk_bin_collection/councils/BarnetCouncil.py,sha256=n6lNsO_ognmaxq_wIz91Dxt5XjBVQe60X5p6w1_mO1A,8174
20
20
  uk_bin_collection/uk_bin_collection/councils/BarnsleyMBCouncil.py,sha256=MgF_7XyIcIoNzFR0OJsjBkLCZKgWxBrV6nTcutMxO1Q,4244
21
+ uk_bin_collection/uk_bin_collection/councils/BasildonCouncil.py,sha256=SBvAa0GZM3V7ygK8ARawbHAPH6R_303U30RH8WYPi5Q,3020
21
22
  uk_bin_collection/uk_bin_collection/councils/BasingstokeCouncil.py,sha256=VPWGljnH4C3q8qs5ZmCtqjNjgWQvviALzjk00q3EZeQ,2632
22
23
  uk_bin_collection/uk_bin_collection/councils/BathAndNorthEastSomersetCouncil.py,sha256=N_TPiIv8VBzN3rY0p3JtLlxSEru-6k1wW4UNIhN5X1M,3709
23
24
  uk_bin_collection/uk_bin_collection/councils/BedfordBoroughCouncil.py,sha256=CvGB7w9HMn7XyEtwfd9MWZE_HlZ75pDcaKMsQJz0xhk,1669
@@ -33,7 +34,7 @@ uk_bin_collection/uk_bin_collection/councils/BrightonandHoveCityCouncil.py,sha25
33
34
  uk_bin_collection/uk_bin_collection/councils/BristolCityCouncil.py,sha256=kJmmDJz_kQ45DHmG7ocrUpNJonEn0kuXYEDQyZaf9ks,5576
34
35
  uk_bin_collection/uk_bin_collection/councils/BromleyBoroughCouncil.py,sha256=_bAFykZWZkEVUB-QKeVLfWO8plG6nRgn71QF2BUN2rk,4329
35
36
  uk_bin_collection/uk_bin_collection/councils/BroxtoweBoroughCouncil.py,sha256=-Facq-ToQkcWUePpKBwq90LZUFxgUSydNL2sYaLX4yw,4473
36
- uk_bin_collection/uk_bin_collection/councils/BuckinghamshireCouncil.py,sha256=-EAj7-CCb1kWEglITePOsSCUknJRU9hnuS5sysCf19w,3319
37
+ uk_bin_collection/uk_bin_collection/councils/BuckinghamshireCouncil.py,sha256=_ELVUM5VLp1nwDxRpvpsp6n8SzLJvp_UyMp-i_MXYuo,4383
37
38
  uk_bin_collection/uk_bin_collection/councils/BuryCouncil.py,sha256=H7wAxO1nfxkewVoRRolumq8bBJG04siE3jieFH3RGpQ,2632
38
39
  uk_bin_collection/uk_bin_collection/councils/CalderdaleCouncil.py,sha256=OJZcHYlvZDzmBpjjPPm3J8CRK9Twc49vRj7O9c5fyQ4,4971
39
40
  uk_bin_collection/uk_bin_collection/councils/CannockChaseDistrictCouncil.py,sha256=ZamevXN8_Q8mRZOTESWtkb8jVyDXkTczcmhXMAVVSkM,2276
@@ -50,6 +51,7 @@ uk_bin_collection/uk_bin_collection/councils/ConwyCountyBorough.py,sha256=el75qv
50
51
  uk_bin_collection/uk_bin_collection/councils/CornwallCouncil.py,sha256=W7_wWHLbE0cyE90GkNaZHmR7Lbd2Aq48A1hKMUNO-vg,2806
51
52
  uk_bin_collection/uk_bin_collection/councils/CrawleyBoroughCouncil.py,sha256=_BEKZAjlS5Ad5DjyxqAEFSLn8F-KYox0zmn4BXaAD6A,2367
52
53
  uk_bin_collection/uk_bin_collection/councils/CroydonCouncil.py,sha256=QJH27plySbbmoNcLNUXq-hUiFmZ5zBlRS5mzOJgWSK8,11594
54
+ uk_bin_collection/uk_bin_collection/councils/DacorumBoroughCouncil.py,sha256=Tm_6pvBPj-6qStbe6-02LXaoCOlnnDvVXAAocGVvf_E,3970
53
55
  uk_bin_collection/uk_bin_collection/councils/DartfordBoroughCouncil.py,sha256=SPirUUoweMwX5Txtsr0ocdcFtKxCQ9LhzTTJN20tM4w,1550
54
56
  uk_bin_collection/uk_bin_collection/councils/DerbyshireDalesDistrictCouncil.py,sha256=MQC1-jXezXczrxTcvPQvkpGgyyAbzSKlX38WsmftHak,4007
55
57
  uk_bin_collection/uk_bin_collection/councils/DoncasterCouncil.py,sha256=b7pxoToXu6dBBYXsXmlwfPXE8BjHxt0hjCOBNlNgvX8,3118
@@ -92,6 +94,7 @@ uk_bin_collection/uk_bin_collection/councils/LeedsCityCouncil.py,sha256=iSZApZ9o
92
94
  uk_bin_collection/uk_bin_collection/councils/LisburnCastlereaghCityCouncil.py,sha256=vSOzdEwp9ZeUhed7E3eVv9ReD-2XgbSkpyAbVnfc-Gk,3309
93
95
  uk_bin_collection/uk_bin_collection/councils/LiverpoolCityCouncil.py,sha256=n17OqZrCGrPrnxGUfHc-RGkb4oJ9Bx6uUWiLdzxfQlY,2587
94
96
  uk_bin_collection/uk_bin_collection/councils/LondonBoroughHounslow.py,sha256=UOeiOxGMvVMm2UFaqjmQpm7vxzqJNSSN8LM9lAUjs2c,3021
97
+ uk_bin_collection/uk_bin_collection/councils/LondonBoroughLambeth.py,sha256=r9D5lHe5kIRStCd5lRIax16yhb4KTFzzfYEFv1bacWw,2009
95
98
  uk_bin_collection/uk_bin_collection/councils/LondonBoroughRedbridge.py,sha256=A_6Sis5hsF53Th04KeadHRasGbpAm6aoaWJ6X8eC4Y8,6604
96
99
  uk_bin_collection/uk_bin_collection/councils/MaldonDistrictCouncil.py,sha256=PMVt2XFggttPmbWyrBrHJ-W6R_6-0ux1BkY1kj1IKzg,1997
97
100
  uk_bin_collection/uk_bin_collection/councils/MalvernHillsDC.py,sha256=iQG0EkX2npBicvsGKQRYyBGSBvKVUbKvUvvwrC9xV1A,2100
@@ -102,7 +105,7 @@ uk_bin_collection/uk_bin_collection/councils/MidAndEastAntrimBoroughCouncil.py,s
102
105
  uk_bin_collection/uk_bin_collection/councils/MidSussexDistrictCouncil.py,sha256=AZgC9wmDLEjUOtIFvf0ehF5LHturXTH4DkE3ioPSVBA,6254
103
106
  uk_bin_collection/uk_bin_collection/councils/MiltonKeynesCityCouncil.py,sha256=3olsWa77L34vz-c7NgeGK9xmNuR4Ws_oAk5D4UpIkPw,2005
104
107
  uk_bin_collection/uk_bin_collection/councils/MoleValleyDistrictCouncil.py,sha256=54-autRRGAM4pBxlqmUE6g825rmUF-gRqrcgHL_lkIk,3994
105
- uk_bin_collection/uk_bin_collection/councils/NeathPortTalbotCouncil.py,sha256=2DJ0gK385CWTdJzWyDdmNBTTrwX6F5YbIXg7di2oXDQ,5506
108
+ uk_bin_collection/uk_bin_collection/councils/NeathPortTalbotCouncil.py,sha256=V9URKAv3dA_deYmStL2Nmn4GbVCM-tU2qnKobivmGew,5583
106
109
  uk_bin_collection/uk_bin_collection/councils/NewForestCouncil.py,sha256=ylTn9KmWITtaO9_Z8kJCN2w2ALfhrfGt3SeJ78lgw7M,5391
107
110
  uk_bin_collection/uk_bin_collection/councils/NewarkAndSherwoodDC.py,sha256=lAleYfCGUWCKOi7Ye_cjgfpI3pWwTcFctlYmh0hjebM,2140
108
111
  uk_bin_collection/uk_bin_collection/councils/NewcastleCityCouncil.py,sha256=eJMX10CG9QO7FRhHSmUDL-jO_44qoK3_1ztNTAXhkbw,2085
@@ -129,7 +132,7 @@ uk_bin_collection/uk_bin_collection/councils/ReigateAndBansteadBoroughCouncil.py
129
132
  uk_bin_collection/uk_bin_collection/councils/RenfrewshireCouncil.py,sha256=VlWm-w4d-UchoENe_hCTCGlfSHiMlS4wNEeMvxuNR2U,5109
130
133
  uk_bin_collection/uk_bin_collection/councils/RhonddaCynonTaffCouncil.py,sha256=wInyVG_0wRrX_dRO9qbAzPhlXDseXapj2zQhsISw8gg,3233
131
134
  uk_bin_collection/uk_bin_collection/councils/RochdaleCouncil.py,sha256=UTSwSw515VehGn4xkjjRhUlzS4lDj4hgna6y-4VW3uM,2379
132
- uk_bin_collection/uk_bin_collection/councils/RochfordCouncil.py,sha256=_J2Eb5gec2Gmv_QYzKHT6sQleDnIHsn-5qf-T6TM9X0,2613
135
+ uk_bin_collection/uk_bin_collection/councils/RochfordCouncil.py,sha256=7J83wWoFYukSnVgpV_bDLZlAbeTi94xgjMe9x_su7m8,2646
133
136
  uk_bin_collection/uk_bin_collection/councils/RotherhamCouncil.py,sha256=sjZ00iBdqn2kOvGsWyMpY3h-HWmfC_K6LQ2wz1X5WGg,1761
134
137
  uk_bin_collection/uk_bin_collection/councils/RugbyBoroughCouncil.py,sha256=a5ySLmFvvY56QMA7-bk6MVBxRp5tPBIBg4navH0eYas,4306
135
138
  uk_bin_collection/uk_bin_collection/councils/RushcliffeBoroughCouncil.py,sha256=wMtiYRirT585vtsEOIyXHugk7aEj3pvyVWBaAePdqtE,4005
@@ -184,16 +187,18 @@ uk_bin_collection/uk_bin_collection/councils/WestNorthamptonshireCouncil.py,sha2
184
187
  uk_bin_collection/uk_bin_collection/councils/WestSuffolkCouncil.py,sha256=HMFWxM7VMhBuC7iubNGbZYEoCVWi--gRHDJMVdPPFOM,2633
185
188
  uk_bin_collection/uk_bin_collection/councils/WiganBoroughCouncil.py,sha256=3gqFA4-BVx_In6QOu3KUNqPN4Fkn9iMlZTeopMK9p6A,3746
186
189
  uk_bin_collection/uk_bin_collection/councils/WiltshireCouncil.py,sha256=it2Oh5Kmq3lD30gAZgk2bzZPNCtJcFHyQO1NgOQtfvU,5653
190
+ uk_bin_collection/uk_bin_collection/councils/WinchesterCityCouncil.py,sha256=W2k00N5n9-1MzjMEqsNjldsQdOJPEPMjK7OGSinZm5Y,4335
187
191
  uk_bin_collection/uk_bin_collection/councils/WindsorAndMaidenheadCouncil.py,sha256=7Qhznj95ktAQjpWm5C8pbD5UcvfXm7Mwb7_DQxwjGSM,1777
188
192
  uk_bin_collection/uk_bin_collection/councils/WokingBoroughCouncil.py,sha256=37igH9g0xe4XIhRhcJ-ZJBU8MxTp5yzgpadWbdE33Yg,5205
193
+ uk_bin_collection/uk_bin_collection/councils/WokinghamBoroughCouncil.py,sha256=qQOg5HjvHMTRuy3SVX8vbXhSqYBFZmN-htk_Dtoae0M,4126
189
194
  uk_bin_collection/uk_bin_collection/councils/WychavonDistrictCouncil.py,sha256=YuZdzEW0CZLwusm1VQcGRIKXAab_UDFLaCnN60itt_E,5776
190
195
  uk_bin_collection/uk_bin_collection/councils/WyreCouncil.py,sha256=zDDa7n4K_zm5PgDL08A26gD9yOOsOhuexI3x2seaBF4,3511
191
196
  uk_bin_collection/uk_bin_collection/councils/YorkCouncil.py,sha256=I2kBYMlsD4bIdsvmoSzBjJAvTTi6yPfJa8xjJx1ys2w,1490
192
197
  uk_bin_collection/uk_bin_collection/councils/council_class_template/councilclasstemplate.py,sha256=4s9ODGPAwPqwXc8SrTX5Wlfmizs3_58iXUtHc4Ir86o,1162
193
198
  uk_bin_collection/uk_bin_collection/create_new_council.py,sha256=m-IhmWmeWQlFsTZC4OxuFvtw5ZtB8EAJHxJTH4O59lQ,1536
194
199
  uk_bin_collection/uk_bin_collection/get_bin_data.py,sha256=YvmHfZqanwrJ8ToGch34x-L-7yPe31nB_x77_Mgl_vo,4545
195
- uk_bin_collection-0.92.0.dist-info/LICENSE,sha256=vABBUOzcrgfaTKpzeo-si9YVEun6juDkndqA8RKdKGs,1071
196
- uk_bin_collection-0.92.0.dist-info/METADATA,sha256=I8N4Q2TwfwCtMksjuMddP2UDKku-BH3LrMasukfm4Z0,16642
197
- uk_bin_collection-0.92.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
198
- uk_bin_collection-0.92.0.dist-info/entry_points.txt,sha256=36WCSGMWSc916S3Hi1ZkazzDKHaJ6CD-4fCEFm5MIao,90
199
- uk_bin_collection-0.92.0.dist-info/RECORD,,
200
+ uk_bin_collection-0.94.0.dist-info/LICENSE,sha256=vABBUOzcrgfaTKpzeo-si9YVEun6juDkndqA8RKdKGs,1071
201
+ uk_bin_collection-0.94.0.dist-info/METADATA,sha256=E6gDNbSQbwplbWVHaTuOwLPKE3XsRLRIFQRMLCTn7QI,16843
202
+ uk_bin_collection-0.94.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
203
+ uk_bin_collection-0.94.0.dist-info/entry_points.txt,sha256=36WCSGMWSc916S3Hi1ZkazzDKHaJ6CD-4fCEFm5MIao,90
204
+ uk_bin_collection-0.94.0.dist-info/RECORD,,