Skip to content

Commit

Permalink
Adding west lothian (mampfes#2920)
Browse files Browse the repository at this point in the history
* West Lothian council collection info

* West Lothian council collection info

* Short-term fix for West Lothian when their Ical generator is broken

* Update README and info

* Remove pycharm-added dashes

* ./update_docu_links.py

---------

Co-authored-by: 5ila5 <[email protected]>
Co-authored-by: 5ila5 <[email protected]>
  • Loading branch information
3 people authored Oct 25, 2024
1 parent 23bb66c commit 21f6744
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2174,6 +2174,7 @@ If your service provider is not listed, feel free to open a [source request issu
- [West Berkshire Council](/doc/source/westberks_gov_uk.md) / westberks.gov.uk
- [West Devon Borough Council](/doc/source/fccenvironment_co_uk.md) / westdevon.gov.uk
- [West Dunbartonshire Council](/doc/source/west_dunbartonshire_gov_uk.md) / west-dunbarton.gov.uk
- [West Lothian Council](/doc/source/westlothian_gov_uk.md) / westlothian.gov.uk
- [West Northamptonshire council](/doc/source/westnorthants_gov_uk.md) / westnorthants.gov.uk
- [West Oxfordshire District Council](/doc/source/westoxon_gov_uk.md) / westoxon.gov.uk
- [West Suffolk Council](/doc/source/westsuffolk_gov_uk.md) / westsuffolk.gov.uk
Expand Down
6 changes: 6 additions & 0 deletions custom_components/waste_collection_schedule/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -13761,6 +13761,12 @@
"default_params": {},
"id": "west_dunbartonshire_gov_uk"
},
{
"title": "West Lothian Council",
"module": "westlothian_gov_uk",
"default_params": {},
"id": "westlothian_gov_uk"
},
{
"title": "West Northamptonshire council",
"module": "westnorthants_gov_uk",
Expand Down
25 changes: 25 additions & 0 deletions custom_components/waste_collection_schedule/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -15940,6 +15940,31 @@
"uprn": "Eine einfache Möglichkeit, Ihre Unique Property Reference Number (UPRN) zu finden, besteht darin, auf https://www.findmyaddress.co.uk/ zu gehen und Ihre Adressdaten einzugeben."
}
},
"args_westlothian_gov_uk": {
"title": "Quelle konfigurieren",
"description": "Konfiguriere deinen Service Provider. Mehr details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westlothian_gov_uk.md",
"data": {
"calendar_title": "Kalender Titel",
"postcode": "PLZ",
"uprn": "UPRN"
},
"data_description": {
"calendar_title": "Ein lesbarerer oder benutzerfreundlicherer Name für den Müllkalender. Wenn nichts angegeben wird, wird der Name der Quelle verwendet.",
"uprn": "Eine einfache Möglichkeit, Ihre Unique Property Reference Number (UPRN) zu finden, besteht darin, auf https://www.findmyaddress.co.uk/ zu gehen und Ihre Adressdaten einzugeben."
}
},
"reconfigure_westlothian_gov_uk": {
"title": "Quelle Neu Konfigurieren",
"description": "Konfiguriere deinen Service Provider. Mehr details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westlothian_gov_uk.md",
"data": {
"calendar_title": "Kalender Titel",
"postcode": "PLZ",
"uprn": "UPRN"
},
"data_description": {
"uprn": "Eine einfache Möglichkeit, Ihre Unique Property Reference Number (UPRN) zu finden, besteht darin, auf https://www.findmyaddress.co.uk/ zu gehen und Ihre Adressdaten einzugeben."
}
},
"args_westnorthants_gov_uk": {
"title": "Quelle konfigurieren",
"description": "Konfiguriere deinen Service Provider. Mehr details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westnorthants_gov_uk.md",
Expand Down
25 changes: 25 additions & 0 deletions custom_components/waste_collection_schedule/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -15953,6 +15953,31 @@
"uprn": "An easy way to discover your Unique Property Reference Number (UPRN) is by going to https://www.findmyaddress.co.uk/ and entering in your address details."
}
},
"args_westlothian_gov_uk": {
"title": "Configure Source",
"description": "Configure your service provider. More details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westlothian_gov_uk.md.",
"data": {
"calendar_title": "Calendar Title",
"postcode": "Postcode",
"uprn": "UPRN"
},
"data_description": {
"calendar_title": "A more readable, or user-friendly, name for the waste calendar. If nothing is provided, the name returned by the source will be used.",
"uprn": "An easy way to discover your Unique Property Reference Number (UPRN) is by going to https://www.findmyaddress.co.uk/ and entering in your address details."
}
},
"reconfigure_westlothian_gov_uk": {
"title": "Reconfigure Source",
"description": "Configure your service provider. More details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westlothian_gov_uk.md.",
"data": {
"calendar_title": "Calendar Title",
"postcode": "Postcode",
"uprn": "UPRN"
},
"data_description": {
"uprn": "An easy way to discover your Unique Property Reference Number (UPRN) is by going to https://www.findmyaddress.co.uk/ and entering in your address details."
}
},
"args_westnorthants_gov_uk": {
"title": "Configure Source",
"description": "Configure your service provider. More details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westnorthants_gov_uk.md.",
Expand Down
25 changes: 25 additions & 0 deletions custom_components/waste_collection_schedule/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -15929,6 +15929,31 @@
"uprn": "Un modo facile per scoprire il tuo Numero di Riferimento Proprietà Unica (UPRN) è andare su https://www.findmyaddress.co.uk/ e inserire i dettagli del tuo indirizzo."
}
},
"args_westlothian_gov_uk": {
"title": "Configurazione Sorgente",
"description": "Compila i campi per ottenere le informazioni sul tuo servizio di raccolta. Maggiori informazioni: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westlothian_gov_uk.md.",
"data": {
"calendar_title": "Nome Calendario",
"postcode": "Codice Postale CAP",
"uprn": "UPRN"
},
"data_description": {
"calendar_title": "Puoi cambiare il nome del calendario della raccolta dei rifiuti, altrimenti di default verra' utilizzato il nome del tuo fornitore di servizi.",
"uprn": "Un modo facile per scoprire il tuo Numero di Riferimento Proprietà Unica (UPRN) è andare su https://www.findmyaddress.co.uk/ e inserire i dettagli del tuo indirizzo."
}
},
"reconfigure_westlothian_gov_uk": {
"title": "Riconfigurazione Sorgente",
"description": "Compila i campi per ottenere le informazioni sul tuo servizio di raccolta. Per maggiori informazioni: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westlothian_gov_uk.md.",
"data": {
"calendar_title": "Nome Calendario",
"postcode": "Codice Postale CAP",
"uprn": "UPRN"
},
"data_description": {
"uprn": "Un modo facile per scoprire il tuo Numero di Riferimento Proprietà Unica (UPRN) è andare su https://www.findmyaddress.co.uk/ e inserire i dettagli del tuo indirizzo."
}
},
"args_westnorthants_gov_uk": {
"title": "Configurazione Sorgente",
"description": "Compila i campi per ottenere le informazioni sul tuo servizio di raccolta. Maggiori informazioni: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/westnorthants_gov_uk.md.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import base64
import json
import re
from urllib.parse import parse_qs, urlparse
import requests
from bs4 import BeautifulSoup
# from dateutil import parser
from datetime import datetime
from waste_collection_schedule.collection import Collection # type: ignore[attr-defined]
from waste_collection_schedule.service.ICS import ICS # type: ignore[attr-defined]


TITLE = "West Lothian Council"
DESCRIPTION = "Source for services for West Lothian"
URL = "https://www.westlothian.gov.uk"
COLLECTION_PAGE_URL = "https://www.westlothian.gov.uk/bin-collections"

TEST_CASES = {
"Test_001": {"postcode": "EH48+4DD", "uprn": "135007799"},
"Test_002": {"postcode": "EH55+8FJ", "uprn": "135051417"},
}

ICON_MAP = {
"Grey": "mdi:trash-can",
"Brown": "mdi:leaf",
"Green": "mdi:glass-fragile",
"Blue": "mdi:note",
}


class Source:
def __init__(self, postcode, uprn):
self._postcode = postcode
self._uprn = str(uprn)
self._ics = ICS()

def fetch(self):
session = requests.Session()
session.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Sec-Fetch-Dest": "document",
"Host": "www.westlothian.gov.uk",
"Sec-Fetch-User": "?1",
"Accept-Language": "en-GB,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Referer": "westlothian.gov.uk",
"Cache-Control": "no-cache",
"DNT": "1"
}
address_page = self.__get_address_page(session)
bin_collection_info_page = self.__get_bin_collection_info_page(
session, address_page, self._postcode, self._uprn
)
bin_collection_info = self.__get_ical_bin_collection_info(bin_collection_info_page)
if bin_collection_info.get("ICALCONTENT", {}).get("value", {}).get("error", None) is not None:
# West Lothian have broken their iCal generation again - use the page content
bin_collection_info = self.__get_immediate_bin_collection_info(bin_collection_info_page)
return self.__generate_collection_entries(bin_collection_info)

