Пример #1
0
def check_file(args):
    file_path = args['<file>']

    if os.path.isfile(file_path):
        return file_path

    Logger.error("File does not exist: %s" % file_path, sysexit=True)
Пример #2
0
    def authenticate(self, login, password):
        """
        Performs authentication

        :param login: BGG login
        :param password: BGG password
        """
        Logger.info("Authenticating...", break_line=False)

        self.driver.get("%s/login" % BGG_BASE_URL)

        # When user is already authenticated, just skip this task
        # TODO Handle case where another user is logged in
        if self.is_authenticated(login):
            Logger.info(" (already logged) [done]", append=True)
            return True

        self.update_text(self.driver.find_element_by_id("login_username"), login)
        self.update_text(self.driver.find_element_by_id("login_password"), password)
        self.driver.find_element_by_xpath("//div[@class='menu_login']//input[@type='submit']")\
            .click()

        if self.is_authenticated(login):
            Logger.info(" [done]", append=True)
            return True

        Logger.info(" [error]", append=True)
        Logger.error("Authentication failed, check your credentials!")
        return False
Пример #3
0
    def delete(self, game_attrs):
        """
        Delete a game in the collection

        :param game_attrs: Game attributes as a dictionary. Only the id will be used
        """

        self.goto(game_attrs)
        #<button type="button" uib-tooltip="More options" tooltip-popup-delay="500" tooltip-append-to-body="true" tooltip-placement="left" class="btn btn-empty text-muted dropdown-toggle" uib-dropdown-toggle="" aria-haspopup="true" aria-expanded="false"> 				<span class="glyphicon glyphicon-option-vertical"></span> 			</button>
        try:
            if self.notincollection():
                Logger.info(" (not in collection)",
                            append=True,
                            break_line=False)
                return  # Not in collection
            self.openeditform()

            more = self.driver.find_element_by_xpath(
                "//button[@uib-tooltip='More options']'")
            more.click()

            #<button type="button" class="btn btn-empty" ng-click="editctrl.deleteItem(editctrl.editdata.item)"> 					Delete from Collection 				</button>
            del_button = self.driver.find_element_by_xpath(
                '//button[ng-click="editctrl.deleteItem(editctrl.editdata.item)"]'
            )
            del_button.click()
        except NoSuchElementException:
            Logger.info(" Failed: can't find delete button!",
                        append=True,
                        break_line=False)
            return
Пример #4
0
    def create_ci_driver(self):
        # See http://docs.travis-ci.com/user/gui-and-headless-browsers/
        capabilities = DesiredCapabilities.FIREFOX.copy()
        if os.environ.get('TRAVIS') == 'true':
            Logger.info("Configure Travis for Sauce Labs")
            capabilities.update(
                {
                    'tunnel-identifier': os.environ["TRAVIS_JOB_NUMBER"],
                    'build': os.environ["TRAVIS_JOB_NUMBER"],
                    'tags': [os.environ["TRAVIS_PYTHON_VERSION"], "CI"]
                }
            )
            sauce_url = "http://%s:%s@localhost:4445/wd/hub" %\
                        ((os.environ["SAUCE_USERNAME"]), (os.environ["SAUCE_ACCESS_KEY"]))
        else:
            Logger.info("Configure direct usage of Sauce Labs")
            sauce_url = "http://%s:%[email protected]:80/wd/hub" %\
                        ((os.environ["SAUCE_USERNAME"]), (os.environ["SAUCE_ACCESS_KEY"]))

        capabilities.update(
            {
                'name': 'bggcli-%s' % self.name
            }
        )

        result = webdriver.Remote(desired_capabilities=capabilities, command_executor=sauce_url)
        result.implicitly_wait(20)

        return result
Пример #5
0
def show_duration(timer_start):
    m, s = divmod(time.time() - timer_start, 60)
    h, m = divmod(m, 60)
    if h > 0:
        Logger.info("(took %d:%02d:%02d)" % (h, m, s))
    else:
        Logger.info("(took %02d:%02d)" % (m, s))
Пример #6
0
    def create_ci_driver(self):
        # See http://docs.travis-ci.com/user/gui-and-headless-browsers/
        capabilities = DesiredCapabilities.FIREFOX.copy()
        if os.environ.get('TRAVIS') == 'true':
            Logger.info("Configure Travis for Sauce Labs")
            capabilities.update({
                'tunnel-identifier':
                os.environ["TRAVIS_JOB_NUMBER"],
                'build':
                os.environ["TRAVIS_JOB_NUMBER"],
                'tags': [os.environ["TRAVIS_PYTHON_VERSION"], "CI"]
            })
            sauce_url = "http://%s:%s@localhost:4445/wd/hub" %\
                        ((os.environ["SAUCE_USERNAME"]), (os.environ["SAUCE_ACCESS_KEY"]))
        else:
            Logger.info("Configure direct usage of Sauce Labs")
            sauce_url = "http://%s:%[email protected]:80/wd/hub" %\
                        ((os.environ["SAUCE_USERNAME"]), (os.environ["SAUCE_ACCESS_KEY"]))

        capabilities.update({'name': 'bggcli-%s' % self.name})

        result = webdriver.Remote(desired_capabilities=capabilities,
                                  command_executor=sauce_url)
        result.implicitly_wait(20)

        return result
Пример #7
0
def default_export(req):
    response = urllib2.urlopen(req)

    if response.code == 202:
        Logger.info('Export is queued, will retry in %ss' %
                    EXPORT_QUERY_INTERVAL)
        time.sleep(EXPORT_QUERY_INTERVAL)
        return default_export(req)

    if response.code == 200:
        return response

    # Write response in a text file otherwise
    try:
        with open(ERROR_FILE_PATH, "wb") as error_file:
            error_file.write(response.read())
        Logger.error("Unexpected response, content has been written in %s" %
                     ERROR_FILE_PATH)
    except Exception as e:
        raise Exception(
            'Unexpected HTTP response for export request, and cannot write '
            'response content in %s: %s' % (ERROR_FILE_PATH, e))
    raise Exception(
        'Unexpected HTTP response for export request, response content written in '
        '%s' % ERROR_FILE_PATH)
