uk_bin_collection 0.152.0__py3-none-any.whl → 0.152.2__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 +92 -58
- uk_bin_collection/uk_bin_collection/councils/AdurAndWorthingCouncils.py +69 -24
- uk_bin_collection/uk_bin_collection/councils/BexleyCouncil.py +24 -47
- 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 -3
- 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/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/NorthLincolnshireCouncil.py +2 -1
- 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/SouthRibbleCouncil.py +115 -65
- uk_bin_collection/uk_bin_collection/councils/StokeOnTrentCityCouncil.py +10 -5
- 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.152.0.dist-info → uk_bin_collection-0.152.2.dist-info}/METADATA +179 -1
- {uk_bin_collection-0.152.0.dist-info → uk_bin_collection-0.152.2.dist-info}/RECORD +36 -34
- {uk_bin_collection-0.152.0.dist-info → uk_bin_collection-0.152.2.dist-info}/LICENSE +0 -0
- {uk_bin_collection-0.152.0.dist-info → uk_bin_collection-0.152.2.dist-info}/WHEEL +0 -0
- {uk_bin_collection-0.152.0.dist-info → uk_bin_collection-0.152.2.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,10 @@
|
|
1
1
|
import time
|
2
2
|
|
3
3
|
from bs4 import BeautifulSoup
|
4
|
+
from selenium.common.exceptions import TimeoutException
|
4
5
|
from selenium.webdriver.common.by import By
|
6
|
+
from selenium.webdriver.support import expected_conditions as EC
|
7
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
5
8
|
|
6
9
|
from uk_bin_collection.uk_bin_collection.common import *
|
7
10
|
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
@@ -42,28 +45,61 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
42
45
|
driver = create_webdriver(web_driver, headless, None, __name__)
|
43
46
|
driver.get(page)
|
44
47
|
|
45
|
-
|
48
|
+
# Create wait object
|
49
|
+
wait = WebDriverWait(driver, 20)
|
46
50
|
|
47
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
# Wait for and click cookie button
|
52
|
+
cookie_button = wait.until(
|
53
|
+
EC.element_to_be_clickable((By.ID, "ccc-notify-accept"))
|
54
|
+
)
|
55
|
+
cookie_button.click()
|
56
|
+
|
57
|
+
# Wait for and find house number input
|
58
|
+
inputElement_hn = wait.until(
|
59
|
+
EC.presence_of_element_located(
|
60
|
+
(
|
61
|
+
By.ID,
|
62
|
+
"p_lt_ctl04_pageplaceholder_p_lt_ctl02_WasteCollectionCalendars_NCCAddressLookup_txtHouse",
|
63
|
+
)
|
64
|
+
)
|
53
65
|
)
|
54
|
-
|
55
|
-
|
56
|
-
|
66
|
+
|
67
|
+
# Wait for and find postcode input
|
68
|
+
inputElement_pc = wait.until(
|
69
|
+
EC.presence_of_element_located(
|
70
|
+
(
|
71
|
+
By.ID,
|
72
|
+
"p_lt_ctl04_pageplaceholder_p_lt_ctl02_WasteCollectionCalendars_NCCAddressLookup_txtPostcode",
|
73
|
+
)
|
74
|
+
)
|
57
75
|
)
|
58
76
|
|
77
|
+
# Enter details
|
59
78
|
inputElement_pc.send_keys(user_postcode)
|
60
79
|
inputElement_hn.send_keys(user_paon)
|
61
80
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
81
|
+
# Click lookup button and wait for results
|
82
|
+
lookup_button = wait.until(
|
83
|
+
EC.element_to_be_clickable(
|
84
|
+
(
|
85
|
+
By.ID,
|
86
|
+
"p_lt_ctl04_pageplaceholder_p_lt_ctl02_WasteCollectionCalendars_NCCAddressLookup_butLookup",
|
87
|
+
)
|
88
|
+
)
|
89
|
+
)
|
90
|
+
lookup_button.click()
|
91
|
+
|
92
|
+
# Wait for results to load
|
93
|
+
route_summary = wait.until(
|
94
|
+
EC.presence_of_element_located(
|
95
|
+
(
|
96
|
+
By.ID,
|
97
|
+
"p_lt_ctl04_pageplaceholder_p_lt_ctl02_WasteCollectionCalendars_spanRouteSummary",
|
98
|
+
)
|
99
|
+
)
|
100
|
+
)
|
66
101
|
|
102
|
+
# Get page source after everything has loaded
|
67
103
|
soup = BeautifulSoup(driver.page_source, features="html.parser")
|
68
104
|
|
69
105
|
# Work out which bins can be collected for this address. Glass bins are only on some houses due to pilot programme.
|
@@ -1,12 +1,29 @@
|
|
1
|
-
import
|
2
|
-
|
1
|
+
from typing import Dict, List, Any, Optional
|
2
|
+
from bs4 import BeautifulSoup
|
3
|
+
from dateutil.relativedelta import relativedelta
|
3
4
|
import requests
|
4
|
-
|
5
|
+
import logging
|
6
|
+
import re
|
7
|
+
from datetime import datetime
|
5
8
|
from uk_bin_collection.uk_bin_collection.common import *
|
9
|
+
from dateutil.parser import parse
|
10
|
+
|
11
|
+
from uk_bin_collection.uk_bin_collection.common import check_uprn, check_postcode
|
6
12
|
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
7
13
|
|
8
14
|
|
9
|
-
|
15
|
+
def get_token(page) -> str:
|
16
|
+
"""
|
17
|
+
Get a __token to include in the form data
|
18
|
+
:param page: Page html
|
19
|
+
:return: Form __token
|
20
|
+
"""
|
21
|
+
soup = BeautifulSoup(page.text, features="html.parser")
|
22
|
+
soup.prettify()
|
23
|
+
token = soup.find("input", {"name": "__token"}).get("value")
|
24
|
+
return token
|
25
|
+
|
26
|
+
|
10
27
|
class CouncilClass(AbstractGetBinDataClass):
|
11
28
|
"""
|
12
29
|
Concrete classes have to implement all abstract operations of the
|
@@ -14,70 +31,103 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
14
31
|
implementation.
|
15
32
|
"""
|
16
33
|
|
17
|
-
def
|
34
|
+
def get_data(self, url: str) -> str:
|
35
|
+
"""This method makes the request to the council
|
18
36
|
|
19
|
-
|
20
|
-
|
21
|
-
|
37
|
+
Keyword arguments:
|
38
|
+
url -- the url to get the data from
|
39
|
+
"""
|
40
|
+
# Set a user agent so we look like a browser ;-)
|
41
|
+
user_agent = (
|
42
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
43
|
+
"Chrome/108.0.0.0 Safari/537.36"
|
44
|
+
)
|
45
|
+
headers = {"User-Agent": user_agent}
|
46
|
+
requests.packages.urllib3.disable_warnings()
|
22
47
|
|
23
|
-
|
48
|
+
# Make the Request - change the URL - find out your property number
|
49
|
+
try:
|
50
|
+
session = requests.Session()
|
51
|
+
session.headers.update(headers)
|
52
|
+
full_page = session.get(url)
|
53
|
+
return full_page
|
54
|
+
except requests.exceptions.HTTPError as errh:
|
55
|
+
logging.error(f"Http Error: {errh}")
|
56
|
+
raise
|
57
|
+
except requests.exceptions.ConnectionError as errc:
|
58
|
+
logging.error(f"Error Connecting: {errc}")
|
59
|
+
raise
|
60
|
+
except requests.exceptions.Timeout as errt:
|
61
|
+
logging.error(f"Timeout Error: {errt}")
|
62
|
+
raise
|
63
|
+
except requests.exceptions.RequestException as err:
|
64
|
+
logging.error(f"Oops: Something Else {err}")
|
65
|
+
raise
|
24
66
|
|
25
|
-
|
67
|
+
def parse_data(self, page: str, **kwargs: Any) -> Dict[str, List[Dict[str, str]]]:
|
68
|
+
uprn: Optional[str] = kwargs.get("uprn")
|
69
|
+
postcode: Optional[str] = kwargs.get("postcode")
|
26
70
|
|
27
|
-
|
28
|
-
"
|
29
|
-
|
30
|
-
|
31
|
-
},
|
32
|
-
"Your details": {
|
33
|
-
"LLPGUPRN": {"value": user_uprn},
|
34
|
-
},
|
35
|
-
},
|
36
|
-
}
|
71
|
+
if uprn is None:
|
72
|
+
raise ValueError("UPRN is required and must be a non-empty string.")
|
73
|
+
if postcode is None:
|
74
|
+
raise ValueError("Postcode is required and must be a non-empty string.")
|
37
75
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
"
|
43
|
-
"
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
session_data = r.json()
|
49
|
-
sid = session_data["auth-session"]
|
50
|
-
params = {
|
51
|
-
"id": "58ab44ef078bd",
|
52
|
-
"repeat_against": "",
|
53
|
-
"noRetry": "false",
|
54
|
-
"getOnlyTokens": "undefined",
|
55
|
-
"log_id": "",
|
56
|
-
"app_name": "AF-Renderer::Self",
|
57
|
-
# unix_timestamp
|
58
|
-
"_": str(int(time.time() * 1000)),
|
59
|
-
"sid": sid,
|
76
|
+
check_uprn(uprn)
|
77
|
+
check_postcode(postcode)
|
78
|
+
|
79
|
+
values = {
|
80
|
+
"__token": get_token(page),
|
81
|
+
"page": "491",
|
82
|
+
"locale": "en_GB",
|
83
|
+
"q1f8ccce1d1e2f58649b4069712be6879a839233f_0_0": postcode,
|
84
|
+
"q1f8ccce1d1e2f58649b4069712be6879a839233f_1_0": uprn,
|
85
|
+
"next": "Next",
|
60
86
|
}
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
87
|
+
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64)"}
|
88
|
+
requests.packages.urllib3.disable_warnings()
|
89
|
+
response = requests.request(
|
90
|
+
"POST",
|
91
|
+
"https://forms.chorleysouthribble.gov.uk/xfp/form/70",
|
92
|
+
headers=headers,
|
93
|
+
data=values,
|
94
|
+
)
|
95
|
+
|
96
|
+
soup = BeautifulSoup(response.text, features="html.parser")
|
97
|
+
|
98
|
+
rows = soup.find("table").find_all("tr")
|
99
|
+
|
100
|
+
# Form a JSON wrapper
|
101
|
+
data: Dict[str, List[Dict[str, str]]] = {"bins": []}
|
102
|
+
|
103
|
+
# Loops the Rows
|
104
|
+
for row in rows:
|
105
|
+
cells = row.find_all("td")
|
106
|
+
if cells:
|
107
|
+
bin_type = cells[0].get_text(strip=True)
|
108
|
+
collection_next = cells[1].get_text(strip=True)
|
109
|
+
|
110
|
+
collection_date = re.findall(r"\(.*?\)", collection_next)
|
111
|
+
|
112
|
+
if len(collection_date) != 1:
|
113
|
+
continue
|
114
|
+
|
115
|
+
collection_date_obj = parse(
|
116
|
+
re.sub(r"[()]", "", collection_date[0])
|
117
|
+
).date()
|
118
|
+
|
119
|
+
# since we only have the next collection day, if the parsed date is in the past,
|
120
|
+
# assume the day is instead next month
|
121
|
+
if collection_date_obj < datetime.now().date():
|
122
|
+
collection_date_obj += relativedelta(months=1)
|
123
|
+
|
124
|
+
# Make each Bin element in the JSON
|
125
|
+
dict_data = {
|
126
|
+
"type": bin_type,
|
127
|
+
"collectionDate": collection_date_obj.strftime(date_format),
|
128
|
+
}
|
129
|
+
|
130
|
+
# Add data to the main JSON Wrapper
|
131
|
+
data["bins"].append(dict_data)
|
132
|
+
|
133
|
+
return data
|
@@ -54,14 +54,19 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
54
54
|
bin_type = bin_types.get(
|
55
55
|
item.find_next("Bin").text.replace("EMPTY BINS", "").strip()
|
56
56
|
)
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
date_text = item.find_next("DateTime").text.strip()
|
58
|
+
|
59
|
+
# Handle inconsistent date formats
|
60
|
+
if " " in date_text: # Date and time present
|
61
|
+
bin_date = datetime.strptime(date_text, "%d/%m/%Y %H:%M:%S")
|
62
|
+
else: # Only date present
|
63
|
+
bin_date = datetime.strptime(date_text, "%d/%m/%Y")
|
64
|
+
|
60
65
|
if bin_date >= datetime.now():
|
61
66
|
collections.append((bin_type, bin_date))
|
62
|
-
except:
|
67
|
+
except Exception as e:
|
63
68
|
raise SystemError(
|
64
|
-
"Error has been encountered parsing API. Please try again later and if the issue "
|
69
|
+
f"Error has been encountered parsing API: {e}. Please try again later and if the issue "
|
65
70
|
"persists, open a GitHub ticket!"
|
66
71
|
)
|
67
72
|
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# This script pulls bin collection data from Barking and Dagenham Council
|
2
|
-
# Example URL: https://www.lbbd.gov.uk/rubbish-recycling/household-bin-collection/check-your-bin-collection-days
|
3
1
|
import time
|
4
2
|
|
5
3
|
from bs4 import BeautifulSoup
|
@@ -39,7 +37,7 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
39
37
|
|
40
38
|
driver = create_webdriver(web_driver, headless, None, __name__)
|
41
39
|
print(f"Navigating to URL: {url}")
|
42
|
-
driver.get(
|
40
|
+
driver.get("https://www.torbay.gov.uk/recycling/bin-collections/")
|
43
41
|
print("Successfully loaded the page")
|
44
42
|
|
45
43
|
driver.maximize_window()
|
@@ -21,6 +21,9 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
21
21
|
)
|
22
22
|
driver.get(kwargs.get("url"))
|
23
23
|
|
24
|
+
# This URL format also works:
|
25
|
+
# https://www.wakefield.gov.uk/where-i-live?a=115%20Elizabeth%20Drive
|
26
|
+
|
24
27
|
# Make a BS4 object
|
25
28
|
soup = BeautifulSoup(driver.page_source, features="html.parser")
|
26
29
|
soup.prettify()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: uk_bin_collection
|
3
|
-
Version: 0.152.
|
3
|
+
Version: 0.152.2
|
4
4
|
Summary: Python Lib to collect UK Bin Data
|
5
5
|
Author: Robert Bradley
|
6
6
|
Author-email: robbrad182@gmail.com
|
@@ -398,6 +398,184 @@ Open the map viewer in VS Code:
|
|
398
398
|

