uk_bin_collection 0.134.3__py3-none-any.whl → 0.135.1__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.
Files changed (23) hide show
  1. uk_bin_collection/tests/check_selenium_url_in_input.json.py +209 -0
  2. uk_bin_collection/tests/input.json +49 -4
  3. uk_bin_collection/uk_bin_collection/councils/AmberValleyBoroughCouncil.py +60 -0
  4. uk_bin_collection/uk_bin_collection/councils/BolsoverCouncil.py +298 -0
  5. uk_bin_collection/uk_bin_collection/councils/CherwellDistrictCouncil.py +75 -0
  6. uk_bin_collection/uk_bin_collection/councils/ConwyCountyBorough.py +11 -3
  7. uk_bin_collection/uk_bin_collection/councils/CotswoldDistrictCouncil.py +3 -5
  8. uk_bin_collection/uk_bin_collection/councils/DerbyshireDalesDistrictCouncil.py +54 -50
  9. uk_bin_collection/uk_bin_collection/councils/EpsomandEwellBoroughCouncil.py +86 -0
  10. uk_bin_collection/uk_bin_collection/councils/GloucesterCityCouncil.py +1 -1
  11. uk_bin_collection/uk_bin_collection/councils/LeedsCityCouncil.py +2 -1
  12. uk_bin_collection/uk_bin_collection/councils/MiddlesbroughCouncil.py +100 -0
  13. uk_bin_collection/uk_bin_collection/councils/NeathPortTalbotCouncil.py +2 -0
  14. uk_bin_collection/uk_bin_collection/councils/RedcarandClevelandCouncil.py +108 -0
  15. uk_bin_collection/uk_bin_collection/councils/RunnymedeBoroughCouncil.py +54 -0
  16. uk_bin_collection/uk_bin_collection/councils/SunderlandCityCouncil.py +21 -15
  17. uk_bin_collection/uk_bin_collection/councils/TendringDistrictCouncil.py +1 -1
  18. uk_bin_collection/uk_bin_collection/councils/TorridgeDistrictCouncil.py +1 -35
  19. {uk_bin_collection-0.134.3.dist-info → uk_bin_collection-0.135.1.dist-info}/METADATA +1 -1
  20. {uk_bin_collection-0.134.3.dist-info → uk_bin_collection-0.135.1.dist-info}/RECORD +23 -15
  21. {uk_bin_collection-0.134.3.dist-info → uk_bin_collection-0.135.1.dist-info}/LICENSE +0 -0
  22. {uk_bin_collection-0.134.3.dist-info → uk_bin_collection-0.135.1.dist-info}/WHEEL +0 -0
  23. {uk_bin_collection-0.134.3.dist-info → uk_bin_collection-0.135.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,209 @@
1
+ import json
2
+ import requests
3
+ import sys
4
+ import base64
5
+ from tabulate import tabulate
6
+
7
+
8
+ def get_council_files(repo, branch):
9
+ """
10
+ Get a list of all .py council files in the 'councils' directory
11
+ from the GitHub repo (via API), plus a mapping from council name
12
+ to the file's GitHub 'download_url' or 'contents_url'.
13
+ """
14
+ url = f"https://api.github.com/repos/{repo}/contents/uk_bin_collection/uk_bin_collection/councils?ref={branch}"
15
+ print(f"Fetching council files from: {url}")
16
+ response = requests.get(url, headers={"Accept": "application/vnd.github.v3+json"})
17
+ if response.status_code == 200:
18
+ data = response.json()
19
+ # data should be a list of items in that folder
20
+ if isinstance(data, list):
21
+ councils = {}
22
+ for item in data:
23
+ name = item["name"]
24
+ if name.endswith(".py"):
25
+ council_name = name.replace(".py", "")
26
+ councils[council_name] = item["url"] # 'url' gives API-based content URL
27
+ return councils
28
+ else:
29
+ raise ValueError("Expected a list from the GitHub response but got something else.")
30
+ else:
31
+ print(f"Failed to fetch councils from files: {response.content}")
32
+ return {}
33
+
34
+
35
+ def get_council_file_content(api_url):
36
+ """
37
+ Given the API URL for a file in GitHub, fetch its content (decoded).
38
+ The 'download_url' is direct raw, but the 'url' is the API URL for the content.
39
+ We'll use the latter, decode base64, and return the text.
40
+ """
41
+ # Example: https://api.github.com/repos/robbrad/UKBinCollectionData/contents/...
42
+ response = requests.get(api_url, headers={"Accept": "application/vnd.github.v3+json"})
43
+ if response.status_code == 200:
44
+ file_json = response.json()
45
+ # file_json["content"] is base64-encoded
46
+ content = file_json.get("content", "")
47
+ decoded = base64.b64decode(content).decode("utf-8")
48
+ return decoded
49
+ else:
50
+ print(f"Failed to fetch file content: {response.content}")
51
+ return ""
52
+
53
+
54
+ def get_input_json_data(repo, branch):
55
+ """
56
+ Fetch the entire input.json from GitHub and return it as a Python dict.
57
+ """
58
+ url = f"https://api.github.com/repos/{repo}/contents/uk_bin_collection/tests/input.json?ref={branch}"
59
+ print(f"Fetching input JSON from: {url}")
60
+ response = requests.get(url, headers={"Accept": "application/vnd.github.v3+json"})
61
+ if response.status_code == 200:
62
+ try:
63
+ file_json = response.json()
64
+ content = file_json.get("content", "")
65
+ decoded = base64.b64decode(content).decode("utf-8")
66
+ data = json.loads(decoded)
67
+ return data
68
+ except json.JSONDecodeError as e:
69
+ print(f"JSON decoding error: {e}")
70
+ raise
71
+ else:
72
+ print(f"Failed to fetch input JSON: {response.content}")
73
+ return {}
74
+
75
+
76
+ def council_needs_update(council_name, json_data, council_file_content):
77
+ """
78
+ Check if the given council needs an update:
79
+ - We say 'needs update' if 'web_driver' is missing in the JSON,
80
+ BUT the script uses 'create_webdriver' in code.
81
+ """
82
+ # If the council isn't in the JSON at all, we can't do the check
83
+ # (or we assume no JSON data => no web_driver?).
84
+ council_data = json_data.get(council_name, {})
85
+ web_driver_missing = ("web_driver" not in council_data)
86
+ create_webdriver_present = ("create_webdriver" in council_file_content)
87
+
88
+ return web_driver_missing and create_webdriver_present
89
+
90
+
91
+ def compare_councils(file_council_dict, json_data):
92
+ """
93
+ Compare councils in files vs councils in JSON, check for needs_update,
94
+ and gather everything for final tabulation.
95
+
96
+ Returns:
97
+ - all_councils_data: dict keyed by council name:
98
+ {
99
+ "in_files": bool,
100
+ "in_json": bool,
101
+ "discrepancies_count": int,
102
+ "needs_update": bool
103
+ }
104
+ - any_discrepancies_found: bool (if any differences in in_files vs in_json)
105
+ - any_updates_needed: bool (if any council needs update)
106
+ """
107
+ file_councils = set(file_council_dict.keys())
108
+ json_councils = set(json_data.keys())
109
+
110
+ all_councils = file_councils.union(json_councils)
111
+ all_council_data = {}
112
+
113
+ any_discrepancies_found = False
114
+ any_updates_needed = False
115
+
116
+ for council in all_councils:
117
+ in_files = council in file_councils
118
+ in_json = council in json_councils
119
+ # Count how many are False
120
+ discrepancies_count = [in_files, in_json].count(False)
121
+
122
+ # If the file is in the repo, fetch its content for checking
123
+ content = ""
124
+ if in_files:
125
+ file_api_url = file_council_dict[council]
126
+ content = get_council_file_content(file_api_url)
127
+
128
+ # Evaluate "needs_update" only if the file is in place
129
+ # (If there's no file, you might consider it "False" by default)
130
+ needs_update = False
131
+ if in_files:
132
+ needs_update = council_needs_update(council, json_data, content)
133
+
134
+ if discrepancies_count > 0:
135
+ any_discrepancies_found = True
136
+ if needs_update:
137
+ any_updates_needed = True
138
+
139
+ all_council_data[council] = {
140
+ "in_files": in_files,
141
+ "in_json": in_json,
142
+ "discrepancies_count": discrepancies_count,
143
+ "needs_update": needs_update,
144
+ }
145
+
146
+ return all_council_data, any_discrepancies_found, any_updates_needed
147
+
148
+
149
+ def main(repo="robbrad/UKBinCollectionData", branch="master"):
150
+ print(f"Starting comparison for repo: {repo}, branch: {branch}")
151
+
152
+ # 1) Get council file data (dict: { council_name: content_api_url, ... })
153
+ file_council_dict = get_council_files(repo, branch)
154
+
155
+ # 2) Get the entire JSON data
156
+ json_data = get_input_json_data(repo, branch)
157
+
158
+ # 3) Compare
159
+ (
160
+ all_councils_data,
161
+ discrepancies_found,
162
+ updates_needed,
163
+ ) = compare_councils(file_council_dict, json_data)
164
+
165
+ # 4) Print results
166
+ table_data = []
167
+ headers = ["Council Name", "In Files", "In JSON", "Needs Update?", "Discrepancies"]
168
+ # Sort councils so that ones with the highest discrepancy or update appear first
169
+ # Then alphabetical if tie:
170
+ def sort_key(item):
171
+ # item is (council_name, data_dict)
172
+ return (
173
+ item[1]["needs_update"], # sort by needs_update (False < True)
174
+ item[1]["discrepancies_count"], # then by discrepancies
175
+ item[0], # then by name
176
+ )
177
+
178
+ # We'll sort descending for "needs_update", so invert the boolean or reverse later
179
+ sorted_councils = sorted(
180
+ all_councils_data.items(),
181
+ key=lambda x: (not x[1]["needs_update"], x[1]["discrepancies_count"], x[0])
182
+ )
183
+
184
+ for council, presence in sorted_councils:
185
+ row = [
186
+ council,
187
+ "✔" if presence["in_files"] else "✘",
188
+ "✔" if presence["in_json"] else "✘",
189
+ "Yes" if presence["needs_update"] else "No",
190
+ presence["discrepancies_count"],
191
+ ]
192
+ table_data.append(row)
193
+
194
+ print(tabulate(table_data, headers=headers, tablefmt="grid"))
195
+
196
+ # 5) Determine exit code:
197
+ # If any discrepancies OR any council needs updates -> fail
198
+ if discrepancies_found or updates_needed:
199
+ print("Some discrepancies found or updates are needed. Failing workflow.")
200
+ sys.exit(1)
201
+ else:
202
+ print("No discrepancies found and no updates needed. Workflow successful.")
203
+
204
+
205
+ if __name__ == "__main__":
206
+ # Optional CLI args: python script.py <repo> <branch>
207
+ repo_arg = sys.argv[1] if len(sys.argv) > 1 else "robbrad/UKBinCollectionData"
208
+ branch_arg = sys.argv[2] if len(sys.argv) > 2 else "master"
209
+ main(repo_arg, branch_arg)
@@ -18,6 +18,12 @@
18
18
  "wiki_name": "Adur and Worthing Councils",
19
19
  "wiki_note": "Replace XXXXXXXX with your UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find it."
20
20
  },