Пример #8
0
def check_file(args):
    file_path = args["<file>"]

    if os.path.isfile(file_path):
        return file_path

    Logger.error("File does not exist: %s" % file_path, sysexit=True)
Пример #9
0
def show_duration(timer_start):
    m, s = divmod(time.time() - timer_start, 60)
    h, m = divmod(m, 60)
    if h > 0:
        Logger.info("(took %d:%02d:%02d)" % (h, m, s))
    else:
        Logger.info("(took %02d:%02d)" % (m, s))
Пример #10
0
def update_collid(opener, collid, fieldname, values):
    values.update({'ajax': 1, 'action': 'savedata',
                   'collid': collid, 'fieldname': fieldname})
    values = { k:unicode(v).encode('utf-8') for k,v in values.iteritems() }
    response = opener.open(BGG_BASE_URL + '/geekcollection.php', urlencode(values))

    if response.code != 200:
        Logger.error("Failed to update 'collid'=%s!" % collid, sysexit=True)
Пример #11
0
def parse_commad_args(command_module, argv):
    result = docopt(command_module.__doc__, argv, version='bggcli %s' % VERSION,
                    options_first=False)

    try:
        return result, explode_dyn_args(result['-c'])
    except StandardError:
        Logger.info('Invalid syntax for -c option, should be "-c key=value"!')
        return None
Пример #12
0
 def __exit__(self, type, value, traceback):
     if not self.browser_keep:
         try:
             self.driver.quit()
             Logger.verbose("Close webdriver '%s'" % self.name)
         except WebDriverException as e:
             Logger.info("Encountered error when closing webdriver '%s' (will be skipped): %s"
                         % (self.name, repr(e)))
     return type is None
Пример #13
0
    def check(self):
        if 'objectid' not in self.reader.fieldnames:
            Logger.error("Cannot process the CSV file, it should contain at least a column named "
                         "'objectid'! Provided columns: %s" % self.reader.fieldnames, sysexit=True)
            return

        unknown_fields = set(self.reader.fieldnames) - set(BGG_SUPPORTED_FIELDS)
        if unknown_fields:
            Logger.info('Some fields are not supported in your CSV file, they will be skipped: %s'
                        % unknown_fields)
Пример #14
0
def show_help(command_args):
    if len(command_args) > 0:
        help_command = command_args[0]
        try:
            command_module = import_command_module(help_command)
            Logger.info(docopt(command_module.__doc__, ['-h']))
        except ImportError:
            exit_unknown_command(help_command)
    else:
        Logger.info(docopt(__doc__, argv=['-h']))
Пример #15
0
 def __exit__(self, type, value, traceback):
     if not self.browser_keep:
         try:
             self.driver.quit()
             Logger.verbose("Close webdriver '%s'" % self.name)
         except WebDriverException as e:
             Logger.info(
                 "Encountered error when closing webdriver '%s' (will be skipped): %s"
                 % (self.name, repr(e)))
     return type is None
Пример #16
0
 def notincollection(self):
     """Either returns button or False, suitable for if statement"""
     try:
         Logger.info("edit button? ", append=True, break_line=False)
         button1 = self.driver.find_element_by_xpath(
             "(//button[contains(@ng-click, 'colltoolbarctrl.editItem')])[last()]"
         )
         return button1
     except NoSuchElementException:
         return False
Пример #17
0
def show_help(command_args):
    if len(command_args) > 0:
        help_command = command_args[0]
        try:
            command_module = import_command_module(help_command)
            Logger.info(docopt(command_module.__doc__, ['-h']))
        except ImportError:
            exit_unknown_command(help_command)
    else:
        Logger.info(docopt(__doc__, argv=['-h']))
Пример #18
0
    def update(self, game_attrs):
        """
        Update game details

        :param game_attrs: Game attributes as a dictionary
        """
        self.goto(game_attrs)

        try:
            self.itemEl = self.driver.find_element_by_xpath(
                "//table[@class='collectionmodule_table']")
            Logger.info(" (already in collection)",
                        append=True,
                        break_line=False)
        except NoSuchElementException:
            self.driver.find_element_by_xpath(
                "(//a[contains(@onclick, 'CE_ModuleAddItem')])[last()]").click(
                )
            self.itemEl = self.wait.until(
                EC.element_to_be_clickable(
                    (By.XPATH, "//table[@class='collectionmodule_table']")))

        # Fill all provided values using dynamic invocations 'fill_[fieldname]'
        for key in game_attrs:
            if key in BGG_SUPPORTED_FIELDS:
                value = game_attrs[key]
                if value is not None:
                    getattr(self, "fill_%s" % key)(value)

        # Save "Private Info" popup if opened
        try:
            self.privateInfoPopupEl.find_element_by_xpath(
                ".//input[@type='submit']").click()
        except WebDriverException:
            pass
        else:
            self.wait.until(
                EC.element_to_be_clickable(
                    (By.XPATH,
                     ".//td[@class='collection_ownershipmod editfield']")))

        # Save "Version" popup if opened
        try:
            self.versionPopupEl.find_element_by_xpath(
                ".//input[@type='submit']").click()
        except WebDriverException:
            pass
        else:
            self.wait.until(
                EC.element_to_be_clickable(
                    (By.XPATH,
                     ".//td[@class='collection_versionmod editfield']")))
