diff --git a/uk_bin_collection/tests/features/validate_council_outputs.feature b/uk_bin_collection/tests/features/validate_council_outputs.feature index 912ce532bf..e97aa50569 100644 --- a/uk_bin_collection/tests/features/validate_council_outputs.feature +++ b/uk_bin_collection/tests/features/validate_council_outputs.feature @@ -103,6 +103,7 @@ Feature: Test each council output matches expected results | WealdenDistrictCouncil | None | None | | WelhatCouncil | None | None | | WestLothianCouncil | http://selenium:4444 | local | + | WestSuffolkCouncil | http://selenium:4444 | local | | WiganBoroughCouncil | None | None | | WiltshireCouncil | None | None | | WindsorAndMaidenheadCouncil | None | None | diff --git a/uk_bin_collection/tests/input.json b/uk_bin_collection/tests/input.json index 230f803712..ebfbd492dd 100644 --- a/uk_bin_collection/tests/input.json +++ b/uk_bin_collection/tests/input.json @@ -648,6 +648,15 @@ "wiki_name": "West Lothian Council", "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes" }, + "WestSuffolkCouncil": { + "house_number": "1 The Drift", + "postcode": "IP28 6DR", + "skip_get_url": true, + "url": "https://westsuffolk-self.achieveservice.com/service/WSS_EX_Inf_Bin_Collection_Postcode_Lookup", + "web_driver": "http://selenium:4444", + "wiki_name": "West Suffolk Council", + "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes and the postcode in the postcode parameter" + }, "WiganBoroughCouncil": { "postcode": "WN24UQ", "skip_get_url": true, @@ -683,4 +692,4 @@ "url": "https://waste-api.york.gov.uk/api/Collections/GetBinCollectionDataForUprn/", "wiki_name": "York Council" } -} +} \ No newline at end of file diff --git a/uk_bin_collection/uk_bin_collection/councils/WestSuffolkCouncil.py b/uk_bin_collection/uk_bin_collection/councils/WestSuffolkCouncil.py new file mode 100644 index 0000000000..bb9521b96c --- /dev/null +++ b/uk_bin_collection/uk_bin_collection/councils/WestSuffolkCouncil.py @@ -0,0 +1,120 @@ +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys +from selenium.common.exceptions import TimeoutException +from selenium.webdriver.support.ui import Select, WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from dateutil.parser import parse + +from uk_bin_collection.uk_bin_collection.common import create_webdriver, date_format +from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass + + +class CouncilClass(AbstractGetBinDataClass): + def wait_for_element(self, driver, element_type, element: str, timeout: int = 5): + element_present = EC.presence_of_element_located((element_type, element)) + self.wait_for_element_conditions(driver, element_present, timeout=timeout) + + def wait_for_element_conditions(self, driver, conditions, timeout: int = 5): + try: + WebDriverWait(driver, timeout).until(conditions) + except TimeoutException: + print("Timed out waiting for page to load") + raise + + def parse_data(self, page: str, **kwargs) -> dict: + web_driver = kwargs.get("web_driver") + page = "https://westsuffolk-self.achieveservice.com/service/WSS_EX_Inf_Bin_Collection_Postcode_Lookup" + + # Assign user info + user_postcode = kwargs.get("postcode") + user_paon = kwargs.get("paon") + + # Create Selenium webdriver + driver = create_webdriver(web_driver) + driver.get(page) + + # Click the cookie button + css_selector = "#close-cookie-message" + self.wait_for_element(driver, By.CSS_SELECTOR, css_selector) + cookie_button = driver.find_element(By.CSS_SELECTOR, css_selector) + cookie_button.click() + + # switch to the form iframe + iframe = driver.find_element(By.CSS_SELECTOR, "#fillform-frame-1") + driver.switch_to.frame(iframe) + + # Wait for form to load + xpath_selector = '//*[@id="postcode_search"]' + self.wait_for_element(driver, By.XPATH, xpath_selector, timeout=6) + + # Send postcode + postcode_input_box = driver.find_element(By.XPATH, xpath_selector) + postcode_input_box.send_keys(user_postcode) + postcode_input_box.send_keys(Keys.ENTER) + + search_for_address_button = driver.find_element( + By.CSS_SELECTOR, "#addresssearch" + ) + search_for_address_button.click() + + # Find address + self.wait_for_element( + driver, By.CSS_SELECTOR, "#selectaddr > option:nth-child(2)" + ) + select_address_dropdown = Select( + driver.find_element(By.CSS_SELECTOR, "#selectaddr") + ) + if user_paon is not None: + for option in select_address_dropdown.options: + if user_paon in option.text: + select_address_dropdown.select_by_visible_text(option.text) + break + else: + # If we've not been supplied an address, pick the second entry + select_address_dropdown.select_by_index(1) + + # Click the get schedule button (Once it's available) + self.wait_for_element_conditions( + driver, + EC.element_to_be_clickable((By.CSS_SELECTOR, "#searchforcollections")), + ) + check_schedules_button = driver.find_element( + By.CSS_SELECTOR, "#searchforcollections" + ) + check_schedules_button.click() + + # Grab the bin output data + # Span is of the format: + # Your Next bin collections are: + # Black bin - Wednesday 29th November + # Blue bin - Wednesday 6th December + # Brown bin - Wednesday 6th December + output_span_css_selector = "//span[@data-name='statictext6']" + output_span_ec = EC.all_of( + EC.presence_of_element_located((By.XPATH, output_span_css_selector)), + EC.text_to_be_present_in_element( + (By.XPATH, output_span_css_selector), "bin collections" + ), + ) + self.wait_for_element_conditions(driver, output_span_ec) + data_span = driver.find_element(By.XPATH, output_span_css_selector) + + data = {"bins": []} + for item in data_span.text.splitlines(): + if "bin" in item and " - " in item: + bin_data = item.split(" - ") + bin_name = bin_data[0] + bin_date = bin_data[1] + parsed_bin_date = parse(bin_date, fuzzy_with_tokens=True)[0] + + dict_data = { + "type": bin_name, + "collectionDate": parsed_bin_date.strftime(date_format), + } + + data["bins"].append(dict_data) + + # Quit Selenium webdriver to release session + driver.quit() + + return data