21
+ "AmberValleyBoroughCouncil": {
22
+ "url": "https://ambervalley.gov.uk",
23
+ "uprn": "100030026621",
24
+ "wiki_name": "Amber Valley Borough Council",
25
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
26
+ },
21
27
  "AntrimAndNewtonabbeyCouncil": {
22
28
  "url": "https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule/?Id=643",
23
29
  "wiki_command_url_override": "https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule/?Id=XXXX",
@@ -197,6 +203,12 @@
197
203
  "wiki_name": "Blaenau Gwent County Borough Council",
198
204
  "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
199
205
  },
206
+ "BolsoverCouncil": {
207
+ "url": "https://bolsover.gov.uk",
208
+ "uprn": "100030066827",
209
+ "wiki_name": "Bolsover Council",
210
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
211
+ },
200
212
  "BoltonCouncil": {
201
213
  "postcode": "BL1 5PQ",
202
214
  "skip_get_url": true,
@@ -391,6 +403,12 @@
391
403
  "wiki_name": "Cheltenham Borough Council",
392
404
  "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
393
405
  },
406
+ "CherwellDistrictCouncil": {
407
+ "uprn": "100121292407",
408
+ "url": "https://www.cherwell.gov.uk",
409
+ "wiki_name": "Cherwell District Council",
410
+ "wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
411
+ },
394
412
  "CheshireEastCouncil": {
395
413
  "url": "https://online.cheshireeast.gov.uk/MyCollectionDay/SearchByAjax/GetBartecJobList?uprn=100012791226&onelineaddress=3%20COBBLERS%20YARD,%20SK9%207DZ&_=1689413260149",
396
414
  "wiki_command_url_override": "https://online.cheshireeast.gov.uk/MyCollectionDay/SearchByAjax/GetBartecJobList?uprn=XXXXXXXX&onelineaddress=XXXXXXXX&_=1689413260149",
@@ -440,11 +458,10 @@
440
458
  "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes."
441
459
  },
