diff --git a/modules/cmso/module.py b/modules/cmso/module.py index 546d171fdb9d61af37acee54e6920925923e0e28..7bdf9299f89e30ab8456e0b4db3926cffb040350 100644 --- a/modules/cmso/module.py +++ b/modules/cmso/module.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . +# flake8: compatible + from __future__ import unicode_literals from weboob.capabilities.bank import CapBankTransfer, Account, AccountNotFound, RecipientNotFound @@ -41,23 +43,40 @@ class CmsoModule(Module, CapBankTransfer, CapBankWealth, CapContact, CapProfile) VERSION = '2.1' DESCRIPTION = 'Crédit Mutuel Sud-Ouest' LICENSE = 'LGPLv3+' - CONFIG = BackendConfig(ValueBackendPassword('login', label='Identifiant', masked=False), - ValueBackendPassword('password', label='Mot de passe'), - ValueTransient('code'), - ValueTransient('request_information'), - Value('website', label='Type de compte', default='par', - choices={'par': 'Particuliers', 'pro': 'Professionnels'})) + CONFIG = BackendConfig( + ValueBackendPassword('login', label='Identifiant', masked=False), + ValueBackendPassword('password', label='Mot de passe'), + ValueTransient('code'), + ValueTransient('request_information'), + Value( + 'website', + label='Type de compte', + default='par', + choices={ + 'par': 'Particuliers', + 'pro': 'Professionnels', + } + ) + ) BROWSER = CmsoParBrowser AVAILABLE_BROWSERS = {'par': CmsoParBrowser, 'pro': CmsoProBrowser} def create_default_browser(self): self.BROWSER = self.AVAILABLE_BROWSERS[self.config['website'].get()] - return self.create_browser("%s.%s" % (self.NAME, 'com' if self.NAME == 'cmso' else 'fr'), - self.config, - self.config['login'].get(), - self.config['password'].get(), - weboob=self.weboob) + + if self.NAME == 'cmso': + tld = 'com' + else: + tld = 'fr' + + return self.create_browser( + "%s.%s" % (self.NAME, tld), + self.config, + self.config['login'].get(), + self.config['password'].get(), + weboob=self.weboob + ) def get_account(self, _id): return find_object(self.browser.iter_accounts(), id=_id, error=AccountNotFound) diff --git a/modules/cmso/par/browser.py b/modules/cmso/par/browser.py index cd4ab43a2fba91d1a7c286036b202ceef7272bc8..bf0ea2f4619bef8a0a48c5bb303ac759eb160082 100644 --- a/modules/cmso/par/browser.py +++ b/modules/cmso/par/browser.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . +# flake8: compatible + from __future__ import unicode_literals import time @@ -54,7 +56,8 @@ def retry(exc_check, tries=4): def decorator(func): @wraps(func) def wrapper(browser, *args, **kwargs): - cb = lambda: func(browser, *args, **kwargs) + def cb(): + return func(browser, *args, **kwargs) for i in range(tries, 0, -1): try: @@ -102,9 +105,12 @@ class CmsoParBrowser(TwoFactorBrowser): RedirectInsurancePage ) lifeinsurance = URL(r'https://domiweb.suravenir.fr', LifeinsurancePage) - market = URL(r'/domiapi/oauth/json/ssoDomifronttitre', - r'https://www.(?P.*)/domifronttitre/front/sso/domiweb/01/(?P.*)Portefeuille\?csrf=', - r'https://www.*/domiweb/prive/particulier', MarketPage) + market = URL( + r'/domiapi/oauth/json/ssoDomifronttitre', + r'https://www.(?P.*)/domifronttitre/front/sso/domiweb/01/(?P.*)Portefeuille\?csrf=', + r'https://www.*/domiweb/prive/particulier', + MarketPage + ) advisor = URL(r'/edrapi/v(?P\w+)/oauth/(?P\w+)', AdvisorPage) transfer_info = URL(r'/domiapi/oauth/json/transfer/transferinfos', TransferInfoPage) @@ -112,7 +118,10 @@ class CmsoParBrowser(TwoFactorBrowser): # recipients ext_recipients_list = URL(r'/transfersfedesapi/api/beneficiaries', RecipientsListPage) int_recipients_list = URL(r'/transfersfedesapi/api/accounts', RecipientsListPage) - available_int_recipients = URL(r'/transfersfedesapi/api/credited-accounts/(?P.*)', AllowedRecipientsPage) + available_int_recipients = URL( + r'/transfersfedesapi/api/credited-accounts/(?P.*)', + AllowedRecipientsPage + ) # transfers init_transfer_page = URL(r'/transfersfedesapi/api/transfers/control', TransferPage) @@ -149,7 +158,7 @@ def init_login(self): if self.headers: self.session.headers = self.headers else: - self.set_profile(self.PROFILE) # reset headers but don't clear them + self.set_profile(self.PROFILE) # reset headers but don't clear them self.session.cookies.clear() self.accounts_list = [] @@ -166,7 +175,7 @@ def send_sms(self): data = { 'template': '', 'typeMedia': 'SMS', # can be SVI for interactive voice server - 'valueMedia': contact_information['portable']['numeroCrypte'] + 'valueMedia': contact_information['portable']['numeroCrypte'], } self.location('/securityapi/otp/generate', json=data) @@ -192,7 +201,7 @@ def get_sms_data(self): 'accessInfos': { 'efs': self.arkea, 'si': self.arkea_si, - } + }, } def get_login_data(self): @@ -355,7 +364,7 @@ def iter_history(self, account): self.history.go( json={ 'index': account._index, - 'filtreOperationsComptabilisees': "MOIS_MOINS_UN" + 'filtreOperationsComptabilisees': "MOIS_MOINS_UN", }, page="detailcompte" ) @@ -394,9 +403,9 @@ def iter_coming(self, account): if hasattr(c, '_deferred_date'): c.bdate = c.rdate c.date = c._deferred_date - c.type = Transaction.TYPE_DEFERRED_CARD # force deferred card type for comings inside cards + c.type = Transaction.TYPE_DEFERRED_CARD # force deferred card type for comings inside cards - c.vdate = None # vdate don't work for comings + c.vdate = None # vdate don't work for comings comings.append(c) return iter(comings) @@ -612,8 +621,7 @@ def __next__(self): # recreated iterator, consume previous items try: - nb = -1 - for nb, sent in enumerate(self.items): + for sent in self.items: new = next(self.it) if hasattr(new, 'iter_fields'): equal = dict(sent.iter_fields()) == dict(new.iter_fields()) @@ -623,7 +631,7 @@ def __next__(self): # safety is not guaranteed raise BrowserUnavailable('Site replied inconsistently between retries, %r vs %r', sent, new) except StopIteration: - raise BrowserUnavailable('Site replied fewer elements (%d) than last iteration (%d)', nb + 1, len(self.items)) + raise BrowserUnavailable('Site replied fewer elements than last iteration') except self.exc_check as exc: self.delogged = True if self.logger: diff --git a/modules/cmso/par/pages.py b/modules/cmso/par/pages.py index 1ef4de643e025262fa3875952ac35acf7b4479f5..e15084b1a87387db908325f1697da8f3d995263c 100644 --- a/modules/cmso/par/pages.py +++ b/modules/cmso/par/pages.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . +# flake8: compatible + from __future__ import unicode_literals import re @@ -88,10 +90,11 @@ def get_keys(self): def check_response(self): if "exception" in self.doc: - self.logger.warning("There are no checking accounts: exception '{}' with code {}".format( - self.doc['exception']['message'], - self.doc['exception']['code']) - ) + self.logger.warning( + "There are no checking accounts: exception %r with code %s", + self.doc['exception']['message'], + self.doc['exception']['code'] + ) def get_numbers(self): keys = self.get_keys() @@ -114,7 +117,7 @@ def find_elements(self): selector = self.item_xpath.split('/') for sub_element in selector: if isinstance(self.el, dict) and self.el and sub_element == '*': - self.el = next(iter(self.el.values())) # replace self.el with its first value + self.el = next(iter(self.el.values())) # replace self.el with its first value if sub_element == '*': continue self.el = self.el[sub_element] @@ -124,11 +127,12 @@ def find_elements(self): class item(ItemElement): klass = Account - condition = lambda self: "LIVRET" not in Dict('accountType')(self.el) + def condition(self): + return "LIVRET" not in Dict('accountType')(self.el) obj_id = Dict('numeroContratSouscrit') obj_label = Upper(Dict('lib')) - obj_currency = Dict('deviseCompteCode') + obj_currency = Dict('deviseCompteCode') obj_coming = CleanDecimal(Dict('AVenir', default=None), default=NotAvailable) # Iban is available without last 5 numbers, or by sms obj_iban = NotAvailable @@ -170,7 +174,9 @@ def obj__recipient_id(self): def obj_balance(self): balance = CleanDecimal(Dict('soldeEuro', default="0"))(self) - return -abs(balance) if Field('type')(self) == Account.TYPE_LOAN else balance + if Field('type')(self) == Account.TYPE_LOAN: + balance = -abs(balance) + return balance # It can have revolving credit on this page def obj__total_amount(self): @@ -305,7 +311,7 @@ def obj__recipient_id(self): # between the request in iter_accounts and the requests # listing recipients. Sorting the owner name is a way to # have the same md5 hash in both of those cases. - to_hash = '%s %s' % ( + to_hash = '%s %s' % ( Upper(Field('label'))(self), ''.join(sorted(Field('_owner_name')(self))), ) @@ -343,14 +349,14 @@ def obj_maturity_date(self): # Key not always available, when revolving credit not yet consummed timestamp = Dict('dateFin', default=None)(self) if timestamp: - return dt.date.fromtimestamp(timestamp/1000) + return dt.date.fromtimestamp(timestamp / 1000) return NotAvailable def obj_next_payment_date(self): # Key not always available, when revolving credit not yet consummed timestamp = Dict('dateProchaineEcheance', default=None)(self) if timestamp: - return dt.date.fromtimestamp(timestamp/1000) + return dt.date.fromtimestamp(timestamp / 1000) return NotAvailable def obj_balance(self): @@ -366,16 +372,17 @@ def obj_ownership(self): class Transaction(FrenchTransaction): - PATTERNS = [(re.compile(r'^CARTE (?P
\d{2})/(?P\d{2}) (?P.*)'), FrenchTransaction.TYPE_CARD), - (re.compile(r'^(?P(PRLV|PRELEVEMENTS).*)'), FrenchTransaction.TYPE_ORDER), - (re.compile(r'^(?PRET DAB.*)'), FrenchTransaction.TYPE_WITHDRAWAL), - (re.compile(r'^(?PECH.*)'), FrenchTransaction.TYPE_LOAN_PAYMENT), - (re.compile(r'^(?PVIR.*)'), FrenchTransaction.TYPE_TRANSFER), - (re.compile(r'^(?PANN.*)'), FrenchTransaction.TYPE_PAYBACK), - (re.compile(r'^(?P(VRST|VERSEMENT).*)'), FrenchTransaction.TYPE_DEPOSIT), - (re.compile(r'^(?PCHQ.*)'), FrenchTransaction.TYPE_CHECK), - (re.compile(r'^(?P.*)'), FrenchTransaction.TYPE_BANK) - ] + PATTERNS = [ + (re.compile(r'^CARTE (?P
\d{2})/(?P\d{2}) (?P.*)'), FrenchTransaction.TYPE_CARD), + (re.compile(r'^(?P(PRLV|PRELEVEMENTS).*)'), FrenchTransaction.TYPE_ORDER), + (re.compile(r'^(?PRET DAB.*)'), FrenchTransaction.TYPE_WITHDRAWAL), + (re.compile(r'^(?PECH.*)'), FrenchTransaction.TYPE_LOAN_PAYMENT), + (re.compile(r'^(?PVIR.*)'), FrenchTransaction.TYPE_TRANSFER), + (re.compile(r'^(?PANN.*)'), FrenchTransaction.TYPE_PAYBACK), + (re.compile(r'^(?P(VRST|VERSEMENT).*)'), FrenchTransaction.TYPE_DEPOSIT), + (re.compile(r'^(?PCHQ.*)'), FrenchTransaction.TYPE_CHECK), + (re.compile(r'^(?P.*)'), FrenchTransaction.TYPE_BANK), + ] class HistoryPage(LoggedPage, JsonPage): @@ -444,7 +451,7 @@ def parse(self, el): deferred_date = Dict('dateDiffere', default=None)(x) if deferred_date: break - setattr(self.obj, '_deferred_date', self.FromTimestamp().filter(deferred_date)) + self.obj._deferred_date = self.FromTimestamp().filter(deferred_date) class RedirectInsurancePage(LoggedPage, JsonPage): @@ -485,7 +492,11 @@ def obj_url(self): @method class fill_account(ItemElement): def obj_valuation_diff_ratio(self): - valuation_diff_percent = CleanDecimal.French('//div[@class="perfContrat"]/span[@class="value"]', default=None)(self) + valuation_diff_percent = CleanDecimal.French( + '//div[@class="perfContrat"]/span[@class="value"]', + default=None + )(self) + if valuation_diff_percent: return valuation_diff_percent / 100 return NotAvailable @@ -543,7 +554,8 @@ def obj_diff_ratio(self): class MarketPage(LoggedPage, HTMLPage): def find_account(self, acclabel, accowner): - accowner = sorted(accowner.lower().split()) # first name and last name may not be ordered the same way on market site... + # first name and last name may not be ordered the same way on market site... + accowner = sorted(accowner.lower().split()) def get_ids(ref, acclabel, accowner): ids = None @@ -564,8 +576,10 @@ def get_ids(ref, acclabel, accowner): return False ref = CleanText(self.doc.xpath('//a[contains(@href, "indiceCompte")]'))(self) - return get_ids('onclick', acclabel, accowner) if not ref else get_ids('href', acclabel, accowner) - + if not ref: + return get_ids('onclick', acclabel, accowner) + else: + return get_ids('href', acclabel, accowner) def get_account_id(self, acclabel, owner): account = self.find_account(acclabel, owner) @@ -638,7 +652,8 @@ class iter_investment(TableElement): class item(ItemElement): klass = Investment - condition = lambda self: not CleanText('//div[has-class("errorConteneur")]', default=None)(self.el) + def condition(self): + return not CleanText('//div[has-class("errorConteneur")]', default=None)(self.el) obj_label = Upper(TableCell('label')) obj_quantity = CleanDecimal.French(TableCell('quantity'), default=NotAvailable) @@ -681,11 +696,13 @@ class get_profile(ItemElement): klass = Profile def obj_id(self): - return (Dict('identifiantExterne',default=None)(self) - or Dict('login')(self)) + return ( + Dict('identifiantExterne', default=None)(self) + or Dict('login')(self) + ) obj_name = Format('%s %s', Dict('firstName'), Dict('lastName')) - obj_email = Dict('email', default=NotAvailable) # can be unavailable on pro website for example + obj_email = Dict('email', default=NotAvailable) # can be unavailable on pro website for example def get_token(self): return Dict('loginEncrypted')(self.doc) diff --git a/modules/cmso/par/transfer_pages.py b/modules/cmso/par/transfer_pages.py index c56be2300365c5ac8c1b1b19b77293fd0d6f27c2..fe3d5dd79a34ed0d402f2deeb3742aea2eb26d67 100644 --- a/modules/cmso/par/transfer_pages.py +++ b/modules/cmso/par/transfer_pages.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . +# flake8: compatible + from __future__ import unicode_literals from hashlib import md5 @@ -156,7 +158,8 @@ def find_elements(self): class item(ItemElement): klass = Emitter - condition = lambda self: Dict('eligibiliteDebit', default=None)(self.el) + def condition(self): + return Dict('eligibiliteDebit', default=None)(self.el) obj_id = Dict('numeroContratSouscrit') obj_label = Upper(Dict('lib')) @@ -173,8 +176,10 @@ def on_load(self): if self.doc.get('exception') and not self.doc.get('debitAccountOwner'): if Dict('exception/type')(self.doc) == 1: # technical error - assert False, 'Error with code %s occured during init_transfer: %s' % \ - (Dict('exception/code')(self.doc), Dict('exception/message')(self.doc)) + raise AssertionError( + 'Error with code %s occured during init_transfer: %s' + % (Dict('exception/code')(self.doc), Dict('exception/message')(self.doc)) + ) elif Dict('exception/type')(self.doc) == 2: # user error raise TransferBankError(message=Dict('exception/message')(self.doc)) diff --git a/modules/cmso/pro/browser.py b/modules/cmso/pro/browser.py index a1404844066924b77f5e21987e53a11de1e5c78b..9f975de7e548ae600336fe51a4a594c2cd330af7 100644 --- a/modules/cmso/pro/browser.py +++ b/modules/cmso/pro/browser.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . +# flake8: compatible + from __future__ import unicode_literals import datetime @@ -43,12 +45,24 @@ class CmsoProBrowser(LoginBrowser): login = URL(r'https://api.(?P[\w.]+)/oauth-implicit/token', LoginPage) subscription = URL(r'https://api.(?P[\w.]+)/domiapi/oauth/json/accesAbonnement', SubscriptionPage) - accounts = URL(r'/domiweb/prive/professionnel/situationGlobaleProfessionnel/0-situationGlobaleProfessionnel.act', AccountsPage) - history = URL(r'/domiweb/prive/professionnel/situationGlobaleProfessionnel/1-situationGlobaleProfessionnel.act', HistoryPage) - password_creation = URL(r'/domiweb/prive/particulier/modificationMotDePasse/0-creationMotDePasse.act', PasswordCreationPage) + accounts = URL( + r'/domiweb/prive/professionnel/situationGlobaleProfessionnel/0-situationGlobaleProfessionnel.act', + AccountsPage + ) + history = URL( + r'/domiweb/prive/professionnel/situationGlobaleProfessionnel/1-situationGlobaleProfessionnel.act', + HistoryPage + ) + password_creation = URL( + r'/domiweb/prive/particulier/modificationMotDePasse/0-creationMotDePasse.act', + PasswordCreationPage + ) useless = URL(r'/domiweb/prive/particulier/modificationMotDePasse/0-expirationMotDePasse.act', UselessPage) investment = URL(r'/domiweb/prive/particulier/portefeuilleSituation/0-situationPortefeuille.act', InvestmentPage) - invest_account = URL(r'/domiweb/prive/particulier/portefeuilleSituation/2-situationPortefeuille.act\?(?:csrf=[^&]*&)?indiceCompte=(?P\d+)&idRacine=(?P\d+)', InvestmentAccountPage) + invest_account = URL( + r'/domiweb/prive/particulier/portefeuilleSituation/2-situationPortefeuille.act\?(?:csrf=[^&]*&)?indiceCompte=(?P\d+)&idRacine=(?P\d+)', + InvestmentAccountPage + ) error = URL(r'https://pro.(?P[\w.]+)/auth/errorauthn', ErrorPage) profile = URL(r'https://api.(?P[\w.]+)/domiapi/oauth/json/edr/infosPerson', ProfilePage) ssoDomiweb = URL(r'https://api.(?P[\w.]+)/domiapi/oauth/json/ssoDomiwebEmbedded', SSODomiPage) @@ -201,10 +215,14 @@ def iter_accounts(self): def _build_next_date_range(self, date_range): date_format = '%d/%m/%Y' + last_day = datetime.datetime.strptime(date_range[10:], date_format) first_day = last_day + datetime.timedelta(days=1) last_day = first_day + relativedelta(months=1, days=-1) - return ''.join((datetime.datetime.strftime(first_day, date_format), datetime.datetime.strftime(last_day, date_format))) + + first_str = datetime.datetime.strftime(first_day, date_format) + last_str = datetime.datetime.strftime(last_day, date_format) + return first_str + last_str @need_login def iter_history(self, account): diff --git a/modules/cmso/pro/pages.py b/modules/cmso/pro/pages.py index 7a7df7d38ade8f50b79ecd85d16d0e32e0e778ad..32aed111505fb6e6cae930b86321d458d929d653 100644 --- a/modules/cmso/pro/pages.py +++ b/modules/cmso/pro/pages.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . +# flake8: compatible + from __future__ import unicode_literals import re @@ -24,7 +26,9 @@ from weboob.exceptions import BrowserIncorrectPassword from weboob.browser.pages import HTMLPage, JsonPage, pagination, LoggedPage from weboob.browser.elements import ListElement, ItemElement, TableElement, method -from weboob.browser.filters.standard import CleanText, CleanDecimal, DateGuesser, Env, Field, Filter, Regexp, Currency, Date +from weboob.browser.filters.standard import ( + CleanText, CleanDecimal, DateGuesser, Env, Field, Filter, Regexp, Currency, Date, +) from weboob.browser.filters.html import Link, Attr, TableCell from weboob.capabilities.bank import Account from weboob.capabilities.wealth import Investment @@ -68,11 +72,12 @@ def logged(self): class AccountsPage(CMSOPage): - TYPES = {'COMPTE CHEQUES': Account.TYPE_CHECKING, - 'COMPTE TITRES': Account.TYPE_MARKET, - "ACTIV'EPARGNE": Account.TYPE_SAVINGS, - "TRESO'VIV": Account.TYPE_SAVINGS, - } + TYPES = { + 'COMPTE CHEQUES': Account.TYPE_CHECKING, + 'COMPTE TITRES': Account.TYPE_MARKET, + "ACTIV'EPARGNE": Account.TYPE_SAVINGS, + "TRESO'VIV": Account.TYPE_SAVINGS, + } @method class iter_accounts(ListElement): @@ -107,8 +112,7 @@ def validate(self, obj): def on_load(self): if self.doc.xpath('//p[contains(text(), "incident technique")]'): - raise BrowserIncorrectPassword("Vous n'avez aucun compte sur cet espace. " \ - "Veuillez choisir un autre type de compte.") + raise BrowserIncorrectPassword("Vous n'avez aucun compte sur cet espace. Veuillez choisir un autre type de compte.") class InvestmentPage(CMSOPage): @@ -123,8 +127,12 @@ class item(ItemElement): klass = Account def obj_id(self): - area_id = Regexp(CleanText('(./preceding-sibling::tr[@class="LnMnTiers"][1])//span[@class="CelMnTiersT1"]'), - r'\((\d+)\)', default='')(self) + area_id = Regexp( + CleanText('(./preceding-sibling::tr[@class="LnMnTiers"][1])//span[@class="CelMnTiersT1"]'), + r'\((\d+)\)', + default='' + )(self) + acc_id = Regexp(CleanText('./td[1]'), r'(\d+)\s*(\d+)', r'\1\2')(self) if area_id: return '%s.%s' % (area_id, acc_id) @@ -177,33 +185,38 @@ class item(ItemElement): def obj_code(self): if Field('label')(self) == "LIQUIDITES": return 'XX-liquidity' + code = CleanText(TableCell('code'))(self) - return code if is_isin_valid(code) else NotAvailable + if is_isin_valid(code): + return code + return NotAvailable def obj_code_type(self): - return Investment.CODE_TYPE_ISIN if is_isin_valid(Field('code')(self)) else NotAvailable + if is_isin_valid(Field('code')(self)): + return Investment.CODE_TYPE_ISIN + return NotAvailable class Transaction(FrenchTransaction): - PATTERNS = [(re.compile(r'^RET DAB (?P
\d{2})/?(?P\d{2})(/?(?P\d{2}))? (?P.*)'), - FrenchTransaction.TYPE_WITHDRAWAL), - (re.compile(r'CARTE (?P
\d{2})/(?P\d{2}) (?P.*)'), - FrenchTransaction.TYPE_CARD), - (re.compile(r'^(?PVIR(EMEN)?T? (SEPA)?(RECU|FAVEUR)?)( /FRM)?(?P.*)'), - FrenchTransaction.TYPE_TRANSFER), - (re.compile(r'^PRLV (?P.*)( \d+)?$'), FrenchTransaction.TYPE_ORDER), - (re.compile(r'^(CHQ|CHEQUE) .*$'), FrenchTransaction.TYPE_CHECK), - (re.compile(r'^(AGIOS /|FRAIS) (?P.*)'), FrenchTransaction.TYPE_BANK), - (re.compile(r'^(CONVENTION \d+ |F )?COTIS(ATION)? (?P.*)'), - FrenchTransaction.TYPE_BANK), - (re.compile(r'^REMISE (?P.*)'), FrenchTransaction.TYPE_DEPOSIT), - (re.compile(r'^(?P.*)( \d+)? QUITTANCE .*'), - FrenchTransaction.TYPE_ORDER), - (re.compile(r'^.* LE (?P
\d{2})/(?P\d{2})/(?P\d{2})$'), - FrenchTransaction.TYPE_UNKNOWN), - (re.compile(r'^.* PAIEMENT (?P
\d{2})/(?P\d{2}) (?P.*)'), - FrenchTransaction.TYPE_UNKNOWN), - ] + PATTERNS = [ + ( + re.compile(r'^RET DAB (?P
\d{2})/?(?P\d{2})(/?(?P\d{2}))? (?P.*)'), + FrenchTransaction.TYPE_WITHDRAWAL, + ), + (re.compile(r'CARTE (?P
\d{2})/(?P\d{2}) (?P.*)'), FrenchTransaction.TYPE_CARD), + ( + re.compile(r'^(?PVIR(EMEN)?T? (SEPA)?(RECU|FAVEUR)?)( /FRM)?(?P.*)'), + FrenchTransaction.TYPE_TRANSFER, + ), + (re.compile(r'^PRLV (?P.*)( \d+)?$'), FrenchTransaction.TYPE_ORDER), + (re.compile(r'^(CHQ|CHEQUE) .*$'), FrenchTransaction.TYPE_CHECK), + (re.compile(r'^(AGIOS /|FRAIS) (?P.*)'), FrenchTransaction.TYPE_BANK), + (re.compile(r'^(CONVENTION \d+ |F )?COTIS(ATION)? (?P.*)'), FrenchTransaction.TYPE_BANK), + (re.compile(r'^REMISE (?P.*)'), FrenchTransaction.TYPE_DEPOSIT), + (re.compile(r'^(?P.*)( \d+)? QUITTANCE .*'), FrenchTransaction.TYPE_ORDER), + (re.compile(r'^.* LE (?P
\d{2})/(?P\d{2})/(?P\d{2})$'), FrenchTransaction.TYPE_UNKNOWN), + (re.compile(r'^.* PAIEMENT (?P
\d{2})/(?P\d{2}) (?P.*)'), FrenchTransaction.TYPE_UNKNOWN), + ] class CmsoTransactionElement(ItemElement): @@ -234,9 +247,11 @@ def next_page(self): return self.page.browser.build_request(url_next_page) class item(CmsoTransactionElement): - def date(selector): - return DateGuesser(Regexp(CleanText(selector), r'\w+ (\d{2}/\d{2})'), Env('date_guesser')) | Transaction.Date(selector) + return ( + DateGuesser(Regexp(CleanText(selector), r'\w+ (\d{2}/\d{2})'), Env('date_guesser')) + | Transaction.Date(selector) + ) # CAUTION: this website write a 'Date valeur' inside a div with a class == 'c-ope' # and a 'Date opération' inside a div with a class == 'c-val' @@ -260,4 +275,3 @@ def get_sso_url(self): class AuthCheckUser(HTMLPage): pass -