|
399
399
|
|
400
400
|
---
|
401
|
+
## ICS Calendar Generation
|
402
|
+
|
403
|
+
You can convert bin collection data to an ICS calendar file that can be imported into calendar applications like Google Calendar, Apple Calendar, Microsoft Outlook, etc.
|
404
|
+
|
405
|
+
### Overview
|
406
|
+
|
407
|
+
The `bin_to_ics.py` script allows you to:
|
408
|
+
- Convert JSON output from bin collection data into ICS calendar events
|
409
|
+
- Group multiple bin collections on the same day into a single event
|
410
|
+
- Create all-day events (default) or timed events
|
411
|
+
- Add optional reminders/alarms to events
|
412
|
+
- Customize the calendar name
|
413
|
+
|
414
|
+
### Requirements
|
415
|
+
|
416
|
+
- Python 3.6 or higher
|
417
|
+
- The `icalendar` package, which can be installed with:
|
418
|
+
```bash
|
419
|
+
pip install icalendar
|
420
|
+
```
|
421
|
+
|
422
|
+
### Basic Usage
|
423
|
+
|
424
|
+
```bash
|
425
|
+
# Basic usage with stdin input and default output file (bin.ics)
|
426
|
+
python bin_to_ics.py < bin_data.json
|
427
|
+
|
428
|
+
# Specify input and output files
|
429
|
+
python bin_to_ics.py -i bin_data.json -o my_calendar.ics
|
430
|
+
|
431
|
+
# Custom calendar name
|
432
|
+
python bin_to_ics.py -i bin_data.json -o my_calendar.ics -n "My Bin Collections"
|
433
|
+
```
|
434
|
+
|
435
|
+
### Options
|
436
|
+
|
437
|
+
```
|
438
|
+
--input, -i Input JSON file (if not provided, read from stdin)
|
439
|
+
--output, -o Output ICS file (default: bin.ics)
|
440
|
+
--name, -n Calendar name (default: Bin Collections)
|
441
|
+
--alarms, -a Comma-separated list of alarm times before event (e.g., "1d,2h,30m")
|
442
|
+
--no-all-day Create timed events instead of all-day events
|
443
|
+
```
|
444
|
+
|
445
|
+
### Examples
|
446
|
+
|
447
|
+
#### Adding Reminders (Alarms)
|
448
|
+
|
449
|
+
Add reminders 1 day and 2 hours before each collection:
|
450
|
+
|
451
|
+
```bash
|
452
|
+
python bin_to_ics.py -i bin_data.json -a "1d,2h"
|
453
|
+
```
|
454
|
+
|
455
|
+
The time format supports:
|
456
|
+
- Days: `1d`, `2day`, `3days`
|
457
|
+
- Hours: `1h`, `2hour`, `3hours`
|
458
|
+
- Minutes: `30m`, `45min`, `60mins`, `90minutes`
|
459
|
+
|
460
|
+
#### Creating Timed Events
|
461
|
+
|
462
|
+
By default, events are created as all-day events. To create timed events instead (default time: 7:00 AM):
|
463
|
+
|
464
|
+
```bash
|
465
|
+
python bin_to_ics.py -i bin_data.json --no-all-day
|
466
|
+
```
|
467
|
+
|
468
|
+
### Integration with Bin Collection Data Retriever
|
469
|
+
|
470
|
+
You can pipe the output from the bin collection data retriever directly to the ICS generator. The required parameters (postcode, house number, UPRN, etc.) depend on the specific council implementation - refer to the [Quickstart](#quickstart) section above or check the [project wiki](https://github.com/robbrad/UKBinCollectionData/wiki) for details about your council.
|
471
|
+
|
472
|
+
```bash
|
473
|
+
python uk_bin_collection/uk_bin_collection/collect_data.py CouncilName "URL" [OPTIONS] |
|
474
|
+
python bin_to_ics.py [OPTIONS]
|
475
|
+
```
|
476
|
+
|
477
|
+
#### Complete Example for a Council
|
478
|
+
|
479
|
+
```bash
|
480
|
+
python uk_bin_collection/uk_bin_collection/collect_data.py CouncilName \
|
481
|
+
"council_url" \
|
482
|
+
-p "YOUR_POSTCODE" \
|
483
|
+
-n "YOUR_HOUSE_NUMBER" \
|
484
|
+
-w "http://localhost:4444/wd/hub" |
|
485
|
+
python bin_to_ics.py \
|
486
|
+
--name "My Bin Collections" \
|
487
|
+
--output my_bins.ics \
|
488
|
+
--alarms "1d,12h"
|
489
|
+
```
|
490
|
+
|
491
|
+
This will:
|
492
|
+
1. Fetch bin collection data for your address from your council's website
|
493
|
+
2. Convert it to an ICS file named "my_bins.ics"
|
494
|
+
3. Set the calendar name to "My Bin Collections"
|
495
|
+
4. Add reminders 1 day and 12 hours before each collection
|
496
|
+
|
497
|
+
For postcode lookup and UPRN information, please check the [UPRN Finder](#uprn-finder) section above.
|
498
|
+
|
499
|
+
### Using the Calendar
|
500
|
+
|
501
|
+
You have two options for using the generated ICS file:
|
502
|
+
|
503
|
+
#### 1. Importing the Calendar
|
504
|
+
|
505
|
+
You can directly import the ICS file into your calendar application:
|
506
|
+
|
507
|
+
- **Google Calendar**: Go to Settings > Import & export > Import
|
508
|
+
- **Apple Calendar**: File > Import
|
509
|
+
- **Microsoft Outlook**: File > Open & Export > Import/Export > Import an iCalendar (.ics)
|
510
|
+
|
511
|
+
Note: Importing creates a static copy of the calendar events. If bin collection dates change, you'll need to re-import the calendar.
|
512
|
+
|
513
|
+
#### 2. Subscribing to the Calendar
|
514
|
+
|
515
|
+
If you host the ICS file on a publicly accessible web server, you can subscribe to it as an internet calendar:
|
516
|
+
|
517
|
+
- **Google Calendar**: Go to "Other calendars" > "+" > "From URL" > Enter the URL of your hosted ICS file
|
518
|
+
- **Apple Calendar**: File > New Calendar Subscription > Enter the URL
|
519
|
+
- **Microsoft Outlook**: File > Account Settings > Internet Calendars > New > Enter the URL
|
520
|
+
|
521
|
+
Benefits of subscribing:
|
522
|
+
- Calendar automatically updates when the source file changes
|
523
|
+
- No need to manually re-import when bin collection dates change
|
524
|
+
- Easily share the calendar with household members
|
525
|
+
|
526
|
+
You can set up a cron job or scheduled task to regularly:
|
527
|
+
1. Retrieve the latest bin collection data
|
528
|
+
2. Generate a fresh ICS file
|
529
|
+
3. Publish it to a web-accessible location
|
530
|
+
|
531
|
+
### Additional Examples and Use Cases
|
532
|
+
|
533
|
+
#### Automation with Cron Jobs
|
534
|
+
|
535
|
+
Create a weekly update script on a Linux/Mac system:
|
536
|
+
|
537
|
+
```bash
|
538
|
+
#!/bin/bash
|
539
|
+
# File: update_bin_calendar.sh
|
540
|
+
|
541
|
+
# Set variables
|
542
|
+
COUNCIL="YourCouncilName"
|
543
|
+
COUNCIL_URL="https://your-council-website.gov.uk/bins"
|
544
|
+
POSTCODE="YOUR_POSTCODE"
|
545
|
+
HOUSE_NUMBER="YOUR_HOUSE_NUMBER"
|
546
|
+
OUTPUT_DIR="/var/www/html/calendars" # Web-accessible directory
|
547
|
+
CALENDAR_NAME="Household Bins"
|
548
|
+
|
549
|
+
# Ensure output directory exists
|
550
|
+
mkdir -p $OUTPUT_DIR
|
551
|
+
|
552
|
+
# Run the collector and generate the calendar
|
553
|
+
cd /path/to/UKBinCollectionData && \
|
554
|
+
python uk_bin_collection/uk_bin_collection/collect_data.py $COUNCIL "$COUNCIL_URL" \
|
555
|
+
-p "$POSTCODE" -n "$HOUSE_NUMBER" | \
|
556
|
+
python bin_to_ics.py --name "$CALENDAR_NAME" --output "$OUTPUT_DIR/bins.ics" --alarms "1d,6h"
|
557
|
+
|
558
|
+
# Add timestamp to show last update time
|
559
|
+
echo "Calendar last updated: $(date)" > "$OUTPUT_DIR/last_update.txt"
|
560
|
+
```
|
561
|
+
|
562
|
+
Make the script executable:
|
563
|
+
```bash
|
564
|
+
chmod +x update_bin_calendar.sh
|
565
|
+
```
|
566
|
+
|
567
|
+
Add to crontab to run weekly (every Monday at 2 AM):
|
568
|
+
```bash
|
569
|
+
0 2 * * 1 /path/to/update_bin_calendar.sh
|
570
|
+
```
|
571
|
+
|
572
|
+
**Google Assistant/Alexa Integration**
|
573
|
+
|
574
|
+
If you have your calendar connected to Google Calendar or Outlook, you can ask your smart assistant about upcoming bin collections:
|
575
|
+
|
576
|
+
- "Hey Google, when is my next bin collection?"
|
577
|
+
- "Alexa, what's on my calendar tomorrow?" (will include bin collections)
|
578
|
+
|
401
579
|
## Docker API Server
|
402
580
|
We have created an API for this located under [uk_bin_collection_api_server](https://github.com/robbrad/UKBinCollectionData/uk_bin_collection_api_server)
|
403
581
|
|