From dadcaf798ba91b2e66ef4f5842cf46a986f6478c Mon Sep 17 00:00:00 2001 From: Vincent Ardisson Date: Tue, 19 May 2020 15:13:59 +0200 Subject: [PATCH] [boursorama] fix style --- modules/boursorama/browser.py | 154 +++++++++---- modules/boursorama/module.py | 2 + modules/boursorama/pages.py | 332 +++++++++++++++++++-------- modules/boursorama/transfer_pages.py | 2 + 4 files changed, 346 insertions(+), 144 deletions(-) diff --git a/modules/boursorama/browser.py b/modules/boursorama/browser.py index 767e5fb7c9..0ef6be67c2 100644 --- a/modules/boursorama/browser.py +++ b/modules/boursorama/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 requests @@ -71,23 +73,29 @@ class BoursoramaBrowser(RetryLoginBrowser, TwoFactorBrowser): status = URL(r'/aide/messages/dashboard\?showza=0&_hinclude=1', StatusPage) calendar = URL('/compte/cav/.*/calendrier', CalendarPage) card_calendar = URL('https://api.boursorama.com/services/api/files/download.phtml.*', CardCalendarPage) - error = URL('/connexion/compte-verrouille', - '/infos-profil', ErrorPage) + error = URL( + '/connexion/compte-verrouille', + '/infos-profil', + ErrorPage + ) login = URL(r'/connexion/saisie-mot-de-passe/', PasswordPage) - accounts = URL('/dashboard/comptes\?_hinclude=300000', AccountsPage) - accounts_error = URL('/dashboard/comptes\?_hinclude=300000', AccountsErrorPage) + accounts = URL(r'/dashboard/comptes\?_hinclude=300000', AccountsPage) + accounts_error = URL(r'/dashboard/comptes\?_hinclude=300000', AccountsErrorPage) pro_accounts = URL(r'/dashboard/comptes-professionnels\?_hinclude=1', AccountsPage) - no_account = URL('/dashboard/comptes\?_hinclude=300000', - '/dashboard/comptes-professionnels\?_hinclude=1', NoAccountPage) + no_account = URL( + r'/dashboard/comptes\?_hinclude=300000', + r'/dashboard/comptes-professionnels\?_hinclude=1', + NoAccountPage + ) - history = URL('/compte/(cav|epargne)/(?P.*)/mouvements.*', HistoryPage) + history = URL(r'/compte/(cav|epargne)/(?P.*)/mouvements.*', HistoryPage) card_transactions = URL('/compte/cav/(?P.*)/carte/.*', HistoryPage) deffered_card_history = URL('https://api.boursorama.com/services/api/files/download.phtml.*', CardHistoryPage) budget_transactions = URL('/budget/compte/(?P.*)/mouvements.*', HistoryPage) other_transactions = URL('/compte/cav/(?P.*)/mouvements.*', HistoryPage) saving_transactions = URL('/compte/epargne/csl/(?P.*)/mouvements.*', HistoryPage) - saving_pep = URL('/compte/epargne/pep', PEPPage) + saving_pep = URL('/compte/epargne/pep', PEPPage) incident = URL('/compte/cav/(?P.*)/mes-incidents.*', IncidentPage) # transfer @@ -98,35 +106,60 @@ class BoursoramaBrowser(RetryLoginBrowser, TwoFactorBrowser): r'/compte/(?P[^/]+)/(?P\w+)/virements/suivi/(?P\w+)/[a-zA-Z0-9]{30,}$', TransferListPage ) - transfer_info = URL(r'/compte/(?P[^/]+)/(?P\w+)/virements/suivi/(?P\w+)/details/[\w-]{40,}', TransferInfoPage) + transfer_info = URL( + r'/compte/(?P[^/]+)/(?P\w+)/virements/suivi/(?P\w+)/details/[\w-]{40,}', + TransferInfoPage + ) transfer_main_page = URL(r'/compte/(?P[^/]+)/(?P\w+)/virements$', TransferMainPage) - transfer_accounts = URL(r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau$', - r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/1', TransferAccounts) - recipients_page = URL(r'/compte/(?P[^/]+)/(?P\w+)/virements$', - r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/2', - TransferRecipients) - transfer_charac = URL(r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/3', - TransferCharac) - transfer_confirm = URL(r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/4', - TransferConfirm) - transfer_sent = URL(r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/5', - TransferSent) - rcpt_page = URL(r'/compte/(?P[^/]+)/(?P\w+)/virements/comptes-externes/nouveau/(?P\w+)/\d', - AddRecipientPage) + transfer_accounts = URL( + r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau$', + r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/1', + TransferAccounts + ) + recipients_page = URL( + r'/compte/(?P[^/]+)/(?P\w+)/virements$', + r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/2', + TransferRecipients + ) + transfer_charac = URL( + r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/3', + TransferCharac + ) + transfer_confirm = URL( + r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/4', + TransferConfirm + ) + transfer_sent = URL( + r'/compte/(?P[^/]+)/(?P\w+)/virements/nouveau/(?P\w+)/5', + TransferSent + ) + rcpt_page = URL( + r'/compte/(?P[^/]+)/(?P\w+)/virements/comptes-externes/nouveau/(?P\w+)/\d', + AddRecipientPage + ) asv = URL('/compte/assurance-vie/.*', AsvPage) - saving_history = URL('/compte/cefp/.*/(positions|mouvements)', - '/compte/.*ord/.*/mouvements', - '/compte/pea/.*/mouvements', - '/compte/0%25pea/.*/mouvements', - '/compte/pea-pme/.*/mouvements', SavingMarketPage) - market = URL('/compte/(?!assurance|cav|epargne).*/(positions|mouvements|ordres)', - '/compte/ord/.*/positions', MarketPage) - loans = URL(r'/credit/paiement-3x/.*/informations', - r'/credit/immobilier/.*/informations', - r'/credit/immobilier/.*/caracteristiques', - r'/credit/consommation/.*/informations', - r'/credit/lombard/.*/caracteristiques', LoanPage) + saving_history = URL( + '/compte/cefp/.*/(positions|mouvements)', + '/compte/.*ord/.*/mouvements', + '/compte/pea/.*/mouvements', + '/compte/0%25pea/.*/mouvements', + '/compte/pea-pme/.*/mouvements', + SavingMarketPage + ) + market = URL( + r'/compte/(?!assurance|cav|epargne).*/(positions|mouvements|ordres)', + r'/compte/ord/.*/positions', + MarketPage + ) + loans = URL( + r'/credit/paiement-3x/.*/informations', + r'/credit/immobilier/.*/informations', + r'/credit/immobilier/.*/caracteristiques', + r'/credit/consommation/.*/informations', + r'/credit/lombard/.*/caracteristiques', + LoanPage + ) authentication = URL('/securisation', AuthenticationPage) iban = URL('/compte/(?P.*)/rib', IbanPage) profile = URL('/mon-profil/', ProfilePage) @@ -137,7 +170,10 @@ class BoursoramaBrowser(RetryLoginBrowser, TwoFactorBrowser): cards = URL('/compte/cav/cb', CardsNumberPage) currencylist = URL('https://www.boursorama.com/bourse/devises/parite/_detail-parite', CurrencyListPage) - currencyconvert = URL('https://www.boursorama.com/bourse/devises/convertisseur-devises/convertir', CurrencyConvertPage) + currencyconvert = URL( + 'https://www.boursorama.com/bourse/devises/convertisseur-devises/convertir', + CurrencyConvertPage + ) __states__ = ('auth_token', 'recipient_form',) @@ -189,10 +225,13 @@ def handle_sms(self): # PSD2 way else: # we can't access form without sending a SMS again - self.location('/securisation/authentification/validation', data={ - 'strong_authentication_confirm[code]': self.config['pin_code'].get(), - 'strong_authentication_confirm[type]': 'brs-otp-sms', - }) + self.location( + '/securisation/authentification/validation', + data={ + 'strong_authentication_confirm[code]': self.config['pin_code'].get(), + 'strong_authentication_confirm[type]': 'brs-otp-sms', + } + ) if self.authentication.is_here(): raise BrowserIncorrectAuthenticationCode() @@ -219,7 +258,7 @@ def init_login(self): elif any(msg in error for msg in wrongpass_messages): raise BrowserIncorrectPassword(error) - assert False, 'Unhandled error message : "%s"' % error + raise AssertionError('Unhandled error message : "%s"' % error) # After login, we might be redirected to the two factor authentication page. self.handle_authentication() @@ -271,7 +310,7 @@ def get_accounts_list(self): self.status.go() exc = None - for x in range(3): + for _ in range(3): if self.accounts_list is not None: break @@ -331,12 +370,22 @@ def get_accounts_list(self): if not card.number: self.accounts_list.remove(card) + type_with_iban = ( + Account.TYPE_CHECKING, + Account.TYPE_SAVINGS, + Account.TYPE_MARKET, + Account.TYPE_PEA, + ) for account in self.accounts_list: - if account.type not in (Account.TYPE_CARD, Account.TYPE_LOAN, Account.TYPE_CONSUMER_CREDIT, Account.TYPE_MORTGAGE, Account.TYPE_REVOLVING_CREDIT, Account.TYPE_LIFE_INSURANCE): + if account.type in type_with_iban: account.iban = self.iban.go(webid=account._webid).get_iban() for card in self.cards_list: - checking, = [account for account in self.accounts_list if account.type == Account.TYPE_CHECKING and account.url in card.url] + checking, = [ + account + for account in self.accounts_list + if account.type == Account.TYPE_CHECKING and account.url in card.url + ] card.parent = checking if exc: @@ -428,7 +477,10 @@ def get_invest_transactions(self, account, coming): @retry_on_logout() @need_login def iter_investment(self, account): - if '/compte/derive' in account.url or account.type not in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET, Account.TYPE_PEA): + if ( + '/compte/derive' in account.url + or account.type not in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET, Account.TYPE_PEA) + ): return [] self.location(account.url) return self.page.iter_investment() @@ -467,7 +519,7 @@ def go_recipients_list(self, account_url, account_id): parts = [part for part in url.path.split('/') if part] assert len(parts) > 2, 'Account url missing some important part to iter recipient' - account_type = parts[1] # cav, ord, epargne ... + account_type = parts[1] # cav, ord, epargne ... account_webid = parts[-1] self.transfer_main_page.go(acc_type=account_type, webid=account_webid) # may raise a BrowserHTTPNotFound @@ -532,11 +584,17 @@ def init_transfer(self, transfer, **kwargs): # at this stage, the site doesn't show the real ids/ibans, we can only guess if recipients[0].label != ret.recipient_label: - self.logger.info('Recipients from iter_recipient and from the transfer are diffent: "%s" and "%s"' % (recipients[0].label, ret.recipient_label)) + self.logger.info( + 'Recipients from iter_recipient and from the transfer are diffent: "%s" and "%s"', + recipients[0].label, ret.recipient_label + ) if not ret.recipient_label.startswith('%s - ' % recipients[0].label): # the label displayed here is " - " # but in the recipients list it is ""... - assert False, 'Recipient label changed during transfer (from "%s" to "%s")' % (recipients[0].label, ret.recipient_label) + raise AssertionError( + 'Recipient label changed during transfer (from "%s" to "%s")' + % (recipients[0].label, ret.recipient_label) + ) ret.recipient_id = recipients[0].id ret.recipient_iban = recipients[0].iban @@ -717,7 +775,7 @@ def get_rate(self, curr_from, curr_to): params = { 'from': curr_from, 'to': curr_to, - 'amount': '1' + 'amount': '1', } r.currency_from = curr_from r.currency_to = curr_to diff --git a/modules/boursorama/module.py b/modules/boursorama/module.py index 240edc6a9c..0c49531e76 100644 --- a/modules/boursorama/module.py +++ b/modules/boursorama/module.py @@ -19,6 +19,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 diff --git a/modules/boursorama/pages.py b/modules/boursorama/pages.py index df50da2531..825dccad27 100644 --- a/modules/boursorama/pages.py +++ b/modules/boursorama/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 datetime @@ -25,7 +27,10 @@ from datetime import date from base64 import b64decode -from weboob.browser.pages import HTMLPage, LoggedPage, pagination, NextPage, FormNotFound, PartialHTMLPage, LoginPage, CsvPage, RawPage, JsonPage +from weboob.browser.pages import ( + HTMLPage, LoggedPage, pagination, NextPage, FormNotFound, PartialHTMLPage, + LoginPage, CsvPage, RawPage, JsonPage, +) from weboob.browser.elements import ListElement, ItemElement, method, TableElement, SkipItem, DictElement from weboob.browser.filters.standard import ( CleanText, CleanDecimal, Field, Format, @@ -50,7 +55,10 @@ from weboob.tools.value import Value from weboob.tools.date import parse_french_date from weboob.tools.compat import urljoin, urlencode, urlparse, range -from weboob.exceptions import BrowserQuestion, BrowserIncorrectPassword, BrowserHTTPNotFound, BrowserUnavailable, ActionNeeded +from weboob.exceptions import ( + BrowserQuestion, BrowserIncorrectPassword, BrowserHTTPNotFound, BrowserUnavailable, + ActionNeeded, +) class IncidentPage(HTMLPage): @@ -71,10 +79,15 @@ def _bourso_id(self): class IbanPage(LoggedPage, HTMLPage): def get_iban(self): - if self.doc.xpath('//div[has-class("alert")]/p[contains(text(), "Une erreur est survenue")]') or \ - self.doc.xpath('//div[has-class("alert")]/p[contains(text(), "Le compte est introuvable")]'): + if ( + self.doc.xpath('//div[has-class("alert")]/p[contains(text(), "Une erreur est survenue")]') + or self.doc.xpath('//div[has-class("alert")]/p[contains(text(), "Le compte est introuvable")]') + ): return NotAvailable - return CleanText('//div[strong[contains(text(),"IBAN")]]/div[contains(@class, "definition")]', replace=[(' ', '')])(self.doc) + return CleanText( + '//div[strong[contains(text(),"IBAN")]]/div[contains(@class, "definition")]', + replace=[(' ', '')] + )(self.doc) class AuthenticationPage(HTMLPage): @@ -119,38 +132,71 @@ def sms_second_step(self): class Transaction(FrenchTransaction): - PATTERNS = [(re.compile('^(Virement .* )?VIR( SEPA)? (?P.*)'), FrenchTransaction.TYPE_TRANSFER), - (re.compile(u'^CHQ\. (?P.*)'), FrenchTransaction.TYPE_CHECK), - (re.compile('^(ACHAT|PAIEMENT) CARTE (?P
\d{2})(?P\d{2})(?P\d{2}) (?P.*)'), - FrenchTransaction.TYPE_CARD), - (re.compile('^(ACHAT |PAIEMENT )?CARTE (?P
\d{2})/(?P\d{2})/(?P\d{2}) (?P.*)'), - FrenchTransaction.TYPE_CARD), - (re.compile(r'^(?P.+)?(ACHAT|PAIEMENT) CARTE (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), - FrenchTransaction.TYPE_CARD), - (re.compile(r'^(?P.+)?(ACHAT|PAIEMENT) CARTE (?P
\d{2})/(?P\d{2})/(?P\d{4}) (?P.*)'), - FrenchTransaction.TYPE_CARD), - (re.compile(r'^(?P.+)?((ACHAT|PAIEMENT)\s)?CARTE (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), - FrenchTransaction.TYPE_CARD), - (re.compile(r'^(?P.+)?((ACHAT|PAIEMENT)\s)?CARTE (?P
\d{2})/(?P\d{2})/(?P\d{4}) (?P.*)'), - FrenchTransaction.TYPE_CARD), - (re.compile(r'(?P.+) CARTE (?P
\d{2})/(?P\d{2})/(?P\d{2}) (?P.*)'), FrenchTransaction.TYPE_CARD), - (re.compile('^(PRLV SEPA |PRLV |TIP )(?P.*)'), - FrenchTransaction.TYPE_ORDER), - (re.compile('^RETRAIT DAB (?P
\d{2})/?(?P\d{2})/?(?P\d{2}) (?P.*)'), - FrenchTransaction.TYPE_WITHDRAWAL), - (re.compile(r'^([A-Z][\sa-z]* )?RETRAIT DAB (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), - FrenchTransaction.TYPE_WITHDRAWAL), - (re.compile(r'^([A-Z][\sa-z]* )?Retrait dab (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), - FrenchTransaction.TYPE_WITHDRAWAL), - (re.compile(r'^AVOIR (?P
\d{2})/?(?P\d{2})/?(?P\d{2}) (?P.*)'), FrenchTransaction.TYPE_PAYBACK), - (re.compile(r'^(?P[A-Z][\sa-z]* )?AVOIR (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), FrenchTransaction.TYPE_PAYBACK), - (re.compile('^REM CHQ (?P.*)'), FrenchTransaction.TYPE_DEPOSIT), - (re.compile(u'^([*]{3} solde des operations cb [*]{3} )?Relevé différé Carte (.*)'), FrenchTransaction.TYPE_CARD_SUMMARY), - (re.compile(u'^[*]{3} solde des operations cb [*]{3}(.*)'), FrenchTransaction.TYPE_CARD), - (re.compile(r'^Ech pret'), FrenchTransaction.TYPE_LOAN_PAYMENT), - (re.compile(r'\*INTER(ETS DEBITEURS AU|\.BRUTS) (?P
\d{2})/(?P\d{2})/(?P\d{2})'), FrenchTransaction.TYPE_BANK), - (re.compile(r'^\*.*'), FrenchTransaction.TYPE_BANK), - ] + PATTERNS = [ + (re.compile(r'^(Virement .* )?VIR( SEPA)? (?P.*)'), FrenchTransaction.TYPE_TRANSFER), + (re.compile(r'^CHQ\. (?P.*)'), FrenchTransaction.TYPE_CHECK), + ( + re.compile(r'^(ACHAT|PAIEMENT) CARTE (?P
\d{2})(?P\d{2})(?P\d{2}) (?P.*)'), + FrenchTransaction.TYPE_CARD, + ), + ( + re.compile(r'^(ACHAT |PAIEMENT )?CARTE (?P
\d{2})/(?P\d{2})/(?P\d{2}) (?P.*)'), + FrenchTransaction.TYPE_CARD, + ), + ( + re.compile(r'^(?P.+)?(ACHAT|PAIEMENT) CARTE (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), + FrenchTransaction.TYPE_CARD, + ), + ( + re.compile(r'^(?P.+)?(ACHAT|PAIEMENT) CARTE (?P
\d{2})/(?P\d{2})/(?P\d{4}) (?P.*)'), + FrenchTransaction.TYPE_CARD, + ), + ( + re.compile(r'^(?P.+)?((ACHAT|PAIEMENT)\s)?CARTE (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), + FrenchTransaction.TYPE_CARD, + ), + ( + re.compile(r'^(?P.+)?((ACHAT|PAIEMENT)\s)?CARTE (?P
\d{2})/(?P\d{2})/(?P\d{4}) (?P.*)'), + FrenchTransaction.TYPE_CARD, + ), + ( + re.compile(r'(?P.+) CARTE (?P
\d{2})/(?P\d{2})/(?P\d{2}) (?P.*)'), + FrenchTransaction.TYPE_CARD, + ), + (re.compile(r'^(PRLV SEPA |PRLV |TIP )(?P.*)'), FrenchTransaction.TYPE_ORDER), + ( + re.compile(r'^RETRAIT DAB (?P
\d{2})/?(?P\d{2})/?(?P\d{2}) (?P.*)'), + FrenchTransaction.TYPE_WITHDRAWAL, + ), + ( + re.compile(r'^([A-Z][\sa-z]* )?RETRAIT DAB (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), + FrenchTransaction.TYPE_WITHDRAWAL, + ), + ( + re.compile(r'^([A-Z][\sa-z]* )?Retrait dab (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), + FrenchTransaction.TYPE_WITHDRAWAL, + ), + ( + re.compile(r'^AVOIR (?P
\d{2})/?(?P\d{2})/?(?P\d{2}) (?P.*)'), + FrenchTransaction.TYPE_PAYBACK, + ), + ( + re.compile(r'^(?P[A-Z][\sa-z]* )?AVOIR (?P
\d{2})(?P\d{2})(?P\d{4}) (?P.*)'), + FrenchTransaction.TYPE_PAYBACK, + ), + (re.compile('^REM CHQ (?P.*)'), FrenchTransaction.TYPE_DEPOSIT), + ( + re.compile(u'^([*]{3} solde des operations cb [*]{3} )?Relevé différé Carte (.*)'), + FrenchTransaction.TYPE_CARD_SUMMARY, + ), + (re.compile(u'^[*]{3} solde des operations cb [*]{3}(.*)'), FrenchTransaction.TYPE_CARD), + (re.compile(r'^Ech pret'), FrenchTransaction.TYPE_LOAN_PAYMENT), + ( + re.compile(r'\*INTER(ETS DEBITEURS AU|\.BRUTS) (?P
\d{2})/(?P\d{2})/(?P\d{2})'), + FrenchTransaction.TYPE_BANK, + ), + (re.compile(r'^\*.*'), FrenchTransaction.TYPE_BANK), + ] class VirtKeyboardPage(HTMLPage): @@ -180,19 +226,26 @@ def get_string_code(self, string): class PasswordPage(LoginPage, HTMLPage): - TO_DIGIT = {'2': ['a', 'b', 'c'], - '3': ['d', 'e', 'f'], - '4': ['g', 'h', 'i'], - '5': ['j', 'k', 'l'], - '6': ['m', 'n', 'o'], - '7': ['p', 'q', 'r', 's'], - '8': ['t', 'u', 'v'], - '9': ['w', 'x', 'y', 'z'] - } + TO_DIGIT = { + 'a': '2', 'b': '2', 'c': '2', + 'd': '3', 'e': '3', 'f': '3', + 'g': '4', 'h': '4', 'i': '4', + 'j': '5', 'k': '5', 'l': '5', + 'm': '6', 'n': '6', 'o': '6', + 'p': '7', 'q': '7', 'r': '7', 's': '7', + 't': '8', 'u': '8', 'v': '8', + 'w': '9', 'x': '9', 'y': '9', 'z': '9', + } def enter_password(self, username, password): if not password.isdigit(): - password = ''.join([c if c.isdigit() else [k for k, v in self.TO_DIGIT.items() if c in v][0] for c in password.lower()]) + old_password = password + password = '' + for c in old_password.lower(): + if c.isdigit(): + password += c + else: + password += self.TO_DIGIT[c] keyboard_page = self.browser.keyboard.open() vk = BoursoramaVirtKeyboard(keyboard_page) @@ -262,12 +315,24 @@ class item(ItemElement): def condition(self): # Ignore externally aggregated accounts and insurances: - return not self.is_external() and not any(x in Field('url')(self) for x in ('automobile', 'assurance/protection', 'assurance/comptes', 'assurance/famille')) + return ( + not self.is_external() + and not re.search( + 'automobile|assurance/protection|assurance/comptes|assurance/famille', + Field('url')(self) + ) + ) obj_label = CleanText('.//a[has-class("account--name")] | .//div[has-class("account--name")]') 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_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__holder = None obj__amount = CleanDecimal.French('.//a[has-class("account--balance")]') @@ -288,7 +353,12 @@ def obj_coming(self): # report deferred expenses in the coming attribute if Field('type')(self) == Account.TYPE_CARD: return Field('_amount')(self) - return Async('details', CleanDecimal(u'//li[h4[text()="Mouvements à venir"]]/h3', replace_dots=True, default=NotAvailable))(self) + return Async( + 'details', + CleanDecimal( + u'//li[h4[text()="Mouvements à venir"]]/h3', replace_dots=True, default=NotAvailable + ) + )(self) def obj_id(self): type = Field('type')(self) @@ -299,7 +369,14 @@ def obj_id(self): return self.obj__idparts()[1] # sometimes it's
sometimes it's

- id = Async('details', Regexp(CleanText('//*[has-class("account-number")]', transliterate=True), r'Reference du compte : (\d+)', default=NotAvailable))(self) + id = Async( + 'details', + Regexp( + CleanText('//*[has-class("account-number")]', transliterate=True), + r'Reference du compte : (\d+)', + default=NotAvailable + ) + )(self) if not id: raise SkipItem() return id @@ -354,7 +431,7 @@ def is_external(self): return '/budget/' in Field('url')(self) def obj__idparts(self): - return re.findall('[a-z\d]{32}', Field('url')(self)) + return re.findall(r'[a-z\d]{32}', Field('url')(self)) def obj__webid(self): parts = self.obj__idparts() @@ -363,8 +440,10 @@ def obj__webid(self): # 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) + 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 LoanPage(LoggedPage, HTMLPage): @@ -380,14 +459,26 @@ class get_loan(ItemElement): klass = Loan obj_id = CleanText('//h3[contains(@class, "account-number")]/strong') - obj_label = CleanText('//h2[contains(@class, "page-title__account")]//*[@class="account-edit-label"]/span[1]') + obj_label = CleanText(r'//h2[contains(@class, "page-title__account")]//*[@class="account-edit-label"]/span[1]') obj_currency = CleanCurrency('//p[contains(text(), "Solde impayé")]/span') obj_duration = CleanDecimal.French('//p[contains(text(), "échéances restantes")]/span', default=NotAvailable) - obj_rate = CleanDecimal.French('//p[contains(text(), "Taux nominal en vigueur du prêt")]/span', default=NotAvailable) - obj_nb_payments_left = CleanDecimal.French('//p[contains(text(), "échéances restantes")]/span', default=NotAvailable) - obj_next_payment_amount = CleanDecimal.French('//p[contains(text(), "Montant de la prochaine échéance")]/span', default=NotAvailable) + obj_rate = CleanDecimal.French( + '//p[contains(text(), "Taux nominal en vigueur du prêt")]/span', + default=NotAvailable + ) + obj_nb_payments_left = CleanDecimal.French( + '//p[contains(text(), "échéances restantes")]/span', + default=NotAvailable + ) + obj_next_payment_amount = CleanDecimal.French( + '//p[contains(text(), "Montant de la prochaine échéance")]/span', + default=NotAvailable + ) obj_nb_payments_total = CleanDecimal.French('//p[contains(text(), "écheances totales") or contains(text(), "Nombre total")]/span') - obj_subscription_date = Date(CleanText('//p[contains(text(), "Date de départ du prêt")]/span'), parse_func=parse_french_date) + obj_subscription_date = Date( + CleanText('//p[contains(text(), "Date de départ du prêt")]/span'), + parse_func=parse_french_date + ) def obj_total_amount(self): total_amount = CleanText('//p[contains(text(), "Montant emprunt")]/span')(self) @@ -401,8 +492,15 @@ def obj_maturity_date(self): # Sometimes there is no maturity date, so instead there is just a dash if maturity_date == '-': return NotAvailable - return Date(CleanText('//p[contains(text(), "échéance finale")]/span'), parse_func=parse_french_date)(self) - return Date(Regexp(CleanText('//p[contains(text(), "date de votre dernière échéance")]'), r'(\d.*)'), parse_func=parse_french_date, default=NotAvailable)(self) + return Date( + CleanText('//p[contains(text(), "échéance finale")]/span'), + parse_func=parse_french_date + )(self) + + return Date( + Regexp(CleanText('//p[contains(text(), "date de votre dernière échéance")]'), r'(\d.*)'), + parse_func=parse_french_date, default=NotAvailable + )(self) def obj_balance(self): balance = CleanDecimal.French('//div[contains(text(), "Capital restant dû")]/following-sibling::div')(self) @@ -437,22 +535,30 @@ def on_load(self): # handle ics calendar dates = page_content.split('BEGIN:VEVENT')[1:] - assert len(dates)%2 == 0, 'List lenght should be even-numbered' + assert len(dates) % 2 == 0, 'List length should be even-numbered' # get all dates dates = [re.search(r'(?<=VALUE\=DATE:)(\d{8})', el).group(1) for el in dates] dates.sort() for i in range(0, len(dates), 2): - if len(dates[i:i+2]) == 2: + if len(dates[i:i + 2]) == 2: # list contains tuple like (vdate, date) - self.browser.deferred_card_calendar.append((Date().filter(dates[i]), Date().filter(dates[i+1]))) + self.browser.deferred_card_calendar.append( + ( + Date().filter(dates[i]), + Date().filter(dates[i + 1]), + ) + ) class CalendarPage(LoggedPage, HTMLPage): def on_load(self): # redirect - calendar_ics_url = urljoin(self.browser.BASEURL, CleanText('//a[contains(@href, "calendrier.ics")]/@href')(self.doc)) + calendar_ics_url = urljoin( + self.browser.BASEURL, + CleanText('//a[contains(@href, "calendrier.ics")]/@href')(self.doc) + ) self.browser.location(calendar_ics_url) @@ -460,10 +566,12 @@ class HistoryPage(LoggedPage, HTMLPage): @pagination @method class iter_history(ListElement): - item_xpath = '//ul[has-class("list__movement")]/li[div and not(contains(@class, "summary")) \ - and not(contains(@class, "graph")) \ - and not(contains(@class, "separator")) \ - and not(contains(@class, "list__movement__line--deffered"))]' + item_xpath = ''' + //ul[has-class("list__movement")]/li[div and not(contains(@class, "summary")) + and not(contains(@class, "graph")) + and not(contains(@class, "separator")) + and not(contains(@class, "list__movement__line--deffered"))] + ''' def next_page(self): next_page = self.el.xpath('//li[a[contains(text(), "Mouvements")]]') @@ -471,11 +579,10 @@ def next_page(self): next_page_token = Attr('.', 'data-operations-next-pagination')(next_page[0]) params = { 'rumroute': 'accounts.bank.movements', - 'continuationToken': next_page_token + 'continuationToken': next_page_token, } parsed = urlparse(self.page.url) - return '%s://%s%s?%s' %(parsed.scheme, parsed.netloc, parsed.path, urlencode(params)) - + return '%s://%s%s?%s' % (parsed.scheme, parsed.netloc, parsed.path, urlencode(params)) class item(ItemElement): klass = Transaction @@ -491,7 +598,10 @@ class item(ItemElement): ) def obj_id(self): - return Attr('.', 'data-id', default=NotAvailable)(self) or Attr('.', 'data-custom-id', default=NotAvailable)(self) + return ( + Attr('.', 'data-id', default=NotAvailable)(self) + or Attr('.', 'data-custom-id', default=NotAvailable)(self) + ) def obj_type(self): # In order to set TYPE_DEFERRED_CARD transactions correctly, @@ -514,7 +624,7 @@ def obj_rdate(self): # Transaction.Raw may have already set it return self.obj.rdate - s = Regexp(Field('raw'), ' (\d{2}/\d{2}/\d{2}) | (?!NUM) (\d{6}) ', default=NotAvailable)(self) + s = Regexp(Field('raw'), r' (\d{2}/\d{2}/\d{2}) | (?!NUM) (\d{6}) ', default=NotAvailable)(self) if not s: return Field('date')(self) s = s.replace('/', '') @@ -522,7 +632,11 @@ def obj_rdate(self): return Date(dayfirst=True, default=NotAvailable).filter('%s-%s-%s' % (s[:2], s[2:4], s[4:])) def obj__is_coming(self): - return Env('coming', default=False)(self) or len(self.xpath('.//span[@title="Mouvement à débit différé"]')) or self.obj_date() > date.today() + return ( + Env('coming', default=False)(self) + or len(self.xpath('.//span[@title="Mouvement à débit différé"]')) + or self.obj_date() > date.today() + ) def obj_date(self): # Months with accents are retrieved like that: f\xe9vrier @@ -542,9 +656,11 @@ def validate(self, obj): # TYPE_DEFERRED_CARD transactions are already present in the card history # so we only return TYPE_DEFERRED_CARD for the coming: if not Env('coming', default=False)(self): - return not len(self.xpath('.//span[has-class("icon-carte-bancaire")]')) \ - and not len(self.xpath('.//a[contains(@href, "/carte")]')) \ - and obj.type != Transaction.TYPE_DEFERRED_CARD + return ( + not len(self.xpath('.//span[has-class("icon-carte-bancaire")]')) + and not len(self.xpath('.//a[contains(@href, "/carte")]')) + and obj.type != Transaction.TYPE_DEFERRED_CARD + ) elif Env('coming', default=False)(self): # Do not return coming from deferred cards if their # summary does not have a fixed amount yet: @@ -602,7 +718,7 @@ def obj_rdate(self): # Transaction.Raw may have already set it return self.obj.rdate - s = Regexp(Field('raw'), ' (\d{2}/\d{2}/\d{2}) | (?!NUM) (\d{6}) ', default=NotAvailable)(self) + s = Regexp(Field('raw'), r' (\d{2}/\d{2}/\d{2}) | (?!NUM) (\d{6}) ', default=NotAvailable)(self) if not s: return Field('date')(self) s = s.replace('/', '') @@ -680,10 +796,16 @@ def inner(page, *args, **kwargs): class MarketPage(LoggedPage, HTMLPage): def get_balance(self, account_type): - txt = u"Solde au" if account_type is Account.TYPE_LIFE_INSURANCE else u"Total Portefeuille" + if account_type == Account.TYPE_LIFE_INSURANCE: + txt = "Solde au" + else: + txt = "Total Portefeuille" # HTML tags are usually h4-h3 but may also be span-span h_balance = CleanDecimal('//li[h4[contains(text(), "%s")]]/h3' % txt, replace_dots=True, default=None)(self.doc) - span_balance = CleanDecimal('//li/span[contains(text(), "%s")]/following-sibling::span' % txt, replace_dots=True, default=None)(self.doc) + span_balance = CleanDecimal( + '//li/span[contains(text(), "%s")]/following-sibling::span' % txt, + replace_dots=True, default=None + )(self.doc) return h_balance or span_balance or None def get_market_order_link(self): @@ -716,22 +838,26 @@ def obj_date(self): def parse(self, el): if el.xpath('./td[2]/a'): - m = re.search('(\d+)', el.xpath('./td[2]/a')[0].get('data-modal-alert-behavior', '')) + m = re.search(r'(\d+)', el.xpath('./td[2]/a')[0].get('data-modal-alert-behavior', '')) if m: - self.env['account']._history_pages.append((Field('raw')(self),\ - self.page.browser.open('%s%s%s' % (self.page.url.split('mouvements')[0], 'mouvement/', m.group(1))).page)) + url = '%s%s%s' % (self.page.url.split('mouvements')[0], 'mouvement/', m.group(1)) + page = self.page.browser.open(url).page + self.env['account']._history_pages.append((Field('raw')(self), page)) raise SkipItem() @method class get_investment(Myiter_investment): class item (Myitem): def obj_unitvalue(self): - return CleanDecimal(replace_dots=True, default=NotAvailable).filter((TableCell('unitvalue')(self)[0]).xpath('./span[not(@class)]')) + el = TableCell('unitvalue')(self)[0].xpath('./span[not(@class)]') + return CleanDecimal(replace_dots=True, default=NotAvailable).filter(el) def iter_investment(self): # Xpath can be h3/h4 or div/span; in both cases # the first node contains "Solde Espèces": - valuation = CleanDecimal('//li/*[contains(text(), "Solde Espèces")]/following-sibling::*', replace_dots=True, default=None)(self.doc) + valuation = CleanDecimal( + '//li/*[contains(text(), "Solde Espèces")]/following-sibling::*', replace_dots=True, default=None + )(self.doc) if not empty(valuation): yield create_french_liquidity(valuation) @@ -746,7 +872,10 @@ def get_transactions_from_detail(self, account): for table in page.doc.xpath('//table'): t = Transaction() - t.date = Date(CleanText(page.doc.xpath('//span[contains(text(), "Date d\'effet")]/following-sibling::span')), dayfirst=True)(page) + t.date = Date( + CleanText(page.doc.xpath('//span[contains(text(), "Date d\'effet")]/following-sibling::span')), + dayfirst=True + )(page) t.label = label t.amount = CleanDecimal(replace_dots=True).filter(amounts[0]) amounts.pop(0) @@ -873,8 +1002,10 @@ def obj_label(self): class ErrorPage(HTMLPage): def on_load(self): - error = (Attr('//input[@required][@id="profile_lei_type_identifier"]', 'data-message', default=None)(self.doc) or - CleanText('//h2[@class="page-title"][contains(text(), "Actualisation")]', default=None)(self.doc)) + error = ( + Attr('//input[@required][@id="profile_lei_type_identifier"]', 'data-message', default=None)(self.doc) + or CleanText('//h2[@class="page-title"][contains(text(), "Actualisation")]', default=None)(self.doc) + ) if error: raise ActionNeeded(error) @@ -938,8 +1069,8 @@ def populate_cards_number(self, cards): for _hash in self.doc.xpath('//div[contains(@class, "credit-card-carousel")]/@data-card-key'): # We get the card number associate to the cards_hash card_number = CleanText( - '//div[@data-card-key="%s" and contains(@class, "credit-card-carousel")]' - '//*[local-name()="svg"]//*[local-name()="tspan"]' % _hash, + '//div[@data-card-key="%s" and contains(@class, "credit-card-carousel")]' % _hash + + '//*[local-name()="svg"]//*[local-name()="tspan"]', replace=[(' ', '')] )(self.doc) @@ -1011,10 +1142,16 @@ class item(ItemElement): klass = Recipient obj_id = CleanText('.//div[@class="c-card-ghost__sub-label"]') - obj_bank_name = Regexp(CleanText('.//div[@class="transfer__account-name"]'), pattern=r'- ([^-]*)$', default=NotAvailable) + obj_bank_name = Regexp( + CleanText('.//div[@class="transfer__account-name"]'), pattern=r'- ([^-]*)$', + default=NotAvailable + ) def obj_label(self): - label = Regexp(CleanText('.//div[@class="c-card-ghost__top-label"]'), pattern=r'^(.*?)(?: -[^-]*)?$')(self) + label = Regexp( + CleanText('.//div[@class="c-card-ghost__top-label"]'), + pattern=r'^(.*?)(?: -[^-]*)?$' + )(self) return label.rstrip('-').rstrip() def obj_category(self): @@ -1099,7 +1236,10 @@ def obj_exec_date(self): if type_ == 'Ponctuel': return datetime.date.today() elif type_ == 'Différé': - return Date(CleanText('//span[@id="transfer-date"]/span[@class="transfer__account-value"]'), dayfirst=True)(self) + return Date( + CleanText('//span[@id="transfer-date"]/span[@class="transfer__account-value"]'), + dayfirst=True + )(self) def submit(self): form = self.get_form(name='Confirm') @@ -1202,7 +1342,7 @@ def get_currency_list(self): class CurrencyConvertPage(JsonPage): def get_rate(self): - if not 'error' in self.doc: + if 'error' not in self.doc: return Decimal(str(self.doc['rate'])) diff --git a/modules/boursorama/transfer_pages.py b/modules/boursorama/transfer_pages.py index 8cf2619df7..4a2f057ddd 100644 --- a/modules/boursorama/transfer_pages.py +++ b/modules/boursorama/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 weboob.browser.pages import HTMLPage, LoggedPage, pagination -- GitLab