StegSchreck/RatS

View on GitHub
RatS/base/base_site.py

Summary

Maintainability
A
0 mins
Test Coverage
import logging
import time

import datetime
import json
import os
from configparser import RawConfigParser
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By

from RatS.base.base_exceptions import LoginFailedException, SiteNotReachableException
from RatS.base.movie_entity import Site
from RatS.utils.browser_handler import BrowserHandler

TIMESTAMP = datetime.datetime.fromtimestamp(time.time()).strftime("%Y%m%d%H%M%S")
EXPORTS_FOLDER = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, "RatS", "exports"))


class BaseSite:
    def __init__(self, args):
        self.args = args

        self.site_name: str = type(self).__name__
        self.site: Site = Site(self.site_name.upper())

        self.config = RawConfigParser()
        self.__read_config_file("credentials.cfg.orig")
        self.__read_config_file("credentials.cfg")
        self._parse_credentials()
        self.CREDENTIALS_VALID = self._validate_credentials()

        if self.CREDENTIALS_VALID:
            self.LOGIN_PAGE: str = self._get_login_page_url()

            self._init_browser()

            self._parse_configuration()

    def __read_config_file(self, filename: str):
        self.config.read(os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, filename)))

    def _parse_credentials(self):
        if os.environ.get(self.site_name.upper() + "_USERNAME"):
            self.USERNAME: str = os.environ.get(self.site_name.upper() + "_USERNAME")
        else:
            self.USERNAME: str = self.config[self.site_name]["USERNAME"]
        if os.environ.get(self.site_name.upper() + "_PASSWORD"):
            self.PASSWORD: str = os.environ.get(self.site_name.upper() + "_PASSWORD")
        else:
            self.PASSWORD: str = self.config[self.site_name]["PASSWORD"]

    def _validate_credentials(self):
        return (
            self.USERNAME
            and self.PASSWORD
            and self.USERNAME != "abc"
            and self.USERNAME != "abc@def.de"
            and self.PASSWORD != "def"
        )

    def _get_login_page_url(self):
        raise NotImplementedError("This is not the implementation you are looking for.")

    def _parse_configuration(self):
        # this method should be overwritten by a site, if there are more configs to parse than just the credentials
        # and for things which need a running browser
        pass

    def _init_browser(self):
        self.browser_handler = BrowserHandler(self.args)
        self.browser = self.browser_handler.browser
        self.login()

    def login(self):
        logging.info(f"===== {self.site_name}: performing login")
        self.open_url_with_521_retry(self.LOGIN_PAGE)
        time.sleep(1)

        self._handle_cookie_notice_if_present()
        self._pre_login_action()

        iteration = 0
        while self._user_is_not_logged_in():
            iteration += 1
            try:
                self._insert_login_credentials()
                self._click_login_button()
            except NoSuchElementException as e:
                if iteration > 10:
                    raise e
                time.sleep(iteration * 1)
                continue
            self._handle_captcha_challenge_if_present()
            if iteration > 2:
                self._handle_login_unsuccessful()

    def _pre_login_action(self):
        # to be implemented for sites individually
        pass

    def _handle_cookie_notice_if_present(self):
        # to be implemented for sites with cookie banners individually
        pass

    def _handle_captcha_challenge_if_present(self):
        # to be implemented for sites with captchas individually
        pass

    def _handle_login_unsuccessful(self):
        time.sleep(1)
        if self._user_is_not_logged_in():
            self.browser_handler.kill()
            raise LoginFailedException(
                f"Login to {self.site_name} failed. - "
                "Please check if the credentials are correctly set in your credentials.cfg"
            )

    def _user_is_not_logged_in(self):
        return (
            len(self.browser.find_elements(By.XPATH, self.LOGIN_BUTTON_SELECTOR)) > 0
            and len(self.browser.find_elements(By.XPATH, self.LOGIN_USERNAME_SELECTOR)) > 0
            and len(self.browser.find_elements(By.XPATH, self.LOGIN_PASSWORD_SELECTOR)) > 0
        )

    def _insert_login_credentials(self):
        login_field_user = self.browser.find_element(By.XPATH, self.LOGIN_USERNAME_SELECTOR)
        login_field_user.clear()
        login_field_user.send_keys(self.USERNAME)
        login_field_password = self.browser.find_element(By.XPATH, self.LOGIN_PASSWORD_SELECTOR)
        login_field_password.clear()
        login_field_password.send_keys(self.PASSWORD)

    def _click_login_button(self):
        login_button = self.browser.find_element(By.XPATH, self.LOGIN_BUTTON_SELECTOR)
        login_button.click()
        time.sleep(2)  # wait for page to load

    def get_json_from_html(self):
        response = self.browser.find_element(By.TAG_NAME, "pre").text.strip()
        return json.loads(response)

    def open_url_with_521_retry(self, url: str):
        iteration = 0
        self.browser.get(url)

        while len(self.browser.find_elements(By.ID, "cf-error-details")) > 0:
            if iteration >= 10:
                raise SiteNotReachableException
            iteration += 1
            self.browser.get(url)