442
460
  "ConwyCountyBorough": {
443
- "postcode": "LL30 2DF",
444
461
  "uprn": "100100429249",
445
- "url": "https://www.conwy.gov.uk/Contensis-Forms/erf/collection-result-soap-xmas.asp?ilangid=1&uprn=100100429249",
462
+ "url": "https://www.conwy.gov.uk",
446
463
  "wiki_name": "Conwy County Borough Council",
447
- "wiki_note": "Conwy County Borough Council uses a straight UPRN in the URL, e.g., `&uprn=XXXXXXXXXXXXX`."
464
+ "wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
448
465
  },
449
466
  "CopelandBoroughCouncil": {
450
467
  "uprn": "100110734613",
@@ -535,7 +552,6 @@
535
552
  "skip_get_url": true,
536
553
  "uprn": "10070102161",
537
554
  "url": "https://www.derbyshiredales.gov.uk/",
538
- "web_driver": "http://selenium:4444",
539
555
  "wiki_name": "Derbyshire Dales District Council",
540
556
  "wiki_note": "Pass the UPRN and postcode. To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
541
557
  },
@@ -709,6 +725,12 @@
709
725
  "wiki_name": "Epping Forest District Council",
710
726
  "wiki_note": "Replace the postcode in the URL with your own."
711
727
  },
