uk_bin_collection 0.74.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/README.rst +0 -0
- uk_bin_collection/tests/council_feature_input_parity.py +79 -0
- uk_bin_collection/tests/features/environment.py +7 -0
- uk_bin_collection/tests/features/validate_council_outputs.feature +767 -0
- uk_bin_collection/tests/input.json +1077 -0
- uk_bin_collection/tests/output.schema +41 -0
- uk_bin_collection/tests/step_defs/step_helpers/file_handler.py +46 -0
- uk_bin_collection/tests/step_defs/test_validate_council.py +87 -0
- uk_bin_collection/tests/test_collect_data.py +104 -0
- uk_bin_collection/tests/test_common_functions.py +342 -0
- uk_bin_collection/uk_bin_collection/collect_data.py +133 -0
- uk_bin_collection/uk_bin_collection/common.py +292 -0
- uk_bin_collection/uk_bin_collection/councils/AdurAndWorthingCouncils.py +43 -0
- uk_bin_collection/uk_bin_collection/councils/ArunCouncil.py +97 -0
- uk_bin_collection/uk_bin_collection/councils/AylesburyValeCouncil.py +69 -0
- uk_bin_collection/uk_bin_collection/councils/BCPCouncil.py +51 -0
- uk_bin_collection/uk_bin_collection/councils/BarnetCouncil.py +180 -0
- uk_bin_collection/uk_bin_collection/councils/BarnsleyMBCouncil.py +109 -0
- uk_bin_collection/uk_bin_collection/councils/BasingstokeCouncil.py +72 -0
- uk_bin_collection/uk_bin_collection/councils/BathAndNorthEastSomersetCouncil.py +100 -0
- uk_bin_collection/uk_bin_collection/councils/BedfordBoroughCouncil.py +49 -0
- uk_bin_collection/uk_bin_collection/councils/BedfordshireCouncil.py +70 -0
- uk_bin_collection/uk_bin_collection/councils/BexleyCouncil.py +147 -0
- uk_bin_collection/uk_bin_collection/councils/BirminghamCityCouncil.py +119 -0
- uk_bin_collection/uk_bin_collection/councils/BlackburnCouncil.py +105 -0
- uk_bin_collection/uk_bin_collection/councils/BoltonCouncil.py +104 -0
- uk_bin_collection/uk_bin_collection/councils/BradfordMDC.py +103 -0
- uk_bin_collection/uk_bin_collection/councils/BrightonandHoveCityCouncil.py +137 -0
- uk_bin_collection/uk_bin_collection/councils/BristolCityCouncil.py +141 -0
- uk_bin_collection/uk_bin_collection/councils/BromleyBoroughCouncil.py +115 -0
- uk_bin_collection/uk_bin_collection/councils/BroxtoweBoroughCouncil.py +107 -0
- uk_bin_collection/uk_bin_collection/councils/BuckinghamshireCouncil.py +95 -0
- uk_bin_collection/uk_bin_collection/councils/BuryCouncil.py +65 -0
- uk_bin_collection/uk_bin_collection/councils/CalderdaleCouncil.py +123 -0
- uk_bin_collection/uk_bin_collection/councils/CannockChaseDistrictCouncil.py +65 -0
- uk_bin_collection/uk_bin_collection/councils/CardiffCouncil.py +172 -0
- uk_bin_collection/uk_bin_collection/councils/CastlepointDistrictCouncil.py +96 -0
- uk_bin_collection/uk_bin_collection/councils/CharnwoodBoroughCouncil.py +54 -0
- uk_bin_collection/uk_bin_collection/councils/ChelmsfordCityCouncil.py +127 -0
- uk_bin_collection/uk_bin_collection/councils/CheshireEastCouncil.py +32 -0
- uk_bin_collection/uk_bin_collection/councils/CheshireWestAndChesterCouncil.py +125 -0
- uk_bin_collection/uk_bin_collection/councils/ChorleyCouncil.py +134 -0
- uk_bin_collection/uk_bin_collection/councils/ConwyCountyBorough.py +27 -0
- uk_bin_collection/uk_bin_collection/councils/CrawleyBoroughCouncil.py +61 -0
- uk_bin_collection/uk_bin_collection/councils/CroydonCouncil.py +291 -0
- uk_bin_collection/uk_bin_collection/councils/DerbyshireDalesDistrictCouncil.py +100 -0
- uk_bin_collection/uk_bin_collection/councils/DoncasterCouncil.py +77 -0
- uk_bin_collection/uk_bin_collection/councils/DorsetCouncil.py +58 -0
- uk_bin_collection/uk_bin_collection/councils/DoverDistrictCouncil.py +41 -0
- uk_bin_collection/uk_bin_collection/councils/DurhamCouncil.py +49 -0
- uk_bin_collection/uk_bin_collection/councils/EastCambridgeshireCouncil.py +44 -0
- uk_bin_collection/uk_bin_collection/councils/EastDevonDC.py +74 -0
- uk_bin_collection/uk_bin_collection/councils/EastLindseyDistrictCouncil.py +108 -0
- uk_bin_collection/uk_bin_collection/councils/EastRidingCouncil.py +142 -0
- uk_bin_collection/uk_bin_collection/councils/EastSuffolkCouncil.py +112 -0
- uk_bin_collection/uk_bin_collection/councils/EastleighBoroughCouncil.py +70 -0
- uk_bin_collection/uk_bin_collection/councils/EnvironmentFirst.py +48 -0
- uk_bin_collection/uk_bin_collection/councils/ErewashBoroughCouncil.py +61 -0
- uk_bin_collection/uk_bin_collection/councils/FenlandDistrictCouncil.py +65 -0
- uk_bin_collection/uk_bin_collection/councils/ForestOfDeanDistrictCouncil.py +113 -0
- uk_bin_collection/uk_bin_collection/councils/GatesheadCouncil.py +118 -0
- uk_bin_collection/uk_bin_collection/councils/GedlingBoroughCouncil.py +1580 -0
- uk_bin_collection/uk_bin_collection/councils/GlasgowCityCouncil.py +55 -0
- uk_bin_collection/uk_bin_collection/councils/GuildfordCouncil.py +150 -0
- uk_bin_collection/uk_bin_collection/councils/HaltonBoroughCouncil.py +142 -0
- uk_bin_collection/uk_bin_collection/councils/HaringeyCouncil.py +59 -0
- uk_bin_collection/uk_bin_collection/councils/HarrogateBoroughCouncil.py +63 -0
- uk_bin_collection/uk_bin_collection/councils/HighPeakCouncil.py +134 -0
- uk_bin_collection/uk_bin_collection/councils/HullCityCouncil.py +48 -0
- uk_bin_collection/uk_bin_collection/councils/HuntingdonDistrictCouncil.py +44 -0
- uk_bin_collection/uk_bin_collection/councils/KingstonUponThamesCouncil.py +84 -0
- uk_bin_collection/uk_bin_collection/councils/KirkleesCouncil.py +130 -0
- uk_bin_collection/uk_bin_collection/councils/KnowsleyMBCouncil.py +139 -0
- uk_bin_collection/uk_bin_collection/councils/LancasterCityCouncil.py +71 -0
- uk_bin_collection/uk_bin_collection/councils/LeedsCityCouncil.py +137 -0
- uk_bin_collection/uk_bin_collection/councils/LisburnCastlereaghCityCouncil.py +101 -0
- uk_bin_collection/uk_bin_collection/councils/LiverpoolCityCouncil.py +65 -0
- uk_bin_collection/uk_bin_collection/councils/LondonBoroughHounslow.py +82 -0
- uk_bin_collection/uk_bin_collection/councils/LondonBoroughRedbridge.py +161 -0
- uk_bin_collection/uk_bin_collection/councils/MaldonDistrictCouncil.py +52 -0
- uk_bin_collection/uk_bin_collection/councils/MalvernHillsDC.py +57 -0
- uk_bin_collection/uk_bin_collection/councils/ManchesterCityCouncil.py +106 -0
- uk_bin_collection/uk_bin_collection/councils/MansfieldDistrictCouncil.py +38 -0
- uk_bin_collection/uk_bin_collection/councils/MertonCouncil.py +58 -0
- uk_bin_collection/uk_bin_collection/councils/MidAndEastAntrimBoroughCouncil.py +128 -0
- uk_bin_collection/uk_bin_collection/councils/MidSussexDistrictCouncil.py +80 -0
- uk_bin_collection/uk_bin_collection/councils/MiltonKeynesCityCouncil.py +54 -0
- uk_bin_collection/uk_bin_collection/councils/MoleValleyDistrictCouncil.py +98 -0
- uk_bin_collection/uk_bin_collection/councils/NeathPortTalbotCouncil.py +139 -0
- uk_bin_collection/uk_bin_collection/councils/NewarkAndSherwoodDC.py +52 -0
- uk_bin_collection/uk_bin_collection/councils/NewcastleCityCouncil.py +57 -0
- uk_bin_collection/uk_bin_collection/councils/NewhamCouncil.py +58 -0
- uk_bin_collection/uk_bin_collection/councils/NewportCityCouncil.py +203 -0
- uk_bin_collection/uk_bin_collection/councils/NorthEastDerbyshireDistrictCouncil.py +115 -0
- uk_bin_collection/uk_bin_collection/councils/NorthEastLincs.py +53 -0
- uk_bin_collection/uk_bin_collection/councils/NorthKestevenDistrictCouncil.py +45 -0
- uk_bin_collection/uk_bin_collection/councils/NorthLanarkshireCouncil.py +46 -0
- uk_bin_collection/uk_bin_collection/councils/NorthLincolnshireCouncil.py +58 -0
- uk_bin_collection/uk_bin_collection/councils/NorthNorfolkDistrictCouncil.py +108 -0
- uk_bin_collection/uk_bin_collection/councils/NorthNorthamptonshireCouncil.py +72 -0
- uk_bin_collection/uk_bin_collection/councils/NorthSomersetCouncil.py +76 -0
- uk_bin_collection/uk_bin_collection/councils/NorthTynesideCouncil.py +220 -0
- uk_bin_collection/uk_bin_collection/councils/NorthWestLeicestershire.py +114 -0
- uk_bin_collection/uk_bin_collection/councils/NorthYorkshire.py +58 -0
- uk_bin_collection/uk_bin_collection/councils/NorthumberlandCouncil.py +123 -0
- uk_bin_collection/uk_bin_collection/councils/NottinghamCityCouncil.py +36 -0
- uk_bin_collection/uk_bin_collection/councils/OldhamCouncil.py +51 -0
- uk_bin_collection/uk_bin_collection/councils/PortsmouthCityCouncil.py +131 -0
- uk_bin_collection/uk_bin_collection/councils/PrestonCityCouncil.py +97 -0
- uk_bin_collection/uk_bin_collection/councils/ReadingBoroughCouncil.py +30 -0
- uk_bin_collection/uk_bin_collection/councils/ReigateAndBansteadBoroughCouncil.py +81 -0
- uk_bin_collection/uk_bin_collection/councils/RenfrewshireCouncil.py +135 -0
- uk_bin_collection/uk_bin_collection/councils/RhonddaCynonTaffCouncil.py +80 -0
- uk_bin_collection/uk_bin_collection/councils/RochdaleCouncil.py +69 -0
- uk_bin_collection/uk_bin_collection/councils/RochfordCouncil.py +60 -0
- uk_bin_collection/uk_bin_collection/councils/RugbyBoroughCouncil.py +93 -0
- uk_bin_collection/uk_bin_collection/councils/RushcliffeBoroughCouncil.py +100 -0
- uk_bin_collection/uk_bin_collection/councils/RushmoorCouncil.py +81 -0
- uk_bin_collection/uk_bin_collection/councils/SalfordCityCouncil.py +70 -0
- uk_bin_collection/uk_bin_collection/councils/SevenoaksDistrictCouncil.py +106 -0
- uk_bin_collection/uk_bin_collection/councils/SheffieldCityCouncil.py +54 -0
- uk_bin_collection/uk_bin_collection/councils/ShropshireCouncil.py +45 -0
- uk_bin_collection/uk_bin_collection/councils/SolihullCouncil.py +48 -0
- uk_bin_collection/uk_bin_collection/councils/SomersetCouncil.py +203 -0
- uk_bin_collection/uk_bin_collection/councils/SouthAyrshireCouncil.py +73 -0
- uk_bin_collection/uk_bin_collection/councils/SouthCambridgeshireCouncil.py +65 -0
- uk_bin_collection/uk_bin_collection/councils/SouthGloucestershireCouncil.py +74 -0
- uk_bin_collection/uk_bin_collection/councils/SouthLanarkshireCouncil.py +78 -0
- uk_bin_collection/uk_bin_collection/councils/SouthNorfolkCouncil.py +91 -0
- uk_bin_collection/uk_bin_collection/councils/SouthOxfordshireCouncil.py +93 -0
- uk_bin_collection/uk_bin_collection/councils/SouthTynesideCouncil.py +98 -0
- uk_bin_collection/uk_bin_collection/councils/StAlbansCityAndDistrictCouncil.py +43 -0
- uk_bin_collection/uk_bin_collection/councils/StHelensBC.py +56 -0
- uk_bin_collection/uk_bin_collection/councils/StaffordshireMoorlandsDistrictCouncil.py +112 -0
- uk_bin_collection/uk_bin_collection/councils/StockportBoroughCouncil.py +39 -0
- uk_bin_collection/uk_bin_collection/councils/StokeOnTrentCityCouncil.py +79 -0
- uk_bin_collection/uk_bin_collection/councils/StratfordUponAvonCouncil.py +94 -0
- uk_bin_collection/uk_bin_collection/councils/SunderlandCityCouncil.py +100 -0
- uk_bin_collection/uk_bin_collection/councils/SwaleBoroughCouncil.py +52 -0
- uk_bin_collection/uk_bin_collection/councils/TamesideMBCouncil.py +62 -0
- uk_bin_collection/uk_bin_collection/councils/TandridgeDistrictCouncil.py +60 -0
- uk_bin_collection/uk_bin_collection/councils/TelfordAndWrekinCouncil.py +50 -0
- uk_bin_collection/uk_bin_collection/councils/TestValleyBoroughCouncil.py +203 -0
- uk_bin_collection/uk_bin_collection/councils/TonbridgeAndMallingBC.py +101 -0
- uk_bin_collection/uk_bin_collection/councils/TorbayCouncil.py +51 -0
- uk_bin_collection/uk_bin_collection/councils/TorridgeDistrictCouncil.py +154 -0
- uk_bin_collection/uk_bin_collection/councils/ValeofGlamorganCouncil.py +119 -0
- uk_bin_collection/uk_bin_collection/councils/ValeofWhiteHorseCouncil.py +103 -0
- uk_bin_collection/uk_bin_collection/councils/WakefieldCityCouncil.py +89 -0
- uk_bin_collection/uk_bin_collection/councils/WarwickDistrictCouncil.py +34 -0
- uk_bin_collection/uk_bin_collection/councils/WaverleyBoroughCouncil.py +119 -0
- uk_bin_collection/uk_bin_collection/councils/WealdenDistrictCouncil.py +86 -0
- uk_bin_collection/uk_bin_collection/councils/WelhatCouncil.py +73 -0
- uk_bin_collection/uk_bin_collection/councils/WestBerkshireCouncil.py +134 -0
- uk_bin_collection/uk_bin_collection/councils/WestLindseyDistrictCouncil.py +118 -0
- uk_bin_collection/uk_bin_collection/councils/WestLothianCouncil.py +103 -0
- uk_bin_collection/uk_bin_collection/councils/WestNorthamptonshireCouncil.py +34 -0
- uk_bin_collection/uk_bin_collection/councils/WestSuffolkCouncil.py +64 -0
- uk_bin_collection/uk_bin_collection/councils/WiganBoroughCouncil.py +97 -0
- uk_bin_collection/uk_bin_collection/councils/WiltshireCouncil.py +135 -0
- uk_bin_collection/uk_bin_collection/councils/WindsorAndMaidenheadCouncil.py +134 -0
- uk_bin_collection/uk_bin_collection/councils/WokingBoroughCouncil.py +114 -0
- uk_bin_collection/uk_bin_collection/councils/WyreCouncil.py +89 -0
- uk_bin_collection/uk_bin_collection/councils/YorkCouncil.py +45 -0
- uk_bin_collection/uk_bin_collection/councils/council_class_template/councilclasstemplate.py +33 -0
- uk_bin_collection/uk_bin_collection/get_bin_data.py +165 -0
- uk_bin_collection-0.74.0.dist-info/LICENSE +21 -0
- uk_bin_collection-0.74.0.dist-info/METADATA +247 -0
- uk_bin_collection-0.74.0.dist-info/RECORD +171 -0
- uk_bin_collection-0.74.0.dist-info/WHEEL +4 -0
- uk_bin_collection-0.74.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
import difflib
|
2
|
+
from datetime import date, datetime
|
3
|
+
|
4
|
+
import requests
|
5
|
+
from bs4 import BeautifulSoup
|
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
|
+
base_url = "https://lisburn.isl-fusion.com"
|
19
|
+
|
20
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
21
|
+
"""
|
22
|
+
This function will make a request to the search endpoint with the postcode, extract the
|
23
|
+
house numbers from the responses, then retrieve the ID of the entry with the house number that matches,
|
24
|
+
to then retrieve the bin schedule.
|
25
|
+
|
26
|
+
The API here is a weird combination of HTML in json responses.
|
27
|
+
"""
|
28
|
+
postcode = kwargs.get("postcode")
|
29
|
+
paon = kwargs.get("paon")
|
30
|
+
|
31
|
+
if not postcode:
|
32
|
+
raise ValueError("Must provide a postcode")
|
33
|
+
|
34
|
+
if not paon:
|
35
|
+
raise ValueError("Must provide a house number")
|
36
|
+
|
37
|
+
search_url = f"{self.base_url}/address/{postcode}"
|
38
|
+
|
39
|
+
requests.packages.urllib3.disable_warnings()
|
40
|
+
s = requests.Session()
|
41
|
+
response = s.get(search_url)
|
42
|
+
response.raise_for_status()
|
43
|
+
|
44
|
+
address_data = response.json()
|
45
|
+
|
46
|
+
address_list = address_data["html"]
|
47
|
+
|
48
|
+
soup = BeautifulSoup(address_list, features="html.parser")
|
49
|
+
|
50
|
+
address_by_id = {}
|
51
|
+
|
52
|
+
for li in soup.find_all("li"):
|
53
|
+
link = li.find_all("a")[0]
|
54
|
+
address_id = link.attrs["href"]
|
55
|
+
address = link.text
|
56
|
+
|
57
|
+
address_by_id[address_id] = address
|
58
|
+
|
59
|
+
addresses = list(address_by_id.values())
|
60
|
+
|
61
|
+
common = difflib.SequenceMatcher(
|
62
|
+
a=addresses[0], b=addresses[1]
|
63
|
+
).find_longest_match()
|
64
|
+
extra_bit = addresses[0][common.a : common.a + common.size]
|
65
|
+
|
66
|
+
ids_by_paon = {
|
67
|
+
a.replace(extra_bit, ""): a_id.replace("/view/", "").replace("/", "")
|
68
|
+
for a_id, a in address_by_id.items()
|
69
|
+
}
|
70
|
+
|
71
|
+
property_id = ids_by_paon.get(paon)
|
72
|
+
if not property_id:
|
73
|
+
raise ValueError(
|
74
|
+
f"Invalid house number, valid values are {', '.join(ids_by_paon.keys())}"
|
75
|
+
)
|
76
|
+
|
77
|
+
today = date.today()
|
78
|
+
calendar_url = (
|
79
|
+
f"{self.base_url}/calendar/{property_id}/{today.strftime('%Y-%m-%d')}"
|
80
|
+
)
|
81
|
+
response = s.get(calendar_url)
|
82
|
+
response.raise_for_status()
|
83
|
+
calendar_data = response.json()
|
84
|
+
next_collections = calendar_data["nextCollections"]
|
85
|
+
|
86
|
+
collections = list(next_collections["collections"].values())
|
87
|
+
|
88
|
+
data = {"bins": []}
|
89
|
+
|
90
|
+
for collection in collections:
|
91
|
+
collection_date = datetime.strptime(collection["date"], "%Y-%m-%d")
|
92
|
+
bins = [c["name"] for c in collection["collections"].values()]
|
93
|
+
|
94
|
+
for bin in bins:
|
95
|
+
data["bins"].append(
|
96
|
+
{
|
97
|
+
"type": bin,
|
98
|
+
"collectionDate": collection_date.strftime(date_format),
|
99
|
+
}
|
100
|
+
)
|
101
|
+
return data
|
@@ -0,0 +1,65 @@
|
|
1
|
+
from bs4 import BeautifulSoup
|
2
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
3
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
4
|
+
from dateutil.relativedelta import relativedelta
|
5
|
+
|
6
|
+
|
7
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
8
|
+
class CouncilClass(AbstractGetBinDataClass):
|
9
|
+
"""
|
10
|
+
Concrete classes have to implement all abstract operations of the
|
11
|
+
base class. They can also override some operations with a default
|
12
|
+
implementation.
|
13
|
+
"""
|
14
|
+
|
15
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
16
|
+
# Add in some variables we need
|
17
|
+
data = {"bins": []}
|
18
|
+
collections = []
|
19
|
+
curr_date = datetime.today()
|
20
|
+
|
21
|
+
# Parse the page
|
22
|
+
soup = BeautifulSoup(page.text, features="html.parser")
|
23
|
+
soup.prettify()
|
24
|
+
|
25
|
+
# Get all table rows on the page - enumerate gives us an index, which is handy for to keep a row count.
|
26
|
+
# In this case, the first (0th) row is headings, so we can skip it, then parse the other data.
|
27
|
+
for idx, row in enumerate(soup.find_all("tr")):
|
28
|
+
if idx == 0:
|
29
|
+
continue
|
30
|
+
|
31
|
+
row_type = row.find("th").text.strip()
|
32
|
+
row_data = row.find_all("td")
|
33
|
+
|
34
|
+
# When we get the row data, we can loop through it all and parse it to datetime. Because there are no
|
35
|
+
# years, we must add it in, then check if we need to overflow it to the following year.
|
36
|
+
for item in row_data:
|
37
|
+
item_text = item.text.strip()
|
38
|
+
|
39
|
+
if item_text == "Today":
|
40
|
+
collections.append((row_type, curr_date))
|
41
|
+
elif item_text == "Tomorrow":
|
42
|
+
collections.append((row_type, curr_date + relativedelta(days=1)))
|
43
|
+
else:
|
44
|
+
bin_date = datetime.strptime(
|
45
|
+
remove_ordinal_indicator_from_date_string(item_text),
|
46
|
+
"%A, %d %B",
|
47
|
+
).replace(year=curr_date.year)
|
48
|
+
|
49
|
+
if curr_date.month == 12 and bin_date.month == 1:
|
50
|
+
bin_date = bin_date + relativedelta(years=1)
|
51
|
+
|
52
|
+
collections.append((row_type, bin_date))
|
53
|
+
|
54
|
+
# Sort the text and list elements by date
|
55
|
+
ordered_data = sorted(collections, key=lambda x: x[1])
|
56
|
+
|
57
|
+
# Put the elements into the dictionary
|
58
|
+
for item in ordered_data:
|
59
|
+
dict_data = {
|
60
|
+
"type": item[0],
|
61
|
+
"collectionDate": item[1].strftime(date_format),
|
62
|
+
}
|
63
|
+
data["bins"].append(dict_data)
|
64
|
+
|
65
|
+
return data
|
@@ -0,0 +1,82 @@
|
|
1
|
+
from bs4 import BeautifulSoup
|
2
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
3
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
4
|
+
|
5
|
+
|
6
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
7
|
+
class CouncilClass(AbstractGetBinDataClass):
|
8
|
+
"""
|
9
|
+
Concrete classes have to implement all abstract operations of the
|
10
|
+
base class. They can also override some operations with a default
|
11
|
+
implementation.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
15
|
+
api_url = "https://www.hounslow.gov.uk/homepage/86/recycling_and_waste_collection_day_finder"
|
16
|
+
user_uprn = kwargs.get("uprn")
|
17
|
+
|
18
|
+
# Check the UPRN is valid
|
19
|
+
check_uprn(user_uprn)
|
20
|
+
|
21
|
+
# Create the form data
|
22
|
+
form_data = {
|
23
|
+
"UPRN": user_uprn,
|
24
|
+
}
|
25
|
+
|
26
|
+
# Make a request to the API
|
27
|
+
requests.packages.urllib3.disable_warnings()
|
28
|
+
response = requests.post(api_url, data=form_data)
|
29
|
+
|
30
|
+
# Make a BS4 object
|
31
|
+
soup = BeautifulSoup(response.text, features="html.parser")
|
32
|
+
soup.prettify()
|
33
|
+
|
34
|
+
data = {"bins": []}
|
35
|
+
|
36
|
+
# Get the div element
|
37
|
+
div_element = soup.find("div", {"class": "bin_day_main_wrapper"})
|
38
|
+
|
39
|
+
# Get all bins with their corresponding dates using list comprehension
|
40
|
+
# This creates a list of tuples, where each tuple contains the bin type and collection date
|
41
|
+
bins_with_dates = [
|
42
|
+
(
|
43
|
+
bin.get_text().strip(),
|
44
|
+
h4.get_text().replace("This ", "").replace("Next ", ""),
|
45
|
+
)
|
46
|
+
# This first for loop iterates over each h4 element
|
47
|
+
for h4 in div_element.find_all("h4")
|
48
|
+
# This nested for loop iterates over each li element within the corresponding ul element
|
49
|
+
for bin in h4.find_next_sibling("ul").find_all("li")
|
50
|
+
]
|
51
|
+
|
52
|
+
for bin_type, collection_date in bins_with_dates:
|
53
|
+
if "-" in collection_date:
|
54
|
+
date_part = collection_date.split(" - ")[1]
|
55
|
+
data["bins"].append(
|
56
|
+
{
|
57
|
+
"type": bin_type,
|
58
|
+
"collectionDate": datetime.strptime(
|
59
|
+
date_part, "%d %b %Y"
|
60
|
+
).strftime(date_format),
|
61
|
+
}
|
62
|
+
)
|
63
|
+
elif len(collection_date.split(" ")) == 4:
|
64
|
+
data["bins"].append(
|
65
|
+
{
|
66
|
+
"type": bin_type,
|
67
|
+
"collectionDate": datetime.strptime(
|
68
|
+
collection_date, "%A %d %b %Y"
|
69
|
+
).strftime(date_format),
|
70
|
+
}
|
71
|
+
)
|
72
|
+
else:
|
73
|
+
data["bins"].append(
|
74
|
+
{
|
75
|
+
"type": bin_type,
|
76
|
+
"collectionDate": datetime.strptime(
|
77
|
+
collection_date, "%d %b %Y"
|
78
|
+
).strftime(date_format),
|
79
|
+
}
|
80
|
+
)
|
81
|
+
|
82
|
+
return data
|
@@ -0,0 +1,161 @@
|
|
1
|
+
import re
|
2
|
+
import requests
|
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
|
+
# This script pulls (in one hit) the data from Bromley Council Bins Data
|
9
|
+
import datetime
|
10
|
+
from datetime import datetime
|
11
|
+
from selenium.webdriver.common.by import By
|
12
|
+
from selenium.webdriver.support import expected_conditions as EC
|
13
|
+
from selenium.webdriver.support.ui import Select
|
14
|
+
from selenium.webdriver.support.wait import WebDriverWait
|
15
|
+
from selenium.webdriver.common.keys import Keys
|
16
|
+
import time
|
17
|
+
|
18
|
+
|
19
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
20
|
+
class CouncilClass(AbstractGetBinDataClass):
|
21
|
+
"""
|
22
|
+
Concrete classes have to implement all abstract operations of the
|
23
|
+
base class. They can also override some operations with a default
|
24
|
+
implementation.
|
25
|
+
"""
|
26
|
+
|
27
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
28
|
+
driver = None
|
29
|
+
try:
|
30
|
+
data = {"bins": []}
|
31
|
+
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64)"}
|
32
|
+
|
33
|
+
uprn = kwargs.get("uprn")
|
34
|
+
postcode = kwargs.get("postcode")
|
35
|
+
web_driver = kwargs.get("web_driver")
|
36
|
+
headless = kwargs.get("headless")
|
37
|
+
driver = create_webdriver(web_driver, headless)
|
38
|
+
driver.get(kwargs.get("url"))
|
39
|
+
|
40
|
+
wait = WebDriverWait(driver, 60)
|
41
|
+
post_code_search = wait.until(
|
42
|
+
EC.presence_of_element_located(
|
43
|
+
(By.XPATH, f"//input[contains(@class, 'searchAddress')]")
|
44
|
+
)
|
45
|
+
)
|
46
|
+
post_code_search.send_keys(postcode)
|
47
|
+
|
48
|
+
submit_btn = wait.until(
|
49
|
+
EC.presence_of_element_located(
|
50
|
+
(By.XPATH, f"//button[contains(@class, 'searchAddressButton')]")
|
51
|
+
)
|
52
|
+
)
|
53
|
+
|
54
|
+
submit_btn.send_keys(Keys.ENTER)
|
55
|
+
|
56
|
+
address_link = wait.until(
|
57
|
+
EC.presence_of_element_located(
|
58
|
+
(By.XPATH, f'//a[contains(@data-uprn,"{uprn}")]')
|
59
|
+
)
|
60
|
+
)
|
61
|
+
|
62
|
+
address_link.send_keys(Keys.ENTER)
|
63
|
+
|
64
|
+
address_results = wait.until(
|
65
|
+
EC.presence_of_element_located(
|
66
|
+
(By.CLASS_NAME, "your-collection-schedule-container")
|
67
|
+
)
|
68
|
+
)
|
69
|
+
|
70
|
+
# Make a BS4 object
|
71
|
+
soup = BeautifulSoup(driver.page_source, features="html.parser")
|
72
|
+
data = {"bins": []}
|
73
|
+
|
74
|
+
# Get the current month and year
|
75
|
+
current_month = datetime.now().month
|
76
|
+
current_year = datetime.now().year
|
77
|
+
|
78
|
+
# Function to extract collection data
|
79
|
+
def extract_collection_data(collection_div, collection_type):
|
80
|
+
if collection_div:
|
81
|
+
date_element = (
|
82
|
+
collection_div.find(class_="recycling-collection-day-numeric")
|
83
|
+
or collection_div.find(class_="refuse-collection-day-numeric")
|
84
|
+
or collection_div.find(class_="garden-collection-day-numeric")
|
85
|
+
)
|
86
|
+
month_element = (
|
87
|
+
collection_div.find(class_="recycling-collection-month")
|
88
|
+
or collection_div.find(class_="refuse-collection-month")
|
89
|
+
or collection_div.find(class_="garden-collection-month")
|
90
|
+
)
|
91
|
+
|
92
|
+
if date_element and month_element:
|
93
|
+
collection_date = date_element.get_text(strip=True)
|
94
|
+
collection_month = month_element.get_text(strip=True)
|
95
|
+
|
96
|
+
# Combine month, date, and year into a string
|
97
|
+
date_string = (
|
98
|
+
f"{collection_date} {collection_month} {current_year}"
|
99
|
+
)
|
100
|
+
|
101
|
+
try:
|
102
|
+
# Convert the date string to a datetime object
|
103
|
+
collection_date_obj = datetime.strptime(
|
104
|
+
date_string, "%d %B %Y"
|
105
|
+
)
|
106
|
+
|
107
|
+
# Check if the month is ahead of the current month
|
108
|
+
if collection_date_obj.month >= current_month:
|
109
|
+
# If the month is ahead, use the current year
|
110
|
+
formatted_date = collection_date_obj.strftime(
|
111
|
+
date_format
|
112
|
+
)
|
113
|
+
else:
|
114
|
+
# If the month is before the current month, use the next year
|
115
|
+
formatted_date = collection_date_obj.replace(
|
116
|
+
year=current_year + 1
|
117
|
+
).strftime(date_format)
|
118
|
+
# Create a dictionary for each collection entry
|
119
|
+
dict_data = {
|
120
|
+
"type": collection_type,
|
121
|
+
"collectionDate": formatted_date,
|
122
|
+
}
|
123
|
+
|
124
|
+
# Append dictionary data to the 'bins' list in the 'data' dictionary
|
125
|
+
data["bins"].append(dict_data)
|
126
|
+
|
127
|
+
except ValueError as e:
|
128
|
+
# Handle the case where the date format is invalid
|
129
|
+
formatted_date = "Invalid Date Format"
|
130
|
+
|
131
|
+
# Extract Recycling collection data
|
132
|
+
extract_collection_data(
|
133
|
+
soup.find(
|
134
|
+
class_="container-fluid RegularCollectionDay"
|
135
|
+
).find_next_sibling("div"),
|
136
|
+
"Recycling",
|
137
|
+
)
|
138
|
+
|
139
|
+
# Extract Refuse collection data
|
140
|
+
for refuse_div in soup.find_all(
|
141
|
+
class_="container-fluid RegularCollectionDay"
|
142
|
+
):
|
143
|
+
extract_collection_data(refuse_div, "Refuse")
|
144
|
+
|
145
|
+
# Extract Garden Waste collection data
|
146
|
+
extract_collection_data(
|
147
|
+
soup.find(class_="container-fluid gardenwasteCollectionDay"),
|
148
|
+
"Garden Waste",
|
149
|
+
)
|
150
|
+
|
151
|
+
# Print the extracted data
|
152
|
+
except Exception as e:
|
153
|
+
# Here you can log the exception if needed
|
154
|
+
print(f"An error occurred: {e}")
|
155
|
+
# Optionally, re-raise the exception if you want it to propagate
|
156
|
+
raise
|
157
|
+
finally:
|
158
|
+
# This block ensures that the driver is closed regardless of an exception
|
159
|
+
if driver:
|
160
|
+
driver.quit()
|
161
|
+
return data
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from bs4 import BeautifulSoup
|
2
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
3
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
4
|
+
|
5
|
+
|
6
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
7
|
+
class CouncilClass(AbstractGetBinDataClass):
|
8
|
+
"""
|
9
|
+
Concrete classes have to implement all abstract operations of the
|
10
|
+
base class. They can also override some operations with a default
|
11
|
+
implementation.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
15
|
+
data = {"bins": []}
|
16
|
+
uprn = kwargs.get("uprn")
|
17
|
+
check_uprn(uprn)
|
18
|
+
|
19
|
+
requests.packages.urllib3.disable_warnings()
|
20
|
+
response = requests.get(
|
21
|
+
f"https://maldon.suez.co.uk/maldon/ServiceSummary?uprn={uprn}",
|
22
|
+
headers={"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64)"},
|
23
|
+
)
|
24
|
+
if response.status_code != 200:
|
25
|
+
raise ValueError("No bin data found for provided UPRN.")
|
26
|
+
|
27
|
+
soup = BeautifulSoup(response.text, features="html.parser")
|
28
|
+
collections = soup.find_all("div", {"class": "panel"})
|
29
|
+
for c in collections:
|
30
|
+
binType = c.find("div", {"class": "panel-heading"}).get_text(strip=True)
|
31
|
+
collectionDate = ""
|
32
|
+
rows = c.find("div", {"class": "panel-body"}).find_all(
|
33
|
+
"div", {"class": "row"}
|
34
|
+
)
|
35
|
+
for row in rows:
|
36
|
+
if row.find("strong").get_text(strip=True).lower() == "next collection":
|
37
|
+
collectionDate = row.find("div", {"class": "col-sm-9"}).get_text(
|
38
|
+
strip=True
|
39
|
+
)
|
40
|
+
|
41
|
+
if collectionDate != "":
|
42
|
+
collection_data = {
|
43
|
+
"type": binType,
|
44
|
+
"collectionDate": collectionDate,
|
45
|
+
}
|
46
|
+
data["bins"].append(collection_data)
|
47
|
+
|
48
|
+
data["bins"].sort(
|
49
|
+
key=lambda x: datetime.strptime(x.get("collectionDate"), date_format)
|
50
|
+
)
|
51
|
+
|
52
|
+
return data
|
@@ -0,0 +1,57 @@
|
|
1
|
+
from bs4 import BeautifulSoup
|
2
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
3
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
4
|
+
|
5
|
+
|
6
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
7
|
+
class CouncilClass(AbstractGetBinDataClass):
|
8
|
+
"""
|
9
|
+
Concrete classes have to implement all abstract operations of the
|
10
|
+
base class. They can also override some operations with a default
|
11
|
+
implementation.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
15
|
+
api_url = "https://swict.malvernhills.gov.uk/mhdcroundlookup/HandleSearchScreen"
|
16
|
+
|
17
|
+
user_uprn = kwargs.get("uprn")
|
18
|
+
# Check the UPRN is valid
|
19
|
+
check_uprn(user_uprn)
|
20
|
+
|
21
|
+
# Create the form data
|
22
|
+
form_data = {"nmalAddrtxt": "", "alAddrsel": user_uprn}
|
23
|
+
# expects postcode to be looked up and then uprn used.
|
24
|
+
# we can just provide uprn
|
25
|
+
|
26
|
+
# Make a request to the API
|
27
|
+
requests.packages.urllib3.disable_warnings()
|
28
|
+
response = requests.post(api_url, data=form_data, verify=False)
|
29
|
+
|
30
|
+
# Make a BS4 object
|
31
|
+
soup = BeautifulSoup(response.text, features="html.parser")
|
32
|
+
soup.prettify()
|
33
|
+
|
34
|
+
# Find results table
|
35
|
+
table_element = soup.find("table")
|
36
|
+
table_body = table_element.find("tbody")
|
37
|
+
rows = table_body.find_all("tr")
|
38
|
+
|
39
|
+
data = {"bins": []}
|
40
|
+
|
41
|
+
for row in rows:
|
42
|
+
columns = row.find_all("td")
|
43
|
+
columns = [ele.text.strip() for ele in columns]
|
44
|
+
|
45
|
+
thisCollection = [ele for ele in columns if ele] # Get rid of empty values
|
46
|
+
|
47
|
+
# if not signed up for garden waste, this appears as Not applicable
|
48
|
+
if "Not applicable" not in thisCollection[1]:
|
49
|
+
bin_type = thisCollection[0].replace("collection", "").strip()
|
50
|
+
date = datetime.strptime(thisCollection[1], "%A %d/%m/%Y")
|
51
|
+
dict_data = {
|
52
|
+
"type": bin_type,
|
53
|
+
"collectionDate": date.strftime(date_format),
|
54
|
+
}
|
55
|
+
data["bins"].append(dict_data)
|
56
|
+
|
57
|
+
return data
|
@@ -0,0 +1,106 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
|
3
|
+
import requests
|
4
|
+
from bs4 import BeautifulSoup
|
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
|
+
base class. They can also override some operations with a default
|
13
|
+
implementation.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
17
|
+
# Get and check UPRN
|
18
|
+
user_uprn = kwargs.get("uprn")
|
19
|
+
check_uprn(user_uprn)
|
20
|
+
|
21
|
+
# Start a new session to walk through the form
|
22
|
+
requests.packages.urllib3.disable_warnings()
|
23
|
+
s = requests.session()
|
24
|
+
|
25
|
+
# There's a cookie that makes the whole thing valid when you search for a postcode,
|
26
|
+
# but postcode and UPRN is a hassle imo, so this makes a request for the session to get a cookie
|
27
|
+
# using a Manchester City Council postcode I hardcoded in the data payload
|
28
|
+
postcode_request_header = {
|
29
|
+
"authority": "www.manchester.gov.uk",
|
30
|
+
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,"
|
31
|
+
"image/webp,image/apng,*/*;q=0.8",
|
32
|
+
"accept-language": "en-GB,en;q=0.6",
|
33
|
+
"cache-control": "max-age=0",
|
34
|
+
# Requests sorts cookies= alphabetically
|
35
|
+
"origin": "https://www.manchester.gov.uk",
|
36
|
+
"referer": "https://www.manchester.gov.uk/bincollections",
|
37
|
+
"sec-fetch-dest": "document",
|
38
|
+
"sec-fetch-mode": "navigate",
|
39
|
+
"sec-fetch-site": "same-origin",
|
40
|
+
"sec-fetch-user": "?1",
|
41
|
+
"sec-gpc": "1",
|
42
|
+
"upgrade-insecure-requests": "1",
|
43
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, "
|
44
|
+
"like Gecko) Chrome/104.0.5112.102 Safari/537.36",
|
45
|
+
}
|
46
|
+
postcode_request_data = {
|
47
|
+
"mcc_bin_dates_search_term": "M2 5DB",
|
48
|
+
"mcc_bin_dates_submit": "Go",
|
49
|
+
}
|
50
|
+
response = s.post(
|
51
|
+
"https://www.manchester.gov.uk/bincollections",
|
52
|
+
headers=postcode_request_header,
|
53
|
+
data=postcode_request_data,
|
54
|
+
)
|
55
|
+
|
56
|
+
# Make a POST with the same cookie-fied session using the user's UPRN data
|
57
|
+
uprn_request_headers = {
|
58
|
+
"authority": "www.manchester.gov.uk",
|
59
|
+
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
60
|
+
"accept-language": "en-GB,en;q=0.6",
|
61
|
+
"cache-control": "max-age=0",
|
62
|
+
# Requests sorts cookies= alphabetically
|
63
|
+
# 'cookie': 'TestCookie=Test; CookieConsent={stamp:%27D8rypjMDBJhpfMWybSMdGXP1hCZWGJYtGETiMTu1UuXTdRIKl8SU5g==%27%2Cnecessary:true%2Cpreferences:true%2Cstatistics:true%2Cmarketing:true%2Cver:6%2Cutc:1661783732090%2Cregion:%27gb%27}; PHPSESSID=kElJxYAt%2Cf-4ZWoskt0s5tn32BUQRXDYUVp3G-NsqOAOaeIcKlm2T4r7ATSgqfz6',
|
64
|
+
"origin": "https://www.manchester.gov.uk",
|
65
|
+
"referer": "https://www.manchester.gov.uk/bincollections",
|
66
|
+
"sec-fetch-dest": "document",
|
67
|
+
"sec-fetch-mode": "navigate",
|
68
|
+
"sec-fetch-site": "same-origin",
|
69
|
+
"sec-fetch-user": "?1",
|
70
|
+
"sec-gpc": "1",
|
71
|
+
"upgrade-insecure-requests": "1",
|
72
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36",
|
73
|
+
}
|
74
|
+
uprn_request_data = {
|
75
|
+
"mcc_bin_dates_uprn": user_uprn,
|
76
|
+
"mcc_bin_dates_submit": "Go",
|
77
|
+
}
|
78
|
+
response = s.post(
|
79
|
+
"https://www.manchester.gov.uk/bincollections",
|
80
|
+
headers=uprn_request_headers,
|
81
|
+
data=uprn_request_data,
|
82
|
+
)
|
83
|
+
|
84
|
+
# Make that BS4 object and use it to prettify the response
|
85
|
+
soup = BeautifulSoup(response.content, features="html.parser")
|
86
|
+
soup.prettify()
|
87
|
+
|
88
|
+
# Get the collection items on the page and strip the bits of text that we don't care for
|
89
|
+
collections = []
|
90
|
+
for bin in soup.find_all("div", {"class": "collection"}):
|
91
|
+
bin_type = bin.find_next("h3").text.replace(" DUE TODAY", "").strip()
|
92
|
+
next_collection = bin.find_next("p").text.replace("Next collection ", "")
|
93
|
+
next_collection = datetime.strptime(next_collection, "%A %d %b %Y")
|
94
|
+
collections.append((bin_type, next_collection))
|
95
|
+
|
96
|
+
# Sort the collections by date order rather than bin type, then return as a dictionary (with str date)
|
97
|
+
ordered_data = sorted(collections, key=lambda x: x[1])
|
98
|
+
data = {"bins": []}
|
99
|
+
for item in ordered_data:
|
100
|
+
dict_data = {
|
101
|
+
"type": item[0],
|
102
|
+
"collectionDate": item[1].strftime(date_format),
|
103
|
+
}
|
104
|
+
data["bins"].append(dict_data)
|
105
|
+
|
106
|
+
return data
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import requests
|
2
|
+
|
3
|
+
from bs4 import BeautifulSoup
|
4
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
5
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
6
|
+
|
7
|
+
|
8
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
9
|
+
class CouncilClass(AbstractGetBinDataClass):
|
10
|
+
"""
|
11
|
+
Concrete classes have to implement all abstract operations of the
|
12
|
+
base class. They can also override some operations with a default
|
13
|
+
implementation.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
17
|
+
user_uprn = kwargs.get("uprn")
|
18
|
+
check_uprn(user_uprn)
|
19
|
+
|
20
|
+
data = {"bins": []}
|
21
|
+
api_url = f'https://portal.mansfield.gov.uk/MDCWhiteSpaceWebService/WhiteSpaceWS.asmx/GetCollectionByUPRNAndDate?apiKey=mDc-wN3-B0f-f4P&UPRN={user_uprn}&coldate={datetime.now().strftime("%d/%m/%Y")}'
|
22
|
+
|
23
|
+
response = requests.get(api_url)
|
24
|
+
if response.status_code != 200:
|
25
|
+
raise ConnectionError("Could not get latest data!")
|
26
|
+
|
27
|
+
json_data = response.json()["Collections"]
|
28
|
+
for item in json_data:
|
29
|
+
|
30
|
+
dict_data = {
|
31
|
+
"type": item.get("Service").split(" ")[0] + " bin",
|
32
|
+
"collectionDate": datetime.strptime(
|
33
|
+
item.get("Date"), "%d/%m/%Y %H:%M:%S"
|
34
|
+
).strftime(date_format),
|
35
|
+
}
|
36
|
+
data["bins"].append(dict_data)
|
37
|
+
|
38
|
+
return data
|