Пример #19
0
def execute(args, options):
    login = args['--login']

    file_path = check_file(args)

    csv_reader = CsvReader(file_path)
    csv_reader.open()

    game_count = csv_reader.rowCount

    if not args['--force']:
        sys.stdout.write(
            "You are about to delete %s games in you collection (%s), "
            "please enter the number of games displayed here to confirm you want to continue: "
            % (game_count, login))

        if raw_input() != game_count.__str__():
            Logger.error('Operation canceled, number does not match (should be %s).' % game_count,
                         sysexit=True)
            return

    Logger.info("Deleting games for '%s' account..." % login)

    with WebDriver('collection-delete', args, options) as web_driver:
        if not LoginPage(web_driver.driver).authenticate(login, args['--password']):
            sys.exit(1)

        Logger.info("Deleting %s games..." % game_count)
        game_page = GamePage(web_driver.driver)
        csv_reader.iterate(lambda row: game_page.delete(row))
        Logger.info("Deletion has finished.")
Пример #20
0
def execute(args, options):
    login = args['--login']

    file_path = check_file(args)

    csv_reader = CsvReader(file_path)
    csv_reader.open()

    game_count = csv_reader.rowCount

    if not args['--force']:
        sys.stdout.write(
            "You are about to delete %s games in you collection (%s), "
            "please enter the number of games displayed here to confirm you want to continue: "
            % (game_count, login))

        if raw_input() != game_count.__str__():
            Logger.error(
                'Operation canceled, number does not match (should be %s).' %
                game_count,
                sysexit=True)
            return

    Logger.info("Deleting games for '%s' account..." % login)

    with WebDriver('collection-delete', args, options) as web_driver:
        if not LoginPage(web_driver.driver).authenticate(
                login, args['--password']):
            sys.exit(1)

        Logger.info("Deleting %s games..." % game_count)
        game_page = GamePage(web_driver.driver)
        csv_reader.iterate(lambda row: game_page.delete(row))
        Logger.info("Deletion has finished.")
Пример #21
0
    def check(self):
        if 'objectid' not in self.reader.fieldnames:
            Logger.error(
                "Cannot process the CSV file, it should contain at least a column named "
                "'objectid'! Provided columns: %s" % self.reader.fieldnames,
                sysexit=True)
            return

        unknown_fields = set(
            self.reader.fieldnames) - set(BGG_SUPPORTED_FIELDS)
        if unknown_fields:
            Logger.info(
                'Some fields are not supported in your CSV file, they will be skipped: %s'
                % unknown_fields)
Пример #22
0
def game_deleter(opener, row):
    collid = row['collid']
    if not collid:
        return

    response = opener.open(
        BGG_BASE_URL + '/geekcollection.php',
        urlencode({
            'ajax': 1,
            'action': 'delete',
            'collid': collid
        }))

    if response.code != 200:
        Logger.error("Failed to delete 'collid'=%s!" % collid, sysexit=True)
Пример #23
0
 def game_importer(opener, row, force_new=False):
    collid = row['collid']

    if force_new or not collid:
        objectid = row['objectid']
        if not objectid.isdigit():
            Logger.error("Invalid 'objectid'=%s!" % objectid, sysexit=True)
        collid = create_collid(opener, objectid)

    values = { k:v for k,v in row.iteritems() if k in [
        'own', 'prevowned', 'fortrade', 'want', 'wanttobuy',
        'wishlist', 'wishlistpriority', 'wanttoplay', 'preordered'] }
    if len(values):
        update_collid(opener, collid, 'status', values)

    values = { k:v for k,v in row.iteritems() if k in [
        'pp_currency', 'pricepaid', 'cv_currency', 'currvalue', 'quantity',
        'acquisitiondate', 'acquiredfrom', 'privatecomment', 'invdate', 'invlocation'] }
    if len(values):
        update_collid(opener, collid, 'ownership', values)

    if '_versionid' in row.keys() and row['_versionid'].isdigit():
        update_collid(opener, collid, 'version', {'geekitem_version': 1, 'objectid': row['_versionid']})
    elif 'geekitem_version' in row.keys() and 'objectid' in row.keys() and \
            row['geekitem_version'].isdigit() and int(row['geekitem_version']) == 1:
        update_collid(opener, collid, 'version', {'geekitem_version': 1, 'objectid': row['objectid']})
    else:
        values = { k:v for k,v in row.iteritems() if k in [
            'imageid', 'publisherid', 'languageid', 'year', 'other', 'barcode'] }
        if len(values):
            update_collid(opener, collid, 'version', values)

    if 'objectname' in row.keys():
        update_collid(opener, collid, 'objectname', {'value': row['objectname']})
    if 'rating' in row.keys():
        update_collid(opener, collid, 'rating', {'rating': row['rating']})
    if 'weight' in row.keys():
        update_collid(opener, collid, 'weight', {'weight': row['weight']})
    if 'comment' in row.keys():
        update_collid(opener, collid, 'comment', {'value': row['comment']})
    if 'conditiontext' in row.keys():
        update_collid(opener, collid, 'conditiontext', {'value': row['conditiontext']})
    if 'wantpartslist' in row.keys():
        update_collid(opener, collid, 'wantpartslist', {'value': row['wantpartslist']})
    if 'haspartslist' in row.keys():
        update_collid(opener, collid, 'haspartslist', {'value': row['haspartslist']})
    if 'wishlistcomment' in row.keys():
        update_collid(opener, collid, 'wishlistcomment', {'value': row['wishlistcomment']})
Пример #24
0
    def update_simple(self, game_attrs):
        """
        Added by bbever (07/27/15)
        Update game details using simple mode.
        Wont update details, simply adds item to collection.

        :param game_attrs: Game attributes as a dictionary
        """
        self.goto(game_attrs)

        try:
            self.itemEl = self.driver.find_element_by_xpath(
                "//table[@class='collectionmodule_table']")
            Logger.info(" (already in collection)", append=True, break_line=False)
        except NoSuchElementException:
            self.driver.get("javascript:void(CE_ModuleAddItem({objecttype:'thing',objectid:'%s',addowned:'true',instanceid:'21'}))" % (game_attrs['objectid']))
