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 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.balance = Decimal("".join(tmp_balance.replace(".", "").replace(",", ".").split())) self.account_list.append(account)
def get_cards(self, account_id): divs = self.doc.xpath('//div[@class="content-boxed"]') assert len(divs) msgs = re.compile(u'Vous avez fait opposition sur cette carte bancaire.' + '|Votre carte bancaire a été envoyée.' + '|BforBank a fait opposition sur votre carte' + '|Pour des raisons de sécurité, la demande de réception du code confidentiel de votre carte par SMS est indisponible') divs = [d for d in divs if not msgs.search(CleanText('.//div[has-class("alert")]', default='')(d))] divs = [d.xpath('.//div[@class="m-card-infos"]')[0] for d in divs] divs = [d for d in divs if not d.xpath('.//div[@class="m-card-infos-body-text"][text()="Débit immédiat"]')] if not len(divs): self.logger.warning('all cards are cancelled, acting as if there is no card') return [] cards = [] for div in divs: label = CleanText('.//div[@class="m-card-infos-body-title"]')(div) number = CleanText('.//div[@class="m-card-infos-body-num"]', default='')(div) number = re.sub('[^\d*]', '', number).replace('*', 'x') debit = CleanText(u'.//div[@class="m-card-infos-body-text"][contains(text(),"Débit")]')(div) assert debit == u'Débit différé', 'unrecognized card type %s: %s' % (number, debit) card = Account() card.id = '%s.%s' % (account_id, number) card.label = label card.number = number card.type = Account.TYPE_CARD cards.append(card) return cards
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_accounts_list(self): #Parse CCP compte_table = self.document.xpath("//table[@id='comptes']", smart_strings=False)[0] compte_ligne = compte_table.xpath("./tbody/tr") for compte in compte_ligne: account = Account() tp = compte.xpath("./td/a")[0] account.label = tp.text account.link_id = tp.get("href") account.id = compte.xpath("./td")[1].text account.balance = float(''.join( compte.xpath("./td/span")[0].text.replace('.','').replace(',','.').split() )) self.Account_List.append(account) #Parse epargne epargne_table = self.document.xpath("//table[@id='comptesEpargne']", smart_strings=False)[0] epargne_ligne = epargne_table.xpath("./tbody/tr") for epargne in epargne_ligne: account = Account() tp = epargne.xpath("./td/a")[0] account.label = tp.text account.link_id = tp.get("href") account.id = epargne.xpath("./td")[1].text account.balance = float(''.join( epargne.xpath("./td/span")[0].text.replace('.','').replace(',','.').split() ) ) self.Account_List.append(account) return self.Account_List
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 _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): for line in self.document.xpath('//li[@id="menu-n2-mesproduits"]//li//a'): if line.get('onclick') is None: continue account = Account() account.id = line.get('onclick').split("'")[1] account.label = self.parser.tocleanstring(line) yield account
def get_accounts_list(self): self.location('%s://%s/summarySearch.do?reqCode=search' % (self.PROTOCOL, self.DOMAIN)) account = Account() account.id = u'1' account.currency = 'EUR' account.balance = self.page.get_balance() account.label = u'Synthèse financière' return [account]
def get_accounts_list(self): self.location("%s://%s/summarySearch.do?reqCode=search" % (self.PROTOCOL, self.DOMAIN)) account = Account() account.id = u"1" account.currency = Account.CUR_EUR account.balance = self.page.get_balance() account.label = u"Synthèse financière" return [account]
def get_accounts_list(self): balance = self.index.stay_or_go().get_balance() account = Account() account.id = self.username account.label = u'Carte Oney' account.balance = balance account.currency = u'EUR' return [account]
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_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): l = [] for cpt in self.document.xpath(".//*[@class='synthese_id_compte']"): account = Account() # account.id account.id = ( cpt.xpath("./span[1]/text()")[0] .replace(u"\xa0", "") .replace(",", ".") .replace("EUR", "") .replace("\n", "") .replace("\t", "") .replace(u"\xb0", "") .replace(" ", "") .replace("N", "") ) # account balance account.balance = Decimal( cpt.xpath("./span[2]/text()")[0] .replace("+", "") .replace(u"\xa0", "") .replace(",", ".") .replace("EUR", "") .replace("\n", "") .replace("\t", "") .replace(" ", "") ) # account coming TODO # mycomingval = cpt.xpath("../../following-sibling::*[1]/td[2]/a[@class='lien_synthese_encours']/span/text()")[0].replace(',', '.').replace("EUR", "").replace("\n", "").replace("\t", "").replace(u"\xa0", "") # mycomingval = cpt.xpath("../../following-sibling::*[1]/td[2]")[0] # mycomingval = cpt.xpath("./../../../a[@class='lien_synthese_encours']/span[@class='synthese_encours']/text()")[0].replace(',', '.').replace("EUR", "").replace("\n", "").replace("\t", "").replace(u"\xa0", "") # if mycomingval == '-': # account.coming = Decimal(0) # else: # account.coming = Decimal(mycomingval) # account._link_id url_to_parse = cpt.xpath("@href")[0].replace("\n", "") # link compte_id_re = re.compile(r"/prive/mes-comptes/([^/]+/).*COMPTE_ACTIF=([^\&]+)\&?") account._link_id = "/fr/prive/mes-comptes/%sconsulter-situation/consulter-solde.jsp?COMPTE_ACTIF=%s" % ( compte_id_re.search(url_to_parse).groups()[0], compte_id_re.search(url_to_parse).groups()[1], ) # account.label temp_label = cpt.xpath("./text()")[1].replace(u"-\xa0", "").replace("\n", "").replace("\t", "") account.label = " ".join(temp_label.split(" ")[:2]) l.append(account) return l
def _parse_account(self, tr): account = Account() 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.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): 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_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="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 = 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_account(self): if not self.is_on_page(IndexPage): self.home() account = Account() account.id = '0' account.label = self.page.get_card_name() self.location('/site/s/detailcompte/ongletdetailcompte.html') account.balance = self.page.get_balance() return 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)
def get_list(self): """ Returns the list of available bank accounts """ l = [] for div in self.document.getiterator('div'): if div.attrib.get('class', '') == 'dv' and div.getchildren()[0].tag in ('a', 'br'): account = Account() if div.getchildren()[0].tag == 'a': # This is at least present on CA Nord-Est account.label = ' '.join(div.find('a').text.split()[:-1]) account.link_id = div.find('a').get('href', '') account.id = div.find('a').text.split()[-1] s = div.find('div').find('b').find('span').text else: # This is at least present on CA Toulouse account.label = div.find('a').text.strip() account.link_id = div.find('a').get('href', '') account.id = div.findall('br')[1].tail.strip() s = div.find('div').find('span').find('b').text balance = u'' for c in s: if c.isdigit(): balance += c if c == ',': balance += '.' account.balance = float(balance) l.append(account) return l
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 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 get_list(self): accounts = [] for tr in self.document.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(txt.strip(' EUR').replace(' ', '').replace(',', '.')) for txt in cols[-1].itertext() if len(txt.strip()) > 0]) except InvalidOperation: continue a = cols[0].find('a') if a is None: # this line is a cards line. attach it on the first account. if len(accounts) == 0: self.logger.warning('There is a card link but no accounts!') continue for a in cols[0].xpath('.//li/a'): args = self.js2args(a.attrib['href']) if not 'numero_compte' in args or not 'numero_poste' in args: self.logger.warning('Card link with strange args: %s' % args) continue accounts[0]._card_links.append('%s.%s' % (args['numero_compte'], args['numero_poste'])) if not accounts[0].coming: accounts[0].coming = Decimal('0.0') accounts[0].coming += amount continue args = self.js2args(a.attrib['href']) if not 'numero_compte' in args or not 'numero_poste' 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._card_links = [] accounts.append(account) return accounts
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 iter_accounts(self): if not self.islogged: self.login() data = self.browser.open("https://www.cmb.fr/domiweb/prive/particulier/releve/0-releve.act").content parser = etree.HTMLParser() tree = etree.parse(StringIO(data), parser) table = tree.xpath('/html/body/table') if len(table) == 0: title = tree.xpath('/html/head/title')[0].text if title == u"Utilisateur non identifié": self.login() data = self.browser.open("https://www.cmb.fr/domiweb/prive/particulier/releve/0-releve.act").content parser = etree.HTMLParser() tree = etree.parse(StringIO(data), parser) table = tree.xpath('/html/body/table') if len(table) == 0: raise ParseError() else: raise ParseError() for tr in tree.xpath('/html/body//table[contains(@class, "Tb")]/tr'): if tr.get('class', None) not in ('LnTit', 'LnTot', 'LnMnTiers', None): account = Account() td = tr.xpath('td') a = td[1].xpath('a') account.label = unicode(a[0].text).strip() href = a[0].get('href') m = match(r"javascript:releve\((.*),'(.*)','(.*)'\)", href) if not m: continue account.id = unicode(m.group(1) + m.group(2) + m.group(3)) account._cmbvaleur = m.group(1) account._cmbvaleur2 = m.group(2) account._cmbtype = m.group(3) balance = u''.join([txt.strip() for txt in td[2].itertext()]) balance = balance.replace(',', '.').replace(u"\xa0", '') account.balance = Decimal(balance) span = td[4].xpath('a/span') if len(span): coming = span[0].text.replace(' ', '').replace(',', '.') coming = coming.replace(u"\xa0", '') account.coming = Decimal(coming) else: account.coming = NotAvailable yield account
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_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_loans_list(self): r = self.api_open('/transactionnel/services/applications/prets/liste') if 'content' in r.json(): for content in r.json()['content']: a = Account() a.id = "%s.%s" % (content['comptePrets'].strip(), content['numeroDossier'].strip()) a.type = Account.TYPE_LOAN a.label = ' '.join([ content['intitule'].strip(), content['libellePrets'].strip() ]) a.balance = -Decimal(str( content['montantCapitalDu']['valeur'])) a.currency = content['montantCapitalDu']['monnaie'][ 'code'].strip() yield a
def get_list(self): ids = set() for td in self.document.xpath('.//td[@nowrap="nowrap"]'): account = Account() link = td.xpath('.//a')[0] account._index = int(re.search('\d', link.attrib['href']).group(0)) if not account._index in ids: ids.add(account._index) account.id = unicode(link.text.strip()) account.label = account.id urltofind = './/a[@href="' + link.attrib['href'] + '"]' linkbis = self.document.xpath(urltofind).pop() if linkbis.text == link.text: linkbis = self.document.xpath(urltofind)[1] account.balance = Decimal(linkbis.text.replace('.', '').\ replace(' ', '').replace(',', '.')) account.coming = NotAvailable yield account
def iter_accounts(self, ibans): for f in self.path('data.infoUdc.familleCompte.*'): for a in f.get('compte'): yield Account.from_dict({ 'id': a.get('key'), 'label': a.get('libellePersoProduit') or a.get('libelleProduit'), 'currency': a.get('devise'), 'type': self.FAMILY_TO_TYPE.get(f.get('idFamilleCompte')) or Account.TYPE_UNKNOWN, 'balance': a.get('soldeDispo'), 'coming': a.get('soldeAVenir'), 'iban': ibans.get(a.get('key')) })
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') account.label = unicode(a.find("span").text) 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': balance = td.find('div').text if balance != None: balance = balance.replace(u'\xa0','').replace(',','.') account.balance = Decimal(balance) else: account.balance = Decimal(0) 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)
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.strip(' EUR')) 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 = [] account.balance = balance accounts[account.id] = account return accounts.itervalues()
def parse(self): for tr in self.document.xpath('//tbody/tr'): tlink = tr.xpath('./td[@class="desc"]/a[@class="rowClick"]')[0].attrib['href'].strip() t = FrenchTransaction(tlink[tlink.find('&id=')+4:]) date = parse_french_date(tr.xpath('./td[@class="date"]')[0].text.strip()) raw = tr.xpath('./td[@class="desc"]/a[@class="rowClick"]')[0].tail.strip() # Filter lines that do not actually modify the balance if raw.startswith('Autorisation ') or raw.endswith(' en attente par PayPal'): continue t.parse(date=date, raw=raw) amount = tr.xpath('./td[@class="price-value net"]')[0].text.strip() t.set_amount(amount) commission = tr.xpath('./td[@class="price-value fee"]')[0].text.strip() t.commission = Decimal(t.clean_amount(commission)) t.label = t.raw if t.commission: t.label += " (%s)" % tr.xpath('./td[@class="price-value gross"]')[0].text.strip() t._currency = Account.get_currency(amount) yield t
def get_accounts(self): accounts = {} content = self.document.xpath('//div[@id="moneyPage"]')[0] # Primary currency account primary_account = Account() primary_account.type = Account.TYPE_CHECKING balance = self.parser.tocleanstring( content.xpath( '//div[contains(@class, "col-md-6")][contains(@class, "available")]' )[0]) 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 iter_accounts(self): for f in self.path('tableauSoldes.listeGroupes'): for g in f: for a in g.get('listeComptes'): yield Account.from_dict({ 'id': a.get('numeroCompte'), 'iban': a.get('numeroCompte'), 'type': self.FAMILY_TO_TYPE.get(a.get('libelleType')) or Account.TYPE_UNKNOWN, 'label': '%s %s' % (a.get('libelleType'), a.get('libelleTitulaire')), 'currency': a.get('deviseTenue'), 'balance': Decimal(a.get('soldeComptable')) / 100, 'coming': Decimal(a.get('soldePrevisionnel')) / 100, })
def get_accounts_list(self): data = { 'clang': self.LANG, 'ctcc': self.CTCC, 'login': self.username, 'session': self.sessionId } for dispositif in self.accountsp.go(data=data).get_list(): if dispositif['montantBrutDispositif'] == 0: continue a = Account() a.id = dispositif['codeDispositif'] a.type = Account.TYPE_MARKET a.balance = Decimal(dispositif["montantBrutDispositif"]).quantize( Decimal('.01')) a.label = dispositif['titreDispositif'] a.currency = u"EUR" # Don't find any possbility to get that from configuration. a._investments = [] for fund in dispositif['listeFonds']: if fund['montantValeurEuro'] == 0: continue i = Investment() i.id = i.code = dispositif['codeEntreprise'] + dispositif[ "codeDispositif"] + fund["codeSupport"] i.label = fund['libelleSupport'] i.unitvalue = Decimal(fund["montantValeur"]).quantize( Decimal('.01')) i.valuation = Decimal(fund["montantValeurEuro"]).quantize( Decimal('.01')) i.quantity = i.valuation / i.unitvalue i.vdate = parse_date(fund['dateValeur'], dayfirst=True) a._investments.append(i) 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_list(self): l = [] for cpt in self.document.xpath(".//*[@class='synthese_id_compte']"): account = Account() # account.id account.id = cpt.xpath("./span[1]/text()")[0].replace( u"\xa0", "").replace(',', '.').replace("EUR", "").replace( "\n", "").replace("\t", "").replace(u"\xb0", '').replace(" ", "").replace('N', '') # account balance account.balance = Decimal( cpt.xpath("./span[2]/text()")[0].replace("+", "").replace( u"\xa0", "").replace(',', '.').replace("EUR", "").replace( "\n", "").replace("\t", "").replace(" ", "")) # account coming TODO #mycomingval = cpt.xpath("../../following-sibling::*[1]/td[2]/a[@class='lien_synthese_encours']/span/text()")[0].replace(',', '.').replace("EUR", "").replace("\n", "").replace("\t", "").replace(u"\xa0", "") #mycomingval = cpt.xpath("../../following-sibling::*[1]/td[2]")[0] #mycomingval = cpt.xpath("./../../../a[@class='lien_synthese_encours']/span[@class='synthese_encours']/text()")[0].replace(',', '.').replace("EUR", "").replace("\n", "").replace("\t", "").replace(u"\xa0", "") #if mycomingval == '-': # account.coming = Decimal(0) #else: # account.coming = Decimal(mycomingval) # account._link_id url_to_parse = cpt.xpath('@href')[0].replace("\n", "") # link compte_id_re = re.compile( r'/prive/mes-comptes/([^/]+/).*COMPTE_ACTIF=([^\&]+)\&?') account._link_id = '/fr/prive/mes-comptes/%sconsulter-situation/consulter-solde.jsp?COMPTE_ACTIF=%s' % \ (compte_id_re.search(url_to_parse).groups()[0], compte_id_re.search(url_to_parse).groups()[1]) # account.label temp_label = cpt.xpath('./text()')[1].replace( u'-\xa0', '').replace("\n", "").replace("\t", "") account.label = " ".join(temp_label.split(" ")[:2]) l.append(account) return l
def add_por_accounts(self, accounts): for ele in self.doc.xpath( '//select[contains(@name, "POR_Synthese")]/option'): for a in accounts: if a.id.startswith(ele.attrib['value']): a._is_inv = True a.type = Account.TYPE_MARKET self.fill(a) break else: acc = Account() acc.id = ele.attrib['value'] if acc.id == '9999': # fake account continue acc.label = unicode(re.sub("\d", '', ele.text).strip()) acc._link_id = None acc.type = Account.TYPE_MARKET acc._is_inv = True self.fill(acc) accounts.append(acc)
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: self.env['raw'] = "%s" % (CleanText().filter( TableCell('commerce')(self)[0].text)) self.env['amount'] = CleanDecimal( replace_dots=True).filter( TableCell('credit')(self)[0].text) original_amount = TableCell('credit')(self)[0].xpath( './span')[0].text if original_amount: self.env['original_amount'] = CleanDecimal( replace_dots=True).filter(original_amount) self.env['original_currency'] = Account.get_currency( original_amount[1:-1]) else: self.env['original_amount'] = NotAvailable self.env['original_currency'] = NotAvailable
def get_cards(self, account_id): divs = self.doc.xpath('//div[@class="content-boxed"]') msgs = re.compile( 'Vous avez fait opposition sur cette carte bancaire.' + '|Votre carte bancaire a été envoyée.' + '|Carte bancaire commandée.' + '|BforBank a fait opposition sur votre carte' + '|Pour des raisons de sécurité, la demande de réception du code confidentiel de votre carte par SMS est indisponible' ) divs = [ d for d in divs if not msgs.search( CleanText('.//div[has-class("alert")]', default='')(d)) ] divs = [d.xpath('.//div[@class="m-card-infos"]')[0] for d in divs] divs = [ d for d in divs if not d.xpath( './/div[@class="m-card-infos-body-text"][text()="Débit immédiat"]' ) ] if not len(divs): self.logger.warning( 'all cards are cancelled, acting as if there is no card') return [] cards = [] for div in divs: label = CleanText('.//div[@class="m-card-infos-body-title"]')(div) number = CleanText('.//div[@class="m-card-infos-body-num"]', default='')(div) number = re.sub('[^\d*]', '', number).replace('*', 'x') debit = CleanText( u'.//div[@class="m-card-infos-body-text"][contains(text(),"Débit")]' )(div) assert debit == u'Débit différé', 'unrecognized card type %s: %s' % ( number, debit) card = Account() card.id = '%s.%s' % (account_id, number) card.label = label card.number = number card.type = Account.TYPE_CARD cards.append(card) return cards
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.doc\ .xpath('//table[@id="TabFact"]/thead//th')[self.COL_CARD_AMOUNT]\ .text\ .replace('(', ' ')\ .replace(')', ' ')) for i, tr in enumerate( self.doc.xpath('//table[@id="TabFact"]/tbody/tr')): tds = tr.findall('td') if len(tds) < 3: continue t = Transaction() cleaner = CleanText(None).filter date = cleaner(tds[self.COL_CARD_DATE]) label = cleaner(tds[self.COL_CARD_LABEL]) amount = '-' + cleaner(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_card(self, account_id): divs = self.doc.xpath('//div[@class="content-boxed"]') assert len(divs) msgs = re.compile( u'Vous avez fait opposition sur cette carte bancaire.|Votre carte bancaire a été envoyée.' ) divs = [ d for d in divs if not msgs.search( CleanText('.//div[has-class("alert")]', default='')(d)) ] divs = [d.xpath('.//div[@class="m-card-infos"]')[0] for d in divs] if not len(divs): self.logger.warning( 'all cards are cancelled, acting as if there is no card') return cards = [] for div in divs: label = CleanText('.//div[@class="m-card-infos-body-title"]')(div) number = CleanText('.//div[@class="m-card-infos-body-num"]', default='')(div) number = re.sub('[^\d*]', '', number).replace('*', 'x') debit = CleanText( u'.//div[@class="m-card-infos-body-text"][contains(text(),"Débit")]' )(div) if debit == u'Débit immédiat': self.logger.debug('immediate debit card %s', number) continue assert debit == u'Débit différé', 'unrecognized card type %s: %s' % ( number, debit) card = Account() card.id = '%s.%s' % (account_id, number) card.label = label card.number = number card.type = Account.TYPE_CARD cards.append(card) # Crash on multiple cards if at least one is deferred assert (len(divs) > 1 and not cards) or len(divs) == 1 return cards[0] if cards else None
def iter_accounts(self): for line in self.doc.xpath( '//script[@id="initial-state"]')[0].text.split('\n'): m = re.search('window.__INITIAL_STATE__ = (.*);', line) if m: data = list(flatten(json.loads(literal_eval(m.group(1))))) break else: assert False, "data was not found" assert data.count( 'display_account_number') == 1, 'there should be exactly one card' acc = Account() acc.id = 'XXX-%s' % data[1 + data.index('display_account_number')] acc.label = '%s %s' % (data[1 + data.index('description')], data[1 + data.index('embossed_name')]) acc._index = data[1 + data.index('sorted_index')] acc._token = data[1 + data.index('account_token')] yield acc
def get_list(self): for table in self.document.xpath('//table[@cellpadding="1"]'): account_type = Account.TYPE_UNKNOWN for tr in table.xpath('./tr'): tds = tr.findall('td') if tr.attrib.get('class', '') == 'DataGridHeader': account_type = self.ACCOUNT_TYPES.get(tds[1].text.strip(), Account.TYPE_UNKNOWN) else: a = tds[1].find('a') m = re.match("^javascript:__doPostBack\('.*','HISTORIQUE_COMPTE&(\d+)'\)", a.attrib.get('href', '')) if not m: self.logger.warning('Unable to parse account %s' % (a.text.strip() if a.text is not None else '')) continue account = Account() account.id = m.group(1) account.label = unicode(a.text.strip()) account.type = account_type amount = u''.join([txt.strip() for txt in tds[-1].itertext()]) account.balance = Decimal(FrenchTransaction.clean_amount(amount.rstrip(' EUR'))) yield account
def get_list(self): accounts = [] for cpt in self.doc.xpath( '//div[contains(@class, " compte") and not(contains(@class, "compte_selected"))]' ): # ignore auto assurance accounts if 'aut' in cpt.get('class'): continue account = Account() account._history_link = Link( './ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "contrat") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) # this is to test if access to the accounts info is blocked for different reasons page = self.browser.open(account._history_link).page if isinstance(page, LoanPage): account = Loan() account._history_link = Link( './ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "contrat") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) if isinstance(page, LoanPage): account.id = CleanText( '(//p[@id="c_montantEmprunte"]//span[@class="valStatic"]//strong)[1]' )(cpt) account.label = CleanText( '(//p[@id="c_montantEmprunte"]//span[@class="valStatic"]//strong)[1]' )(cpt) account.type = Account.TYPE_LOAN account_history_page = self.browser.open( account._history_link).page account.total_amount = account_history_page.get_total_amount() account.next_payment_amount = account_history_page.get_next_payment_amount( ) account.next_payment_date = account_history_page.get_next_payment_date( ) account.account_label = account_history_page.get_account_label( ) account.subscription_date = account_history_page.get_subscription_date( ) account.maturity_date = account_history_page.get_maturity_date( ) if len(accounts) == 0: global_error_message = page.doc.xpath( '//div[@id="as_renouvellementMIFID.do_"]/div[contains(text(), "Bonjour")] ' '| //div[@id="as_afficherMessageBloquantMigration.do_"]//div[@class="content_message"] ' '| //p[contains(text(), "Et si vous faisiez de Fortuneo votre banque principale")] ' '| //div[@id="as_renouvellementMotDePasse.do_"]//p[contains(text(), "votre mot de passe")]' '| //div[@id="as_afficherSecuriteForteOTPIdentification.do_"]//span[contains(text(), "Pour valider ")]' ) if global_error_message: raise ActionNeeded(CleanText('.')(global_error_message[0])) local_error_message = page.doc.xpath( '//div[@id="error"]/p[@class="erreur_texte1"]') if local_error_message: raise BrowserUnavailable( CleanText('.')(local_error_message[0])) number = RawText('./a[contains(@class, "numero_compte")]')( cpt).replace(u'N° ', '') account.id = CleanText(None).filter(number).replace(u'N°', '') account._card_links = [] card_link = Link('./ul/li/a[contains(text(), "Carte bancaire")]', default='')(cpt) if len(card_link) > 0: account._card_links.append(card_link) account.label = CleanText( './a[contains(@class, "numero_compte")]/@title')(cpt) for pattern, type in self.ACCOUNT_TYPES.items(): if pattern in account._history_link: account.type = type break if account.type in { Account.TYPE_PEA, Account.TYPE_MARKET, Account.TYPE_LIFE_INSURANCE }: account._investment_link = Link( './ul/li/a[contains(@id, "portefeuille")]')(cpt) balance = self.browser.open( account._investment_link).page.get_balance(account.type) if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: self.browser.investments[account.id] = list( self.browser.open( account._investment_link).page.get_investments( account)) else: balance = self.browser.open( account._history_link).page.get_balance() if account.type is not Account.TYPE_LOAN: account.coming = self.browser.open( account._history_link).page.get_coming() if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: account.currency = self.browser.open( account._investment_link).page.get_currency() else: account.currency = account.get_currency(balance) account.balance = CleanDecimal(None, replace_dots=True).filter(balance) if account.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS): # Need a token sent by SMS to customers account.iban = NotAvailable if (account.label, account.id, account.balance) not in [ (a.label, a.id, a.balance) for a in accounts ]: accounts.append(account) return accounts
def get_list(self): for table in self.has_accounts(): tds = table.xpath('./tbody/tr')[0].findall('td') if len(tds) < 3: if tds[0].text_content() == u'Pr\xeat Personnel': account = Account() args = self.js2args(table.xpath('.//a')[0].attrib['onclick']) account._args = args account.label = CleanText().filter(tds[0].xpath('./ancestor::table[has-class("tableaux-pret-personnel")]/caption')) account.id = account.label.split()[-1] + args['paramNumContrat'] loan_details = self.browser.open('/webapp/axabanque/jsp/panorama.faces', data=args).page # Need to go back on home page after open self.browser.bank_accounts.open() account.balance = loan_details.get_loan_balance() account.currency = loan_details.get_loan_currency() # Skip loans without any balance (already fully reimbursed) if empty(account.balance): continue account.type = Account.TYPE_LOAN account._acctype = "bank" account._hasinv = False account._is_debit_card = False yield account continue boxes = table.xpath('./tbody//tr[not(.//strong[contains(text(), "Total")])]') foot = table.xpath('./tfoot//tr') for box in boxes: account = Account() account._url = None 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: #The displaying of life insurances is very different from the other if args.get('idPanorama:_idcl').split(":")[1] == 'tableaux-direct-solution-vie': account_details = self.browser.open("#", data=args) scripts = account_details.page.doc.xpath('//script[@type="text/javascript"]/text()') script = filter(lambda x: "src" in x, scripts)[0] iframe_url = re.search("src:(.*),", script).group()[6:-2] account_details_iframe = self.browser.open(iframe_url, data=args) account.id = CleanText('//span[contains(@id,"NumeroContrat")]/text()')(account_details_iframe.page.doc) account._url = iframe_url account.type = account.TYPE_LIFE_INSURANCE account.balance = MyDecimal('//span[contains(@id,"MontantEpargne")]/text()')(account_details_iframe.page.doc) account._acctype = "bank" account._is_debit_card = False else: 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 if account.type != account.TYPE_LIFE_INSURANCE: # get accounts type account_type_str = '' for l in table.attrib['class'].split(' '): if 'tableaux-comptes-' in l: account_type_str = l[len('tableaux-comptes-'):].lower() break account.type = Account.TYPE_UNKNOWN for pattern, type in self.ACCOUNT_TYPES.items(): if pattern in account_type_str or pattern in account.label.lower(): account.type = type break # get accounts id 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 u'Valorisation' in account.label or u'Liquidités' in account.label: account.id += args[next(k for k in args.keys() if "_idcl" in k)].split('Jsp')[-1] except KeyError: account.id = args['paramNumCompte'] # get accounts balance try: balance_value = CleanText('.//td[has-class("montant")]')(box) # skip debit card # some cards don't have information in balance tab, skip them if balance_value == u'Débit immédiat' or balance_value == '': account._is_debit_card = True else: account._is_debit_card = False account.balance = Decimal(FrenchTransaction.clean_amount(self.parse_number(balance_value))) if account.type == Account.TYPE_CARD: account.coming = account.balance account.balance = Decimal(0) except InvalidOperation: #The account doesn't have a amount pass account._url = self.doc.xpath('//form[contains(@action, "panorama")]/@action')[0] account._acctype = "bank" # get accounts currency 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)) account._args = args account._hasinv = True if "Valorisation" in account.label else False yield account
def get_currency(self): return Account.get_currency( CleanText( '//div[@id="valorisation_compte"]//td[contains(text(), "Solde")]' )(self.doc))
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 account(self): detact = self.doc['accountDetailsAndActivity'] details = detact['accountDetails'] account = Account() account.type = Account.TYPE_CARD account.label = re.sub(r'<[^>]+>', '', detact['accountName']) account.id = account.label[-4:] for bal in details['accountBalances']: label, value = bal['label'], (bal['value'] or ['0'])[0] if label == u'Current Balance:': account.currency = Account.get_currency(value) account.balance = -AmTr.decimal_amount(value) elif label == u'Total Revolving Credit Line:': account.cardlimit = AmTr.decimal_amount(value) elif label.startswith(u'Minimum Payment Due'): d = re.match(r'.*(..-..-....):$', label).group(1) account.paydate = datetime.strptime(d, '%m-%d-%Y') account.paymin = AmTr.decimal_amount(value) return account
def iter_accounts(self, next_pages): params = self.get_params() account = None currency = None for th in self.document.xpath('//table[@id="TabCtes"]//thead//th'): m = re.match('.*\((\w+)\)$', th.text) if m and currency is None: currency = Account.get_currency(m.group(1)) for tr in self.document.xpath('//table[@id="TabCtes"]/tbody/tr'): cols = tr.xpath('./td') id = self.parser.tocleanstring(cols[self.COL_ID]) if len(id) > 0: if account is not None: yield account account = Account() account.id = id.replace(' ', '') account.type = Account.TYPE_CARD account.balance = account.coming = Decimal('0') account._next_debit = datetime.date.today() account._prev_debit = datetime.date(2000, 1, 1) account.label = u' '.join([ self.parser.tocleanstring(cols[self.COL_TYPE]), self.parser.tocleanstring(cols[self.COL_LABEL]) ]) account.currency = currency account._params = None account._invest_params = None account._coming_params = params.copy() account._coming_params[ 'dialogActionPerformed'] = 'SELECTION_ENCOURS_CARTE' account._coming_params[ 'attribute($SEL_$%s)' % tr.attrib['id'].split('_')[0]] = tr.attrib['id'].split( '_', 1)[1] elif account is None: raise BrokenPageError('Unable to find accounts on cards page') else: account._params = params.copy() account._params[ 'dialogActionPerformed'] = 'SELECTION_ENCOURS_CARTE' account._params[ 'attribute($SEL_$%s)' % tr.attrib['id'].split('_')[0]] = tr.attrib['id'].split( '_', 1)[1] date_col = self.parser.tocleanstring(cols[self.COL_DATE]) m = re.search('(\d+)/(\d+)/(\d+)', date_col) if not m: self.logger.warning('Unable to parse date %r' % date_col) continue date = datetime.date(*reversed(map(int, m.groups()))) if date.year < 100: date = date.replace(year=date.year + 2000) amount = Decimal( FrenchTransaction.clean_amount( self.parser.tocleanstring(cols[self.COL_AMOUNT]))) if not date_col.endswith('(1)'): # debited account.coming += -abs(amount) account._next_debit = date elif date > account._prev_debit: account._prev_balance = -abs(amount) account._prev_debit = date if account is not None: yield account # Needed to preserve navigation. btn = self.document.xpath('.//button/span[text()="Retour"]') if len(btn) > 0: btn = btn[0].getparent() actions = self.get_button_actions() _params = params.copy() _params.update(actions[btn.attrib['id']]) self.browser.openurl('/cyber/internet/ContinueTask.do', urllib.urlencode(_params))
def filter(self, text): text = super(FrenchTransaction.Currency, self).filter(text) return Account.get_currency(text)
def iter_accounts(self, next_pages): account_type = Account.TYPE_UNKNOWN params = self.get_params() actions = self.get_button_actions() for div in self.document.getroot().cssselect('div.btit'): if div.text in (None, u'Synthèse'): continue account_type = self.ACCOUNT_TYPES.get(div.text.strip(), Account.TYPE_UNKNOWN) if account_type is None: # ignore services accounts self.logger.debug('Ignore account type %s', div.text.strip()) continue # Go to the full list of this kind of account, if any. btn = div.getparent().xpath('.//button/span[text()="Suite"]') if len(btn) > 0: btn = btn[0].getparent() _params = params.copy() _params.update(actions[btn.attrib['id']]) next_pages.append(_params) 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 account._invest_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' # If there is an action needed before going to the cards page, save it. m = re.search('dialogActionPerformed=([\w_]+)', self.url) if m and m.group(1) != 'EQUIPEMENT_COMPLET': _params['prevAction'] = m.group(1) next_pages.append(_params) if not account._params: account._invest_params = params.copy() account._invest_params['dialogActionPerformed'] = 'CONTRAT' account._invest_params[ 'attribute($SEL_$%s)' % tr.attrib['id'].split('_')[0]] = tr.attrib['id'].split( '_', 1)[1] yield account # Needed to preserve navigation. btn = self.document.xpath('.//button/span[text()="Retour"]') if len(btn) > 0: btn = btn[0].getparent() _params = params.copy() _params.update(actions[btn.attrib['id']]) self.browser.openurl('/cyber/internet/ContinueTask.do', urllib.urlencode(_params))
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): 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): 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): 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