def get_list(self): for tr in self.document.xpath('//table[@class="ca-table"]/tr'): if not tr.attrib.get('class', '').startswith('colcelligne'): continue cols = tr.findall('td') if not cols: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]) account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.type = self.TYPES.get(account.label, Account.TYPE_UNKNOWN) balance = self.parser.tocleanstring(cols[self.COL_VALUE]) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if balance in ('indisponible', ''): continue account.balance = Decimal(Transaction.clean_amount(balance)) account.currency = account.get_currency(self.parser.tocleanstring(cols[self.COL_CURRENCY])) account._link = None a = cols[0].find('a') if a is not None: account._link = a.attrib['href'].replace(' ', '%20') yield account
def get_list(self): accounts = [] previous_account = None noaccounts = self.get_from_js('_js_noMvts =', ';') if noaccounts is not None: assert 'avez aucun compte' in noaccounts return [] txt = self.get_from_js('_data = new Array(', ');', is_list=True) if txt is None: raise BrowserUnavailable('Unable to find accounts list in scripts') data = json.loads('[%s]' % txt.replace("'", '"')) for line in data: a = Account() a.id = line[self.COL_ID].replace(' ', '') if re.match(r'Classement=(.*?):::Banque=(.*?):::Agence=(.*?):::SScompte=(.*?):::Serie=(.*)', a.id): a.id = str(CleanDecimal().filter(a.id)) a._acc_nb = a.id.split('_')[0] if len(a.id.split('_')) > 1 else None a.label = MyStrip(line[self.COL_LABEL], xpath='.//div[@class="libelleCompteTDB"]') # This account can be multiple life insurance accounts if a.label == 'ASSURANCE VIE-BON CAPI-SCPI-DIVERS *': continue a.balance = Decimal(FrenchTransaction.clean_amount(line[self.COL_BALANCE])) a.currency = a.get_currency(line[self.COL_BALANCE]) a.type = self.get_account_type(a.label) # The parent account must be created right before if a.type == Account.TYPE_CARD: # duplicate if find_object(accounts, id=a.id): self.logger.warning('Ignoring duplicate card %r', a.id) continue a.parent = previous_account if line[self.COL_HISTORY] == 'true': a._inv = False a._link = self.get_history_link() a._args = self.make__args_dict(line) else: a._inv = True a._args = {'_ipc_eventValue': line[self.COL_ID], '_ipc_fireEvent': line[self.COL_FIRE_EVENT], } a._link = self.doc.xpath('//form[@name="changePageForm"]')[0].attrib['action'] if a.type is Account.TYPE_CARD: a.coming = a.balance a.balance = Decimal('0.0') accounts.append(a) previous_account = a return accounts
def get_list(self): for tr in self.document.xpath('//table[@class="ca-table"]/tr'): if not tr.attrib.get('class', '').startswith('colcelligne'): continue cols = tr.findall('td') if not cols: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]) account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.type = self.TYPES.get(account.label, Account.TYPE_UNKNOWN) balance = self.parser.tocleanstring(cols[self.COL_VALUE]) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if balance in ('indisponible', ''): continue account.balance = Decimal(Transaction.clean_amount(balance)) account.currency = account.get_currency( self.parser.tocleanstring(cols[self.COL_CURRENCY])) account._link = None a = cols[0].find('a') if a is not None: account._link = a.attrib['href'].replace(' ', '%20') yield account
def get_list(self): for div in self.document.xpath('.//div[@id="card-details"]'): a = Account() a.id = self.parser.tocleanstring( div.xpath('.//span[@class="acc-num"]')[0]) a.label = self.parser.tocleanstring( div.xpath('.//span[@class="card-desc"]')[0]) if "carte" in a.label.lower(): a.type = Account.TYPE_CARD balance = self.parser.tocleanstring( div.xpath('.//span[@class="balance-data"]')[0]) if balance in (u'Indisponible', u'Indisponible Facturation en cours', ''): a.balance = NotAvailable else: a.currency = a.get_currency(balance) a.balance = -abs( CleanDecimal( replace_dots=a.currency == 'EUR').filter(balance)) # Cancel card don't have a link to watch history link = self.document.xpath('.//div[@class="wide-bar"]/h3/a') if len(link) == 1: a._link = link[0].attrib['href'] else: a._link = None yield a
def get_list(self): account_type = Account.TYPE_UNKNOWN accounts = [] for tr in self.document.xpath('//table[@class="ecli"]/tr'): if tr.attrib.get('class', '') == 'entete': account_type = self.ACCOUNT_TYPES.get(tr.find('th').text.strip(), Account.TYPE_UNKNOWN) continue tds = tr.findall('td') balance = tds[-1].text.strip() if balance == '': continue account = Account() account.label = u' '.join([txt.strip() for txt in tds[0].itertext()]) account.label = re.sub(u'[ \xa0\u2022\r\n\t]+', u' ', account.label).strip() account.id = re.findall('(\d+)', account.label)[0] account.balance = Decimal(FrenchTransaction.clean_amount(balance)) account.currency = account.get_currency(balance) account.type = account_type m = re.search(r"javascript:submitForm\(([\w_]+),'([^']+)'\);", tds[0].find('a').attrib['onclick']) if not m: self.logger.warning('Unable to find link for %r' % account.label) account._link = None else: account._link = m.group(2) accounts.append(account) return accounts
def get_list(self): accounts = [] txt = self.get_from_js('_data = new Array(', ');', is_list=True) if txt is None: raise BrokenPageError('Unable to find accounts list in scripts') data = json.loads('[%s]' % txt.replace("'", '"')) for line in data: a = Account() a.id = line[self.COL_ID].replace(' ', '') a._acc_nb = a.id.split('_')[0] if len( a.id.split('_')) > 1 else None fp = StringIO( unicode(line[self.COL_LABEL]).encode(self.browser.ENCODING)) a.label = self.parser.tocleanstring( self.parser.parse(fp, self.browser.ENCODING).xpath( '//div[@class="libelleCompteTDB"]')[0]) # This account can be multiple life insurance accounts if a.label == 'ASSURANCE VIE-BON CAPI-SCPI-DIVERS *': continue a.balance = Decimal( FrenchTransaction.clean_amount(line[self.COL_BALANCE])) a.currency = a.get_currency(line[self.COL_BALANCE]) a.type = self.get_account_type(a.label) if line[self.COL_HISTORY] == 'true': a._inv = False a._link = self.get_history_link() a._args = { '_eventId': 'clicDetailCompte', '_ipc_eventValue': '', '_ipc_fireEvent': '', 'deviseAffichee': 'DEVISE', 'execution': self.get_execution(), 'idCompteClique': line[self.COL_ID], } else: a._inv = True a._args = { '_ipc_eventValue': line[self.COL_ID], '_ipc_fireEvent': line[self.COL_FIRE_EVENT], } a._link = self.document.xpath( '//form[@name="changePageForm"]')[0].attrib['action'] if a.id.find('_CarteVisa') >= 0: accounts[-1]._card_ids.append(a._args) if not accounts[-1].coming: accounts[-1].coming = Decimal('0.0') accounts[-1].coming += a.balance continue a._card_ids = [] accounts.append(a) return accounts
def get_list(self): TABLE_XPATH = '//table[caption[@class="caption tdb-cartes-caption" or @class="ca-table caption"]]' cards_tables = self.document.xpath(TABLE_XPATH) currency = self.document.xpath('//table/caption//span/text()[starts-with(.,"Montants en ")]')[0].replace("Montants en ", "") or None if cards_tables: self.logger.debug('There are several cards') xpaths = { '_id': './caption/span[@class="tdb-cartes-num"]', 'label1': './caption/span[contains(@class, "tdb-cartes-carte")]', 'label2': './caption/span[@class="tdb-cartes-prop"]', 'balance': './/tr/td[@class="cel-num"]', 'currency': '//table/caption//span/text()[starts-with(.,"Montants en ")]', 'link': './/tr//a/@href[contains(., "fwkaction=Detail")]', } else: self.logger.debug('There is only one card') xpaths = { '_id': './/tr/td[@class="cel-texte"]', 'label1': './/tr[@class="ligne-impaire ligne-bleu"]/th', 'label2': './caption/span[@class="tdb-cartes-prop"]/b', 'balance': './/tr[last()-1]/td[@class="cel-num"] | .//tr[last()-2]/td[@class="cel-num"]', 'currency': '//table/caption//span/text()[starts-with(.,"Montants en ")]', } TABLE_XPATH = '(//table[@class="ca-table"])[1]' cards_tables = self.document.xpath(TABLE_XPATH) for table in cards_tables: get = lambda name: self.parser.tocleanstring(table.xpath(xpaths[name])[0]) account = Account() account.type = account.TYPE_CARD account.id = ''.join(get('_id').split()[1:]) account._id = ' '.join(get('_id').split()[1:]) account.label = '%s - %s' % (get('label1'), re.sub('\s*-\s*$', '', get('label2'))) try: account.balance = Decimal(Transaction.clean_amount(table.xpath(xpaths['balance'])[-1].text)) account.currency = account.get_currency(self.document .xpath(xpaths['currency'])[0].replace("Montants en ", "")) if not account.currency and currency: account.currency = Account.get_currency(currency) except IndexError: account.balance = Decimal('0.0') if 'link' in xpaths: try: account._link = table.xpath(xpaths['link'])[-1] except IndexError: account._link = None else: account._link = re.sub('[\n\r\t]+', '', account._link) else: account._link = self.url account._perimeter = self.browser.current_perimeter yield account
def get_list(self): account_type = Account.TYPE_UNKNOWN accounts = [] for tr in self.doc.xpath('//div[@class="finance"]/form/table[@class="ecli"]/tr'): if tr.attrib.get('class', '') == 'entete': account_type = self.ACCOUNT_TYPES.get(tr.find('th').text.strip(), Account.TYPE_UNKNOWN) continue tds = tr.findall('td') a = tds[0].find('a') # Skip accounts that can't be accessed if a is None: continue balance = tds[-1].text.strip() account = Account() account.label = u' '.join([txt.strip() for txt in tds[0].itertext()]) account.label = re.sub(u'[ \xa0\u2022\r\n\t]+', u' ', account.label).strip() # take "N° (FOO123 456)" but "N° (FOO123) MR. BAR" account.id = re.search(r'N° (\w+( \d+)*)', account.label).group(1).replace(' ', '') account.type = account_type for patt, type in self.ACCOUNT_TYPES2.items(): if patt in account.label.lower(): account.type = type break if balance: account.balance = Decimal(FrenchTransaction.clean_amount(balance)) account.currency = account.get_currency(balance) if 'onclick' in a.attrib: m = re.search(r"javascript:submitForm\(([\w_]+),'([^']+)'\);", a.attrib['onclick']) if not m: self.logger.warning('Unable to find link for %r' % account.label) account._link = None else: account._link = m.group(2) else: account._link = a.attrib['href'].strip() if accounts and accounts[-1].label == account.label and account.type == Account.TYPE_PEA: self.logger.warning('%s seems to be a duplicate of %s, skipping', account, accounts[-1]) continue accounts.append(account) return accounts
def get_list(self): accounts = [] txt = self.get_from_js('_data = new Array(', ');', is_list=True) if txt is None: raise BrokenPageError('Unable to find accounts list in scripts') data = json.loads('[%s]' % txt.replace("'", '"')) for line in data: a = Account() a.id = line[self.COL_ID].replace(' ', '') a._acc_nb = a.id.split('_')[0] if len(a.id.split('_')) > 1 else None fp = StringIO(unicode(line[self.COL_LABEL]).encode(self.browser.ENCODING)) a.label = self.parser.tocleanstring(self.parser.parse(fp, self.browser.ENCODING).xpath('//div[@class="libelleCompteTDB"]')[0]) # This account can be multiple life insurance accounts if a.label == 'ASSURANCE VIE-BON CAPI-SCPI-DIVERS *': continue a.balance = Decimal(FrenchTransaction.clean_amount(line[self.COL_BALANCE])) a.currency = a.get_currency(line[self.COL_BALANCE]) a.type = self.get_account_type(a.label) if line[self.COL_HISTORY] == 'true': a._inv = False a._link = self.get_history_link() a._args = {'_eventId': 'clicDetailCompte', '_ipc_eventValue': '', '_ipc_fireEvent': '', 'deviseAffichee': 'DEVISE', 'execution': self.get_execution(), 'idCompteClique': line[self.COL_ID], } else: a._inv = True a._args = {'_ipc_eventValue': line[self.COL_ID], '_ipc_fireEvent': line[self.COL_FIRE_EVENT], } a._link = self.document.xpath('//form[@name="changePageForm"]')[0].attrib['action'] if a.id.find('_CarteVisa') >= 0: accounts[-1]._card_ids.append(a._args) if not accounts[-1].coming: accounts[-1].coming = Decimal('0.0') accounts[-1].coming += a.balance continue a._card_ids = [] accounts.append(a) return accounts
def get_list(self): account_type = Account.TYPE_UNKNOWN accounts = [] for tr in self.doc.xpath( '//div[@class="finance"]/form/table[@class="ecli"]/tr'): if tr.attrib.get('class', '') == 'entete': account_type = self.ACCOUNT_TYPES.get( tr.find('th').text.strip(), Account.TYPE_UNKNOWN) continue tds = tr.findall('td') a = tds[0].find('a') # Skip accounts that can't be accessed if a is None: continue balance = tds[-1].text.strip() account = Account() account.label = u' '.join( [txt.strip() for txt in tds[0].itertext()]) account.label = re.sub(u'[ \xa0\u2022\r\n\t]+', u' ', account.label).strip() account.id = Regexp(pattern=u'N° ((.*?) |(.*))').filter( account.label).strip() account.type = account_type if balance: account.balance = Decimal( FrenchTransaction.clean_amount(balance)) account.currency = account.get_currency(balance) if 'onclick' in a.attrib: m = re.search(r"javascript:submitForm\(([\w_]+),'([^']+)'\);", a.attrib['onclick']) if not m: self.logger.warning('Unable to find link for %r' % account.label) account._link = None else: account._link = m.group(2) else: account._link = a.attrib['href'].strip() accounts.append(account) return accounts
def get_list(self): for tr in self.document.xpath('//table[@class="datas"]//tr'): if tr.attrib.get('class', '') == 'entete': continue cols = tr.findall('td') a = Account() a.label = unicode(cols[self.COL_ID].xpath('.//span[@class="left-underline"]')[0].text.strip()) a.type = self.get_account_type(a.label) balance = self.parser.tocleanstring(cols[self.COL_BALANCE]) a.balance = Decimal(FrenchTransaction.clean_amount(balance)) a.currency = a.get_currency(balance) a._link, a._args = self.params_from_js(cols[self.COL_ID].find('a').attrib['href']) a._acc_nb = cols[self.COL_ID].xpath('.//span[@class="right-underline"]')[0].text.replace(' ', '').strip() if a._args: a.id = '%s%s%s' % (a._acc_nb, a._args['IndiceCompte'], a._args['Indiceclassement']) else: a.id = a._acc_nb # This account can be multiple life insurance accounts if any(a.label.startswith(lab) for lab in ['ASS.VIE-BONS CAPI-SCPI-DIVERS', 'BONS CAPI-SCPI-DIVERS']) or \ u'Aucun d\\351tail correspondant pour ce compte' in tr.xpath('.//a/@href')[0]: continue if a.type is Account.TYPE_CARD: a.coming = a.balance a.balance = Decimal('0.0') a._inv = False yield a
def get_list(self): account_type = Account.TYPE_UNKNOWN for tr in self.document.xpath('//table[@class="ca-table"]/tr'): try: title = tr.xpath('.//h3/text()')[0].lower().strip() except IndexError: pass else: account_type = self.TYPES.get(title, Account.TYPE_UNKNOWN) if not tr.attrib.get('class', '').startswith('colcelligne'): continue cols = tr.findall('td') if not cols or len(cols) < self.NB_COLS: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]) account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.type = account_type or self.TYPES.get(account.label, Account.TYPE_UNKNOWN) balance = self.parser.tocleanstring(cols[self.COL_VALUE]) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if balance in ('indisponible', ''): continue account.balance = Decimal(Transaction.clean_amount(balance)) account.currency = account.get_currency(self.parser.tocleanstring(cols[self.COL_CURRENCY])) account._link = None self.set_link(account, cols) account._perimeter = self.browser.current_perimeter yield account
def get_list(self): for tr in self.document.xpath('//table[@class="datas"]//tr'): if tr.attrib.get('class', '') == 'entete': continue cols = tr.findall('td') a = Account() a.label = unicode(cols[self.COL_ID].xpath( './/span[@class="left-underline"]')[0].text.strip()) a.type = self.get_account_type(a.label) balance = self.parser.tocleanstring(cols[self.COL_BALANCE]) a.balance = Decimal(FrenchTransaction.clean_amount(balance)) a.currency = a.get_currency(balance) a._link, a._args = self.params_from_js( cols[self.COL_ID].find('a').attrib['href']) a._acc_nb = cols[self.COL_ID].xpath( './/span[@class="right-underline"]')[0].text.replace( ' ', '').strip() if a._args: a.id = '%s%s%s' % (a._acc_nb, a._args['IndiceCompte'], a._args['Indiceclassement']) else: a.id = a._acc_nb a._card_ids = [] a._inv = False yield a
def get_list(self): for box in self.document.getroot().cssselect( 'div.roundedBox div.contentBox'): a = Account() a.id = self.parser.tocleanstring( box.xpath( './/tr[@id="summaryImageHeaderRow"]//div[@class="summaryTitles"]' )[0]) a.label = self.parser.tocleanstring( box.xpath('.//span[@class="cardTitle"]')[0]) if "carte" in a.label.lower(): a.type = Account.TYPE_CARD balance = self.parser.tocleanstring( self.parser.select(box, 'td#colOSBalance div.summaryValues', 1)) if balance in (u'Indisponible', u'Indisponible Facturation en cours', ''): a.balance = NotAvailable else: a.currency = a.get_currency(balance) a.balance = -abs( CleanDecimal( replace_dots=a.currency == 'EUR').filter(balance)) a._link = self.parser.select(box, 'div.summaryTitles a.summaryLink', 1).attrib['href'] yield a
def populate(self, accounts): cards = [] for account in accounts: for li in self.doc.xpath('//li[@class="nav-category"]'): title = CleanText().filter(li.xpath('./h3')) for a in li.xpath('./ul/li//a'): label = CleanText().filter(a.xpath('.//span[@class="nav-category__name"]')) balance_el = a.xpath('.//span[@class="nav-category__value"]') balance = CleanDecimal(replace_dots=True, default=NotAvailable).filter(balance_el) if 'CARTE' in label and balance: acc = Account() acc.balance = balance acc.label = label acc.currency = FrenchTransaction.Currency().filter(balance_el) acc._link = Link().filter(a.xpath('.')) acc._history_page = acc._link acc.id = acc._webid = Regexp(pattern='([^=]+)$').filter(Link().filter(a.xpath('.'))) acc.type = Account.TYPE_CARD if not acc in cards: cards.append(acc) elif account.label == label and account.balance == balance: if not account.type: account.type = AccountsPage.ACCOUNT_TYPES.get(title, Account.TYPE_UNKNOWN) if account.type == Account.TYPE_LOAN: account._history_page = None elif account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET): account._history_page = re.sub('/$', '', Link().filter(a.xpath('.'))) elif '/compte/cav' in a.attrib['href'] or not 'titulaire' in self.url: account._history_page = self.browser.other_transactions else: account._history_page = self.browser.budget_transactions account._webid = Attr(None, 'data-account-label').filter(a.xpath('.//span[@class="nav-category__name"]')) accounts.extend(cards)
def get_list(self): iban = None for tr in self.document.xpath('//table[@class="ca-table"]/tr'): if not tr.attrib.get('class', '').startswith('colcelligne'): continue cols = tr.findall('td') if not cols or len(cols) < 7: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]) account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.type = self.TYPES.get(account.label, Account.TYPE_UNKNOWN) balance = self.parser.tocleanstring(cols[self.COL_VALUE]) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if balance in ('indisponible', ''): continue account.balance = Decimal(Transaction.clean_amount(balance)) account.currency = account.get_currency( self.parser.tocleanstring(cols[self.COL_CURRENCY])) account._link = None a = cols[0].find('a') if a is not None: account._link = a.attrib['href'].replace(' ', '%20') page = self.browser.get_page( self.browser.openurl(account._link)) url = page.get_iban_url() if url: page = self.browser.get_page(self.browser.openurl(url)) iban = account.iban = page.get_iban() elif iban: # In case there is no available IBAN on this account (for # example saving account), calculate it from the previous # IBAN. bankcode = iban[4:9] counter = iban[9:14] key = 97 - ((int(bankcode) * 89 + int(counter) * 15 + int(account.id) * 3) % 97) account.iban = iban[: 4] + bankcode + counter + account.id + str( key) yield account
def get_accounts(self): for el in self.doc.getroot().cssselect('div#content tr.row'): account = Account() balance = el.cssselect('td.Balance')[0].text account.balance = Decimal(Transaction.clean_amount(balance)) account.id = el.cssselect('span')[0].text.strip() account.currency = u'NZD' # TODO: handle other currencies if el.cssselect('td.AccountName > a'): label_el = el.cssselect('td.AccountName > a')[0] account._link = label_el.get('href') else: label_el = el.cssselect('td.AccountName')[0] account._link = None account.label = unicode(label_el.text.strip()) yield account
def get_list(self): iban = None for tr in self.document.xpath('//table[@class="ca-table"]/tr'): if not tr.attrib.get('class', '').startswith('colcelligne'): continue cols = tr.findall('td') if not cols or len(cols) < 7: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]) account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.type = self.TYPES.get(account.label, Account.TYPE_UNKNOWN) balance = self.parser.tocleanstring(cols[self.COL_VALUE]) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if balance in ('indisponible', ''): continue account.balance = Decimal(Transaction.clean_amount(balance)) account.currency = account.get_currency(self.parser.tocleanstring(cols[self.COL_CURRENCY])) account._link = None a = cols[0].find('a') if a is not None: account._link = a.attrib['href'].replace(' ', '%20') page = self.browser.get_page(self.browser.openurl(account._link)) url = page.get_iban_url() if url: page = self.browser.get_page(self.browser.openurl(url)) iban = account.iban = page.get_iban() elif iban: # In case there is no available IBAN on this account (for # example saving account), calculate it from the previous # IBAN. bankcode = iban[4:9] counter = iban[9:14] key = 97 - ((int(bankcode) * 89 + int(counter) * 15 + int(account.id) * 3) % 97) account.iban = iban[:4] + bankcode + counter + account.id + str(key) yield account
def get_list(self): names = set() for li in self.document.xpath('//div[@class="affichMontant"]/ul/li/a'): account = Account() account.label = unicode(li.cssselect('div.row-lib u')[0].text.strip()) account.id = re.sub('[ \.\-]+', '', account.label) while account.id in names: account.id = account.id + '1' names.add(account.id) account.balance = Decimal(li.cssselect('p.row-right')[0].text.strip().replace(' ', '').replace(u'\xa0', '').replace(',', '')) account._link = li.attrib['href'] yield account
def get_list(self): for div in self.doc.xpath('.//div[@id="card-details"]'): a = Account() a.id = CleanText().filter(div.xpath('.//span[@class="acc-num"]')) a.label = CleanText().filter(div.xpath('.//span[@class="card-desc"]')) if "carte" in a.label.lower(): a.type = Account.TYPE_CARD balance = CleanText().filter(div.xpath('.//span[@class="balance-data"]')) if balance in (u'Indisponible', u'Indisponible Facturation en cours', ''): a.balance = NotAvailable else: a.currency = a.get_currency(balance) a.balance = - abs(CleanDecimal(replace_dots=a.currency == 'EUR').filter(balance)) # Cancel card don't have a link to watch history link = self.doc.xpath('.//div[@class="wide-bar"]/h3/a') if len(link) == 1: a._link = link[0].attrib['href'] else: a._link = None yield a
def get_list(self): rib = None currency = None for script in self.document.xpath('//script'): if script.text is None: continue m = re.search('var rib = "(\d+)"', script.text) if m: rib = m.group(1) m = re.search("var devise='(\w+)'", script.text) if m: currency = m.group(1) if all((rib, currency)): break if not all((rib, currency)): self.logger.error('Unable to find rib or currency') for tr in self.document.xpath('//table[@id="tab-corps"]//tr'): tds = tr.findall('td') if len(tds) != 3: continue account = Account() account.type = Account.TYPE_CARD account.label = self.parser.tocleanstring(tds[self.COL_LABEL]) if len(account.label) == 0: continue link = tds[self.COL_ID].xpath('.//a')[0] m = re.match(r"changeCarte\('(\d+)','(\d+)','([^']+)'\);.*", link.attrib['onclick']) if not m: self.logger.error('Unable to parse link %r' % link.attrib['onclick']) continue account._link_num = m.group(1) #useless account._link = m.group(2) account.id = m.group(2) + account._link_num account._link_date = urllib.quote(m.group(3)) account._link_rib = rib account._link_currency = currency account._is_card = True tdbalance = self.parser.tocleanstring(tds[self.COL_BALANCE]) account.balance = -Decimal(Transaction.clean_amount(tdbalance)) account.currency = account.get_currency(tdbalance) yield account
def get_list(self): for box in self.document.getroot().cssselect('div.roundedBox div.contentBox'): a = Account() a.id = self.parser.tocleanstring(box.xpath('.//tr[@id="summaryImageHeaderRow"]//div[@class="summaryTitles"]')[0]) a.label = self.parser.tocleanstring(box.xpath('.//span[@class="cardTitle"]')[0]) balance = self.parser.tocleanstring(self.parser.select(box, 'td#colOSBalance div.summaryValues', 1)) if balance in (u'Indisponible', u'Indisponible Facturation en cours', ''): a.balance = NotAvailable else: a.balance = - abs(Decimal(Transaction.clean_amount(balance))) a.currency = a.get_currency(balance) a._link = self.parser.select(box, 'div.summaryTitles a.summaryLink', 1).attrib['href'] yield a
def get_list(self): for div in self.document.xpath('.//div[@id="card-details"]'): a = Account() a.id = self.parser.tocleanstring(div.xpath('.//span[@class="acc-num"]')[0]) a.label = self.parser.tocleanstring(div.xpath('.//span[@class="card-desc"]')[0]) balance = self.parser.tocleanstring(div.xpath('.//span[@class="balance-data"]')[0]) if balance in (u"Indisponible", u"Indisponible Facturation en cours", ""): a.balance = NotAvailable else: a.balance = -abs(Decimal(Transaction.clean_amount(balance))) a.currency = a.get_currency(balance) a._link = self.document.xpath('.//div[@class="wide-bar"]/h3/a')[0].attrib["href"] yield a
def get_list(self): account_type = Account.TYPE_UNKNOWN accounts = [] for tr in self.document.xpath('//table[@class="ecli"]/tr'): if tr.attrib.get('class', '') == 'entete': account_type = self.ACCOUNT_TYPES.get( tr.find('th').text.strip(), Account.TYPE_UNKNOWN) continue tds = tr.findall('td') balance = tds[-1].text.strip() if balance == '': continue account = Account() account.label = u' '.join( [txt.strip() for txt in tds[0].itertext()]) account.label = re.sub(u'[ \xa0\u2022\r\n\t]+', u' ', account.label).strip() account.id = re.findall('(\d+)', account.label)[0] account.balance = Decimal(FrenchTransaction.clean_amount(balance)) account.currency = account.get_currency(balance) account.type = account_type m = re.search(r"javascript:submitForm\(([\w_]+),'([^']+)'\);", tds[0].find('a').attrib['onclick']) if not m: self.logger.warning('Unable to find link for %r' % account.label) account._link = None else: account._link = m.group(2) accounts.append(account) return accounts
def get_av_accounts(self): for tr in self.document.xpath('//table[@class="datas"]/tr[not(@class)]'): cols = tr.findall('td') if len(cols) != 4: continue a = Account() a.label = self.parser.tocleanstring(cols[self.COL_LABEL]) a.type = Account.TYPE_LIFE_INSURANCE a.balance = Decimal(FrenchTransaction.clean_amount(self.parser.tocleanstring(cols[self.COL_BALANCE]))) a._link, a._args = self.get_params(cols[self.COL_LABEL].find('span/a').attrib['href']) a.id = '%s%s' % (a._args['IndiceSupport'], a._args['NumPolice']) a._acc_nb = None a._inv = True yield a
def get_av_accounts(self): for tr in self.doc.xpath('//table[@class="datas"]/tr[not(@class)]'): cols = tr.findall('td') if len(cols) != 4: continue a = Account() a.label = CleanText('.')(cols[self.COL_LABEL]) a.type = Account.TYPE_LIFE_INSURANCE a.balance = MyDecimal('.')(cols[self.COL_BALANCE]) a._link, a._args = self.get_params(cols[self.COL_LABEL].find('span/a').attrib['href']) a.id = a._args['IndiceSupport'] + a._args['NumPolice'] a._acc_nb = None a._inv = True yield a
def get_list(self): no_accounts_message = self.doc.xpath( u'//span/b[contains(text(),"Votre abonnement est clôturé. Veuillez contacter votre conseiller.")]/text()' ) if no_accounts_message: raise ActionNeeded(no_accounts_message[0]) for tr in self.doc.xpath('//table[has-class("datas")]//tr'): if tr.attrib.get('class', '') == 'entete': continue cols = tr.findall('td') a = Account() a.label = unicode(cols[self.COL_ID].xpath( './/span[@class="left-underline"] | .//span[@class="left"]/a') [0].text.strip()) a.type = self.get_account_type(a.label) balance = CleanText('.')(cols[self.COL_BALANCE]) if balance == '': continue a.balance = CleanDecimal(replace_dots=True).filter(balance) a.currency = a.get_currency(balance) if cols[self.COL_ID].find('a'): a._link, a._args = self.params_from_js( cols[self.COL_ID].find('a').attrib['href']) a._acc_nb = cols[self.COL_ID].xpath( './/span[@class="right-underline"] | .//span[@class="right"]' )[0].text.replace(' ', '').strip() if hasattr(a, '_args') and a._args: a.id = '%s%s%s' % (a._acc_nb, a._args['IndiceCompte'], a._args['Indiceclassement']) else: a.id = a._acc_nb # This account can be multiple life insurance accounts if any(a.label.startswith(lab) for lab in ['ASS.VIE-BONS CAPI-SCPI-DIVERS', 'BONS CAPI-SCPI-DIVERS']) or \ u'Aucun d\\351tail correspondant pour ce compte' in tr.xpath('.//a/@href')[0]: continue if a.type is Account.TYPE_CARD: a.coming = a.balance a.balance = Decimal('0.0') a._inv = False yield a
def get_list(self): rib = None currency = None for script in self.document.xpath('//script'): if script.text is None: continue m = re.search('var rib = "(\d+)"', script.text) if m: rib = m.group(1) m = re.search("var devise='(\w+)'", script.text) if m: currency = m.group(1) if all((rib, currency)): break if not all((rib, currency)): self.logger.error('Unable to find rib or currency') for tr in self.document.xpath('//table[@id="tab-corps"]//tr'): tds = tr.findall('td') if len(tds) != 3: continue account = Account() account.type = Account.TYPE_CARD account.label = self.parser.tocleanstring(tds[self.COL_LABEL]) if len(account.label) == 0: continue link = tds[self.COL_ID].xpath('.//a')[0] m = re.match(r"changeCarte\('(\d+)','(\d+)','([^']+)'\);.*", link.attrib['onclick']) if not m: self.logger.error('Unable to parse link %r' % link.attrib['onclick']) continue account._link_num = m.group(1) #useless account._link = m.group(2) account.id = m.group(2) + account._link_num account._link_date = urllib.quote(m.group(3)) account._link_rib = rib account._link_currency = currency account._is_card = True tdbalance = self.parser.tocleanstring(tds[self.COL_BALANCE]) account.balance = - Decimal(Transaction.clean_amount(tdbalance)) account.currency = account.get_currency(tdbalance) yield account
def populate(self, accounts): cards = [] for account in accounts: for li in self.doc.xpath('//li[@class="nav-category"]'): title = CleanText().filter(li.xpath('./h3')) for a in li.xpath('./ul/li//a'): label = CleanText().filter( a.xpath('.//span[@class="nav-category__name"]')) balance_el = a.xpath( './/span[@class="nav-category__value"]') balance = CleanDecimal( replace_dots=True, default=NotAvailable).filter(balance_el) if 'CARTE' in label and balance: acc = Account() acc.balance = balance acc.label = label acc.currency = FrenchTransaction.Currency().filter( balance_el) acc._link = Link().filter(a.xpath('.')) acc._history_page = acc._link acc.id = acc._webid = Regexp( pattern='([^=]+)$').filter(Link().filter( a.xpath('.'))) acc.type = Account.TYPE_CARD if not acc in cards: cards.append(acc) elif account.label == label and account.balance == balance: if not account.type: account.type = AccountsPage.ACCOUNT_TYPES.get( title, Account.TYPE_UNKNOWN) if account.type == Account.TYPE_LOAN: account._history_page = None elif account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET): account._history_page = re.sub( '/$', '', Link().filter(a.xpath('.'))) elif '/compte/cav' in a.attrib[ 'href'] or not 'titulaire' in self.url: account._history_page = self.browser.other_transactions else: account._history_page = self.browser.budget_transactions account._webid = Attr( None, 'data-account-label').filter( a.xpath( './/span[@class="nav-category__name"]')) accounts.extend(cards)
def get_av_accounts(self): for tr in self.document.xpath('//table[@class="datas"]/tr[not(@class)]'): cols = tr.findall('td') if len(cols) != 4: continue a = Account() a.label = self.parser.tocleanstring(cols[self.COL_LABEL]) a.type = Account.TYPE_LIFE_INSURANCE a.balance = Decimal(FrenchTransaction.clean_amount(self.parser.tocleanstring(cols[self.COL_BALANCE]))) a._link, a._args = self.get_params(cols[self.COL_LABEL].find('span/a').attrib['href']) a.id = '%s%s' % (a._args['IndiceSupport'], a._args['NumPolice']) a._acc_nb = None a._card_ids = [] a._inv = True yield a
def get_list(self): accounts = [] txt = self.get_from_js('_data = new Array(', ');', is_list=True) if txt is None: raise BrokenPageError('Unable to find accounts list in scripts') data = json.loads('[%s]' % txt.replace("'", '"')) for line in data: a = Account() a.id = line[self.COL_ID].replace(' ', '') fp = StringIO( unicode(line[self.COL_LABEL]).encode(self.browser.ENCODING)) a.label = self.parser.tocleanstring( self.parser.parse(fp, self.browser.ENCODING).xpath( '//div[@class="libelleCompteTDB"]')[0]) a.balance = Decimal( FrenchTransaction.clean_amount(line[self.COL_BALANCE])) a.currency = a.get_currency(line[self.COL_BALANCE]) a.type = self.get_account_type(a.label) a._link = self.get_history_link() if line[self.COL_HISTORY] == 'true': a._args = { '_eventId': 'clicDetailCompte', '_ipc_eventValue': '', '_ipc_fireEvent': '', 'deviseAffichee': 'DEVISE', 'execution': self.get_execution(), 'idCompteClique': line[self.COL_ID], } else: a._args = None if a.id.find('_CarteVisa') >= 0: accounts[-1]._card_ids.append(a._args) if not accounts[-1].coming: accounts[-1].coming = Decimal('0.0') accounts[-1].coming += a.balance continue a._card_ids = [] accounts.append(a) return iter(accounts)
def get_list(self): for box in self.document.getroot().cssselect("div.roundedBox div.contentBox"): a = Account() a.id = self.parser.tocleanstring( box.xpath('.//tr[@id="summaryImageHeaderRow"]//div[@class="summaryTitles"]')[0] ) a.label = self.parser.tocleanstring(box.xpath('.//span[@class="cardTitle"]')[0]) a.balance = Decimal("0.0") coming = self.parser.tocleanstring(self.parser.select(box, "td#colOSBalance div.summaryValues", 1)) if coming in (u"Indisponible", ""): a.coming = NotAvailable else: a.coming = -abs(Decimal(Transaction.clean_amount(coming))) a.currency = a.get_currency(coming) a._link = self.parser.select(box, "div.summaryTitles a.summaryLink", 1).attrib["href"] yield a
def get_av_accounts(self): for table in self.doc.xpath('//table[@class="datas"]'): head_cols = table.xpath('./tr[@class="entete"]/td') for tr in table.xpath('./tr[not(@class)]'): cols = tr.findall('td') if len(cols) != 4: continue a = Account() a.label = CleanText('.')(cols[self.COL_LABEL]) a.type = Account.TYPE_LIFE_INSURANCE a.balance = MyDecimal('.')(cols[self.COL_BALANCE]) a.currency = a.get_currency(CleanText('.')(head_cols[self.COL_BALANCE])) a._link, a._args = self.get_params(cols[self.COL_LABEL].find('span/a').attrib['href']) a.id = a._args['IndiceSupport'] + a._args['NumPolice'] a._acc_nb = None a._inv = True yield a
def get_list(self): for tr in self.document.xpath('//table[@class="datas"]//tr'): if tr.attrib.get('class', '') == 'entete': continue cols = tr.findall('td') a = Account() a.id = cols[self.COL_ID].xpath('.//span[@class="right-underline"]')[0].text.strip() a.label = unicode(cols[self.COL_ID].xpath('.//span[@class="left-underline"]')[0].text.strip()) balance = self.parser.tocleanstring(cols[self.COL_BALANCE]) a.balance = Decimal(FrenchTransaction.clean_amount(balance)) a.currency = a.get_currency(balance) a._link, a._args = self.params_from_js(cols[self.COL_ID].find('a').attrib['href']) a._card_ids = [] yield a
def get_list(self): for div in self.document.xpath('.//div[@id="card-details"]'): a = Account() a.id = self.parser.tocleanstring( div.xpath('.//span[@class="acc-num"]')[0]) a.label = self.parser.tocleanstring( div.xpath('.//span[@class="card-desc"]')[0]) balance = self.parser.tocleanstring( div.xpath('.//span[@class="balance-data"]')[0]) if balance in (u'Indisponible', u'Indisponible Facturation en cours', ''): a.balance = NotAvailable else: a.balance = -abs(Decimal(Transaction.clean_amount(balance))) a.currency = a.get_currency(balance) a._link = self.document.xpath( './/div[@class="wide-bar"]/h3/a')[0].attrib['href'] yield a
def get_list(self): accounts = [] txt = self.get_from_js('_data = new Array(', ');', is_list=True) if txt is None: raise BrokenPageError('Unable to find accounts list in scripts') data = json.loads('[%s]' % txt.replace("'", '"')) for line in data: a = Account() a.id = line[self.COL_ID].replace(' ', '') fp = StringIO(unicode(line[self.COL_LABEL]).encode(self.browser.ENCODING)) a.label = self.parser.tocleanstring(self.parser.parse(fp, self.browser.ENCODING).xpath('//div[@class="libelleCompteTDB"]')[0]) a.balance = Decimal(FrenchTransaction.clean_amount(line[self.COL_BALANCE])) a.currency = a.get_currency(line[self.COL_BALANCE]) a.type = self.get_account_type(a.label) a._link = self.get_history_link() if line[self.COL_HISTORY] == 'true': a._args = {'_eventId': 'clicDetailCompte', '_ipc_eventValue': '', '_ipc_fireEvent': '', 'deviseAffichee': 'DEVISE', 'execution': self.get_execution(), 'idCompteClique': line[self.COL_ID], } else: a._args = None if a.id.find('_CarteVisa') >= 0: accounts[-1]._card_ids.append(a._args) if not accounts[-1].coming: accounts[-1].coming = Decimal('0.0') accounts[-1].coming += a.balance continue a._card_ids = [] accounts.append(a) return iter(accounts)
def get_list(self): for box in self.document.getroot().cssselect( 'div.roundedBox div.contentBox'): a = Account() a.id = self.parser.tocleanstring( box.xpath( './/tr[@id="summaryImageHeaderRow"]//div[@class="summaryTitles"]' )[0]) a.label = self.parser.tocleanstring( box.xpath('.//span[@class="cardTitle"]')[0]) balance = self.parser.tocleanstring( self.parser.select(box, 'td#colOSBalance div.summaryValues', 1)) if balance in (u'Indisponible', ''): a.balance = NotAvailable else: a.balance = -abs(Decimal(Transaction.clean_amount(balance))) a.currency = a.get_currency(balance) a._link = self.parser.select(box, 'div.summaryTitles a.summaryLink', 1).attrib['href'] yield a
def get_list(self): account_type = Account.TYPE_UNKNOWN for tr in self.document.xpath('//table[@class="ca-table"]/tr'): try: title = tr.xpath('.//h3/text()')[0].lower().strip() except IndexError: pass else: account_type = self.TYPES.get(title, Account.TYPE_UNKNOWN) if not tr.attrib.get('class', '').startswith('colcelligne'): continue cols = tr.findall('td') if not cols or len(cols) < self.NB_COLS: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]) account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.type = self.TYPES.get(account.label, Account.TYPE_UNKNOWN) or account_type balance = self.parser.tocleanstring(cols[self.COL_VALUE]) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if balance in ('indisponible', ''): continue account.balance = Decimal(Transaction.clean_amount(balance)) account.currency = account.get_currency( self.parser.tocleanstring(cols[self.COL_CURRENCY])) account._link = None self.set_link(account, cols) account._perimeter = self.browser.current_perimeter yield account
def get_list(self): for tr in self.document.xpath('//table[@class="datas"]//tr'): if tr.attrib.get('class', '') == 'entete': continue cols = tr.findall('td') a = Account() a.label = unicode(cols[self.COL_ID].xpath('.//span[@class="left-underline"]')[0].text.strip()) a.type = self.get_account_type(a.label) balance = self.parser.tocleanstring(cols[self.COL_BALANCE]) a.balance = Decimal(FrenchTransaction.clean_amount(balance)) a.currency = a.get_currency(balance) a._link, a._args = self.params_from_js(cols[self.COL_ID].find('a').attrib['href']) a._acc_nb = cols[self.COL_ID].xpath('.//span[@class="right-underline"]')[0].text.replace(' ', '').strip() if a._args: a.id = '%s%s%s' % (a._acc_nb, a._args['IndiceCompte'], a._args['Indiceclassement']) else: a.id = a._acc_nb a._card_ids = [] a._inv = False yield a
def populate(self, accounts): cards = [] for account in accounts: for li in self.doc.xpath('//li[@class="nav-category"]'): title = CleanText().filter(li.xpath('./h3')) for a in li.xpath('./ul/li//a'): label = CleanText().filter( a.xpath('.//span[@class="nav-category__name"]')) balance_el = a.xpath( './/span[@class="nav-category__value"]') balance = CleanDecimal( replace_dots=True, default=NotAvailable).filter(balance_el) if 'CARTE' in label and balance: acc = Account() acc.balance = balance acc.label = label acc.currency = FrenchTransaction.Currency().filter( balance_el) acc._link = Link().filter(a.xpath('.')) acc._history_page = acc._link acc.id = acc._webid = Regexp( pattern='carte/(.*)$').filter(Link().filter( a.xpath('.'))) acc.type = Account.TYPE_CARD if not acc in cards: cards.append(acc) elif account.label == label and account.balance == balance: if not account.type: account.type = AccountsPage.ACCOUNT_TYPES.get( title, Account.TYPE_UNKNOWN) account._webid = Attr( None, 'data-account-label').filter( a.xpath( './/span[@class="nav-category__name"]')) accounts.extend(cards)
def get_av_accounts(self): for table in self.doc.xpath('//table[@class="datas"]'): head_cols = table.xpath('./tr[@class="entete"]/td') for tr in table.xpath('./tr[not(@class)]'): cols = tr.findall('td') if len(cols) != 4: continue a = Account() # get acc_nb like on accounts page a._acc_nb = Regexp( CleanText('//div[@id="v1-cadre"]//b[contains(text(), "Compte N")]', replace=[(' ', '')]), r'(\d+)' )(self.doc)[5:] a.label = CleanText('.')(cols[self.COL_LABEL]) a.type = Account.TYPE_LIFE_INSURANCE a.balance = MyDecimal('.')(cols[self.COL_BALANCE]) a.currency = a.get_currency(CleanText('.')(head_cols[self.COL_BALANCE])) a._link, a._args = self.get_params(cols[self.COL_LABEL].find('span/a').attrib['href']) a.id = '%s%s%s' % (a._acc_nb, a._args['IndiceSupport'], a._args['NumPolice']) a._inv = True yield a
def get_list(self): no_accounts_message = self.doc.xpath(u'//span/b[contains(text(),"Votre abonnement est clôturé. Veuillez contacter votre conseiller.")]/text()') if no_accounts_message: raise ActionNeeded(no_accounts_message[0]) previous_checking_account = None # Several deposit accounts ('Compte à terme') have the same id and the same label # So a number is added to distinguish them previous_deposit_account = None deposit_count = 1 for tr in self.doc.xpath('//table[has-class("datas")]//tr'): if tr.attrib.get('class', '') == 'entete': continue cols = tr.findall('td') a = Account() a.label = unicode(cols[self.COL_ID].xpath('.//span[@class="left-underline"] | .//span[@class="left"]/a')[0].text.strip()) a.type = self.get_account_type(a.label) balance = CleanText('.')(cols[self.COL_BALANCE]) if balance == '': continue a.balance = CleanDecimal(replace_dots=True).filter(balance) a.currency = a.get_currency(balance) if cols[self.COL_ID].find('a'): a._link, a._args = self.params_from_js(cols[self.COL_ID].find('a').attrib['href']) # There may be a href with 'javascript:NoDetail();' # The _link and _args should be None else: a._link, a._args = None, None a._acc_nb = cols[self.COL_ID].xpath('.//span[@class="right-underline"] | .//span[@class="right"]')[0].text.replace(' ', '').strip() a.id = a._acc_nb if hasattr(a, '_args') and a._args: if a._args['IndiceCompte'].isdigit(): a.id = '%s%s' % (a.id, a._args['IndiceCompte']) if a._args['Indiceclassement'].isdigit(): a.id = '%s%s' % (a.id, a._args['Indiceclassement']) # This account can be multiple life insurance accounts if (any(a.label.startswith(lab) for lab in ['ASS.VIE-BONS CAPI-SCPI-DIVERS', 'BONS CAPI-SCPI-DIVERS']) or (u'Aucun d\\351tail correspondant pour ce compte' in tr.xpath('.//a/@href')[0]) and 'COMPTE A TERME' not in tr.xpath('.//span[contains(@class, "left")]/text()')[0]): continue if a.type is Account.TYPE_CARD: a.coming = a.balance a.balance = Decimal('0.0') # Take the predecessiong checking account as parent if previous_checking_account: a.parent = previous_checking_account else: self.logger.warning('The card account %s has no parent account' % a.id) a._inv = True if a.type == Account.TYPE_CHECKING: previous_checking_account = a if previous_deposit_account and previous_deposit_account.id == a.id: a.id = a.id + '_%s' % deposit_count deposit_count += 1 previous_deposit_account = a if a.type == Account.TYPE_DEPOSIT: previous_deposit_account = a yield a
def get_list(self): TABLE_XPATH = '//table[caption[@class="caption tdb-cartes-caption" or @class="ca-table caption"]]' cards_tables = self.document.xpath(TABLE_XPATH) if cards_tables: self.logger.debug('There are several cards') xpaths = { '_id': './caption/span[@class="tdb-cartes-num"]', 'label1': './caption/span[contains(@class, "tdb-cartes-carte")]', 'label2': './caption/span[@class="tdb-cartes-prop"]', 'balance': './/tr/td[@class="cel-num"]', 'currency': '//table/caption//span/text()[starts-with(.,"Montants en ")]', 'link': './/tr//a/@href[contains(., "fwkaction=Detail")]', } else: self.logger.debug('There is only one card') xpaths = { '_id': './/tr/td[@class="cel-texte"]', 'label1': './/tr[@class="ligne-impaire ligne-bleu"]/th', 'label2': './caption/span[@class="tdb-cartes-prop"]/b', 'balance': './/tr[last()-1]/td[@class="cel-num"]', 'currency': '//table/caption//span/text()[starts-with(.,"Montants en ")]', } TABLE_XPATH = '(//table[@class="ca-table"])[1]' cards_tables = self.document.xpath(TABLE_XPATH) for table in cards_tables: get = lambda name: self.parser.tocleanstring( table.xpath(xpaths[name])[0]) account = Account() account.type = account.TYPE_CARD account.id = ''.join(get('_id').split()[1:]) account.label = '%s - %s' % (get('label1'), re.sub('\s*-\s*$', '', get('label2'))) try: account.balance = Decimal( Transaction.clean_amount( table.xpath(xpaths['balance'])[-1].text)) account.currency = account.get_currency( self.document.xpath(xpaths['currency'])[0].replace( "Montants en ", "")) except IndexError: account.balance = Decimal('0.0') if 'link' in xpaths: try: account._link = table.xpath(xpaths['link'])[-1] except IndexError: account._link = None else: account._link = re.sub('[\n\r\t]+', '', account._link) else: account._link = self.url yield account
def get_list(self): account_type = Account.TYPE_UNKNOWN accounts = [] for tr in self.doc.xpath( '//div[@class="finance"]/form/table[@class="ecli"]/tr'): if tr.attrib.get('class', '') == 'entete': account_type = self.ACCOUNT_TYPES.get( tr.find('th').text.strip(), Account.TYPE_UNKNOWN) continue tds = tr.findall('td') a = tds[0].find('a') # Skip accounts that can't be accessed if a is None: continue balance = tds[-1].text.strip() account = Account() account.label = ' '.join( [txt.strip() for txt in tds[0].itertext()]) account.label = re.sub(r'[\s\xa0\u2022]+', ' ', account.label).strip() # take "N° (FOO123 456)" but "N° (FOO123) MR. BAR" account.id = re.search(r'N° (\w+( \d+)*)', account.label).group(1).replace(' ', '') account.type = account_type for patt, type in self.ACCOUNT_TYPES2.items(): if patt in account.label.lower(): account.type = type break if balance: account.balance = Decimal( FrenchTransaction.clean_amount(balance)) account.currency = account.get_currency(balance) if 'onclick' in a.attrib: m = re.search(r"javascript:submitForm\(([\w]+),'([^']+)'\);", a.attrib['onclick']) if not m: self.logger.warning('Unable to find link for %r', account.label) account._link = None else: account._link = m.group(2) else: account._link = a.attrib['href'].strip() if accounts and accounts[ -1].label == account.label and account.type == Account.TYPE_PEA: self.logger.warning( '%s seems to be a duplicate of %s, skipping', account, accounts[-1]) continue accounts.append(account) return accounts
def get_list(self): accounts = [] for block in self.document.xpath('//div[@class="pave"]/div'): head_type = block.xpath('./div/span[@class="accGroupLabel"]')[0].text.strip() account_type = self.ACCOUNT_TYPES.get(head_type, Account.TYPE_UNKNOWN) for tr in block.cssselect('ul li.tbord_account'): id = tr.attrib.get('id', '') if id.find('contratId') != 0: self.logger.warning('Unable to parse contract ID: %r' % id) continue id = id[id.find('contratId')+len('contratId'):] link = tr.cssselect('span.accountLabel a')[0] balance = Decimal(FrenchTransaction.clean_amount(tr.cssselect('span.accountTotal')[0].text)) if id.endswith('CRT'): account = accounts[-1] account._card_links.append(link.attrib['href']) if not account.coming: account.coming = Decimal('0.0') account.coming += balance continue account = Account() account.id = id account.label = unicode(link.text.strip()) account.type = account_type account.balance = balance account.currency = account.get_currency(tr.cssselect('span.accountDev')[0].text) account._link = link.attrib['href'] account._card_links = [] accounts.append(account) if len(accounts) == 0: # Sometimes, accounts are only in javascript... for script in self.document.xpath('//script'): text = script.text if text is None: continue if 'remotePerso' not in text: continue account = None card_account = None attribs = {} account_type = Account.TYPE_UNKNOWN for line in text.split('\n'): line = line.strip() m = re.match("data.libelle = '(.*)';", line) if m: account_type = self.ACCOUNT_TYPES.get(m.group(1), Account.TYPE_UNKNOWN) elif line == 'var remotePerso = new Object;': account = Account() elif account is not None: m = re.match("remotePerso.(\w+) = '?(.*?)'?;", line) if m: attribs[m.group(1)] = m.group(2) elif line.startswith('listProduitsGroup'): account.id = attribs['refContrat'] account.label = attribs['libelle'] account.type = account_type account.balance = Decimal(FrenchTransaction.clean_amount(attribs['soldeDateOpeValeurFormatted'])) account.currency = account.get_currency(attribs['codeDevise']) account._link = 'tbord.do?id=%s&%s' % (attribs['id'], self.browser.SESSION_PARAM) account._card_links = [] if account.id.endswith('CRT'): if not len(accounts): card_account = account else: a = accounts[-1] a._card_links.append(account._link) if not a.coming: a.coming = Decimal('0.0') a.coming += account.balance else: if 'COURANT' in account.label: account.type = account.TYPE_CHECKING elif account.id.endswith('TTR'): account.type = account.TYPE_MARKET elif re.match('^\d+C$', account.id): account.type = account.TYPE_LIFE_INSURANCE elif re.match('^\d+PRT$', account.id): account.type = account.TYPE_LOAN elif not account.type: account.type = account.TYPE_SAVINGS if card_account: account._card_links.append(card_account._link) if not account.coming: account.coming = Decimal('0.0') account.coming += card_account.balance card_account = None accounts.append(account) account = None return accounts
def get_list(self): no_accounts_message = self.doc.xpath(u'//span/b[contains(text(),"Votre abonnement est clôturé. Veuillez contacter votre conseiller.")]/text()') if no_accounts_message: raise ActionNeeded(no_accounts_message[0]) previous_checking_account = None # Several deposit accounts ('Compte à terme') have the same id and the same label # So a number is added to distinguish them previous_deposit_account = None deposit_count = 1 for tr in self.doc.xpath('//table[has-class("datas")]//tr'): if tr.attrib.get('class', '') == 'entete': owner = CleanText('.')(tr.findall('td')[0]) continue cols = tr.findall('td') a = Account() a.label = unicode(cols[self.COL_ID].xpath('.//span[@class="left-underline"] | .//span[@class="left"]/a')[0].text.strip()) a.type = self.get_account_type(a.label) a.ownership = self.get_account_ownership(owner) balance = CleanText('.')(cols[self.COL_BALANCE]) if balance == '': continue a.balance = CleanDecimal(replace_dots=True).filter(balance) a.currency = a.get_currency(balance) if cols[self.COL_ID].find('a'): a._link, a._args = self.params_from_js(cols[self.COL_ID].find('a').attrib['href']) # There may be a href with 'javascript:NoDetail();' # The _link and _args should be None else: a._link, a._args = None, None a._acc_nb = cols[self.COL_ID].xpath('.//span[@class="right-underline"] | .//span[@class="right"]')[0].text.replace(' ', '').strip() a.id = a._acc_nb # If available we add 'IndiceCompte' and 'IndiceClassement' to the id due to the lack of information # on the website. This method is not enough because on some connections, if there are multiple account with the # same id and the same label, but with different currencies, we will add an index at the end of the id relative to the # order the accounts appear on the website. This will cause the accounts to be shifted when the user will add a new account # with same label/id, if this happens the new account will appear first on the website and it will take the index of '1' # previously used by the first account. the already gathered transactions of the previously first account will appear on # the new first account, the already gathered transactions of the previously second account will appear on the new # second account (the previous one), etc. if hasattr(a, '_args') and a._args: if a._args['IndiceCompte'].isdigit(): a.id = '%s%s' % (a.id, a._args['IndiceCompte']) if a._args['Indiceclassement'].isdigit(): a.id = '%s%s' % (a.id, a._args['Indiceclassement']) # This account can be multiple life insurance accounts if (any(a.label.startswith(lab) for lab in ['ASS.VIE-BONS CAPI-SCPI-DIVERS', 'BONS CAPI-SCPI-DIVERS']) or (u'Aucun d\\351tail correspondant pour ce compte' in tr.xpath('.//a/@href')[0]) and 'COMPTE A TERME' not in tr.xpath('.//span[contains(@class, "left")]/text()')[0]): continue if a.type is Account.TYPE_CARD: a.coming = a.balance a.balance = Decimal('0.0') # Take the predecessiong checking account as parent if previous_checking_account: a.parent = previous_checking_account else: self.logger.warning('The card account %s has no parent account' % a.id) a._inv = True if a.type == Account.TYPE_CHECKING: previous_checking_account = a if previous_deposit_account and previous_deposit_account.id == a.id: a.id = a.id + '_%s' % deposit_count deposit_count += 1 previous_deposit_account = a if a.type == Account.TYPE_DEPOSIT: previous_deposit_account = a yield a
def get_list(self): no_accounts_message = self.doc.xpath(u'//span/b[contains(text(),"Votre abonnement est clôturé. Veuillez contacter votre conseiller.")]/text()') if no_accounts_message: raise ActionNeeded(no_accounts_message[0]) previous_checking_account = None # Several deposit accounts ('Compte à terme') have the same id and the same label # So a number is added to distinguish them previous_deposit_account = None deposit_count = 1 for tr in self.doc.xpath('//table[has-class("datas")]//tr'): if tr.attrib.get('class', '') == 'entete': continue cols = tr.findall('td') a = Account() a.label = unicode(cols[self.COL_ID].xpath('.//span[@class="left-underline"] | .//span[@class="left"]/a')[0].text.strip()) a.type = self.get_account_type(a.label) balance = CleanText('.')(cols[self.COL_BALANCE]) if balance == '': continue a.balance = CleanDecimal(replace_dots=True).filter(balance) a.currency = a.get_currency(balance) if cols[self.COL_ID].find('a'): a._link, a._args = self.params_from_js(cols[self.COL_ID].find('a').attrib['href']) # There may be a href with 'javascript:NoDetail();' # The _link and _args should be None else: a._link, a._args = None, None a._acc_nb = cols[self.COL_ID].xpath('.//span[@class="right-underline"] | .//span[@class="right"]')[0].text.replace(' ', '').strip() if hasattr(a, '_args') and a._args: a.id = '%s%s%s' % (a._acc_nb, a._args['IndiceCompte'], a._args['Indiceclassement']) else: a.id = a._acc_nb # This account can be multiple life insurance accounts if (any(a.label.startswith(lab) for lab in ['ASS.VIE-BONS CAPI-SCPI-DIVERS', 'BONS CAPI-SCPI-DIVERS']) or (u'Aucun d\\351tail correspondant pour ce compte' in tr.xpath('.//a/@href')[0]) and 'COMPTE A TERME' not in tr.xpath('.//span[contains(@class, "left")]/text()')[0]): continue if a.type is Account.TYPE_CARD: a.coming = a.balance a.balance = Decimal('0.0') # Take the predecessiong checking account as parent if previous_checking_account: a.parent = previous_checking_account else: self.logger.warning('The card account %s has no parent account' % a.id) a._inv = False if a.type == Account.TYPE_CHECKING: previous_checking_account = a if previous_deposit_account and previous_deposit_account.id == a.id: a.id = a.id + '_%s' % deposit_count deposit_count += 1 previous_deposit_account = a if a.type == Account.TYPE_DEPOSIT: previous_deposit_account = a yield a
def get_list(self): accounts = [] for block in self.document.xpath('//div[@class="pave"]/div'): head_type = block.xpath( './div/span[@class="accGroupLabel"]')[0].text.strip() account_type = self.ACCOUNT_TYPES.get(head_type, Account.TYPE_UNKNOWN) for tr in block.cssselect('ul li.tbord_account'): id = tr.attrib.get('id', '') if id.find('contratId') != 0: self.logger.warning('Unable to parse contract ID: %r' % id) continue id = id[id.find('contratId') + len('contratId'):] link = tr.cssselect('span.accountLabel a')[0] balance = Decimal( FrenchTransaction.clean_amount( tr.cssselect('span.accountTotal')[0].text)) if id.endswith('CRT'): account = accounts[-1] account._card_links.append(link.attrib['href']) if not account.coming: account.coming = Decimal('0.0') account.coming += balance continue account = Account() account.id = id account.label = unicode(link.text.strip()) account.type = account_type account.balance = balance account.currency = account.get_currency( tr.cssselect('span.accountDev')[0].text) account._link = link.attrib['href'] account._card_links = [] accounts.append(account) if not accounts: # Sometimes, accounts are only in javascript... for script in self.document.xpath('//script'): text = script.text if text is None: continue if 'remotePerso' not in text: continue account = None attribs = {} account_type = Account.TYPE_UNKNOWN for line in text.split('\n'): line = line.strip() m = re.match("data.libelle = '(.*)';", line) if m: account_type = self.ACCOUNT_TYPES.get( m.group(1), Account.TYPE_UNKNOWN) elif line == 'var remotePerso = new Object;': account = Account() elif account is not None: m = re.match("remotePerso.(\w+) = '?(.*?)'?;", line) if m: attribs[m.group(1)] = m.group(2) elif line.startswith('listProduitsGroup'): account.id = attribs['refContrat'] account.label = attribs['libelle'] account.type = account_type account.balance = Decimal( FrenchTransaction.clean_amount( attribs['soldeDateOpeValeurFormatted'])) account.currency = account.get_currency( attribs['codeDevise']) account._link = 'tbord.do?id=%s' % attribs['id'] account._card_links = [] if account.id.endswith('CRT'): a = accounts[-1] a._card_links.append(account._link) if not a.coming: a.coming = Decimal('0.0') a.coming += account.balance else: accounts.append(account) account = None return accounts
def get_list(self): accounts = [] for block in self.doc.xpath('//div[@class="pave"]/div'): head_type = block.xpath( './div/span[@class="accGroupLabel"]')[0].text.strip() account_type = self.ACCOUNT_TYPES.get(head_type, Account.TYPE_UNKNOWN) for tr in block.cssselect('ul li.tbord_account'): id = tr.attrib.get('id', '') if id.find('contratId') != 0: self.logger.warning('Unable to parse contract ID: %r' % id) continue id = id[id.find('contratId') + len('contratId'):] link = tr.cssselect('span.accountLabel a')[0] balance = CleanDecimal('.//span[@class="accountTotal"]', replace_dots=True)(tr) account = Account() account._attached_acc = None account.id = id account.label = unicode(link.text.strip()) account.type = account_type account.balance = balance account.currency = account.get_currency( tr.cssselect('span.accountDev')[0].text) account._link = link.attrib['href'] if id.endswith('CRT'): self.populate_cards(account) accounts.append(account) if len(accounts) == 0: # Sometimes, accounts are only in javascript... for script in self.doc.xpath('//script'): text = script.text if text is None: continue if 'remotePerso' not in text: continue account = None attribs = {} account_type = Account.TYPE_UNKNOWN for line in text.split('\n'): line = line.strip() m = re.match("data.libelle = '(.*)';", line) if m: account_type = self.ACCOUNT_TYPES.get( m.group(1), Account.TYPE_UNKNOWN) elif line == 'var remotePerso = new Object;': account = Account() elif account is not None: m = re.match("remotePerso.(\w+) = '?(.*?)'?;", line) if m: attribs[m.group(1)] = m.group(2) elif line.startswith('listProduitsGroup'): account.id = attribs['refContrat'] account.label = attribs['libelle'] account.type = account_type account.balance = Decimal( FrenchTransaction.clean_amount( attribs['soldeDateOpeValeurFormatted'])) account.currency = account.get_currency( attribs['codeDevise']) account._link = 'tbord.do?id=%s&%s' % ( attribs['id'], self.browser.SESSION_PARAM) account._attached_acc = None if account.id.endswith('CRT'): self.populate_cards(account) elif any([ word in account.label.lower() for word in ['courant', 'joint', 'perso'] ]): account.type = account.TYPE_CHECKING elif account.id.endswith('TTR'): account.type = account.TYPE_MARKET elif re.match('^\d+C$', account.id): account.type = account.TYPE_LIFE_INSURANCE elif re.match('^\d+PRT$', account.id): account.type = account.TYPE_LOAN elif not account.type: account.type = account.TYPE_SAVINGS accounts.append(account) account = None return accounts