Пример #25
0
def execute_command(command, argv):
    timer_start = time.time()

    try:
        command_module = import_command_module(command)
        command_args, command_args_options = parse_commad_args(command_module, argv)

        if command_args:
            command_module.execute(command_args, command_args_options)
            show_duration(timer_start)
    except ImportError:
        exit_unknown_command(command)
    except WebDriverException as e:
        Logger.error(UI_ERROR_MSG, e)
    except Exception as e:
        Logger.error("Encountered an unexpected error, please report the issue to the author", e)
Пример #26
0
def create_collid(opener, objectid):
    response = opener.open(BGG_BASE_URL + '/geekcollection.php', urlencode({
        'ajax': 1, 'action': 'additem', 'force': 'true',
        'objecttype': 'thing', 'objectid': objectid}))

    if response.code != 200:
        Logger.error("Failed to create item of 'objectid'=%s!" % objectid, sysexit=True)

    # There seems to be no straightforward way to get the collid of the item just created.
    # To work around this we fetch a list of all items of this objectid and scrape it to
    # find the largest collid. This might fail if the collection is concurrently modified.

    response = opener.open(BGG_BASE_URL + '/geekcollection.php?' + urlencode({
        'ajax': 1, 'action': 'module', 'objecttype': 'thing', 'objectid': objectid}))

    return max(int(m.group(1)) for m in re.finditer(
        r"(?i)<input\s+type='hidden'\s+name='collid'\s+value='(\d+)'[^>]*>", response.read()))
Пример #27
0
def parse_commad_args(command_module, argv):
    #print('dir',dir(command_module))
    #print(command_module.__doc__,argv,VERSION)
    #try:
    result = docopt(command_module.__doc__,
                    argv,
                    version='bggcli %s' % VERSION,
                    options_first=False)
    # except DocoptExit:
    # traceback.print_exc()
    # #print('hi!')
    # raise
    #print('result=',result)
    try:
        return result, explode_dyn_args(result['-c'])
    except StandardError:
        Logger.info('Invalid syntax for -c option, should be "-c key=value"!')
        return None
Пример #28
0
    def delete(self, game_attrs):
        """
        Delete a game in the collection

        :param game_attrs: Game attributes as a dictionary. Only the id will be used
        """
        self.goto(game_attrs)
        try:
            del_button = self.driver.find_element_by_xpath("//a[contains(@onclick, "
                                                           "'CE_ModuleDeleteItem')]")
        except NoSuchElementException:
            Logger.info(" (not in collection)", append=True, break_line=False)
            return

        del_button.click()

        # Confirm alert message
        self.wait_and_accept_alert()
Пример #29
0
    def delete(self, game_attrs):
        """
        Delete a game in the collection

        :param game_attrs: Game attributes as a dictionary. Only the id will be used
        """
        self.goto(game_attrs)
        try:
            del_button = self.driver.find_element_by_xpath(
                "//a[contains(@onclick, "
                "'CE_ModuleDeleteItem')]")
        except NoSuchElementException:
            Logger.info(" (not in collection)", append=True, break_line=False)
            return

        del_button.click()

        # Confirm alert message
        self.wait_and_accept_alert()
Пример #30
0
def execute_command(command, argv):
    timer_start = time.time()

    try:
        command_module = import_command_module(command)
##        command_args, command_args_options = parse_commad_args(command_module, argv)
        command_args = parse_commad_args(command_module, argv)

        if command_args:
##            command_module.execute(command_args, command_args_options)
            command_module.execute(command_args)
            show_duration(timer_start)
    except ImportError:
        exit_unknown_command(command)
##    except WebDriverException as e:
    except URLError as e: 
        Logger.error(UI_ERROR_MSG, e)
    except Exception as e:
        Logger.error("Encountered an unexpected error, please report the issue to the author", e)
Пример #31
0
    def update(self, game_attrs):
        """
        Update game details

        :param game_attrs: Game attributes as a dictionary
        """
        self.goto(game_attrs)

        try:
            self.itemEl = self.driver.find_element_by_xpath(
                "//table[@class='collectionmodule_table']")
            Logger.info(" (already in collection)", append=True, break_line=False)
        except NoSuchElementException:
            self.driver.find_element_by_xpath(
                "(//a[contains(@onclick, 'CE_ModuleAddItem')])[last()]").click()
            self.itemEl = self.wait.until(EC.element_to_be_clickable(
                (By.XPATH, "//table[@class='collectionmodule_table']")))

        # Fill all provided values using dynamic invocations 'fill_[fieldname]'
        for key in game_attrs:
            if key in BGG_SUPPORTED_FIELDS:
                value = game_attrs[key]
                if value is not None:
                    getattr(self, "fill_%s" % key)(value)

        # Save "Private Info" popup if opened
        try:
            self.privateInfoPopupEl.find_element_by_xpath(".//input[@type='submit']").click()
        except WebDriverException:
            pass
        else:
            self.wait.until(EC.element_to_be_clickable(
                (By.XPATH, ".//td[@class='collection_ownershipmod editfield']")))

        # Save "Version" popup if opened
        try:
            self.versionPopupEl.find_element_by_xpath(".//input[@type='submit']").click()
        except WebDriverException:
            pass
        else:
            self.wait.until(EC.element_to_be_clickable(
                (By.XPATH, ".//td[@class='collection_versionmod editfield']")))
Пример #32
0
def default_export(req):
    response = urllib2.urlopen(req)

    if response.code == 202:
        Logger.info('Export is queued, will retry in %ss' % EXPORT_QUERY_INTERVAL)
        time.sleep(EXPORT_QUERY_INTERVAL)
        return default_export(req)

    if response.code == 200:
        return response

    # Write response in a text file otherwise
    try:
        with open(ERROR_FILE_PATH, "wb") as error_file:
            error_file.write(response.read())
        Logger.error("Unexpected response, content has been written in %s" % ERROR_FILE_PATH)
    except Exception as e:
        raise Exception('Unexpected HTTP response for export request, and cannot write '
                        'response content in %s: %s' % (ERROR_FILE_PATH, e))
    raise Exception('Unexpected HTTP response for export request, response content written in '
                    '%s' % ERROR_FILE_PATH)
