uk_bin_collection 0.117.0__py3-none-any.whl → 0.119.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (20) hide show
  1. uk_bin_collection/tests/input.json +75 -2
  2. uk_bin_collection/uk_bin_collection/councils/AberdeenCityCouncil.py +122 -0
  3. uk_bin_collection/uk_bin_collection/councils/BaberghDistrictCouncil.py +62 -0
  4. uk_bin_collection/uk_bin_collection/councils/BraintreeDistrictCouncil.py +70 -0
  5. uk_bin_collection/uk_bin_collection/councils/BurnleyBoroughCouncil.py +88 -0
  6. uk_bin_collection/uk_bin_collection/councils/CopelandBoroughCouncil.py +93 -0
  7. uk_bin_collection/uk_bin_collection/councils/CrawleyBoroughCouncil.py +91 -40
  8. uk_bin_collection/uk_bin_collection/councils/EdinburghCityCouncil.py +98 -0
  9. uk_bin_collection/uk_bin_collection/councils/ExeterCityCouncil.py +52 -0
  10. uk_bin_collection/uk_bin_collection/councils/MidSuffolkDistrictCouncil.py +62 -0
  11. uk_bin_collection/uk_bin_collection/councils/RotherDistrictCouncil.py +84 -0
  12. uk_bin_collection/uk_bin_collection/councils/SouthHamsDistrictCouncil.py +90 -0
  13. uk_bin_collection/uk_bin_collection/councils/StevenageBoroughCouncil.py +101 -0
  14. uk_bin_collection/uk_bin_collection/councils/ThanetDistrictCouncil.py +51 -0
  15. uk_bin_collection/uk_bin_collection/councils/WolverhamptonCityCouncil.py +57 -0
  16. {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/METADATA +1 -1
  17. {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/RECORD +20 -9
  18. {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/LICENSE +0 -0
  19. {uk_bin_collection-0.117.0.dist-info → uk_bin_collection-0.119.0.dist-info}/WHEEL +0 -0
  20. {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