728
+ "EpsomandEwellBoroughCouncil": {
729
+ "uprn": "100061349083",
730
+ "url": "https://www.epsom-ewell.gov.uk",
731
+ "wiki_name": "Epsom and Ewell Borough Council",
732
+ "wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
733
+ },
712
734
  "ErewashBoroughCouncil": {
713
735
  "skip_get_url": true,
714
736
  "uprn": "10003582028",
@@ -1162,6 +1184,14 @@
1162
1184
  "wiki_name": "Mid Devon Council",
1163
1185
  "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
1164
1186
  },
1187
+ "MiddlesbroughCouncil": {
1188
+ "house_number": "12 Constantine Court Park Road North, Middlesbrough",
1189
+ "skip_get_url": true,
1190
+ "url": "https://www.midsussex.gov.uk/waste-recycling/bin-collection/",
1191
+ "web_driver": "http://selenium:4444",
1192
+ "wiki_name": "Middlesbrough Council",
1193
+ "wiki_note": "Pass the entire address without postcode as it appears when you type it on the website. This parser requires a Selenium webdriver."
1194
+ },
1165
1195
  "MidSuffolkDistrictCouncil": {
1166
1196
  "house_number": "Monday",
1167
1197
  "postcode": "Week 2",
@@ -1457,6 +1487,14 @@
1457
1487
  "wiki_name": "Reading Borough Council",
1458
1488
  "wiki_note": "Replace XXXXXXXX with your property's UPRN."
1459
1489
  },
1490
+ "RedcarandClevelandCouncil": {
1491
+ "house_number": "11",
1492
+ "postcode": "TS10 2RE",
1493
+ "skip_get_url": true,
1494
+ "url": "https://www.redcar-cleveland.gov.uk",
1495
+ "wiki_name": "Redcar and Cleveland Council",
1496
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters"
1497
+ },
1460
1498
  "RedditchBoroughCouncil": {
1461
1499
  "uprn": "10094557691",
1462
1500
  "url": "https://redditchbc.gov.uk",
@@ -1531,6 +1569,13 @@
1531
1569
  "wiki_name": "Rugby Borough Council",
1532
1570
  "wiki_note": "Provide your UPRN and postcode. You can find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
1533
1571
  },