Пример #33
0
def execute(args, options):
    login = args['--login']

    file_path = check_file(args)

    csv_reader = CsvReader(file_path)
    csv_reader.open()

    Logger.info("Importing games for '%s' account..." % login)

    with WebDriver('collection-import', args, options) as web_driver:
        if not LoginPage(web_driver.driver).authenticate(login, args['--password']):
            sys.exit(1)

        Logger.info("Importing %s games..." % csv_reader.rowCount)
        game_page = GamePage(web_driver.driver)
        csv_reader.iterate(lambda row: game_page.update(row))
        Logger.info("Import has finished.")
Пример #34
0
def execute(args, options):
    login = args['--login']

    file_path = check_file(args)

    csv_reader = CsvReader(file_path)
    csv_reader.open()

    Logger.info("Importing games for '%s' account..." % login)

    with WebDriver('collection-import', args, options) as web_driver:
        if not LoginPage(web_driver.driver).authenticate(
                login, args['--password']):
            sys.exit(1)

        Logger.info("Importing %s games..." % csv_reader.rowCount)
        game_page = GamePage(web_driver.driver)
        csv_reader.iterate(lambda row: game_page.update(row))
        Logger.info("Import has finished.")
Пример #35
0
def exit_error(msg, args):
    Logger.error(msg)
    Logger.error(docopt(__doc__, args))
    exit(1)
Пример #36
0
def execute(args, options):
    print('Executing!')
    login = args['--login']

    file_path = check_file(args)

    csv_reader = CsvReader(file_path)
    csv_reader.open()
    rows = []
    #    try:
    Logger.info("Parsing input file '{}'...".format(file_path))
    csv_reader.iterate(lambda row: rows.append(row))
    #Logger.info("Found %s games to put in collection..." % csv_reader.rowCount)
    rows.reverse()
    firstrow = rows[0]
    loop = 0

    Logger.info("Importing {} games to collection of '{}' ...".format(
        csv_reader.rowCount, login))
    while rows:
        try:
            with WebDriver('collection-import', args, options) as web_driver:
                if not LoginPage(web_driver.driver).authenticate(
                        login, args['--password']):
                    sys.exit(1)
                #input("Kill Firefox, then Press Enter to continue...")
                game_page = GamePage(web_driver.driver)
                while rows:
                    row = rows.pop()
                    if firstrow is None or firstrow == row:
                        loop += 1
                        if loop >= LOOPLIMIT:
                            Logger.info(
                                "Loop limit of {} reached.".format(loop))
                            return
                        Logger.info('Loop {} (maximum {})'.format(
                            loop, LOOPLIMIT))
                        if rows:
                            firstrow = rows[0]
                            Logger.info('First assigned {}'.format(
                                firstrow['objectname']))
                        else:
                            firstrow = None
                            Logger.info('First assigned None')

                    Logger.info('(BGGID {}) Name: {} ({} game left)'.format(
                        row['objectid'], row['objectname'],
                        len(rows) + 1))

                    try:
                        val = game_page.update(row)
                        Logger.info('update returned {}'.format(val))

                        if val:
                            #Logger.info('Updated (BGGID {0}) "{1}"'.format(row['objectid'],row['objectname']))
                            Logger.info('(BGGID {}) Name: {} UPDATED!'.format(
                                row['objectid'], row['objectname'], len(rows)))
                            #  ({} game left)
                        else:
                            rows.insert(0, row)
                            Logger.info(
                                'returned False??, back in queue.'.format(
                                    len(rows)))  #  ({} game left)

                    except WebDriverException:
                        rows.insert(0, row)
                        Logger.info(
                            'Exception occurred, back in queue.'.format(
                                len(rows)))  # ({} left)
                        Logger.info(
                            'WebDriverException occurred, restarting browser.')
                        raise

                    except Exception as e:
                        traceback.print_exc(limit=2, file=sys.stdout)

                        rows.insert(0, row)
                        Logger.info(
                            'Exception occurred, back in queue.'.format(
                                len(rows)))  #  ({} left)

                        #badrows.append(row)
                # for row in rows:
                # try:
                # game_page.update(row)
                # except:
                # badrows.append(row)
                # print
        except WebDriverException:
            pass
    Logger.info("Import has finished.")
Пример #37
0
def execute(args):
    login = args['--login']

    file_path = check_file(args)

    csv_reader = CsvReader(file_path)
    csv_reader.open()

    game_count = csv_reader.rowCount

    if not args['--force']:
        sys.stdout.write(
            "You are about to delete %s games in you collection (%s), "
            "please enter the number of games displayed here to confirm you want to continue: "
            % (game_count, login))

        if raw_input() != game_count.__str__():
            Logger.error(
                'Operation canceled, number does not match (should be %s).' %
                game_count,
                sysexit=True)
            return

    Logger.info("Deleting games for '%s' account..." % login)

    cj = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    Logger.info("Authenticating...", break_line=False)
    opener.open(
        BGG_BASE_URL + '/login',
        urlencode({
            'action': 'login',
            'username': login,
            'password': args['--password']
        }))
    if not any(cookie.name == "bggusername" for cookie in cj):
        Logger.info(" [error]", append=True)
        Logger.error("Authentication failed for user '%s'!" % login,
                     sysexit=True)
    Logger.info(" [done]", append=True)

    Logger.info("Deleting %s games..." % game_count)
    csv_reader.iterate(lambda row: game_deleter(opener, row))
    Logger.info("Deletion has finished.")
