From 81418f3d2fa7716c092dde324a592e914eea890b Mon Sep 17 00:00:00 2001 From: Leonardo Barcaroli Date: Mon, 14 Jan 2019 12:11:17 +0100 Subject: [PATCH] login/logout mostly working. --- bot_z/bot_z.py | 116 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 16 deletions(-) diff --git a/bot_z/bot_z.py b/bot_z/bot_z.py index 5b1cd25..facf719 100644 --- a/bot_z/bot_z.py +++ b/bot_z/bot_z.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- -"""Main module.""" +""" +Operator is the main object that interacts with the foreign +service. It exposes methods to login, logout and check in and out. +""" +from datetime import datetime, timedelta import logging import os import time @@ -15,10 +19,12 @@ from selenium.common.exceptions import WebDriverException, NoSuchElementExceptio logging.basicConfig( - level=logging.INFO, + level=os.environ.get('BOTZ_LOGLEVEL', logging.INFO), format='%(levelname)s: [%(name)s] -> %(message)s' ) +m_logger = logging.getLogger(__name__) +m_logger.debug("Init at debug") def safely(f: T.Callable) -> T.Callable: def _protection(self, *args, **kwargs): @@ -30,11 +36,43 @@ def safely(f: T.Callable) -> T.Callable: return _protection +def _is_present(driver: wd.Firefox, xpath: str) -> bool: + try: + driver.find_element_by_xpath(xpath) + return True + except NoSuchElementException: + return False + + +def is_present(driver: wd.Firefox, + xpath: str, + timeout: T.Optional[timedelta]=None + ) -> bool: + """ + Helper function. If an element is present in the DOM tree, + returns true. False otherwise. + """ + if timeout is None: + return _is_present(driver, xpath) + + _now = datetime.now() + _elapsed = timedelta(seconds=0) + while _elapsed < timeout: + m_logger.debug("Not yet present: %s", xpath) + if _is_present(driver, xpath): + m_logger.debug("Present: %s", xpath) + return True + time.sleep(0.5) + _elapsed = datetime.now() - _now + return False + + class Operator(wd.Firefox): def __init__( self, base_uri: str, name: str = None, + timeout: int=20, proxy: T.Optional[T.Tuple[str, int]] = None, headless: bool = True, debug: bool = False, @@ -53,6 +91,7 @@ class Operator(wd.Firefox): self.debug = debug self.base_uri = base_uri + self.timeout = timedelta(seconds=timeout) if proxy: self.profile.set_preference('network.proxy.type', 1) @@ -72,15 +111,29 @@ class Operator(wd.Firefox): self._checked_in = False @safely - def do_login(self, user: str, password: str) -> None: + def login(self, user: str, password: str, force: bool=False) -> None: """ Do the login and proceed. """ + if self._logged_in: + self.logger.warning("Already logged in: %s", user) + if not force: + return + self.logger.info("Forcing login: %s", user) # Retrieve login page - #self.get('{}/jsp/login.jsp'.format(self.base_uri)) self.get(self.base_uri) + _correct_url = 'cpccchk' in self.current_url + _now = datetime.now() + _elapsed = timedelta(seconds=0) + while not _correct_url: + self.logger.debug("Not yet on login page: %s", self.current_url) + time.sleep(0.5) + _correct_url = 'cpccchk' in self.current_url + _elapsed = datetime.now() - _now + if _elapsed > self.timeout: + break self.logger.debug("After login get: %s", self.current_url) - time.sleep(3) + time.sleep(1) # Username user_form = self.find_element_by_name('m_cUserName') # Password @@ -91,24 +144,51 @@ class Operator(wd.Firefox): user_form.send_keys(user) pass_form.send_keys(password) do_it = True - if self.debug: - _do_it = input("Really do the login? [y/n]").lower() + if self.debug and not force: + _do_it = input("Really do the login? [y/n] ").lower() do_it = True if _do_it == "y" else False if do_it: - self.logger.debug("Before clicking: %s", self.current_url) login_butt.submit() - self.logger.debug("After clicking: %s", self.current_url) + time.sleep(5) self.logger.debug("Login result: %s", self.title) if 'Routine window' in self.title: + self.logger.debug("Reloading...") self.refresh() - try: - self.find_element_by_xpath('//input[contains(@value, "Accedi")]') - # TODO: reckon a proper timeout mechanism - # based on cookie expire time + self.switch_to.alert.accept() + if is_present(self, '//a[contains(@class, "imgMenu_ctrl")]', self.timeout): self._logged_in = True self.logger.info("Login success for user: %s", user) - except NoSuchElementException as e: - self.logger.error("Login failed: %s", e) + else: + self.logger.error("Login failed: %s", user) + + @safely + def logout(self, user: str, force: bool=False) -> None: + """ + Do the logout. + """ + if not self._logged_in: + self.logger.warning("Not yet logged in for user: %s", user) + if not force: + return + self.logger.info("Forcing logout: %s", user) + # Find the Profile menu and open it + profile_butt = self.find_element_by_xpath('//span[contains(@id, "imgNoPhotoLabel")]') + profile_butt.click() + time.sleep(1) + # Find the logout button + logout_butt = self.find_element_by_xpath('//input[@value="Logout user"]') + logout_butt.click() + if "jsp/usut_wapplogout_portlet.jsp" in self.current_url: + self.logger.info("User successfully logged out: %s", user) + else: + self.logger.warning("Logout failed: %s", user) + + def logged_in(self): + """ + Check if already logged in. Checks if page is '/jsp/home.jsp' + and if login cookie is set (and not expired). + """ + pass @safely def check_in(self, force: bool=False) -> None: @@ -121,6 +201,8 @@ class Operator(wd.Firefox): self.logger.warn("Already logged in!") if not force: return + enter_butt = self.find_element_by_xpath('//input[@value="Entrata"]') + enter_butt.submit() # Click the check in button and change # self._checked_in state in case of success pass @@ -136,9 +218,11 @@ class Operator(wd.Firefox): self.logger.warn("Not yet logged in!") if not force: return + exit_butt = self.find_element_by_xpath('//input[@value="Uscita"]') + exit_butt.submit() # Click the check in button and change # self._checked_in state in case of success pass - def __del__(self): + def __del__(self) -> None: self.quit()