def get_loan_list(self): accounts = OrderedDict() # Old website for tr in self.doc.xpath( '//table[@cellpadding="1"]/tr[not(@class) and td[a]]'): tds = tr.findall('td') account = Account() account.id = CleanText('./a')(tds[2]).split('-')[0].strip() account.label = CleanText('./a')(tds[2]).split('-')[-1].strip() account.type = Account.TYPE_LOAN account.balance = -CleanDecimal('./a', replace_dots=True)(tds[4]) account.currency = account.get_currency(CleanText('./a')(tds[4])) accounts[account.id] = account if len(accounts) == 0: # New website for table in self.doc.xpath('//div[@class="panel"]'): title = table.getprevious() if title is None: continue account_type = self.ACCOUNT_TYPES.get( CleanText('.')(title), Account.TYPE_UNKNOWN) for tr in table.xpath( './table/tbody/tr[contains(@id,"MM_SYNTHESE_CREDITS") and contains(@id,"IdTrGlobal")]' ): tds = tr.findall('td') if len(tds) == 0: continue for i in tds[0].xpath('.//a/strong'): label = i.text.strip() break if len(tds) == 3 and Decimal( FrenchTransaction.clean_amount( CleanText('.')(tds[-2]))) and any( cls in Attr('.', 'id')(tr) for cls in ['dgImmo', 'dgConso']) == False: # in case of Consumer credit or revolving credit, we substract avalaible amount with max amout # to get what was spend balance = Decimal( FrenchTransaction.clean_amount( CleanText('.')(tds[-2]))) - Decimal( FrenchTransaction.clean_amount( CleanText('.')(tds[-1]))) else: balance = Decimal( FrenchTransaction.clean_amount( CleanText('.')(tds[-1]))) account = Account() account.id = label.split(' ')[-1] account.label = unicode(label) account.type = account_type account.balance = -abs(balance) account.currency = account.get_currency( CleanText('.')(tds[-1])) account._card_links = [] accounts[account.id] = account return accounts.itervalues()
def get_list(self): table = self.find_table() for tr in self.parser.select(table, 'tr', 'many'): tds = self.parser.select(tr, 'td') if len(tds) != 6: continue tdlabel, tdid, tdcur, tdupdated, tdbal, tdbalcur = tds account = Account() account.label = to_unicode(tdlabel.text_content().strip()) # this is important - and is also the last part of the id (considering spaces) # we can't use only the link as it does not goes where we want try: link = self.parser.select(tdlabel, 'a', 1) except BrokenPageError: # probably an account we can't display the history account._link_id = None else: account._link_id = parse_qs(link.attrib['href'])['ch4'][0] account.id = to_unicode(tdid.text.strip().replace(' ', '')) account.iban = 'FR76' + account.id # just in case we are showing the converted balances account._main_currency = Account.get_currency(tdcur.text) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if tdbal.text_content().strip() == 'indisponible': continue account.balance = Decimal( Transaction.clean_amount(tdbal.text_content())) account.currency = Account.get_currency(tdbalcur.text) account._updated = datetime.strptime(tdupdated.text, '%d/%m/%Y') yield account
def get_list(self): for tr in self.doc.xpath('//table[@class="compteTable"]/tr'): if not tr.attrib.get('class', '').startswith('ligne_'): continue cols = tr.findall('td') if len(cols) < 2: continue try: amount = sum([ Decimal(FrenchTransaction.clean_amount(txt)) for txt in cols[-1].itertext() if len(txt.strip()) > 0 ]) except InvalidOperation: continue a = cols[0].find('a') if a is None: for a in cols[0].xpath('.//li/a'): args = self.js2args(a.attrib['href']) if 'numero_compte' not in args or 'numero_poste' not in args: self.logger.warning('Card link with strange args: %s' % args) continue account = Account() account.id = '%s.%s' % (args['numero_compte'], args['numero_poste']) account.label = u'Carte %s' % CleanText().filter(a) account.balance = amount account.type = account.TYPE_CARD account.currency = [ account.get_currency(txt) for txt in cols[-1].itertext() if len(txt.strip()) > 0 ][0] yield account continue args = self.js2args(a.attrib['href']) if 'numero_compte' not in args or 'numero_poste' not in args: self.logger.warning( 'Account link for %r with strange args: %s' % (a.attrib.get('alt', a.text), args)) continue account = Account() account.id = u'%s.%s' % (args['numero_compte'], args['numero_poste']) account.label = to_unicode(a.attrib.get('alt', a.text.strip())) account.balance = amount account.currency = [ account.get_currency(txt) for txt in cols[-1].itertext() if len(txt.strip()) > 0 ][0] account.type = self.ACCOUNT_TYPES.get(account.label, Account.TYPE_UNKNOWN) yield account
def get_list(self): table = self.find_table() for tr in self.parser.select(table, 'tr', 'many'): tds = self.parser.select(tr, 'td') if len(tds) != 6: continue tdlabel, tdid, tdcur, tdupdated, tdbal, tdbalcur = tds account = Account() account.label = to_unicode(tdlabel.text_content().strip()) # this is important - and is also the last part of the id (considering spaces) # we can't use only the link as it does not goes where we want try: link = self.parser.select(tdlabel, 'a', 1) except BrokenPageError: # probably an account we can't display the history account._link_id = None else: account._link_id = parse_qs(link.attrib['href'])['ch4'][0] account.id = to_unicode(tdid.text.strip().replace(' ', '')) account.iban = 'FR76' + account.id # just in case we are showing the converted balances account._main_currency = Account.get_currency(tdcur.text) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if tdbal.text_content().strip() == 'indisponible': continue account.balance = Decimal(Transaction.clean_amount(tdbal.text_content())) account.currency = Account.get_currency(tdbalcur.text) account._updated = datetime.strptime(tdupdated.text, '%d/%m/%Y') yield account
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_loan_list(self): accounts = OrderedDict() # Old website for tr in self.doc.xpath('//table[@cellpadding="1"]/tr[not(@class) and td[a]]'): tds = tr.findall('td') account = Account() account.id = CleanText('./a')(tds[2]).split('-')[0].strip() account.label = CleanText('./a')(tds[2]).split('-')[-1].strip() account.type = Account.TYPE_LOAN account.balance = -CleanDecimal('./a', replace_dots=True)(tds[4]) account.currency = account.get_currency(CleanText('./a')(tds[4])) accounts[account.id] = account if len(accounts) == 0: # New website for table in self.doc.xpath('//div[@class="panel"]'): title = table.getprevious() if title is None: continue account_type = self.ACCOUNT_TYPES.get(CleanText('.')(title), Account.TYPE_UNKNOWN) for tr in table.xpath('./table/tbody/tr[contains(@id,"MM_SYNTHESE_CREDITS") and contains(@id,"IdTrGlobal")]'): tds = tr.findall('td') if len(tds) == 0 : continue for i in tds[0].xpath('.//a/strong'): label = i.text.strip() break if len(tds) == 3 and Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-2]))) and any(cls in Attr('.', 'id')(tr) for cls in ['dgImmo', 'dgConso']) == False: # in case of Consumer credit or revolving credit, we substract avalaible amount with max amout # to get what was spend balance = Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-2]))) - Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-1]))) else: balance = Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-1]))) account = Loan() account.id = label.split(' ')[-1] account.label = unicode(label) account.type = account_type account.balance = -abs(balance) account.currency = account.get_currency(CleanText('.')(tds[-1])) account._card_links = [] if "immobiliers" in CleanText('.')(title): xp = './/div[contains(@id, "IdDivDetail")]/table/tbody/tr[contains(@id, "%s")]/td' account.maturity_date = Date(CleanText(xp % 'IdDerniereEcheance'), dayfirst=True, default=NotAvailable)(tr) account.total_amount = CleanDecimal(CleanText(xp % 'IdCapitalEmprunte'), replace_dots=True, default=NotAvailable)(tr) account.subscription_date = Date(CleanText(xp % 'IdDateOuverture'), dayfirst=True, default=NotAvailable)(tr) account.next_payment_date = Date(CleanText(xp % 'IdDateProchaineEcheance'), dayfirst=True, default=NotAvailable)(tr) account.rate = CleanDecimal(CleanText(xp % 'IdTaux'), replace_dots=True, default=NotAvailable)(tr) account.next_payment_amount = CleanDecimal(CleanText(xp % 'IdMontantEcheance'), replace_dots=True, default=NotAvailable)(tr) elif "renouvelables" in CleanText('.')(title): self.go_loans_conso(tr) d = self.browser.loans_conso() if d: account.total_amount = d['contrat']['creditMaxAutorise'] account.available_amount = d['situationCredit']['disponible'] account.next_payment_amount = d['situationCredit']['mensualiteEnCours'] accounts[account.id] = account return accounts.values()
def get_accounts(self): accounts = {} content = self.document.xpath('//div[@id="main"]//div[@class="col first"]')[0] # Total currency balance. # If there are multiple currencies, this balance is all currencies # converted to the main currency. balance = content.xpath('.//h3/span[@class="balance"]') if not balance: balance = content.xpath('.//li[@class="balance"]//span/strong') balance = balance[0].text_content().strip() # Primary currency account primary_account = Account() primary_account.type = Account.TYPE_CHECKING primary_account.balance = clean_amount(balance) primary_account.currency = Account.get_currency(balance) primary_account.id = unicode(primary_account.currency) primary_account.label = u'%s %s*' % (self.browser.username, balance.split()[-1]) accounts[primary_account.id] = primary_account # The following code will only work if the user enabled multiple currencies. balance = content.xpath('.//div[@class="body"]//ul/li[@class="balance"]/span') table = content.xpath('.//table[@id="balanceDetails"]//tbody//tr') # sanity check if bool(balance) is not bool(table): raise BrokenPageError('Unable to find all required multiple currency entries') # Primary currency balance. # If the user enabled multiple currencies, we get this one instead. # An Account object has only one currency; secondary currencies should be other accounts. if balance: balance = balance[0].text_content().strip() primary_account.balance = clean_amount(balance) # The primary currency of the "head balance" is the same; ensure we got the right one assert primary_account.currency == primary_account.get_currency(balance) for row in table: balance = row.xpath('.//td')[-1].text_content().strip() account = Account() account.type = Account.TYPE_CHECKING # XXX it ignores 5+ devises, so it's bad, but it prevents a crash, cf #1216 try: account.balance = clean_amount(balance) except InvalidOperation: continue account.currency = Account.get_currency(balance) account.id = unicode(account.currency) account.label = u'%s %s' % (self.browser.username, balance.split()[-1]) if account.id == primary_account.id: assert account.balance == primary_account.balance assert account.currency == primary_account.currency elif account.currency: accounts[account.id] = account return accounts
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): account_type = Account.TYPE_UNKNOWN params = {} for field in self.document.xpath('//input'): params[field.attrib['name']] = field.attrib.get('value', '') for div in self.document.xpath('//div[@class="btit"]'): account_type = self.ACCOUNT_TYPES.get(div.text.strip(), Account.TYPE_UNKNOWN) for tr in div.getnext().xpath('.//tbody/tr'): if not 'id' in tr.attrib: continue args = dict(parse_qsl(tr.attrib['id'])) tds = tr.findall('td') if len(tds) < 4 or not 'identifiant' in args: self.logger.warning('Unable to parse an account') continue account = Account() account.id = args['identifiant'] account.label = u' '.join([u''.join([txt.strip() for txt in tds[1].itertext()]), u''.join([txt.strip() for txt in tds[2].itertext()])]).strip() account.type = account_type balance = u''.join([txt.strip() for txt in tds[3].itertext()]) account.balance = Decimal(FrenchTransaction.clean_amount(balance)) account.currency = account.get_currency(balance) account._params = params.copy() account._params['dialogActionPerformed'] = 'SOLDE' account._params['attribute($SEL_$%s)' % tr.attrib['id'].split('_')[0]] = tr.attrib['id'].split('_', 1)[1] yield account return
def get_loan_list(self): accounts = OrderedDict() # New website for table in self.document.xpath('//div[@class="panel"]'): title = table.getprevious() if title is None: continue account_type = self.ACCOUNT_TYPES.get(self.parser.tocleanstring(title), Account.TYPE_UNKNOWN) for tr in table.xpath('./table/tbody/tr[contains(@id,"MM_SYNTHESE_CREDITS") and contains(@id,"IdTrGlobal")]'): tds = tr.findall('td') if len(tds) == 0 : continue for i in tds[0].xpath('.//a/strong'): label = i.text.strip() break balance = Decimal(FrenchTransaction.clean_amount(self.parser.tocleanstring(tds[-1]))) account = Account() account.id = label.split(' ')[-1] account.label = unicode(label) account.type = account_type account.balance = -abs(balance) account.currency = account.get_currency(self.parser.tocleanstring(tds[-1])) account._card_links = [] accounts[account.id] = account return accounts.itervalues()
def _add_account(self, accounts, link, label, account_type, balance): info = self._get_account_info(link) if info is None: self.logger.warning('Unable to parse account %r: %r' % (label, link)) return account = Account() account.id = info['id'] account.iban = u'FR76' + info['id'] account._info = info account.label = label account.type = info['acc_type'] if 'acc_type' in info else account_type account.balance = Decimal(FrenchTransaction.clean_amount(balance)) if balance else self.get_balance(account) account.currency = account.get_currency(balance) account._card_links = [] if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts: a = accounts[account.id] if not a.coming: a.coming = Decimal('0.0') a.coming += account.balance a._card_links.append(account._info) return accounts[account.id] = account
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_account(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(parse_decimal(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.url = urljoin(self.url, link[0].attrib['href']) else: a.url = None return a
def get_accounts_list(self): for table in self.doc.xpath('//div[@class="comptestabl"]/table'): try: account_type = self.ACCOUNT_TYPES[table.get('summary').lower()] if not account_type: account_type = self.ACCOUNT_TYPES[table.xpath( './caption/text()')[0].strip().lower()] except (IndexError, KeyError): account_type = Account.TYPE_UNKNOWN for tr in table.xpath('./tbody/tr'): cols = tr.findall('td') link = cols[0].find('a') if link is None: continue a = Account() a.type = account_type a.id = unicode( re.search('([A-Z\d]{4}[A-Z\d\*]{3}[A-Z\d]{4})', link.attrib['title']).group(1)) a.label = unicode(link.attrib['title'].replace( '%s ' % a.id, '')) # We use '.text_content()' to avoid HTML comments like '<!-- ... -->' tmp_balance = CleanText(None).filter(cols[1].text_content()) a.currency = a.get_currency(tmp_balance) if not a.currency: a.currency = u'EUR' a.balance = Decimal(Transaction.clean_amount(tmp_balance)) a._has_cards = False a.url = urljoin(self.url, link.attrib['href']) yield a
def get_list(self, accounts_ids): l = [] # Read the json data json_data = self.browser.readurl('/banque/PA_Autonomy-war/ProxyIAService?cleOutil=IA_SMC_UDC&service=getlstcpt&dashboard=true&refreshSession=true&cre=udc&poka=true') json_infos = json.loads(json_data) for famille in json_infos['smc']['data']['familleCompte']: id_famille = famille['idFamilleCompte'] for compte in famille['compte']: account = Account() account.label = u''+compte['libellePersoProduit'] account.currency = account.get_currency(compte['devise']) account.balance = Decimal(compte['soldeDispo']) account.coming = Decimal(compte['soldeAVenir']) account.type = self.ACCOUNT_TYPES.get(id_famille, Account.TYPE_UNKNOWN) account.id = 0 account._link_id = 'KEY'+compte['key'] # IBAN aren't in JSON # Fast method, get it from transfer page. for i,a in accounts_ids.items(): if a.label == account.label: account.id = i # But it's doesn't work with LOAN and MARKET, so use slow method : Get it from transaction page. if account.id == 0: account.id = self.browser.get_IBAN_from_account(account) l.append(account) if len(l) == 0: print 'no accounts' # oops, no accounts? check if we have not exhausted the allowed use # of this password for img in self.document.getroot().cssselect('img[align="middle"]'): if img.attrib.get('alt', '') == 'Changez votre code secret': raise BrowserPasswordExpired('Your password has expired') return l
def parse(self, el): page, url, data = self.page.get_changecompte(Link('.')(self)) if self.env['multi'] else (self.page, None, None) balance_xpath = '//div[contains(text(),"Solde")]/strong' self.env['balance'] = MyDecimal().filter(page.doc.xpath(balance_xpath)) self.env['currency'] = Account.get_currency(CleanText().filter(page.doc.xpath(balance_xpath))) self.env['url'] = url self.env['data'] = data
def get_list(self): div = self.document.xpath('//div[@id="descriptifdroite"]')[0] account = Account() account.id = re.search( u'(\d+)', div.xpath('.//div[@class="credithauttexte"]')[0].text).group(1) account.label = u'Carte PASS' account.balance = Decimal('0') for tr in div.xpath('.//table/tr'): tds = tr.findall('td') if len(tds) < 3: continue label = u''.join([txt.strip() for txt in tds[1].itertext()]) value = u''.join([txt.strip() for txt in tds[2].itertext()]) if 'encours depuis le dernier' in label.lower(): coming = u'-' + value account.coming = Decimal( FrenchTransaction.clean_amount(coming)) account.currency = account.get_currency(coming) elif u'arrêté de compte' in label.lower(): m = re.search(u'(\d+)/(\d+)/(\d+)', label) if m: account._outstanding_date = datetime.date( *reversed(map(int, m.groups()))) break yield account
def get_loan_list(self): accounts = OrderedDict() # New website for table in self.document.xpath('//div[@class="panel"]'): title = table.getprevious() if title is None: continue account_type = self.ACCOUNT_TYPES.get( self.parser.tocleanstring(title), Account.TYPE_UNKNOWN) for tr in table.xpath( './table/tbody/tr[contains(@id,"MM_SYNTHESE_CREDITS") and contains(@id,"IdTrGlobal")]' ): tds = tr.findall('td') if len(tds) == 0: continue for i in tds[0].xpath('.//a/strong'): label = i.text.strip() break balance = Decimal( FrenchTransaction.clean_amount( self.parser.tocleanstring(tds[-1]))) account = Account() account.id = label.split(' ')[-1] account.label = unicode(label) account.type = account_type account.balance = -abs(balance) account.currency = account.get_currency( self.parser.tocleanstring(tds[-1])) account._card_links = [] accounts[account.id] = account return accounts.itervalues()
def parse_table(self, what): tables = self.document.xpath("//table[@id='%s']" % what, smart_strings=False) if len(tables) < 1: return lines = tables[0].xpath(".//tbody/tr") for line in lines: account = Account() tmp = line.xpath("./td//a")[0] account.label = to_unicode(tmp.text) account._link_id = tmp.get("href") if "BourseEnLigne" in account._link_id: continue tmp = line.xpath("./td/span/strong") if len(tmp) >= 2: tmp_id = tmp[0].text tmp_balance = tmp[1].text else: tmp_id = line.xpath("./td//span")[1].text tmp_balance = tmp[0].text account.id = tmp_id account.currency = account.get_currency(tmp_balance) account.balance = Decimal(FrenchTransaction.clean_amount(tmp_balance)) if account.id in self.accounts: a = self.accounts[account.id] a._card_links.append(account._link_id) if not a.coming: a.coming = Decimal("0.0") a.coming += account.balance else: account._card_links = [] self.accounts[account.id] = account
def _parse_account(self, tr): account = Account() # for pro usage account._stp = None account.id = tr.xpath('.//td[@class="libelleCompte"]/input')[0].attrib['id'][len('libelleCompte'):] if len(str(account.id)) == 23: account.id = str(account.id)[5:21] a = tr.xpath('.//td[@class="libelleCompte"]/a')[0] m = re.match(r'javascript:goToStatements\(\'(\d+)\'', a.get('onclick', '')) if m: account._link_id = m.group(1) else: # Can't get history for this account. account._link_id = None # To prevent multiple-IDs for CIF (for example), add an arbitrary char in ID. account.id += 'C' account.label = u''+a.text.strip() tds = tr.findall('td') account.currency = account.get_currency(tds[3].find('a').text) account.balance = self._parse_amount(tds[3].find('a')) if tds[4].find('a') is not None: account.coming = self._parse_amount(tds[4].find('a')) else: account.coming = NotAvailable return account
def _parse_account(self, tr): account = Account() # for pro usage account._stp = None account.id = tr.xpath('.//td[@class="libelleCompte"]/input' )[0].attrib['id'][len('libelleCompte'):] if len(str(account.id)) == 23: account.id = str(account.id)[5:21] a = tr.xpath('.//td[@class="libelleCompte"]/a')[0] m = re.match(r'javascript:goToStatements\(\'(\d+)\'', a.get('onclick', '')) if m: account._link_id = m.group(1) else: # Can't get history for this account. account._link_id = None # To prevent multiple-IDs for CIF (for example), add an arbitrary char in ID. account.id += 'C' account.label = u'' + a.text.strip() tds = tr.findall('td') account.currency = account.get_currency(tds[3].find('a').text) account.balance = self._parse_amount(tds[3].find('a')) if tds[4].find('a') is not None: account.coming = self._parse_amount(tds[4].find('a')) else: account.coming = NotAvailable return 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]) 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 get_list(self): for trCompte in self.document.xpath('//table[@id="compte"]/tbody/tr'): tds = trCompte.findall('td') account = Account() account.id = tds[self.CPT_ROW_ID].text.strip() account.label = unicode(tds[self.CPT_ROW_NAME].text.strip()) account_type_str = "".join([ td.text for td in tds[self.CPT_ROW_NATURE].xpath('.//td[@class="txt"]') ]).strip() account.type = self.ACCOUNT_TYPES.get(account_type_str, Account.TYPE_UNKNOWN) account.balance = Decimal( FrenchTransaction.clean_amount( self.parser.tocleanstring(tds[self.CPT_ROW_BALANCE]))) account.coming = Decimal( FrenchTransaction.clean_amount( self.parser.tocleanstring(tds[self.CPT_ROW_ENCOURS]))) account.currency = account.get_currency( tds[self.CPT_ROW_BALANCE].find("a").text) yield account return
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 _add_account(self, accounts, link, label, account_type, balance): info = self._get_account_info(link) if info is None: self.logger.warning('Unable to parse account %r: %r' % (label, link)) return account = Account() account.id = info['id'] account.iban = u'FR76' + info['id'] account._info = info account.label = label account.type = info['acc_type'] if 'acc_type' in info else account_type account.balance = Decimal(FrenchTransaction.clean_amount( balance)) if balance else self.get_balance(account) account.currency = account.get_currency(balance) account._card_links = [] if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts: a = accounts[account.id] if not a.coming: a.coming = Decimal('0.0') a.coming += account.balance a._card_links.append(account._info) return accounts[account.id] = account
def account(self): label = u' '.join(self.doc.xpath( '//div[contains(@class,"myCreditCardDetails")]')[0]\ .text_content().split()) balance = self.amount(u'Balance') cardlimit = self.doc.xpath( u'//li[text()="Available to Spend"]')[0].text_content()\ .replace(u'Available to Spend', u'').replace(u'Limit', u'').strip() paymin = self.amount(u'Payment Due') if self.doc.xpath(u'//li[@class="noPaymentDue"]'): # If payment date is not scheduled yet, set it somewhere in a # distant future, so that we always have a valid date. paydate = datetime.now() + timedelta(days=999) else: rawtext = self.doc.xpath( u'//li[contains(text(),"Due Date")]')[0].text_content() datetext = re.match('.*(\d\d/\d\d/\d\d\d\d).*', rawtext).group(1) paydate = datetime.strptime(datetext, '%m/%d/%Y') a = Account() a.id = label[-4:] a.label = label a.currency = Account.get_currency(balance) a.balance = -AmTr.decimal_amount(balance) a.type = Account.TYPE_CARD a.cardlimit = AmTr.decimal_amount(cardlimit) a.paymin = AmTr.decimal_amount(paymin) if paydate is not None: a.paydate = paydate return a
def get_accounts_list(self): for table in self.doc.xpath('//div[@class="comptestabl"]/table'): try: account_type = self.ACCOUNT_TYPES[table.get('summary').lower()] if not account_type: account_type = self.ACCOUNT_TYPES[table.xpath('./caption/text()')[0].strip().lower()] except (IndexError,KeyError): account_type = Account.TYPE_UNKNOWN for tr in table.xpath('./tbody/tr'): cols = tr.findall('td') link = cols[0].find('a') if link is None: continue a = Account() a.type = account_type a.id = unicode(re.search('([A-Z\d]{4}[A-Z\d\*]{3}[A-Z\d]{4})', link.attrib['title']).group(1)) a.label = unicode(link.attrib['title'].replace('%s ' % a.id, '')) tmp_balance = CleanText(None).filter(cols[1]) a.currency = a.get_currency(tmp_balance) if not a.currency: a.currency = u'EUR' a.balance = Decimal(Transaction.clean_amount(tmp_balance)) a._has_cards = False a.url = urljoin(self.url, link.attrib['href']) yield a
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 account(self): label = u' '.join( self.doc.xpath('//div[contains(@class,"myCreditCardDetails")]') [0].text_content().split()) balance = self.amount(u'Balance') cardlimit = self.doc.xpath( u'//li[text()="Available to Spend"]')[0].text_content()\ .replace(u'Available to Spend', u'').replace(u'Limit', u'').strip() paymin = self.amount(u'Payment Due') if self.doc.xpath(u'//li[@class="noPaymentDue"]'): # If payment date is not scheduled yet, set it somewhere in a # distant future, so that we always have a valid date. paydate = datetime.now() + timedelta(days=999) else: rawtext = self.doc.xpath( u'//li[contains(text(),"Due Date")]')[0].text_content() datetext = re.match('.*(\d\d/\d\d/\d\d\d\d).*', rawtext).group(1) paydate = datetime.strptime(datetext, '%m/%d/%Y') a = Account() a.id = label[-4:] a.label = label a.currency = Account.get_currency(balance) a.balance = -AmTr.decimal_amount(balance) a.type = Account.TYPE_CARD a.cardlimit = AmTr.decimal_amount(cardlimit) a.paymin = AmTr.decimal_amount(paymin) if paydate is not None: a.paydate = paydate return 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]) 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_accounts_list(self): for table in self.document.xpath('//div[@class="comptestabl"]/table'): try: account_type = self.ACCOUNT_TYPES[table.get('summary')] if not account_type: account_type = self.ACCOUNT_TYPES[table.xpath( './caption/text()')[0].strip()] except (IndexError, KeyError): account_type = Account.TYPE_UNKNOWN for tr in table.xpath('./tbody/tr'): cols = tr.findall('td') link = cols[0].find('a') if link is None: continue a = Account() a.type = account_type a.id = unicode( re.search('([A-Z\d]{4}[A-Z\d\*]{3}[A-Z\d]{4})', link.attrib['title']).group(1)) a.label = unicode(link.attrib['title'].replace( '%s ' % a.id, '')) tmp_balance = self.parser.tocleanstring(cols[1]) a.currency = a.get_currency(tmp_balance) a.balance = Decimal(Transaction.clean_amount(tmp_balance)) a._card_links = [] a._link_id = link.attrib['href'] yield a
def get_card_history(self, account, coming): if coming: debit_date = account._next_debit elif not hasattr(account, '_prev_balance'): return else: debit_date = account._prev_debit if 'ContinueTask.do' in self.url: t = Transaction() t.parse(debit_date, 'RELEVE CARTE') t.amount = -account._prev_balance yield t currency = Account.get_currency(self.document\ .xpath('//table[@id="TabFact"]/thead//th')[self.COL_CARD_AMOUNT]\ .text\ .replace('(', ' ')\ .replace(')', ' ')) for i, tr in enumerate(self.document.xpath('//table[@id="TabFact"]/tbody/tr')): tds = tr.findall('td') if len(tds) < 3: continue t = Transaction() date = self.parser.tocleanstring(tds[self.COL_CARD_DATE]) label = self.parser.tocleanstring(tds[self.COL_CARD_LABEL]) amount = '-' + self.parser.tocleanstring(tds[self.COL_CARD_AMOUNT]) t.parse(debit_date, re.sub(r'[ ]+', ' ', label)) t.set_amount(amount) t.rdate = t.parse_date(date) t.original_currency = currency yield t
def get_list(self, accounts_ids): l = [] # Read the json data json_data = self.browser.readurl('/banque/PA_Autonomy-war/ProxyIAService?cleOutil=IA_SMC_UDC&service=getlstcpt&dashboard=true&refreshSession=true&cre=udc&poka=true') json_infos = json.loads(json_data) for famille in json_infos['smc']['data']['familleCompte']: id_famille = famille['idFamilleCompte'] for compte in famille['compte']: account = Account() account.label = u''+compte['libellePersoProduit'] account.currency = account.get_currency(compte['devise']) account.balance = Decimal(str(compte['soldeDispo'])) account.coming = Decimal(str(compte['soldeAVenir'])) account.type = self.ACCOUNT_TYPES.get(id_famille, Account.TYPE_UNKNOWN) account.id = 0 account._link_id = 'KEY'+compte['key'] # IBAN aren't in JSON # Fast method, get it from transfer page. for i,a in accounts_ids.items(): if a.label == account.label: account.id = i # But it's doesn't work with LOAN and MARKET, so use slow method : Get it from transaction page. if account.id == 0: account.id = self.browser.get_IBAN_from_account(account) l.append(account) if len(l) == 0: print 'no accounts' # oops, no accounts? check if we have not exhausted the allowed use # of this password for img in self.document.getroot().cssselect('img[align="middle"]'): if img.attrib.get('alt', '') == 'Changez votre code secret': raise BrowserPasswordExpired('Your password has expired') return l
def get_accounts(self): accounts = {} content = self.doc.xpath('//div[@id="moneyPage"]')[0] # Primary currency account primary_account = Account() primary_account.type = Account.TYPE_CHECKING try: balance = CleanText('.')(content.xpath( '//div[contains(@class, "col-md-6")][contains(@class, "available")]' )[0]) except IndexError: primary_account.id = 'EUR' primary_account.currency = u'EUR' primary_account.balance = NotAvailable primary_account.label = u'%s' % (self.browser.username) else: primary_account.currency = Account.get_currency(balance) primary_account.id = unicode(primary_account.currency) primary_account.balance = Decimal( FrenchTransaction.clean_amount(balance)) primary_account.label = u'%s %s*' % (self.browser.username, primary_account.currency) accounts[primary_account.id] = primary_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 a._card_ids = [] a._inv = False yield a
def get_list(self): div = self.document.xpath('//div[@id="descriptifdroite"]')[0] account = Account() account.id = re.search(u'(\d+)', div.xpath('.//div[@class="credithauttexte"]')[0].text).group(1) account.label = u'Carte PASS' account.balance = Decimal('0') for tr in div.xpath('.//table/tr'): tds = tr.findall('td') if len(tds) < 3: continue label = u''.join([txt.strip() for txt in tds[1].itertext()]) value = u''.join([txt.strip() for txt in tds[2].itertext()]) if 'encours depuis le dernier' in label.lower(): coming = u'-' + value account.coming = Decimal(FrenchTransaction.clean_amount(coming)) account.currency = account.get_currency(coming) elif u'arrêté de compte' in label.lower(): m = re.search(u'(\d+)/(\d+)/(\d+)', label) if m: account._outstanding_date = datetime.date(*reversed(map(int, m.groups()))) break yield account
def _add_account(self, accounts, link, label, account_type, balance): info = self._get_account_info(link, accounts) if info is None: self.logger.warning('Unable to parse account %r: %r' % (label, link)) return account = Account() account.id = info['id'] if is_rib_valid(info['id']): account.iban = rib2iban(info['id']) account._info = info account.label = label account.type = self.ACCOUNT_TYPES.get(label, info['acc_type'] if 'acc_type' in info else account_type) balance = balance or self.get_balance(account) account.balance = Decimal(FrenchTransaction.clean_amount(balance)) if balance and balance is not NotAvailable else NotAvailable account.currency = account.get_currency(balance) if balance and balance is not NotAvailable else NotAvailable account._card_links = [] if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts: a = accounts[account.id] if not a.coming: a.coming = Decimal('0.0') if account.balance and account.balance is not NotAvailable: a.coming += account.balance a._card_links.append(account._info) return accounts[account.id] = account
def parse(self, el): try: self.env['raw'] = "%s %s" % (CleanText().filter( TableCell('commerce') (self)[0].text), CleanText().filter( TableCell('ville')(self)[0].text)) except (ColumnNotFound, AttributeError): self.env['raw'] = "%s" % (CleanText().filter( TableCell('commerce')(self)[0].text)) self.env['type'] = Transaction.TYPE_DEFERRED_CARD \ if CleanText(u'//a[contains(text(), "Prélevé fin")]', default=None) else Transaction.TYPE_CARD self.env['differed_date'] = parse_french_date( Regexp(CleanText(u'//*[contains(text(), "Achats")]'), 'au[\s]+(.*)')(self)).date() amount = TableCell('credit')(self)[0] if self.page.browser.is_new_website: amount = amount.xpath('./div')[0] if len( amount.xpath('./div')) else TableCell('debit')( self)[0].xpath('./div')[0] self.env['amount'] = CleanDecimal( replace_dots=True).filter(amount.text) original_amount = None if self.page.browser.is_new_website else amount.xpath( './span')[0].text self.env['original_amount'] = CleanDecimal(replace_dots=True).filter(original_amount) \ if original_amount is not None else NotAvailable self.env['original_currency'] = Account.get_currency(original_amount[1:-1]) \ if original_amount is not None else NotAvailable
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 account(self): id_ = self.doc.xpath( u'//strong[contains(text(),' '"credit card account ending in")]/text()')[0].strip()[-4:] balance = self.doc.xpath( u'//section[@id=" account_summary"]' '//span[text()="Current Balance"]/../span[2]/text()')[0].strip() cardlimit = self.doc.xpath(u'//span[contains(text(),"Credit limit")]' '/text()')[0].split()[-1] paymin = self.doc.xpath( u'//section[@id=" account_summary"]' '//strong[text()="Minimum Payment Due"]/../../span[2]/text()' )[0].strip() a = Account() a.id = id_ a.label = u'ACCOUNT ENDING IN %s' % id_ a.currency = Account.get_currency(balance) a.balance = -AmTr.decimal_amount(balance) a.type = Account.TYPE_CARD a.cardlimit = AmTr.decimal_amount(cardlimit) a.paymin = AmTr.decimal_amount(paymin) #TODO: Add paydate. #Oleg: I don't have an account with scheduled payment. # Need to wait for a while... return 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): accounts = OrderedDict() for tr in self.document.getiterator('tr'): first_td = tr.getchildren()[0] if (first_td.attrib.get('class', '') == 'i g' or first_td.attrib.get('class', '') == 'p g') \ and first_td.find('a') is not None: a = first_td.find('a') link = a.get('href', '') if link.startswith('POR_SyntheseLst'): continue url = urlparse(link) p = parse_qs(url.query) if not 'rib' in p: continue for i in (2,1): balance = FrenchTransaction.clean_amount(tr.getchildren()[i].text) currency = Account.get_currency(tr.getchildren()[i].text) if len(balance) > 0: break balance = Decimal(balance) id = p['rib'][0] if id in accounts: account = accounts[id] if not account.coming: account.coming = Decimal('0.0') account.coming += balance account._card_links.append(link) continue account = Account() account.id = id account.label = unicode(a.text).strip().lstrip(' 0123456789').title() account._link_id = link account._card_links = [] # Find accounting amount page = self.browser.get_document(self.browser.openurl(link)) coming = self.find_amount(page, u"Opérations à venir") accounting = self.find_amount(page, u"Solde comptable") if accounting is not None and accounting + (coming or Decimal('0')) != balance: self.logger.warning('%s + %s != %s' % (accounting, coming, balance)) if accounting is not None: balance = accounting if coming is not None: account.coming = coming account.balance = balance account.currency = currency accounts[account.id] = account return accounts.itervalues()
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 iter_accounts(self, next_pages): account_type = Account.TYPE_UNKNOWN params = {} for field in self.document.xpath('//input'): params[field.attrib['name']] = field.attrib.get('value', '') for div in self.document.getroot().cssselect('div.btit'): if div.text is None: continue account_type = self.ACCOUNT_TYPES.get(div.text.strip(), Account.TYPE_UNKNOWN) if account_type is None: # ignore services accounts continue currency = None for th in div.getnext().xpath('.//thead//th'): m = re.match('.*\((\w+)\)$', th.text) if m and currency is None: currency = Account.get_currency(m.group(1)) for tr in div.getnext().xpath('.//tbody/tr'): if 'id' not in tr.attrib: continue args = dict(parse_qsl(tr.attrib['id'])) tds = tr.findall('td') if len(tds) < 4 or 'identifiant' not in args: self.logger.warning('Unable to parse an account') continue account = Account() account.id = args['identifiant'].replace(' ', '') account.label = u' '.join([u''.join([txt.strip() for txt in tds[1].itertext()]), u''.join([txt.strip() for txt in tds[2].itertext()])]).strip() account.type = account_type balance = FrenchTransaction.clean_amount(u''.join([txt.strip() for txt in tds[3].itertext()])) account.balance = Decimal(balance or '0.0') account.currency = currency if account.type == account.TYPE_LOAN: account.balance = - abs(account.balance) account._prev_debit = None account._next_debit = None account._params = None account._coming_params = None if balance != u'' and len(tds[3].xpath('.//a')) > 0: account._params = params.copy() account._params['dialogActionPerformed'] = 'SOLDE' account._params['attribute($SEL_$%s)' % tr.attrib['id'].split('_')[0]] = tr.attrib['id'].split('_', 1)[1] if len(tds) >= 5 and len(tds[self.COL_COMING].xpath('.//a')) > 0: _params = account._params.copy() _params['dialogActionPerformed'] = 'ENCOURS_COMPTE' next_pages.append(_params) yield account
def get_list(self): """ Returns the list of available bank accounts """ for div in self.document.getiterator('div'): if div.attrib.get('class', '') in ( 'dv', 'headline') and div.getchildren()[0].tag in ('a', 'br'): self.logger.debug("Analyzing div %s" % div) # Step 1: extract text tokens tokens = [] required_tokens = {} optional_tokens = {} token_extractor = TokenExtractor() for token in token_extractor.extract_tokens(div): self.logger.debug('Extracted text token: "%s"' % token) tokens.append(token) # Step 2: analyse tokens for token in tokens: if self.look_like_account_number(token): required_tokens['account_number'] = token elif self.look_like_amount(token): required_tokens['account_amount'] = token elif self.look_like_account_name(token): required_tokens['account_name'] = token elif self.look_like_account_owner(token): if 'account_owner' in optional_tokens and 'account_name' not in required_tokens: required_tokens['account_name'] = optional_tokens[ 'account_owner'] optional_tokens['account_owner'] = token # Step 3: create account objects if len(required_tokens) >= 3: account = Account() account.label = required_tokens['account_name'] account.id = required_tokens['account_number'] account.balance = FrenchTransaction.clean_amount( required_tokens['account_amount']) account.currency = account.get_currency( required_tokens['account_amount']) # we found almost all required information to create an account object self.logger.debug( 'Found account %s with number %s and balance = %.2f' % (account.label, account.id, account.balance)) # we may have found the owner name too if optional_tokens.get('account_owner') is not None: # well, we could add it to the label, but is this really required? self.logger.debug(' the owner appears to be %s' % optional_tokens['account_owner']) # we simply lack the link to the account history... which remains optional first_link = div.find('a') if first_link is not None: account._link_id = first_link.get('href') self.logger.debug( ' the history link appears to be %s' % account._link_id) else: account._link_id = None yield account
def parse(self, el): page = self.page.get_changecompte(Link('.')(self)) if self.page.multi else self.page self.env['accid'] = CleanText('.')(self).strip().replace(' ', '').split('-')[0] self.env['label'] = CleanText('.')(self).split("-")[-1].strip() balance_xpath = '//div[contains(text(),"Solde")]/strong' self.env['balance'] = MyDecimal().filter(page.doc.xpath(balance_xpath)) self.env['currency'] = Account.get_currency(CleanText().filter(page.doc.xpath(balance_xpath))) self.env['page'] = page
def get_list(self): l = [] ids = set() for a in self.document.getiterator('a'): link = a.attrib.get('href') if link is None: continue if link.startswith("/outil/UWLM/ListeMouvements"): account = Account() account._link_id = link + "&mode=45" account._coming_links = [] parameters = link.split("?").pop().split("&") for parameter in parameters: list = parameter.split("=") value = list.pop() name = list.pop() if name == "agence": account.id = value elif name == "compte": account.id += value elif name == "nature": # TODO parse this string to get the right Account.TYPE_* to # store in account.type. account._type = value if account.id in ids: continue ids.add(account.id) div = a.getparent().getprevious() if not div.text.strip(): div = div.find('div') account.label = u'' + div.text.strip() balance = FrenchTransaction.clean_amount(a.text) if '-' in balance: balance = '-' + balance.replace('-', '') account.balance = Decimal(balance) account.currency = account.get_currency(a.text) self.logger.debug('%s Type: %s' % (account.label, account._type)) l.append(account) if link.startswith('/outil/UWCB/UWCBEncours'): if len(l) == 0: self.logger.warning( 'There is a card account but not any check account') continue account = l[-1] coming = FrenchTransaction.clean_amount(a.text) if '-' in coming: coming = '-' + coming.replace('-', '') if not account.coming: account.coming = Decimal('0') account.coming += Decimal(coming) account._coming_links.append(link) return l
def get_list(self): for tr in self.doc.xpath('//table[@class="compteTable"]/tr'): if not tr.attrib.get('class', '').startswith('ligne_'): continue cols = tr.findall('td') if len(cols) < 2: continue try: amount = sum([Decimal(FrenchTransaction.clean_amount(txt)) for txt in cols[-1].itertext() if len(txt.strip()) > 0]) except InvalidOperation: continue a = cols[0].find('a') if a is None: for a in cols[0].xpath('.//li/a'): args = self.js2args(a.attrib['href']) if 'numero_compte' not in args or 'numero_poste' not in args: self.logger.warning('Card link with strange args: %s' % args) continue account = Account() account.id = '%s.%s' % (args['numero_compte'], args['numero_poste']) account.label = u'Carte %s' % CleanText().filter(a) account.balance = amount account.type = account.TYPE_CARD account.currency = [account.get_currency(txt) for txt in cols[-1].itertext() if len(txt.strip()) > 0][0] yield account continue args = self.js2args(a.attrib['href']) if 'numero_compte' not in args or 'numero_poste' not in args: self.logger.warning('Account link for %r with strange args: %s' % (a.attrib.get('alt', a.text), args)) continue account = Account() account.id = u'%s.%s' % (args['numero_compte'], args['numero_poste']) account.label = to_unicode(a.attrib.get('alt', a.text.strip())) account.balance = amount account.currency = [account.get_currency(txt) for txt in cols[-1].itertext() if len(txt.strip()) > 0][0] account.type = self.ACCOUNT_TYPES.get(account.label, Account.TYPE_UNKNOWN) yield account
def get_list(self): for table in self.document.getroot().cssselect('div#table-panorama table.table-produit'): tds = table.xpath('./tbody/tr')[0].findall('td') if len(tds) < 3: continue boxes = table.xpath('./tbody//tr') foot = table.xpath('./tfoot//tr') for box in boxes: account = Account() if len(box.xpath('.//a')) != 0 and 'onclick' in box.xpath('.//a')[0].attrib: args = self.js2args(box.xpath('.//a')[0].attrib['onclick']) account.label = u'{0} {1}'.format(unicode(table.xpath('./caption')[0].text.strip()), unicode(box.xpath('.//a')[0].text.strip())) elif len(foot[0].xpath('.//a')) != 0 and 'onclick' in foot[0].xpath('.//a')[0].attrib: args = self.js2args(foot[0].xpath('.//a')[0].attrib['onclick']) account.label = unicode(table.xpath('./caption')[0].text.strip()) else: continue self.logger.debug('Args: %r' % args) if 'paramNumCompte' not in args: try: label = unicode(table.xpath('./caption')[0].text.strip()) except Exception: label = 'Unable to determine' self.logger.warning('Unable to get account ID for %r' % label) continue try: account.id = args['paramNumCompte'] + args['paramNumContrat'] if 'Visa' in account.label: card_id = re.search('(\d+)', box.xpath('./td[2]')[0].text.strip()) if card_id: account.id += card_id.group(1) if 'Valorisation' in account.label or u'Liquidités' in account.label: account.id += args['idPanorama:_idcl'].split('Jsp')[-1] except KeyError: account.id = args['paramNumCompte'] account_type_str = table.attrib['class'].split(' ')[-1][len('tableaux-comptes-'):] account.type = self.ACCOUNT_TYPES.get(account_type_str, Account.TYPE_UNKNOWN) currency_title = table.xpath('./thead//th[@class="montant"]')[0].text.strip() m = re.match('Montant \((\w+)\)', currency_title) if not m: self.logger.warning('Unable to parse currency %r' % currency_title) else: account.currency = account.get_currency(m.group(1)) try: account.balance = Decimal(FrenchTransaction.clean_amount(self.parse_number(u''.join([txt.strip() for txt in box.cssselect("td.montant")[0].itertext()])))) except InvalidOperation: #The account doesn't have a amount pass account._args = args yield account
def get_list(self): for table in self.document.getroot().cssselect('div#table-panorama table.table-produit'): tds = table.xpath('./tbody/tr')[0].findall('td') if len(tds) < 3: continue boxes = table.xpath('./tbody//tr') foot = table.xpath('./tfoot//tr') for box in boxes: account = Account() if len(box.xpath('.//a')) != 0 and 'onclick' in box.xpath('.//a')[0].attrib: args = self.js2args(box.xpath('.//a')[0].attrib['onclick']) account.label = u'{0} {1}'.format(unicode(table.xpath('./caption')[0].text.strip()), unicode(box.xpath('.//a')[0].text.strip())) elif len(foot[0].xpath('.//a')) != 0 and 'onclick' in foot[0].xpath('.//a')[0].attrib: args = self.js2args(foot[0].xpath('.//a')[0].attrib['onclick']) account.label = unicode(table.xpath('./caption')[0].text.strip()) else: continue self.logger.debug('Args: %r' % args) if 'paramNumCompte' not in args: try: label = unicode(table.xpath('./caption')[0].text.strip()) except Exception: label = 'Unable to determine' self.logger.warning('Unable to get account ID for %r' % label) continue try: account.id = args['paramNumCompte'] + args['paramNumContrat'] if 'Visa' in account.label: card_id = re.search('(\d+)', box.xpath('./td[2]')[0].text.strip()) if card_id: account.id += card_id.group(1) if 'Valorisation' in account.label or u'Liquidités' in account.label: account.id += args['idPanorama:_idcl'].split('Jsp')[-1] except KeyError: account.id = args['paramNumCompte'] account_type_str = table.attrib['class'].split(' ')[-1][len('tableaux-comptes-'):] account.type = self.ACCOUNT_TYPES.get(account_type_str, Account.TYPE_UNKNOWN) currency_title = table.xpath('./thead//th[@class="montant"]')[0].text.strip() m = re.match('Montant \((\w+)\)', currency_title) if not m: self.logger.warning('Unable to parse currency %r' % currency_title) else: account.currency = account.get_currency(m.group(1)) try: account.balance = Decimal(FrenchTransaction.clean_amount(u''.join([txt.strip() for txt in box.cssselect("td.montant")[0].itertext()]))) except InvalidOperation: #The account doesn't have a amount pass account._args = args yield account
def get_av_balance(self): balance_xpath = '//p[contains(text(), "montant de votre épargne")]' balance = CleanDecimal(Regexp(CleanText(balance_xpath), r'est de ([\s\d,]+)', default=NotAvailable), replace_dots=True, default=NotAvailable)(self.doc) currency = Account.get_currency(CleanText(balance_xpath)(self.doc)) return balance, currency
def get_list(self): l = [] ids = set() for a in self.document.getiterator('a'): link=a.attrib.get('href') if link is None: continue if link.startswith("/outil/UWLM/ListeMouvements"): account = Account() #by default the website propose the last 7 days or last 45 days but we can force to have the last 55days account._link_id=link+"&mode=55" account._coming_links = [] parameters=link.split("?").pop().split("&") for parameter in parameters: list=parameter.split("=") value=list.pop() name=list.pop() if name=="agence": account.id=value elif name=="compte": account.id+=value elif name=="nature": # TODO parse this string to get the right Account.TYPE_* to # store in account.type. account._type=value if account.id in ids: continue ids.add(account.id) div = a.getparent().getprevious() if not div.text.strip(): div = div.find('div') account.label=u''+div.text.strip() balance = FrenchTransaction.clean_amount(a.text) if '-' in balance: balance='-'+balance.replace('-', '') account.balance=Decimal(balance) account.currency = account.get_currency(a.text) self.logger.debug('%s Type: %s' % (account.label, account._type)) l.append(account) if link.startswith('/outil/UWCB/UWCBEncours'): if len(l) == 0: self.logger.warning('There is a card account but not any check account') continue account = l[-1] coming = FrenchTransaction.clean_amount(a.text) if '-' in coming: coming = '-'+coming.replace('-', '') if not account.coming: account.coming = Decimal('0') account.coming += Decimal(coming) account._coming_links.append(link) return l
def _get_account(self, line): tds = line.findall('td') account = Account() account.id = self.parser.tocleanstring(tds[self.COL_ID]) account.label = self.parser.tocleanstring(tds[self.COL_LABEL]) balance_str = self.parser.tocleanstring(tds[self.COL_AMOUNT]) account.balance = Decimal(FrenchTransaction.clean_amount(balance_str)) account.currency = account.get_currency(balance_str) return account
def iter_accounts(self): self.start() bal = self.wait('div.cT-valueItem span.cT-balanceIndicator1')[0].text account = Account() account.id = self._account_id() account.label = self._account_link().text account.currency = Account.get_currency(bal) account.balance = -AmTr.decimal_amount(bal) account.type = Account.TYPE_CARD self.finish() yield account
def account(self): label = u' '.join(u''.join(self.doc.xpath( u'//text()[contains(.,"Account ending in")]')).split()) balance = self.doc.xpath( '//span[@id="currentBalance"]/..')[0].text_content() a = Account() a.id = label[-4:] a.label = label a.currency = Account.get_currency(balance) a.balance = -AmTr.decimal_amount(balance) a.type = Account.TYPE_CARD return a
def get_list(self): for div in self.document.getiterator('div'): if div.attrib.get('id', '') == 'synthese-list': for tr in div.getiterator('tr'): account = Account() account.id = None account._link_id = None for td in tr.getiterator('td'): if td.attrib.get('class', '') == 'account-cb': break elif td.attrib.get('class', '') == 'account-name': try: span = td.xpath('./span[@class="label"]')[0] except IndexError: # ignore account break account.label = self.parser.tocleanstring(span) try: account._link_id = td.xpath('.//a')[0].attrib['href'] except KeyError: pass elif td.attrib.get('class', '') == 'account-more-actions': for a in td.getiterator('a'): # For normal account, two "account-more-actions" # One for the account, one for the credit card. Take the good one if "mouvements.phtml" in a.attrib['href'] and "/cartes/" not in a.attrib['href']: account._link_id = a.attrib['href'] elif td.attrib.get('class', '') == 'account-number': id = td.text id = id.strip(u' \n\t') account.id = id elif td.attrib.get('class', '') == 'account-total': span = td.find('span') if span is None: balance = td.text else: balance = span.text account.currency = account.get_currency(balance) balance = FrenchTransaction.clean_amount(balance) if balance != "": account.balance = Decimal(balance) else: account.balance = Decimal(0) else: # because of some weird useless <tr> if account.id is not None: yield account
def get_list(self): accounts = [] for tr in self.document.getiterator('tr'): if not 'LGNTableRow' in tr.attrib.get('class', '').split(): continue account = Account() for td in tr.getiterator('td'): if td.attrib.get('headers', '') == 'TypeCompte': a = td.find('a') if a is None: break account.label = unicode(a.find("span").text) for pattern, actype in self.TYPES.iteritems(): if account.label.startswith(pattern): account.type = actype account._link_id = a.get('href', '') elif td.attrib.get('headers', '') == 'NumeroCompte': id = td.text id = id.replace(u'\xa0','') account.id = id elif td.attrib.get('headers', '') == 'Libelle': pass elif td.attrib.get('headers', '') == 'Solde': div = td.xpath('./div[@class="Solde"]') if len(div) > 0: balance = self.parser.tocleanstring(div[0]) if len(balance) > 0 and balance not in ('ANNULEE', 'OPPOSITION'): try: account.balance = Decimal(FrenchTransaction.clean_amount(balance)) except InvalidOperation: raise BrokenPageError('Unable to parse balance %r' % balance) account.currency = account.get_currency(balance) else: account.balance = NotAvailable if not account.label or empty(account.balance): continue if 'CARTE_' in account._link_id: ac = accounts[0] ac._card_links.append(account._link_id) if not ac.coming: ac.coming = Decimal('0.0') ac.coming += account.balance else: account._card_links = [] accounts.append(account) return iter(accounts)