Пример #38
0
    def updateid(self, game_attrs):
        """
        Update game details

        :param game_attrs: Game attributes as a dictionary
        """
        # General use of Selenium is
        #   A) goto page,
        #   B) find element using xpath expression, then
        #   C) take action related to the element.
        #
        # Flow of this function is:
        #    1) Go to game page
        #    2) Try to click "Add to Collection" button.
        #    3) If it didn't exist,
        #       a) click the "In Collection" button.
        #       b) click the "Edit" button
        #    4) Open the additional two form dialogs
        #       (Show Advanced, Show Custom)
        #    5) fill out items on form, dependencies show which values must
        #       exist before the indicated item.
        #       For example, 'wishlistpriority':{'wishlist':1},
        #       means that to set wishlistpriority, then
        #       wishlist must equal 1.
        #    6) Finally, find the form element and submit it.

        #Logger.info("updateid()", append=True, break_line=True)
        #Logger.info("{} {}, ".format(game_attrs.get('objectid',''),game_attrs.get('objectname','')), append=True, break_line=True)

        self.goto(game_attrs)

        Logger.info("page, ", append=False, break_line=False)

        self.openeditform()

        # Open advanced data entry panel.
        #<a class="toggler-caret" ng-href="" ng-click="editctrl.showvars.showAdvanced = !editctrl.showvars.showAdvanced" ng-class="{ 'is-active': editctrl.showvars.showAdvanced }"> 			<span class="glyphicon glyphicon-menu-right"></span> 			<strong>Advanced</strong> (private info, parts exchange) 		</a>
        Logger.info("Advanced button...", append=True, break_line=False)
        self.wait.until(
            EC.element_to_be_clickable((
                By.XPATH,
                "//a[starts-with(@class,'toggler-caret') and starts-with(@ng-click,'editctrl.showvars.showAdvanced')]"
            )))
        try:
            Logger.info("finding advanced dropdown...",
                        append=True,
                        break_line=False)
            b = self.itemEl.find_element_by_xpath(
                ".//a[@class='toggler-caret' and starts-with(@ng-click,'editctrl.showvars.showAdvanced')]"
            )
            Logger.info("Click. ", append=True, break_line=False)
            b.click()
        except:
            Logger.info("Failed.", append=True, break_line=False)
            pass
        # Open Customize Game Info data entry panel.
        #<a class="toggler-caret" ng-href="" ng-click="editctrl.showvars.showCustom = !editctrl.showvars.showCustom" ng-class="{ 'is-active': editctrl.showvars.showCustom }"> 				<span class="glyphicon glyphicon-menu-right"></span> 				<strong>Customize Game Info</strong> (title, image) 			</a>
        Logger.info("Custom button...", append=True, break_line=False)
        self.wait.until(
            EC.element_to_be_clickable((
                By.XPATH,
                "//a[starts-with(@class,'toggler-caret') and starts-with(@ng-click,'editctrl.showvars.showCustom')]"
            )))
        try:
            self.itemEl.find_element_by_xpath(
                ".//a[@class='toggler-caret' and starts-with(@ng-click,'editctrl.showvars.showCustom')]"
            ).click()
        except:
            Logger.info("Failed. ", append=True, break_line=False)

        # custom = self.wait.until(EC.element_to_be_clickable(
        # (By.XPATH, ".//a[starts-with(@ng-click,'editctrl.showvars.showCustom')]")))
        # custom.click()

        #<input ng-model="editctrl.editdata.item.textfield.customname.value" class="form-control ng-pristine ng-valid ng-empty ng-touched" type="text" placeholder="Gloomhaven Nickname" style="">
        Logger.info("Name field...", append=True, break_line=False)
        nickname = self.wait.until(
            EC.element_to_be_clickable((
                By.XPATH,
                './/input[@ng-model="editctrl.editdata.item.textfield.customname.value"]'
            )))

        # self.itemEl = self.wait.until(EC.element_to_be_clickable(
        # (By.XPATH, "//form[@name='collectioneditorform']")))
        # Fill all provided values using dynamic invocations 'fill_[fieldname]'
        dependencies = {
            'wishlistpriority': {
                'wishlist': 1
            },
            'wishlistcomment': {
                'wishlist': 1
            },
            'conditiontext': {
                'fortrade': 1
            }
        }

        # 'fortrade', 'conditiontext',   # these must be in this order
        # 'wishlist', 'wishlistpriority', 'wishlistcomment', # these must be in this order

        Logger.info("Updating fields: ", append=True, break_line=False)
        try:
            for key in BGG_SUPPORTED_FIELDS:
                dontdo = 0
                if key in game_attrs:
                    value = game_attrs[key]
                    if value is not None:
                        #print '{}'.format(key),
                        #time.sleep(2)
                        dep = dependencies.get(key, None)
                        if dep:
                            #print 'depends on: ',
                            for k, v in dep.items(
                            ):  # for python 2: iteritems()
                                #print '{}={}?'.format(k,v),
                                if str(game_attrs[k]) != str(v):
                                    dontdo = 1
                        if dontdo:
                            continue
                        getattr(self, "fill_%s" % key)(value)
        except:
            Logger.info("\nEXCEPTION.", append=True, break_line=True)
            traceback.print_exc()
            return False
        # <button class="visible-xs-inline btn btn-primary" ng-disabled="editctrl.saving" type="submit"> 				<span>Save</span> 			</button>
        #savebutton = self.itemEl.find_element_by_xpath(".//button[@ng-disabled='editctrl.saving']")
        Logger.info("Form? ", append=True, break_line=False)
        form = self.itemEl.find_element_by_xpath(
            ".//form[@name='collectioneditorform']")
        # action=selenium.interactions.Actions(driver);
        # import selenium.webdriver.common.actions.pointer_actions
        # selenium.webdriver.common.actions.pointer_actions().click(savebutton)
        #Logger.info("submitting, ", append=True, break_line=False)
        form.submit()
        Logger.info("submitted. ", append=True, break_line=False)
        time.sleep(0.1)
        # action.moveToElement(savebutton).click().perform();
        return True
        # Save "Private Info" popup if opened
        try:
            self.privateInfoPopupEl.find_element_by_xpath(
                ".//input[@type='submit']").click()
        except WebDriverException:
            pass
        else:
            self.wait.until(
                EC.element_to_be_clickable(
                    (By.XPATH,
                     ".//td[@class='collection_ownershipmod editfield']")))

        # Save "Version" popup if opened
        try:
            self.versionPopupEl.find_element_by_xpath(
                ".//input[@type='submit']").click()
        except WebDriverException:
            pass
        else:
            self.wait.until(
                EC.element_to_be_clickable(
                    (By.XPATH,
                     ".//td[@class='collection_versionmod editfield']")))

        return True
