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.
- uk_bin_collection/tests/input.json +137 -66
- uk_bin_collection/uk_bin_collection/councils/AdurAndWorthingCouncils.py +69 -24
- uk_bin_collection/uk_bin_collection/councils/AngusCouncil.py +149 -0
- uk_bin_collection/uk_bin_collection/councils/BarkingDagenham.py +11 -2
- uk_bin_collection/uk_bin_collection/councils/BexleyCouncil.py +24 -47
- uk_bin_collection/uk_bin_collection/councils/BrightonandHoveCityCouncil.py +11 -2
- uk_bin_collection/uk_bin_collection/councils/BroadlandDistrictCouncil.py +21 -6
- uk_bin_collection/uk_bin_collection/councils/CharnwoodBoroughCouncil.py +14 -3
- uk_bin_collection/uk_bin_collection/councils/CheltenhamBoroughCouncil.py +12 -12
- uk_bin_collection/uk_bin_collection/councils/CheshireEastCouncil.py +24 -2
- uk_bin_collection/uk_bin_collection/councils/ChichesterDistrictCouncil.py +105 -53
- uk_bin_collection/uk_bin_collection/councils/ChorleyCouncil.py +4 -0
- uk_bin_collection/uk_bin_collection/councils/CroydonCouncil.py +114 -261
- uk_bin_collection/uk_bin_collection/councils/DartfordBoroughCouncil.py +13 -0
- uk_bin_collection/uk_bin_collection/councils/DoverDistrictCouncil.py +17 -2
- uk_bin_collection/uk_bin_collection/councils/EastDevonDC.py +14 -1
- uk_bin_collection/uk_bin_collection/councils/EastbourneBoroughCouncil.py +76 -0
- uk_bin_collection/uk_bin_collection/councils/EastleighBoroughCouncil.py +59 -45
- uk_bin_collection/uk_bin_collection/councils/EnvironmentFirst.py +2 -0
- uk_bin_collection/uk_bin_collection/councils/EppingForestDistrictCouncil.py +47 -15
- uk_bin_collection/uk_bin_collection/councils/FermanaghOmaghDistrictCouncil.py +102 -0
- uk_bin_collection/uk_bin_collection/councils/GlasgowCityCouncil.py +13 -1
- uk_bin_collection/uk_bin_collection/councils/GuildfordCouncil.py +2 -3
- uk_bin_collection/uk_bin_collection/councils/HerefordshireCouncil.py +13 -2
- uk_bin_collection/uk_bin_collection/councils/HuntingdonDistrictCouncil.py +18 -4
- uk_bin_collection/uk_bin_collection/councils/LewesDistrictCouncil.py +76 -0
- uk_bin_collection/uk_bin_collection/councils/LiverpoolCityCouncil.py +16 -4
- uk_bin_collection/uk_bin_collection/councils/MaidstoneBoroughCouncil.py +42 -47
- uk_bin_collection/uk_bin_collection/councils/NewhamCouncil.py +13 -6
- uk_bin_collection/uk_bin_collection/councils/NorthEastDerbyshireDistrictCouncil.py +61 -39
- uk_bin_collection/uk_bin_collection/councils/NorthSomersetCouncil.py +14 -9
- uk_bin_collection/uk_bin_collection/councils/NorthTynesideCouncil.py +2 -2
- uk_bin_collection/uk_bin_collection/councils/NorthumberlandCouncil.py +50 -14
- uk_bin_collection/uk_bin_collection/councils/SloughBoroughCouncil.py +140 -0
- uk_bin_collection/uk_bin_collection/councils/SouthRibbleCouncil.py +115 -65
- uk_bin_collection/uk_bin_collection/councils/StokeOnTrentCityCouncil.py +10 -5
- uk_bin_collection/uk_bin_collection/councils/TewkesburyBoroughCouncil.py +40 -0
- uk_bin_collection/uk_bin_collection/councils/TorbayCouncil.py +1 -3
- uk_bin_collection/uk_bin_collection/councils/WakefieldCityCouncil.py +3 -0
- {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/METADATA +1 -1
- {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/RECORD +44 -38
- {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/LICENSE +0 -0
- {uk_bin_collection-0.151.0.dist-info → uk_bin_collection-0.152.1.dist-info}/WHEEL +0 -0
- {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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
23
|
-
|
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
|
-
|
27
|
-
|
28
|
-
soup.prettify()
|
37
|
+
# Data to return
|
38
|
+
data = {"bins": []}
|
29
39
|
|
30
|
-
|
31
|
-
|
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
|
-
|
34
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
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
|
-
|
53
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
63
|
-
|
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
|
-
|
70
|
-
|
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,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.
|
6
|
+
from selenium.webdriver.common.keys import Keys
|
8
7
|
from selenium.webdriver.support import expected_conditions as EC
|
9
|
-
from
|
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
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
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
|
-
|
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(), '{
|
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
|
-
|
20
|
-
soup.
|
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
|
-
|
22
|
-
soup.
|
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
|