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,39 @@
|
|
1
|
+
# This script pulls (in one hit) the
|
2
|
+
# data from Warick District Council Bins Data
|
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
|
+
# Make a BS4 object
|
18
|
+
soup = BeautifulSoup(page.text, features="html.parser")
|
19
|
+
soup.prettify()
|
20
|
+
|
21
|
+
data = {"bins": []}
|
22
|
+
|
23
|
+
for bins in soup.select('div[class*="service-item"]'):
|
24
|
+
bin_type = bins.div.h3.text.strip()
|
25
|
+
binCollection = datetime.strptime(
|
26
|
+
bins.select("div > p")[1].get_text(strip=True), "%A, %d %B %Y"
|
27
|
+
)
|
28
|
+
# binImage = "https://myaccount.stockport.gov.uk" + bins.img['src']
|
29
|
+
|
30
|
+
# batteries don't have a service date or other
|
31
|
+
# info associated with them.
|
32
|
+
if binCollection:
|
33
|
+
dict_data = {
|
34
|
+
"type": bin_type,
|
35
|
+
"collectionDate": binCollection.strftime(date_format),
|
36
|
+
}
|
37
|
+
data["bins"].append(dict_data)
|
38
|
+
|
39
|
+
return data
|
@@ -0,0 +1,79 @@
|
|
1
|
+
from bs4 import BeautifulSoup
|
2
|
+
|
3
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
4
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
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
|
+
# Make a BS4 object
|
17
|
+
soup = BeautifulSoup(page.text, features="lxml-xml")
|
18
|
+
soup.prettify()
|
19
|
+
|
20
|
+
data = {"bins": []}
|
21
|
+
collections = []
|
22
|
+
|
23
|
+
# Match bin types from API to their actual type
|
24
|
+
bin_types = {
|
25
|
+
"RESIDUAL BIN": "Grey bin",
|
26
|
+
"RES 180": "Grey bin",
|
27
|
+
"RES 240 STD": "Grey bin",
|
28
|
+
"RES 360 STD+": "Grey bin",
|
29
|
+
"RES 360 STD": "Grey bin",
|
30
|
+
"RES 660": "Grey bin",
|
31
|
+
"RES 770": "Grey bin",
|
32
|
+
"RES BAG": "Grey bin",
|
33
|
+
"RES 140 SML": "Grey bin",
|
34
|
+
"REC 180 SML": "Blue bin",
|
35
|
+
"REC 240 STD": "Blue bin",
|
36
|
+
"REC 360": "Blue bin",
|
37
|
+
"REC 770": "Blue bin",
|
38
|
+
"MIXED REC 55 BOX": "Blue box",
|
39
|
+
"PAPER 44 BOX": "Blue box",
|
40
|
+
"PAPER BAG": "Paper bag",
|
41
|
+
"ORG 180": "Brown bin",
|
42
|
+
"ORG 240 STD": "Brown bin",
|
43
|
+
"PAID ORGANIC": "Brown bin",
|
44
|
+
"RES 1100": "Grey trade container",
|
45
|
+
"REC GL 770": "Blue trade container",
|
46
|
+
}
|
47
|
+
# If the API errors, throw the exception
|
48
|
+
if soup.find("Error") is not None:
|
49
|
+
raise ConnectionAbortedError(soup.find("Error").text.strip())
|
50
|
+
|
51
|
+
# Parse the XML and add to a list of collections
|
52
|
+
for item in soup.find_all("BinRound"):
|
53
|
+
try:
|
54
|
+
bin_type = bin_types.get(
|
55
|
+
item.find_next("Bin").text.replace("EMPTY BINS", "").strip()
|
56
|
+
)
|
57
|
+
bin_date = datetime.strptime(
|
58
|
+
item.find_next("DateTime").text, "%d/%m/%Y %H:%M:%S"
|
59
|
+
)
|
60
|
+
if bin_date >= datetime.now():
|
61
|
+
collections.append((bin_type, bin_date))
|
62
|
+
except:
|
63
|
+
raise SystemError(
|
64
|
+
"Error has been encountered parsing API. Please try again later and if the issue "
|
65
|
+
"persists, open a GitHub ticket!"
|
66
|
+
)
|
67
|
+
|
68
|
+
# Sort the collections list by date
|
69
|
+
ordered_data = sorted(collections, key=lambda x: x[1])
|
70
|
+
|
71
|
+
# Put the elements into the dictionary
|
72
|
+
for item in ordered_data:
|
73
|
+
dict_data = {
|
74
|
+
"type": item[0],
|
75
|
+
"collectionDate": item[1].strftime(date_format),
|
76
|
+
}
|
77
|
+
data["bins"].append(dict_data)
|
78
|
+
|
79
|
+
return data
|
@@ -0,0 +1,94 @@
|
|
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
|
+
driver = None
|
16
|
+
try:
|
17
|
+
# Get postcode and UPRN from kwargs
|
18
|
+
# user_postcode = kwargs.get("postcode")
|
19
|
+
user_uprn = kwargs.get("uprn")
|
20
|
+
# check_postcode(user_postcode)
|
21
|
+
check_uprn(user_uprn)
|
22
|
+
url = "https://www.stratford.gov.uk/waste-recycling/when-we-collect.cfm/part/calendar"
|
23
|
+
payload = {
|
24
|
+
"frmAddress1": "",
|
25
|
+
"frmAddress2": "",
|
26
|
+
"frmAddress3": "",
|
27
|
+
"frmAddress4": "",
|
28
|
+
"frmPostcode": "",
|
29
|
+
"frmUPRN": user_uprn,
|
30
|
+
}
|
31
|
+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
32
|
+
|
33
|
+
requests.packages.urllib3.disable_warnings()
|
34
|
+
response = requests.request("POST", url, data=payload, headers=headers)
|
35
|
+
|
36
|
+
# Make a BS4 object
|
37
|
+
soup = BeautifulSoup(response.content, features="html.parser")
|
38
|
+
soup.prettify()
|
39
|
+
|
40
|
+
# Find the table
|
41
|
+
table = soup.find("table", class_="table")
|
42
|
+
|
43
|
+
data = {"bins": []}
|
44
|
+
|
45
|
+
if table:
|
46
|
+
# Extract the column headers (bin names)
|
47
|
+
column_headers = [
|
48
|
+
header.text.strip()
|
49
|
+
for header in table.select("thead th.text-center strong")
|
50
|
+
]
|
51
|
+
|
52
|
+
# Extract the rows containing collection information
|
53
|
+
collection_rows = table.select("tbody tr")
|
54
|
+
|
55
|
+
# Create a dictionary to store the next date for each bin
|
56
|
+
next_collection_dates = {bin: None for bin in column_headers}
|
57
|
+
|
58
|
+
# Iterate through the rows
|
59
|
+
for row in collection_rows:
|
60
|
+
# Get the date from the first cell
|
61
|
+
date_str = row.find("td").text.strip()
|
62
|
+
date_obj = datetime.strptime(date_str, "%A, %d/%m/%Y")
|
63
|
+
|
64
|
+
# Get the collection information for each bin (td elements with title attribute)
|
65
|
+
collection_info = [
|
66
|
+
cell["title"] if cell["title"] else "Not Collected"
|
67
|
+
for cell in row.select("td.text-center")
|
68
|
+
]
|
69
|
+
|
70
|
+
# Iterate through each bin type and its collection date
|
71
|
+
for bin, status in zip(column_headers, collection_info):
|
72
|
+
# If the bin hasn't had a collection date yet or the new date is earlier, update it
|
73
|
+
if status != "Not Collected" and (
|
74
|
+
not next_collection_dates[bin]
|
75
|
+
or date_obj < next_collection_dates[bin]
|
76
|
+
):
|
77
|
+
next_collection_dates[bin] = date_obj
|
78
|
+
|
79
|
+
data["bins"] = [
|
80
|
+
{"type": bin, "collectionDate": next_date.strftime(date_format)}
|
81
|
+
for bin, next_date in next_collection_dates.items()
|
82
|
+
]
|
83
|
+
else:
|
84
|
+
print("Table not found in the HTML content.")
|
85
|
+
except Exception as e:
|
86
|
+
# Here you can log the exception if needed
|
87
|
+
print(f"An error occurred: {e}")
|
88
|
+
# Optionally, re-raise the exception if you want it to propagate
|
89
|
+
raise
|
90
|
+
finally:
|
91
|
+
# This block ensures that the driver is closed regardless of an exception
|
92
|
+
if driver:
|
93
|
+
driver.quit()
|
94
|
+
return data
|
@@ -0,0 +1,100 @@
|
|
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 selenium.webdriver.common.by import By
|
5
|
+
from selenium.webdriver.support import expected_conditions as EC
|
6
|
+
from selenium.webdriver.support.ui import Select
|
7
|
+
from selenium.webdriver.support.wait import WebDriverWait
|
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
|
+
driver = None
|
20
|
+
try:
|
21
|
+
data = {"bins": []}
|
22
|
+
collections = []
|
23
|
+
|
24
|
+
user_paon = kwargs.get("paon")
|
25
|
+
user_postcode = kwargs.get("postcode")
|
26
|
+
web_driver = kwargs.get("web_driver")
|
27
|
+
headless = kwargs.get("headless")
|
28
|
+
check_paon(user_paon)
|
29
|
+
check_postcode(user_postcode)
|
30
|
+
|
31
|
+
driver = create_webdriver(web_driver, headless)
|
32
|
+
# driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
|
33
|
+
driver.get(
|
34
|
+
"https://webapps.sunderland.gov.uk/WEBAPPS/WSS/Sunderland_Portal/Forms/bindaychecker.aspx"
|
35
|
+
)
|
36
|
+
|
37
|
+
inputElement_postcode = WebDriverWait(driver, 30).until(
|
38
|
+
EC.presence_of_element_located(
|
39
|
+
(By.ID, "ContentPlaceHolder1_tbPostCode_controltext")
|
40
|
+
)
|
41
|
+
)
|
42
|
+
inputElement_postcode.send_keys(user_postcode)
|
43
|
+
|
44
|
+
inputElement_submit_button = WebDriverWait(driver, 30).until(
|
45
|
+
EC.element_to_be_clickable((By.ID, "ContentPlaceHolder1_btnLLPG"))
|
46
|
+
)
|
47
|
+
inputElement_submit_button.click()
|
48
|
+
|
49
|
+
addressList = WebDriverWait(driver, 30).until(
|
50
|
+
EC.presence_of_element_located(
|
51
|
+
(By.ID, "ContentPlaceHolder1_ddlAddresses")
|
52
|
+
)
|
53
|
+
)
|
54
|
+
selected_addressList = Select(addressList)
|
55
|
+
for idx, addr_option in enumerate(selected_addressList.options):
|
56
|
+
option_name = addr_option.accessible_name[0 : len(user_paon)]
|
57
|
+
if option_name == user_paon:
|
58
|
+
break
|
59
|
+
selected_addressList.select_by_index(idx)
|
60
|
+
|
61
|
+
# Make a BS4 object
|
62
|
+
soup = BeautifulSoup(driver.page_source, features="html.parser")
|
63
|
+
soup.prettify()
|
64
|
+
|
65
|
+
household_bin_date = datetime.strptime(
|
66
|
+
soup.find("span", {"id": "ContentPlaceHolder1_LabelHouse"}).get_text(
|
67
|
+
strip=True
|
68
|
+
),
|
69
|
+
"%A %d %B %Y",
|
70
|
+
)
|
71
|
+
collections.append(("Household bin", household_bin_date))
|
72
|
+
|
73
|
+
recycling_bin_date = datetime.strptime(
|
74
|
+
soup.find("span", {"id": "ContentPlaceHolder1_LabelRecycle"}).get_text(
|
75
|
+
strip=True
|
76
|
+
),
|
77
|
+
"%A %d %B %Y",
|
78
|
+
)
|
79
|
+
collections.append(("Recycling bin", recycling_bin_date))
|
80
|
+
|
81
|
+
ordered_data = sorted(collections, key=lambda x: x[1])
|
82
|
+
for item in ordered_data:
|
83
|
+
dict_data = {
|
84
|
+
"type": item[0].capitalize(),
|
85
|
+
"collectionDate": item[1].strftime(date_format),
|
86
|
+
}
|
87
|
+
data["bins"].append(dict_data)
|
88
|
+
|
89
|
+
except Exception as e:
|
90
|
+
# Here you can log the exception if needed
|
91
|
+
print(f"An error occurred: {e}")
|
92
|
+
# Optionally, re-raise the exception if you want it to propagate
|
93
|
+
raise
|
94
|
+
|
95
|
+
finally:
|
96
|
+
# This block ensures that the driver is closed regardless of an exception
|
97
|
+
if driver:
|
98
|
+
driver.quit()
|
99
|
+
|
100
|
+
return data
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import requests
|
2
|
+
from bs4 import BeautifulSoup
|
3
|
+
from uk_bin_collection.uk_bin_collection.common import *
|
4
|
+
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
|
5
|
+
|
6
|
+
|
7
|
+
# import the wonderful Beautiful Soup and the URL grabber
|
8
|
+
|
9
|
+
|
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
|
+
# Get postcode and UPRN from kwargs
|
19
|
+
user_postcode = kwargs.get("postcode")
|
20
|
+
user_uprn = kwargs.get("uprn")
|
21
|
+
check_postcode(user_postcode)
|
22
|
+
check_uprn(user_uprn)
|
23
|
+
|
24
|
+
# Build URL to parse
|
25
|
+
council_url = f"https://swale.gov.uk/bins-littering-and-the-environment/bins/collection-days?postcode={user_postcode.replace(' ', '+')}&addresses={user_uprn}&address-submit="
|
26
|
+
|
27
|
+
# Parse URL and read if connection successful
|
28
|
+
requests.packages.urllib3.disable_warnings()
|
29
|
+
response = requests.get(council_url, verify=False)
|
30
|
+
if response.status_code == 200:
|
31
|
+
soup = BeautifulSoup(response.text, features="html.parser")
|
32
|
+
soup.prettify()
|
33
|
+
else:
|
34
|
+
raise ConnectionAbortedError("Could not parse council website.")
|
35
|
+
|
36
|
+
data = {"bins": []}
|
37
|
+
|
38
|
+
# Get the collection bullet points on the page and parse them
|
39
|
+
form_area = soup.find("form", {"class": "integration bin-lookup"})
|
40
|
+
collections = [
|
41
|
+
item.text.strip().split(",") for item in form_area.find_all("li")
|
42
|
+
]
|
43
|
+
for c in collections:
|
44
|
+
bin_type = c[0].strip()
|
45
|
+
# temp_date = c[2].strip() + " " + str(datetime.now().year)
|
46
|
+
bin_date = datetime.strptime(
|
47
|
+
c[2].strip() + " " + str(datetime.now().year), "%d %B %Y"
|
48
|
+
).strftime(date_format)
|
49
|
+
dict_data = {"type": bin_type, "collectionDate": bin_date}
|
50
|
+
data["bins"].append(dict_data)
|
51
|
+
|
52
|
+
return data
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import json
|
2
|
+
from datetime import datetime, timedelta
|
3
|
+
|
4
|
+
import requests
|
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
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
11
|
+
api_url = "http://lite.tameside.gov.uk/BinCollections/CollectionService.svc/GetBinCollection"
|
12
|
+
uprn = kwargs.get("uprn")
|
13
|
+
check_uprn(uprn)
|
14
|
+
|
15
|
+
params = {
|
16
|
+
"version": "3.1.4",
|
17
|
+
"uprn": uprn,
|
18
|
+
"token": "",
|
19
|
+
"notification": "1",
|
20
|
+
"operatingsystemid": "2",
|
21
|
+
"testmode": "true",
|
22
|
+
}
|
23
|
+
|
24
|
+
headers = {"content-type": "text/plain"}
|
25
|
+
|
26
|
+
requests.packages.urllib3.disable_warnings()
|
27
|
+
response = requests.post(api_url, json=params, headers=headers)
|
28
|
+
|
29
|
+
json_response = json.loads(response.content)["GetBinCollectionResult"]["Data"]
|
30
|
+
|
31
|
+
today = datetime.today()
|
32
|
+
eight_weeks = datetime.today() + timedelta(days=8 * 7)
|
33
|
+
data = {"bins": []}
|
34
|
+
collection_tuple = []
|
35
|
+
|
36
|
+
bin_friendly_names = {
|
37
|
+
"2": "Blue Bin",
|
38
|
+
"6": "Green Bin",
|
39
|
+
"5": "Black Bin",
|
40
|
+
"3": "Brown Bin",
|
41
|
+
}
|
42
|
+
|
43
|
+
for item in json_response:
|
44
|
+
collection_date = datetime.strptime(
|
45
|
+
item.get("CollectionDate"), "%d/%m/%Y %H:%M:%S"
|
46
|
+
)
|
47
|
+
if today.date() <= collection_date.date() <= eight_weeks.date():
|
48
|
+
bin_type = bin_friendly_names.get(item.get("BinType"))
|
49
|
+
collection_tuple.append(
|
50
|
+
(bin_type, collection_date.strftime(date_format))
|
51
|
+
)
|
52
|
+
|
53
|
+
ordered_data = sorted(collection_tuple, key=lambda x: x[1])
|
54
|
+
|
55
|
+
for item in ordered_data:
|
56
|
+
dict_data = {
|
57
|
+
"type": item[0],
|
58
|
+
"collectionDate": item[1],
|
59
|
+
}
|
60
|
+
data["bins"].append(dict_data)
|
61
|
+
|
62
|
+
return data
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import requests
|
2
|
+
import json
|
3
|
+
import urllib.parse
|
4
|
+
from dateutil.relativedelta import relativedelta
|
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
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
19
|
+
user_uprn = kwargs.get("uprn")
|
20
|
+
check_uprn(user_uprn)
|
21
|
+
|
22
|
+
data = {"bins": []}
|
23
|
+
|
24
|
+
headers = {
|
25
|
+
"Accept": "application/json, text/javascript, */*; q=0.01",
|
26
|
+
"Accept-Language": "en-GB,en;q=0.9",
|
27
|
+
"Cache-Control": "no-cache",
|
28
|
+
"Connection": "keep-alive",
|
29
|
+
# Already added when you pass json=
|
30
|
+
# 'Content-Type': 'application/json',
|
31
|
+
# 'Cookie': 'ASP.NET_SessionId=n2kxv5ssap4gobb11va1oxge',
|
32
|
+
"Origin": "https://tdcws01.tandridge.gov.uk",
|
33
|
+
"Pragma": "no-cache",
|
34
|
+
"Referer": "https://tdcws01.tandridge.gov.uk/TDCWebAppsPublic/tfaBranded/408?utm_source=pressrelease&utm_medium=smposts&utm_campaign=check_my_bin_day",
|
35
|
+
"Sec-Fetch-Dest": "empty",
|
36
|
+
"Sec-Fetch-Mode": "cors",
|
37
|
+
"Sec-Fetch-Site": "same-origin",
|
38
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.186 Safari/537.36",
|
39
|
+
}
|
40
|
+
|
41
|
+
params = {
|
42
|
+
"UPRN": f"{user_uprn}",
|
43
|
+
}
|
44
|
+
|
45
|
+
json_data = requests.post(
|
46
|
+
"https://tdcws01.tandridge.gov.uk/TDCWebAppsPublic/TDCMiddleware/RESTAPI/WhiteSpaceAPI/GetCompleteRecordByUPRN",
|
47
|
+
headers=headers,
|
48
|
+
json=params,
|
49
|
+
).json()["lstNextCollections"]
|
50
|
+
|
51
|
+
for item in json_data:
|
52
|
+
dict_data = {
|
53
|
+
"type": item.get("Service").replace("Collection Service", "").strip(),
|
54
|
+
"collectionDate": datetime.strptime(
|
55
|
+
item.get("Date"), "%d/%m/%Y %H:%M:%S"
|
56
|
+
).strftime(date_format),
|
57
|
+
}
|
58
|
+
data["bins"].append(dict_data)
|
59
|
+
|
60
|
+
return data
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import requests
|
2
|
+
import json
|
3
|
+
|
4
|
+
from dateutil.relativedelta import relativedelta
|
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
|
+
def parse_data(self, page: str, **kwargs) -> dict:
|
19
|
+
user_uprn = kwargs.get("uprn")
|
20
|
+
check_uprn(user_uprn)
|
21
|
+
|
22
|
+
data = {"bins": []}
|
23
|
+
collections = []
|
24
|
+
api_url = f"https://dac.telford.gov.uk/BinDayFinder/Find/PropertySearch?uprn={user_uprn}"
|
25
|
+
|
26
|
+
response = requests.get(api_url)
|
27
|
+
if response.status_code != 200:
|
28
|
+
raise ConnectionError("Could not get latest data!")
|
29
|
+
|
30
|
+
json_data = json.loads(response.text.replace("\\", "")[1:-1])["bincollections"]
|
31
|
+
for item in json_data:
|
32
|
+
collection_date = datetime.strptime(
|
33
|
+
remove_ordinal_indicator_from_date_string(item.get("nextDate")),
|
34
|
+
"%A %d %B",
|
35
|
+
)
|
36
|
+
next_collection = collection_date.replace(year=datetime.now().year)
|
37
|
+
if datetime.now().month == 12 and next_collection.month == 1:
|
38
|
+
next_collection = next_collection + relativedelta(years=1)
|
39
|
+
|
40
|
+
collections.append((item.get("name"), next_collection))
|
41
|
+
|
42
|
+
ordered_data = sorted(collections, key=lambda x: x[1])
|
43
|
+
for item in ordered_data:
|
44
|
+
dict_data = {
|
45
|
+
"type": item[0],
|
46
|
+
"collectionDate": item[1].strftime(date_format),
|
47
|
+
}
|
48
|
+
data["bins"].append(dict_data)
|
49
|
+
|
50
|
+
return data
|