def __generate_collection_entries(self, bin_collection_info):
icalContent = bin_collection_info.get("ICALCONTENT")
webpageContent = bin_collection_info.get("PAGE2_1")
if icalContent is not None:
if icalContent['error'] is not None:
raise Exception(icalContent['error'])
# iCal data returned isn't compatible with _ics.convert because it's UNTIL values
# don't specify a timezone, but the ICS module asks for "timezone-aware" parsing.
# So, change the UNTILs to be Z because they're date only and are UK-based.
ics_data = re.sub(
r"UNTIL=([0-9]+)",
lambda m: "UNTIL=" + m.group(1) + "Z",
icalContent["value"],
)
dates = self._ics.convert(ics_data)
entries = []
for d in dates:
icon = ICON_MAP.get(d[1].split(" ")[0])
if icon is None:
icon = ICON_MAP.get(d[1])
entries.append(Collection(d[0], d[1], icon=icon))

return entries
else:
if webpageContent is not None:
collections = json.loads(webpageContent["COLLECTIONS"])
entries = []
for d in collections:
icon = ICON_MAP.get(d['binName'].split(" ")[0])
if icon is None:
icon = ICON_MAP.get(d['binType'])
entries.append(Collection(datetime.strptime(d['nextCollectionISO'], "%Y-%m-%d").date(), d['binType'], icon=icon))