Пример #39
0
def exit_error(msg, args):
    Logger.error(msg)
    Logger.error(docopt(__doc__, args))
    exit(1)
Пример #40
0
    def iterate(self, callback):
        try:
            index = 1
            for row in self.reader:
                objectid = row.get('objectid')
                if objectid is None or not objectid.isdigit():
                    Logger.error("No valid 'objectid' at line %s!" % index,
                                 None,
                                 sysexit=True)
                    return

                # Encode in UTF-8
                for key in row:
                    value = row[key]
                    if value is not None:
                        row[key] = unicode(value, 'utf-8')

                objectname = row['objectname']
                if objectname is None or objectname == "":
                    objectname = "(name not available for objectid=%s)" % objectid

                Logger.info("[%s/%s] %s... " %
                            (index, self.rowCount, objectname),
                            break_line=False)
                try:
                    callback(row)
                except WebDriverException as e:
                    Logger.error(UI_ERROR_MSG, e, sysexit=True)
                    return
                except Exception as e:
                    Logger.info("", append=True)
                    Logger.error("Unexpected error while processing row %s" %
                                 index,
                                 e,
                                 sysexit=True)
                    return
                Logger.info(" [done]", append=True)
                index += 1
        except csv.Error as e:
            Logger.error('Error while reading file %s at line %d: %s' %
                         (file, self.reader.line_num, e),
                         sysexit=True)
Пример #41
0
    def openeditform(self):
        button = self.notincollection()
        if button:
            button.click()
        else:
            Logger.info(" not found. ", append=True, break_line=False)
            Logger.info("(i.e. game in col'n)...",
                        append=True,
                        break_line=False)
            # div = self.driver.find_element_by_xpath(
            # "(//div[@class, 'toolbar-actions'])[last()]")

            Logger.info("'In Col'n' button? ", append=True, break_line=False)
            button = self.driver.find_element_by_xpath(
                '('
                '//'
                'button[@id="button-collection" and descendant::'
                'span[starts-with(@ng-show,"colltoolbarctrl.collection.items.length") and '
                'contains(text(),"In Collection")]'
                ']'
                ')[last()]')
            Logger.info("Click. ", append=True, break_line=False)
            button.click()

            clickable = self.driver.find_element_by_xpath(
                '//span[@class="collection-dropdown-item-edit" and //button[contains(text(),"Edit")]]'
            )
            Logger.info("Click col'n dropdown. ",
                        append=True,
                        break_line=False)
            clickable.click()

        Logger.info("form...", append=True, break_line=False)

        self.itemEl = self.wait.until(
            EC.element_to_be_clickable(
                (By.XPATH, "//div[@class='modal-content']")))
Пример #42
0
    if 'wantpartslist' in row.keys():
        update_collid(opener, collid, 'wantpartslist', {'value': row['wantpartslist']})
    if 'haspartslist' in row.keys():
        update_collid(opener, collid, 'haspartslist', {'value': row['haspartslist']})
    if 'wishlistcomment' in row.keys():
        update_collid(opener, collid, 'wishlistcomment', {'value': row['wishlistcomment']})
 
 
 def execute(args):    login = args['--login']

    file_path = check_file(args)

    csv_reader = CsvReader(file_path)
    csv_reader.open()

    Logger.info("Importing games for '%s' account..." % login)

    cj = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    Logger.info("Authenticating...", break_line=False)
    opener.open(BGG_BASE_URL + '/login', urlencode({
        'action': 'login', 'username': login, 'password': args['--password']}))
    if not any(cookie.name == "bggusername" for cookie in cj):
        Logger.info(" [error]", append=True)
        Logger.error("Authentication failed for user '%s'!" % login, sysexit=True)
    Logger.info(" [done]", append=True)
 
    Logger.info("Importing %s games..." % csv_reader.rowCount)
    csv_reader.iterate(lambda row: game_importer(opener, row, args['--force-new']))
    Logger.info("Import has finished.")
Пример #43
0
def execute(args, options):
    login = args['--login']
    dest_path = args['<file>']

    Logger.info("Exporting collection for '%s' account..." % login)

    # 1. Authentication
    with WebDriver('collection-export', args, options) as web_driver:
        if not LoginPage(web_driver.driver).authenticate(
                login, args['--password']):
            sys.exit(1)
        auth_cookie = web_driver.driver.get_cookie(
            BGG_SESSION_COOKIE_NAME)['value']

    # 2. Export
    # Easier to rely on a client HTTP call rather than Selenium to download a file
    # Just need to pass the session cookie to get the full export with private information

    # Use XML2 API, see https://www.boardgamegeek.com/wiki/page/BGG_XML_API2#Collection
    # Default CSV export doesn't provide version info!
    url = '%s/xmlapi2/collection?username=%s&version=1&showprivate=1&stats=1' \
          % (BGG_BASE_URL, login)
    req = urllib2.Request(
        url, None,
        {'Cookie': '%s=%s' % (BGG_SESSION_COOKIE_NAME, auth_cookie)})

    # Get a BadStatusLine error most of times without this delay!
    # Related to Selenium, but in some conditions that I have not identified
    time.sleep(8)
    try:
        Logger.info('Launching export...')
        response = default_export(req)
    except Exception as e:
        Logger.error('Error while fetching export file!', e, sysexit=True)
        return

    # 3. Store XML file if requested
    xml_file = options.get('save-xml-file')
    if xml_file == 'true':
        xml_file_path = write_xml_file(response, dest_path)
        Logger.info("XML file save as %s" % xml_file_path)
        source = open(xml_file_path, 'rU')
    else:
        source = response

    # 4. Write CSV file
    try:
        write_csv(source, dest_path)
    except Exception as e:
        Logger.error('Error while writing export file in file system!',
                     e,
                     sysexit=True)
        return
    finally:
        source.close()

    # End
    Logger.info("Collection has been exported as %s" % dest_path)
