Source code for dallinger.bots

"""Bots."""

import logging
from cached_property import cached_property
from urlparse import urlparse, parse_qs

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

logger = logging.getLogger(__file__)


[docs]class BotBase(object): """A base class for Bots that works with the built-in demos.""" def __init__(self, URL, assignment_id='', worker_id=''): logger.info("Creating bot with URL: %s." % URL) self.URL = URL parts = urlparse(URL) query = parse_qs(parts.query) if not assignment_id: assignment_id = query.get('assignmentId', [''])[0] self.assignment_id = assignment_id if not worker_id: worker_id = query.get('workerId', [''])[0] self.worker_id = worker_id self.unique_id = worker_id + ':' + assignment_id @cached_property def driver(self): """Returns a Selenium WebDriver instance of the type requested in the configuration.""" from dallinger.config import get_config config = get_config() if not config.ready: config.load() driver_url = config.get('webdriver_url', None) driver_type = config.get('webdriver_type', 'phantomjs').lower() if driver_url: capabilities = {} if driver_type == 'firefox': capabilities = webdriver.DesiredCapabilities.FIREFOX elif driver_type == 'chrome': capabilities = webdriver.DesiredCapabilities.CHROME elif driver_type == 'phantomjs': capabilities = webdriver.DesiredCapabilities.PHANTOMJS else: raise ValueError( 'Unsupported remote webdriver_type: {}'.format(driver_type)) driver = webdriver.Remote( desired_capabilities=capabilities, command_executor=driver_url ) elif driver_type == 'phantomjs': driver = webdriver.PhantomJS() elif driver_type == 'firefox': driver = webdriver.Firefox() elif driver_type == 'chrome': driver = webdriver.Chrome() else: raise ValueError( 'Unsupported webdriver_type: {}'.format(driver_type)) driver.set_window_size(1024, 768) logger.info("Created {} webdriver.".format(driver_type)) return driver
[docs] def sign_up(self): """Accept HIT, give consent and start experiment.""" try: self.driver.get(self.URL) logger.info("Loaded ad page.") begin = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CLASS_NAME, 'btn-primary'))) begin.click() logger.info("Clicked begin experiment button.") WebDriverWait(self.driver, 10).until( lambda d: len(d.window_handles) == 2) self.driver.switch_to_window(self.driver.window_handles[-1]) self.driver.set_window_size(1024, 768) logger.info("Switched to experiment popup.") consent = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.ID, 'consent'))) consent.click() logger.info("Clicked consent button.") participate = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CLASS_NAME, 'btn-success'))) participate.click() logger.info("Clicked start button.") return True except TimeoutException: logger.error("Error during experiment sign up.") return False
[docs] def participate(self): """Participate in the experiment.""" logger.error("Bot class does not define participate method.") raise NotImplementedError
[docs] def complete_questionnaire(self): """Complete the standard debriefing form.""" pass
[docs] def sign_off(self): """Submit questionnaire and finish.""" try: feedback = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.ID, 'submit-questionnaire'))) self.complete_questionnaire() feedback.click() logger.info("Clicked submit questionnaire button.") self.driver.switch_to_window(self.driver.window_handles[0]) self.driver.set_window_size(1024, 768) logger.info("Switched back to initial window.") return True except TimeoutException: logger.error("Error during experiment sign off.") return False
def complete_experiment(self, status): url = self.driver.current_url p = urlparse(url) complete_url = '%s://%s/%s?uniqueId=%s' complete_url = complete_url % (p.scheme, p.netloc, status, self.unique_id) self.driver.get(complete_url) logger.info("Forced call to %s: %s" % (status, complete_url))
[docs] def run_experiment(self): """Sign up, run the ``participate`` method, then sign off and close the driver.""" try: self.sign_up() self.participate() if self.sign_off(): self.complete_experiment('worker_complete') else: self.complete_experiment('worker_failed') finally: self.driver.quit()