1572
+ "RunnymedeBoroughCouncil": {
1573
+ "skip_get_url": true,
1574
+ "uprn": "100061483636",
1575
+ "url": "https://www.runnymede.gov.uk/",
1576
+ "wiki_name": "Runnymede Borough Council",
1577
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
1578
+ },
1534
1579
  "RushcliffeBoroughCouncil": {
1535
1580
  "postcode": "NG13 8TZ",
1536
1581
  "skip_get_url": true,
@@ -0,0 +1,60 @@
1
+ from datetime import datetime
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
+ WASTE_TYPES_DATE_KEY = {
24
+ "REFUSE": "refuseNextDate",
25
+ "RECYCLING": "recyclingNextDate",
26
+ "GREEN": "greenNextDate",
27
+ "COMMUNAL REFUSE": "communalRefNextDate",
28
+ "COMMUNAL RECYCLING": "communalRycNextDate",
29
+ }
30
+
31
+ URI = f"https://info.ambervalley.gov.uk/WebServices/AVBCFeeds/WasteCollectionJSON.asmx/GetCollectionDetailsByUPRN?uprn={user_uprn}"
32
+
33
+ # Make the GET request
34
+ response = requests.get(URI)
35
+
36
+ # Parse the JSON response
37
+ bin_collection = response.json()
38
+
39
+ # print(bin_collection)
40
+
41
+ for bin, datge_key in WASTE_TYPES_DATE_KEY.items():
42
+ date_ = datetime.strptime(
43
+ bin_collection[datge_key], "%Y-%m-%dT%H:%M:%S"
44
+ ).strftime(date_format)
45
+ if date_ == "01/01/1":
46
+ continue
47
+ elif date_ == "01/01/1900":
48
+ continue
49
+
50
+ dict_data = {
51
+ "type": bin,
52
+ "collectionDate": date_,
53
+ }
54
+ bindata["bins"].append(dict_data)
55
+
56
+ bindata["bins"].sort(
57
+ key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
58
+ )
59
+
60
+ return bindata
@@ -0,0 +1,298 @@
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://selfservice.bolsover.gov.uk/authapi/isauthenticated?uri=https%253A%252F%252Fselfservice.bolsover.gov.uk%252Fservice%252FCheck_your_Bin_Day&hostname=selfservice.bolsover.gov.uk&withCredentials=true"
24
+
25
+ API_URL = "https://selfservice.bolsover.gov.uk/apibroker/runLookup"
26
+
27
+ data = {
28
+ "formValues": {"Bin Collection": {"uprnLoggedIn": {"value": user_uprn}}},
29
+ }
30
+ headers = {
31
+ "Content-Type": "application/json",
32
+ "Accept": "application/json",
33
+ "User-Agent": "Mozilla/5.0",
34
+ "X-Requested-With": "XMLHttpRequest",
35
+ "Referer": "https://selfservice.bolsover.gov.uk/fillform/?iframe_id=fillform-frame-1&db_id=",
36
+ }
37
+ s = requests.session()
38
+ r = s.get(SESSION_URL)
39
+ r.raise_for_status()
40
+ session_data = r.json()
41
+ sid = session_data["auth-session"]
42
+ params = {
43
+ "id": "6023d37e037c3",
44
+ "repeat_against": "",
45
+ "noRetry": "true",
46
+ "getOnlyTokens": "undefined",
47
+ "log_id": "",
48
+ "app_name": "AF-Renderer::Self",
49
+ # unix_timestamp
50
+ "_": str(int(time.time() * 1000)),
51
+ "sid": sid,
52
+ }
53
+
54
+ r = s.post(API_URL, json=data, headers=headers, params=params)
55
+ r.raise_for_status()
56
+
57
+ data = r.json()
58
+ rows_data = data["integration"]["transformed"]["rows_data"]["0"]
59
+ if not isinstance(rows_data, dict):
60
+ raise ValueError("Invalid data returned from API")
61
+
62
+ # print(rows_data)
63
+
64
+ route = rows_data["Route"]
65
+
66
+ # print(route)
67
+
68
+ def get_route_number(route):
69
+ if route[:2] == "Mo":
70
+ return 0
71
+ elif route[:2] == "Tu":
72
+ return 1
73
+ elif route[:2] == "We":
74
+ return 2
75
+ elif route[:2] == "Th":
76
+ return 3
77
+ elif route[:2] == "Fr":
78
+ return 4
79
+ else:
80
+ return None # Default case if none of the conditions match
81
+
82
+ dayOfCollectionAsNumber = get_route_number(route)
83
+ # print(dayOfCollectionAsNumber)
84
+
85
+ def calculate_collection_date(
86
+ dayOfCollectionAsNumber,
87
+ currentDayAsNumber,
88
+ today,
89
+ dayDiffPlus,
90
+ dayDiffMinus,
91
+ ):
92
+ if dayOfCollectionAsNumber == currentDayAsNumber:
93
+ return today
94
+ elif dayOfCollectionAsNumber > currentDayAsNumber:
95
+ return today + timedelta(days=dayDiffPlus)
96
+ else:
97
+ return today + timedelta(days=dayDiffMinus)
98
+
99
+ # Example usage
100
+ today = datetime.today() # Current date
101
+ currentDayAsNumber = today.weekday()
102
+ dayDiffPlus = dayOfCollectionAsNumber - currentDayAsNumber
103
+ dayDiffMinus = dayOfCollectionAsNumber - currentDayAsNumber + 7
104
+
105
+ week1 = calculate_collection_date(
106
+ dayOfCollectionAsNumber,
107
+ currentDayAsNumber,
108
+ today,
109
+ dayDiffPlus,
110
+ dayDiffMinus,
111
+ )
112
+ week2 = week1 + timedelta(days=7)
113
+ week3 = week2 + timedelta(days=7)
114
+ week4 = week3 + timedelta(days=7)
115
+
116
+ # print(week1.strftime(date_format))
117
+ # print(week2.strftime(date_format))
118
+ # print(week3.strftime(date_format))
119
+ # print(week4.strftime(date_format))
120
+
121
+ greenSusStart = datetime.strptime("2024-11-08", "%Y-%m-%d")
122
+ greenSusEnd = datetime.strptime("2025-03-18", "%Y-%m-%d")
123
+
124
+ def is_within_green_sus(dtDay0, greenSusStart, greenSusEnd):
125
+ return "Yes" if greenSusStart <= dtDay0 < greenSusEnd else "No"
126
+
127
+ week1InSus = is_within_green_sus(week1, greenSusStart, greenSusEnd)
128
+ week2InSus = is_within_green_sus(week2, greenSusStart, greenSusEnd)
129
+ week3InSus = is_within_green_sus(week3, greenSusStart, greenSusEnd)
130
+ week4InSus = is_within_green_sus(week4, greenSusStart, greenSusEnd)
131
+
132
+ # print(week1InSus)
133
+ # print(week2InSus)
134
+ # print(week3InSus)
135
+ # print(week4InSus)
136
+
137
+ WeekBlack = rows_data["WeekBlack"]
138
+ WeekBandG = rows_data["WeekBandG"]
139
+
140
+ if WeekBlack == "1":
141
+ WeekBandG = ""
142
+ if WeekBandG == "1":
143
+ WeekBlack = ""
144
+
145
+ def determine_bin_collection_week1(
146
+ txtBlack, txtBurgGreen, dtDay0, today, week1InSus
147
+ ):
148
+ # Check for empty values
149
+ if txtBlack == "" and txtBurgGreen == "":
150
+ return ""
151
+
152
+ # Black Bin Collection
153
+ if txtBlack == "1" and dtDay0 >= today:
154
+ return "Black Bin"
155
+
156
+ # Burgundy Bin Collection
157
+ if txtBurgGreen == "1" and dtDay0 > today:
158
+ if week1InSus == "Yes":
159
+ return "Burgundy Bin"
160
+ elif week1InSus == "No":
161
+ return "Burgundy Bin & Green Bin"
162
+
163
+ # Default cases based on week1InSus
164
+ if txtBlack == "" and dtDay0 >= today:
165
+ if week1InSus == "Yes":
166
+ return "Burgundy Bin"
167
+ elif week1InSus == "No":
168
+ return "Burgundy Bin & Green Bin"
169
+
170
+ return "" # Default empty case
171
+
172
+ def determine_bin_collection_week2(
173
+ txtBlack, txtBurgGreen, dtDay7, today, week2InSus
174
+ ):
175
+ # Check for empty values
176
+ if txtBlack == "" and txtBurgGreen == "":
177
+ return ""
178
+
179
+ # Black Bin Collection
180
+ if txtBlack == "" and dtDay7 >= today:
181
+ return "Black Bin"
182
+
183
+ # Burgundy Bin Collection (week2InSus check)
184
+ if txtBurgGreen == "1" and dtDay7 > today:
185
+ if week2InSus == "Yes":
186
+ return "Burgundy Bin"
187
+ elif week2InSus == "No":
188
+ return "Burgundy Bin & Green Bin"
189
+
190
+ # Burgundy Bin Collection for txtBlack = '1'
191
+ if txtBlack == "1" and dtDay7 >= today:
192
+ if week2InSus == "Yes":
193
+ return "Burgundy Bin"
194
+ elif week2InSus == "No":
195
+ return "Burgundy Bin & Green Bin"
196
+
197
+ return "" # Default empty case
198
+
199
+ def determine_bin_collection_week3(
200
+ txtBlack, txtBurgGreen, dtDay14, today, week3InSus
201
+ ):
202
+ # Check for empty values
203
+ if txtBlack == "" and txtBurgGreen == "":
204
+ return ""
205
+
206
+ # Black Bin Collection
207
+ if txtBlack == "1" and dtDay14 >= today:
208
+ return "Black Bin"
209
+
210
+ # Burgundy Bin Collection (week3InSus check)
211
+ if txtBurgGreen == "1" and dtDay14 > today:
212
+ if week3InSus == "Yes":
213
+ return "Burgundy Bin"
214
+ elif week3InSus == "No":
215
+ return "Burgundy Bin & Green Bin"
216
+
217
+ # Burgundy Bin Collection for txtBlack = ''
218
+ if txtBlack == "" and dtDay14 >= today:
219
+ if week3InSus == "Yes":
220
+ return "Burgundy Bin"
221
+ elif week3InSus == "No":
222
+ return "Burgundy Bin & Green Bin"
223
+
224
+ return "" # Default empty case
225
+
226
+ def determine_bin_collection_week4(
227
+ txtBlack, txtBurgGreen, dtDay21, today, week4InSus
228
+ ):
229
+ # Check for empty values
230
+ if txtBlack == "" and txtBurgGreen == "":
231
+ return ""
232
+
233
+ # Black Bin Collection
234
+ if txtBlack == "" and dtDay21 >= today:
235
+ return "Black Bin"
236
+
237
+ # Burgundy Bin Collection (week4InSus check)
238
+ if txtBurgGreen == "1" and dtDay21 > today:
239
+ if week4InSus == "Yes":
240
+ return "Burgundy Bin"
241
+ elif week4InSus == "No":
242
+ return "Burgundy Bin & Green Bin"
243
+
244
+ # Burgundy Bin Collection for txtBlack = '1'
245
+ if txtBlack == "1" and dtDay21 >= today:
246
+ if week4InSus == "Yes":
247
+ return "Burgundy Bin"
248
+ elif week4InSus == "No":
249
+ return "Burgundy Bin & Green Bin"
250
+
251
+ return "" # Default empty case
252
+
253
+ week1Text = determine_bin_collection_week1(
254
+ WeekBlack, WeekBandG, week1, today, week1InSus
255
+ )
256
+ week2Text = determine_bin_collection_week2(
257
+ WeekBlack, WeekBandG, week2, today, week2InSus
258
+ )
259
+ week3Text = determine_bin_collection_week3(
260
+ WeekBlack, WeekBandG, week3, today, week3InSus
261
+ )
262
+ week4Text = determine_bin_collection_week4(
263
+ WeekBlack, WeekBandG, week4, today, week4InSus
264
+ )
265
+
266
+ # print(week1Text)
267
+ # print(week2Text)
268
+ # print(week3Text)
269
+ # print(week4Text)
270
+
271
+ week_data = [
272
+ (week1Text, week1),
273
+ (week2Text, week2),
274
+ (week3Text, week3),
275
+ (week4Text, week4),
276
+ ]
277
+
278
+ # print(week_data)
279
+
280
+ # Iterate through the array
281
+ for week_text, week_date in week_data:
282
+ # Check if '&' exists and split
283
+ if "&" in week_text:
284
+ split_texts = [text.strip() for text in week_text.split("&")]
285
+ for text in split_texts:
286
+ dict_data = {
287
+ "type": text,
288
+ "collectionDate": week_date.strftime(date_format),
289
+ }
290
+ bindata["bins"].append(dict_data)
291
+ else:
292
+ dict_data = {
293
+ "type": week_text,
294
+ "collectionDate": week_date.strftime(date_format),
295
+ }
296
+ bindata["bins"].append(dict_data)
297
+
298
+ return bindata