Пример #44
0
    def iterate(self, callback):
        try:
            index = 1
            for row in self.reader:
                objectid = row.get('objectid')
                if objectid is None or not objectid.isdigit():
                    Logger.error("No valid 'objectid' at line %s!" % index, None, sysexit=True)
                    return

                # Encode in UTF-8
                for key in row:
                    value = row[key]
                    if value is not None:
                        row[key] = unicode(value, 'utf-8')

                objectname = row['objectname']
                if objectname is None or objectname == "":
                    objectname = "(name not available for objectid=%s)" % objectid

                Logger.info("[%s/%s] %s... " % (index, self.rowCount, objectname),
                            break_line=False)
                try:
                    callback(row)
                except WebDriverException as e:
                    Logger.error(UI_ERROR_MSG, e, sysexit=True)
                    return
                except Exception as e:
                    Logger.info("", append=True)
                    Logger.error("Unexpected error while processing row %s" % index, e,
                                 sysexit=True)
                    return
                Logger.info(" [done]", append=True)
                index += 1
        except csv.Error as e:
            Logger.error('Error while reading file %s at line %d: %s'
                         % (file, self.reader.line_num, e), sysexit=True)
Пример #45
0
def execute(args):
    login = args['--login']
    dest_path = args['<file>']

    Logger.info("Exporting collection for '%s' account..." % login)

    # 1. Authentication

    cj = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    Logger.info("Authenticating...", break_line=False)
    opener.open(BGG_BASE_URL + '/login', urlencode({
        'action': 'login', 'username': login, 'password': args['--password']}))
    if not any(cookie.name == "bggusername" for cookie in cj):
        Logger.info(" [error]", append=True)
        Logger.error("Authentication failed for user '%s'!" % login, sysexit=True)
    Logger.info(" [done]", append=True)
    # 2. Export
    # Easier to rely on a client HTTP call rather than Selenium to download a file
    # Just need to pass the session cookie to get the full export with private information

    # Use XML2 API, see https://www.boardgamegeek.com/wiki/page/BGG_XML_API2#Collection
    # Default CSV export doesn't provide version info!

    url = BGG_BASE_URL + '/xmlapi2/collection?' + urlencode({
            'username': login, 'version': 1, 'showprivate': 1, 'stats': 1})
    req = urllib2.Request(url)
 
    # Get a BadStatusLine error most of times without this delay!
    # Related to Selenium, but in some conditions that I have not identified
    time.sleep(8)
    try:
        Logger.info('Launching export...')
        response = default_export(opener, req)
    except Exception as e:
        Logger.error('Error while fetching export file!', e, sysexit=True)
        return

# 3. Store XML file if requested

    if args['--save-xml-file']:
        xml_file_path = write_xml_file(response, dest_path)
        Logger.info("XML file save as %s" % xml_file_path)
        source = open(xml_file_path, 'rU')
        
    else:
        source = response

# 4. Write CSV file
    try:
        write_csv(source, dest_path)
    except Exception as e:
        Logger.error('Error while writing export file in file system!', e, sysexit=True)
        return
    finally:
        source.close()
    
    # End
    Logger.info("Collection has been exported as %s" % dest_path)
Пример #46
0
def execute(args, options):
    login = args['--login']
    dest_path = args['<file>']

    Logger.info("Exporting collection for '%s' account..." % login)

    # 1. Authentication
    with WebDriver('collection-export', args, options) as web_driver:
        if not LoginPage(web_driver.driver).authenticate(login, args['--password']):
            sys.exit(1)
        auth_cookie = web_driver.driver.get_cookie(BGG_SESSION_COOKIE_NAME)['value']

    # 2. Export
    # Easier to rely on a client HTTP call rather than Selenium to download a file
    # Just need to pass the session cookie to get the full export with private information

    # Use XML2 API, see https://www.boardgamegeek.com/wiki/page/BGG_XML_API2#Collection
    # Default CSV export doesn't provide version info!
    url = '%s/xmlapi2/collection?username=%s&version=1&showprivate=1&stats=1' \
          % (BGG_BASE_URL, login)
    req = urllib2.Request(url, None, {'Cookie': '%s=%s' % (BGG_SESSION_COOKIE_NAME, auth_cookie)})

    # Get a BadStatusLine error most of times without this delay!
    # Related to Selenium, but in some conditions that I have not identified
    time.sleep(8)
    try:
        Logger.info('Launching export...')
        response = default_export(req)
    except Exception as e:
        Logger.error('Error while fetching export file!', e, sysexit=True)
        return

    # 3. Store XML file if requested
    xml_file = options.get('save-xml-file')
    if xml_file == 'true':
        xml_file_path = write_xml_file(response, dest_path)
        Logger.info("XML file save as %s" % xml_file_path)
        source = open(xml_file_path, 'rU')
    else:
        source = response

    # 4. Write CSV file
    try:
        write_csv(source, dest_path)
    except Exception as e:
        Logger.error('Error while writing export file in file system!', e, sysexit=True)
        return
    finally:
        source.close()

    # End
    Logger.info("Collection has been exported as %s" % dest_path)