uk_bin_collection 0.117.0__py3-none-any.whl → 0.119.0__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 +75 -2
- uk_bin_collection/uk_bin_collection/councils/AberdeenCityCouncil.py +122 -0
- uk_bin_collection/uk_bin_collection/councils/BaberghDistrictCouncil.py +62 -0
- uk_bin_collection/uk_bin_collection/councils/BraintreeDistrictCouncil.py +70 -0
- uk_bin_collection/uk_bin_collection/councils/BurnleyBoroughCouncil.py +88 -0
- uk_bin_collection/uk_bin_collection/councils/CopelandBoroughCouncil.py +93 -0
- uk_bin_collection/uk_bin_collection/councils/CrawleyBoroughCouncil.py +91 -40
- uk_bin_collection/uk_bin_collection/councils/EdinburghCityCouncil.py +98 -0
- uk_bin_collection/uk_bin_collection/councils/ExeterCityCouncil.py +52 -0
- uk_bin_collection/uk_bin_collection/councils/MidSuffolkDistrictCouncil.py +62 -0
- uk_bin_collection/uk_bin_collection/councils/RotherDistrictCouncil.py +84 -0
- uk_bin_collection/uk_bin_collection/councils/SouthHamsDistrictCouncil.py +90 -0
- uk_bin_collection/uk_bin_collection/councils/StevenageBoroughCouncil.py +101 -0
- uk_bin_collection/uk_bin_collection/councils/ThanetDistrictCouncil.py +51 -0
- uk_bin_collection/uk_bin_collection/councils/WolverhamptonCityCouncil.py +57 -0
- {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/METADATA +1 -1
- {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/RECORD +20 -9
- {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/LICENSE +0 -0
- {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/WHEEL +0 -0
- {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/entry_points.txt +0 -0
@@ -6,6 +6,12 @@
|
|
6
6
|
"wiki_name": "Aberdeenshire Council",
|
7
7
|
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
8
8
|
},
|
9
|
+
"AberdeenCityCouncil": {
|
10
|
+
"url": "https://www.aberdeencity.gov.uk",
|
11
|
+
"uprn": "9051156186",
|
12
|
+
"wiki_name": "Aberdeen City Council",
|
13
|
+
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
|
14
|
+
},
|
9
15
|
"AdurAndWorthingCouncils": {
|
10
16
|
"url": "https://www.adur-worthing.gov.uk/bin-day/?brlu-selected-address=100061878829",
|
11
17
|
"wiki_command_url_override": "https://www.adur-worthing.gov.uk/bin-day/?brlu-selected-address=XXXXXXXX",
|
@@ -81,9 +87,10 @@
|
|
81
87
|
"BaberghDistrictCouncil": {
|
82
88
|
"skip_get_url": true,
|
83
89
|
"house_number": "Monday",
|
90
|
+
"postcode": "Week 1",
|
84
91
|
"url": "https://www.babergh.gov.uk",
|
85
92
|
"wiki_name": "Babergh District Council",
|
86
|
-
"wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday"
|
93
|
+
"wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday. [OPTIONAL] Use the 'postcode' field to pass the WEEK for your garden collection. [Week 1/Week 2]"
|
87
94
|
},
|
88
95
|
"BCPCouncil": {
|
89
96
|
"skip_get_url": true,
|
@@ -212,6 +219,14 @@
|
|
212
219
|
"wiki_name": "Bradford MDC",
|
213
220
|
"wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search). Postcode isn't parsed by this script, but you can pass it in double quotes."
|
214
221
|
},
|
222
|
+
"BraintreeDistrictCouncil": {
|
223
|
+
"postcode": "CO5 9BD",
|
224
|
+
"skip_get_url": true,
|
225
|
+
"uprn": "10006930172",
|
226
|
+
"url": "https://www.braintree.gov.uk/",
|
227
|
+
"wiki_name": "Braintree District Council",
|
228
|
+
"wiki_note": "Provide your UPRN and postcode. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
229
|
+
},
|
215
230
|
"BrecklandCouncil": {
|
216
231
|
"url": "https://www.breckland.gov.uk",
|
217
232
|
"wiki_command_url_override": "https://www.breckland.gov.uk",
|
@@ -275,6 +290,12 @@
|
|
275
290
|
"wiki_name": "Buckinghamshire Council (Chiltern, South Bucks, Wycombe)",
|
276
291
|
"wiki_note": "Pass the house name/number and postcode in their respective arguments, both wrapped in quotes."
|
277
292
|
},
|
293
|
+
"BurnleyBoroughCouncil": {
|
294
|
+
"uprn": "100010347165",
|
295
|
+
"url": "https://www.burnley.gov.uk",
|
296
|
+
"wiki_name": "Burnley Borough Council",
|
297
|
+
"wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
298
|
+
},
|
278
299
|
"BuryCouncil": {
|
279
300
|
"house_number": "3",
|
280
301
|
"postcode": "M26 3XY",
|
@@ -397,6 +418,12 @@
|
|
397
418
|
"wiki_name": "Conwy County Borough Council",
|
398
419
|
"wiki_note": "Conwy County Borough Council uses a straight UPRN in the URL, e.g., `&uprn=XXXXXXXXXXXXX`."
|
399
420
|
},
|
421
|
+
"CopelandBoroughCouncil": {
|
422
|
+
"uprn": "100110734613",
|
423
|
+
"url": "https://www.copeland.gov.uk",
|
424
|
+
"wiki_name": "Copeland Borough Council",
|
425
|
+
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
426
|
+
},
|
400
427
|
"CornwallCouncil": {
|
401
428
|
"skip_get_url": true,
|
402
429
|
"uprn": "100040128734",
|
@@ -590,6 +617,14 @@
|
|
590
617
|
"wiki_name": "Eastleigh Borough Council",
|
591
618
|
"wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
592
619
|
},
|
620
|
+
"EdinburghCityCouncil": {
|
621
|
+
"skip_get_url": true,
|
622
|
+
"house_number": "Tuesday",
|
623
|
+
"postcode": "Week 1",
|
624
|
+
"url": "https://www.edinburgh.gov.uk",
|
625
|
+
"wiki_name": "Edinburgh City Council",
|
626
|
+
"wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday. Use the 'postcode' field to pass the WEEK for your collection. [Week 1/Week 2]"
|
627
|
+
},
|
593
628
|
"ElmbridgeBoroughCouncil": {
|
594
629
|
"url": "https://www.elmbridge.gov.uk",
|
595
630
|
"wiki_command_url_override": "https://www.elmbridge.gov.uk",
|
@@ -625,6 +660,12 @@
|
|
625
660
|
"wiki_name": "Erewash Borough Council",
|
626
661
|
"wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
627
662
|
},
|
663
|
+
"ExeterCityCouncil": {
|
664
|
+
"uprn": "100040212270",
|
665
|
+
"url": "https://www.exeter.gov.uk",
|
666
|
+
"wiki_name": "Exeter City Council",
|
667
|
+
"wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
668
|
+
},
|
628
669
|
"FalkirkCouncil": {
|
629
670
|
"url": "https://www.falkirk.gov.uk",
|
630
671
|
"wiki_command_url_override": "https://www.falkirk.gov.uk",
|
@@ -1005,9 +1046,10 @@
|
|
1005
1046
|
"MidSuffolkDistrictCouncil": {
|
1006
1047
|
"skip_get_url": true,
|
1007
1048
|
"house_number": "Monday",
|
1049
|
+
"postcode": "Week 2",
|
1008
1050
|
"url": "https://www.midsuffolk.gov.uk",
|
1009
1051
|
"wiki_name": "Mid Suffolk District Council",
|
1010
|
-
"wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday"
|
1052
|
+
"wiki_note": "Use the House Number field to pass the DAY of the week for your collections. Monday/Tuesday/Wednesday/Thursday/Friday. [OPTIONAL] Use the 'postcode' field to pass the WEEK for your garden collection. [Week 1/Week 2]"
|
1011
1053
|
},
|
1012
1054
|
"MidSussexDistrictCouncil": {
|
1013
1055
|
"house_number": "OAKLANDS, OAKLANDS ROAD RH16 1SS",
|
@@ -1303,6 +1345,12 @@
|
|
1303
1345
|
"wiki_name": "Rochford Council",
|
1304
1346
|
"wiki_note": "No extra parameters are required. Dates presented should be read as 'week commencing'."
|
1305
1347
|
},
|
1348
|
+
"RotherDistrictCouncil": {
|
1349
|
+
"uprn": "100061937338",
|
1350
|
+
"url": "https://www.rother.gov.uk",
|
1351
|
+
"wiki_name": "Rother District Council",
|
1352
|
+
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
1353
|
+
},
|
1306
1354
|
"RotherhamCouncil": {
|
1307
1355
|
"url": "https://www.rotherham.gov.uk/bin-collections?address=100050866000&submit=Submit",
|
1308
1356
|
"uprn": "100050866000",
|
@@ -1412,6 +1460,12 @@
|
|
1412
1460
|
"wiki_name": "South Gloucestershire Council",
|
1413
1461
|
"wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
1414
1462
|
},
|
1463
|
+
"SouthHamsDistrictCouncil": {
|
1464
|
+
"uprn": "10004742851",
|
1465
|
+
"url": "https://www.southhams.gov.uk",
|
1466
|
+
"wiki_name": "South Hams District Council",
|
1467
|
+
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
1468
|
+
},
|
1415
1469
|
"SouthKestevenDistrictCouncil": {
|
1416
1470
|
"house_number": "2 Althorpe Close, Market Deeping, PE6 8BL",
|
1417
1471
|
"postcode": "PE68BL",
|
@@ -1476,6 +1530,12 @@
|
|
1476
1530
|
"wiki_name": "St Albans City and District Council",
|
1477
1531
|
"wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
1478
1532
|
},
|
1533
|
+
"StevenageBoroughCouncil": {
|
1534
|
+
"uprn": "100080878852",
|
1535
|
+
"url": "https://www.stevenage.gov.uk",
|
1536
|
+
"wiki_name": "Stevenage Borough Council",
|
1537
|
+
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
1538
|
+
},
|
1479
1539
|
"StHelensBC": {
|
1480
1540
|
"house_number": "15",
|
1481
1541
|
"postcode": "L34 2GA",
|
@@ -1630,6 +1690,12 @@
|
|
1630
1690
|
"wiki_name": "Test Valley Borough Council",
|
1631
1691
|
"wiki_note": "Provide your UPRN and postcode. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
1632
1692
|
},
|
1693
|
+
"ThanetDistrictCouncil": {
|
1694
|
+
"uprn": "100061111858",
|
1695
|
+
"url": "https://www.thanet.gov.uk",
|
1696
|
+
"wiki_name": "Thanet District Council",
|
1697
|
+
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
1698
|
+
},
|
1633
1699
|
"ThreeRiversDistrictCouncil": {
|
1634
1700
|
"postcode": "WD3 7AZ",
|
1635
1701
|
"skip_get_url": true,
|
@@ -1897,6 +1963,13 @@
|
|
1897
1963
|
"wiki_name": "Worcester City Council",
|
1898
1964
|
"wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
|
1899
1965
|
},
|
1966
|
+
"WolverhamptonCityCouncil": {
|
1967
|
+
"uprn": "100071205205",
|
1968
|
+
"postcode": "WV3 9NZ",
|
1969
|
+
"url": "https://www.wolverhampton.gov.uk",
|
1970
|
+
"wiki_name": "Wolverhampton City Council",
|
1971
|
+
"wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
|
1972
|
+
},
|
1900
1973
|
"WorcesterCityCouncil": {
|
1901
1974
|
"url": "https://www.Worcester.gov.uk",
|
1902
1975
|
"wiki_command_url_override": "https://www.Worcester.gov.uk",
|
@@ -0,0 +1,122 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
import requests
|
4
|
+
|
5
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
6
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
7
|
+
|
8
|
+
|
9
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
10
|
+
class CouncilClass(AbstractGetBinDataClass):
|
11
|
+
"""
|
12
|
+
Concrete classes have to implement all abstract operations of the
|
13
|
+
base class. They can also override some operations with a default
|
14
|
+
implementation.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
18
|
+
|
19
|
+
user_uprn = kwargs.get("uprn")
|
20
|
+
check_uprn(user_uprn)
|
21
|
+
bindata = {"bins": []}
|
22
|
+
|
23
|
+
SESSION_URL = "https://integration.aberdeencity.gov.uk/authapi/isauthenticated?uri=https%253A%252F%252Fintegration.aberdeencity.gov.uk%252Fservice%252Fbin_collection_calendar___view&hostname=integration.aberdeencity.gov.uk&withCredentials=true"
|
24
|
+
|
25
|
+
API_URL = "https://integration.aberdeencity.gov.uk/apibroker/runLookup"
|
26
|
+
|
27
|
+
headers = {
|
28
|
+
"Content-Type": "application/json",
|
29
|
+
"Accept": "application/json",
|
30
|
+
"User-Agent": "Mozilla/5.0",
|
31
|
+
"X-Requested-With": "XMLHttpRequest",
|
32
|
+
"Referer": "https://integration.aberdeencity.gov.uk/fillform/?iframe_id=fillform-frame-1&db_id=",
|
33
|
+
}
|
34
|
+
s = requests.session()
|
35
|
+
r = s.get(SESSION_URL)
|
36
|
+
r.raise_for_status()
|
37
|
+
session_data = r.json()
|
38
|
+
sid = session_data["auth-session"]
|
39
|
+
params = {
|
40
|
+
"id": "583c08ffc47fe",
|
41
|
+
"repeat_against": "",
|
42
|
+
"noRetry": "true",
|
43
|
+
"getOnlyTokens": "undefined",
|
44
|
+
"log_id": "",
|
45
|
+
"app_name": "AF-Renderer::Self",
|
46
|
+
# unix_timestamp
|
47
|
+
"_": str(int(time.time() * 1000)),
|
48
|
+
"sid": sid,
|
49
|
+
}
|
50
|
+
|
51
|
+
r = s.post(API_URL, headers=headers, params=params)
|
52
|
+
r.raise_for_status()
|
53
|
+
|
54
|
+
data = r.json()
|
55
|
+
rows_data = data["integration"]["transformed"]["rows_data"]["0"]
|
56
|
+
if not isinstance(rows_data, dict):
|
57
|
+
raise ValueError("Invalid data returned from API")
|
58
|
+
token = rows_data["token"]
|
59
|
+
|
60
|
+
data = {
|
61
|
+
"formValues": {
|
62
|
+
"Section 1": {
|
63
|
+
"nauprn": {
|
64
|
+
"value": user_uprn,
|
65
|
+
},
|
66
|
+
"token": {
|
67
|
+
"value": token,
|
68
|
+
},
|
69
|
+
"mindate": {
|
70
|
+
"value": datetime.now().strftime("%Y-%m-%d"),
|
71
|
+
},
|
72
|
+
"maxdate": {
|
73
|
+
"value": (datetime.now() + timedelta(days=30)).strftime(
|
74
|
+
"%Y-%m-%d"
|
75
|
+
),
|
76
|
+
},
|
77
|
+
},
|
78
|
+
},
|
79
|
+
}
|
80
|
+
|
81
|
+
params = {
|
82
|
+
"id": "5a3141caf4016",
|
83
|
+
"repeat_against": "",
|
84
|
+
"noRetry": "true",
|
85
|
+
"getOnlyTokens": "undefined",
|
86
|
+
"log_id": "",
|
87
|
+
"app_name": "AF-Renderer::Self",
|
88
|
+
# unix_timestamp
|
89
|
+
"_": str(int(time.time() * 1000)),
|
90
|
+
"sid": sid,
|
91
|
+
}
|
92
|
+
|
93
|
+
r = s.post(API_URL, json=data, headers=headers, params=params)
|
94
|
+
r.raise_for_status()
|
95
|
+
|
96
|
+
data = r.json()
|
97
|
+
rows_data = data["integration"]["transformed"]["rows_data"]["0"]
|
98
|
+
if not isinstance(rows_data, dict):
|
99
|
+
raise ValueError("Invalid data returned from API")
|
100
|
+
|
101
|
+
date_pattern = re.compile(r"^(.*?)(Date\d+)$")
|
102
|
+
count_pattern = re.compile(r"^Count(.*)$")
|
103
|
+
for key, value in rows_data.items():
|
104
|
+
date_match = date_pattern.match(key)
|
105
|
+
# Match count keys
|
106
|
+
count_match = count_pattern.match(key)
|
107
|
+
if count_match:
|
108
|
+
continue
|
109
|
+
|
110
|
+
# Match date keys
|
111
|
+
date_match = date_pattern.match(key)
|
112
|
+
if date_match:
|
113
|
+
bin_type = date_match.group(1)
|
114
|
+
dict_data = {
|
115
|
+
"type": bin_type,
|
116
|
+
"collectionDate": datetime.strptime(value, "%A %d %B %Y").strftime(
|
117
|
+
date_format
|
118
|
+
),
|
119
|
+
}
|
120
|
+
bindata["bins"].append(dict_data)
|
121
|
+
|
122
|
+
return bindata
|
@@ -23,6 +23,7 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
23
23
|
def parse_data(self, page: str, **kwargs) -> dict:
|
24
24
|
|
25
25
|
collection_day = kwargs.get("paon")
|
26
|
+
garden_collection_week = kwargs.get("postcode")
|
26
27
|
bindata = {"bins": []}
|
27
28
|
|
28
29
|
days_of_week = [
|
@@ -35,10 +36,14 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
35
36
|
"Sunday",
|
36
37
|
]
|
37
38
|
|
39
|
+
garden_week = ["Week 1", "Week 2"]
|
40
|
+
|
38
41
|
refusestartDate = datetime(2024, 11, 4)
|
39
42
|
recyclingstartDate = datetime(2024, 11, 11)
|
40
43
|
|
41
44
|
offset_days = days_of_week.index(collection_day)
|
45
|
+
if garden_collection_week:
|
46
|
+
garden_collection = garden_week.index(garden_collection_week)
|
42
47
|
|
43
48
|
refuse_dates = get_dates_every_x_days(refusestartDate, 14, 28)
|
44
49
|
recycling_dates = get_dates_every_x_days(recyclingstartDate, 14, 28)
|
@@ -125,6 +130,63 @@ class CouncilClass(AbstractGetBinDataClass):
|
|
125
130
|
}
|
126
131
|
bindata["bins"].append(dict_data)
|
127
132
|
|
133
|
+
if garden_collection_week:
|
134
|
+
if garden_collection == 0:
|
135
|
+
gardenstartDate = datetime(2024, 11, 11)
|
136
|
+
elif garden_collection == 1:
|
137
|
+
gardenstartDate = datetime(2024, 11, 4)
|
138
|
+
|
139
|
+
garden_dates = get_dates_every_x_days(gardenstartDate, 14, 28)
|
140
|
+
|
141
|
+
garden_bank_holidays = [
|
142
|
+
("23/12/2024", 1),
|
143
|
+
("24/12/2024", 1),
|
144
|
+
("25/12/2024", 1),
|
145
|
+
("26/12/2024", 1),
|
146
|
+
("27/12/2024", 1),
|
147
|
+
("30/12/2024", 1),
|
148
|
+
("31/12/2024", 1),
|
149
|
+
("01/01/2025", 1),
|
150
|
+
("02/01/2025", 1),
|
151
|
+
("03/01/2025", 1),
|
152
|
+
]
|
153
|
+
|
154
|
+
for gardenDate in garden_dates:
|
155
|
+
|
156
|
+
collection_date = (
|
157
|
+
datetime.strptime(gardenDate, "%d/%m/%Y")
|
158
|
+
+ timedelta(days=offset_days)
|
159
|
+
).strftime("%d/%m/%Y")
|
160
|
+
|
161
|
+
garden_holiday = next(
|
162
|
+
(
|
163
|
+
value
|
164
|
+
for date, value in garden_bank_holidays
|
165
|
+
if date == collection_date
|
166
|
+
),
|
167
|
+
0,
|
168
|
+
)
|
169
|
+
|
170
|
+
if garden_holiday > 0:
|
171
|
+
continue
|
172
|
+
|
173
|
+
holiday_offset = next(
|
174
|
+
(value for date, value in bank_holidays if date == collection_date),
|
175
|
+
0,
|
176
|
+
)
|
177
|
+
|
178
|
+
if holiday_offset > 0:
|
179
|
+
collection_date = (
|
180
|
+
datetime.strptime(collection_date, "%d/%m/%Y")
|
181
|
+
+ timedelta(days=holiday_offset)
|
182
|
+
).strftime("%d/%m/%Y")
|
183
|
+
|
184
|
+
dict_data = {
|
185
|
+
"type": "Garden Bin",
|
186
|
+
"collectionDate": collection_date,
|
187
|
+
}
|
188
|
+
bindata["bins"].append(dict_data)
|
189
|
+
|
128
190
|
bindata["bins"].sort(
|
129
191
|
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
|
130
192
|
)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
import requests
|
4
|
+
from bs4 import BeautifulSoup
|
5
|
+
|
6
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
7
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
8
|
+
|
9
|
+
|
10
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
11
|
+
class CouncilClass(AbstractGetBinDataClass):
|
12
|
+
"""
|
13
|
+
Concrete classes have to implement all abstract operations of the
|
14
|
+
base class. They can also override some operations with a default
|
15
|
+
implementation.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
19
|
+
|
20
|
+
user_postcode = kwargs.get("postcode")
|
21
|
+
user_uprn = kwargs.get("uprn")
|
22
|
+
check_postcode(user_postcode)
|
23
|
+
check_uprn(user_uprn)
|
24
|
+
bindata = {"bins": []}
|
25
|
+
|
26
|
+
URI = "https://www.braintree.gov.uk/xfp/form/554"
|
27
|
+
|
28
|
+
response = requests.get(URI)
|
29
|
+
soup = BeautifulSoup(response.content, "html.parser")
|
30
|
+
token = (soup.find("input", {"name": "__token"})).get("value")
|
31
|
+
|
32
|
+
headers = {
|
33
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
34
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
|
35
|
+
"Referer": "https://www.braintree.gov.uk/xfp/form/554",
|
36
|
+
}
|
37
|
+
|
38
|
+
form_data = {
|
39
|
+
"__token": token,
|
40
|
+
"page": "5730",
|
41
|
+
"locale": "en_GB",
|
42
|
+
"qe15dda0155d237d1ea161004d1839e3369ed4831_0_0": user_postcode,
|
43
|
+
"qe15dda0155d237d1ea161004d1839e3369ed4831_1_0": user_uprn,
|
44
|
+
"next": "Next",
|
45
|
+
}
|
46
|
+
collection_lookup = requests.post(URI, data=form_data, headers=headers)
|
47
|
+
collection_lookup.raise_for_status()
|
48
|
+
for results in BeautifulSoup(collection_lookup.text, "html.parser").find_all(
|
49
|
+
"div", class_="date_display"
|
50
|
+
):
|
51
|
+
collection_info = results.text.strip().split("\n")
|
52
|
+
collection_type = collection_info[0].strip()
|
53
|
+
|
54
|
+
# Skip if no collection date is found
|
55
|
+
if len(collection_info) < 2:
|
56
|
+
continue
|
57
|
+
|
58
|
+
collection_date = collection_info[1].strip()
|
59
|
+
|
60
|
+
dict_data = {
|
61
|
+
"type": collection_type,
|
62
|
+
"collectionDate": collection_date,
|
63
|
+
}
|
64
|
+
bindata["bins"].append(dict_data)
|
65
|
+
|
66
|
+
bindata["bins"].sort(
|
67
|
+
key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
|
68
|
+
)
|
69
|
+
|
70
|
+
return bindata
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
import requests
|
4
|
+
|
5
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
6
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
7
|
+
|
8
|
+
|
9
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
10
|
+
class CouncilClass(AbstractGetBinDataClass):
|
11
|
+
"""
|
12
|
+
Concrete classes have to implement all abstract operations of the
|
13
|
+
base class. They can also override some operations with a default
|
14
|
+
implementation.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
18
|
+
|
19
|
+
user_uprn = kwargs.get("uprn")
|
20
|
+
check_uprn(user_uprn)
|
21
|
+
bindata = {"bins": []}
|
22
|
+
|
23
|
+
SESSION_URL = "https://your.burnley.gov.uk/authapi/isauthenticated?uri=https%253A%252F%252Fyour.burnley.gov.uk%252Fen%252FAchieveForms%252F%253Fform_uri%253Dsandbox-publish%253A%252F%252FAF-Process-b41dcd03-9a98-41be-93ba-6c172ba9f80c%252FAF-Stage-edb97458-fc4d-4316-b6e0-85598ec7fce8%252Fdefinition.json%2526redirectlink%253D%25252Fen%2526cancelRedirectLink%253D%25252Fen%2526consentMessage%253Dyes&hostname=your.burnley.gov.uk&withCredentials=true"
|
24
|
+
|
25
|
+
API_URL = "https://your.burnley.gov.uk/apibroker/runLookup"
|
26
|
+
|
27
|
+
headers = {
|
28
|
+
"Content-Type": "application/json",
|
29
|
+
"Accept": "application/json",
|
30
|
+
"User-Agent": "Mozilla/5.0",
|
31
|
+
"X-Requested-With": "XMLHttpRequest",
|
32
|
+
"Referer": "https://your.burnley.gov.uk/fillform/?iframe_id=fillform-frame-1&db_id=",
|
33
|
+
}
|
34
|
+
s = requests.session()
|
35
|
+
r = s.get(SESSION_URL)
|
36
|
+
r.raise_for_status()
|
37
|
+
session_data = r.json()
|
38
|
+
sid = session_data["auth-session"]
|
39
|
+
|
40
|
+
data = {
|
41
|
+
"formValues": {
|
42
|
+
"Section 1": {
|
43
|
+
"case_uprn1": {
|
44
|
+
"value": user_uprn,
|
45
|
+
}
|
46
|
+
},
|
47
|
+
},
|
48
|
+
}
|
49
|
+
|
50
|
+
params = {
|
51
|
+
"id": "607fe757df87c",
|
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,
|
60
|
+
}
|
61
|
+
|
62
|
+
r = s.post(API_URL, json=data, headers=headers, params=params)
|
63
|
+
r.raise_for_status()
|
64
|
+
|
65
|
+
data = r.json()
|
66
|
+
rows_data = data["integration"]["transformed"]["rows_data"]
|
67
|
+
if not isinstance(rows_data, dict):
|
68
|
+
raise ValueError("Invalid data returned from API")
|
69
|
+
|
70
|
+
current_year = (datetime.now()).year
|
71
|
+
for key, value in rows_data.items():
|
72
|
+
bin_type = value["display"].split(" - ")[0]
|
73
|
+
collection_date = datetime.strptime(
|
74
|
+
value["display"].split(" - ")[1], "%A %d %B"
|
75
|
+
)
|
76
|
+
|
77
|
+
if collection_date.month == 1:
|
78
|
+
collection_date = collection_date.replace(year=current_year + 1)
|
79
|
+
else:
|
80
|
+
collection_date = collection_date.replace(year=current_year)
|
81
|
+
|
82
|
+
dict_data = {
|
83
|
+
"type": bin_type,
|
84
|
+
"collectionDate": collection_date.strftime(date_format),
|
85
|
+
}
|
86
|
+
bindata["bins"].append(dict_data)
|
87
|
+
|
88
|
+
return bindata
|
@@ -0,0 +1,93 @@
|
|
1
|
+
from xml.etree import ElementTree
|
2
|
+
|
3
|
+
from bs4 import BeautifulSoup
|
4
|
+
|
5
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
6
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
7
|
+
|
8
|
+
|
9
|
+
class CouncilClass(AbstractGetBinDataClass):
|
10
|
+
"""
|
11
|
+
Concrete classes have to implement all abstract operations of the
|
12
|
+
baseclass. They can also override some
|
13
|
+
operations with a default implementation.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
17
|
+
uprn = kwargs.get("uprn")
|
18
|
+
check_uprn(uprn)
|
19
|
+
council = "CPL"
|
20
|
+
|
21
|
+
# Make SOAP request
|
22
|
+
headers = {
|
23
|
+
"Content-Type": "text/xml; charset=UTF-8",
|
24
|
+
"Referer": "https://collections-copeland.azurewebsites.net/calendar.html",
|
25
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
26
|
+
}
|
27
|
+
requests.packages.urllib3.disable_warnings()
|
28
|
+
post_data = (
|
29
|
+
'<?xml version="1.0" encoding="utf-8"?>'
|
30
|
+
'<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'
|
31
|
+
'<soap:Body><getRoundCalendarForUPRN xmlns="http://webaspx-collections.azurewebsites.net/">'
|
32
|
+
"<council>" + council + "</council><UPRN>" + uprn + "</UPRN>"
|
33
|
+
"<from>Chtml</from></getRoundCalendarForUPRN></soap:Body></soap:Envelope>"
|
34
|
+
)
|
35
|
+
response = requests.post(
|
36
|
+
"https://collections-copeland.azurewebsites.net/WSCollExternal.asmx",
|
37
|
+
headers=headers,
|
38
|
+
data=post_data,
|
39
|
+
)
|
40
|
+
|
41
|
+
if response.status_code != 200:
|
42
|
+
raise ValueError("No bin data found for provided UPRN.")
|
43
|
+
|
44
|
+
# Get HTML from SOAP response
|
45
|
+
xmltree = ElementTree.fromstring(response.text)
|
46
|
+
html = xmltree.find(
|
47
|
+
".//{http://webaspx-collections.azurewebsites.net/}getRoundCalendarForUPRNResult"
|
48
|
+
).text
|
49
|
+
# Parse with BS4
|
50
|
+
soup = BeautifulSoup(html, features="html.parser")
|
51
|
+
soup.prettify()
|
52
|
+
|
53
|
+
data = {"bins": []}
|
54
|
+
for bin_type in ["Refuse", "Recycling", "Garden"]:
|
55
|
+
bin_el = soup.find("b", string=bin_type)
|
56
|
+
if bin_el:
|
57
|
+
bin_info = bin_el.next_sibling.split(": ")[1]
|
58
|
+
collection_date = ""
|
59
|
+
results = re.search("([A-Za-z]+ \\d\\d? [A-Za-z]+) then", bin_info)
|
60
|
+
if results:
|
61
|
+
if results[1] == "Today":
|
62
|
+
date = datetime.now()
|
63
|
+
elif results[1] == "Tomorrow":
|
64
|
+
date = datetime.now() + timedelta(days=1)
|
65
|
+
else:
|
66
|
+
date = get_next_occurrence_from_day_month(
|
67
|
+
datetime.strptime(
|
68
|
+
results[1] + " " + datetime.now().strftime("%Y"),
|
69
|
+
"%a %d %b %Y",
|
70
|
+
)
|
71
|
+
)
|
72
|
+
if date:
|
73
|
+
collection_date = date.strftime(date_format)
|
74
|
+
else:
|
75
|
+
results2 = re.search("([A-Za-z]+) then", bin_info)
|
76
|
+
if results2:
|
77
|
+
if results2[1] == "Today":
|
78
|
+
collection_date = datetime.now().strftime(date_format)
|
79
|
+
elif results2[1] == "Tomorrow":
|
80
|
+
collection_date = (
|
81
|
+
datetime.now() + timedelta(days=1)
|
82
|
+
).strftime(date_format)
|
83
|
+
else:
|
84
|
+
collection_date = results2[1]
|
85
|
+
|
86
|
+
if collection_date != "":
|
87
|
+
dict_data = {
|
88
|
+
"type": bin_type,
|
89
|
+
"collectionDate": collection_date,
|
90
|
+
}
|
91
|
+
data["bins"].append(dict_data)
|
92
|
+
|
93
|
+
return data
|