return entries
raise Exception('No entries could be parsed')

def __get_ical_bin_collection_info(self, bin_collection_info_page):
serialized_collection_info_pattern = re.compile(
r'var WLBINCOLLECTIONSerializedVariables = "(.*?)";$',
re.MULTILINE | re.DOTALL,
)
soup = BeautifulSoup(bin_collection_info_page, "html.parser")
script = soup.find("script", text=serialized_collection_info_pattern)
if not script:
raise Exception(
"no script tag cannot find WLBINCOLLECTIONSerializedVariables"
)
match = serialized_collection_info_pattern.search(script.text)
if not match:
raise Exception("no match cannot find WLBINCOLLECTIONSerializedVariables")
serialized_collection_info = match.group(1)
collection_info = json.loads(base64.b64decode(serialized_collection_info))
return collection_info

def __get_immediate_bin_collection_info(self, bin_collection_info_page):
serialized_collection_info_pattern = re.compile(
r'var WLBINCOLLECTIONFormData = "(.*?)";$',
re.MULTILINE | re.DOTALL,
)
soup = BeautifulSoup(bin_collection_info_page, "html.parser")
script = soup.find("script", text=serialized_collection_info_pattern)
if not script:
raise Exception(
"no script tag cannot find WLBINCOLLECTIONFormData"
)
match = serialized_collection_info_pattern.search(script.text)
if not match:
raise Exception("no match cannot find WLBINCOLLECTIONFormData")
serialized_collection_info = match.group(1)
collection_info = json.loads(base64.b64decode(serialized_collection_info))
return collection_info

def __get_bin_collection_info_page(self, session, address_page, postcode, uprn):
soup = BeautifulSoup(address_page, "html.parser")
form = soup.find(id="WLBINCOLLECTION_FORM")
goss_ids = self.__get_goss_form_ids(form["action"])
r = session.post(
form["action"],
allow_redirects=True,
data={
"WLBINCOLLECTION_PAGESESSIONID": goss_ids["page_session_id"],
"WLBINCOLLECTION_SESSIONID": goss_ids["session_id"],
"WLBINCOLLECTION_NONCE": goss_ids["nonce"],
"WLBINCOLLECTION_VARIABLES": "e30=",
"WLBINCOLLECTION_PAGENAME": "PAGE1",
"WLBINCOLLECTION_PAGEINSTANCE": "0",
"WLBINCOLLECTION_PAGE1_UPRN": uprn,
"WLBINCOLLECTION_PAGE1_ADDRESSLOOKUPPOSTCODE": postcode,
"WLBINCOLLECTION_PAGE1_ADDRESSLOOKUPADDRESS": "4",
"WLBINCOLLECTION_FORMACTION_NEXT": "WLBINCOLLECTION_PAGE1_NAVBUTTONS",
})
r.raise_for_status()
return r.text

def __get_address_page(self, s):
r = s.get(COLLECTION_PAGE_URL)
r.raise_for_status()
return r.text

def __get_goss_form_ids(self, url):
parsed_form_url = urlparse(url)
form_url_values = parse_qs(parsed_form_url.query)
return {
"page_session_id": form_url_values["pageSessionId"][0],
"session_id": form_url_values["fsid"][0],
"nonce": form_url_values["fsn"][0],
}
37 changes: 37 additions & 0 deletions doc/source/westlothian_gov_uk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# East Renfrewshire Council

Support for schedules provided by [West Lothian Council](https://www.westlothian.gov.uk/bin-collections).

## Configuration via configuration.yaml

```yaml
waste_collection_schedule:
sources:
- name: westlothian_gov_uk
args:
postcode: POSTCODE
uprn: UNIQUE_PROPERTY_REFERENCE_NUMBER
```
### Configuration Variables
**postcode**
*(string) (required)*
**uprn**
*(string) (required)*
## Example
```yaml
waste_collection_schedule:
sources:
- name: westlothian_gov_uk
args:
postcode: "EH55 8FJ"
uprn: "135051417"
```
## How to find your `UPRN`

An easy way to find your Unique Property Reference Number (UPRN) is by going to <https://www.findmyaddress.co.uk/> and entering your address details.
Loading

0 comments on commit 21f6744

Please sign in to comment.