uk_bin_collection 0.114.6__py3-none-any.whl → 0.116.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.
Files changed (20) hide show
  1. uk_bin_collection/tests/input.json +84 -21
  2. uk_bin_collection/uk_bin_collection/councils/AntrimAndNewtonabbeyCouncil.py +53 -0
  3. uk_bin_collection/uk_bin_collection/councils/ArgyllandButeCouncil.py +67 -0
  4. uk_bin_collection/uk_bin_collection/councils/AshfieldDistrictCouncil.py +105 -0
  5. uk_bin_collection/uk_bin_collection/councils/BaberghDistrictCouncil.py +132 -0
  6. uk_bin_collection/uk_bin_collection/councils/BradfordMDC.py +36 -6
  7. uk_bin_collection/uk_bin_collection/councils/BroxbourneCouncil.py +71 -0
  8. uk_bin_collection/uk_bin_collection/councils/CheshireWestAndChesterCouncil.py +95 -113
  9. uk_bin_collection/uk_bin_collection/councils/DerbyCityCouncil.py +55 -0
  10. uk_bin_collection/uk_bin_collection/councils/GraveshamBoroughCouncil.py +122 -0
  11. uk_bin_collection/uk_bin_collection/councils/HertsmereBoroughCouncil.py +161 -0
  12. uk_bin_collection/uk_bin_collection/councils/MidSuffolkDistrictCouncil.py +132 -0
  13. uk_bin_collection/uk_bin_collection/councils/MiltonKeynesCityCouncil.py +60 -41
  14. uk_bin_collection/uk_bin_collection/councils/WarringtonBoroughCouncil.py +50 -0
  15. uk_bin_collection/uk_bin_collection/councils/WestLancashireBoroughCouncil.py +114 -0
  16. {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/METADATA +1 -1
  17. {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/RECORD +20 -9
  18. {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/LICENSE +0 -0
  19. {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/WHEEL +0 -0
  20. {uk_bin_collection-0.114.6.dist-info → uk_bin_collection-0.116.0.dist-info}/entry_points.txt +0 -0
@@ -12,6 +12,12 @@
12
12
  "wiki_name": "Adur and Worthing Councils",
13
13
  "wiki_note": "Replace XXXXXXXX with your UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find it."
14
14
  },
15
+ "AntrimAndNewtonabbeyCouncil": {
16
+ "url": "https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule/?Id=643",
17
+ "wiki_command_url_override": "https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule/?Id=XXXX",
18
+ "wiki_name": "Antrim & Newtonabbey Council",
19
+ "wiki_note": "Navigate to [https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule] and search for your street name. Use the URL with the ID to replace XXXXXXXX with your specific ID."
20
+ },
15
21
  "ArdsAndNorthDownCouncil": {
16
22
  "url": "https://www.ardsandnorthdown.gov.uk",
17
23
  "wiki_command_url_override": "https://www.ardsandnorthdown.gov.uk",
@@ -26,6 +32,13 @@
26
32
  "wiki_name": "Ards and North Down Council",
27
33
  "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
28
34
  },
35
+ "ArgyllandButeCouncil": {
36
+ "uprn": "125061759",
37
+ "skip_get_url": true,
38
+ "url": "https://www.argyll-bute.gov.uk",
39
+ "wiki_name": "Argyll and Bute Council",
40
+ "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
41
+ },
29
42
  "ArmaghBanbridgeCraigavonCouncil": {
30
43
  "url": "https://www.armaghbanbridgecraigavon.gov.uk/",
31
44
  "wiki_command_url_override": "https://www.armaghbanbridgecraigavon.gov.uk/",
@@ -42,13 +55,13 @@
42
55
  "wiki_name": "Arun Council",
43
56
  "wiki_note": "Pass the house name/number and postcode in their respective parameters, both wrapped in double quotes. This parser requires a Selenium webdriver."
44
57
  },
45
- "AshfordBoroughCouncil": {
46
- "url": "https://ashford.gov.uk",
47
- "wiki_command_url_override": "https://ashford.gov.uk",
48
- "postcode": "TN23 7SP",
49
- "uprn": "100060777899",
50
- "wiki_name": "Ashford Borough Council",
51
- "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
58
+ "AshfieldDistrictCouncil": {
59
+ "url": "https://www.ashfield.gov.uk",
60
+ "postcode": "NG16 6RH",
61
+ "house_number": "1",
62
+ "web_driver": "http://selenium:4444",
63
+ "wiki_name": "Ashfield District Council",
64
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters, both wrapped in double quotes. This parser requires a Selenium webdriver"
52
65
  },
53
66
  "AshfordBoroughCouncil": {
54
67
  "url": "https://ashford.gov.uk",
@@ -65,6 +78,13 @@
65
78
  "wiki_name": "Aylesbury Vale Council (Buckinghamshire)",
66
79
  "wiki_note": "To get the UPRN, please use [FindMyAddress](https://www.findmyaddress.co.uk/search). Returns all published collections in the past, present, future."
67
80
  },
81
+ "BaberghDistrictCouncil": {
82
+ "skip_get_url": true,
83
+ "house_number": "Monday",
84
+ "url": "https://www.babergh.gov.uk",
85
+ "wiki_name": "Babergh District Council",
86
+ "wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday"
87
+ },
68
88
  "BCPCouncil": {
69
89
  "skip_get_url": true,
70
90
  "uprn": "100040810214",
@@ -230,6 +250,13 @@
230
250
  "wiki_name": "Bromsgrove District Council",
231
251
  "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
232
252
  },
253
+ "BroxbourneCouncil": {
254
+ "url": "https://www.broxbourne.gov.uk",
255
+ "uprn": "148048608",
256
+ "postcode": "EN8 7FL",
257
+ "wiki_name": "Broxbourne Council",
258
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
259
+ },
233
260
  "BroxtoweBoroughCouncil": {
234
261
  "postcode": "NG16 2LY",
235
262
  "skip_get_url": true,
@@ -322,26 +349,19 @@
322
349
  "wiki_note": "Both the UPRN and a one-line address are passed in the URL, which needs to be wrapped in double quotes. The one-line address is made up of the house number, street name, and postcode. Use the form [here](https://online.cheshireeast.gov.uk/mycollectionday/) to find them, then take the first line and postcode and replace all spaces with `%20`."
323
350
  },
324
351
  "CheshireWestAndChesterCouncil": {
325
- "house_number": "Hill View House",
326
- "postcode": "CH3 9ER",
352
+ "uprn": "100012346655",
327
353
  "skip_get_url": true,
328
- "url": "https://www.cheshirewestandchester.gov.uk/residents/waste-and-recycling/your-bin-collection/collection-day",
354
+ "url": "https://my.cheshirewestandchester.gov.uk",
329
355
  "wiki_name": "Cheshire West and Chester Council",
330
- "wiki_note": "Pass the house name/number and postcode in their respective parameters."
356
+ "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
331
357
  },
332
358
  "ChesterfieldBoroughCouncil": {
333
359
  "uprn": "74008234",
334
360
  "skip_get_url": true,
335
- "url": "https://www.cheshirewestandchester.gov.uk/residents/waste-and-recycling/your-bin-collection/collection-day",
361
+ "url": "https://www.chesterfield.gov.uk",
336
362
  "wiki_name": "Chesterfield Borough Council",
337
363
  "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
338
364
  },
339
- "ChesterfieldBoroughCouncil": {
340
- "uprn": "74008234",
341
- "skip_get_url": true,
342
- "url": "https://www.cheshirewestandchester.gov.uk/residents/waste-and-recycling/your-bin-collection/collection-day",
343
- "wiki_name": "Chesterfield Borough Council"
344
- },
345
365
  "ChichesterDistrictCouncil": {
346
366
  "house_number": "7, Plaistow Road, Kirdford, Billingshurst, West Sussex",
347
367
  "postcode": "RH14 0JT",
@@ -436,6 +456,12 @@
436
456
  "wiki_name": "Dartford Borough Council",
437
457
  "wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
438
458
  },
459
+ "DerbyCityCouncil": {
460
+ "url": "https://www.derby.gov.uk",
461
+ "uprn": "10010684240",
462
+ "wiki_name": "Derby City Council",
463
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
464
+ },
439
465
  "DerbyshireDalesDistrictCouncil": {
440
466
  "postcode": "DE4 3AS",
441
467
  "skip_get_url": true,
@@ -689,6 +715,13 @@
689
715
  "wiki_name": "Gloucester City Council",
690
716
  "wiki_note": "Pass the house number, postcode, and UPRN in their respective parameters. This parser requires a Selenium webdriver."
691
717
  },
718
+ "GraveshamBoroughCouncil": {
719
+ "uprn": "100060927046",
720
+ "skip_get_url": true,
721
+ "url": "https://www.gravesham.gov.uk",
722
+ "wiki_name": "Gravesham Borough Council",
723
+ "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
724
+ },
692
725
  "GuildfordCouncil": {
693
726
  "house_number": "THE LODGE, PUTTENHAM HILL HOUSE, PUTTENHAM HILL, PUTTENHAM, GUILDFORD, GU3 1AH",
694
727
  "postcode": "GU3 1AH",
@@ -736,6 +769,15 @@
736
769
  "wiki_name": "Harrogate Borough Council",
737
770
  "wiki_note": "Pass the UPRN, which can be found at [this site](https://secure.harrogate.gov.uk/inmyarea). URL doesn't need to be passed."
738
771
  },
772
+ "HertsmereBoroughCouncil": {
773
+ "house_number": "1",
774
+ "postcode": "WD7 9HZ",
775
+ "skip_get_url": true,
776
+ "url": "https://www.hertsmere.gov.uk",
777
+ "web_driver": "http://selenium:4444",
778
+ "wiki_name": "Hertsmere Borough Council",
779
+ "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
780
+ },
739
781
  "HighlandCouncil": {
740
782
  "url": "https://www.highland.gov.uk",
741
783
  "wiki_command_url_override": "https://www.highland.gov.uk",
@@ -960,6 +1002,13 @@
960
1002
  "wiki_name": "Midlothian Council",
961
1003
  "wiki_note": "Pass the house name/number wrapped in double quotes along with the postcode parameter."
962
1004
  },
1005
+ "MidSuffolkDistrictCouncil": {
1006
+ "skip_get_url": true,
1007
+ "house_number": "Monday",
1008
+ "url": "https://www.midsuffolk.gov.uk",
1009
+ "wiki_name": "Mid Suffolk District Council",
1010
+ "wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday"
1011
+ },
963
1012
  "MidSussexDistrictCouncil": {
964
1013
  "house_number": "OAKLANDS, OAKLANDS ROAD RH16 1SS",
965
1014
  "postcode": "RH16 1SS",
@@ -970,10 +1019,10 @@
970
1019
  "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes. This parser requires a Selenium webdriver."
971
1020
  },
972
1021
  "MiltonKeynesCityCouncil": {
973
- "uprn": "Fullers Slade",
974
- "url": "https://www.milton-keynes.gov.uk/waste-and-recycling/collection-days",
1022
+ "uprn": "25109551",
1023
+ "url": "https://mycouncil.milton-keynes.gov.uk/en/service/Waste_Collection_Round_Checker",
975
1024
  "wiki_name": "Milton Keynes City Council",
976
- "wiki_note": "Pass the name of the estate with the UPRN parameter, wrapped in double quotes."
1025
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
977
1026
  },
978
1027
  "MoleValleyDistrictCouncil": {
979
1028
  "postcode": "RH4 1SJ",
@@ -1664,6 +1713,13 @@
1664
1713
  "wiki_name": "Waltham Forest",
1665
1714
  "wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
1666
1715
  },
1716
+ "WarringtonBoroughCouncil": {
1717
+ "url": "https://www.warrington.gov.uk",
1718
+ "wiki_command_url_override": "https://www.warrington.gov.uk",
1719
+ "uprn": "10094964379",
1720
+ "wiki_name": "Warrington Borough Council",
1721
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
1722
+ },
1667
1723
  "WarwickDistrictCouncil": {
1668
1724
  "url": "https://estates7.warwickdc.gov.uk/PropertyPortal/Property/Recycling/100070263793",
1669
1725
  "wiki_command_url_override": "https://estates7.warwickdc.gov.uk/PropertyPortal/Property/Recycling/XXXXXXXX",
@@ -1715,6 +1771,13 @@
1715
1771
  "wiki_name": "West Berkshire Council",
1716
1772
  "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
1717
1773
  },
1774
+ "WestLancashireBoroughCouncil": {
1775
+ "url": "https://www.westlancs.gov.uk",
1776
+ "uprn": "10012343339",
1777
+ "postcode": "WN8 0HR",
1778
+ "wiki_name": "West Lancashire Borough Council",
1779
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
1780
+ },
1718
1781
  "WestLindseyDistrictCouncil": {
1719
1782
  "house_number": "PRIVATE ACCOMMODATION",
1720
1783
  "postcode": "LN8 2AR",
@@ -0,0 +1,53 @@
1
+ from bs4 import BeautifulSoup
2
+
3
+ from uk_bin_collection.uk_bin_collection.common import *
4
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
5
+
6
+
7
+ # import the wonderful Beautiful Soup and the URL grabber
8
+ class CouncilClass(AbstractGetBinDataClass):
9
+ """
10
+ Concrete classes have to implement all abstract operations of the
11
+ base class. They can also override some operations with a default
12
+ implementation.
13
+ """
14
+
15
+ def parse_data(self, page: str, **kwargs) -> dict:
16
+
17
+ bindata = {"bins": []}
18
+
19
+ soup = BeautifulSoup(page.content, "html.parser")
20
+ soup.prettify
21
+
22
+ collection_divs = soup.select("div.feature-box.bins")
23
+ if not collection_divs:
24
+ raise Exception("No collections found")
25
+
26
+ for collection_div in collection_divs:
27
+ date_p = collection_div.select_one("p.date")
28
+ if not date_p:
29
+ continue
30
+
31
+ # Thu 22 Aug, 2024
32
+ date_ = datetime.strptime(date_p.text.strip(), "%a %d %b, %Y").strftime(
33
+ "%d/%m/%Y"
34
+ )
35
+ bins = collection_div.select("li")
36
+ if not bins:
37
+ continue
38
+ for bin in bins:
39
+ if not bin.text.strip():
40
+ continue
41
+ bin_type = bin.text.strip()
42
+
43
+ dict_data = {
44
+ "type": bin_type,
45
+ "collectionDate": date_,
46
+ }
47
+ bindata["bins"].append(dict_data)
48
+
49
+ bindata["bins"].sort(
50
+ key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
51
+ )
52
+
53
+ return bindata
@@ -0,0 +1,67 @@
1
+ import time
2
+
3
+ import requests
4
+ from bs4 import BeautifulSoup
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
+
20
+ user_uprn = kwargs.get("uprn")
21
+ check_uprn(user_uprn)
22
+ user_uprn = user_uprn.zfill(12)
23
+ bindata = {"bins": []}
24
+
25
+ URI = "https://www.argyll-bute.gov.uk/rubbish-and-recycling/household-waste/bin-collection"
26
+
27
+ data = {"addressSelect": user_uprn}
28
+
29
+ s = requests.session()
30
+ r = s.post(URI, data=data)
31
+ r.raise_for_status()
32
+
33
+ soup = BeautifulSoup(r.content, features="html.parser")
34
+ soup.prettify()
35
+
36
+ # Find the table and extract the rows with bin schedule information
37
+ table = soup.find("table", class_="table table-bordered")
38
+ rows = table.find_all("tr")[1:] # Skip the header row
39
+
40
+ current_year = datetime.now().year
41
+ # Loop through each row and extract the bin type and collection date
42
+ for row in rows:
43
+ cells = row.find_all("td")
44
+ bin_type = cells[0].get_text(strip=True)
45
+ collection_date = cells[1].get_text(strip=True)
46
+
47
+ collection_date = datetime.strptime(
48
+ collection_date,
49
+ "%A %d %B",
50
+ )
51
+
52
+ if collection_date.month == 1:
53
+ collection_date = collection_date.replace(year=current_year + 1)
54
+ else:
55
+ collection_date = collection_date.replace(year=current_year)
56
+
57
+ dict_data = {
58
+ "type": bin_type,
59
+ "collectionDate": collection_date.strftime(date_format),
60
+ }
61
+ bindata["bins"].append(dict_data)
62
+
63
+ bindata["bins"].sort(
64
+ key=lambda x: datetime.strptime(x.get("collectionDate"), date_format)
65
+ )
66
+
67
+ return bindata
@@ -0,0 +1,105 @@
1
+ import time
2
+ from datetime import datetime
3
+
4
+ from bs4 import BeautifulSoup
5
+ from selenium import webdriver
6
+ from selenium.webdriver.common.by import By
7
+ from selenium.webdriver.support import expected_conditions as EC
8
+ from selenium.webdriver.support.wait import WebDriverWait
9
+
10
+ from uk_bin_collection.uk_bin_collection.common import *
11
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
12
+
13
+
14
+ class CouncilClass(AbstractGetBinDataClass):
15
+ """
16
+ Concrete classes have to implement all abstract operations of the
17
+ base class. They can also override some operations with a default
18
+ implementation.
19
+ """
20
+
21
+ def parse_data(self, page: str, **kwargs) -> dict:
22
+ # Get and check UPRN
23
+ user_postcode = kwargs.get("postcode")
24
+ user_paon = kwargs.get("paon")
25
+ check_paon(user_paon)
26
+ check_postcode(user_postcode)
27
+ web_driver = kwargs.get("web_driver")
28
+ headless = kwargs.get("headless")
29
+ bindata = {"bins": []}
30
+
31
+ API_URL = "https://portal.digital.ashfield.gov.uk/w/webpage/raise-case?service=bin_calendar"
32
+
33
+ # Create Selenium webdriver
34
+ driver = create_webdriver(web_driver, headless, None, __name__)
35
+ driver.get(API_URL)
36
+
37
+ title = WebDriverWait(driver, 10).until(
38
+ EC.presence_of_element_located((By.ID, "sub_page_title"))
39
+ )
40
+
41
+ # Wait for the postcode field to appear then populate it
42
+ WebDriverWait(driver, 10).until(
43
+ EC.presence_of_element_located(
44
+ (By.CSS_SELECTOR, "input.relation_path_type_ahead_search")
45
+ )
46
+ )
47
+
48
+ inputElement_postcode = WebDriverWait(driver, 10).until(
49
+ EC.presence_of_element_located(
50
+ (By.CSS_SELECTOR, "input.relation_path_type_ahead_search")
51
+ )
52
+ )
53
+ inputElement_postcode.clear()
54
+ inputElement_postcode.send_keys(user_postcode)
55
+
56
+ # Wait for the 'Select your property' dropdown to appear and select the first result
57
+ dropdown = WebDriverWait(driver, 10).until(
58
+ EC.element_to_be_clickable(
59
+ (
60
+ By.CLASS_NAME,
61
+ "result_list ",
62
+ )
63
+ )
64
+ )
65
+
66
+ address_element = (
67
+ WebDriverWait(driver, 10)
68
+ .until(
69
+ EC.element_to_be_clickable(
70
+ (By.XPATH, f"//li[starts-with(@aria-label, '{user_paon}')]")
71
+ )
72
+ )
73
+ .click()
74
+ )
75
+
76
+ search_button = WebDriverWait(driver, 10).until(
77
+ EC.element_to_be_clickable(
78
+ (By.XPATH, "//input[@type='submit' and @value='Search']")
79
+ )
80
+ )
81
+ search_button.click()
82
+
83
+ time.sleep(10)
84
+
85
+ soup = BeautifulSoup(driver.page_source, features="html.parser")
86
+ soup.prettify()
87
+
88
+ # Find the table by class name
89
+ table = soup.find("table", {"class": "table listing table-striped"})
90
+
91
+ # Iterate over each row in the tbody of the table
92
+ for row in table.find("tbody").find_all("tr"):
93
+ # Extract the service, day, and date for each row
94
+ service = row.find_all("td")[0].get_text(strip=True)
95
+ date = row.find_all("td")[2].get_text(strip=True)
96
+
97
+ dict_data = {
98
+ "type": service,
99
+ "collectionDate": datetime.strptime(date, "%a, %d %b %Y").strftime(
100
+ date_format
101
+ ),
102
+ }
103
+ bindata["bins"].append(dict_data)
104
+
105
+ return bindata
@@ -0,0 +1,132 @@
1
+ import re
2
+ import time
3
+
4
+ import requests
5
+ from bs4 import BeautifulSoup
6
+ from selenium.webdriver.common.by import By
7
+ from selenium.webdriver.support import expected_conditions as EC
8
+ from selenium.webdriver.support.ui import Select
9
+ from selenium.webdriver.support.wait import WebDriverWait
10
+
11
+ from uk_bin_collection.uk_bin_collection.common import *
12
+ from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
13
+
14
+
15
+ # import the wonderful Beautiful Soup and the URL grabber
16
+ class CouncilClass(AbstractGetBinDataClass):
17
+ """
18
+ Concrete classes have to implement all abstract operations of the
19
+ base class. They can also override some operations with a default
20
+ implementation.
21
+ """
22
+
23
+ def parse_data(self, page: str, **kwargs) -> dict:
24
+
25
+ collection_day = kwargs.get("paon")
26
+ bindata = {"bins": []}
27
+
28
+ days_of_week = [
29
+ "Monday",
30
+ "Tuesday",
31
+ "Wednesday",
32
+ "Thursday",
33
+ "Friday",
34
+ "Saturday",
35
+ "Sunday",
36
+ ]
37
+
38
+ refusestartDate = datetime(2024, 11, 4)
39
+ recyclingstartDate = datetime(2024, 11, 11)
40
+
41
+ offset_days = days_of_week.index(collection_day)
42
+
43
+ refuse_dates = get_dates_every_x_days(refusestartDate, 14, 28)
44
+ recycling_dates = get_dates_every_x_days(recyclingstartDate, 14, 28)
45
+
46
+ bank_holidays = [
47
+ ("25/12/2024", 2),
48
+ ("26/12/2024", 2),
49
+ ("27/12/2024", 3),
50
+ ("30/12/2024", 1),
51
+ ("31/12/2024", 2),
52
+ ("01/01/2025", 2),
53
+ ("02/01/2025", 2),
54
+ ("03/01/2025", 3),
55
+ ("06/01/2025", 1),
56
+ ("07/01/2025", 1),
57
+ ("08/01/2025", 1),
58
+ ("09/01/2025", 1),
59
+ ("10/01/2025", 1),
60
+ ("18/04/2025", 1),
61
+ ("21/04/2025", 1),
62
+ ("22/04/2025", 1),
63
+ ("23/04/2025", 1),
64
+ ("24/04/2025", 1),
65
+ ("25/04/2025", 1),
66
+ ("05/05/2025", 1),
67
+ ("06/05/2025", 1),
68
+ ("07/05/2025", 1),
69
+ ("08/05/2025", 1),
70
+ ("09/05/2025", 1),
71
+ ("26/05/2025", 1),
72
+ ("27/05/2025", 1),
73
+ ("28/05/2025", 1),
74
+ ("29/05/2025", 1),
75
+ ("30/05/2025", 1),
76
+ ("25/08/2025", 1),
77
+ ("26/08/2025", 1),
78
+ ("27/08/2025", 1),
79
+ ("28/08/2025", 1),
80
+ ("29/08/2025", 1),
81
+ ]
82
+
83
+ for refuseDate in refuse_dates:
84
+
85
+ collection_date = (
86
+ datetime.strptime(refuseDate, "%d/%m/%Y") + timedelta(days=offset_days)
87
+ ).strftime("%d/%m/%Y")
88
+
89
+ holiday_offset = next(
90
+ (value for date, value in bank_holidays if date == collection_date), 0
91
+ )
92
+
93
+ if holiday_offset > 0:
94
+ collection_date = (
95
+ datetime.strptime(collection_date, "%d/%m/%Y")
96
+ + timedelta(days=holiday_offset)
97
+ ).strftime("%d/%m/%Y")
98
+
99
+ dict_data = {
100
+ "type": "Refuse Bin",
101
+ "collectionDate": collection_date,
102
+ }
103
+ bindata["bins"].append(dict_data)
104
+
105
+ for recyclingDate in recycling_dates:
106
+
107
+ collection_date = (
108
+ datetime.strptime(recyclingDate, "%d/%m/%Y")
109
+ + timedelta(days=offset_days)
110
+ ).strftime("%d/%m/%Y")
111
+
112
+ holiday_offset = next(
113
+ (value for date, value in bank_holidays if date == collection_date), 0
114
+ )
115
+
116
+ if holiday_offset > 0:
117
+ collection_date = (
118
+ datetime.strptime(collection_date, "%d/%m/%Y")
119
+ + timedelta(days=holiday_offset)
120
+ ).strftime("%d/%m/%Y")
121
+
122
+ dict_data = {
123
+ "type": "Recycling Bin",
124
+ "collectionDate": collection_date,
125
+ }
126
+ bindata["bins"].append(dict_data)
127
+
128
+ bindata["bins"].sort(
129
+ key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
130
+ )
131
+
132
+ return bindata
@@ -1,3 +1,5 @@
1
+ import re
2
+
1
3
  import requests
2
4
  from bs4 import BeautifulSoup
3
5
 
@@ -89,12 +91,40 @@ class CouncilClass(AbstractGetBinDataClass):
89
91
  )
90
92
  ).strftime(date_format)
91
93
 
92
- # Build data dict for each entry
93
- dict_data = {
94
- "type": bin_type,
95
- "collectionDate": bin_date,
96
- }
97
- data["bins"].append(dict_data)
94
+ # Build data dict for each entry
95
+ dict_data = {
96
+ "type": bin_type,
97
+ "collectionDate": bin_date,
98
+ }
99
+ data["bins"].append(dict_data)
100
+
101
+ for bin in soup.find_all(attrs={"id": re.compile(r"CTID-D0TUYGxO-\d+-A")}):
102
+ dict_data = {
103
+ "type": "General Waste",
104
+ "collectionDate": datetime.strptime(
105
+ bin.text.strip(),
106
+ "%a %b %d %Y",
107
+ ).strftime(date_format),
108
+ }
109
+ data["bins"].append(dict_data)
110
+ for bin in soup.find_all(attrs={"id": re.compile(r"CTID-d3gapLk-\d+-A")}):
111
+ dict_data = {
112
+ "type": "Recycling Waste",
113
+ "collectionDate": datetime.strptime(
114
+ bin.text.strip(),
115
+ "%a %b %d %Y",
116
+ ).strftime(date_format),
117
+ }
118
+ data["bins"].append(dict_data)
119
+ for bin in soup.find_all(attrs={"id": re.compile(r"CTID-L8OidMPA-\d+-A")}):
120
+ dict_data = {
121
+ "type": "Garden Waste (Subscription Only)",
122
+ "collectionDate": datetime.strptime(
123
+ bin.text.strip(),
124
+ "%a %b %d %Y",
125
+ ).strftime(date_format),
126
+ }
127
+ data["bins"].append(dict_data)
98
128
 
99
129
  data["bins"].sort(
100
130
  key=lambda x: datetime.strptime(x.get("collectionDate"), date_format)