forked from mampfes/hacs_waste_collection_schedule
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for Royal Borough Of Greenwich (mampfes#3277)
- Loading branch information
Showing
8 changed files
with
291 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
...nents/waste_collection_schedule/waste_collection_schedule/source/royalgreenwich_gov_uk.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import re | ||
import datetime | ||
|
||
import requests | ||
from bs4 import BeautifulSoup, Tag | ||
from dateutil import parser | ||
from typing import Optional | ||
from waste_collection_schedule import Collection # type: ignore[attr-defined] | ||
from waste_collection_schedule.exceptions import SourceArgAmbiguousWithSuggestions, SourceArgumentNotFound | ||
|
||
TITLE = "Royal Borough Of Greenwich" | ||
DESCRIPTION = "Source for services from the Royal Borough Of Greenwich" | ||
URL = "https://www.royalgreenwich.gov.uk/" | ||
TEST_CASES = { | ||
"address": {"address": "25 - Tizzard Grove - London - SE3 9DH"}, | ||
"houseNumber": {"post_code": "SE9 5AW", "house": "11"}, | ||
"alternativeWeek": {"address": "32 - Glenlyon Road - London - SE9 1AJ"} | ||
} | ||
|
||
ADDRESS_SEARCH_URL = "https://www.royalgreenwich.gov.uk/site/custom_scripts/apps/waste-collection/new2023/source.php" | ||
|
||
|
||
DAYS = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"] | ||
ICON_MAP = { | ||
"recycling": "mdi:recycle", | ||
"garden": "mdi:leaf", | ||
"food": "mdi:food-apple", | ||
} | ||
|
||
|
||
class InsufficientDataError(Exception): | ||
pass | ||
|
||
|
||
class Source: | ||
def __init__( | ||
self, | ||
post_code: Optional[str] = None, | ||
house: Optional[str] = None, | ||
address: Optional[str] = None | ||
): | ||
self._post_code = post_code | ||
self._house = house | ||
self._address = address | ||
|
||
def _find_address(self) -> str: | ||
term_list = [self._post_code] | ||
if self._house: | ||
term_list.append(self._house) | ||
|
||
s = requests.Session() | ||
search_term = " ".join(term_list) | ||
r = s.get(ADDRESS_SEARCH_URL, params={"term": search_term}) | ||
r.raise_for_status() | ||
|
||
addresses = r.json() | ||
|
||
if len(addresses) > 1: | ||
raise SourceArgAmbiguousWithSuggestions("house", self._house, addresses) | ||
|
||
if len(addresses) == 0: | ||
raise SourceArgumentNotFound("house", self._house) | ||
|
||
return addresses[0] | ||
|
||
def _get_black_top_bin_next_collection_date(self, week_name: str, this_week_collection_date: datetime.datetime) -> datetime.datetime: | ||
s = requests.Session() | ||
r = s.get( | ||
"https://www.royalgreenwich.gov.uk/info/200171/recycling_and_rubbish/2436/black_top_bin_collections" | ||
) | ||
r.raise_for_status() | ||
|
||
soup = BeautifulSoup(r.text, "html.parser") | ||
|
||
black_top_bin_schedule_table = soup.find("table") | ||
if not isinstance(black_top_bin_schedule_table, Tag): | ||
raise Exception("Could not find address form") | ||
|
||
headers = black_top_bin_schedule_table.find_all("th") | ||
week_column_index = list(map(lambda h: h.text, headers)).index(week_name) | ||
if week_column_index < 0: | ||
raise Exception("Cannot find black top bin collection weeks") | ||
|
||
# e.g. Monday 1 January to Friday 5 January | ||
first_week_dates_range_str: str = black_top_bin_schedule_table.find("tbody").find("tr").find_all('td')[week_column_index].text | ||
|
||
first_week_date = parser.parse(first_week_dates_range_str.split(" to ")[0]).date() | ||
|
||
# we assume that this "first_week_date" is always Monday (as per schedule) | ||
first_week_collection_date = first_week_date + datetime.timedelta(this_week_collection_date.isoweekday() - 1) | ||
return this_week_collection_date + datetime.timedelta(weeks=1) if (this_week_collection_date - first_week_collection_date).days % 14 else this_week_collection_date | ||
|
||
def fetch(self) -> list[Collection]: | ||
if not self._address: | ||
self._address = self._find_address() | ||
|
||
s = requests.Session() | ||
|
||
r = s.get( | ||
"https://www.royalgreenwich.gov.uk/site/custom_scripts/repo/apps/waste-collection/new2023/ajax-response-uprn.php", | ||
params={ "address": self._address } | ||
) | ||
r.raise_for_status() | ||
|
||
# even if address is part of the borough it doesn't mean they will provide data for it | ||
# e.g. for flats they explicitly mentioned to contact management company instead | ||
# so in this case address can be found in previous steps, but there is no data for it and this error is returned | ||
if r.text == "ADDRESS_NOT_FOUND": | ||
raise Exception(f"No data found for address '{self._address}'") | ||
|
||
data = r.json() | ||
|
||
collection_day = data["Day"] | ||
black_top_bin_week = data["Frequency"] | ||
|
||
today = datetime.date.today() | ||
collection_day_index = DAYS.index(collection_day.upper()) + 1 | ||
this_week_collection_date = today + datetime.timedelta( | ||
(collection_day_index - today.isoweekday()) % 7 | ||
) | ||
|
||
next_food_collection_date = self._get_black_top_bin_next_collection_date(black_top_bin_week, this_week_collection_date) | ||
|
||
weeks_to_generate = 10 | ||
|
||
recycling_collections = [ | ||
Collection( | ||
date=this_week_collection_date + datetime.timedelta(weeks=i), | ||
t="recycling", | ||
icon=ICON_MAP.get("recycling"), | ||
) for i in range(weeks_to_generate) | ||
] | ||
|
||
garden_collections = [ | ||
Collection( | ||
date=this_week_collection_date + datetime.timedelta(weeks=i), | ||
t="garden", | ||
icon=ICON_MAP.get("garden"), | ||
) for i in range(weeks_to_generate) | ||
] | ||
|
||
food_collections = [ | ||
Collection( | ||
date=next_food_collection_date + datetime.timedelta(weeks=i * 2), | ||
t="food", | ||
icon=ICON_MAP.get("food"), | ||
) for i in range(int(weeks_to_generate / 2)) | ||
] | ||
|
||
return recycling_collections + garden_collections + food_collections |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Royal Borough Of Greenwich | ||
|
||
Support for schedules provided by the [Royal Borough Of Greenwich](https://www.royalgreenwich.gov.uk/info/200171/recycling_and_rubbish/100/find_your_bin_collection_day). | ||
|
||
## Configuration via configuration.yaml | ||
|
||
```yaml | ||
waste_collection_schedule: | ||
sources: | ||
- name: royalgreenwich_gov_uk | ||
args: | ||
post_code: POST_CODE | ||
house: HOUSE_NUMBER | ||
address: FULL_ADDRESS | ||
``` | ||
### Configuration Variables | ||
**address** | ||
*(string) (optional)* | ||
This is required if you do not supply any other options. (Using this removes the need to do an address look up web request) | ||
**house** | ||
*(string) (optional)* | ||
This is required if you supply a Postcode. | ||
**post_code** | ||
*(string) (optional)* | ||
This is required if you do not supply an Address. Single space between 1st and 2nd part of postcode is optional. | ||
#### How to find your `FULL_ADDRESS` | ||
|
||
An easy way to discover your full address is: | ||
|
||
1. Go to <https://www.royalgreenwich.gov.uk/info/200171/recycling_and_rubbish/100/find_your_bin_collection_day> | ||
1. Find your property and click "Search" button | ||
1. First bold text in the message below the search bar (right after "At" and before ":") is your address, use it as-is. | ||
|
||
## Example using FULL_ADDRESS | ||
|
||
```yaml | ||
waste_collection_schedule: | ||
sources: | ||
- name: royalgreenwich_gov_uk | ||
args: | ||
address: "32 - Glenlyon Road - London - SE9 1AJ" | ||
``` | ||
|
||
## Example using Address lookup | ||
|
||
```yaml | ||
waste_collection_schedule: | ||
sources: | ||
- name: royalgreenwich_gov_uk | ||
args: | ||
post_code: "SE9 5AW" | ||
number: "11" | ||
``` |
Oops, something went wrong.