diff --git a/modules/caissedepargne/base_pages.py b/modules/caissedepargne/base_pages.py
index 9aa1298b44f6c97d43f9d69984f0c7b077973d6d..b7fd6d5dcd2fdbfbbc4405a3f1e80ffdf46ceb1f 100644
--- a/modules/caissedepargne/base_pages.py
+++ b/modules/caissedepargne/base_pages.py
@@ -17,12 +17,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this weboob module. If not, see .
+# flake8: compatible
+
from weboob.browser.pages import HTMLPage
def fix_form(form):
- keys = ['MM$HISTORIQUE_COMPTE$btnCumul', 'Cartridge$imgbtnMessagerie', 'MM$m_CH$ButtonImageFondMessagerie',
- 'MM$m_CH$ButtonImageMessagerie']
+ keys = [
+ 'MM$HISTORIQUE_COMPTE$btnCumul', 'Cartridge$imgbtnMessagerie', 'MM$m_CH$ButtonImageFondMessagerie',
+ 'MM$m_CH$ButtonImageMessagerie',
+ ]
for name in keys:
form.pop(name, None)
diff --git a/modules/caissedepargne/browser.py b/modules/caissedepargne/browser.py
index ccd5d7ec24886dcd30c3c0000a072e8cc390defd..4ac3d7db8c14b0747ed2a2e844de533f1c3207f6 100644
--- a/modules/caissedepargne/browser.py
+++ b/modules/caissedepargne/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
@@ -56,12 +58,16 @@
from weboob.tools.decorators import retry
from .pages import (
- IndexPage, ErrorPage, MarketPage, LifeInsurance, LifeInsuranceHistory, LifeInsuranceInvestments, GarbagePage, MessagePage, LoginPage,
+ IndexPage, ErrorPage, MarketPage, LifeInsurance, LifeInsuranceHistory, LifeInsuranceInvestments,
+ GarbagePage, MessagePage, LoginPage,
TransferPage, ProTransferPage, TransferConfirmPage, TransferSummaryPage, ProTransferConfirmPage,
ProTransferSummaryPage, ProAddRecipientOtpPage, ProAddRecipientPage,
- SmsPage, ValidationPageOption, AuthentPage, RecipientPage, CanceledAuth, CaissedepargneKeyboard, CaissedepargneNewKeyboard,
- TransactionsDetailsPage, LoadingPage, ConsLoanPage, MeasurePage, NatixisLIHis, NatixisLIInv, NatixisRedirectPage,
- SubscriptionPage, CreditCooperatifMarketPage, UnavailablePage, CardsPage, CardsComingPage, CardsOldWebsitePage, TransactionPopupPage,
+ SmsPage, ValidationPageOption, AuthentPage, RecipientPage, CanceledAuth,
+ CaissedepargneKeyboard, CaissedepargneNewKeyboard,
+ TransactionsDetailsPage, LoadingPage, ConsLoanPage, MeasurePage,
+ NatixisLIHis, NatixisLIInv, NatixisRedirectPage,
+ SubscriptionPage, CreditCooperatifMarketPage, UnavailablePage,
+ CardsPage, CardsComingPage, CardsOldWebsitePage, TransactionPopupPage,
OldLeviesPage, NewLeviesPage, NewLoginPage, JsFilePage, AuthorizePage,
AuthenticationMethodPage, VkImagePage, AuthenticationStepPage, LoginTokensPage,
AppValidationPage,
@@ -146,7 +152,8 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
# Login and transfer authentication
authentication_step = URL(
- r'https://(?Pwww.icgauth.[^/]+)/dacsrest/api/v1u0/transaction/(?P[^/]+)/step', AuthenticationStepPage
+ r'https://(?Pwww.icgauth.[^/]+)/dacsrest/api/v1u0/transaction/(?P[^/]+)/step',
+ AuthenticationStepPage
)
authentication_method_page = URL(
r'https://(?Pwww.icgauth.[^/]+)/dacsrest/api/v1u0/transaction/(?P)',
@@ -161,13 +168,22 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
# eg of both possible regexes:
# https://www.icgauth.caisse-epargne.fr/dacstemplate-SOL/index.html?transactionID=CtxDACSP[a-f0-9]+
# https://www.icgauth.caisse-epargne.fr/dacstemplate-SOL/_12579/index.html?transactionID=CtxDACSP[a-f0-9]+
- validation_option = URL(r'https://(?Pwww.icgauth.[^/]+)/dacstemplate-SOL/(?:[^/]+/)?index.html\?transactionID=.*', ValidationPageOption)
+ validation_option = URL(
+ r'https://(?Pwww.icgauth.[^/]+)/dacstemplate-SOL/(?:[^/]+/)?index.html\?transactionID=.*',
+ ValidationPageOption
+ )
sms = URL(r'https://(?Pwww.icgauth.[^/]+)/dacswebssoissuer/AuthnRequestServlet', SmsPage)
app_validation = URL(r'https://(?Pwww.icgauth.[^/]+)/dacsrest/WaitingCallbackHandler', AppValidationPage)
- account_login = URL(r'/authentification/manage\?step=account&identifiant=(?P.*)&account=(?P.*)', LoginPage)
+ account_login = URL(
+ r'/authentification/manage\?step=account&identifiant=(?P.*)&account=(?P.*)',
+ LoginPage
+ )
loading = URL(r'https://.*/CreditConso/ReroutageCreditConso.aspx', LoadingPage)
- cons_loan = URL(r'https://www.credit-conso-cr.caisse-epargne.fr/websavcr-web/rest/contrat/getContrat\?datePourIe=(?P)', ConsLoanPage)
+ cons_loan = URL(
+ r'https://www.credit-conso-cr.caisse-epargne.fr/websavcr-web/rest/contrat/getContrat\?datePourIe=(?P)',
+ ConsLoanPage
+ )
transaction_detail = URL(r'https://.*/Portail.aspx.*', TransactionsDetailsPage)
recipient = URL(r'https://.*/Portail.aspx.*', RecipientPage)
checking = URL(r'https://.*/Portail.aspx.*', CheckingPage)
@@ -211,15 +227,27 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
r'https://www.espace-assurances.caisse-epargne.fr/espaceinternet-ce/views/common/routage-itce.xhtml\?windowId=automatedEntryPoint',
NatixisRedirectPage
)
- life_insurance_history = URL(r'https://www.extranet2.caisse-epargne.fr/cin-front/contrats/evenements', LifeInsuranceHistory)
- life_insurance_investments = URL(r'https://www.extranet2.caisse-epargne.fr/cin-front/contrats/details', LifeInsuranceInvestments)
+ life_insurance_history = URL(
+ r'https://www.extranet2.caisse-epargne.fr/cin-front/contrats/evenements',
+ LifeInsuranceHistory
+ )
+ life_insurance_investments = URL(
+ r'https://www.extranet2.caisse-epargne.fr/cin-front/contrats/details',
+ LifeInsuranceInvestments
+ )
life_insurance = URL(
r'https://.*/Assurance/Pages/Assurance.aspx',
r'https://www.extranet2.caisse-epargne.fr.*',
LifeInsurance
)
- natixis_life_ins_his = URL(r'https://www.espace-assurances.caisse-epargne.fr/espaceinternet-ce/rest/v2/contratVie/load-operation/(?P\w+)/(?P\w+)/(?P)', NatixisLIHis)
- natixis_life_ins_inv = URL(r'https://www.espace-assurances.caisse-epargne.fr/espaceinternet-ce/rest/v2/contratVie/load/(?P\w+)/(?P\w+)/(?P)', NatixisLIInv)
+ natixis_life_ins_his = URL(
+ r'https://www.espace-assurances.caisse-epargne.fr/espaceinternet-ce/rest/v2/contratVie/load-operation/(?P\w+)/(?P\w+)/(?P)',
+ NatixisLIHis
+ )
+ natixis_life_ins_inv = URL(
+ r'https://www.espace-assurances.caisse-epargne.fr/espaceinternet-ce/rest/v2/contratVie/load/(?P\w+)/(?P\w+)/(?P)',
+ NatixisLIInv
+ )
message = URL(r'https://www.caisse-epargne.offrebourse.com/DetailMessage\?refresh=O', MessagePage)
garbage = URL(
r'https://www.caisse-epargne.offrebourse.com/Portefeuille',
@@ -236,23 +264,25 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
# Accounts managed in life insurance space (not in linebourse)
- insurance_accounts = ('AIKIDO',
- 'ASSURECUREUIL',
- 'ECUREUIL PROJET',
- 'GARANTIE RETRAITE EU',
- 'INITIATIVES PLUS',
- 'INITIATIVES TRANSMIS',
- 'LIVRET ASSURANCE VIE',
- 'OCEOR EVOLUTION',
- 'PATRIMONIO CRESCENTE',
- 'PEP TRANSMISSION',
- 'PERP',
- 'PERSPECTIVES ECUREUI',
- 'POINTS RETRAITE ECUR',
- 'RICOCHET',
- 'SOLUTION PERP',
- 'TENDANCES',
- 'YOGA', )
+ insurance_accounts = (
+ 'AIKIDO',
+ 'ASSURECUREUIL',
+ 'ECUREUIL PROJET',
+ 'GARANTIE RETRAITE EU',
+ 'INITIATIVES PLUS',
+ 'INITIATIVES TRANSMIS',
+ 'LIVRET ASSURANCE VIE',
+ 'OCEOR EVOLUTION',
+ 'PATRIMONIO CRESCENTE',
+ 'PEP TRANSMISSION',
+ 'PERP',
+ 'PERSPECTIVES ECUREUI',
+ 'POINTS RETRAITE ECUR',
+ 'RICOCHET',
+ 'SOLUTION PERP',
+ 'TENDANCES',
+ 'YOGA',
+ )
def __init__(self, nuser, *args, **kwargs):
self.BASEURL = kwargs.pop('domain', self.BASEURL)
@@ -411,7 +441,7 @@ def check_connection_data(self, data):
self.typeAccount = accounts_types[self.inexttype]
else:
- assert False, 'should have logged in with at least one connection type'
+ raise AssertionError('should have logged in with at least one connection type')
self.inexttype += 1
data = self.account_login.go(login=self.username, accountType=self.typeAccount).get_response()
@@ -435,7 +465,7 @@ def do_old_login(self, data, type_account, accounts_types):
'step': 'authentification',
'ctx': 'typsrv={}'.format(type_account),
'clavierSecurise': '1',
- 'nuabbd': self.username
+ 'nuabbd': self.username,
}
try:
@@ -485,7 +515,7 @@ def get_auth_mechanisms_validation_info(self):
if transaction_id:
transaction_id = transaction_id.group(1)
else:
- assert False, 'Transfer transaction id was not found in url'
+ raise AssertionError('Transfer transaction id was not found in url')
otp_validation_domain = urlparse(self.url).netloc
@@ -699,6 +729,29 @@ def do_new_login(self, data):
# 'Accept': 'applcation/json'. If we do not add this header, we
# instead have a form that we can directly send to complete
# the login.
+
+ claims = {
+ 'userinfo': {
+ 'cdetab': None,
+ 'authMethod': None,
+ 'authLevel': None,
+ },
+ 'id_token': {
+ 'auth_time': {"essential": True},
+ "last_login": None,
+ },
+ }
+ bpcesta = {
+ "csid": csid,
+ "typ_app": "rest",
+ "enseigne": "ce",
+ "typ_sp": "out-band",
+ "typ_act": "auth",
+ "snid": snid,
+ "cdetab": url_params['cdetab'][0],
+ "typ_srv": "part",
+ }
+
self.authorize.go(
params={
'nonce': nonce,
@@ -709,9 +762,10 @@ def do_new_login(self, data):
'login_hint': self.username,
'display': 'page',
'client_id': client_id,
- 'claims': '{"userinfo":{"cdetab":null,"authMethod":null,"authLevel":null},"id_token":{"auth_time":{"essential":true},"last_login":null}}',
- 'bpcesta': '{"csid":"%s","typ_app":"rest","enseigne":"ce","typ_sp":"out-band","typ_act":"auth","snid":"%s","cdetab":"%s","typ_srv":"part"}' % (csid, snid, url_params['cdetab'][0]),
- },
+ # don't know if the separators= is really needed
+ 'claims': json.dumps(claims, separators=(',', ':')),
+ 'bpcesta': json.dumps(bpcesta, separators=(',', ':')),
+ }
)
self.page.send_form()
@@ -758,7 +812,10 @@ def loans_conso(self):
# for non-DST
# d = '%s %s %s %s %s:%s:%s GMT+0100 (heure normale d’Europe centrale)' % (days[now.weekday()], now.day, month[now.month - 1], now.year, now.hour, format(now.minute, "02"), now.second)
# TODO use babel library to simplify this code
- d = '%s %s %s %s %s:%s:%s GMT+0200 (heure d’été d’Europe centrale)' % (days[now.weekday()], now.day, month[now.month - 1], now.year, now.hour, format(now.minute, "02"), now.second)
+ d = '%s %s %s %s %s:%s:%s GMT+0200 (heure d’été d’Europe centrale)' % (
+ days[now.weekday()], now.day, month[now.month - 1], now.year,
+ now.hour, format(now.minute, "02"), now.second,
+ )
if self.home.is_here():
msg = self.page.loan_unavailable_msg()
if msg:
@@ -767,11 +824,11 @@ def loans_conso(self):
self.cons_loan.go(datepourie=d)
return self.page.get_conso()
- def go_measure_list(self, page_num = 0):
+ def go_measure_list(self, page_num=0):
self.home.go()
if not self.measure_page.is_here():
- assert False, 'Should be on measure_page'
+ raise AssertionError('Should be on measure_page')
self.page.go_measure_list()
for _ in range(page_num):
@@ -812,7 +869,6 @@ def get_measure_accounts_list(self):
break
self.page.goto_next_page()
-
for account in self.accounts:
if 'acc_type' in account._info and account._info['acc_type'] == Account.TYPE_LIFE_INSURANCE:
self.go_measure_list(account._info['measure_id_page_num'])
@@ -868,7 +924,7 @@ def add_linebourse_accounts_data(self):
# We need to go back to the synthesis, else we can not go home later
self.home_tache.go(tache='CPTSYNT0')
else:
- assert False, "new domain that hasn't been seen so far ?"
+ raise AssertionError("new domain that hasn't been seen so far?")
def add_card_accounts(self):
"""
@@ -973,7 +1029,7 @@ def get_loans_list(self):
if self.page.check_no_accounts() or self.page.check_no_loans():
return []
- for trial in range(5):
+ for _ in range(5):
for _ in range(3):
self.home_tache.go(tache='CRESYNT0')
if self.home.is_here():
@@ -1049,7 +1105,10 @@ def _get_history(self, info, account_card=None):
if self.card_matches(tr.card, account_card.number):
card_and_forms.append((tr.card, self.page.get_form_to_detail(tr)))
else:
- self.logger.debug('will skip summary detail (%r) for different card %r', tr, account_card.number)
+ self.logger.debug(
+ 'will skip summary detail (%r) for different card %r',
+ tr, account_card.number
+ )
elif tr.type == FrenchTransaction.TYPE_CARD and 'fac cb' in tr.raw.lower() and not account_card:
# for immediate debits made with a def card the label is way too empty for certain clients
# we therefore open a popup and find the rest of the label
@@ -1084,7 +1143,7 @@ def _get_history(self, info, account_card=None):
self.page.go_form_to_summary()
# going back to summary goes back to first page
- for j in range(i):
+ for _ in range(i):
assert self.page.go_next()
# order by date the transactions without the summaries
@@ -1098,7 +1157,7 @@ def _get_history(self, info, account_card=None):
if not self.page.go_next():
return
- assert False, 'More than {} history pages'.format(self.HISTORY_MAX_PAGE)
+ raise AssertionError('More than {} history pages'.format(self.HISTORY_MAX_PAGE))
@need_login
def _get_history_invests(self, account):
@@ -1171,7 +1230,10 @@ def match_cb(tr):
if not hasattr(account, '_info'):
raise NotImplementedError
- if account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_CAPITALISATION) and 'measure_id' not in account._info:
+ if (
+ account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_CAPITALISATION)
+ and 'measure_id' not in account._info
+ ):
return self._get_history_invests(account)
if account.type in (Account.TYPE_MARKET, Account.TYPE_PEA):
self.page.go_history(account._info)
@@ -1241,7 +1303,15 @@ def get_coming_card(self, account):
@need_login
def get_investment(self, account):
self.deleteCTX()
- if account.type not in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_CAPITALISATION, Account.TYPE_MARKET, Account.TYPE_PEA) or 'measure_id' in account._info:
+
+ investable_types = (
+ Account.TYPE_LIFE_INSURANCE, Account.TYPE_CAPITALISATION,
+ Account.TYPE_MARKET, Account.TYPE_PEA,
+ )
+ if (
+ account.type not in investable_types
+ or 'measure_id' in account._info
+ ):
raise NotImplementedError()
if account.type == Account.TYPE_PEA and account.label == 'PEA NUMERAIRE':
@@ -1435,7 +1505,7 @@ def otp_sms_continue_transfer(self, transfer, **params):
if self.transfer.is_here():
self.page.continue_transfer(transfer.account_label, transfer.recipient_label, transfer.label)
return self.page.update_transfer(transfer)
- assert False, 'Blank page instead of the TransferPage'
+ raise AssertionError('Blank page instead of the TransferPage')
@need_login
def execute_transfer(self, transfer):
@@ -1503,13 +1573,18 @@ def new_recipient(self, recipient, **params):
if 'pro_password' in params:
return self.end_pro_recipient(recipient, **params)
- self.pre_transfer(next(acc for acc in self.get_accounts_list() if acc.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS)))
+ first_transfer_account = next(
+ acc
+ for acc in self.get_accounts_list()
+ if acc.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS)
+ )
+ self.pre_transfer(first_transfer_account)
# This send sms to user.
self.page.go_add_recipient()
if self.transfer.is_here():
self.page.handle_error()
- assert False, 'We should not be on this page.'
+ raise AssertionError('We should not be on this page')
if self.home.is_here():
# If we land here it might be because the user has no 2fa method
@@ -1542,12 +1617,17 @@ def new_recipient(self, recipient, **params):
# pro add recipient.
elif self.page.need_auth():
self.page.set_browser_form()
- raise AddRecipientStep(self.get_recipient_obj(recipient), Value('pro_password', label=self.page.get_prompt_text()))
-
+ raise AddRecipientStep(
+ self.get_recipient_obj(recipient),
+ Value('pro_password', label=self.page.get_prompt_text())
+ )
else:
self.page.check_canceled_auth()
self.page.set_browser_form()
- raise AddRecipientStep(self.get_recipient_obj(recipient), Value('sms_password', label=self.page.get_prompt_text()))
+ raise AddRecipientStep(
+ self.get_recipient_obj(recipient),
+ Value('sms_password', label=self.page.get_prompt_text())
+ )
def go_documents_without_sub(self):
self.home_tache.go(tache='CPTSYNT0')
diff --git a/modules/caissedepargne/cenet/browser.py b/modules/caissedepargne/cenet/browser.py
index e305c2f87813af89a1a5f959034969738979888b..340363b7be344d58cb684458640cf8ab9780affa 100644
--- a/modules/caissedepargne/cenet/browser.py
+++ b/modules/caissedepargne/cenet/browser.py
@@ -17,8 +17,9 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this weboob module. If not, see .
-from __future__ import unicode_literals
+# flake8: compatible
+from __future__ import unicode_literals
from collections import Counter
from fnmatch import fnmatch
@@ -57,7 +58,10 @@ class CenetBrowser(LoginBrowser, StatesMixin):
r'https://.*/login.aspx',
LoginPage,
)
- account_login = URL(r'https://(?P[^/]+)/authentification/manage\?step=account&identifiant=(?P.*)&account=(?P.*)', LoginPage)
+ account_login = URL(
+ r'https://(?P[^/]+)/authentification/manage\?step=account&identifiant=(?P.*)&account=(?P.*)',
+ LoginPage
+ )
cenet_vk = URL(r'https://www.cenet.caisse-epargne.fr/Web/Api/ApiAuthentification.asmx/ChargerClavierVirtuel')
cenet_home = URL(r'/Default.aspx$', CenetHomePage)
cenet_accounts = URL(r'/Web/Api/ApiComptes.asmx/ChargerSyntheseComptes', CenetAccountsPage)
@@ -127,7 +131,7 @@ def do_login(self):
post_data = {
'CodeEtablissement': data['codeCaisse'],
'NumeroBad': self.username,
- 'NumeroUtilisateur': self.nuser
+ 'NumeroUtilisateur': self.nuser,
}
self.location(data['url'], data=post_data, headers={'Referer': 'https://www.cenet.caisse-epargne.fr/'})
@@ -141,7 +145,7 @@ def get_accounts_list(self):
'contexte': '',
'dateEntree': None,
'donneesEntree': 'null',
- 'filtreEntree': None
+ 'filtreEntree': None,
}
# get accounts from CenetAccountsPage
@@ -238,7 +242,7 @@ def get_history_base(self, account, card_number=None):
'contexte': '',
'dateEntree': None,
'donneesEntree': json.dumps(donneesEntree).replace('/', '\\/'),
- 'filtreEntree': json.dumps(tr._data).replace('/', '\\/')
+ 'filtreEntree': json.dumps(tr._data).replace('/', '\\/'),
}
tr_detail_page = self.cenet_tr_detail.open(json=deferred_data)
@@ -267,7 +271,7 @@ def get_coming(self, account):
'contexte': '',
'dateEntree': None,
'donneesEntree': json.dumps(account._hist),
- 'filtreEntree': None
+ 'filtreEntree': None,
}
self.cenet_account_coming.go(json=data)
@@ -305,7 +309,7 @@ def iter_subscription(self):
'contexte': '',
'dateEntree': None,
'donneesEntree': 'null',
- 'filtreEntree': None
+ 'filtreEntree': None,
}
self.subscription.go(json=json_data)
return self.page.iter_subscription(subscriber=subscriber)
@@ -328,7 +332,7 @@ def iter_documents(self, subscription):
'contexte': '',
'dateEntree': None,
'donneesEntree': 'null',
- 'filtreEntree': json.dumps(input_filter)
+ 'filtreEntree': json.dumps(input_filter),
}
self.documents.go(json=json_data)
return self.page.iter_documents(sub_id=sub_id, sub_label=subscription.label, username=self.username)
diff --git a/modules/caissedepargne/cenet/pages.py b/modules/caissedepargne/cenet/pages.py
index 6e922b443bda9730bfd70952abeb0db4af67716c..bb719a55cc29055d54d78f2c53c2b9a4f21924c9 100644
--- a/modules/caissedepargne/cenet/pages.py
+++ b/modules/caissedepargne/cenet/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 copy import deepcopy
@@ -41,9 +43,18 @@
class Transaction(FrenchTransaction):
PATTERNS = [
- (re.compile(r'^CB (?P.*?) FACT (?P\d{2})(?P\d{2})(?P\d{2})', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
+ (
+ re.compile(r'^CB (?P.*?) FACT (?P\d{2})(?P\d{2})(?P\d{2})', re.IGNORECASE),
+ FrenchTransaction.TYPE_CARD,
+ ),
(re.compile(r'^RET(RAIT)? DAB (?P\d+)-(?P\d+)-.*', re.IGNORECASE), FrenchTransaction.TYPE_WITHDRAWAL),
- (re.compile(r'^RET(RAIT)? DAB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2}) (?P\d{2})H(?P\d{2})', re.IGNORECASE), FrenchTransaction.TYPE_WITHDRAWAL),
+ (
+ re.compile(
+ r'^RET(RAIT)? DAB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2}) (?P\d{2})H(?P\d{2})',
+ re.IGNORECASE,
+ ),
+ FrenchTransaction.TYPE_WITHDRAWAL,
+ ),
(re.compile(r'^VIR(EMENT)?(\.PERIODIQUE)? (?P.*)', re.IGNORECASE), FrenchTransaction.TYPE_TRANSFER),
(re.compile(r'^PRLV (?P.*)', re.IGNORECASE), FrenchTransaction.TYPE_ORDER),
(re.compile(r'^CHEQUE.*', re.IGNORECASE), FrenchTransaction.TYPE_CHECK),
@@ -53,8 +64,14 @@ class Transaction(FrenchTransaction):
(re.compile(r'^(?P.*)( \d+)? QUITTANCE .*', re.IGNORECASE), FrenchTransaction.TYPE_ORDER),
(re.compile(r'^CB [\d\*]+ TOT DIF .*', re.IGNORECASE), FrenchTransaction.TYPE_CARD_SUMMARY),
(re.compile(r'^CB [\d\*]+ (?P.*)', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
- (re.compile(r'^CB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2})', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
- (re.compile(r'\*CB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2})', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
+ (
+ re.compile(r'^CB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2})', re.IGNORECASE),
+ FrenchTransaction.TYPE_CARD,
+ ),
+ (
+ re.compile(r'\*CB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2})', re.IGNORECASE),
+ FrenchTransaction.TYPE_CARD,
+ ),
(re.compile(r'^FAC CB (?P.*?) (?P\d{2})/(?P\d{2})', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
]
@@ -65,13 +82,20 @@ def get_response(self):
class CenetLoginPage(HTMLPage):
- def login(self, username, password, nuser, codeCaisse, _id, vkpass):
+ def login(self, username, password, nuser, codeCaisse, vkid, vkpass):
form = self.get_form(id='aspnetForm')
-
form['__EVENTTARGET'] = "btn_authentifier_securise"
- form['__EVENTARGUMENT'] = '{"CodeCaisse":"%s","NumeroBad":"%s","NumeroUsager":"%s",\
- "MotDePasse":"%s","IdentifiantClavier":"%s","ChaineConnexion":"%s"}' \
- % (codeCaisse, username, nuser, password, _id, vkpass)
+ form['__EVENTARGUMENT'] = json.dumps(
+ {
+ "CodeCaisse": codeCaisse,
+ "NumeroBad": username,
+ "NumeroUsager": nuser,
+ "MotDePasse": password,
+ "IdentifiantClavier": vkid,
+ "ChaineConnexion": vkpass,
+ },
+ separators=(',', ':')
+ )
form.submit()
@@ -305,8 +329,10 @@ def obj_type(self):
def obj_amount(self):
amount = CleanDecimal(Dict('Montant/Valeur'))(self)
-
- return -amount if Dict('Montant/CodeSens')(self) == "D" else amount
+ if Dict('Montant/CodeSens')(self) == "D":
+ return -amount
+ else:
+ return amount
def obj__data(self):
return self.el
@@ -341,15 +367,17 @@ def obj_raw(self):
return label
def obj_rdate(self):
- rdate = re.search('(FACT\s)(\d{6})', Field('label')(self))
+ rdate = re.search(r'(FACT\s)(\d{6})', Field('label')(self))
if rdate.group(2):
return Date(dayfirst=True).filter(rdate.group(2))
return NotAvailable
def obj_amount(self):
amount = CleanDecimal(Dict('Montant/Valeur'))(self)
-
- return -amount if Dict('Montant/CodeSens')(self) == "D" else amount
+ if Dict('Montant/CodeSens')(self) == "D":
+ return -amount
+ else:
+ return amount
class _LogoutPage(HTMLPage):
@@ -411,7 +439,7 @@ def download_form(self, document):
'Numero': document._numero,
'Libelle': document._sub_label.replace(' ', '+'),
'DateArrete': '',
- 'IdDocument': document._download_id
+ 'IdDocument': document._download_id,
}
form = self.get_form(id='aspnetForm')
form['__EVENTTARGET'] = 'btn_telecharger'
diff --git a/modules/caissedepargne/linebourse_browser.py b/modules/caissedepargne/linebourse_browser.py
index e3ce553a4cb34d310bfca3084663a55ec7583b57..516fdfcb34a28ffad69aca97edb94d9b690077b2 100644
--- a/modules/caissedepargne/linebourse_browser.py
+++ b/modules/caissedepargne/linebourse_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 weboob.browser import AbstractBrowser
diff --git a/modules/caissedepargne/module.py b/modules/caissedepargne/module.py
index c7b77a4530ac06df172ccf616796fc002421982b..7259ea226f9733083a3221cbcab73cd05632e36c 100644
--- a/modules/caissedepargne/module.py
+++ b/modules/caissedepargne/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
import re
@@ -49,25 +51,36 @@ class CaisseEpargneModule(Module, CapBankWealth, CapBankTransferAddRecipient, Ca
DESCRIPTION = 'Caisse d\'Épargne'
LICENSE = 'LGPLv3+'
BROWSER = ProxyBrowser
- website_choices = OrderedDict([(k, u'%s (%s)' % (v, k)) for k, v in sorted({
- 'www.caisse-epargne.fr': u'Caisse d\'Épargne',
- 'www.banquebcp.fr': u'Banque BCP',
- }.items(), key=lambda k_v: (k_v[1], k_v[0]))])
+ website_choices = {
+ 'www.caisse-epargne.fr': u"Caisse d'Épargne",
+ 'www.banquebcp.fr': u'Banque BCP',
+ }
+ website_choices = OrderedDict(
+ [
+ (k, u'%s (%s)' % (v, k))
+ for k, v in sorted(
+ website_choices.items(),
+ key=lambda k_v: (k_v[1], k_v[0])
+ )
+ ]
+ )
CONFIG = BackendConfig(
- Value('website', label='Banque', choices=website_choices, default='www.caisse-epargne.fr'),
+ Value('website', label='Banque', choices=website_choices, default='www.caisse-epargne.fr'),
ValueBackendPassword('login', label='Identifiant client', masked=False),
- ValueBackendPassword('password', label='Code personnel', regexp='\d+'),
- Value('nuser', label='User ID (optional)', default='', regexp='[A-Z\d]{0,8}'),
+ ValueBackendPassword('password', label='Code personnel', regexp=r'\d+'),
+ Value('nuser', label='User ID (optional)', default='', regexp=r'[A-Z0-9]{0,8}'),
)
accepted_document_types = (DocumentTypes.STATEMENT, DocumentTypes.OTHER,)
def create_default_browser(self):
- return self.create_browser(nuser=self.config['nuser'].get(),
- username=self.config['login'].get(),
- password=self.config['password'].get(),
- domain=self.config['website'].get(),
- weboob=self.weboob)
+ return self.create_browser(
+ nuser=self.config['nuser'].get(),
+ username=self.config['login'].get(),
+ password=self.config['password'].get(),
+ domain=self.config['website'].get(),
+ weboob=self.weboob
+ )
def iter_accounts(self):
for account in self.browser.get_accounts_list():
@@ -106,16 +119,23 @@ def init_transfer(self, transfer, **params):
return self.browser.otp_sms_continue_transfer(transfer, **params)
self.logger.info('Going to do a new transfer')
- transfer.label = ' '.join(w for w in re.sub('[^0-9a-zA-Z/\-\?:\(\)\.,\'\+ ]+', '', transfer.label).split()).upper()
+ transfer.label = re.sub(r"[^0-9A-Z/?:().,'+ -]+", '', transfer.label.upper())
+ transfer.label = re.sub(r'\s+', ' ', transfer.label)
if transfer.account_iban:
account = find_object(self.iter_accounts(), iban=transfer.account_iban, error=AccountNotFound)
else:
account = find_object(self.iter_accounts(), id=transfer.account_id, error=AccountNotFound)
if transfer.recipient_iban:
- recipient = find_object(self.iter_transfer_recipients(account.id), iban=transfer.recipient_iban, error=RecipientNotFound)
+ recipient = find_object(
+ self.iter_transfer_recipients(account.id), iban=transfer.recipient_iban,
+ error=RecipientNotFound
+ )
else:
- recipient = find_object(self.iter_transfer_recipients(account.id), id=transfer.recipient_id, error=RecipientNotFound)
+ recipient = find_object(
+ self.iter_transfer_recipients(account.id), id=transfer.recipient_id,
+ error=RecipientNotFound
+ )
transfer.amount = transfer.amount.quantize(Decimal(10) ** -2)
@@ -125,7 +145,6 @@ def execute_transfer(self, transfer, **params):
return self.browser.execute_transfer(transfer)
def new_recipient(self, recipient, **params):
- # recipient.label = ' '.join(w for w in re.sub('[^0-9a-zA-Z:\/\-\?\(\)\.,\'\+ ]+', '', recipient.label).split())
return self.browser.new_recipient(recipient, **params)
def iter_resources(self, objs, split_path):
diff --git a/modules/caissedepargne/pages.py b/modules/caissedepargne/pages.py
index dee24d03203de19b39e8eff009f41d7e09bbba7c..824d04e9922eead89084d5d79109c4c970fa40b4 100644
--- a/modules/caissedepargne/pages.py
+++ b/modules/caissedepargne/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 division
from __future__ import unicode_literals
@@ -39,7 +41,7 @@
Field, Eval, Format, Currency, Coalesce,
)
from weboob.browser.filters.html import Link, Attr, TableCell
-from weboob.capabilities import NotAvailable
+from weboob.capabilities.base import NotAvailable, empty
from weboob.capabilities.bank import (
Account, Loan, AccountOwnership,
Transfer, TransferBankError, TransferInvalidOTP,
@@ -95,11 +97,11 @@ def get_response(self):
def get_wrongpass_message(self):
error_msg = Dict('error')(self.doc)
if (
- "Nous n'avons pas réussi à vous authentifier" in error_msg or
- 'abonnement est bloqué' in error_msg
+ "Nous n'avons pas réussi à vous authentifier" in error_msg
+ or 'abonnement est bloqué' in error_msg
):
return error_msg
- assert False, 'Other error message to catch on LoginPage'
+ raise AssertionError('Other error message to catch on LoginPage')
class JsFilePage(RawPage):
@@ -128,7 +130,9 @@ def get_wrong_pre_login_status(self):
# 'validationUnits' informs about auth method
# not having any is faulty for the connection
status = self.doc['response']['status']
- assert status in ('AUTHENTICATION_FAILED',), 'Unhandled status when checking if authentication method is informed: %s' % status
+ assert status in ('AUTHENTICATION_FAILED',), (
+ 'Unhandled status when checking if authentication method is informed: %s' % status
+ )
return status
@property
@@ -196,7 +200,7 @@ def check_errors(self, feature):
}
FEATURES_ERRORS[feature](error=result)
- assert False, 'Error during %s authentication is not handled yet: %s' % (feature, result)
+ raise AssertionError('Error during %s authentication is not handled yet: %s' % (feature, result))
class AuthenticationStepPage(AuthenticationMethodPage):
@@ -227,7 +231,10 @@ class CaissedepargneNewKeyboard(SplitKeyboard):
'1': ('529819241cce382b429b4624cb019b56', '0ea8c08e52d992a28aa26043ffc7c044'),
'2': 'fab68678204198b794ce580015c8637f',
'3': '3fc5280d17cf057d1c4b58e4f442ceb8',
- '4': ('dea8800bdd5fcaee1903a2b097fbdef0', 'e413098a4d69a92d08ccae226cea9267', '61f720966ccac6c0f4035fec55f61fe6', '2cbd19a4b01c54b82483f0a7a61c88a1'),
+ '4': (
+ 'dea8800bdd5fcaee1903a2b097fbdef0', 'e413098a4d69a92d08ccae226cea9267',
+ '61f720966ccac6c0f4035fec55f61fe6', '2cbd19a4b01c54b82483f0a7a61c88a1',
+ ),
'5': 'ff1909c3b256e7ab9ed0d4805bdbc450',
'6': '7b014507ffb92a80f7f0534a3af39eaa',
'7': '7d598ff47a5607022cab932c6ad7bc5b',
@@ -247,7 +254,13 @@ def __init__(self, browser, images):
threshold=3,
))
img = img.convert('L', dither=None)
- img = Image.eval(img, lambda x: 0 if x < 20 else 255)
+
+ def threshold(px):
+ if px < 20:
+ return 0
+ return 255
+
+ img = Image.eval(img, threshold)
b = BytesIO()
img.save(b, format='PNG')
code_to_filedata[img_item['value']] = b.getvalue()
@@ -286,7 +299,7 @@ def on_load(self):
if go_back_link is not NotAvailable:
assert len(go_back_link) != 1
- go_back_link = re.search('\(~deibaseurl\)(.*)$', go_back_link).group(1)
+ go_back_link = re.search(r'\(~deibaseurl\)(.*)$', go_back_link).group(1)
self.browser.location('%s%s' % (self.browser.BASEURL, go_back_link))
@@ -314,9 +327,18 @@ class ErrorPage(_LogoutPage):
class Transaction(FrenchTransaction):
PATTERNS = [
- (re.compile(r'^CB (?P.*?) FACT (?P\d{2})(?P\d{2})(?P\d{2})\b', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
+ (
+ re.compile(r'^CB (?P.*?) FACT (?P\d{2})(?P\d{2})(?P\d{2})\b', re.IGNORECASE),
+ FrenchTransaction.TYPE_CARD,
+ ),
(re.compile(r'^RET(RAIT)? DAB (?P\d+)-(?P\d+)-.*', re.IGNORECASE), FrenchTransaction.TYPE_WITHDRAWAL),
- (re.compile(r'^RET(RAIT)? DAB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2}) (?P\d{2})H(?P\d{2})\b', re.IGNORECASE), FrenchTransaction.TYPE_WITHDRAWAL),
+ (
+ re.compile(
+ r'^RET(RAIT)? DAB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2}) (?P\d{2})H(?P\d{2})\b',
+ re.IGNORECASE
+ ),
+ FrenchTransaction.TYPE_WITHDRAWAL,
+ ),
(re.compile(r'^VIR(EMENT)?(\.PERIODIQUE)? (?P.*)', re.IGNORECASE), FrenchTransaction.TYPE_TRANSFER),
(re.compile(r'^PRLV (?P.*)', re.IGNORECASE), FrenchTransaction.TYPE_ORDER),
(re.compile(r'^CHEQUE.*', re.IGNORECASE), FrenchTransaction.TYPE_CHECK),
@@ -327,9 +349,18 @@ class Transaction(FrenchTransaction):
(re.compile(r'^(?P.*)( \d+)? QUITTANCE .*', re.IGNORECASE), FrenchTransaction.TYPE_ORDER),
(re.compile(r'^CB [\d\*]+ TOT DIF .*', re.IGNORECASE), FrenchTransaction.TYPE_CARD_SUMMARY),
(re.compile(r'^CB [\d\*]+ (?P.*)', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
- (re.compile(r'^CB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2})\b', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
- (re.compile(r'\*CB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2})\b', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
- (re.compile(r'^FAC CB (?P.*?) (?P\d{2})/(?P\d{2})\b', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
+ (
+ re.compile(r'^CB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2})\b', re.IGNORECASE),
+ FrenchTransaction.TYPE_CARD,
+ ),
+ (
+ re.compile(r'\*CB (?P.*?) (?P\d{2})(?P\d{2})(?P\d{2})\b', re.IGNORECASE),
+ FrenchTransaction.TYPE_CARD,
+ ),
+ (
+ re.compile(r'^FAC CB (?P.*?) (?P\d{2})/(?P\d{2})\b', re.IGNORECASE),
+ FrenchTransaction.TYPE_CARD,
+ ),
(re.compile(r'^\*?CB (?P.*)', re.IGNORECASE), FrenchTransaction.TYPE_CARD),
# For life insurances and capitalisation contracts
(re.compile(r'^VERSEMENT', re.IGNORECASE), FrenchTransaction.TYPE_DEPOSIT),
@@ -392,15 +423,21 @@ def on_load(self):
self.browser.location(link)
else:
message = CleanText(self.doc.xpath('//span[contains(@id, "OIC_QCF")]/p'))(self)
- if message and "investissement financier (QCF) n’est plus valide à ce jour ou que vous avez refusé d’y répondre" in message:
+ expected = "investissement financier (QCF) n’est plus valide à ce jour ou que vous avez refusé d’y répondre"
+ if message and expected in message:
raise ActionNeeded(message)
- mess = CleanText('//body/div[@class="content"]//p[contains(text(), "indisponible pour cause de maintenance")]')(self.doc)
- if mess:
- raise BrowserUnavailable(mess)
+ message = CleanText(
+ '//body/div[@class="content"]//p[contains(text(), "indisponible pour cause de maintenance")]'
+ )(self.doc)
+ if message:
+ raise BrowserUnavailable(message)
# This page is sometimes an useless step to the market website.
- bourse_link = Link('//div[@id="MM_COMPTE_TITRE_pnlbourseoic"]//a[contains(text(), "Accédez à la consultation")]', default=None)(self.doc)
+ bourse_link = Link(
+ '//div[@id="MM_COMPTE_TITRE_pnlbourseoic"]//a[contains(text(), "Accédez à la consultation")]',
+ default=None
+ )(self.doc)
if bourse_link:
self.browser.location(bourse_link)
@@ -415,10 +452,14 @@ def check_no_loans(self):
)
def check_measure_accounts(self):
- return not CleanText('//div[@class="MessageErreur"]/ul/li[contains(text(), "Aucun compte disponible")]')(self.doc)
+ return not CleanText(
+ '//div[@class="MessageErreur"]/ul/li[contains(text(), "Aucun compte disponible")]'
+ )(self.doc)
def check_no_accounts(self):
- no_account_message = CleanText('//span[@id="MM_LblMessagePopinError"]/p[contains(text(), "Aucun compte disponible")]')(self.doc)
+ no_account_message = CleanText(
+ '//span[@id="MM_LblMessagePopinError"]/p[contains(text(), "Aucun compte disponible")]'
+ )(self.doc)
if no_account_message:
raise NoAccountsException(no_account_message)
@@ -435,7 +476,10 @@ def find_and_replace(self, info, acc_id):
return
def _get_account_info(self, a, accounts):
- m = re.search("PostBack(Options)?\([\"'][^\"']+[\"'],\s*['\"]([HISTORIQUE_\w|SYNTHESE_ASSURANCE_CNP|BOURSE|COMPTE_TITRE][\d\w&]+)?['\"]", a.attrib.get('href', ''))
+ m = re.search(
+ r"PostBack(Options)?\([\"'][^\"']+[\"'],\s*['\"]([HISTORIQUE_\w|SYNTHESE_ASSURANCE_CNP|BOURSE|COMPTE_TITRE][\d\w&]+)?['\"]",
+ a.attrib.get('href', '')
+ )
if m is None:
return None
else:
@@ -445,12 +489,16 @@ def _get_account_info(self, a, accounts):
parts = link.split('&')
info = {}
info['link'] = link
- id = re.search("([\d]+)", a.attrib.get('title', ''))
+ id = re.search(r"([\d]+)", a.attrib.get('title', ''))
if len(parts) > 1:
info['type'] = parts[0]
info['id'] = info['_id'] = parts[1]
if id or info['id'] in [acc._info['_id'] for acc in accounts.values()]:
- _id = id.group(1) if id else next(iter({k for k, v in accounts.items() if info['id'] == v._info['_id']}))
+ if id:
+ _id = id.group(1)
+ else:
+ unique_ids = {k for k, v in accounts.items() if info['id'] == v._info['_id']}
+ _id = list(unique_ids)[0]
self.find_and_replace(info, _id)
else:
info['type'] = link
@@ -479,7 +527,7 @@ def _add_account(self, accounts, link, label, account_type, balance, number=None
account.number = number
account.label = label
account.ownership = ownership
- account.type = self.ACCOUNT_TYPES.get(label, info['acc_type'] if 'acc_type' in info else account_type)
+ account.type = self.ACCOUNT_TYPES.get(label, info.get('acc_type', account_type))
if 'PERP' in account.label:
account.type = Account.TYPE_PERP
if 'NUANCES CAPITALISATI' in account.label:
@@ -489,9 +537,12 @@ def _add_account(self, accounts, link, label, account_type, balance, number=None
balance = balance or self.get_balance(account)
- account.balance = Decimal(FrenchTransaction.clean_amount(balance)) if balance and balance is not NotAvailable else NotAvailable
+ if not empty(balance):
+ account.balance = Decimal(FrenchTransaction.clean_amount(balance))
+ account.currency = account.get_currency(balance)
+ else:
+ account.currency = account.balance = NotAvailable
- account.currency = account.get_currency(balance) if balance and balance is not NotAvailable else NotAvailable
account._card_links = []
# Set coming history link to the parent account. At this point, we don't have card account yet.
@@ -508,16 +559,21 @@ def get_balance(self, account):
if account.type not in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_PERP, Account.TYPE_CAPITALISATION):
return NotAvailable
page = self.go_history(account._info).page
- balance = page.doc.xpath('.//tr[td[contains(@id,"NumContrat")]]/td[@class="somme"]/a[contains(@href, $id)]', id=account.id)
+ balance = page.doc.xpath(
+ './/tr[td[contains(@id,"NumContrat")]]/td[@class="somme"]/a[contains(@href, $id)]',
+ id=account.id
+ )
if len(balance) > 0:
balance = CleanText('.')(balance[0])
- balance = balance if balance != '' else NotAvailable
+ if balance == '':
+ balance = NotAvailable
else:
# Specific xpath for some Life Insurances:
balance = page.doc.xpath('//tr[td[contains(text(), $id)]]/td/div[contains(@id, "Solde")]', id=account.id)
if len(balance) > 0:
balance = CleanText('.')(balance[0])
- balance = balance if balance != '' else NotAvailable
+ if balance == '':
+ balance = NotAvailable
else:
# sometimes the accounts are attached but no info is available
balance = NotAvailable
@@ -535,7 +591,7 @@ def get_measure_balance(self, account):
def get_measure_ids(self):
accounts_id = []
for a in self.doc.xpath('//table[@cellpadding="1"]/tr/td[2]/a'):
- accounts_id.append(re.search("(\d{6,})", Attr('.', 'href')(a)).group(1))
+ accounts_id.append(re.search(r"(\d{6,})", Attr('.', 'href')(a)).group(1))
return accounts_id
def has_next_page(self):
@@ -586,7 +642,10 @@ def get_list(self, owner_name):
balance = CleanText('.')(tds[-1].xpath('./a')[i])
self._add_account(accounts, a, label, account_type, balance, ownership=ownership)
- self.logger.warning('we are on the %s website', 'old' if accounts else 'new')
+ website = 'old'
+ if accounts:
+ website = 'new'
+ self.logger.debug('we are on the %s website', website)
if len(accounts) == 0:
# New website
@@ -622,7 +681,12 @@ def get_ownership(self, tds, owner_name):
if len(tds) > 2:
account_owner = CleanText('.', default=None)(tds[2]).upper()
if account_owner and any(title in account_owner for title in ('M', 'MR', 'MLLE', 'MLE', 'MME')):
- if re.search(r'(m|mr|me|mme|mlle|mle|ml)\.? ?(.*)\bou (m|mr|me|mme|mlle|mle|ml)\b(.*)', account_owner, re.IGNORECASE):
+ pattern = re.compile(
+ r'(m|mr|me|mme|mlle|mle|ml)\.? ?(.*)\bou (m|mr|me|mme|mlle|mle|ml)\b(.*)',
+ re.IGNORECASE
+ )
+
+ if pattern.search(account_owner):
return AccountOwnership.CO_OWNER
elif all(n in account_owner for n in owner_name.split()):
return AccountOwnership.OWNER
@@ -639,7 +703,7 @@ def is_access_error(self):
def go_loans_conso(self, tr):
link = tr.xpath('./td/a[contains(@id, "IdaCreditPerm")]')
- m = re.search('CREDITCONSO&(\w+)', link[0].attrib['href'])
+ m = re.search(r'CREDITCONSO&(\w+)', link[0].attrib['href'])
if m:
account = m.group(1)
@@ -665,7 +729,10 @@ def get_loan_list(self):
account.currency = account.get_currency(CleanText('./a')(tds[4]))
accounts[account.id] = account
- self.logger.debug('we are on the %s website', 'old' if accounts else 'new')
+ website = 'old'
+ if accounts:
+ website = 'new'
+ self.logger.debug('we are on the %s website', website)
if len(accounts) == 0:
# New website
@@ -682,12 +749,19 @@ def get_loan_list(self):
for i in tds[0].xpath('.//a/strong'):
label = i.text.strip()
break
- if len(tds) == 3 and Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-2]))) and any(cls in Attr('.', 'id')(tr) for cls in ['dgImmo', 'dgConso']) is False:
- # in case of Consumer credit or revolving credit, we substract avalaible amount with max amout
- # to get what was spend
- balance = Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-2]))) - Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-1])))
- else:
- balance = Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-1])))
+
+ balance = Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-1])))
+ if len(tds) == 3:
+ available = Decimal(FrenchTransaction.clean_amount(CleanText('.')(tds[-2])))
+
+ if (
+ available
+ and not any(cls in Attr('.', 'id')(tr) for cls in ['dgImmo', 'dgConso'])
+ ):
+ # in case of Consumer credit or revolving credit, we substract avalaible amount with max amout
+ # to get what was spend
+ balance = available - balance
+
account = Loan()
account.id = label.split(' ')[-1]
account.label = unicode(label)
@@ -707,14 +781,18 @@ def get_loan_list(self):
self.go_loans_conso(tr)
except ClientError as e:
if e.response.status_code == 401:
- raise ActionNeeded('La situation actuelle de votre dossier ne vous permet pas d\'accéder à cette fonctionnalité. '
- 'Nous vous invitons à contacter votre Centre de relation Clientèle pour accéder à votre prêt.')
+ raise ActionNeeded(
+ 'La situation actuelle de votre dossier ne vous permet pas d\'accéder à cette fonctionnalité. '
+ + 'Nous vous invitons à contacter votre Centre de relation Clientèle pour accéder à votre prêt.'
+ )
raise
d = self.browser.loans_conso()
if d:
account.total_amount = float_to_decimal(d['contrat']['creditMaxAutorise'])
account.available_amount = float_to_decimal(d['situationCredit']['disponible'])
- account.next_payment_amount = float_to_decimal(d['situationCredit']['mensualiteEnCours'])
+ account.next_payment_amount = float_to_decimal(
+ d['situationCredit']['mensualiteEnCours']
+ )
accounts[account.id] = account
return list(accounts.values())
@@ -751,7 +829,13 @@ class item(ItemElement):
obj_currency = Currency(MyTableCell("balance"))
obj_last_payment_date = Date(CleanText(MyTableCell("last_payment_date")))
obj_next_payment_amount = MyDecimal(MyTableCell("next_payment_amount"))
- obj_next_payment_date = Date(CleanText(MyTableCell("next_payment_date", default=''), default=NotAvailable), default=NotAvailable)
+ obj_next_payment_date = Date(
+ CleanText(
+ MyTableCell("next_payment_date", default=''),
+ default=NotAvailable
+ ),
+ default=NotAvailable
+ )
obj_rate = MyDecimal(MyTableCell("rate", default=NotAvailable), default=NotAvailable)
# The website doesn't show any information relative to the loan
# owner, we can then assume they all belong to the credentials owner.
@@ -812,7 +896,10 @@ def go_list(self):
def go_cards(self):
# Do not try to go the card summary if we have no card, it breaks the session
- if self.browser.new_website and not CleanText('//form[@id="main"]//a/span[text()="Mes cartes bancaires"]')(self.doc):
+ if (
+ self.browser.new_website
+ and not CleanText('//form[@id="main"]//a/span[text()="Mes cartes bancaires"]')(self.doc)
+ ):
self.logger.info("Do not try to go the CardsPage, there is not link on the main page")
return
@@ -942,7 +1029,12 @@ def is_history_of(self, account_id):
def go_history(self, info, is_cbtab=False):
form = self.get_form(id='main')
- form['__EVENTTARGET'] = 'MM$%s' % (info['type'] if is_cbtab else 'SYNTHESE')
+ if is_cbtab:
+ target = info['type']
+ else:
+ target = 'SYNTHESE'
+
+ form['__EVENTTARGET'] = 'MM$%s' % target
form['__EVENTARGUMENT'] = info['link']
if "MM$m_CH$IsMsgInit" in form and (form['MM$m_CH$IsMsgInit'] == "0" or info['type'] == 'ASSURANCE_VIE'):
@@ -966,7 +1058,7 @@ def go_history_netpro(self, info, ):
return form.submit()
def get_form_to_detail(self, transaction):
- m = re.match('.*\("(.*)", "(DETAIL_OP&[\d]+).*\)\)', transaction._link)
+ m = re.match(r'.*\("(.*)", "(DETAIL_OP&[\d]+).*\)\)', transaction._link)
# go to detailcard page
form = self.get_form(id='main')
form['__EVENTTARGET'] = m.group(1)
@@ -1039,7 +1131,7 @@ def go_next(self):
return False
account_type = 'COMPTE'
- m = re.search('HISTORIQUE_(\w+)', link[0].attrib['href'])
+ m = re.search(r'HISTORIQUE_(\w+)', link[0].attrib['href'])
if m:
account_type = m.group(1)
@@ -1062,7 +1154,10 @@ def go_life_insurance(self, account):
return
link = self.doc.xpath('//tr[td[contains(., ' + account.id + ') ]]//a')[0]
- m = re.search("PostBackOptions?\([\"']([^\"']+)[\"'],\s*['\"]((REDIR_ASS_VIE)?[\d\w&]+)?['\"]", link.attrib.get('href', ''))
+ m = re.search(
+ r"PostBackOptions?\([\"']([^\"']+)[\"'],\s*['\"]((REDIR_ASS_VIE)?[\d\w&]+)?['\"]",
+ link.attrib.get('href', '')
+ )
if m is not None:
form = self.get_form(id='main')
@@ -1096,7 +1191,10 @@ def go_transfer_page(self):
return False
else:
link = link[0]
- m = re.search("PostBackOptions?\([\"']([^\"']+)[\"'],\s*['\"]([^\"']+)?['\"]", link.attrib.get('href', ''))
+ m = re.search(
+ r"PostBackOptions?\([\"']([^\"']+)[\"'],\s*['\"]([^\"']+)?['\"]",
+ link.attrib.get('href', '')
+ )
form = self.get_form(id='main')
if 'MM$HISTORIQUE_COMPTE$btnCumul' in form:
del form['MM$HISTORIQUE_COMPTE$btnCumul']
@@ -1112,7 +1210,9 @@ def go_emitters(self):
return self.go_transfer_page()
def transfer_unavailable(self):
- return CleanText('//li[contains(text(), "Pour accéder à cette fonctionnalité, vous devez disposer d’un moyen d’authentification renforcée")]')(self.doc)
+ return CleanText(
+ '''//li[contains(text(), "Pour accéder à cette fonctionnalité, vous devez disposer d’un moyen d’authentification renforcée")]'''
+ )(self.doc)
def loan_unavailable_msg(self):
msg = CleanText('//span[@id="MM_LblMessagePopinError"] | //p[@id="MM_ERREUR_PAGE_BLANCHE_pAlert"]')(self.doc)
@@ -1157,10 +1257,14 @@ def get_unavailable_2fa_message(self):
class TransactionPopupPage(LoggedPage, HTMLPage):
def is_here(self):
- return CleanText('''//div[@class="scrollPane"]/table[//caption[contains(text(), "Détail de l'opération")]]''')(self.doc)
+ return CleanText(
+ '''//div[@class="scrollPane"]/table[//caption[contains(text(), "Détail de l'opération")]]'''
+ )(self.doc)
def complete_label(self):
- return CleanText('''//div[@class="scrollPane"]/table[//caption[contains(text(), "Détail de l'opération")]]//tr[2]''')(self.doc)
+ return CleanText(
+ '''//div[@class="scrollPane"]/table[//caption[contains(text(), "Détail de l'opération")]]//tr[2]'''
+ )(self.doc)
class NewLeviesPage(IndexPage):
@@ -1194,7 +1298,11 @@ class item(ItemElement):
obj_date = Date(CleanText(TableCell('date')), dayfirst=True)
def condition(self):
- return not CleanText('''//p[contains(text(), "Vous n'avez pas de prélèvement en attente d'exécution.")]''')(self)
+ return (
+ not CleanText('''
+ //p[contains(text(), "Vous n'avez pas de prélèvement en attente d'exécution.")]
+ ''')(self)
+ )
class OldLeviesPage(IndexPage):
@@ -1228,7 +1336,9 @@ class item(ItemElement):
obj_date = Date(CleanText(TableCell('date')), dayfirst=True)
def condition(self):
- return not CleanText('''//table[@id="MM_SYNTHESE_SDD_RECUS_rpt_dgList_0"]//td[contains(text(), "Vous n'avez pas de prélèvements")]''')(self)
+ return not CleanText('''
+ //table[@id="MM_SYNTHESE_SDD_RECUS_rpt_dgList_0"]//td[contains(text(), "Vous n'avez pas de prélèvements")]
+ ''')(self)
class CardsPage(IndexPage):
@@ -1277,7 +1387,11 @@ def condition(self):
# for now to make the debug easier
immediate_str = '[Immediate card]'
- self.logger.warning('Skip card %s (no history/coming information) %s', Field('number')(self), immediate_str)
+ self.logger.warning(
+ 'Skip card %s (no history/coming information) %s',
+ Field('number')(self),
+ immediate_str,
+ )
return False
@@ -1341,7 +1455,9 @@ def get_card_coming_info(self, number, info):
class CardsOldWebsitePage(IndexPage):
def is_here(self):
- return CleanText('//span[@id="MM_m_CH_lblTitle" and contains(text(), "Historique de vos encours CB")]')(self.doc)
+ return CleanText('''
+ //span[@id="MM_m_CH_lblTitle" and contains(text(), "Historique de vos encours CB")]
+ ''')(self.doc)
def get_account(self):
infos = CleanText('.//span[@id="MM_HISTORIQUE_CB"]/table[position()=1]//td')(self.doc)
@@ -1460,7 +1576,10 @@ def iter_investment(self):
inv = Investment()
inv.label = CleanText('.')(tbody.xpath('./tr[1]/td[1]/a/span')[0])
inv.code = CleanText('.')(tbody.xpath('./tr[1]/td[1]/a')[0]).split(' - ')[1]
- inv.code_type = Investment.CODE_TYPE_ISIN if is_isin_valid(inv.code) else NotAvailable
+ if is_isin_valid(inv.code):
+ inv.code_type = Investment.CODE_TYPE_ISIN
+ else:
+ inv.code_type = NotAvailable
inv.quantity = self.parse_decimal(tbody.xpath('./tr[2]/td[2]')[0])
inv.unitvalue = self.parse_decimal(tbody.xpath('./tr[2]/td[3]')[0])
inv.unitprice = self.parse_decimal(tbody.xpath('./tr[2]/td[5]')[0])
@@ -1471,10 +1590,13 @@ def iter_investment(self):
def get_valuation_diff(self, account):
val = CleanText(self.doc.xpath('//td[contains(text(), "values latentes")]/following-sibling::*[1]'))
- account.valuation_diff = CleanDecimal(Regexp(val, '([^\(\)]+)'), replace_dots=True)(self)
+ account.valuation_diff = CleanDecimal(Regexp(val, r'([^\(\)]+)'), replace_dots=True)(self)
def is_on_right_portfolio(self, account):
- return len(self.doc.xpath('//form[@class="choixCompte"]//option[@selected and contains(text(), $id)]', id=account._info['id']))
+ return len(self.doc.xpath(
+ '//form[@class="choixCompte"]//option[@selected and contains(text(), $id)]',
+ id=account._info['id']
+ ))
def get_compte(self, account):
return self.doc.xpath('//option[contains(text(), $id)]/@value', id=account._info['id'])[0]
@@ -1698,7 +1820,12 @@ def parse(self, el):
# Autres comptes
if value == 'AC':
raise SkipItem()
- self.env['category'] = 'Interne' if value[0] == 'I' else 'Externe'
+
+ if value[0] == 'I':
+ self.env['category'] = 'Interne'
+ else:
+ self.env['category'] = 'Externe'
+
if self.env['category'] == 'Interne':
# TODO use after 'I'?
_id = Regexp(CleanText('.'), r'- (\w+\d\w+)')(self) # at least one digit
@@ -1749,22 +1876,34 @@ def is_here(self):
def can_transfer(self, account):
for o in self.doc.xpath('//select[@id="MM_VIREMENT_SAISIE_VIREMENT_ddlCompteDebiter"]/option'):
- if Regexp(CleanText('.'), '- (\d+)')(o) in account.id:
+ if Regexp(CleanText('.'), r'- (\d+)')(o) in account.id:
return True
def get_origin_account_value(self, account):
- origin_value = [Attr('.', 'value')(o) for o in self.doc.xpath('//select[@id="MM_VIREMENT_SAISIE_VIREMENT_ddlCompteDebiter"]/option') if
- Regexp(CleanText('.'), '- (\d+)')(o) in account.id]
+ origin_value = [
+ Attr('.', 'value')(o)
+ for o in self.doc.xpath('//select[@id="MM_VIREMENT_SAISIE_VIREMENT_ddlCompteDebiter"]/option')
+ if Regexp(CleanText('.'), r'- (\d+)')(o) in account.id
+ ]
assert len(origin_value) == 1, 'error during origin account matching'
return origin_value[0]
def get_recipient_value(self, recipient):
if recipient.category == 'Externe':
- recipient_value = [Attr('.', 'value')(o) for o in self.doc.xpath(self.RECIPIENT_XPATH) if
- Regexp(CleanText('.'), '.* - ([A-Za-z0-9]*) -', default=NotAvailable)(o) == recipient.iban]
+ recipient_value = [
+ Attr('.', 'value')(o)
+ for o in self.doc.xpath(self.RECIPIENT_XPATH)
+ if Regexp(CleanText('.'), r'.* - ([A-Za-z0-9]*) -', default=NotAvailable)(o) == recipient.iban
+ ]
elif recipient.category == 'Interne':
- recipient_value = [Attr('.', 'value')(o) for o in self.doc.xpath(self.RECIPIENT_XPATH) if
- Regexp(CleanText('.'), '- (\d+)', default=NotAvailable)(o) and Regexp(CleanText('.'), '- (\d+)', default=NotAvailable)(o) in recipient.id]
+ recipient_value = [
+ Attr('.', 'value')(o)
+ for o in self.doc.xpath(self.RECIPIENT_XPATH)
+ if (
+ Regexp(CleanText('.'), r'- (\d+)', default=NotAvailable)(o)
+ and Regexp(CleanText('.'), r'- (\d+)', default=NotAvailable)(o) in recipient.id
+ )
+ ]
assert len(recipient_value) == 1, 'error during recipient matching'
return recipient_value[0]
@@ -1802,7 +1941,10 @@ def continue_transfer(self, origin_label, recipient_label, label):
form = self.get_form(id='main')
transfer_type = self.get_transfer_type()
- fill = lambda s, t: s % (t.upper(), t.capitalize())
+
+ def fill(s, t):
+ return s % (t.upper(), t.capitalize())
+
form['__EVENTTARGET'] = 'MM$VIREMENT$m_WizardBar$m_lnkNext$m_lnkButton'
form[fill('MM$VIREMENT$SAISIE_VIREMENT_%s$m_Virement%s$txtIdentBenef', transfer_type)] = recipient_label
form[fill('MM$VIREMENT$SAISIE_VIREMENT_%s$m_Virement%s$txtIdent', transfer_type)] = origin_label
@@ -1813,7 +1955,10 @@ def continue_transfer(self, origin_label, recipient_label, label):
def go_add_recipient(self):
form = self.get_form(id='main')
link = self.doc.xpath('//a[span[contains(text(), "Ajouter un compte bénéficiaire")]]')[0]
- m = re.search("PostBackOptions?\([\"']([^\"']+)[\"'],\s*['\"]([^\"']+)?['\"]", link.attrib.get('href', ''))
+ m = re.search(
+ r"PostBackOptions?\([\"']([^\"']+)[\"'],\s*['\"]([^\"']+)?['\"]",
+ link.attrib.get('href', '')
+ )
form['__EVENTTARGET'] = m.group(1)
form['__EVENTARGUMENT'] = m.group(2)
form.submit()
@@ -1869,11 +2014,20 @@ def update_transfer(self, transfer, account=None, recipient=None):
or CleanText('.//tr[td[contains(text(), "Libellé")]]/td[not(@class)]')(self.doc)
or CleanText('.//tr[th[contains(text(), "Libellé")]]/td[not(@class)]')(self.doc)
)
- transfer.exec_date = Date(CleanText('.//tr[th[contains(text(), "En date du")]]/td[not(@class)]'), dayfirst=True)(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.currency = FrenchTransaction.Currency('.//tr[td[contains(text(), "Montant")]]/td[not(@class)] | \
- .//tr[th[contains(text(), "Montant")]]/td[not(@class)]')(self.doc)
+ transfer.exec_date = Date(
+ CleanText('.//tr[th[contains(text(), "En date du")]]/td[not(@class)]'),
+ dayfirst=True
+ )(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.currency = FrenchTransaction.Currency('''
+ .//tr[td[contains(text(), "Montant")]]/td[not(@class)]
+ | .//tr[th[contains(text(), "Montant")]]/td[not(@class)]
+ ''')(self.doc)
# recipient transfer informations, update information if there is no OTP SMS validation
if recipient:
@@ -1881,12 +2035,16 @@ def update_transfer(self, transfer, account=None, recipient=None):
transfer.recipient_id = recipient.id
if recipient.category == 'Externe':
- for word in Upper(CleanText('.//tr[th[contains(text(), "Compte à créditer")]]/td[not(@class)]'))(self.doc).split():
+ all_text = Upper(CleanText(
+ '''.//tr[th[contains(text(), "Compte à créditer")]]/td[not(@class)]'''
+ ))(self.doc)
+
+ for word in all_text.split():
if is_iban_valid(word):
transfer.recipient_iban = word
break
else:
- assert False, 'Unable to find IBAN (original was %s)' % recipient.iban
+ raise AssertionError('Unable to find IBAN (original was %s)' % recipient.iban)
else:
transfer.recipient_iban = recipient.iban
@@ -1902,7 +2060,9 @@ def update_transfer(self, transfer, account=None, recipient=None):
class ProTransferConfirmPage(TransferConfirmPage):
def is_here(self):
- return bool(CleanText('//span[@id="MM_m_CH_lblTitle" and contains(text(), "Confirmez votre virement")]')(self.doc))
+ return bool(CleanText('''
+ //span[@id="MM_m_CH_lblTitle" and contains(text(), "Confirmez votre virement")]
+ ''')(self.doc))
def continue_transfer(self, origin_label, recipient, label):
# Pro internal transfer initiation doesn't need a second step.
@@ -1910,18 +2070,26 @@ def continue_transfer(self, origin_label, recipient, label):
def create_transfer(self, account, recipient, transfer):
t = Transfer()
- t.currency = FrenchTransaction.Currency('//span[@id="MM_VIREMENT_CONF_VIREMENT_MontantVir"] | \
- //span[@id="MM_VIREMENT_CONF_VIREMENT_lblMontantSelect"]')(self.doc)
- t.amount = CleanDecimal('//span[@id="MM_VIREMENT_CONF_VIREMENT_MontantVir"] | \
- //span[@id="MM_VIREMENT_CONF_VIREMENT_lblMontantSelect"]', replace_dots=True)(self.doc)
+ t.currency = FrenchTransaction.Currency('''
+ //span[@id="MM_VIREMENT_CONF_VIREMENT_MontantVir"]
+ | //span[@id="MM_VIREMENT_CONF_VIREMENT_lblMontantSelect"]
+ ''')(self.doc)
+ t.amount = CleanDecimal(
+ '''
+ //span[@id="MM_VIREMENT_CONF_VIREMENT_MontantVir"]
+ | //span[@id="MM_VIREMENT_CONF_VIREMENT_lblMontantSelect"]
+ ''',
+ replace_dots=True
+ )(self.doc)
t.account_iban = account.iban
if recipient.category == 'Externe':
- for word in Upper(CleanText('//span[@id="MM_VIREMENT_CONF_VIREMENT_lblCptCrediterResult"]'))(self.doc).split():
+ all_text = Upper(CleanText('//span[@id="MM_VIREMENT_CONF_VIREMENT_lblCptCrediterResult"]'))(self.doc)
+ for word in all_text.split():
if is_iban_valid(word):
t.recipient_iban = word
break
else:
- assert False, 'Unable to find IBAN (original was %s)' % recipient.iban
+ raise AssertionError('Unable to find IBAN (original was %s)' % recipient.iban)
else:
t.recipient_iban = recipient.iban
t.recipient_iban = recipient.iban
@@ -1931,8 +2099,10 @@ def create_transfer(self, account, recipient, transfer):
t.recipient_label = recipient.label
t._account = account
t._recipient = recipient
- t.label = CleanText('//span[@id="MM_VIREMENT_CONF_VIREMENT_Libelle"] | \
- //span[@id="MM_VIREMENT_CONF_VIREMENT_lblMotifSelect"]')(self.doc)
+ t.label = CleanText('''
+ //span[@id="MM_VIREMENT_CONF_VIREMENT_Libelle"]
+ | //span[@id="MM_VIREMENT_CONF_VIREMENT_lblMotifSelect"]
+ ''')(self.doc)
t.exec_date = Date(CleanText('//span[@id="MM_VIREMENT_CONF_VIREMENT_DateVir"]'), dayfirst=True)(self.doc)
t.account_balance = account.balance
return t
@@ -1943,7 +2113,7 @@ def is_here(self):
return bool(CleanText('//h2[contains(text(), "Accusé de réception")]')(self.doc))
def populate_reference(self, transfer):
- transfer.id = Regexp(CleanText('//p[contains(text(), "a bien été enregistré")]'), '(\d+)')(self.doc)
+ transfer.id = Regexp(CleanText('//p[contains(text(), "a bien été enregistré")]'), r'(\d+)')(self.doc)
return transfer
@@ -1952,7 +2122,10 @@ def is_here(self):
return bool(CleanText('//span[@id="MM_m_CH_lblTitle" and contains(text(), "Accusé de réception")]')(self.doc))
def populate_reference(self, transfer):
- transfer.id = Regexp(CleanText('//span[@id="MM_VIREMENT_AR_VIREMENT_lblVirementEnregistre"]'), '(\d+( - \d+)?)')(self.doc)
+ transfer.id = Regexp(
+ CleanText('//span[@id="MM_VIREMENT_AR_VIREMENT_lblVirementEnregistre"]'),
+ r'(\d+( - \d+)?)'
+ )(self.doc)
return transfer
@@ -1960,7 +2133,9 @@ class ProTransferPage(TransferPage):
RECIPIENT_XPATH = '//select[@id="MM_VIREMENT_SAISIE_VIREMENT_ddlCompteCrediterPro"]/option'
def is_here(self):
- return CleanText('//span[contains(text(), "Créer une liste de virements")] | //span[contains(text(), "Réalisez un virement")]')(self.doc)
+ return CleanText('''
+ //span[contains(text(), "Créer une liste de virements")] | //span[contains(text(), "Réalisez un virement")]
+ ''')(self.doc)
@method
class iter_recipients(MyRecipients):
@@ -2061,8 +2236,10 @@ def on_load(self):
raise AddRecipientBankError(message=error)
def is_here(self):
- return bool(CleanText('//h2[contains(text(), "Ajouter un compte bénéficiaire")] |\
- //h2[contains(text(), "Confirmer l\'ajout d\'un compte bénéficiaire")]')(self.doc))
+ return bool(CleanText('''
+ //h2[contains(text(), "Ajouter un compte bénéficiaire")]
+ | //h2[contains(text(), "Confirmer l\'ajout d\'un compte bénéficiaire")]
+ ''')(self.doc))
def post_recipient(self, recipient):
form = self.get_form(id='main')
@@ -2102,14 +2279,18 @@ class ProAddRecipientPage(RecipientPage):
FORM_FIELD_ADD = 'MM$WIZARD_AJOUT_COMPTE_TIERS$COMPTES_TIERS_ADD'
def is_here(self):
- return CleanText('//span[@id="MM_m_CH_lblTitle" and contains(text(), "Ajoutez un compte tiers")] |\
- //span[@id="MM_m_CH_lblTitle" and contains(text(), "Confirmez votre ajout")]')(self.doc)
+ return CleanText('''
+ //span[@id="MM_m_CH_lblTitle" and contains(text(), "Ajoutez un compte tiers")]
+ | //span[@id="MM_m_CH_lblTitle" and contains(text(), "Confirmez votre ajout")]
+ ''')(self.doc)
class TransactionsDetailsPage(LoggedPage, HTMLPage):
def is_here(self):
- return bool(CleanText('//h2[contains(text(), "Débits différés imputés")] | //span[@id="MM_m_CH_lblTitle" and contains(text(), "Débit différé imputé")]')(self.doc))
+ return bool(CleanText(
+ '//h2[contains(text(), "Débits différés imputés")] | //span[@id="MM_m_CH_lblTitle" and contains(text(), "Débit différé imputé")]'
+ )(self.doc))
@pagination
@method
@@ -2124,8 +2305,10 @@ class get_detail(TableElement):
def next_page(self):
# only for new website, don't have any accounts with enough deferred card transactions on old webiste
- if self.page.doc.xpath('//a[contains(@id, "lnkSuivante") and not(contains(@disabled,"disabled")) \
- and not(contains(@class, "aspNetDisabled"))]'):
+ if self.page.doc.xpath('''
+ //a[contains(@id, "lnkSuivante") and not(contains(@disabled,"disabled"))
+ and not(contains(@class, "aspNetDisabled"))]
+ '''):
form = self.page.get_form(id='main')
form['__EVENTTARGET'] = "MM$ECRITURE_GLOBALE$lnkSuivante"
form['__EVENTARGUMENT'] = ''
@@ -2147,7 +2330,7 @@ def obj_amount(self):
def go_form_to_summary(self):
# return to first page
to_history = Link(self.doc.xpath('//a[contains(text(), "Retour à l\'historique")]'))(self.doc)
- n = re.match('.*\([\'\"](MM\$.*?)[\'\"],.*\)$', to_history)
+ n = re.match(r'.*\([\'\"](MM\$.*?)[\'\"],.*\)$', to_history)
form = self.get_form(id='main')
form['__EVENTTARGET'] = n.group(1)
form.submit()
@@ -2188,7 +2371,10 @@ class iter_documents(ListElement):
@property
def item_xpath(self):
if Env('has_subscription')(self):
- return '//h3[contains(text(), "%s")]//following-sibling::div[@class="panel"][1]/table/tbody/tr' % Env('sub_id')(self)
+ return (
+ '//h3[contains(text(), "%s")]//following-sibling::div[@class="panel"][1]/table/tbody/tr'
+ % Env('sub_id')(self)
+ )
return '//div[@id="MM_CONSULTATION_RELEVES_COURRIERS_EDOCUMENTS_divRelevesCourriers"]/table/tbody/tr'
class item(ItemElement):
@@ -2196,7 +2382,12 @@ class item(ItemElement):
obj_format = 'pdf'
obj_url = Regexp(Link('.//td[@class="telecharger"]//a'), r'WebForm_PostBackOptions\("(\S*)"')
- obj_id = Format('%s_%s_%s', Env('sub_id'), CleanText('./td[2]', symbols='/', replace=[(' ', '_')]), Regexp(CleanText('./td[3]'), r'([\wé]*)'))
+ obj_id = Format(
+ '%s_%s_%s',
+ Env('sub_id'),
+ CleanText('./td[2]', symbols='/', replace=[(' ', '_')]),
+ Regexp(CleanText('./td[3]'), r'([\wé]*)')
+ )
obj_label = Format('%s %s', CleanText('./td[3]'), CleanText('./td[2]'))
obj_date = Date(CleanText('./td[2]'), dayfirst=True)
diff --git a/modules/caissedepargne/transfer_pages.py b/modules/caissedepargne/transfer_pages.py
index b1f69a7aeacaea7f71250475648c72dacc427c76..0afeb75dac6763e036aa1ac816023e35da36579a 100644
--- a/modules/caissedepargne/transfer_pages.py
+++ b/modules/caissedepargne/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
import re