Skip to content

Commit

Permalink
Merge pull request #1132 from m26dvd/master
Browse files Browse the repository at this point in the history
  • Loading branch information
robbrad authored Jan 7, 2025
2 parents 49075ed + 2ce01ed commit edcb63f
Show file tree
Hide file tree
Showing 14 changed files with 735 additions and 152 deletions.
53 changes: 47 additions & 6 deletions uk_bin_collection/tests/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@
"wiki_name": "Breckland Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BrentCouncil": {
"house_number": "25",
"postcode": "HA3 0QU",
"url": "https://recyclingservices.brent.gov.uk/waste",
"wiki_name": "Brent Council",
"wiki_note": "Pass the house number and postcode in their respective parameters."
},
"BrightonandHoveCityCouncil": {
"house_number": "44 Carden Avenue, Brighton, BN1 8NE",
"postcode": "BN1 8NE",
Expand Down Expand Up @@ -440,7 +447,7 @@
"uprn": "100110734613",
"url": "https://www.copeland.gov.uk",
"wiki_name": "Copeland Borough Council",
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
"wiki_note": "*****This has now been replaced by Cumberland Council****"
},
"CornwallCouncil": {
"skip_get_url": true,
Expand Down Expand Up @@ -480,6 +487,12 @@
"wiki_name": "Croydon Council",
"wiki_note": "Pass the house number and postcode in their respective parameters."
},
"CumberlandCouncil": {
"uprn": "100110734613",
"url": "https://waste.cumberland.gov.uk",
"wiki_name": "Cumberland Borough Council",
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"CumberlandAllerdaleCouncil": {
"house_number": "2",
"postcode": "CA13 0DE",
Expand All @@ -502,6 +515,12 @@
"wiki_name": "Dartford Borough Council",
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"DenbighshireCouncil": {
"url": "https://www.denbighshire.gov.uk/",
"uprn": "200004299351",
"wiki_name": "Denbighshire Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"DerbyCityCouncil": {
"url": "https://www.derby.gov.uk",
"uprn": "10010684240",
Expand Down Expand Up @@ -544,6 +563,12 @@
"wiki_name": "Dudley Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"DundeeCityCouncil": {
"url": "https://www.dundeecity.gov.uk/",
"uprn": "9059043390",
"wiki_name": "Dundee City Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"DurhamCouncil": {
"skip_get_url": true,
"uprn": "200003218818",
Expand Down Expand Up @@ -799,6 +824,12 @@
"wiki_name": "Guildford Council",
"wiki_note": "If the bin day is 'today' then the collectionDate will only show today's date if before 7 AM; else the date will be in 'previousCollectionDate'. To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"GwyneddCouncil": {
"url": "https://diogel.gwynedd.llyw.cymru",
"uprn": "10070350463",
"wiki_name": "Gwynedd Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"HackneyCouncil": {
"house_number": "101",
"postcode": "N16 9AS",
Expand Down Expand Up @@ -930,13 +961,11 @@
"wiki_note": "Follow the instructions [here](https://waste-services.kingston.gov.uk/waste) until the \"Your bin days\" page, then copy the URL and replace the URL in the command."
},
"KirkleesCouncil": {
"house_number": "24",
"postcode": "HD7 5DX",
"uprn": "83002937",
"skip_get_url": true,
"url": "https://www.kirklees.gov.uk/beta/your-property-bins-recycling/your-bins",
"web_driver": "http://selenium:4444",
"wiki_name": "Kirklees Council",
"wiki_note": "Pass the house number and postcode in their respective parameters. This parser requires a Selenium webdriver."
"wiki_note": "Provide your UPRN. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"KnowsleyMBCouncil": {
"house_number": "22",
Expand Down Expand Up @@ -1319,7 +1348,7 @@
"house_number": "22",
"postcode": "NE46 1UQ",
"skip_get_url": true,
"url": "https://www.northumberland.gov.uk/Waste/Bins/Bin-Calendars.aspx",
"url": "https://www.northumberland.gov.uk/Waste/Household-waste/Household-bin-collections/Bin-Calendars.aspx",
"web_driver": "http://selenium:4444",
"wiki_name": "Northumberland Council",
"wiki_note": "Pass the house number and postcode in their respective parameters. This parser requires a Selenium webdriver."
Expand All @@ -1338,6 +1367,12 @@
"house_number": "Newdigate Road",
"wiki_note": "Pass the name of the street ONLY in the house number parameter, wrapped in double quotes. Street name must match exactly as it appears on the council's website."
},
"OadbyAndWigstonBoroughCouncil": {
"url": "https://my.oadby-wigston.gov.uk",
"uprn": "10010149102",
"wiki_name": "Oadby & Wigston Borough Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"OldhamCouncil": {
"url": "https://portal.oldham.gov.uk/bincollectiondates/details?uprn=422000033556",
"wiki_name": "Oldham Council",
Expand Down Expand Up @@ -1979,6 +2014,12 @@
"wiki_name": "West Berkshire Council",
"wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
},
"WestDunbartonshireCouncil": {
"url": "https://www.west-dunbarton.gov.uk/",
"uprn": "129001383",
"wiki_name": "West Dunbartonshire Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"WestLancashireBoroughCouncil": {
"url": "https://www.westlancs.gov.uk",
"uprn": "10012343339",
Expand Down
115 changes: 115 additions & 0 deletions uk_bin_collection/uk_bin_collection/councils/BrentCouncil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from time import sleep

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:
data = {"bins": []}
user_postcode = kwargs.get("postcode")
user_paon = kwargs.get("paon")
check_postcode(user_postcode)
check_paon(user_paon)

URI = "https://recyclingservices.brent.gov.uk/waste"

payload = {"postcode": user_postcode}

s = requests.Session()

# Make the POST request
response = s.post(URI, data=payload)

# Make a BS4 object
soup = BeautifulSoup(response.content, features="html.parser")

address_list = soup.find_all("option")

current_year = datetime.now().year
next_year = current_year + 1

for address in address_list:
if user_paon in (address.text):
address_id = address.get("value")
URI = f"https://recyclingservices.brent.gov.uk/waste/{address_id}"

counter = 0
r = s.get(URI)
while "Loading your bin days..." in r.text:
counter = counter + 1
if counter == 20:
return data
sleep(2)
r = s.get(URI)

r.raise_for_status()

soup = BeautifulSoup(r.content, features="html.parser")

wastecollections = soup.find("div", {"class": "waste__collections"})

# Find all waste service sections
waste_services = wastecollections.find_all(
"h3", class_="govuk-heading-m waste-service-name"
)

for service in waste_services:
# Get the collection type (e.g., Rubbish, Recycling)
collection_type = (service.get_text(strip=True)).split("\n")[0]

# Find the sibling container holding details
service_details = service.find_next(
"dl", class_="govuk-summary-list"
)

if service_details:

# Extract next collection date
next_collection_row = service_details.find(
"dt", string="Next collection"
)
next_collection = (
next_collection_row.find_next_sibling("dd").get_text(
strip=True
)
if next_collection_row
else "Unknown"
)

# Parse dates into standard dd/mm/yyyy format
next_collection_date = datetime.strptime(
remove_ordinal_indicator_from_date_string(next_collection),
"%A, %d %B",
)

if (datetime.now().month == 12) and (
next_collection.month == 1
):
next_collection_date = next_collection_date.replace(
year=next_year
)
else:
next_collection_date = next_collection_date.replace(
year=current_year
)

dict_data = {
"type": collection_type.strip(),
"collectionDate": next_collection_date.strftime(
date_format
),
}
data["bins"].append(dict_data)

return data
13 changes: 9 additions & 4 deletions uk_bin_collection/uk_bin_collection/councils/CornwallCouncil.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from bs4 import BeautifulSoup
from dateutil.relativedelta import relativedelta

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


# import the wonderful Beautiful Soup and the URL grabber
Expand Down Expand Up @@ -52,9 +53,13 @@ def parse_data(self, page: str, **kwargs) -> dict:

for item in soup.find_all("div", class_="collection text-center service"):
bin_type = item.contents[1].text + " bin"
collection_date = datetime.strptime(item.contents[5].text, "%d %b").replace(
year=curr_date.year
)
try:
collection_date = datetime.strptime(
item.contents[5].text, "%d %b"
).replace(year=curr_date.year)
except:
continue

if curr_date.month == 12 and collection_date.month == 1:
collection_date = collection_date + relativedelta(years=1)
collections.append((bin_type, collection_date))
Expand Down
96 changes: 96 additions & 0 deletions uk_bin_collection/uk_bin_collection/councils/CumberlandCouncil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
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 = "https://waste.cumberland.gov.uk/renderform?t=25&k=E43CEB1FB59F859833EF2D52B16F3F4EBE1CAB6A"

s = requests.Session()

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

# Make a BS4 object
soup = BeautifulSoup(response.content, features="html.parser")

# print(soup)

token = (soup.find("input", {"name": "__RequestVerificationToken"})).get(
"value"
)

formguid = (soup.find("input", {"name": "FormGuid"})).get("value")

# print(token)
# print(formguid)

headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "https://waste.cumberland.gov.uk",
"Referer": "https://waste.cumberland.gov.uk/renderform?t=25&k=E43CEB1FB59F859833EF2D52B16F3F4EBE1CAB6A",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 OPR/98.0.0.0",
"X-Requested-With": "XMLHttpRequest",
}

payload = {
"__RequestVerificationToken": token,
"FormGuid": formguid,
"ObjectTemplateID": "25",
"Trigger": "submit",
"CurrentSectionID": "33",
"TriggerCtl": "",
"FF265": f"U{user_uprn}",
"FF265lbltxt": "Please select your address",
}

# print(payload)

response = s.post(
"https://waste.cumberland.gov.uk/renderform/Form",
headers=headers,
data=payload,
)

soup = BeautifulSoup(response.content, features="html.parser")
for row in soup.find_all("div", class_="resirow"):
# Extract the type of collection (e.g., Recycling, Refuse)
collection_type_div = row.find("div", class_="col")
collection_type = (
collection_type_div.get("class")[1]
if collection_type_div
else "Unknown"
)

# Extract the collection date
date_div = row.find("div", style="width:360px;")
collection_date = date_div.text.strip() if date_div else "Unknown"

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

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

return bindata
Loading

0 comments on commit edcb63f

Please sign in to comment.