# -*- coding: utf-8 -*- # Copyright(C) 2012 Kevin Pouget # # This file is part of weboob. # # weboob is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # weboob is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with weboob. If not, see . from decimal import Decimal import re import time from weboob.tools.browser import BasePage from weboob.capabilities.bank import Account from weboob.tools.capabilities.bank.transactions import FrenchTransaction __all__ = ['LoginPage', 'AccountsPage', 'TransactionsPage', 'ComingTransactionsPage', 'CardTransactionsPage', 'ITransactionsPage'] class LoginPage(BasePage): def login(self, login, pin, strong_auth): form_nb = 1 if strong_auth else 0 indentType = "RENFORCE" if strong_auth else "MDP" self.browser.select_form(name='loginCoForm', nr=form_nb) self.browser['codeUtil'] = login.encode(self.browser.ENCODING) self.browser['motPasse'] = pin.encode(self.browser.ENCODING) assert self.browser['identType'] == indentType self.browser.submit(nologin=True) class AccountsPage(BasePage): ACCOUNT_TYPES = {u'COMPTE NEF': Account.TYPE_CHECKING} CPT_ROW_ID = 0 CPT_ROW_NAME = 1 CPT_ROW_NATURE = 2 CPT_ROW_BALANCE = 3 CPT_ROW_ENCOURS = 4 def is_error(self): for par in self.document.xpath('//p[@class=acctxtnoirlien]'): if par.text is not None and u"La page demandée ne peut pas être affichée." in par.text: return True return False def get_list(self): for trCompte in self.document.xpath('//table[@id="compte"]/tbody/tr'): tds = trCompte.findall('td') account = Account() account.id = tds[self.CPT_ROW_ID].text.strip() account.label = unicode(tds[self.CPT_ROW_NAME].text.strip()) account_type_str = "".join([td.text for td in tds[self.CPT_ROW_NATURE].xpath('.//td[@class="txt"]')]).strip() account.type = self.ACCOUNT_TYPES.get(account_type_str, Account.TYPE_UNKNOWN) account.balance = Decimal(FrenchTransaction.clean_amount(self.parser.tocleanstring(tds[self.CPT_ROW_BALANCE]))) account.coming = Decimal(FrenchTransaction.clean_amount(self.parser.tocleanstring( tds[self.CPT_ROW_ENCOURS]))) account.currency = account.get_currency(tds[self.CPT_ROW_BALANCE].find("a").text) yield account return class Transaction(FrenchTransaction): PATTERNS = [(re.compile('^RETRAIT DAB (?P.*?).*'), FrenchTransaction.TYPE_WITHDRAWAL), (re.compile('^(?P.*) RETRAIT DU (?P
\d{2})(?P\d{2})(?P\d{2}) .*'), FrenchTransaction.TYPE_WITHDRAWAL), (re.compile('^CARTE \d+ .*'), FrenchTransaction.TYPE_CARD), (re.compile('^VIR(EMENT)? (?P.*)'), FrenchTransaction.TYPE_TRANSFER), (re.compile('^(PRLV|PRELEVEMENT) (?P.*)'), FrenchTransaction.TYPE_ORDER), (re.compile('^CHEQUE.*'), FrenchTransaction.TYPE_CHECK), (re.compile('^(AGIOS /|FRAIS) (?P.*)'), FrenchTransaction.TYPE_BANK), (re.compile('^ABONNEMENT (?P.*)'), FrenchTransaction.TYPE_BANK), (re.compile('^REMISE (?P.*)'), FrenchTransaction.TYPE_DEPOSIT), (re.compile('^(?P.*)( \d+)? QUITTANCE .*'), FrenchTransaction.TYPE_ORDER), (re.compile('^.* LE (?P
\d{2})/(?P\d{2})/(?P\d{2})$'), FrenchTransaction.TYPE_UNKNOWN), ] class ITransactionsPage(BasePage): def get_next_url(self): # can be 'Suivant' or ' Suivant' next = self.document.xpath("//a[normalize-space(text()) = 'Suivant']") if not next: return None return next[0].attrib["href"] def get_history(self): raise NotImplementedError() class TransactionsPage(ITransactionsPage): TR_DATE = 0 TR_TEXT = 2 TR_DEBIT = 3 TR_CREDIT = 4 TABLE_NAME = 'operation' def get_history(self): for tr in self.document.xpath('//table[@id="%s"]/tbody/tr' % self.TABLE_NAME): tds = tr.findall('td') def get_content(td): ret = self.parser.tocleanstring(td) return ret.replace(u"\xa0", " ").strip() date = get_content(tds[self.TR_DATE]) raw = get_content(tds[self.TR_TEXT]) debit = get_content(tds[self.TR_DEBIT]) credit = get_content(tds[self.TR_CREDIT]) t = Transaction(date+""+raw) t.parse(date, re.sub(r'[ ]+', ' ', raw)) t.set_amount(credit, debit) yield t class ComingTransactionsPage(TransactionsPage): TR_DATE = 2 TR_TEXT = 1 TR_DEBIT = -2 TR_CREDIT = -1 TABLE_NAME = 'operationAVenir' class CardTransactionsPage(ITransactionsPage): COM_TR_COMMENT = 0 COM_TR_DATE = 1 COM_TR_TEXT = 2 COM_TR_VALUE = 3 def get_history(self): comment = None for tr in self.document.xpath('//table[@id="operation"]/tbody/tr'): tds = tr.findall('td') def get_content(td): ret = td.text return ret.replace(u"\xa0", " ").strip() raw = get_content(tds[self.COM_TR_TEXT]) if comment is None: comment = get_content(tds[self.COM_TR_COMMENT]) raw = "%s (%s) " % (raw, comment) debit = get_content(tds[self.COM_TR_VALUE]) date = get_content(tds[self.COM_TR_DATE]) if comment is not None: #date is 'JJ/MM'. add '/YYYY' date += comment[comment.rindex("/"):] else: date += "/%d" % time.localtime().tm_year t = Transaction(date+""+raw) t.parse(date, re.sub(r'[ ]+', ' ', raw)) t.set_amount("", debit) yield t