Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Council Pack 12 #958

Merged
merged 6 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions uk_bin_collection/tests/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@
"url": "https://www.birmingham.gov.uk/xfp/form/619",
"wiki_name": "Birmingham City Council"
},
"BlabyDistrictCouncil": {
"url": "https://www.blaby.gov.uk",
"wiki_command_url_override": "https://www.blaby.gov.uk",
"uprn": "100030401782",
"wiki_name": "Blaby District Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BlackburnCouncil": {
"skip_get_url": true,
"uprn": "100010733027",
Expand Down Expand Up @@ -189,6 +196,13 @@
"wiki_name": "Bromley Borough Council",
"wiki_note": "Follow the instructions [here](https://recyclingservices.bromley.gov.uk/waste) until the \"Your bin days\" page then copy the URL and replace the URL in the command."
},
"BromsgroveDistrictCouncil": {
"url": "https://www.bromsgrove.gov.uk",
"wiki_command_url_override": "https://www.bromsgrove.gov.uk",
"uprn": "100120584652",
"wiki_name": "Bromsgrove District Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BroxtoweBoroughCouncil": {
"postcode": "NG16 2LY",
"skip_get_url": true,
Expand Down Expand Up @@ -243,6 +257,13 @@
"url": "https://www.cardiff.gov.uk/ENG/resident/Rubbish-and-recycling/When-are-my-bins-collected/Pages/default.aspx",
"wiki_name": "Cardiff Council"
},
"CarmarthenshireCountyCouncil": {
"url": "https://www.carmarthenshire.gov.wales",
"wiki_command_url_override": "https://www.carmarthenshire.gov.wales",
"uprn": "10004859302",
"wiki_name": "Carmarthenshire County Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"CastlepointDistrictCouncil": {
"skip_get_url": true,
"uprn": "4525",
Expand Down Expand Up @@ -416,6 +437,13 @@
"url": "https://www.ealing.gov.uk/site/custom_scripts/WasteCollectionWS/home/FindCollection",
"wiki_name": "Ealing Council"
},
"EastAyrshireCouncil": {
"url": "https://www.east-ayrshire.gov.uk",
"wiki_command_url_override": "https://www.east-ayrshire.gov.uk",
"uprn": "127074727",
"wiki_name": "East Ayrshire Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"EastCambridgeshireCouncil": {
"skip_get_url": true,
"uprn": "10002597178",
Expand All @@ -436,8 +464,8 @@
"wiki_name": "East Herts Council"
},
"EastLindseyDistrictCouncil": {
"house_number": "Raf Coningsby",
"postcode": "LN4 4SY",
"house_number": "1",
"postcode": "PE22 0YD",
"skip_get_url": true,
"url": "https://www.e-lindsey.gov.uk/",
"web_driver": "http://selenium:4444",
Expand Down Expand Up @@ -1106,6 +1134,13 @@
"url": "https://www.salford.gov.uk/bins-and-recycling/bin-collection-days/your-bin-collections",
"wiki_name": "Salford City Council"
},
"SeftonCouncil": {
"house_number": "1",
"postcode": "L20 6GG",
"url": "https://www.sefton.gov.uk",
"wiki_name": "Sefton Council",
"wiki_note": "Pass the postcode and house number in their respective arguments, both wrapped in quotes."
},
"SevenoaksDistrictCouncil": {
"house_number": "60 Hever Road",
"postcode": "TN15 6EB",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import requests
from bs4 import BeautifulSoup

from uk_bin_collection.uk_bin_collection.common import *
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass


# import the wonderful Beautiful Soup and the URL grabber
class CouncilClass(AbstractGetBinDataClass):
"""
Concrete classes have to implement all abstract operations of the
base class. They can also override some operations with a default
implementation.
"""

def parse_data(self, page: str, **kwargs) -> dict:

user_uprn = kwargs.get("uprn")
check_uprn(user_uprn)
bindata = {"bins": []}

URI = f"https://my.blaby.gov.uk/set-location.php?ref={user_uprn}&redirect=collections"

# Make the GET request
response = requests.get(URI)

# Parse the HTML
soup = BeautifulSoup(response.content, "html.parser")

# Find each collection container based on the class "box-item"
for container in soup.find_all(class_="box-item"):

# Get the next collection dates from the <p> tag containing <strong>
dates_tag = (
container.find("p", string=lambda text: "Next" in text)
.find_next("p")
.find("strong")
)
collection_dates = (
dates_tag.text.strip().split(", and then ")
if dates_tag
else "No dates found"
)

for collection_date in collection_dates:
dict_data = {
"type": container.find("h2").text.strip(),
"collectionDate": collection_date,
}
bindata["bins"].append(dict_data)

bindata["bins"].sort(
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
)

return bindata
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import requests
from bs4 import BeautifulSoup

from uk_bin_collection.uk_bin_collection.common import *
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass


# import the wonderful Beautiful Soup and the URL grabber
class CouncilClass(AbstractGetBinDataClass):
"""
Concrete classes have to implement all abstract operations of the
base class. They can also override some operations with a default
implementation.
"""

def parse_data(self, page: str, **kwargs) -> dict:

user_uprn = kwargs.get("uprn")
check_uprn(user_uprn)
bindata = {"bins": []}

URI = f"https://bincollections.bromsgrove.gov.uk/BinCollections/Details?uprn={user_uprn}"

# Make the GET request
response = requests.get(URI)

# Parse the HTML
soup = BeautifulSoup(response.content, "html.parser")

# Find each collection container
for container in soup.find_all(class_="collection-container"):
# Get the bin type from the heading
bin_type = container.find(class_="heading").text.strip()

# Get the next collection date from the caption
next_collection = (
container.find(class_="caption")
.text.replace("Next collection ", "")
.strip()
)

dict_data = {
"type": bin_type,
"collectionDate": datetime.strptime(
next_collection,
"%A, %d %B %Y",
).strftime("%d/%m/%Y"),
}
bindata["bins"].append(dict_data)

bindata["bins"].sort(
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
)

return bindata
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import requests
from bs4 import BeautifulSoup

from uk_bin_collection.uk_bin_collection.common import *
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass


# import the wonderful Beautiful Soup and the URL grabber
class CouncilClass(AbstractGetBinDataClass):
"""
Concrete classes have to implement all abstract operations of the
base class. They can also override some operations with a default
implementation.
"""

def parse_data(self, page: str, **kwargs) -> dict:

user_uprn = kwargs.get("uprn")
check_uprn(user_uprn)
bindata = {"bins": []}

URI = f"https://www.carmarthenshire.gov.wales/umbraco/Surface/SurfaceRecycling/Index/?uprn={user_uprn}&lang=en-GB"

# Make the GET request
response = requests.get(URI)

# Parse the HTML
soup = BeautifulSoup(response.content, "html.parser")

# Find each bin collection container
for container in soup.find_all(class_="bin-day-container"):
# Get the bin type based on the class (e.g., Blue, Black, Garden, Nappy)
bin_type = container.get("class")[1] # Second class represents the bin type

# Find the next collection date
date_tag = container.find(class_="font11 text-center")
if date_tag:
collection_date = date_tag.text.strip()
else:
continue

dict_data = {
"type": bin_type,
"collectionDate": datetime.strptime(
collection_date,
"%A %d/%m/%Y",
).strftime("%d/%m/%Y"),
}
bindata["bins"].append(dict_data)

bindata["bins"].sort(
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
)

return bindata
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import requests
from bs4 import BeautifulSoup

from uk_bin_collection.uk_bin_collection.common import *
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass


# import the wonderful Beautiful Soup and the URL grabber
class CouncilClass(AbstractGetBinDataClass):
"""
Concrete classes have to implement all abstract operations of the
base class. They can also override some operations with a default
implementation.
"""

def parse_data(self, page: str, **kwargs) -> dict:

user_uprn = kwargs.get("uprn")
check_uprn(user_uprn)
bindata = {"bins": []}

URI = f"https://www.east-ayrshire.gov.uk/Housing/RubbishAndRecycling/Collection-days/ViewYourRecyclingCalendar.aspx?r={user_uprn}"

# Make the GET request
response = requests.get(URI)

# Parse the HTML
soup = BeautifulSoup(response.content, "html.parser")

# Find each <time> element in the schedule
for entry in soup.find_all("time"):

dict_data = {
"type": entry.find(class_="ScheduleItem").text.strip(),
"collectionDate": datetime.strptime(
entry["datetime"],
"%Y-%m-%d",
).strftime("%d/%m/%Y"),
}
bindata["bins"].append(dict_data)

bindata["bins"].sort(
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
)

return bindata
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,20 @@ def parse_data(self, page: str, **kwargs) -> dict:

# Create Selenium webdriver
driver = create_webdriver(web_driver, headless, None, __name__)
driver.get(
"https://www.e-lindsey.gov.uk/article/6714/Your-Waste-Collection-Days"
)
driver.get("https://www.e-lindsey.gov.uk/mywastecollections")

# Wait for the postcode field to appear then populate it
inputElement_postcode = WebDriverWait(driver, 30).until(
EC.presence_of_element_located(
(By.ID, "WASTECOLLECTIONDAYS202324_LOOKUP_ADDRESSLOOKUPPOSTCODE")
(By.ID, "WASTECOLLECTIONDAYS202425_LOOKUP_ADDRESSLOOKUPPOSTCODE")
)
)
inputElement_postcode.send_keys(user_postcode)

# Click search button
findAddress = WebDriverWait(driver, 10).until(
EC.presence_of_element_located(
(By.ID, "WASTECOLLECTIONDAYS202324_LOOKUP_ADDRESSLOOKUPSEARCH")
(By.ID, "WASTECOLLECTIONDAYS202425_LOOKUP_ADDRESSLOOKUPSEARCH")
)
)
findAddress.click()
Expand All @@ -53,7 +51,7 @@ def parse_data(self, page: str, **kwargs) -> dict:
EC.element_to_be_clickable(
(
By.XPATH,
"//select[@id='WASTECOLLECTIONDAYS202324_LOOKUP_ADDRESSLOOKUPADDRESS']//option[contains(., '"
"//select[@id='WASTECOLLECTIONDAYS202425_LOOKUP_ADDRESSLOOKUPADDRESS']//option[contains(., '"
+ user_paon
+ "')]",
)
Expand All @@ -63,7 +61,7 @@ def parse_data(self, page: str, **kwargs) -> dict:
# Wait for the submit button to appear, then click it to get the collection dates
submit = WebDriverWait(driver, 10).until(
EC.presence_of_element_located(
(By.ID, "WASTECOLLECTIONDAYS202324_LOOKUP_FIELD2_NEXT")
(By.ID, "WASTECOLLECTIONDAYS202425_LOOKUP_FIELD2_NEXT")
)
)
submit.click()
Expand All @@ -77,25 +75,25 @@ def parse_data(self, page: str, **kwargs) -> dict:

# Get collections
for collection in soup.find_all("div", {"class": "waste-result"}):
ptags = collection.find_all("p")
dict_data = {
"type": collection.find("h3").get_text(strip=True),
"collectionDate": datetime.strptime(
remove_ordinal_indicator_from_date_string(
ptags[1]
.get_text()
.replace("The date of your next collection is", "")
.replace(".", "")
.strip()
),
"%A %d %B %Y",
).strftime(date_format),
}
data["bins"].append(dict_data)
collection_date = None
for p_tag in collection.find_all("p"):
if "next collection is" in p_tag.text:
collection_date = p_tag.find("strong").text
break
if collection_date:
dict_data = {
"type": collection.find("h3").get_text(strip=True),
"collectionDate": datetime.strptime(
remove_ordinal_indicator_from_date_string(collection_date),
"%A %d %B %Y",
).strftime(date_format),
}
data["bins"].append(dict_data)

data["bins"].sort(
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
)

except Exception as e:
# Here you can log the exception if needed
print(f"An error occurred: {e}")
Expand Down
Loading