def handle_response(self, account, recipient, amount, reason): # handle error error_msg = CleanText('//div[@id="blocErreur"]')(self.doc) if error_msg: raise TransferBankError(message=error_msg) account_txt = CleanText('//form//h3[contains(text(), "débiter")]//following::span[1]', replace=[(' ', '')])(self.doc) recipient_txt = CleanText('//form//h3[contains(text(), "créditer")]//following::span[1]', replace=[(' ', '')])(self.doc) assert account.id in account_txt or ''.join(account.label.split()) == account_txt, 'Something went wrong' assert recipient.id in recipient_txt or ''.join(recipient.label.split()) == recipient_txt, 'Something went wrong' amount_element = self.doc.xpath('//h3[contains(text(), "Montant du virement")]//following::span[@class="price"]')[0] r_amount = CleanDecimal.French('.')(amount_element) exec_date = Date(CleanText('//h3[contains(text(), "virement")]//following::span[@class="date"]'), dayfirst=True)(self.doc) currency = FrenchTransaction.Currency('.')(amount_element) transfer = Transfer() transfer.currency = currency transfer.amount = r_amount transfer.account_iban = account.iban transfer.recipient_iban = recipient.iban transfer.account_id = account.id transfer.recipient_id = recipient.id transfer.exec_date = exec_date transfer.label = reason transfer.account_label = account.label transfer.recipient_label = recipient.label transfer.account_balance = account.balance return transfer
def recap(self, origin, recipient, transfer): t = Transfer() t.label = transfer.label assert transfer.amount == CleanDecimal('//div[@id="transferSummary"]/div[@id="virementLabel"]\ //label[@class="digits positive"]', replace_dots=True)(self.doc) t.amount = transfer.amount t.currency = FrenchTransaction.Currency('//div[@id="transferSummary"]/div[@id="virementLabel"]\ //label[@class="digits positive"]')(self.doc) assert origin.label == CleanText('//div[@id="transferSummary"]/div[has-class("debit")]//span[@class="title"]')(self.doc) assert origin.balance == CleanDecimal('//div[@id="transferSummary"]/div[has-class("debit")]\ //label[@class="digits positive"]', replace_dots=True)(self.doc) t.account_balance = origin.balance t.account_label = origin.label t.account_iban = origin.iban t.account_id = origin.id assert recipient.label == CleanText('//div[@id="transferSummary"]/div[has-class("credit")]//span[@class="title"]')(self.doc) t.recipient_label = recipient.label t.recipient_iban = recipient.iban t.recipient_id = recipient.id t.exec_date = parse_french_date(CleanText('//p[has-class("exec-date")]', children=False, replace=[('le', ''), (u'exécuté', ''), ('demain', ''), ('(', ''), (')', '')])(self.doc)).date() return t
class item(ItemElement): klass = Account TYPE = { 'Livret': Account.TYPE_SAVINGS, 'Compte': Account.TYPE_CHECKING, 'PEA': Account.TYPE_MARKET, 'Compte-titres': Account.TYPE_MARKET, 'PEA-PME': Account.TYPE_MARKET, 'Assurance-vie': Account.TYPE_LIFE_INSURANCE, } obj_id = CleanText( './td//div[contains(@class, "-synthese-title") or contains(@class, "-synthese-text")]' ) & Regexp(pattern=r'(\d+)') obj_label = CleanText( './td//div[contains(@class, "-synthese-title")]') obj_balance = MyDecimal( './td//div[contains(@class, "-synthese-num")]', replace_dots=True) obj_currency = FrenchTransaction.Currency( './td//div[contains(@class, "-synthese-num")]') obj_type = Map(Regexp(Field('label'), r'^([^ ]*)'), TYPE, default=Account.TYPE_UNKNOWN) obj__link = CleanText('./@data-href') def condition(self): return not len(self.el.xpath('./td[@class="chart"]'))
class account(ItemElement): klass = Account obj_balance = CleanDecimal(TableCell('balance'), replace_dots=True, sign=lambda x: -1) obj_currency = FrenchTransaction.Currency(TableCell('balance')) obj_type = Account.TYPE_LOAN obj_id = Env('id') obj__transfer_id = None def obj_label(self): has_type = CleanText( './ancestor::table[.//th[contains(text(), "Nature libell")]]', default=None)(self) return CleanText('./td[3]')(self) if has_type else CleanText( './ancestor::table/preceding-sibling::div[1]')(self).split( ' - ')[0] def parse(self, el): label = Field('label')(self) trs = self.xpath( '//td[contains(text(), $label)]/ancestor::tr[1] | ./ancestor::table[1]/tbody/tr', label=label) i = [i for i in range(len(trs)) if el == trs[i]] i = i[0] if i else 0 label = label.replace(' ', '') self.env['id'] = "%s%s%s" % (Regexp( CleanText(TableCell('id')), r'(\w+)\s-\s(\w+)', r'\1\2')(self), label.replace(' ', ''), i)
class item(ItemElement): klass = Account load_details = Field('url') & AsyncLoad obj_label = CleanText('.//a[@class="account--name"] | .//div[@class="account--name"]') obj_balance = CleanDecimal('.//a[has-class("account--balance")]', replace_dots=True) obj_currency = FrenchTransaction.Currency('.//a[has-class("account--balance")]') obj_valuation_diff = Async('details') & CleanDecimal('//li[h4[text()="Total des +/- values"]]/h3 |\ //li[span[text()="Total des +/- values latentes"]]/span[has-class("overview__value")]', replace_dots=True, default=NotAvailable) obj__card = Async('details') & Attr('//a[@data-modal-behavior="credit_card-modal-trigger"]', 'href', default=NotAvailable) obj__holder = None def obj_coming(self): # Don't duplicate coming (card balance with account coming) # TODO: fetch coming which is not card coming for account with cards. if self.obj__card(self): return NotAvailable return Async('details', CleanDecimal(u'//li[h4[text()="Mouvements à venir"]]/h3', replace_dots=True, default=NotAvailable))(self) def obj_id(self): id = Async('details', Regexp(CleanText('//h3[has-class("account-number")]'), r'(\d+)', default=NotAvailable))(self) if not id: raise SkipItem() return id def obj_type(self): for word in Field('label')(self).lower().split(): v = self.page.ACCOUNT_TYPES.get(word) if v: return v category = CleanText('./preceding-sibling::tr[has-class("list--accounts--master")]//h4')(self) v = self.page.ACCOUNT_TYPES.get(category) if v: return v page = Async('details').loaded_page(self) if isinstance(page, LoanPage): return Account.TYPE_LOAN return Account.TYPE_UNKNOWN def obj_url(self): link = Attr('.//a[@class="account--name"] | .//a[2]', 'href', default=NotAvailable)(self) if not self.page.browser.webid: self.page.browser.webid = re.search('\/([^\/|?|$]{32})(\/|\?|$)', link).group(1) return urljoin(self.page.url, link) def obj__webid(self): m = re.search('([a-z\d]{32})', Field('url')(self)) if m: return m.group(1) return None # We do not yield other banks accounts for the moment. def validate(self, obj): return not Async('details', CleanText(u'//h4[contains(text(), "Établissement bancaire")]'))(self) and not \ Async('details', CleanText(u'//h4/div[contains(text(), "Établissement bancaire")]'))(self)
class item(ItemElement): klass = Account def condition(self): return len(self.el.xpath('./td')) > 2 class Label(Filter): def filter(self, text): return text.lstrip(' 0123456789').title() class Type(Filter): PATTERNS = [ ('invest', Account.TYPE_MARKET), ('ldd', Account.TYPE_SAVINGS), ('livret', Account.TYPE_SAVINGS), ('compte', Account.TYPE_CHECKING), ('account', Account.TYPE_CHECKING), ('pret', Account.TYPE_LOAN), ('vie', Account.TYPE_LIFE_INSURANCE), ('strategie patr.', Account.TYPE_LIFE_INSURANCE), ('essentiel', Account.TYPE_LIFE_INSURANCE), ('elysee', Account.TYPE_LIFE_INSURANCE), ('abondance', Account.TYPE_LIFE_INSURANCE), ('ely. retraite', Account.TYPE_LIFE_INSURANCE), ('lae option assurance', Account.TYPE_LIFE_INSURANCE), ('carte ', Account.TYPE_CARD), ('plan assur. innovat.', Account.TYPE_LIFE_INSURANCE), ] def filter(self, label): label = label.lower() for pattern, type in self.PATTERNS: if pattern in label: return type return Account.TYPE_UNKNOWN obj_label = Label(CleanText('./td[1]/a')) obj_coming = Env('coming') obj_currency = FrenchTransaction.Currency('./td[2]') obj_url = AbsoluteLink('./td[1]/a') obj_type = Type(Field('label')) obj_coming = NotAvailable @property def obj_balance(self): if self.el.xpath('./parent::*/tr/th') and self.el.xpath('./parent::*/tr/th')[0].text in [u'Credits', u'Crédits']: return CleanDecimal(replace_dots=True, sign=lambda x: -1).filter(self.el.xpath('./td[3]')) return CleanDecimal(replace_dots=True).filter(self.el.xpath('./td[3]')) @property def obj_id(self): # Investment account and main account can have the same id # so we had account type in case of Investment to prevent conflict if Field('type')(self) == Account.TYPE_MARKET: return CleanText(replace=[('.', ''), (' ', '')]).filter(self.el.xpath('./td[2]')) + ".INVEST" return CleanText(replace=[('.', ''), (' ', '')]).filter(self.el.xpath('./td[2]'))
class item(ItemElement): klass = FrenchTransaction obj_date = Date(CleanText('./td[1]')) obj_rdate = Date(CleanText('./td[1]')) obj_raw = CleanText('./td[2]') obj_amount = CleanDecimal('./td[4]', replace_dots=True) obj_original_currency = FrenchTransaction.Currency('./td[4]') obj_type = Transaction.TYPE_BANK obj__is_coming = False
class item(ItemElement): klass = Account def condition(self): return len(self.el.xpath('./td')) > 2 class Label(Filter): def filter(self, text): return text.lstrip(' 0123456789').title() class Type(Filter): def filter(self, label): invest = ['invest', 'ldd', 'livret a'] account = ['compte', 'account'] loan = ['pret', 'account'] for inv in invest: if inv in label.lower(): return Account.TYPE_MARKET for acc in account: if acc in label.lower(): return Account.TYPE_CHECKING for l in loan: if l in label.lower(): return Account.TYPE_LOAN return Account.TYPE_UNKNOWN obj_label = Label(CleanText('./td[1]/a')) obj_coming = Env('coming') obj_currency = FrenchTransaction.Currency('./td[3]') obj__link_id = Link('./td[1]/a') obj_type = Type(Field('label')) obj_coming = NotAvailable @property def obj_balance(self): if self.el.xpath('./parent::*/tr/th') and self.el.xpath( './parent::*/tr/th')[0].text == 'Credits': balance = CleanDecimal(replace_dots=True).filter( self.el.xpath('./td[3]')) if balance < 0: return balance else: return -balance return CleanDecimal(replace_dots=True).filter( self.el.xpath('./td[3]')) @property def obj_id(self): # Investment account and main account can have the same id # so we had account type in case of Investment to prevent conflict if Field('type')(self) == Account.TYPE_MARKET: return CleanText(replace=[('.', ''), (' ', '')]).filter( self.el.xpath('./td[2]')) + ".INVEST" return CleanText(replace=[('.', ''), (' ', '')]).filter( self.el.xpath('./td[2]'))
class item(ItemElement): klass = Account def condition(self): return len( self.el.xpath('./td') ) > 2 and "en opposition" not in CleanText('./td[1]')(self) # Some accounts have no <a> in the first <td> def obj_label(self): if self.el.xpath('./td[1]/a'): return Label( CleanText('./td[1]/a'))(self) or 'Compte sans libellé' return Label( CleanText('./td[1]'))(self) or 'Compte sans libellé' obj_coming = Env('coming') obj_currency = FrenchTransaction.Currency('./td[2]') def obj_url(self): # Accounts without an <a> in the <td> have no link if self.el.xpath('./td[1]/a'): return CleanText(AbsoluteLink('./td[1]/a'), default=None, replace=[('\n', '')])(self) return None obj_type = AccountsType(Field('label')) obj_coming = NotAvailable @property def obj_balance(self): if self.el.xpath('./parent::*/tr/th') and self.el.xpath( './parent::*/tr/th')[0].text in ['Credits', 'Crédits']: return CleanDecimal(replace_dots=True, sign=lambda x: -1).filter( self.el.xpath('./td[3]')) return CleanDecimal(replace_dots=True).filter( self.el.xpath('./td[3]')) @property def obj_id(self): # Investment account and main account can have the same id # so we had account type in case of Investment to prevent conflict # and also the same problem with scpi accounts. if "Scpi" in Field('label')(self): return CleanText(replace=[('.', ''), (' ', '')]).filter( self.el.xpath('./td[2]')) + ".SCPI" if Field('type')(self) == Account.TYPE_MARKET: return CleanText(replace=[('.', ''), (' ', '')]).filter( self.el.xpath('./td[2]')) + ".INVEST" return CleanText(replace=[('.', ''), (' ', '')]).filter( self.el.xpath('./td[2]'))
class item(ItemElement): klass = Account load_details = Field('_link') & AsyncLoad obj_id = Async('details') & Regexp( CleanText('//h3[has-class("account-number")]'), r'(\d+)') obj_label = CleanText( './/a[@class="account--name"] | .//div[@class="account--name"]' ) obj_balance = CleanDecimal('.//a[has-class("account--balance")]', replace_dots=True) obj_currency = FrenchTransaction.Currency( './/a[has-class("account--balance")]') obj_valuation_diff = Async('details') & CleanDecimal( '//li[h4[text()="Total des +/- values"]]/h3 |\ //li[span[text()="Total des +/- values latentes"]]/span[has-class("overview__value")]', replace_dots=True, default=NotAvailable) obj_coming = Async('details') & CleanDecimal( u'//li[h4[text()="Mouvements à venir"]]/h3', replace_dots=True, default=NotAvailable) obj__card = Async('details') & Attr( '//a[@data-modal-behavior="credit_card-modal-trigger"]', 'href', default=NotAvailable) obj__holder = None obj__webid = None def obj_type(self): return self.page.ACCOUNT_TYPES.get( CleanText( './preceding-sibling::tr[has-class("list--accounts--master")]//h4' )(self), Account.TYPE_UNKNOWN) def obj__link(self): link = Attr('.//a[@class="account--name"] | .//a[2]', 'href', default=NotAvailable)(self) if not self.page.browser.webid: self.page.browser.webid = re.search( '\/([^\/|?|$]{32})(\/|\?|$)', link).group(1) return link # We do not yield other banks accounts for the moment. def validate(self, obj): return not Async( 'details', CleanText( u'//h4[contains(text(), "Établissement bancaire")]'))( self)
def fill(self, acc): self.send_form(acc) ele = self.browser.page.doc.xpath('.//table[@class="fiche bourse"]')[0] balance = CleanDecimal( ele.xpath('.//td[contains(@id, "Valorisation")]'), default=Decimal(0), replace_dots=True)(ele) acc.balance = balance + acc.balance if acc.balance else balance acc.currency = FrenchTransaction.Currency('.')(ele) acc.valuation_diff = CleanDecimal( ele.xpath('.//td[contains(@id, "Variation")]'), default=Decimal(0), replace_dots=True)(ele)
class item(ItemElement): klass = Account TYPE = { 'Livret': Account.TYPE_SAVINGS, 'Compte': Account.TYPE_CHECKING, 'PEA': Account.TYPE_PEA, 'PEA-PME': Account.TYPE_PEA, 'Compte-titres': Account.TYPE_MARKET, 'Assurance-vie': Account.TYPE_LIFE_INSURANCE, 'Crédit': Account.TYPE_LOAN, } obj_id = CleanText( './td//div[contains(@class, "-synthese-title") or contains(@class, "-synthese-text")]' ) & Regexp(pattern=r'(\d+)') obj_label = CleanText( './td//div[contains(@class, "-synthese-title")]') obj_balance = MyDecimal( './td//div[contains(@class, "-synthese-num")]', replace_dots=True) obj_currency = FrenchTransaction.Currency( './td//div[contains(@class, "-synthese-num")]') obj_type = Map(Regexp(Field('label'), r'^([^ ]*)'), TYPE, default=Account.TYPE_UNKNOWN) def obj_url(self): return urljoin(self.page.url, CleanText('./@data-href')(self)) obj__card_balance = CleanDecimal( './td//div[@class="synthese-encours"][last()]/div[2]', default=None) def condition(self): return not len(self.el.xpath('./td[@class="chart"]')) def obj_ownership(self): owner = CleanText( './td//div[contains(@class, "-synthese-text") and not(starts-with(., "N°"))]', default=None)(self) if owner: if re.search( r'(m|mr|me|mme|mlle|mle|ml)\.? (.*)\bou (m|mr|me|mme|mlle|mle|ml)\b(.*)', owner, re.IGNORECASE): return AccountOwnership.CO_OWNER elif all(n in owner.upper() for n in self.env['name'].split()): return AccountOwnership.OWNER return AccountOwnership.ATTORNEY
class item(ItemElement): klass = Account load_details = Attr('.//a', 'href') & AsyncLoad obj__link_id = Async( 'details', Link('//li/a[contains(text(), "Mouvements")]')) obj__link_inv = Link('./td[1]/a') obj_id = CleanText('./td[2]', replace=[(' ', '')]) obj_label = CleanText('./td[1]') obj_balance = CleanDecimal('./td[3]', replace_dots=True) obj_currency = FrenchTransaction.Currency('./td[4]') obj__card_links = [] obj_type = Account.TYPE_LIFE_INSURANCE obj__is_inv = True
def populate(self, accounts): cards = [] for account in accounts: for li in self.doc.xpath('//li[@class="nav-category"]'): title = CleanText().filter(li.xpath('./h3')) for a in li.xpath('./ul/li//a'): label = CleanText().filter( a.xpath('.//span[@class="nav-category__name"]')) balance_el = a.xpath( './/span[@class="nav-category__value"]') balance = CleanDecimal( replace_dots=True, default=NotAvailable).filter(balance_el) if 'CARTE' in label and balance: acc = Account() acc.balance = balance acc.label = label acc.currency = FrenchTransaction.Currency().filter( balance_el) acc._link = Link().filter(a.xpath('.')) acc._history_page = acc._link acc.id = acc._webid = Regexp( pattern='([^=]+)$').filter(Link().filter( a.xpath('.'))) acc.type = Account.TYPE_CARD if not acc in cards: cards.append(acc) elif account.label == label and account.balance == balance: if not account.type: account.type = AccountsPage.ACCOUNT_TYPES.get( title, Account.TYPE_UNKNOWN) if account.type == Account.TYPE_LOAN: account._history_page = None elif account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET): account._history_page = re.sub( '/$', '', Link().filter(a.xpath('.'))) elif '/compte/cav' in a.attrib[ 'href'] or not 'titulaire' in self.url: account._history_page = self.browser.other_transactions else: account._history_page = self.browser.budget_transactions account._webid = Attr( None, 'data-account-label').filter( a.xpath( './/span[@class="nav-category__name"]')) accounts.extend(cards)
class item(ItemElement): klass = Account def condition(self): return CleanDecimal(TableCell('balance'), replace_dots=True, default=NotAvailable)(self) is not NotAvailable TYPE = { 'COMPTE COURANT': Account.TYPE_CHECKING, 'COMPTE TRANSACTION': Account.TYPE_CHECKING, 'COMPTE ORDINAIRE': Account.TYPE_CHECKING, } TYPE_BY_LABELS = { 'CAV': Account.TYPE_CHECKING, } obj_id = CleanText(TableCell('id')) obj_label = CleanText(TableCell('label')) obj_currency = FrenchTransaction.Currency(TableCell('currency')) obj_balance = CleanDecimal(TableCell('balance'), replace_dots=True) def obj__link(self): return Link(TableCell('id')(self)[0].xpath('./a'), default=None)(self) def obj__url(self): return Link(TableCell('rib')(self)[0].xpath('./a[img[starts-with(@alt, "RIB")]]'), default=None)(self) def load_iban(self): link = Link(TableCell('rib')(self)[0].xpath('./a[img[starts-with(@alt, "RIB")]]'), default=None)(self) return self.page.browser.async_open(link) def obj_type(self): try: el_to_check = CleanText(TableCell('type'))(self) type_dict = self.TYPE except ColumnNotFound: el_to_check = Field('label')(self) type_dict = self.TYPE_BY_LABELS for k, v in type_dict.items(): if el_to_check.startswith(k): return v return Account.TYPE_UNKNOWN def obj_iban(self): rib_page = Async('iban').loaded_page(self) if 'RibPdf' in rib_page.url: return rib_page.get_iban() return Join('', Regexp(CleanText('//td[has-class("ColonneCode")][contains(text(), "IBAN")]'), r'\b((?!IBAN)[A-Z0-9]+)\b', nth='*'))(rib_page.doc) or NotAvailable
def create_transfer(self, account, recipient, amount, reason): transfer = Transfer() transfer.currency = FrenchTransaction.Currency('//div[@class="topBox"]/div[@class="montant"]')(self.doc) transfer.amount = CleanDecimal('//div[@class="topBox"]/div[@class="montant"]', replace_dots=True)(self.doc) transfer.account_iban = account.iban transfer.recipient_iban = recipient.iban transfer.account_id = account.id transfer.recipient_id = recipient.id transfer.exec_date = date.today() transfer.label = reason transfer.account_label = account.label transfer.recipient_label = recipient.label transfer._account = account transfer._recipient = recipient transfer.account_balance = account.balance return transfer
class item(ItemElement): klass = Account obj__owner = TableCell('owner') & CleanText obj__link_id = Format('pro/%s', Link('./td[2]/a')) obj_id = Field('_link_id') & Regexp(pattern='ctr=(\d+)') obj_label = Format('%s %s', CleanText(TableCell('card')), Field('_owner')) obj_balance = NotAvailable obj_currency = FrenchTransaction.Currency('./td[3]') obj_type = Account.TYPE_CARD obj__card_links = [] obj__is_inv = False obj__is_webid = False def obj__pre_link(self): return self.page.url
class item(MyRecipient): obj_category = u'Interne' obj_currency = FrenchTransaction.Currency('.//span[@class="solde"]/label') obj_id = Env('id') obj_label = Env('label') obj_iban = Env('iban') obj_bank_name = u'ING' def parse(self, el): _id = Attr('.', 'data-acct-number')(self) accounts = [acc for acc in self.page.browser.get_accounts_list() if _id in acc.id] assert len(accounts) == 1 account = accounts[0] self.env['id'] = account.id self.env['label'] = account.label self.env['iban'] = account.iban
class item(ItemElement): klass = Account def condition(self): if len(self.el.xpath('./td')) < 6: return False return True obj__title = CleanText('td[@id="idCompteIntitule"]') obj__nature = CleanText('td[@id="idCompteNature"]') obj_label = Format('%s %s', Field('_title'), Field('_nature')) obj_currency = FrenchTransaction.Currency( './td[@id="idCompteSoldeUM"]') obj_id = CleanText('td[@id="idCompteLibelle"]/a') obj_balance = CleanDecimal('td[@id="idCompteSolde"]', replace_dots=True) obj__link = Link('td[@id="idCompteLibelle"]/a')
class item(ItemElement): klass = Account obj_label = CleanText('./td[1]') obj_id = CleanText('./td[2]', replace=[(' ', '')]) obj__agency = CleanText('./td[3]') obj_balance = CleanDecimal('./td[4]', replace_dots=True) obj_currency = FrenchTransaction.Currency('./td[4]') def obj_type(self): for wording, acc_type in self.page.TYPES.iteritems(): if wording in Field('label')(self): return acc_type return Account.TYPE_UNKNOWN def condition(self): return Field('label')(self) and len(self.el.xpath('./td')) > 1
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 not empty(balance): acc = Account() acc.balance = balance acc.label = label acc.currency = FrenchTransaction.Currency().filter( balance_el) acc.url = urljoin(self.url, Link().filter(a.xpath('.'))) acc._history_page = acc.url try: acc.id = acc._webid = Regexp( pattern='carte/(.*)$').filter(Link().filter( a.xpath('.'))) except RegexpError: # Those are external cards, ie: amex cards continue 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"]')) if cards: self.browser.go_cards_number(cards[0].url) if self.browser.cards.is_here(): self.browser.page.populate_cards_number(cards) accounts.extend(cards)
class item(ItemElement): klass = FrenchTransaction obj_date = obj_rdate = Transaction.Date(CleanText('./td[1]')) obj_raw = CleanText('./td[2]') obj_amount = CleanDecimal('./td[4]', replace_dots=True, default=Decimal('0')) obj_original_currency = FrenchTransaction.Currency('./td[4]') obj_type = Transaction.TYPE_BANK obj__is_coming = False def obj_commission(self): gross_amount = CleanDecimal('./td[3]', replace_dots=True, default=NotAvailable)(self) if gross_amount: return gross_amount - Field('amount')(self) return NotAvailable
class item(ItemElement): klass = FrenchTransaction obj_rdate = FrenchTransaction.Date(CleanText('./td[1]')) obj_date = FrenchTransaction.Date(CleanText('./td[3]')) obj_raw = FrenchTransaction.Raw(CleanText('./td[2]')) _obj_amnt = FrenchTransaction.Amount(CleanText('./td[5]'), replace_dots=False) obj_original_amount = FrenchTransaction.Amount(CleanText('./td[4]'), replace_dots=False) obj_original_currency = FrenchTransaction.Currency(CleanText('./td[4]')) obj_commission = FrenchTransaction.Amount(CleanText('./td[6]'), replace_dots=False) def obj__coming(self): if Field('date')(self) >= date.today(): return True def obj_amount(self): if not Field('obj_commission'): return Field('_obj_amnt') else: return CleanDecimal(replace_dots=False).filter(self.el.xpath('./td[5]')) - CleanDecimal(replace_dots=False).filter(self.el.xpath('./td[6]'))
class item(ItemElement): klass = Account def condition(self): return len(self.el.xpath('./td')) > 2 class Label(Filter): def filter(self, text): return text.lstrip(' 0123456789').title() class Type(Filter): def filter(self, label): return Account.TYPE_UNKNOWN obj_id = Env('id') obj_label = Label(CleanText('./td[1]/a')) obj_coming = Env('coming') obj_balance = Env('balance') obj_currency = FrenchTransaction.Currency('./td[2] | ./td[3]') obj__link_id = Link('./td[1]/a') obj_type = Type(Field('label')) def parse(self, el): link = el.xpath('./td[1]/a')[0].get('href', '') url = urlparse(link) p = parse_qs(url.query) if 'CPT_IdPrestation' in p: id = p['CPT_IdPrestation'][0] elif 'Ass_IdPrestation' in p: id = p['Ass_IdPrestation'][0] elif 'CB_IdPrestation' in p: id = p['CB_IdPrestation'][0] else: raise SkipItem() balance = CleanDecimal('./td[3]', replace_dots=True)(self) self.env['id'] = id self.env['balance'] = balance self.env['coming'] = NotAvailable
def create_transfer(self, account, recipient, transfer): transfer = Transfer() transfer.currency = FrenchTransaction.Currency( './/tr[td[contains(text(), "Montant")]]/td[not(@class)] | \ .//tr[th[contains(text(), "Montant")]]/td[not(@class)]' )(self.doc) transfer.amount = CleanDecimal( './/tr[td[contains(text(), "Montant")]]/td[not(@class)] | \ .//tr[th[contains(text(), "Montant")]]/td[not(@class)]', replace_dots=True)(self.doc) transfer.account_iban = account.iban if recipient.category == u'Externe': for word in Upper( CleanText( u'.//tr[th[contains(text(), "Compte à créditer")]]/td[not(@class)]' ))(self.doc).split(): if is_iban_valid(word): transfer.recipient_iban = word break else: raise TransferError('Unable to find IBAN (original was %s)' % recipient.iban) else: transfer.recipient_iban = recipient.iban transfer.account_id = unicode(account.id) transfer.recipient_id = unicode(recipient.id) transfer.exec_date = Date(CleanText( './/tr[th[contains(text(), "En date du")]]/td[not(@class)]'), dayfirst=True)(self.doc) transfer.label = CleanText( u'.//tr[td[contains(text(), "Motif de l\'opération")]]/td[not(@class)] | \ .//tr[td[contains(text(), "Libellé")]]/td[not(@class)]' )(self.doc) transfer.account_label = account.label transfer.recipient_label = recipient.label transfer._account = account transfer._recipient = recipient transfer.account_balance = account.balance return transfer
class item(ItemElement): klass = Account def condition(self): return len( self.el.xpath('.//div[@class="catre_col_two"]/h2')) > 0 def obj_balance(self): if len(self.el.xpath('.//div[@class="catre_col_one"]/h2')) > 0: return -CleanDecimal( CleanText('.//div[@class="catre_col_one"]/h2'), replace_dots=True)(self) else: return Decimal('0') obj_id = CleanText('.//div[@class="carte_col_leftcol"]/p' ) & Regexp(pattern=r'(\d+)') obj_label = CleanText('.//div[@class="carte_col_leftcol"]/h2') obj_currency = FrenchTransaction.Currency( './/div[@class="catre_col_two"]/h2') obj__link = Link( './/a[contains(@href, "solde-dernieres-operations")]')
class account(ItemElement): klass = Account obj_label = CleanText('./td[2]') obj_balance = CleanDecimal('./td[4]', replace_dots=True, sign=lambda x: -1) obj_currency = FrenchTransaction.Currency('./td[4]') obj_type = Account.TYPE_LOAN obj_id = Env('id') def parse(self, el): obj_label = CleanText('./td[2]')(el) xpath = self.xpath('//td[contains(text(), "%s")]/../../tr' % obj_label) for i in range(len(xpath)): if el == xpath[i]: self.env['id'] = Format( '%s%s%s', Regexp(CleanText('./td[1]'), r'(\w+)\s-\s(\w+)', r'\1\2'), CleanText('./td[2]', replace=[(' ', '')]), i)(self)
class account(ItemElement): klass = Account def condition(self): return '/outil/UWLM/ListeMouvement' in self.el.attrib[ 'onclick'] NATURE2TYPE = { '001': Account.TYPE_SAVINGS, '005': Account.TYPE_CHECKING, '006': Account.TYPE_CHECKING, '007': Account.TYPE_SAVINGS, '012': Account.TYPE_SAVINGS, '023': Account.TYPE_CHECKING, '046': Account.TYPE_SAVINGS, '047': Account.TYPE_SAVINGS, '049': Account.TYPE_SAVINGS, '058': Account.TYPE_CHECKING, '068': Account.TYPE_PEA, '069': Account.TYPE_SAVINGS, } obj__link_id = Format('%s&mode=190', Regexp(CleanText('./@onclick'), "'(.*)'")) obj__agence = Regexp(Field('_link_id'), r'.*agence=(\w+)') obj__compte = Regexp(Field('_link_id'), r'compte=(\w+)') obj_id = Format('%s%s', Field('_agence'), Field('_compte')) obj__transfer_id = Format('%s0000%s', Field('_agence'), Field('_compte')) obj__coming_links = [] obj_label = CleanText('.//div[@class="libelleCompte"]') obj_balance = MyDecimal('.//td[has-class("right")]', replace_dots=True) obj_currency = FrenchTransaction.Currency( './/td[has-class("right")]') obj_type = Map(Regexp(Field('_link_id'), r'.*nature=(\w+)'), NATURE2TYPE, default=Account.TYPE_UNKNOWN) obj__market_link = None
class item(ItemElement): klass = Account def obj_balance(self): if len(self.el.xpath('.//div[@class="catre_col_one"]/h2')) > 0: balance = CleanDecimal( CleanText('.//div[@class="catre_col_one"]/h2'), replace_dots=True)(self) return -balance if Field('type')( self) is Account.TYPE_CARD else balance return Decimal('0') def obj_type(self): return self.page.TYPES.get( Regexp(Field('_link'), '\/([^-]+)')(self), Account.TYPE_UNKNOWN) obj_id = CleanText('.//div[@class="carte_col_leftcol"]/p' ) & Regexp(pattern=r'(\d+)') obj_label = CleanText('.//div[@class="carte_col_leftcol"]/h2') obj_currency = FrenchTransaction.Currency( './/div[@class="catre_col_one"]/h2') obj__link = Link('.//a[contains(@href, "-operations")]')
class item(ItemElement): klass = Account TYPE = { 'Livret': Account.TYPE_SAVINGS, } def obj_balance(self): balance = CleanText( self.el.xpath('./td/div/div[1]/div/span'))(self) balance = re.sub(r'[^\d\-\,]', '', balance) return Decimal( re.sub(r',(?!(\d+$))', '', balance).replace(',', '.')) obj_id = CleanText('./td/div/div[3]/span') & Regexp( pattern=r'(\d+)') obj_label = CleanText('./td/div/div[2]/span') obj_currency = FrenchTransaction.Currency( './td/div/div[1]/div/span') obj_type = Map(Regexp(Field('label'), r'^(\w+)'), TYPE, default=Account.TYPE_UNKNOWN) obj__link = CleanText('./@data-href')