diff --git a/modules/afer/browser.py b/modules/afer/browser.py index 8cb7fefa0d04a0bf6838789c6734c7e436c4aa97..5b7b70479154aa49542639203f50315e39f4eb80 100644 --- a/modules/afer/browser.py +++ b/modules/afer/browser.py @@ -20,28 +20,29 @@ from __future__ import unicode_literals from random import randint -from weboob.browser import URL, LoginBrowser, need_login -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from weboob.browser.browsers import URL, LoginBrowser, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable, BrowserPasswordExpired from weboob.tools.compat import basestring -from .pages import LoginPage, IndexPage, BadLogin, AccountDetailPage, AccountHistoryPage +from .pages import ( + LoginPage, IndexPage, WrongPasswordPage, WrongWebsitePage, + AccountDetailPage, AccountHistoryPage, MigrationPage, +) class AferBrowser(LoginBrowser): BASEURL = 'https://adherent.gie-afer.fr' - login = URL('/web/ega.nsf/listeAdhesions\?OpenForm', LoginPage) - bad_login = URL('/names.nsf\?Login', BadLogin) + login = URL(r'/espaceadherent/MonCompte/Connexion$', LoginPage) + wrong_password = URL(r'/espaceadherent/MonCompte/Connexion\?err=6001', WrongPasswordPage) + wrong_website = URL(r'/espaceadherent/MonCompte/Connexion\?err=6008', WrongWebsitePage) + migration = URL(r'/espaceadherent/MonCompte/Migration', MigrationPage) index = URL('/web/ega.nsf/listeAdhesions\?OpenForm', IndexPage) account_detail = URL('/web/ega.nsf/soldeEpargne\?openForm', AccountDetailPage) account_history = URL('/web/ega.nsf/generationSearchModule\?OpenAgent', AccountHistoryPage) history_detail = URL('/web/ega.nsf/WOpendetailOperation\?OpenAgent', AccountHistoryPage) def do_login(self): - """ - Attempt to log in. - Note: this method does nothing if we are already logged in. - """ assert isinstance(self.username, basestring) assert isinstance(self.password, basestring) self.login.go() @@ -51,13 +52,14 @@ def do_login(self): except BrowserUnavailable: raise BrowserIncorrectPassword() - if self.bad_login.is_here(): + if self.migration.is_here(): + raise BrowserPasswordExpired(self.page.get_error()) + + if self.wrong_password.is_here(): error = self.page.get_error() - if "La saisie de l’identifiant ou du code confidentiel est incorrecte" in error or \ - "Veuillez-vous identifier" in error: + if error: raise BrowserIncorrectPassword(error) - else: - assert False, "Message d'erreur inconnu: %s" % error + assert False, 'We landed on WrongPasswordPage but no error message was fetched.' @need_login diff --git a/modules/afer/compat/weboob_capabilities_bank.py b/modules/afer/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/afer/compat/weboob_capabilities_bank.py +++ b/modules/afer/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/afer/compat/weboob_exceptions.py b/modules/afer/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/afer/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/afer/module.py b/modules/afer/module.py index 01a4a222556af8815e17a0cbd873aa1f89399c80..5116d8393c8edfccde6d7f4ee81dd73cad523afb 100644 --- a/modules/afer/module.py +++ b/modules/afer/module.py @@ -39,8 +39,11 @@ class AferModule(Module, CapBankWealth): VERSION = '1.5' BROWSER = AferBrowser - CONFIG = BackendConfig(ValueBackendPassword('login', label='Username', regexp='[A-z]\d+', masked=False), - ValueBackendPassword('password', label=u"mdp", regexp='\d{1,8}')) + CONFIG = BackendConfig( + ValueBackendPassword('login', label='Identifiant', regexp=r'.+', masked=False), + ValueBackendPassword('password', label="Mot de passe", regexp=r'\d{1,8}|[a-zA-Z0-9]{7,30}') + # TODO lose previous regex (and in backend) once users credentials migration is complete + ) def create_default_browser(self): return self.create_browser(self.config['login'].get(), diff --git a/modules/afer/pages.py b/modules/afer/pages.py index bbdf8baee72dd118f7bdefc4b9fe34df1c0be052..ea02b77ed8af92b0df73422019f844a77461c7b9 100644 --- a/modules/afer/pages.py +++ b/modules/afer/pages.py @@ -28,23 +28,36 @@ from weboob.browser.pages import HTMLPage, LoggedPage, pagination from .compat.weboob_capabilities_bank import Account, Investment, Transaction from weboob.capabilities.base import NotAvailable -from weboob.exceptions import BrowserUnavailable, ActionNeeded +from .compat.weboob_exceptions import BrowserUnavailable, ActionNeeded class LoginPage(HTMLPage): def login(self, login, passwd): - form = self.get_form(name='_DominoForm') - form['Username'] = login + form = self.get_form(id='loginForm') + form['username'] = login form['password'] = passwd form.submit() - def is_here(self): - return bool(self.doc.xpath('//form[@name="_DominoForm"]')) + +class WrongPasswordPage(HTMLPage): + def get_error(self): + return CleanText('//p[contains(text(), "Votre saisie est erronée")]')(self.doc) + + +class WrongWebsitePage(HTMLPage): + # We land on this page when the website indicates that + # an account is already created on the 'Aviva et moi' space, + # So we check the message and raise ActionNeeded with it + def on_load(self): + message = CleanText('//p[contains(text(), "Vous êtes déjà inscrit")]')(self.doc) + if message: + raise ActionNeeded(message) + assert False, 'We landed on WrongWebsitePage but no message was fetched.' -class BadLogin(HTMLPage): +class MigrationPage(HTMLPage): def get_error(self): - return CleanText('//div[@id="idDivErrorLogin"]')(self.doc) + return CleanText('//h1[contains(text(), "Votre nouvel identifiant et mot de passe")]')(self.doc) class IndexPage(LoggedPage, HTMLPage): diff --git a/modules/amazon/browser.py b/modules/amazon/browser.py index 6a3f88d881dbfc71a2258dc1e11ee14b9a8049b5..cf1b096445570386948b538d17f7ce922ff0b9b2 100644 --- a/modules/amazon/browser.py +++ b/modules/amazon/browser.py @@ -20,8 +20,8 @@ from __future__ import unicode_literals from datetime import date -from weboob.browser import LoginBrowser, URL, need_login, StatesMixin -from weboob.exceptions import ( +from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin +from .compat.weboob_exceptions import ( BrowserIncorrectPassword, BrowserUnavailable, ImageCaptchaQuestion, BrowserQuestion, ActionNeeded, WrongCaptchaResponse ) diff --git a/modules/amazon/compat/weboob_exceptions.py b/modules/amazon/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/amazon/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/amazonstorecard/browser.py b/modules/amazonstorecard/browser.py index 8f63a56d25adf4eb97f5493ae4c7df2dd493abee..aaca60da6886a8d3a4d94741e75ff964f14c244f 100644 --- a/modules/amazonstorecard/browser.py +++ b/modules/amazonstorecard/browser.py @@ -25,7 +25,7 @@ from weboob.browser.browsers import URL, LoginBrowser, need_login from .compat.weboob_capabilities_bank import AccountNotFound -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.compat import unquote from .pages import ActivityPage, SomePage, StatementPage, StatementsPage, SummaryPage diff --git a/modules/amazonstorecard/compat/weboob_capabilities_bank.py b/modules/amazonstorecard/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/amazonstorecard/compat/weboob_capabilities_bank.py +++ b/modules/amazonstorecard/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/amazonstorecard/compat/weboob_exceptions.py b/modules/amazonstorecard/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/amazonstorecard/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ameli/browser.py b/modules/ameli/browser.py index 72b47aaeb1d83b0347e81b690ed9d2b69ae5c710..43f316f57cd445bb119231f2293c4574b994c2df 100644 --- a/modules/ameli/browser.py +++ b/modules/ameli/browser.py @@ -42,7 +42,7 @@ def do_login(self): @need_login def iter_subscription(self): self.subscription_page.go() - return self.page.iter_subscriptions() + yield self.page.get_subscription() @need_login def iter_documents(self, subscription): @@ -75,6 +75,6 @@ def iter_documents(self, subscription): # then we set Rechercher to actionEvt to filter for this subscription, within last 6 months # without first request we would have filter for this subscription but within last 2 months params['actionEvt'] = 'Rechercher' - params['Beneficiaire'] = subscription._param + params['Beneficiaire'] = 'tout_selectionner' self.documents_page.go(params=params) return self.page.iter_documents(subid=subscription.id) diff --git a/modules/ameli/compat/weboob_exceptions.py b/modules/ameli/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ameli/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ameli/pages.py b/modules/ameli/pages.py index d90467b404199abb43590812998700a34b6541b2..91ff969d866d93ebf02f211c9547d376cbb794ec 100644 --- a/modules/ameli/pages.py +++ b/modules/ameli/pages.py @@ -19,15 +19,15 @@ from __future__ import unicode_literals -import re from weboob.browser.elements import method, ListElement, ItemElement -from weboob.browser.filters.html import Attr, Link +from weboob.browser.filters.html import Link from .compat.weboob_browser_filters_standard import CleanText, Regexp, CleanDecimal, Currency, Field, Format, Env from weboob.browser.pages import LoggedPage, HTMLPage, PartialHTMLPage from weboob.capabilities.bill import Subscription, Bill -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from weboob.tools.date import parse_french_date +from weboob.tools.json import json class LoginPage(HTMLPage): @@ -45,32 +45,22 @@ def on_load(self): class SubscriptionPage(LoggedPage, HTMLPage): - @method - class iter_subscriptions(ListElement): - item_xpath = '//div[@id="corps-de-la-page"]//div[@class="tableau"]/div' - - class item(ItemElement): - klass = Subscription - - obj__labelid = Attr('.', 'aria-labelledby') + def get_subscription(self): + sub = Subscription() + # DON'T TAKE social security number for id because it's a very confidential data, take birth date instead + sub.id = CleanText('//button[@id="idAssure"]//td[@class="dateNaissance"]')(self.doc).replace('/', '') + sub.label = sub.subscriber = CleanText('//div[@id="pageAssure"]//span[@class="NomEtPrenomLabel"]')(self.doc) - def obj__birthdate(self): - return CleanText('//button[@id="%s"]//td[@class="dateNaissance"]' % Field('_labelid')(self))(self) + return sub - def obj_id(self): - # DON'T TAKE social security number for id because it's a very confidential data, take birth date instead - return ''.join(re.findall(r'\d+', Field('_birthdate')(self))) - def obj__param(self): - reversed_date = ''.join(reversed(re.findall(r'\d+', Field('_birthdate')(self)))) - name = CleanText('//button[@id="%s"]//td[@class="nom"]' % Field('_labelid')(self))(self) - return '%s!-!%s!-!1' % (reversed_date, name) - - obj_subscriber = CleanText('.//span[@class="NomEtPrenomLabel"]') - obj_label = obj_subscriber +class DocumentsPage(LoggedPage, PartialHTMLPage): + ENCODING = 'utf-8' + def build_doc(self, content): + res = json.loads(content) + return super(DocumentsPage, self).build_doc(res['tableauPaiement'].encode('utf-8')) -class DocumentsPage(LoggedPage, PartialHTMLPage): @method class iter_documents(ListElement): item_xpath = '//ul[@id="unordered_list"]//li[has-class("rowitem")]' @@ -82,7 +72,7 @@ class item(ItemElement): obj_label = CleanText('.//div[has-class("col-label")]') obj_price = CleanDecimal.French('.//div[has-class("col-montant")]/span') obj_currency = Currency('.//div[has-class("col-montant")]/span') - obj_url = Link('.//a[@class="downdetail"]') + obj_url = Link('.//div[@class="col-download"]/a') obj_format = 'pdf' def obj_date(self): diff --git a/modules/amelipro/browser.py b/modules/amelipro/browser.py index 8b695f771ccc3dcf70ab6db8b56be0fced301149..27a2f66b0a2ee9a64ea3c04e0d3974aba431a8a7 100644 --- a/modules/amelipro/browser.py +++ b/modules/amelipro/browser.py @@ -17,8 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.capabilities.bill import Detail from weboob.tools.compat import urlencode from decimal import Decimal diff --git a/modules/amelipro/compat/__init__.py b/modules/amelipro/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/amelipro/compat/weboob_exceptions.py b/modules/amelipro/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/amelipro/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/americanexpress/browser.py b/modules/americanexpress/browser.py index 0282188628b20e6d94936812612ba1740cd299ca..1ccee3283456f832d44c04d2a6d5e7bcb6a02f1e 100644 --- a/modules/americanexpress/browser.py +++ b/modules/americanexpress/browser.py @@ -17,17 +17,20 @@ # 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 + import datetime -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.browser.browsers import LoginBrowser, need_login from weboob.browser.exceptions import HTTPNotFound, ServerError from .compat.weboob_browser_url import URL +from dateutil.parser import parse as parse_date from .pages import ( AccountsPage, JsonBalances, JsonPeriods, JsonHistory, JsonBalances2, CurrencyPage, LoginPage, WrongLoginPage, AccountSuspendedPage, - NoCardPage, NotFoundPage + NoCardPage, NotFoundPage, ) @@ -49,16 +52,16 @@ class AmericanExpressBrowser(LoginBrowser): js_posted = URL(r'/account-data/v1/financials/transactions\?limit=1000&offset=(?P\d+)&statement_end_date=(?P[0-9-]+)&status=posted', JsonHistory) js_periods = URL(r'/account-data/v1/financials/statement_periods', JsonPeriods) - currency_page = URL(r'https://www.aexp-static.com/cdaas/axp-app/modules/axp-offers/1.11.1/fr-fr/axp-offers.json', CurrencyPage) + currency_page = URL(r'https://www.aexp-static.com/cdaas/axp-app/modules/axp-balance-summary/4.7.0/(?P\w\w-\w\w)/axp-balance-summary.json', CurrencyPage) - no_card = URL('https://www.americanexpress.com/us/content/no-card/', - 'https://www.americanexpress.com/us/no-card/', NoCardPage) + no_card = URL(r'https://www.americanexpress.com/us/content/no-card/', + r'https://www.americanexpress.com/us/no-card/', NoCardPage) not_found = URL(r'/accounts/error', NotFoundPage) SUMMARY_CARD_LABEL = [ - u'PAYMENT RECEIVED - THANK YOU', - u'PRELEVEMENT AUTOMATIQUE ENREGISTRE-MERCI' + 'PAYMENT RECEIVED - THANK YOU', + 'PRELEVEMENT AUTOMATIQUE ENREGISTRE-MERCI', ] def __init__(self, *args, **kwargs): @@ -73,7 +76,6 @@ def do_login(self): if self.wrong_login.is_here() or self.login.is_here() or self.account_suspended.is_here(): raise BrowserIncorrectPassword() - @need_login def get_accounts(self): self.accounts.go() @@ -91,7 +93,8 @@ def get_accounts(self): self.page.set_balances(accounts) # get currency - self.currency_page.go() + loc = self.session.cookies.get_dict(domain=".americanexpress.com")['axplocale'].lower() + self.currency_page.go(locale=loc) currency = self.page.get_currency() for acc in accounts: @@ -120,29 +123,30 @@ def iter_history(self, account): @need_login def iter_coming(self, account): - """ - Coming transactions can be found in a'pending' JSON if it exists (corresponding a 'Transactions en attente' tab on the website), - as well as in a 'posted' JSON (corresponding a 'Transactions enregistrées' tab on the website for futur transactions) - """ + # Coming transactions can be found in a 'pending' JSON if it exists + # ('En attente' tab on the website), as well as in a 'posted' JSON + # ('Enregistrées' tab on the website) + # "pending" have no vdate and debit date is in future self.js_periods.go(headers={'account_token': account._token}) - date = datetime.datetime.strptime(self.page.get_periods()[0], '%Y-%m-%d').date() periods = self.page.get_periods() + date = parse_date(periods[0]).date() today = datetime.date.today() - try: - self.js_pending.go(offset=0, headers={'account_token': account._token}) - # when the latest period ends today we can't know the coming debit date - if date != today: + # when the latest period ends today we can't know the coming debit date + if date != today: + try: + self.js_pending.go(offset=0, headers={'account_token': account._token}) + except ServerError as exc: + # At certain times of the month a connection might not have pendings; + # in that case, `js_pending.go` would throw a 502 error Bad Gateway + error_code = exc.response.json().get('code') + error_message = exc.response.json().get('message') + self.logger.warning('No pendings page to access to, got error %s and message "%s" instead.', error_code, error_message) + else: for tr in self.page.iter_history(periods=periods): if tr._owner == account._idforJSON: tr.date = date yield tr - except ServerError as exc: - # At certain time of the month a connection might not have pendings; - # in that case, `js_pending.go` would throw a 502 error Bad Gateway - error_code = exc.response.json().get('code') - error_message = exc.response.json().get('message') - self.logger.warning('No pendings page to access to, got error %s and message "%s" instead.' % (error_code, error_message)) # "posted" have a vdate but debit date can be future or past for p in periods: diff --git a/modules/americanexpress/compat/weboob_capabilities_bank.py b/modules/americanexpress/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/americanexpress/compat/weboob_capabilities_bank.py +++ b/modules/americanexpress/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/americanexpress/compat/weboob_exceptions.py b/modules/americanexpress/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/americanexpress/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/americanexpress/pages.py b/modules/americanexpress/pages.py index 99bac79e791f1452a5e2f1e4ab194b0def0b2638..274efd56a35ba87815b077e5e1faec93a9f69c89 100644 --- a/modules/americanexpress/pages.py +++ b/modules/americanexpress/pages.py @@ -22,7 +22,6 @@ from ast import literal_eval from decimal import Decimal import re -from dateutil.parser import parse as parse_date from weboob.browser.pages import LoggedPage, JsonPage, HTMLPage from weboob.browser.elements import ItemElement, DictElement, method @@ -32,12 +31,14 @@ from weboob.capabilities.base import NotAvailable from weboob.tools.json import json from weboob.tools.compat import basestring -from weboob.exceptions import ActionNeeded, BrowserUnavailable +from .compat.weboob_exceptions import ActionNeeded, BrowserUnavailable +from dateutil.parser import parse as parse_date def float_to_decimal(f): return Decimal(str(f)) + def parse_decimal(s): # we might get 1,399,680 in rupie indonésienne if s.count(',') > 1 and not s.count('.'): @@ -87,13 +88,13 @@ def iter_accounts(self): else: assert False, "data was not found" - assert data[13] == 'core' - assert len(data[14]) == 3 + assert data[15] == 'core' + assert len(data[16]) == 3 # search for products to get products list - for index, el in enumerate(data[14][2]): + for index, el in enumerate(data[16][2]): if 'products' in el: - accounts_data = data[14][2][index+1] + accounts_data = data[16][2][index + 1] assert len(accounts_data) == 2 assert accounts_data[1][4] == 'productsList' @@ -105,14 +106,14 @@ def iter_accounts(self): if isinstance(account_data, basestring): balances_token = account_data - elif isinstance(account_data, list) and not account_data[4][2][0]=="Canceled": + elif isinstance(account_data, list) and not account_data[4][2][0] == "Canceled": acc = Account() if len(account_data) > 15: token.append(account_data[-11]) - acc._idforJSON = account_data[10][-1] + acc._idforJSON = account_data[10][-1] else: acc._idforJSON = account_data[-5][-1] - acc._idforJSON = re.sub('\s+', ' ', acc._idforJSON) + acc._idforJSON = re.sub(r'\s+', ' ', acc._idforJSON) acc.number = '-%s' % account_data[2][2] acc.label = '%s %s' % (account_data[6][4], account_data[10][-1]) acc._balances_token = acc.id = balances_token @@ -142,7 +143,7 @@ def set_balances(self, accounts): class CurrencyPage(LoggedPage, JsonPage): def get_currency(self): - return self.doc['currency'] + return self.doc['localeSettings']['currency_code'] class JsonPeriods(LoggedPage, JsonPage): @@ -172,7 +173,8 @@ def obj_type(self): obj_raw = CleanText(Dict('description', default='')) def obj_date(self): - """ 'statement_end_date' might be absent from this json, we must match the rdate with the right date period """ + # 'statement_end_date' might be absent from this json, + # we must match the rdate with the right date period _date = Date(Dict('statement_end_date', default=None), default=NotAvailable)(self) if not _date: periods = Env('periods')(self) @@ -207,6 +209,4 @@ def obj_original_amount(self): else: return original_amount - # obj__ref = Dict('reference_id') obj__ref = Dict('identifier') - diff --git a/modules/amundi/browser.py b/modules/amundi/browser.py index 8064befe8ebe5db125c0888665ba150ee2dd8a47..b0262920458b40497ebb5542d4b53cdda3ec8d91 100644 --- a/modules/amundi/browser.py +++ b/modules/amundi/browser.py @@ -18,8 +18,8 @@ # along with this weboob module. If not, see . from .pages import LoginPage, AccountsPage, AccountHistoryPage -from weboob.browser import URL, LoginBrowser, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import URL, LoginBrowser, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.browser.exceptions import ClientError from weboob.capabilities.base import empty diff --git a/modules/amundi/compat/weboob_capabilities_bank.py b/modules/amundi/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/amundi/compat/weboob_capabilities_bank.py +++ b/modules/amundi/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/amundi/compat/weboob_exceptions.py b/modules/amundi/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/amundi/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/amundi/pages.py b/modules/amundi/pages.py index a32bc0eaad297913b30a19517940a1ed0aa8d8a6..32672f6be35e854d51798093dbf7542204453654 100644 --- a/modules/amundi/pages.py +++ b/modules/amundi/pages.py @@ -29,7 +29,7 @@ from weboob.browser.pages import LoggedPage, JsonPage from .compat.weboob_capabilities_bank import Account, Investment, Transaction from weboob.capabilities.base import NotAvailable, empty -from weboob.exceptions import NoAccountsException +from .compat.weboob_exceptions import NoAccountsException from weboob.tools.capabilities.bank.investments import is_isin_valid diff --git a/modules/anticaptcha/browser.py b/modules/anticaptcha/browser.py index 14a462d98e3a5ca500fc8e5720da94b143a9a98d..1386e6fba57a91623569a77c5c2eff02cf5e947c 100644 --- a/modules/anticaptcha/browser.py +++ b/modules/anticaptcha/browser.py @@ -22,8 +22,8 @@ from base64 import b64encode from weboob.browser.browsers import APIBrowser -from weboob.exceptions import BrowserIncorrectPassword, BrowserBanned -from weboob.capabilities.captcha import ( +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserBanned +from .compat.weboob_capabilities_captcha import ( ImageCaptchaJob, RecaptchaJob, RecaptchaV3Job, NocaptchaJob, FuncaptchaJob, CaptchaError, InsufficientFunds, UnsolvableCaptcha, InvalidCaptcha, ) diff --git a/modules/anticaptcha/compat/__init__.py b/modules/anticaptcha/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/anticaptcha/compat/weboob_capabilities_captcha.py b/modules/anticaptcha/compat/weboob_capabilities_captcha.py new file mode 100644 index 0000000000000000000000000000000000000000..3b8cc0d9e8ca734cbf9904343dd9add3c90588fc --- /dev/null +++ b/modules/anticaptcha/compat/weboob_capabilities_captcha.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2018 Vincent A +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with weboob. If not, see . + +from time import sleep + +from weboob.capabilities.base import Capability, BaseObject, StringField, UserError, BytesField +from weboob.exceptions import ( + RecaptchaQuestion, RecaptchaV3Question, NocaptchaQuestion, FuncaptchaQuestion, ImageCaptchaQuestion +) + + +__all__ = [ + 'CapCaptchaSolver', + 'SolverJob', 'RecaptchaJob', 'NocaptchaJob', 'ImageCaptchaJob', + 'CaptchaError', 'UnsolvableCaptcha', 'InvalidCaptcha', 'InsufficientFunds', + 'exception_to_job', +] + + +from weboob.capabilities.captcha import SolverJob as _SolverJob +class SolverJob(_SolverJob): + solution = StringField('CAPTCHA solution') + + +from weboob.capabilities.captcha import RecaptchaJob as _RecaptchaJob +class RecaptchaJob(_RecaptchaJob): + site_url = StringField('Site URL for ReCaptcha service') + site_key = StringField('Site key for ReCaptcha service') + + solution_challenge = StringField('Challenge ID of the solution (output value)') + + +class RecaptchaV3Job(SolverJob): + site_url = StringField('Site URL for ReCaptcha service') + site_key = StringField('Site key for ReCaptcha service') + action = StringField('Website owner defines what user is doing on the page through this parameter.') + + +from weboob.capabilities.captcha import NocaptchaJob as _NocaptchaJob +class NocaptchaJob(_NocaptchaJob): + site_url = StringField('Site URL for NoCaptcha service') + site_key = StringField('Site key for NoCaptcha service') + + +from weboob.capabilities.captcha import FuncaptchaJob as _FuncaptchaJob +class FuncaptchaJob(_FuncaptchaJob): + site_url = StringField('Site URL for FunCaptcha service') + site_key = StringField('Site key for FunCaptcha service') + sub_domain = StringField('Required for some complex cases, but Funcaptcha integrations run without it') + + +from weboob.capabilities.captcha import ImageCaptchaJob as _ImageCaptchaJob +class ImageCaptchaJob(_ImageCaptchaJob): + image = BytesField('data of the image to solve') + + +from weboob.capabilities.captcha import CaptchaError as _CaptchaError +class CaptchaError(_CaptchaError): + """Generic solving error""" + + +from weboob.capabilities.captcha import InvalidCaptcha as _InvalidCaptcha +class InvalidCaptcha(_InvalidCaptcha): + """CAPTCHA cannot be used (e.g. invalid image format)""" + + +from weboob.capabilities.captcha import UnsolvableCaptcha as _UnsolvableCaptcha +class UnsolvableCaptcha(_UnsolvableCaptcha): + """CAPTCHA is too hard or impossible""" + + +from weboob.capabilities.captcha import InsufficientFunds as _InsufficientFunds +class InsufficientFunds(_InsufficientFunds): + """Not enough funds to pay solution""" + + +def exception_to_job(exc): + if isinstance(exc, RecaptchaQuestion): + job = RecaptchaJob() + job.site_url = exc.website_url + job.site_key = exc.website_key + elif isinstance(exc, RecaptchaV3Question): + job = RecaptchaV3Job() + job.site_url = exc.website_url + job.site_key = exc.website_key + job.action = exc.action + elif isinstance(exc, NocaptchaQuestion): + job = NocaptchaJob() + job.site_url = exc.website_url + job.site_key = exc.website_key + elif isinstance(exc, FuncaptchaQuestion): + job = FuncaptchaJob() + job.site_url = exc.website_url + job.site_key = exc.website_key + job.sub_domain = exc.sub_domain + elif isinstance(exc, ImageCaptchaQuestion): + job = ImageCaptchaJob() + job.image = exc.image_data + else: + raise NotImplementedError() + + return job + + +from weboob.capabilities.captcha import CapCaptchaSolver as _CapCaptchaSolver +class CapCaptchaSolver(_CapCaptchaSolver): + """ + Provide CAPTCHA solving + """ + + RETRIES = 30 + WAIT_TIME = 2 + + def create_job(self, job): + """Start a CAPTCHA solving job + + The `job.id` shall be filled. The CAPTCHA is not solved yet when the method returns. + + :param job: job to start + :type job: :class:`SolverJob` + :raises: :class:`NotImplementedError` if CAPTCHA type is not supported + :raises: :class:`CaptchaError` in case of other error + """ + raise NotImplementedError() + + def poll_job(self, job): + """Check if a job was solved + + If `job` is solved, return True and fill `job.solution`. + Return False if solution is still pending. + In case of solving problem, an exception may be raised. + + It should not wait for the solution but return the current state. + + :param job: job to check and to fill when solved + :type job: :class:`SolverJob` + :returns: True if the job was solved + :rtype: bool + :raises: :class:`CaptchaError` + """ + raise NotImplementedError() + + def solve_catpcha_blocking(self, job): + """Start a CAPTCHA solving job and wait for its solution + + :param job: job to start and solve + :type job: :class:`SolverJob` + :raises: :class:`CaptchaError` + """ + + self.create_job(job) + for i in range(self.RETRIES): + sleep(self.WAIT_TIME) + if self.poll_job(job): + return job + + def report_wrong_solution(self, job): + """Report a solved job as a wrong solution + + Sometimes, jobs are solved, but the solution is rejected by the CAPTCHA + site because the solution is wrong. + This method reports the solution as wrong to the CAPTCHA solver. + + :param job: job to flag + :type job: :class:`SolverJob` + """ + raise NotImplementedError() + + def get_balance(self): + """Get the prepaid balance left + + :rtype: float + """ + raise NotImplementedError() + diff --git a/modules/anticaptcha/compat/weboob_exceptions.py b/modules/anticaptcha/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/anticaptcha/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/anticaptcha/module.py b/modules/anticaptcha/module.py index 3949ce2b4deccde05deec6e5f429ccd8c62cac48..acd41d413da746fa426ee9157d184a1a51082095 100644 --- a/modules/anticaptcha/module.py +++ b/modules/anticaptcha/module.py @@ -21,7 +21,7 @@ from weboob.tools.backend import Module, BackendConfig -from weboob.capabilities.captcha import ( +from .compat.weboob_capabilities_captcha import ( CapCaptchaSolver, ImageCaptchaJob, RecaptchaJob, RecaptchaV3Job, NocaptchaJob, FuncaptchaJob ) from weboob.tools.value import ValueBackendPassword diff --git a/modules/apivie/browser.py b/modules/apivie/browser.py index 650d284f095cbe9420f3bd140dd59b868751adf3..a4da7dd5a50305e6fb6993ea5d80b7c864e1f437 100644 --- a/modules/apivie/browser.py +++ b/modules/apivie/browser.py @@ -18,9 +18,9 @@ # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL, need_login +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.capabilities.base import find_object -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, AccountsPage, InvestmentsPage, OperationsPage diff --git a/modules/apivie/compat/weboob_capabilities_bank.py b/modules/apivie/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/apivie/compat/weboob_capabilities_bank.py +++ b/modules/apivie/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/apivie/compat/weboob_exceptions.py b/modules/apivie/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/apivie/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/asana/browser.py b/modules/asana/browser.py index 9609f7a652cc725bd2d38fd99c8a949a43e2f116..482dd9754b6c0674a97efe5a7dddc6835c147809 100644 --- a/modules/asana/browser.py +++ b/modules/asana/browser.py @@ -25,7 +25,7 @@ from weboob.browser.exceptions import ClientError from weboob.capabilities.base import NotAvailable from weboob.capabilities.bugtracker import User, Project, Issue, Status, Update -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from dateutil.parser import parse as parse_date diff --git a/modules/asana/compat/__init__.py b/modules/asana/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/asana/compat/weboob_exceptions.py b/modules/asana/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/asana/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/aum/browser.py b/modules/aum/browser.py index 37f3c97bec68c310b67f14de35c8a20560645948..ae2c15ab3b4ba3f2d8417c1116665c0077db4e8a 100644 --- a/modules/aum/browser.py +++ b/modules/aum/browser.py @@ -24,7 +24,7 @@ import math import re -from weboob.exceptions import BrowserIncorrectPassword, BrowserHTTPNotFound, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserHTTPNotFound, BrowserUnavailable from weboob.browser.exceptions import ClientError from weboob.browser.browsers import LoginBrowser, DomainBrowser from weboob.browser.pages import HTMLPage diff --git a/modules/aum/compat/weboob_exceptions.py b/modules/aum/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/aum/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/aum/module.py b/modules/aum/module.py index 7383b72f9bac5382939e7d6f09e28b3963b7488c..d8bb7dde23e95c3e3b1b96c4670d1fde26539407 100644 --- a/modules/aum/module.py +++ b/modules/aum/module.py @@ -36,7 +36,7 @@ from weboob.capabilities.contact import CapContact, ContactPhoto, Query, QueryError from weboob.capabilities.account import CapAccount, StatusField from weboob.tools.backend import Module, BackendConfig -from weboob.exceptions import BrowserUnavailable, BrowserHTTPNotFound +from .compat.weboob_exceptions import BrowserUnavailable, BrowserHTTPNotFound from weboob.tools.value import Value, ValueBool, ValueBackendPassword from weboob.tools.date import local2utc from weboob.tools.misc import to_unicode diff --git a/modules/aum/optim/compat/__init__.py b/modules/aum/optim/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/aum/optim/compat/weboob_exceptions.py b/modules/aum/optim/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/aum/optim/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/aum/optim/profiles_walker.py b/modules/aum/optim/profiles_walker.py index 8563ce22e3a035a789463dae154e5725baf8411c..ffc54c3ee6bbe863541129826d292fd9fae8aba2 100644 --- a/modules/aum/optim/profiles_walker.py +++ b/modules/aum/optim/profiles_walker.py @@ -19,7 +19,7 @@ from random import randint -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from weboob.capabilities.dating import Optimization from weboob.tools.log import getLogger diff --git a/modules/aum/optim/queries_queue.py b/modules/aum/optim/queries_queue.py index c538e536aff9fc6ba941854ece5212a2a27f9a41..2355a4ffddcf42e3600601269a1a385e7cf8ac0d 100644 --- a/modules/aum/optim/queries_queue.py +++ b/modules/aum/optim/queries_queue.py @@ -18,7 +18,7 @@ # along with this weboob module. If not, see . -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from weboob.capabilities.dating import Optimization from weboob.capabilities.contact import QueryError from weboob.tools.log import getLogger diff --git a/modules/aum/optim/visibility.py b/modules/aum/optim/visibility.py index 7197fbe146f966546cfa51862f0665efa922bd4e..e936feefc7cc2ca4dcf12fff388e8049f42006a6 100644 --- a/modules/aum/optim/visibility.py +++ b/modules/aum/optim/visibility.py @@ -18,7 +18,7 @@ # along with this weboob module. If not, see . -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from weboob.capabilities.dating import Optimization diff --git a/modules/aum/test.py b/modules/aum/test.py index cbd9170cf79c40210e058e4e7b4632b7e91b2654..ca1525cda80652f28c4dadd7870020db9ebe88bb 100644 --- a/modules/aum/test.py +++ b/modules/aum/test.py @@ -19,7 +19,7 @@ from weboob.tools.test import BackendTest -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable class AuMTest(BackendTest): diff --git a/modules/axabanque/browser.py b/modules/axabanque/browser.py index f05269ac88e2766bb113080bfc715eebbee3f355..f9d698e73955e494a3173372b8d4db19f7dea72c 100644 --- a/modules/axabanque/browser.py +++ b/modules/axabanque/browser.py @@ -28,7 +28,7 @@ from weboob.capabilities.base import NotAvailable from weboob.capabilities.bill import Subscription from .compat.weboob_capabilities_bank import Account, Transaction, AddRecipientStep, Recipient -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded from weboob.tools.value import Value from weboob.tools.capabilities.bank.transactions import sorted_transactions from weboob.tools.capabilities.bank.investments import create_french_liquidity diff --git a/modules/axabanque/compat/weboob_capabilities_bank.py b/modules/axabanque/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/axabanque/compat/weboob_capabilities_bank.py +++ b/modules/axabanque/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/axabanque/compat/weboob_exceptions.py b/modules/axabanque/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/axabanque/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/axabanque/pages/bank.py b/modules/axabanque/pages/bank.py index 2f53136143ae756b2c1f11c6293c01339f6e7639..3d5a7adcd7f2580885ed690481c1c3b43bf13e09 100644 --- a/modules/axabanque/pages/bank.py +++ b/modules/axabanque/pages/bank.py @@ -24,7 +24,7 @@ from decimal import Decimal, InvalidOperation from datetime import datetime, timedelta -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from weboob.browser.pages import HTMLPage, PDFPage, LoggedPage, AbstractPage from weboob.browser.elements import ItemElement, TableElement, method from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, Date, Regexp, Field, Env, Currency diff --git a/modules/axabanque/pages/compat/weboob_capabilities_bank.py b/modules/axabanque/pages/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/axabanque/pages/compat/weboob_capabilities_bank.py +++ b/modules/axabanque/pages/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/axabanque/pages/compat/weboob_exceptions.py b/modules/axabanque/pages/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/axabanque/pages/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/axabanque/pages/login.py b/modules/axabanque/pages/login.py index faacf2d9a12d21dc301fd165046374d5f3138921..25a80ba7e02db0a1154a92426cd196d014f45818 100644 --- a/modules/axabanque/pages/login.py +++ b/modules/axabanque/pages/login.py @@ -20,7 +20,7 @@ from io import BytesIO -from weboob.exceptions import BrowserBanned, ActionNeeded, BrowserUnavailable +from .compat.weboob_exceptions import BrowserBanned, ActionNeeded, BrowserUnavailable from weboob.browser.pages import HTMLPage, RawPage, JsonPage, PartialHTMLPage from weboob.browser.filters.json import Dict from .compat.weboob_browser_filters_standard import CleanText diff --git a/modules/banqueaccord/compat/weboob_capabilities_bank.py b/modules/banqueaccord/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/banqueaccord/compat/weboob_capabilities_bank.py +++ b/modules/banqueaccord/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/banquepopulaire/browser.py b/modules/banquepopulaire/browser.py index f1db06b5f57602846f15c9d635754c7a6a1551c6..ac893141ddaa7af80bfa3d53c38bf52cf894bba3 100644 --- a/modules/banquepopulaire/browser.py +++ b/modules/banquepopulaire/browser.py @@ -26,7 +26,7 @@ from functools import wraps from dateutil.relativedelta import relativedelta -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable from weboob.browser.exceptions import HTTPNotFound, ServerError from weboob.browser.browsers import LoginBrowser, URL, need_login from .compat.weboob_capabilities_bank import Account, AccountOwnership @@ -276,7 +276,11 @@ def get_accounts_list(self, get_iban=True): # thanks to stateful website next_pages = [] accounts = [] - owner_name = re.search(r' (.+)', self.get_profile().name).group(1).upper() + profile = self.get_profile() + if profile.name: + owner_name = re.search(r' (.+)', profile.name).group(1).upper() + else: + owner_name = re.search(r' (.+)', profile.company_name).group(1).upper() self.go_on_accounts_list() diff --git a/modules/banquepopulaire/compat/weboob_capabilities_bank.py b/modules/banquepopulaire/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/banquepopulaire/compat/weboob_capabilities_bank.py +++ b/modules/banquepopulaire/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/banquepopulaire/compat/weboob_exceptions.py b/modules/banquepopulaire/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/banquepopulaire/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/banquepopulaire/pages.py b/modules/banquepopulaire/pages.py index 21bed70012f72e13bde73d9751625f6bc5fed1fc..cfcff859cb33ce1639db29478c975e4ceac9c33b 100644 --- a/modules/banquepopulaire/pages.py +++ b/modules/banquepopulaire/pages.py @@ -31,7 +31,7 @@ from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, Regexp, Eval, Date, Field from weboob.browser.filters.html import Attr, Link, AttributeNotFound from weboob.browser.filters.json import Dict -from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword, ActionNeeded +from .compat.weboob_exceptions import BrowserUnavailable, BrowserIncorrectPassword, ActionNeeded from weboob.browser.pages import HTMLPage, LoggedPage, FormNotFound, JsonPage, RawPage, XMLPage diff --git a/modules/barclays/browser.py b/modules/barclays/browser.py index b69acdfbbeba9b5429003b6fb6956e7bc3accb6c..fbfb380e21b5e0e418ddfb4c687ccdf6b82e4492 100644 --- a/modules/barclays/browser.py +++ b/modules/barclays/browser.py @@ -23,7 +23,7 @@ from requests.exceptions import ConnectionError from weboob.browser.browsers import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded from .compat.weboob_capabilities_bank import Account from weboob.capabilities.base import NotAvailable from weboob.tools.decorators import retry diff --git a/modules/barclays/compat/weboob_capabilities_bank.py b/modules/barclays/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/barclays/compat/weboob_capabilities_bank.py +++ b/modules/barclays/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/barclays/compat/weboob_exceptions.py b/modules/barclays/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/barclays/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/barclays/pages.py b/modules/barclays/pages.py index ad2517f7c0bbf5ae377b27915857c0b5ff33327c..1eac9662a3d8396773f56f8b66938241753a0148 100644 --- a/modules/barclays/pages.py +++ b/modules/barclays/pages.py @@ -28,7 +28,7 @@ from .compat.weboob_capabilities_bank import Account, Investment, Loan, NotAvailable from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.tools.capabilities.bank.iban import is_iban_valid -from weboob.exceptions import ActionNeeded, BrowserUnavailable +from .compat.weboob_exceptions import ActionNeeded, BrowserUnavailable def MyDecimal(*args, **kwargs): diff --git a/modules/becm/browser.py b/modules/becm/browser.py index e8eb0d9e4209164e9e803d500b9d7c42fb5e2583..69c7f9d4965de59014a0c3a6cfce500d110c6fce 100644 --- a/modules/becm/browser.py +++ b/modules/becm/browser.py @@ -23,7 +23,7 @@ from weboob.browser.profiles import Wget from .compat.weboob_browser_url import URL from weboob.browser.browsers import need_login -from weboob.exceptions import ActionNeeded, AuthMethodNotImplemented +from .compat.weboob_exceptions import ActionNeeded, AuthMethodNotImplemented from .pages import AdvisorPage, LoginPage diff --git a/modules/becm/compat/weboob_capabilities_bank.py b/modules/becm/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/becm/compat/weboob_capabilities_bank.py +++ b/modules/becm/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/becm/compat/weboob_exceptions.py b/modules/becm/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/becm/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/becm/pages.py b/modules/becm/pages.py index 4808f235d947d1762ba36d6a09dc828710c0ba27..bab64a5b2c3577403d48ab8852a5c98648a96700 100644 --- a/modules/becm/pages.py +++ b/modules/becm/pages.py @@ -22,7 +22,7 @@ from weboob.browser.elements import method, ItemElement from .compat.weboob_browser_filters_standard import CleanText, Format from weboob.capabilities import NotAvailable -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword class LoginPage(HTMLPage): diff --git a/modules/bforbank/browser.py b/modules/bforbank/browser.py index ecb89c78d338fdb5794b19eb550afd921fdade2c..4f6ed1e24ca18d34ea73028fe4df686b4d7d433a 100644 --- a/modules/bforbank/browser.py +++ b/modules/bforbank/browser.py @@ -18,7 +18,7 @@ # along with this weboob module. If not, see . import datetime from dateutil.relativedelta import relativedelta -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.browser.browsers import LoginBrowser, URL, need_login from .compat.weboob_capabilities_bank import Account, AccountNotFound from weboob.capabilities.base import empty diff --git a/modules/bforbank/compat/weboob_capabilities_bank.py b/modules/bforbank/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bforbank/compat/weboob_capabilities_bank.py +++ b/modules/bforbank/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bforbank/compat/weboob_exceptions.py b/modules/bforbank/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bforbank/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bforbank/pages.py b/modules/bforbank/pages.py index f076eb65e2787ef1c40d0234a83bc11760476c4c..ac7e9aed8ac89f77d3d1c18f20ea093857c6f3e7 100644 --- a/modules/bforbank/pages.py +++ b/modules/bforbank/pages.py @@ -26,7 +26,7 @@ from PIL import Image -from weboob.exceptions import ActionNeeded +from .compat.weboob_exceptions import ActionNeeded from weboob.browser.pages import LoggedPage, HTMLPage, pagination, AbstractPage from weboob.browser.elements import method, ListElement, ItemElement, TableElement from .compat.weboob_capabilities_bank import Account @@ -301,11 +301,13 @@ def obj_date(self): class CardPage(LoggedPage, HTMLPage): def has_no_card(self): # Persistent message for cardless accounts - return CleanText('//div[@id="alert"]/p[contains(text(), "Aucune donnée n\'a été retournée par le service")]')(self.doc) + return ( + CleanText('//div[@id="alert"]/p[contains(text(), "Aucune donnée n\'a été retournée par le service")]')(self.doc) + or not self.doc.xpath('//div[@class="content-boxed"]') + ) def get_cards(self, account_id): divs = self.doc.xpath('//div[@class="content-boxed"]') - assert len(divs) msgs = re.compile( 'Vous avez fait opposition sur cette carte bancaire.' + '|Votre carte bancaire a été envoyée.' + diff --git a/modules/binck/browser.py b/modules/binck/browser.py index fbf7f75c89ccca86e3d59815c04f30b828ef863b..5f91e11917762705f1551affe5437dedfd432583 100644 --- a/modules/binck/browser.py +++ b/modules/binck/browser.py @@ -22,8 +22,8 @@ from lxml import etree from io import StringIO -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded from weboob.browser.exceptions import HTTPNotFound, ServerError from weboob.tools.capabilities.bank.investments import create_french_liquidity diff --git a/modules/binck/compat/weboob_capabilities_bank.py b/modules/binck/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/binck/compat/weboob_capabilities_bank.py +++ b/modules/binck/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/binck/compat/weboob_exceptions.py b/modules/binck/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/binck/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/binck/pages.py b/modules/binck/pages.py index 7cc58dbf1626037c9ebefd64fe7abc9a4304414a..2061a42947388fe96ac6306e5590a4fae25efca1 100644 --- a/modules/binck/pages.py +++ b/modules/binck/pages.py @@ -23,14 +23,15 @@ from weboob.browser.pages import HTMLPage, JsonPage, LoggedPage from weboob.browser.elements import ItemElement, ListElement, DictElement, TableElement, method -from .compat.weboob_browser_filters_standard import CleanText, Date, Format, CleanDecimal, Eval, Env, Field +from .compat.weboob_browser_filters_standard import CleanText, Date, DateTime, Format, CleanDecimal, Eval, Env, Field from weboob.browser.filters.html import Attr, Link, TableCell from weboob.browser.filters.json import Dict -from weboob.exceptions import BrowserPasswordExpired, ActionNeeded +from .compat.weboob_exceptions import BrowserPasswordExpired, ActionNeeded from .compat.weboob_capabilities_bank import Account, Investment from weboob.capabilities.base import NotAvailable, empty from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.tools.capabilities.bank.investments import is_isin_valid +from .compat.weboob_browser_filters_standard import FilterError def MyDecimal(*args, **kwargs): @@ -212,7 +213,7 @@ class item(ItemElement): obj_label = Dict('SecurityName') obj_quantity = MyDecimal(Dict('Quantity')) - obj_vdate = Date(CleanText(Dict('Time')), dayfirst=True) + obj_vdate = DateTime(CleanText(Dict('Time')), dayfirst=True, strict=False) obj_unitvalue = Env('unitvalue', default=NotAvailable) obj_unitprice = Env('unitprice', default=NotAvailable) obj_valuation = MyDecimal(Dict('ValueInEuro')) @@ -225,6 +226,17 @@ class item(ItemElement): obj_original_diff = Env('o_diff', default=NotAvailable) obj__security_id = Dict('SecurityId') + def obj_vdate(self): + try: + # during stocks closing hours only date (d/m/y) is given + return Date(CleanText(Dict('Time')), dayfirst=True)(self) + except FilterError: + # during stocks opening hours only time (h:m:s) is given, + # can even be realtime, depending on user settings, + # can be given in foreign markets time, + # e.g. 10:00 is displayed at 9:00 for an action in NASADQ Helsinki market + return DateTime(CleanText(Dict('Time')), dayfirst=True, strict=False)(self) + def obj_code(self): if is_isin_valid(Dict('IsinCode')(self)): return Dict('IsinCode')(self) diff --git a/modules/biplan/compat/__init__.py b/modules/biplan/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/bnpcards/browser.py b/modules/bnpcards/browser.py index 8900ba22ed5785330019294a85666e316b48c153..b68b958f989f7a4412f9b94313887b756ead9296 100644 --- a/modules/bnpcards/browser.py +++ b/modules/bnpcards/browser.py @@ -18,8 +18,8 @@ # along with this weboob module. If not, see . -from weboob.exceptions import BrowserIncorrectPassword, BrowserPasswordExpired -from weboob.browser import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserPasswordExpired +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.browser.switch import SiteSwitch from weboob.tools.capabilities.bank.transactions import sorted_transactions from weboob.tools.compat import basestring diff --git a/modules/bnpcards/compat/weboob_capabilities_bank.py b/modules/bnpcards/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bnpcards/compat/weboob_capabilities_bank.py +++ b/modules/bnpcards/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bnpcards/compat/weboob_exceptions.py b/modules/bnpcards/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bnpcards/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bnpcards/corporate/browser.py b/modules/bnpcards/corporate/browser.py index 3ce446433f0ecd6ac07423e5ab6dee6b7e6da3d0..ef6de61cf035d72379610cde6d751c9de92a58d9 100644 --- a/modules/bnpcards/corporate/browser.py +++ b/modules/bnpcards/corporate/browser.py @@ -18,8 +18,8 @@ # along with this weboob module. If not, see . -from weboob.exceptions import BrowserIncorrectPassword, BrowserPasswordExpired -from weboob.browser import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserPasswordExpired +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.tools.capabilities.bank.transactions import sorted_transactions from weboob.tools.compat import basestring diff --git a/modules/bnpcards/corporate/compat/weboob_capabilities_bank.py b/modules/bnpcards/corporate/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bnpcards/corporate/compat/weboob_capabilities_bank.py +++ b/modules/bnpcards/corporate/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bnpcards/corporate/compat/weboob_exceptions.py b/modules/bnpcards/corporate/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bnpcards/corporate/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bnpcards/pages.py b/modules/bnpcards/pages.py index bf6dfc62aa9b756a6c548d1bfaabc2dfd80a5298..d39b8696c9a5b56bc5d398fbdf2897f7c67d65c1 100644 --- a/modules/bnpcards/pages.py +++ b/modules/bnpcards/pages.py @@ -23,7 +23,7 @@ from datetime import date from decimal import Decimal -from weboob.exceptions import BrowserPasswordExpired +from .compat.weboob_exceptions import BrowserPasswordExpired from weboob.browser.pages import HTMLPage, LoggedPage, pagination from weboob.browser.elements import ListElement, ItemElement, method from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, Field, Env, Format diff --git a/modules/bnporc/company/compat/weboob_capabilities_bank.py b/modules/bnporc/company/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bnporc/company/compat/weboob_capabilities_bank.py +++ b/modules/bnporc/company/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bnporc/company/compat/weboob_exceptions.py b/modules/bnporc/company/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bnporc/company/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bnporc/company/pages.py b/modules/bnporc/company/pages.py index 418ea900064954365c649c8e337f221c85de092c..490ee49ae6f40114428f089775c11adc87e87601 100644 --- a/modules/bnporc/company/pages.py +++ b/modules/bnporc/company/pages.py @@ -25,7 +25,7 @@ from datetime import datetime from .compat.weboob_capabilities_bank import Account -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.browser.pages import HTMLPage, JsonPage, LoggedPage from .compat.weboob_tools_captcha_virtkeyboard import MappedVirtKeyboard, VirtKeyboardError diff --git a/modules/bnporc/compat/weboob_capabilities_bank.py b/modules/bnporc/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bnporc/compat/weboob_capabilities_bank.py +++ b/modules/bnporc/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bnporc/enterprise/browser.py b/modules/bnporc/enterprise/browser.py index 47033002d9f28d9bf1f0c74b29db522bfd0f3cc3..6fb65f8967afd88629f5886122f839ffc365dca3 100644 --- a/modules/bnporc/enterprise/browser.py +++ b/modules/bnporc/enterprise/browser.py @@ -27,7 +27,7 @@ from weboob.browser.browsers import LoginBrowser, need_login from weboob.capabilities.base import find_object from .compat.weboob_capabilities_bank import Account -from weboob.exceptions import BrowserIncorrectPassword, BrowserForbidden +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserForbidden from .compat.weboob_browser_url import URL from weboob.tools.capabilities.bank.transactions import sorted_transactions diff --git a/modules/bnporc/enterprise/compat/weboob_capabilities_bank.py b/modules/bnporc/enterprise/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bnporc/enterprise/compat/weboob_capabilities_bank.py +++ b/modules/bnporc/enterprise/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bnporc/enterprise/compat/weboob_exceptions.py b/modules/bnporc/enterprise/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bnporc/enterprise/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bnporc/enterprise/pages.py b/modules/bnporc/enterprise/pages.py index 29edf02cc49ea454ac575a96df63b3a4e0502ad9..ead43bd75e605e0c77bfef9c789086e4fb88f17f 100644 --- a/modules/bnporc/enterprise/pages.py +++ b/modules/bnporc/enterprise/pages.py @@ -30,14 +30,14 @@ from weboob.browser.elements import DictElement, ItemElement, method, TableElement from .compat.weboob_browser_filters_standard import ( CleanText, CleanDecimal, Date, Regexp, Format, Eval, BrowserURL, Field, - Async, Currency, + Currency, ) from .compat.weboob_capabilities_bank import Transaction, Account, Investment from weboob.capabilities.profile import Person from .compat.weboob_tools_captcha_virtkeyboard import MappedVirtKeyboard, VirtKeyboardError from weboob.tools.date import parse_french_date from weboob.capabilities import NotAvailable -from weboob.exceptions import ActionNeeded, BrowserForbidden +from .compat.weboob_exceptions import ActionNeeded, BrowserForbidden def fromtimestamp(milliseconds): return datetime.fromtimestamp(milliseconds/1000) @@ -200,37 +200,33 @@ def load_details(self): 'numero_mvt': Field('_trid')(self), }) - def obj__redacted_card(self): - raw = Field('raw')(self) - - if not raw.startswith('FACTURE CARTE') or ' SUIVANT RELEVE DU ' in raw: - return - - page = Async('details').loaded_page(self) - return page.get_redacted_card() - class AccountHistoryPage(LoggedPage, JsonPage): TYPES = { - u'CARTE': Transaction.TYPE_DEFERRED_CARD, # Cartes - u'CHEQU': Transaction.TYPE_CHECK, # Chèques - u'REMCB': Transaction.TYPE_DEFERRED_CARD, # Remises cartes - u'VIREM': Transaction.TYPE_TRANSFER, # Virements - u'VIRIT': Transaction.TYPE_TRANSFER, # Virements internationaux - u'VIRSP': Transaction.TYPE_TRANSFER, # Virements européens - u'VIRTR': Transaction.TYPE_TRANSFER, # Virements de trésorerie - u'VIRXX': Transaction.TYPE_TRANSFER, # Autres virements - u'PRLVT': Transaction.TYPE_LOAN_PAYMENT, # Prélèvements, TIP et télérèglements - u'AUTOP': Transaction.TYPE_UNKNOWN, # Autres opérations + 'CARTE': Transaction.TYPE_DEFERRED_CARD, # Cartes + 'CHEQU': Transaction.TYPE_CHECK, # Chèques + 'REMCB': Transaction.TYPE_DEFERRED_CARD, # Remises cartes + 'VIREM': Transaction.TYPE_TRANSFER, # Virements + 'VIRIT': Transaction.TYPE_TRANSFER, # Virements internationaux + 'VIRSP': Transaction.TYPE_TRANSFER, # Virements européens + 'VIRTR': Transaction.TYPE_TRANSFER, # Virements de trésorerie + 'VIRXX': Transaction.TYPE_TRANSFER, # Autres virements + 'PRLVT': Transaction.TYPE_ORDER, # Prélèvements, TIP et télérèglements + 'AUTOP': Transaction.TYPE_UNKNOWN, # Autres opérations 'FACCB': Transaction.TYPE_CARD, # Cartes } COMING_TYPES = { - u'0083': Transaction.TYPE_DEFERRED_CARD, - u'0813': Transaction.TYPE_LOAN_PAYMENT, - u'0568': Transaction.TYPE_TRANSFER, - u'1194': Transaction.TYPE_DEFERRED_CARD, # PAYBACK typed as DEFERRED_CARD + '0001': Transaction.TYPE_CHECK, # CHEQUE + '0029': Transaction.TYPE_BANK, # Interets et Commissions + '0083': Transaction.TYPE_DEFERRED_CARD, + '0099': Transaction.TYPE_PAYBACK, # REM. CARTE OU EROCHQ.* + '0512': Transaction.TYPE_TRANSFER, # VIREMENT FAVEUR TIERS + '0558': Transaction.TYPE_TRANSFER, # VIREMENT RECU TIERS.* + '0568': Transaction.TYPE_TRANSFER, # VIREMENT SEPA + '0813': Transaction.TYPE_ORDER, # PRLV SEPA .* + '1194': Transaction.TYPE_DEFERRED_CARD, # PAYBACK typed as DEFERRED_CARD } @method @@ -334,9 +330,7 @@ def obj_amount(self): class TransactionPage(LoggedPage, JsonPage): - def get_redacted_card(self): - # warning: the account on which the transaction is returned depends on this data! - return self.doc['carteNum'] + pass class MarketPage(LoggedPage, HTMLPage): diff --git a/modules/bnporc/pp/browser.py b/modules/bnporc/pp/browser.py index 175981056ac9a229907a85c7ec2a4afc027c6c12..ed524531b8086433bcb35b994143cdbd6d5ca78f 100644 --- a/modules/bnporc/pp/browser.py +++ b/modules/bnporc/pp/browser.py @@ -24,7 +24,7 @@ import time from requests.exceptions import ConnectionError -from weboob.browser.browsers import LoginBrowser, URL, need_login +from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin from weboob.capabilities.base import find_object from .compat.weboob_capabilities_bank import ( AccountNotFound, Account, AddRecipientStep, AddRecipientTimeout, @@ -37,7 +37,7 @@ from weboob.tools.json import json from weboob.browser.exceptions import ServerError from weboob.browser.elements import DataError -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable from weboob.tools.value import Value, ValueBool from weboob.tools.capabilities.bank.investments import create_french_liquidity @@ -75,7 +75,7 @@ def open(self, *args, **kwargs): return super(JsonBrowserMixin, self).open(*args, **kwargs) -class BNPParibasBrowser(JsonBrowserMixin, LoginBrowser): +class BNPParibasBrowser(JsonBrowserMixin, LoginBrowser, StatesMixin): TIMEOUT = 30.0 login = URL(r'identification-wspl-pres/identification\?acceptRedirection=true×tamp=(?P\d+)', @@ -131,12 +131,19 @@ class BNPParibasBrowser(JsonBrowserMixin, LoginBrowser): profile = URL(r'/kyc-wspl/rest/informationsClient', ProfilePage) list_detail_card = URL(r'/udcarte-wspl/rest/listeDetailCartes', ListDetailCardPage) + STATE_DURATION = 10 + + need_reload_state = False + + __states__ = ('need_reload_state', 'rcpt_transfer_id') + def __init__(self, config, *args, **kwargs): super(BNPParibasBrowser, self).__init__(config['login'].get(), config['password'].get(), *args, **kwargs) self.accounts_list = None self.card_to_transaction_type = {} self.rotating_password = config['rotating_password'].get() self.digital_key = config['digital_key'].get() + self.rcpt_transfer_id = None @retry(ConnectionError, tries=3) def open(self, *args, **kwargs): @@ -150,6 +157,13 @@ def do_login(self): if self.login.is_here(): self.page.login(self.username, self.password) + def load_state(self, state): + # reload state only for new recipient feature + if state.get('need_reload_state'): + state.pop('url', None) + self.need_reload_state = False + super(BNPParibasBrowser, self).load_state(state) + def change_pass(self, oldpass, newpass): res = self.open('/identification-wspl-pres/grille?accessible=false') url = '/identification-wspl-pres/grille/%s' % res.json()['data']['idGrille'] @@ -428,6 +442,8 @@ def new_recipient(self, recipient, **params): data=json.dumps(data), headers={'Content-Type': 'application/json'} ).get_recipient(recipient) + self.rcpt_transfer_id = recipient._transfer_id + self.need_reload_state = True raise AddRecipientStep(recipient, Value('code', label='Saisissez le code reçu par SMS.')) elif type_activation == 'digital_key': # recipient validated with digital key are immediatly available @@ -443,9 +459,10 @@ def send_code(self, recipient, **params): add recipient with sms otp authentication """ data = {} - data['idBeneficiaire'] = recipient._transfer_id + data['idBeneficiaire'] = self.rcpt_transfer_id data['typeActivation'] = 1 data['codeActivation'] = params['code'] + self.rcpt_transfer_id = None return self.activate_recip_sms.go(data=json.dumps(data), headers={'Content-Type': 'application/json'}).get_recipient(recipient) @need_login diff --git a/modules/bnporc/pp/compat/weboob_capabilities_bank.py b/modules/bnporc/pp/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bnporc/pp/compat/weboob_capabilities_bank.py +++ b/modules/bnporc/pp/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bnporc/pp/compat/weboob_exceptions.py b/modules/bnporc/pp/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bnporc/pp/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bnporc/pp/pages.py b/modules/bnporc/pp/pages.py index c5521ba8c4c59524c7a19364d706ca0362bc80aa..f4b67a19e4638f21a84e3213c2e1bc180d04f9a9 100644 --- a/modules/bnporc/pp/pages.py +++ b/modules/bnporc/pp/pages.py @@ -43,7 +43,7 @@ from weboob.capabilities.base import empty from weboob.capabilities.contact import Advisor from weboob.capabilities.profile import Person, ProfileMissing -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable, BrowserPasswordExpired, ActionNeeded +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable, BrowserPasswordExpired, ActionNeeded from weboob.tools.capabilities.bank.iban import rib2iban, rebuild_rib, is_iban_valid from weboob.tools.capabilities.bank.transactions import FrenchTransaction from .compat.weboob_tools_captcha_virtkeyboard import GridVirtKeyboard diff --git a/modules/bnppere/browser.py b/modules/bnppere/browser.py index c2a1b2610dc9c458541f8566d9a700514f3d6ec8..7fb9beeae8c2e1f081cdbf6d4111eea85d208af8 100644 --- a/modules/bnppere/browser.py +++ b/modules/bnppere/browser.py @@ -19,8 +19,8 @@ from __future__ import unicode_literals -from weboob.browser import AbstractBrowser, LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable, ActionNeeded +from weboob.browser.browsers import AbstractBrowser, LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable, ActionNeeded from .pages import LoginPage, ProfilePage, ErrorPage, AccountPage, TermPage, UnexpectedPage, HistoryPage diff --git a/modules/bnppere/compat/weboob_capabilities_bank.py b/modules/bnppere/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bnppere/compat/weboob_capabilities_bank.py +++ b/modules/bnppere/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bnppere/compat/weboob_exceptions.py b/modules/bnppere/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bnppere/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bolden/compat/weboob_capabilities_bank.py b/modules/bolden/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bolden/compat/weboob_capabilities_bank.py +++ b/modules/bolden/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bolden/compat/weboob_exceptions.py b/modules/bolden/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bolden/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bolden/pages.py b/modules/bolden/pages.py index 57259ed12778ad2138a69860b01bd8dd03d2252a..da0d70fd8e8985f3938cfbd3ae583144b4d0d0cf 100644 --- a/modules/bolden/pages.py +++ b/modules/bolden/pages.py @@ -29,7 +29,7 @@ from .compat.weboob_capabilities_bank import Account, Transaction, Investment from weboob.capabilities.profile import Profile from weboob.capabilities.bill import Document, DocumentTypes -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.compat import urljoin diff --git a/modules/boursorama/browser.py b/modules/boursorama/browser.py index cbd80d52bbb39e6c9203fdaae3de3f9488bf12ec..97c0b8bb1de0ef1d1f4cdeb999c53f284a194557 100644 --- a/modules/boursorama/browser.py +++ b/modules/boursorama/browser.py @@ -26,7 +26,7 @@ from weboob.browser.retry import login_method, retry_on_logout, RetryLoginBrowser from weboob.browser.browsers import need_login, StatesMixin from .compat.weboob_browser_url import URL -from weboob.exceptions import BrowserIncorrectPassword, BrowserHTTPNotFound, NoAccountsException, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserHTTPNotFound, NoAccountsException, BrowserUnavailable from weboob.browser.exceptions import LoggedOut, ClientError from .compat.weboob_capabilities_bank import ( Account, AccountNotFound, TransferError, TransferInvalidAmount, diff --git a/modules/boursorama/compat/weboob_capabilities_bank.py b/modules/boursorama/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/boursorama/compat/weboob_capabilities_bank.py +++ b/modules/boursorama/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/boursorama/compat/weboob_exceptions.py b/modules/boursorama/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/boursorama/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/boursorama/pages.py b/modules/boursorama/pages.py index 317b1c0301aded0bf241be9c30c7ec5af4a40a11..5cd20d6e7ce6033eaeed549969e8bf5f90d79cb3 100644 --- a/modules/boursorama/pages.py +++ b/modules/boursorama/pages.py @@ -49,7 +49,7 @@ from weboob.tools.date import parse_french_date from .compat.weboob_tools_captcha_virtkeyboard import VirtKeyboard, VirtKeyboardError from weboob.tools.compat import urljoin, urlencode, urlparse -from weboob.exceptions import BrowserQuestion, BrowserIncorrectPassword, BrowserHTTPNotFound, BrowserUnavailable, ActionNeeded +from .compat.weboob_exceptions import BrowserQuestion, BrowserIncorrectPassword, BrowserHTTPNotFound, BrowserUnavailable, ActionNeeded class BrowserAuthenticationCodeMaxLimit(BrowserIncorrectPassword): @@ -579,8 +579,8 @@ def obj_date(self): def obj_amount(self): if Field('type')(self) == Transaction.TYPE_CARD_SUMMARY: # '-' so the reimbursements appear positively in the card transactions: - return -CleanDecimal.US(Dict('amount'))(self) - return CleanDecimal.US(Dict('amount'))(self) + return -CleanDecimal.French(Dict('amount'))(self) + return CleanDecimal.French(Dict('amount'))(self) def obj_rdate(self): if self.obj.rdate: diff --git a/modules/bouygues/browser.py b/modules/bouygues/browser.py index 605e5f182de19352d47fab16772c25f0834c6064..659cfd043287ffd499f0e8e5e90cacb0df57ae33 100644 --- a/modules/bouygues/browser.py +++ b/modules/bouygues/browser.py @@ -22,9 +22,9 @@ from time import time from jose import jwt -from weboob.browser import LoginBrowser, URL, need_login +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.browser.exceptions import HTTPNotFound -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.compat import urlparse, parse_qsl from .pages import ( diff --git a/modules/bouygues/compat/weboob_exceptions.py b/modules/bouygues/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bouygues/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bouygues/pages.py b/modules/bouygues/pages.py index 3b689498c1b790f6558abfb2c85bdfcca8b26245..60208ca4986edead0c0032b646c196482de14667 100644 --- a/modules/bouygues/pages.py +++ b/modules/bouygues/pages.py @@ -28,7 +28,7 @@ from weboob.capabilities import NotAvailable from weboob.capabilities.bill import Subscription, Bill from .compat.weboob_browser_filters_standard import Date, CleanDecimal, Env, Format, Coalesce, CleanText -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword class LoginPage(HTMLPage): diff --git a/modules/bp/browser.py b/modules/bp/browser.py index 2d5f78b3f054d3af324cf29b3d6f0ea8b80eb011..15c3c1d06e5546977d44ffaab2cc14fcbe840f5c 100644 --- a/modules/bp/browser.py +++ b/modules/bp/browser.py @@ -24,7 +24,7 @@ from weboob.browser.browsers import StatesMixin from weboob.browser.exceptions import ServerError from weboob.capabilities.base import NotAvailable -from weboob.exceptions import BrowserIncorrectPassword, BrowserBanned, NoAccountsException, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserBanned, NoAccountsException, BrowserUnavailable from weboob.tools.compat import urlsplit, urlunsplit, parse_qsl from .pages import ( @@ -237,6 +237,7 @@ def get_accounts_list(self): accounts = [] to_check = [] + owner_name = self.get_profile().name self.par_accounts_checking.go() pages = [self.par_accounts_checking, self.par_accounts_savings_and_invests, self.par_accounts_loan] @@ -249,7 +250,7 @@ def get_accounts_list(self): no_accounts += 1 continue - for account in self.page.iter_accounts(): + for account in self.page.iter_accounts(name=owner_name): if account.type == Account.TYPE_LOAN: self.location(account.url) if 'initSSO' not in account.url: diff --git a/modules/bp/compat/weboob_capabilities_bank.py b/modules/bp/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bp/compat/weboob_capabilities_bank.py +++ b/modules/bp/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bp/compat/weboob_exceptions.py b/modules/bp/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bp/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bp/pages/accounthistory.py b/modules/bp/pages/accounthistory.py index 171459a34c4f73092009750e93f6df2e3f3496d6..77733b1c0a758312598f794bae1ca4a3c4a54485 100644 --- a/modules/bp/pages/accounthistory.py +++ b/modules/bp/pages/accounthistory.py @@ -24,7 +24,7 @@ from weboob.capabilities.base import NotAvailable, empty from .compat.weboob_capabilities_bank import Investment, Transaction as BaseTransaction, Account -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.browser.pages import LoggedPage from weboob.browser.elements import TableElement, ItemElement, method diff --git a/modules/bp/pages/accountlist.py b/modules/bp/pages/accountlist.py index cb54d65ea2c1f70e6fa7d4fd9089e3e7e3a6e6ce..f574d0ca13d41df7117c6c005167866913f5a3c4 100644 --- a/modules/bp/pages/accountlist.py +++ b/modules/bp/pages/accountlist.py @@ -24,14 +24,14 @@ from decimal import Decimal from weboob.capabilities.base import NotAvailable -from .compat.weboob_capabilities_bank import Account, Loan +from .compat.weboob_capabilities_bank import Account, Loan, AccountOwnership from weboob.capabilities.contact import Advisor from weboob.capabilities.profile import Person from weboob.browser.elements import ListElement, ItemElement, method, TableElement from weboob.browser.pages import LoggedPage, RawPage, PartialHTMLPage, HTMLPage from weboob.browser.filters.html import Link, TableCell, Attr from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, Regexp, Env, Field, Currency, Async, Date, Format -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from weboob.tools.compat import urljoin, unicode from .base import MyHTMLPage @@ -73,6 +73,15 @@ def obj_url(self): def obj_label(self): return CleanText('.//div[@class="title"]/h3')(self).upper() + def obj_ownership(self): + account_holder = CleanText('.//div[@class="title"]/span')(self) + if re.search(r'(m|mr|me|mme|mlle|mle|ml)\.? (.*)\bou ?(m|mr|me|mme|mlle|mle|ml)?\b(.*)', account_holder, re.IGNORECASE): + return AccountOwnership.CO_OWNER + elif all([n in account_holder for n in self.env['name'].split(' ')]): + return AccountOwnership.OWNER + else: + return AccountOwnership.ATTORNEY + def obj_balance(self): if Field('type')(self) == Account.TYPE_LOAN: balance = CleanDecimal('.//span[@class="number"]', replace_dots=True, default=NotAvailable)(self) diff --git a/modules/bp/pages/compat/weboob_capabilities_bank.py b/modules/bp/pages/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bp/pages/compat/weboob_capabilities_bank.py +++ b/modules/bp/pages/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bp/pages/compat/weboob_exceptions.py b/modules/bp/pages/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bp/pages/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bp/pages/login.py b/modules/bp/pages/login.py index cf339e0dd0294cc1dd272c349d6c65ca6a1fbf19..01ebedfc816ed15e3f5ee6cf3e341a32626aab32 100644 --- a/modules/bp/pages/login.py +++ b/modules/bp/pages/login.py @@ -21,7 +21,7 @@ from io import BytesIO -from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword, NoAccountsException +from .compat.weboob_exceptions import BrowserUnavailable, BrowserIncorrectPassword, NoAccountsException from weboob.browser.pages import LoggedPage from .compat.weboob_browser_filters_standard import CleanText, Regexp from .compat.weboob_tools_captcha_virtkeyboard import VirtKeyboard diff --git a/modules/bp/pages/pro.py b/modules/bp/pages/pro.py index 40ba7070bdc2b025d8048c418669f8e3e72d735d..9907aca90ed0184bf73c95c469dc225f0a339422 100644 --- a/modules/bp/pages/pro.py +++ b/modules/bp/pages/pro.py @@ -26,7 +26,7 @@ from .compat.weboob_capabilities_bank import Account from weboob.capabilities.profile import Company from weboob.capabilities.base import NotAvailable -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from .accounthistory import Transaction from .base import MyHTMLPage diff --git a/modules/bp/pages/transfer.py b/modules/bp/pages/transfer.py index 295625e1767eb1335a1e4b7785e30b1c44bc8b59..b0679c2bba38cdf3e71ae08ce586acf6bd718be8 100644 --- a/modules/bp/pages/transfer.py +++ b/modules/bp/pages/transfer.py @@ -32,7 +32,7 @@ from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.tools.capabilities.bank.iban import is_iban_valid from weboob.tools.value import Value -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable from .base import MyHTMLPage diff --git a/modules/bred/bred/browser.py b/modules/bred/bred/browser.py index a07a65455e7165c3f8805ac0774cfa0e4bb895c2..2024ffed835c3d75a4b9a30be7e0e500e0cc8131 100644 --- a/modules/bred/bred/browser.py +++ b/modules/bred/bred/browser.py @@ -43,24 +43,24 @@ class BredBrowser(LoginBrowser): BASEURL = 'https://www.bred.fr' - home = URL('/$', HomePage) - login = URL('/transactionnel/Authentication', LoginPage) - error = URL('.*gestion-des-erreurs/erreur-pwd', - '.*gestion-des-erreurs/opposition', - '/pages-gestion-des-erreurs/erreur-technique', - '/pages-gestion-des-erreurs/message-tiers-oppose', ErrorPage) - universe = URL('/transactionnel/services/applications/menu/getMenuUnivers', UniversePage) - token = URL(r'/transactionnel/services/rest/User/nonce\?random=(?P.*)', TokenPage) - move_universe = URL('/transactionnel/services/applications/listes/(?P.*)/default', MoveUniversePage) - switch = URL('/transactionnel/services/rest/User/switch', SwitchPage) - loans = URL('/transactionnel/services/applications/prets/liste', LoansPage) - accounts = URL('/transactionnel/services/rest/Account/accounts', AccountsPage) - iban = URL('/transactionnel/services/rest/Account/account/(?P.*)/iban', IbanPage) - life_insurances = URL('/transactionnel/services/applications/avoirsPrepar/getAvoirs', LifeInsurancesPage) - search = URL('/transactionnel/services/applications/operations/getSearch/', SearchPage) - profile = URL('/transactionnel/services/rest/User/user', ProfilePage) - emails = URL('/transactionnel/services/applications/gestionEmail/getAdressesMails', EmailsPage) - error_code = URL('/.*\?errorCode=.*', ErrorCodePage) + home = URL(r'/$', HomePage) + login = URL(r'/transactionnel/Authentication', LoginPage) + error = URL(r'.*gestion-des-erreurs/erreur-pwd', + r'.*gestion-des-erreurs/opposition', + r'/pages-gestion-des-erreurs/erreur-technique', + r'/pages-gestion-des-erreurs/message-tiers-oppose', ErrorPage) + universe = URL(r'/transactionnel/services/applications/menu/getMenuUnivers', UniversePage) + token = URL(r'/transactionnel/services/rest/User/nonce\?random=(?P.*)', TokenPage) + move_universe = URL(r'/transactionnel/services/applications/listes/(?P.*)/default', MoveUniversePage) + switch = URL(r'/transactionnel/services/rest/User/switch', SwitchPage) + loans = URL(r'/transactionnel/services/applications/prets/liste', LoansPage) + accounts = URL(r'/transactionnel/services/rest/Account/accounts', AccountsPage) + iban = URL(r'/transactionnel/services/rest/Account/account/(?P.*)/iban', IbanPage) + life_insurances = URL(r'/transactionnel/services/applications/avoirsPrepar/getAvoirs', LifeInsurancesPage) + search = URL(r'/transactionnel/services/applications/operations/getSearch/', SearchPage) + profile = URL(r'/transactionnel/services/rest/User/user', ProfilePage) + emails = URL(r'/transactionnel/services/applications/gestionEmail/getAdressesMails', EmailsPage) + error_code = URL(r'/.*\?errorCode=.*', ErrorCodePage) def __init__(self, accnum, login, password, *args, **kwargs): kwargs['username'] = login @@ -198,7 +198,7 @@ def get_history(self, account, coming=False): self.logger.debug('stopping coming after %s', t) return - next_page = len(transactions) == 50 + next_page = len(transactions) > 0 offset += 50 # This assert supposedly prevents infinite loops, diff --git a/modules/bred/bred/compat/weboob_capabilities_bank.py b/modules/bred/bred/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bred/bred/compat/weboob_capabilities_bank.py +++ b/modules/bred/bred/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bred/bred/compat/weboob_exceptions.py b/modules/bred/bred/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bred/bred/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bred/bred/pages.py b/modules/bred/bred/pages.py index ad56f208b86ca8c74b4f1050e9e398e362938f24..9823c72f98f55c69369c442dbbaee1ddbaccdfb9 100644 --- a/modules/bred/bred/pages.py +++ b/modules/bred/bred/pages.py @@ -24,7 +24,7 @@ from decimal import Decimal from weboob.tools.date import parse_french_date -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable, ActionNeeded, ParseError +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable, ActionNeeded from weboob.capabilities.base import find_object from weboob.browser.pages import JsonPage, LoggedPage, HTMLPage from weboob.capabilities import NotAvailable @@ -38,21 +38,22 @@ class Transaction(FrenchTransaction): - PATTERNS = [(re.compile(r'^.*Virement (?P.*)'), FrenchTransaction.TYPE_TRANSFER), - (re.compile(r'PRELEV SEPA (?P.*)'), FrenchTransaction.TYPE_ORDER), - (re.compile(r'.*Prélèvement.*'), FrenchTransaction.TYPE_ORDER), - (re.compile(r'^(REGL|Rgt)(?P.*)'), FrenchTransaction.TYPE_ORDER), - (re.compile(r'^(?P.*) Carte \d+\s+ LE (?P
\d{2})/(?P\d{2})/(?P\d{2})'), - FrenchTransaction.TYPE_CARD), - (re.compile(r'^Débit mensuel.*'), FrenchTransaction.TYPE_CARD_SUMMARY), - (re.compile(r"^Retrait d'espèces à un DAB (?P.*) CARTE [X\d]+ LE (?P
\d{2})/(?P\d{2})/(?P\d{2})"), - FrenchTransaction.TYPE_WITHDRAWAL), - (re.compile(r'^Paiement de chèque (?P.*)'), FrenchTransaction.TYPE_CHECK), - (re.compile(r'^(Cotisation|Intérêts) (?P.*)'), FrenchTransaction.TYPE_BANK), - (re.compile(r'^(Remise Chèque|Remise de chèque)\s*(?P.*)'), FrenchTransaction.TYPE_DEPOSIT), - (re.compile(r'^Versement (?P.*)'), FrenchTransaction.TYPE_DEPOSIT), - (re.compile(r'^(?P.*)LE (?P
\d{2})/(?P\d{2})/(?P\d{2})\s*(?P.*)'), - FrenchTransaction.TYPE_UNKNOWN), + PATTERNS = [ + (re.compile(r'^.*Virement (?P.*)'), FrenchTransaction.TYPE_TRANSFER), + (re.compile(r'PRELEV SEPA (?P.*)'), FrenchTransaction.TYPE_ORDER), + (re.compile(r'.*Prélèvement.*'), FrenchTransaction.TYPE_ORDER), + (re.compile(r'^(REGL|Rgt)(?P.*)'), FrenchTransaction.TYPE_ORDER), + (re.compile(r'^(?P.*) Carte \d+\s+ LE (?P
\d{2})/(?P\d{2})/(?P\d{2})'), + FrenchTransaction.TYPE_CARD), + (re.compile(r'^Débit mensuel.*'), FrenchTransaction.TYPE_CARD_SUMMARY), + (re.compile(r"^Retrait d'espèces à un DAB (?P.*) CARTE [X\d]+ LE (?P
\d{2})/(?P\d{2})/(?P\d{2})"), + FrenchTransaction.TYPE_WITHDRAWAL), + (re.compile(r'^Paiement de chèque (?P.*)'), FrenchTransaction.TYPE_CHECK), + (re.compile(r'^(Cotisation|Intérêts) (?P.*)'), FrenchTransaction.TYPE_BANK), + (re.compile(r'^(Remise Chèque|Remise de chèque)\s*(?P.*)'), FrenchTransaction.TYPE_DEPOSIT), + (re.compile(r'^Versement (?P.*)'), FrenchTransaction.TYPE_DEPOSIT), + (re.compile(r'^(?P.*)LE (?P
\d{2})/(?P\d{2})/(?P\d{2})\s*(?P.*)'), + FrenchTransaction.TYPE_UNKNOWN), ] @@ -135,7 +136,7 @@ def iter_accounts(self, accnum, current_univers): accounts_list = [] - for content in self.get_content(): + for content in self.get_content(): if accnum != '00000000000' and content['numero'] != accnum: continue for poste in content['postes']: @@ -263,15 +264,16 @@ def iter_history(self, account, operation_list, seen, today, coming): t = Transaction() t.id = str(op['id']) if op['id'] in seen: - raise ParseError('There are several transactions with the same ID, probably an infinite loop') + self.logger.debug('Skipped transaction : "%s %s"' % (op['id'], op['libelle'])) + continue seen.add(t.id) - d = date.fromtimestamp(op.get('dateDebit', op.get('dateOperation'))/1000) + d = date.fromtimestamp(op.get('dateDebit', op.get('dateOperation')) / 1000) op['details'] = [re.sub(r'\s+', ' ', i).replace('\x00', '') for i in op['details'] if i] # sometimes they put "null" elements... label = re.sub(r'\s+', ' ', op['libelle']).replace('\x00', '') raw = ' '.join([label] + op['details']) - t.rdate = date.fromtimestamp(op.get('dateOperation', op.get('dateDebit'))/1000) - vdate = date.fromtimestamp(op.get('dateValeur', op.get('dateDebit', op.get('dateOperation')))/1000) + t.rdate = date.fromtimestamp(op.get('dateOperation', op.get('dateDebit')) / 1000) + vdate = date.fromtimestamp(op.get('dateValeur', op.get('dateDebit', op.get('dateOperation'))) / 1000) t.parse(d, raw, vdate=vdate) t.amount = Decimal(str(op['montant'])) if 'categorie' in op: diff --git a/modules/bred/compat/weboob_capabilities_bank.py b/modules/bred/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bred/compat/weboob_capabilities_bank.py +++ b/modules/bred/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bred/dispobank/browser.py b/modules/bred/dispobank/browser.py index 01c6f0647b6ecedf4334ad91214ebca7f4e32748..817697b8bd1ebd0c109b2bc98e672a1af5b0016a 100644 --- a/modules/bred/dispobank/browser.py +++ b/modules/bred/dispobank/browser.py @@ -18,8 +18,8 @@ # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, need_login, URL -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, need_login, URL +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, LoginResultPage, AccountsPage, EmptyPage, TransactionsPage diff --git a/modules/bred/dispobank/compat/weboob_capabilities_bank.py b/modules/bred/dispobank/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/bred/dispobank/compat/weboob_capabilities_bank.py +++ b/modules/bred/dispobank/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/bred/dispobank/compat/weboob_exceptions.py b/modules/bred/dispobank/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/bred/dispobank/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/bred/module.py b/modules/bred/module.py index 0f7df0441c2b1c96a6a653933d17a940e13b16af..ff620eec5575829fd4190b3ccc52b760dea6eab3 100644 --- a/modules/bred/module.py +++ b/modules/bred/module.py @@ -17,6 +17,7 @@ # 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 from .compat.weboob_capabilities_bank import CapBankWealth, AccountNotFound, Account from weboob.capabilities.base import find_object @@ -38,21 +39,23 @@ class BredModule(Module, CapBankWealth, CapProfile): VERSION = '1.5' DESCRIPTION = u'Bred' LICENSE = 'LGPLv3+' - CONFIG = BackendConfig(ValueBackendPassword('login', label='Identifiant', masked=False), - ValueBackendPassword('password', label='Mot de passe'), - Value('website', label=u"Site d'accès", default='bred', - choices={'bred': 'BRED', 'dispobank': 'DispoBank'}), - Value('accnum', label=u'Numéro du compte bancaire (optionnel)', default='', masked=False) - ) - - BROWSERS = {'bred': BredBrowser, - 'dispobank': DispoBankBrowser, - } + CONFIG = BackendConfig( + ValueBackendPassword('login', label='Identifiant', masked=False), + ValueBackendPassword('password', label='Mot de passe'), + Value('website', label="Site d'accès", default='bred', + choices={'bred': 'BRED', 'dispobank': 'DispoBank'}), + Value('accnum', label='Numéro du compte bancaire (optionnel)', default='', masked=False), + ) + + BROWSERS = { + 'bred': BredBrowser, + 'dispobank': DispoBankBrowser, + } def create_default_browser(self): self.BROWSER = self.BROWSERS[self.config['website'].get()] - return self.create_browser(self.config['accnum'].get().replace(' ','').zfill(11), + return self.create_browser(self.config['accnum'].get().replace(' ', '').zfill(11), self.config['login'].get(), self.config['password'].get()) diff --git a/modules/btpbanque/compat/weboob_capabilities_bank.py b/modules/btpbanque/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/btpbanque/compat/weboob_capabilities_bank.py +++ b/modules/btpbanque/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/caels/compat/weboob_capabilities_bank.py b/modules/caels/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/caels/compat/weboob_capabilities_bank.py +++ b/modules/caels/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/caissedepargne/browser.py b/modules/caissedepargne/browser.py index 5666933f87422e134e1e7ca5bdc2c8c8b009b96f..d89acc9dbb70a811ccbe2050f6db4f703ec6d6b0 100644 --- a/modules/caissedepargne/browser.py +++ b/modules/caissedepargne/browser.py @@ -22,6 +22,7 @@ import re import datetime import json +from hashlib import sha256 from decimal import Decimal from dateutil import parser @@ -29,11 +30,15 @@ from weboob.browser.browsers import LoginBrowser, need_login, StatesMixin from weboob.browser.switch import SiteSwitch from .compat.weboob_browser_url import URL -from .compat.weboob_capabilities_bank import Account, AddRecipientStep, Recipient, TransferBankError, Transaction, TransferStep +from .compat.weboob_capabilities_bank import ( + Account, AddRecipientStep, Recipient, TransferBankError, Transaction, TransferStep, + TransferInvalidOTP, RecipientInvalidOTP, +) from weboob.capabilities.base import NotAvailable, find_object +from weboob.capabilities.bill import Subscription from weboob.capabilities.profile import Profile from weboob.browser.exceptions import BrowserHTTPNotFound, ClientError, ServerError -from weboob.exceptions import ( +from .compat.weboob_exceptions import ( BrowserIncorrectPassword, BrowserUnavailable, BrowserHTTPError, BrowserPasswordExpired, ActionNeeded ) from weboob.tools.capabilities.bank.transactions import ( @@ -119,9 +124,15 @@ class CaisseEpargne(LoginBrowser, StatesMixin): r'https://.*/particuliers/epargner.*', GarbagePage) sms = URL(r'https://www.icgauth.caisse-epargne.fr/dacswebssoissuer/AuthnRequestServlet', SmsPage) sms_option = URL(r'https://www.icgauth.caisse-epargne.fr/dacstemplate-SOL/index.html\?transactionID=.*', SmsPageOption) - request_sms = URL(r'https://www.icgauth.caisse-epargne.fr/dacsrest/api/v1u0/transaction/(?P)', SmsRequest) + request_sms = URL( + r'https://(?Pwww.icgauth.[^/]+)/dacsrest/api/v1u0/transaction/(?P)', + SmsRequest, + ) - __states__ = ('BASEURL', 'multi_type', 'typeAccount', 'is_cenet_website', 'recipient_form', 'is_send_sms') + __states__ = ( + 'BASEURL', 'multi_type', 'typeAccount', 'is_cenet_website', 'recipient_form', + 'is_send_sms', 'otp_validation', 'otp_url', + ) # Accounts managed in life insurance space (not in linebourse) @@ -158,11 +169,14 @@ def __init__(self, nuser, *args, **kwargs): self.nuser = nuser self.recipient_form = None self.is_send_sms = None + self.otp_validation = None + self.otp_url = None self.weboob = kwargs['weboob'] self.market_url = kwargs.pop( 'market_url', 'https://www.caisse-epargne.offrebourse.com', ) + self.has_subscription = True super(CaisseEpargne, self).__init__(*args, **kwargs) @@ -187,8 +201,7 @@ def load_state(self, state): if state.get('expire') and parser.parse(state['expire']) < datetime.datetime.now(): return self.logger.info('State expired, not reloading it from storage') - # Reload session only for add recipient step - transfer_states = ('recipient_form', 'is_send_sms') + transfer_states = ('recipient_form', 'is_send_sms', 'otp_validation', 'otp_url') for transfer_state in transfer_states: if transfer_state in state and state[transfer_state] is not None: @@ -196,10 +209,10 @@ def load_state(self, state): self.logged = True break - # need to post to valid otp when adding recipient. def locate_browser(self, state): - if 'is_send_sms' in state and state['is_send_sms']: - super(CaisseEpargne, self).locate_browser(state) + # in case of transfer/add recipient, we shouldn't go back to previous page + # site will crash else + pass def do_login(self): """ @@ -927,6 +940,9 @@ def init_transfer(self, account, recipient, transfer): if self.sms_option.is_here(): self.is_send_sms = True + self.otp_update_state() + self.otp_choose_sms() + raise TransferStep( transfer, Value( @@ -946,7 +962,7 @@ def otp_sms_continue_transfer(self, transfer, **params): self.is_send_sms = False assert 'otp_sms' in params, 'OTP SMS is missing' - self.otp_sms_validation(params['otp_sms']) + self.otp_sms_validation(params['otp_sms'], TransferInvalidOTP) if self.transfer.is_here(): self.page.continue_transfer(transfer.account_label, transfer.recipient_label, transfer.label) return self.page.update_transfer(transfer) @@ -968,29 +984,42 @@ def get_recipient_obj(self, recipient): r.bank_name = NotAvailable return r - def otp_sms_validation(self, otp_sms): - tr_id = re.search(r'transactionID=(.*)', self.page.url) - if tr_id: - transaction_id = tr_id.group(1) + def otp_update_state(self): + transaction_id = re.search(r'transactionID=(.*)', self.page.url) + if transaction_id: + transaction_id = transaction_id.group(1) else: assert False, 'Transfer transaction id was not found in url' - self.request_sms.go(param=transaction_id) + self.request_sms.go(domain=urlparse(self.url).netloc, param=transaction_id) + self.otp_validation = self.page.validation_unit() + + self.otp_url = self.url + if not self.url.endswith('/step'): + self.otp_url += '/step' + + def otp_choose_sms(self): + key = next(iter(self.otp_validation)) + if self.otp_validation[key][0]['type'] == 'SMS': + return + + self.location(self.otp_url, json={'fallback': {}}) + self.otp_validation = self.page.validation_unit() - key = self.page.validate_key() + def otp_sms_validation(self, otp_sms, otp_exception): + key = next(iter(self.otp_validation)) data = { 'validate': { key: [{ - 'id': self.page.validation_id(key), + 'id': self.otp_validation[key][0]['id'], 'otp_sms': otp_sms, - 'type': 'SMS' - }] - } + 'type': 'SMS', + }], + }, } - headers = {'Content-Type': 'application/json'} - self.location(self.url + '/step', json=data, headers=headers) + self.location(self.otp_url, json=data) - saml = self.page.get_saml() + saml = self.page.get_saml(otp_exception) action = self.page.get_action() self.location(action, data={'SAMLResponse': saml}) @@ -1025,7 +1054,7 @@ def new_recipient(self, recipient, **params): return self.end_sms_recipient(recipient, **params) if 'otp_sms' in params: - self.otp_sms_validation(params['otp_sms']) + self.otp_sms_validation(params['otp_sms'], RecipientInvalidOTP) if self.authent.is_here(): self.page.go_on() @@ -1038,8 +1067,15 @@ def new_recipient(self, recipient, **params): # 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.' + if self.sms_option.is_here(): self.is_send_sms = True + self.otp_update_state() + self.otp_choose_sms() + raise AddRecipientStep( self.get_recipient_obj(recipient), Value( @@ -1058,6 +1094,10 @@ def new_recipient(self, recipient, **params): self.page.set_browser_form() 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') + assert self.subscription.is_here(), "Couldn't go to documents page" + @need_login def iter_subscription(self): self.home.go() @@ -1068,6 +1108,20 @@ def iter_subscription(self): if self.unavailable_page.is_here(): # some users don't have checking account self.home_tache.go(tache='EPASYNT0') + if self.garbage.is_here(): # User has no subscription, checking if they have documents, if so creating fake subscription + self.has_subscription = False + self.home_tache.go(tache='CPTSYNT0') + if not self.subscription.is_here(): # Looks like there is nothing to return + return [] + self.logger.warning("Couldn't find subscription, creating a fake one to return documents available") + + profile = self.get_profile() + + sub = Subscription() + sub.label = sub.subscriber = profile.name + sub.id = sha256(profile.name.lower().encode('utf-8')).hexdigest() + + return [sub] self.page.go_subscription() if not self.subscription.is_here(): # if user is not allowed to have subscription we are redirected to IndexPage @@ -1081,6 +1135,9 @@ def iter_subscription(self): @need_login def iter_documents(self, subscription): self.home.go() + if not self.has_subscription: + self.go_documents_without_sub() + return self.page.iter_documents(sub_id=subscription.id, has_subscription=self.has_subscription) self.home_tache.go(tache='CPTSYNT1') if self.unavailable_page.is_here(): # some users don't have checking account @@ -1088,11 +1145,14 @@ def iter_documents(self, subscription): self.page.go_subscription() assert self.subscription.is_here() - return self.page.iter_documents(sub_id=subscription.id) + return self.page.iter_documents(sub_id=subscription.id, has_subscription=self.has_subscription) @need_login def download_document(self, document): self.home.go() + if not self.has_subscription: + self.go_documents_without_sub() + return self.page.download_document(document).content self.home_tache.go(tache='CPTSYNT1') if self.unavailable_page.is_here(): # some users don't have checking account diff --git a/modules/caissedepargne/cenet/browser.py b/modules/caissedepargne/cenet/browser.py index a6ab24e6ecb829c5e897ccfa4314553ab22b0a65..fbd7e1f23ba539811515b34e146f062ceffd0a68 100644 --- a/modules/caissedepargne/cenet/browser.py +++ b/modules/caissedepargne/cenet/browser.py @@ -24,7 +24,7 @@ from weboob.browser.browsers import LoginBrowser, need_login, StatesMixin from .compat.weboob_browser_url import URL from weboob.browser.exceptions import ClientError -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable from weboob.capabilities.base import find_object from .compat.weboob_capabilities_bank import Account from weboob.tools.capabilities.bank.transactions import sorted_transactions, FrenchTransaction diff --git a/modules/caissedepargne/cenet/compat/weboob_capabilities_bank.py b/modules/caissedepargne/cenet/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/caissedepargne/cenet/compat/weboob_capabilities_bank.py +++ b/modules/caissedepargne/cenet/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/caissedepargne/cenet/compat/weboob_exceptions.py b/modules/caissedepargne/cenet/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/caissedepargne/cenet/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/caissedepargne/cenet/pages.py b/modules/caissedepargne/cenet/pages.py index 9b6c70e125a2926800a585e4ecd61ff245820b82..2ae07b61a10b42860f6d23d183bacd62db885d98 100644 --- a/modules/caissedepargne/cenet/pages.py +++ b/modules/caissedepargne/cenet/pages.py @@ -31,7 +31,7 @@ from weboob.capabilities.profile import Profile from weboob.capabilities.bill import DocumentTypes, Subscription, Document from weboob.tools.capabilities.bank.transactions import FrenchTransaction -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable class Transaction(FrenchTransaction): diff --git a/modules/caissedepargne/compat/weboob_capabilities_bank.py b/modules/caissedepargne/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/caissedepargne/compat/weboob_capabilities_bank.py +++ b/modules/caissedepargne/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/caissedepargne/compat/weboob_exceptions.py b/modules/caissedepargne/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/caissedepargne/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/caissedepargne/pages.py b/modules/caissedepargne/pages.py index 92a3419f215ddc81cda1283dc21b7309e34d853a..a390e03450565244f77a17c3f7b53201a8d7a1ae 100644 --- a/modules/caissedepargne/pages.py +++ b/modules/caissedepargne/pages.py @@ -38,7 +38,7 @@ from weboob.capabilities import NotAvailable from .compat.weboob_capabilities_bank import ( Account, Investment, Recipient, TransferBankError, Transfer, - AddRecipientBankError, Loan, RecipientInvalidOTP, + AddRecipientBankError, Loan, ) from weboob.capabilities.bill import DocumentTypes, Subscription, Document from weboob.tools.capabilities.bank.investments import is_isin_valid @@ -46,7 +46,7 @@ from weboob.tools.capabilities.bank.iban import is_rib_valid, rib2iban, is_iban_valid from .compat.weboob_tools_captcha_virtkeyboard import GridVirtKeyboard from weboob.tools.compat import unicode -from weboob.exceptions import NoAccountsException, BrowserUnavailable, ActionNeeded +from .compat.weboob_exceptions import NoAccountsException, BrowserUnavailable, ActionNeeded from weboob.browser.filters.json import Dict def MyDecimal(*args, **kwargs): @@ -1057,7 +1057,7 @@ def get_card_coming_info(self, number, info): if CleanText('//a[contains(text(),"%s")]' % number)(self.doc): # For all cards except the first one for the same check account, we have to get info through their href info link = CleanText(Link('//a[contains(text(),"%s")]' % number))(self.doc) - infos = re.match(r'.*(DETAIL_OP_M0&[^\"]+).*', link) + infos = re.match(r'.*(DETAIL_OP_M\d&[^\"]+).*', link) info['link'] = infos.group(1) return info @@ -1506,6 +1506,12 @@ def go_add_recipient(self): form['__EVENTARGUMENT'] = m.group(2) form.submit() + def handle_error(self): + # the website cannot add recipients from out of France + error_msg = CleanText('//div[@id="divPopinInfoAjout"]/p[not(a)]')(self.doc) + if error_msg: + raise AddRecipientBankError(message=error_msg) + class TransferConfirmPage(TransferErrorPage, IndexPage): def build_doc(self, content): @@ -1662,18 +1668,17 @@ class SmsRequestStep(LoggedPage, JsonPage): class SmsRequest(LoggedPage, JsonPage): - def validate_key(self): - return list(self.doc['step']['validationUnits'][0].keys())[0] - - def validation_id(self, key): - return self.doc['step']['validationUnits'][0][key][0]['id'] + def validation_unit(self): + if 'step' in self.doc: + return self.doc['step']['validationUnits'][0] + return self.doc['validationUnits'][0] - def get_saml(self): + def get_saml(self, otp_exception): if not 'response' in self.doc: error = self.doc['phase']['previousResult'] if error == 'FAILED_AUTHENTICATION': - raise RecipientInvalidOTP() + raise otp_exception() assert not error, 'Error during recipient validation: %s' % error return self.doc['response']['saml2_post']['samlResponse'] @@ -1852,14 +1857,16 @@ class iter_documents(ListElement): ignore_duplicate = True @property def item_xpath(self): - return '//h3[contains(text(), "%s")]//following-sibling::div[@class="panel"][1]/table/tbody/tr' % Env('sub_id')(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 '//div[@id="MM_CONSULTATION_RELEVES_COURRIERS_EDOCUMENTS_divRelevesCourriers"]/table/tbody/tr' class item(ItemElement): klass = Document obj_type = DocumentTypes.OTHER obj_format = 'pdf' - obj_url = Regexp(Link('.//td[@class="telecharger"]/a'), r'WebForm_PostBackOptions\("(\S*)"') + 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_label = Format('%s %s', CleanText('./td[3]'), CleanText('./td[2]')) obj_date = Date(CleanText('./td[2]'), dayfirst=True) diff --git a/modules/capeasi/compat/weboob_capabilities_bank.py b/modules/capeasi/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/capeasi/compat/weboob_capabilities_bank.py +++ b/modules/capeasi/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/carrefourbanque/browser.py b/modules/carrefourbanque/browser.py index daf6c544ce243ff22ebf35ff5a973b67a9f9e31d..f0384be92e262a275633082fc42d883a04403099 100644 --- a/modules/carrefourbanque/browser.py +++ b/modules/carrefourbanque/browser.py @@ -19,7 +19,7 @@ from time import sleep from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin -from weboob.exceptions import BrowserIncorrectPassword, NocaptchaQuestion, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, NocaptchaQuestion, BrowserUnavailable from .compat.weboob_capabilities_bank import Account from weboob.tools.compat import basestring diff --git a/modules/carrefourbanque/compat/weboob_capabilities_bank.py b/modules/carrefourbanque/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/carrefourbanque/compat/weboob_capabilities_bank.py +++ b/modules/carrefourbanque/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/carrefourbanque/compat/weboob_exceptions.py b/modules/carrefourbanque/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/carrefourbanque/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/centquatre/browser.py b/modules/centquatre/browser.py index a0f1b155ad54851ae3b3f0d9204509dbd2fefa92..9395611dbdcac43e39eff8800dfa1ae9bce1d08e 100644 --- a/modules/centquatre/browser.py +++ b/modules/centquatre/browser.py @@ -20,8 +20,8 @@ import itertools -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import CentQuatrePage, LoginPage, TicketsPage, TicketsDetailsPage diff --git a/modules/centquatre/compat/weboob_exceptions.py b/modules/centquatre/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/centquatre/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/cic/compat/weboob_capabilities_bank.py b/modules/cic/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cic/compat/weboob_capabilities_bank.py +++ b/modules/cic/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cices/compat/weboob_capabilities_bank.py b/modules/cices/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cices/compat/weboob_capabilities_bank.py +++ b/modules/cices/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/citibank/browser.py b/modules/citibank/browser.py index cc7d26c88552f661bd02a7df732c6cc9f5c43243..5aefd74e24801a0cac39b54ef0b96de77df75e71 100644 --- a/modules/citibank/browser.py +++ b/modules/citibank/browser.py @@ -26,7 +26,7 @@ from weboob.browser.browsers import URL, LoginBrowser, need_login from weboob.browser.pages import HTMLPage, JsonPage, RawPage from .compat.weboob_capabilities_bank import Account, AccountNotFound, Transaction -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.capabilities.bank.transactions import AmericanTransaction as AmTr from weboob.tools.js import Javascript diff --git a/modules/citibank/compat/weboob_capabilities_bank.py b/modules/citibank/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/citibank/compat/weboob_capabilities_bank.py +++ b/modules/citibank/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/citibank/compat/weboob_exceptions.py b/modules/citibank/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/citibank/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/cityscoot/browser.py b/modules/cityscoot/browser.py index 68cc92393e4f0e6aa5507edd96d7a0363fa157f0..ad7cc47cb730f4b6672132f43e8a3026aeec7c20 100644 --- a/modules/cityscoot/browser.py +++ b/modules/cityscoot/browser.py @@ -20,8 +20,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, SubscriptionsPage, DocumentsPage diff --git a/modules/cityscoot/compat/weboob_exceptions.py b/modules/cityscoot/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/cityscoot/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/cmb/compat/weboob_capabilities_bank.py b/modules/cmb/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cmb/compat/weboob_capabilities_bank.py +++ b/modules/cmb/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cmes/browser.py b/modules/cmes/browser.py index f75fba03b7a50992186279ec96ccea8cc419bdfe..29e1ae5d921e7fea9aa3649695cddf362e47db8b 100644 --- a/modules/cmes/browser.py +++ b/modules/cmes/browser.py @@ -22,8 +22,8 @@ from datetime import datetime from dateutil.relativedelta import relativedelta -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import ( LoginPage, AccountsPage, OperationsListPage, OperationPage, ActionNeededPage, InvestmentPage, ) diff --git a/modules/cmes/compat/weboob_capabilities_bank.py b/modules/cmes/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cmes/compat/weboob_capabilities_bank.py +++ b/modules/cmes/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cmes/compat/weboob_exceptions.py b/modules/cmes/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/cmes/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/cmes/pages.py b/modules/cmes/pages.py index 95b083f8d2bb211cda19db68e9afdc5ea23d05fa..49279359e1a28fb6c0616c8d68df2a7fb6a30e61 100644 --- a/modules/cmes/pages.py +++ b/modules/cmes/pages.py @@ -29,7 +29,7 @@ from weboob.browser.filters.html import Link from .compat.weboob_capabilities_bank import Account, Investment, Pocket, NotAvailable from weboob.tools.capabilities.bank.transactions import FrenchTransaction -from weboob.exceptions import ActionNeeded +from .compat.weboob_exceptions import ActionNeeded class Transaction(FrenchTransaction): diff --git a/modules/cmmc/compat/weboob_capabilities_bank.py b/modules/cmmc/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cmmc/compat/weboob_capabilities_bank.py +++ b/modules/cmmc/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cmso/compat/weboob_capabilities_bank.py b/modules/cmso/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cmso/compat/weboob_capabilities_bank.py +++ b/modules/cmso/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cmso/par/browser.py b/modules/cmso/par/browser.py index 9af33b72186e2529e86937132e2718a8cb08b057..5d142b166fa65e060c87943ab24c6d3a81e4ef33 100644 --- a/modules/cmso/par/browser.py +++ b/modules/cmso/par/browser.py @@ -27,7 +27,7 @@ from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin from weboob.browser.exceptions import ClientError, ServerError -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable from .compat.weboob_capabilities_bank import Account, Transaction, AccountNotFound from weboob.capabilities.base import find_object from weboob.tools.capabilities.bank.transactions import sorted_transactions diff --git a/modules/cmso/par/compat/weboob_capabilities_bank.py b/modules/cmso/par/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cmso/par/compat/weboob_capabilities_bank.py +++ b/modules/cmso/par/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cmso/par/compat/weboob_exceptions.py b/modules/cmso/par/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/cmso/par/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/cmso/par/pages.py b/modules/cmso/par/pages.py index 4a411a95ea2ddb9346acf6b8f1889d3df57b995f..95248c52d4d9a3f9402309018ecc4bf5bd0072a5 100644 --- a/modules/cmso/par/pages.py +++ b/modules/cmso/par/pages.py @@ -37,7 +37,7 @@ from weboob.capabilities.base import NotAvailable from weboob.capabilities.profile import Profile from weboob.tools.capabilities.bank.transactions import FrenchTransaction -from weboob.exceptions import ParseError +from .compat.weboob_exceptions import ParseError from weboob.tools.capabilities.bank.investments import is_isin_valid from weboob.tools.compat import unicode diff --git a/modules/cmso/pro/browser.py b/modules/cmso/pro/browser.py index 9cfdb825599939c7c8772c8f7c403f87c2964309..b53668fdec475409f4e562fecfd882db4b830ec3 100644 --- a/modules/cmso/pro/browser.py +++ b/modules/cmso/pro/browser.py @@ -26,7 +26,7 @@ from weboob.tools.capabilities.bank.transactions import sorted_transactions from weboob.capabilities.base import find_object from .compat.weboob_capabilities_bank import Account -from weboob.exceptions import BrowserHTTPError, BrowserIncorrectPassword, ActionNeeded, BrowserUnavailable +from .compat.weboob_exceptions import BrowserHTTPError, BrowserIncorrectPassword, ActionNeeded, BrowserUnavailable from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.browser.exceptions import ServerError from weboob.tools.date import LinearDateGuesser diff --git a/modules/cmso/pro/compat/weboob_capabilities_bank.py b/modules/cmso/pro/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cmso/pro/compat/weboob_capabilities_bank.py +++ b/modules/cmso/pro/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cmso/pro/compat/weboob_exceptions.py b/modules/cmso/pro/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/cmso/pro/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/cmso/pro/pages.py b/modules/cmso/pro/pages.py index d71685abe5d325b1ef9df5a989416752454cfd95..505edcc655f789dc6385d918c65a0c8d4ed00f63 100644 --- a/modules/cmso/pro/pages.py +++ b/modules/cmso/pro/pages.py @@ -21,7 +21,7 @@ import re -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.browser.pages import HTMLPage, JsonPage, pagination from weboob.browser.elements import ListElement, ItemElement, TableElement, method from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, DateGuesser, Env, Field, Filter, Regexp, Currency, Date diff --git a/modules/cragr/api/browser.py b/modules/cragr/api/browser.py index 80758d777c5baf00637df9b34169525bade7ed73..69de701676a81e1a3c4361ccba12ded7ea6e3706 100644 --- a/modules/cragr/api/browser.py +++ b/modules/cragr/api/browser.py @@ -28,7 +28,7 @@ from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.browser.switch import SiteSwitch from weboob.browser.exceptions import ServerError, ClientError, BrowserHTTPNotFound, HTTPNotFound -from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword, ActionNeeded +from .compat.weboob_exceptions import BrowserUnavailable, BrowserIncorrectPassword, ActionNeeded from weboob.tools.capabilities.bank.iban import is_iban_valid from weboob.tools.capabilities.bank.transactions import sorted_transactions diff --git a/modules/cragr/api/compat/weboob_capabilities_bank.py b/modules/cragr/api/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cragr/api/compat/weboob_capabilities_bank.py +++ b/modules/cragr/api/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cragr/api/compat/weboob_exceptions.py b/modules/cragr/api/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/cragr/api/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/cragr/api/pages.py b/modules/cragr/api/pages.py index a20e4057721678408dd41aa48d81f098543593da..bb0cb92f5f8479d3830812cbac6ecc56a598e836 100644 --- a/modules/cragr/api/pages.py +++ b/modules/cragr/api/pages.py @@ -25,7 +25,7 @@ import dateutil from weboob.browser.pages import HTMLPage, JsonPage, LoggedPage -from weboob.exceptions import ActionNeeded +from .compat.weboob_exceptions import ActionNeeded from weboob.capabilities import NotAvailable from weboob.capabilities.base import empty from .compat.weboob_capabilities_bank import ( @@ -41,7 +41,7 @@ from weboob.browser.filters.json import Dict from weboob.tools.capabilities.bank.investments import is_isin_valid -from weboob.exceptions import BrowserPasswordExpired +from .compat.weboob_exceptions import BrowserPasswordExpired def float_to_decimal(f): return Decimal(str(f)) diff --git a/modules/cragr/compat/weboob_capabilities_bank.py b/modules/cragr/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cragr/compat/weboob_capabilities_bank.py +++ b/modules/cragr/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cragr/regions/browser.py b/modules/cragr/regions/browser.py index 3922d5d0563759aa59f89275e2f4b1ec57be5281..df99df1151d983f61360b18b7c86eb1694066801 100644 --- a/modules/cragr/regions/browser.py +++ b/modules/cragr/regions/browser.py @@ -26,7 +26,7 @@ from weboob.browser.browsers import LoginBrowser, URL, need_login from .compat.weboob_browser_url import BrowserParamURL from weboob.browser.exceptions import ServerError, BrowserHTTPNotFound -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded from weboob.tools.compat import urlparse from weboob.tools.capabilities.bank.transactions import sorted_transactions from weboob.tools.capabilities.bank.investments import create_french_liquidity @@ -338,8 +338,11 @@ def iter_perimeter_accounts(self, iban, all_accounts): self.accounts.stay_or_go() self.page.set_cragr_code() for account in self.page.iter_accounts(): + self.accounts.go() if iban and account._form: - account.iban = self.get_account_iban(account._form) + # Refresh account form in case it expired + refreshed_account = find_object(self.page.iter_accounts(), id=account.id) + account.iban = self.get_account_iban(refreshed_account._form) if account.id not in [a.id for a in cragr_accounts]: cragr_accounts.append(account) @@ -619,7 +622,13 @@ def iter_history(self, account, coming=False): ): self.unhandled_method(account.id) - date_guesser = LinearDateGuesser(date_max_bump=timedelta(30)) + class NoCopyLinearDateGuesser(LinearDateGuesser): + # params passed to a @method are deepcopied, in each iteration of ItemElement + # so we want to avoid repeatedly copying objects since we wan't to keep using the same object + def __deepcopy__(self, memo): + return self + + date_guesser = NoCopyLinearDateGuesser(date_max_bump=timedelta(30)) for tr in self.page.iter_history(date_guesser=date_guesser): yield tr diff --git a/modules/cragr/regions/compat/weboob_capabilities_bank.py b/modules/cragr/regions/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/cragr/regions/compat/weboob_capabilities_bank.py +++ b/modules/cragr/regions/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/cragr/regions/compat/weboob_exceptions.py b/modules/cragr/regions/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/cragr/regions/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/cragr/regions/pages.py b/modules/cragr/regions/pages.py index ceea65a5d05475a75f49243cee518b105e4becef..4e84176595677e20504c7607aa099daa9bc5a0c3 100644 --- a/modules/cragr/regions/pages.py +++ b/modules/cragr/regions/pages.py @@ -24,7 +24,7 @@ from decimal import Decimal import re -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded, BrowserPasswordExpired +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded, BrowserPasswordExpired from weboob.browser.pages import HTMLPage, LoggedPage, JsonPage, FormNotFound, pagination from weboob.browser.elements import ListElement, TableElement, DictElement, ItemElement, method @@ -212,9 +212,11 @@ def get_iban(self): 'LDD': Account.TYPE_SAVINGS, 'PEL': Account.TYPE_SAVINGS, 'CEL': Account.TYPE_SAVINGS, + 'CEL2': Account.TYPE_SAVINGS, 'CODEBIS': Account.TYPE_SAVINGS, 'LJMO': Account.TYPE_SAVINGS, 'CSL': Account.TYPE_SAVINGS, + 'CSLB5': Account.TYPE_SAVINGS, 'LEP': Account.TYPE_SAVINGS, 'LEF': Account.TYPE_SAVINGS, 'TIWI': Account.TYPE_SAVINGS, diff --git a/modules/cragr/web/browser.py b/modules/cragr/web/browser.py index c71eeeae7705a3c59240394bcc2b93f09a2637ef..9b884a324564a08dad17e78c91527603e9863ee8 100644 --- a/modules/cragr/web/browser.py +++ b/modules/cragr/web/browser.py @@ -32,9 +32,9 @@ from weboob.capabilities.profile import ProfileMissing from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin from weboob.browser.pages import FormNotFound -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable from weboob.tools.date import ChaoticDateGuesser, LinearDateGuesser -from weboob.exceptions import BrowserHTTPError, ActionNeeded +from .compat.weboob_exceptions import BrowserHTTPError, ActionNeeded from .compat.weboob_browser_filters_standard import CleanText from weboob.tools.value import Value from weboob.tools.compat import urlparse, urljoin, basestring diff --git a/modules/cragr/web/compat/__init__.py b/modules/cragr/web/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/cragr/web/pages.py b/modules/cragr/web/pages.py index 4c00b4817af0f3d94e025e7ca3b760aec4f5774e..28a2831ba9f903e05572c536cb78064f45553da6 100644 --- a/modules/cragr/web/pages.py +++ b/modules/cragr/web/pages.py @@ -33,7 +33,7 @@ ) from weboob.capabilities.contact import Advisor from weboob.capabilities.profile import Profile -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable, ActionNeeded +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable, ActionNeeded from weboob.tools.capabilities.bank.transactions import FrenchTransaction as Transaction from weboob.tools.date import parse_french_date, LinearDateGuesser from weboob.tools.compat import urlparse, urljoin, unicode diff --git a/modules/creditcooperatif/caisseepargne_browser.py b/modules/creditcooperatif/caisseepargne_browser.py index 61fbdb04e4bd4d31c8bcc97f0b48f08cb03e363e..239f9f7c684d9e323881bf93b87d275ed7267777 100644 --- a/modules/creditcooperatif/caisseepargne_browser.py +++ b/modules/creditcooperatif/caisseepargne_browser.py @@ -17,8 +17,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . -from weboob.browser import AbstractBrowser +from weboob.browser import AbstractBrowser, URL + from .linebourse_browser import LinebourseAPIBrowser +from .caisseepargne_pages import SmsPage, SmsPageOption __all__ = ['CaisseEpargneBrowser'] @@ -30,6 +32,15 @@ class CaisseEpargneBrowser(AbstractBrowser): LINEBOURSE_BROWSER = LinebourseAPIBrowser + sms = URL( + r'https://www.icgauth.credit-cooperatif.coop/dacswebssoissuer/AuthnRequestServlet', + SmsPage, + ) + sms_option = URL( + r'https://www.icgauth.credit-cooperatif.coop/dacstemplate-SOL/_(?P\d+)/index.html\?transactionID=.*', + SmsPageOption, + ) + def __init__(self, nuser, *args, **kwargs): kwargs['market_url'] = 'https://www.offrebourse.com' super(CaisseEpargneBrowser, self).__init__(nuser, *args, **kwargs) diff --git a/modules/creditcooperatif/caisseepargne_pages.py b/modules/creditcooperatif/caisseepargne_pages.py new file mode 100644 index 0000000000000000000000000000000000000000..266187cb8a3d82f33654b2f868df96a6778373fc --- /dev/null +++ b/modules/creditcooperatif/caisseepargne_pages.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2012 Budget Insight +# +# This file is part of a weboob module. +# +# This weboob module is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This weboob module is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this weboob module. If not, see . + +from weboob.browser.pages import AbstractPage + + +class SmsPage(AbstractPage): + PARENT = 'caissedepargne' + BROWSER_ATTR = 'package.browser.CaisseEpargne' + PARENT_URL = 'sms' + + +class SmsPageOption(AbstractPage): + PARENT = 'caissedepargne' + BROWSER_ATTR = 'package.browser.CaisseEpargne' + PARENT_URL = 'sms_option' diff --git a/modules/creditcooperatif/compat/weboob_capabilities_bank.py b/modules/creditcooperatif/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/creditcooperatif/compat/weboob_capabilities_bank.py +++ b/modules/creditcooperatif/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/creditdunord/browser.py b/modules/creditdunord/browser.py index 0cff12388e1ff6964d1ade9e791bfbfdcf5c05be..d1951bb53b139314b4565ad86edca025426e7bb4 100644 --- a/modules/creditdunord/browser.py +++ b/modules/creditdunord/browser.py @@ -20,7 +20,7 @@ from __future__ import unicode_literals from weboob.browser.browsers import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword, BrowserPasswordExpired, ActionNeeded, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserPasswordExpired, ActionNeeded, BrowserUnavailable from .compat.weboob_capabilities_bank import Account from weboob.capabilities.base import find_object from weboob.tools.capabilities.bank.investments import create_french_liquidity @@ -30,6 +30,7 @@ ProTransactionsPage, LabelsPage, RgpdPage, ) + class CreditDuNordBrowser(LoginBrowser): ENCODING = 'UTF-8' BASEURL = "https://www.credit-du-nord.fr/" @@ -41,9 +42,13 @@ class CreditDuNordBrowser(LoginBrowser): redirect = URL('/swm/redirectCDN.html', RedirectPage) entrypage = URL('/icd/zco/#zco', EntryPage) multitype_av = URL('/vos-comptes/IPT/appmanager/transac/professionnels\?_nfpb=true&_eventName=onRestart&_pageLabel=synthese_contrats_assurance_vie', AVPage) - loans = URL('/vos-comptes/IPT/appmanager/transac/(?P.*)\?_nfpb=true&_eventName=onRestart&_pageLabel=(?P(creditPersoImmobilier|credit__en_cours|credit_en_cours))', ProAccountsPage) - proaccounts = URL('/vos-comptes/IPT/appmanager/transac/(professionnels|entreprises)\?_nfpb=true&_eventName=onRestart&_pageLabel=(?P(transac_tableau_de_bord|page__synthese_v1|page_synthese_v1))', ProAccountsPage) - accounts = URL('/vos-comptes/IPT/appmanager/transac/(?P.*)\?_nfpb=true&_eventName=onRestart&_pageLabel=(?P(transac_tableau_de_bord|page__synthese_v1|page_synthese_v1))', AccountsPage) + loans = URL(r'/vos-comptes/IPT/appmanager/transac/(?P.*)\?_nfpb=true&_eventName=onRestart&_pageLabel=(?P(creditPersoImmobilier|credit_?_en_cours))', ProAccountsPage) + proaccounts = URL(r'/vos-comptes/IPT/appmanager/transac/(professionnels|entreprises)\?_nfpb=true&_eventName=onRestart&_pageLabel=(?P(transac_tableau_de_bord|page_?_synthese_v1))', + r'/vos-comptes/(professionnels|entreprises)/page_?_synthese', + ProAccountsPage) + accounts = URL(r'/vos-comptes/IPT/appmanager/transac/(?P.*)\?_nfpb=true&_eventName=onRestart&_pageLabel=(?P(transac_tableau_de_bord|page_?_synthese_v1))', + r'/vos-comptes/particuliers', + AccountsPage) multitype_iban = URL('/vos-comptes/IPT/appmanager/transac/professionnels\?_nfpb=true&_eventName=onRestart&_pageLabel=impression_rib', ProIbanPage) transactions = URL('/vos-comptes/IPT/appmanager/transac/particuliers\?_nfpb=true(.*)', TransactionsPage) protransactions = URL('/vos-comptes/(.*)/transac/(professionnels|entreprises)', ProTransactionsPage) diff --git a/modules/creditdunord/compat/weboob_capabilities_bank.py b/modules/creditdunord/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/creditdunord/compat/weboob_capabilities_bank.py +++ b/modules/creditdunord/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/creditdunord/compat/weboob_exceptions.py b/modules/creditdunord/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/creditdunord/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/creditdunord/pages.py b/modules/creditdunord/pages.py index d61698cf7d1bd21810ea08b6e405c76fd9c545be..a55ddf95cf01825d6cde915b096afbb76cbcb6fd 100755 --- a/modules/creditdunord/pages.py +++ b/modules/creditdunord/pages.py @@ -32,7 +32,7 @@ from .compat.weboob_browser_filters_standard import CleanText, Date, CleanDecimal, Regexp, Format, Field, Eval, Lower from weboob.browser.filters.json import Dict from weboob.browser.filters.html import Attr, TableCell -from weboob.exceptions import ActionNeeded, BrowserIncorrectPassword, BrowserUnavailable, BrowserPasswordExpired +from .compat.weboob_exceptions import ActionNeeded, BrowserIncorrectPassword, BrowserUnavailable, BrowserPasswordExpired from .compat.weboob_capabilities_bank import Account, Investment from weboob.capabilities.profile import Profile from weboob.capabilities.base import Currency, find_object @@ -143,7 +143,7 @@ def vk_login(self, username, password): self.browser.location('/swm/redirectCDN.html', data=data) def classic_login(self, username, password): - m = re.match('www.([^\.]+).fr', self.browser.BASEURL) + m = re.match('https://www.([^\.]+).fr', self.browser.BASEURL) if not m: bank_name = 'credit-du-nord' self.logger.error('Unable to find bank name for %s' % self.browser.BASEURL) diff --git a/modules/creditdunordpee/compat/weboob_capabilities_bank.py b/modules/creditdunordpee/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/creditdunordpee/compat/weboob_capabilities_bank.py +++ b/modules/creditdunordpee/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/creditdunordpee/pages.py b/modules/creditdunordpee/pages.py index 2787d427899b6eaa1730ce8cdb70dee4ae4208f7..9dc398a5fdbb17bf3edeba527f57c872e7d43634 100644 --- a/modules/creditdunordpee/pages.py +++ b/modules/creditdunordpee/pages.py @@ -29,7 +29,7 @@ from weboob.browser.filters.html import CleanHTML, TableCell from .compat.weboob_capabilities_bank import Account, Transaction, Investment from weboob.capabilities.base import NotAvailable -from weboob.exceptions import NoAccountsException +from .compat.weboob_exceptions import NoAccountsException class VirtKeyboard(MappedVirtKeyboard): diff --git a/modules/creditmutuel/browser.py b/modules/creditmutuel/browser.py index f06fb5534c11d96ba761616dc9eb599e8102f832..a8bf97d85c6a8f374f9b73a85941380423cbc71c 100644 --- a/modules/creditmutuel/browser.py +++ b/modules/creditmutuel/browser.py @@ -32,7 +32,7 @@ from .compat.weboob_browser_url import URL from weboob.browser.pages import FormNotFound from weboob.browser.exceptions import ClientError, ServerError -from weboob.exceptions import BrowserIncorrectPassword, AuthMethodNotImplemented, BrowserUnavailable, NoAccountsException +from .compat.weboob_exceptions import BrowserIncorrectPassword, AuthMethodNotImplemented, BrowserUnavailable, NoAccountsException from .compat.weboob_capabilities_bank import Account, AddRecipientStep, Recipient, AccountOwnership from weboob.tools.capabilities.bank.investments import create_french_liquidity from weboob.capabilities import NotAvailable diff --git a/modules/creditmutuel/compat/weboob_capabilities_bank.py b/modules/creditmutuel/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/creditmutuel/compat/weboob_capabilities_bank.py +++ b/modules/creditmutuel/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/creditmutuel/compat/weboob_exceptions.py b/modules/creditmutuel/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/creditmutuel/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/creditmutuel/pages.py b/modules/creditmutuel/pages.py index 5c116cc49119b7db7447a88cc30b3234d43eb686..8e6deebe06eaa102773bdad3058be87d1d5b1d8c 100644 --- a/modules/creditmutuel/pages.py +++ b/modules/creditmutuel/pages.py @@ -34,7 +34,7 @@ Filter, Env, CleanText, CleanDecimal, Field, Regexp, Async, AsyncLoad, Date, Format, Type, Currency, ) from weboob.browser.filters.html import Link, Attr, TableCell, ColumnNotFound -from weboob.exceptions import ( +from .compat.weboob_exceptions import ( BrowserIncorrectPassword, ParseError, ActionNeeded, BrowserUnavailable, AuthMethodNotImplemented, AppValidation, ) @@ -116,7 +116,7 @@ def on_load(self): self.logger.warning('This connexion cannot bypass mobile confirmation') msg = CleanText('//div[@id="inMobileAppMessage"]')(self.doc) if msg: - display_msg = re.search(r'Confirmer votre connexion depuis votre appareil "[\w ]+"', msg).group() + display_msg = re.search(r'Confirmer votre connexion depuis votre appareil ".+"', msg).group() raise AppValidation(display_msg) assert False, "Mobile authentication method not handled" @@ -137,9 +137,6 @@ class UserSpacePage(LoggedPage, HTMLPage): def on_load(self): if self.doc.xpath('//form[@id="GoValider"]'): raise ActionNeeded("Le site du contrat Banque à Distance a besoin d'informations supplémentaires") - elif 'Afin de compléter vos informations personnelles, renseignez le formulaire ci-dessous' in self.doc.xpath('//form[@class="_devb_act ___Form"]//div[contains(@class, "bloctxt")]/p[1]/text()')[0]: - raise ActionNeeded("Le site nécessite la saisie des informations personnelles de l'utilisateur.") - super(UserSpacePage, self).on_load() @@ -152,57 +149,59 @@ class item_account_generic(ItemElement): klass = Account TYPES = OrderedDict([ - ('Credits Promoteurs', Account.TYPE_CHECKING), # it doesn't fit loan's model - ('Compte Cheque', Account.TYPE_CHECKING), - ('Compte Courant', Account.TYPE_CHECKING), - ('Cpte Courant', Account.TYPE_CHECKING), - ('Contrat Personnel', Account.TYPE_CHECKING), - ('Cc Contrat Personnel', Account.TYPE_CHECKING), - ('C/C', Account.TYPE_CHECKING), - ('Start', Account.TYPE_CHECKING), - ('Comptes courants', Account.TYPE_CHECKING), - ('Service Accueil', Account.TYPE_CHECKING), - ('Eurocompte Serenite', Account.TYPE_CHECKING), - ('Eurocompte Confort', Account.TYPE_CHECKING), - ('Catip', Account.TYPE_DEPOSIT), - ('Cic Immo', Account.TYPE_MORTGAGE), - ('Credit', Account.TYPE_LOAN), - ('Crédits', Account.TYPE_LOAN), - ('Eco-Prêt', Account.TYPE_LOAN), - ('Mcne', Account.TYPE_LOAN), - ('Nouveau Prêt', Account.TYPE_LOAN), - ('Pret', Account.TYPE_LOAN), - ('Regroupement De Credits', Account.TYPE_LOAN), - ('Nouveau Pret 0%', Account.TYPE_LOAN), - ('Global Auto', Account.TYPE_LOAN), - ('Passeport Credit', Account.TYPE_REVOLVING_CREDIT), - ('Allure', Account.TYPE_REVOLVING_CREDIT), # 'Allure Libre' or 'credit Allure' - ('Preference', Account.TYPE_REVOLVING_CREDIT), - ('Plan 4', Account.TYPE_REVOLVING_CREDIT), - ('P.E.A', Account.TYPE_PEA), - ('Pea', Account.TYPE_PEA), - ('Compte De Liquidite Pea', Account.TYPE_PEA), - ('Compte Epargne', Account.TYPE_SAVINGS), - ('Etalis', Account.TYPE_SAVINGS), - ('Ldd', Account.TYPE_SAVINGS), - ('Livret', Account.TYPE_SAVINGS), - ("Plan D'Epargne", Account.TYPE_SAVINGS), - ('Tonic Croissance', Account.TYPE_SAVINGS), - ('Tonic Societaire', Account.TYPE_SAVINGS), - ('Capital Expansion', Account.TYPE_SAVINGS), - ('Épargne', Account.TYPE_SAVINGS), - ('Capital Plus', Account.TYPE_SAVINGS), - ('Pep', Account.TYPE_SAVINGS), - ('Compte Duo', Account.TYPE_SAVINGS), - ('Compte Garantie Titres', Account.TYPE_MARKET), + (re.compile(r'Credits Promoteurs'), Account.TYPE_CHECKING), # it doesn't fit loan's model + (re.compile(r'Compte Cheque'), Account.TYPE_CHECKING), + (re.compile(r'Compte Courant'), Account.TYPE_CHECKING), + (re.compile(r'Cpte Courant'), Account.TYPE_CHECKING), + (re.compile(r'Contrat Personnel'), Account.TYPE_CHECKING), + (re.compile(r'Cc Contrat Personnel'), Account.TYPE_CHECKING), + (re.compile(r'C/C'), Account.TYPE_CHECKING), + (re.compile(r'Start\b'), Account.TYPE_CHECKING), + (re.compile(r'Comptes courants'), Account.TYPE_CHECKING), + (re.compile(r'Service Accueil'), Account.TYPE_CHECKING), + (re.compile(r'Eurocompte Serenite'), Account.TYPE_CHECKING), + (re.compile(r'Eurocompte Confort'), Account.TYPE_CHECKING), + (re.compile(r'Compte Service Bancaire De Base'), Account.TYPE_CHECKING), + (re.compile(r'Catip\b'), Account.TYPE_DEPOSIT), + (re.compile(r'Cic Immo'), Account.TYPE_MORTGAGE), + (re.compile(r'Credit'), Account.TYPE_LOAN), + (re.compile(r'Crédits'), Account.TYPE_LOAN), + (re.compile(r'Eco-Prêt'), Account.TYPE_LOAN), + (re.compile(r'Mcne'), Account.TYPE_LOAN), + (re.compile(r'Nouveau Prêt'), Account.TYPE_LOAN), + (re.compile(r'Pret\b'), Account.TYPE_LOAN), + (re.compile(r'Regroupement De Credits'), Account.TYPE_LOAN), + (re.compile(r'Nouveau Pret 0%'), Account.TYPE_LOAN), + (re.compile(r'Global Auto'), Account.TYPE_LOAN), + (re.compile(r'Passeport Credit'), Account.TYPE_REVOLVING_CREDIT), + (re.compile(r'Allure\b'), Account.TYPE_REVOLVING_CREDIT), # 'Allure Libre' or 'credit Allure' + (re.compile(r'Preference'), Account.TYPE_REVOLVING_CREDIT), + (re.compile(r'Plan 4'), Account.TYPE_REVOLVING_CREDIT), + (re.compile(r'P.E.A'), Account.TYPE_PEA), + (re.compile(r'Pea\b'), Account.TYPE_PEA), + (re.compile(r'Compte De Liquidite Pea'), Account.TYPE_PEA), + (re.compile(r'Compte Epargne'), Account.TYPE_SAVINGS), + (re.compile(r'Etalis'), Account.TYPE_SAVINGS), + (re.compile(r'Ldd'), Account.TYPE_SAVINGS), + (re.compile(r'Livret'), Account.TYPE_SAVINGS), + (re.compile(r"Plan D'Epargne"), Account.TYPE_SAVINGS), + (re.compile(r'Tonic Croissance'), Account.TYPE_SAVINGS), + (re.compile(r'Tonic Societaire'), Account.TYPE_SAVINGS), + (re.compile(r'Capital Expansion'), Account.TYPE_SAVINGS), + (re.compile(r'Épargne'), Account.TYPE_SAVINGS), + (re.compile(r'Capital Plus'), Account.TYPE_SAVINGS), + (re.compile(r'Pep\b'), Account.TYPE_SAVINGS), + (re.compile(r'Compte Duo'), Account.TYPE_SAVINGS), + (re.compile(r'Compte Garantie Titres'), Account.TYPE_MARKET), + (re.compile(r'Ppe'), Account.TYPE_LOAN), ]) - REVOLVING_LOAN_LABELS = [ - 'Passeport Credit', - 'Allure', - 'Preference', - 'Plan 4', - 'Credit En Reserve', + REVOLVING_LOAN_REGEXES = [ + re.compile(r'Passeport Credit'), + re.compile(r'Allure'), + re.compile(r'Preference'), + re.compile(r'Plan 4'), + re.compile(r'Credit En Reserve'), ] def condition(self): @@ -215,14 +214,38 @@ def condition(self): and (first_td.find('a') is not None or (first_td.find('.//span') is not None and "cartes" in first_td.findtext('.//span') and first_td.find('./div/a') is not None))) + def loan_condition(self, check_no_details=False): + _type = Field('type')(self) + label = Field('label')(self) + details_link = Link('.//a', default=None)(self) + + # mobile accounts are leading to a 404 error when parsing history + # furthermore this is not exactly a loan account + if re.search(r'Le Mobile +([0-9]{2} ?){5}', label): + return False + + if ( + details_link and + item_account_generic.condition and + _type in (Account.TYPE_LOAN, Account.TYPE_MORTGAGE) and + not self.is_revolving(label) + ): + details = self.page.browser.open(details_link).page + if details and 'cloturé' not in CleanText('//form[@id="P:F"]//div[@class="blocmsg info"]//p')(details.doc): + fiche_details = CleanText('//table[@class="fiche"]')(details.doc) + if check_no_details: # check_no_details is used to determine if condition should check the absence of details, otherwise we still check the presence of details + return not fiche_details + return fiche_details + return False + class Label(Filter): def filter(self, text): return text.lstrip(' 0123456789').title() class Type(Filter): def filter(self, label): - for pattern, actype in item_account_generic.TYPES.items(): - if pattern in label: + for regex, actype in item_account_generic.TYPES.items(): + if regex.search(label): return actype return Account.TYPE_UNKNOWN @@ -390,8 +413,8 @@ def parse(self, el): self.env['coming'] = coming or NotAvailable def is_revolving(self, label): - return (any(revolving_loan_label in label - for revolving_loan_label in item_account_generic.REVOLVING_LOAN_LABELS) + return (any(revolving_loan_regex.search(label) + for revolving_loan_regex in item_account_generic.REVOLVING_LOAN_REGEXES) or label.lower() in self.page.browser.revolving_accounts) @@ -411,11 +434,22 @@ def condition(self): return False return item_account_generic.condition(self) and _type not in (Account.TYPE_LOAN, Account.TYPE_MORTGAGE) + class item_loan_low_details(item_account_generic): + klass = Loan + + def condition(self): + return item_account_generic.loan_condition(self, check_no_details=True) + + obj__parent_id = NotAvailable + class item_loan(item_account_generic): klass = Loan load_details = Link('.//a') & AsyncLoad + def condition(self): + return item_account_generic.loan_condition(self) + obj_total_amount = Async('details') & MyDecimal('//div[@id="F4:expContent"]/table/tbody/tr[1]/td[1]/text()') obj_rate = Async('details') & MyDecimal('//div[@id="F4:expContent"]/table/tbody/tr[2]/td[1]') obj_nb_payments_left = Async('details') & Type(CleanText( @@ -444,23 +478,6 @@ def obj__parent_id(self): return parent_id.replace(' ', '') return NotAvailable - def condition(self): - _type = Field('type')(self) - label = Field('label')(self) - details_link = Link('.//a', default=None)(self) - - # mobile accounts are leading to a 404 error when parsing history - # furthermore this is not exactly a loan account - if re.search(r'Le\sMobile\s+([0-9]{2}\s?){5}', label): - return False - - if (details_link and item_account_generic.condition and _type in (Account.TYPE_LOAN, Account.TYPE_MORTGAGE) - and not self.is_revolving(label)): - details = self.page.browser.open(details_link) - if details.page and not 'cloturé' in CleanText('//form[@id="P:F"]//div[@class="blocmsg info"]//p')(details.page.doc): - return True - return False - class item_revolving_loan(item_account_generic): klass = Loan diff --git a/modules/dailymotion/compat/weboob_exceptions.py b/modules/dailymotion/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/dailymotion/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/dailymotion/pages.py b/modules/dailymotion/pages.py index d131dfc4ea5c79944bbc7c5460b69aa3fc2a1cf7..ab9c860df6c856507d4f73fb87115a61c05214ad 100644 --- a/modules/dailymotion/pages.py +++ b/modules/dailymotion/pages.py @@ -26,7 +26,7 @@ from weboob.capabilities.video import BaseVideo from weboob.capabilities.image import Thumbnail -from weboob.exceptions import ParseError +from .compat.weboob_exceptions import ParseError from weboob.tools.json import json from datetime import timedelta diff --git a/modules/delubac/compat/weboob_capabilities_bank.py b/modules/delubac/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/delubac/compat/weboob_capabilities_bank.py +++ b/modules/delubac/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/delubac/compat/weboob_exceptions.py b/modules/delubac/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/delubac/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/delubac/pages.py b/modules/delubac/pages.py index ea7702742eba9e20f217f400ab8829f4fe7f445d..e2669b2f22e589840504cb04b8d0c3c188156949 100644 --- a/modules/delubac/pages.py +++ b/modules/delubac/pages.py @@ -21,7 +21,7 @@ from io import BytesIO from weboob.browser.pages import HTMLPage, LoggedPage -from weboob.exceptions import ParseError, ActionNeeded +from .compat.weboob_exceptions import ParseError, ActionNeeded from .compat.weboob_tools_captcha_virtkeyboard import GridVirtKeyboard from .compat.weboob_browser_filters_standard import CleanText diff --git a/modules/dlfp/browser.py b/modules/dlfp/browser.py index f71ae6c033c85dd64b9035cef35098ff91421b9e..6384110df2c158ed3c0e3ccc3b22abca4b15d81a 100644 --- a/modules/dlfp/browser.py +++ b/modules/dlfp/browser.py @@ -23,9 +23,9 @@ from requests.exceptions import HTTPError -from weboob.browser import LoginBrowser, need_login, URL +from weboob.browser.browsers import LoginBrowser, need_login, URL from weboob.browser.exceptions import HTTPNotFound -from weboob.exceptions import BrowserIncorrectPassword, ParseError +from .compat.weboob_exceptions import BrowserIncorrectPassword, ParseError from weboob.capabilities.messages import CantSendMessage from .pages.index import IndexPage, LoginPage diff --git a/modules/dlfp/compat/__init__.py b/modules/dlfp/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/dlfp/compat/weboob_exceptions.py b/modules/dlfp/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/dlfp/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/dlfp/module.py b/modules/dlfp/module.py index 3c9cca8e0ca10ce7f690f7856a32a8941c64c371..c86144c837a5befb832e351adc666094f7cba689 100644 --- a/modules/dlfp/module.py +++ b/modules/dlfp/module.py @@ -22,7 +22,7 @@ import time from weboob.tools.backend import Module, BackendConfig -from weboob.exceptions import BrowserForbidden +from .compat.weboob_exceptions import BrowserForbidden from weboob.tools.newsfeed import Newsfeed from weboob.tools.value import Value, ValueBool, ValueBackendPassword from weboob.capabilities.messages import CapMessages, CapMessagesPost, Message, Thread, CantSendMessage diff --git a/modules/edf/par/browser.py b/modules/edf/par/browser.py index f62d3f397a5fb8af77cb519de091aa35821b4762..05341a9886c942f4cecc7a44a9fdc5a20d08ebd4 100644 --- a/modules/edf/par/browser.py +++ b/modules/edf/par/browser.py @@ -20,8 +20,8 @@ from time import time -from weboob.browser import LoginBrowser, URL, need_login, StatesMixin -from weboob.exceptions import BrowserIncorrectPassword, BrowserQuestion +from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserQuestion from weboob.tools.decorators import retry from weboob.tools.json import json from weboob.tools.value import Value diff --git a/modules/edf/par/compat/weboob_exceptions.py b/modules/edf/par/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/edf/par/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/edf/pro/browser.py b/modules/edf/pro/browser.py index 1590b3c91b39dcc0b9bc8d44c48018a04931c21c..2b6d25d1f823c25f1c3f1ad9299d3d3ef3af2778 100644 --- a/modules/edf/pro/browser.py +++ b/modules/edf/pro/browser.py @@ -21,9 +21,9 @@ from datetime import datetime, timedelta -from weboob.browser import LoginBrowser, URL, need_login +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.capabilities.base import NotAvailable -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded, BrowserUnavailable from weboob.browser.exceptions import ServerError, ClientError from .pages import ( diff --git a/modules/edf/pro/compat/weboob_exceptions.py b/modules/edf/pro/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/edf/pro/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/edf/pro/pages.py b/modules/edf/pro/pages.py index ce99a68967e45ea07257de0370a7cd2fe3e78873..f4a475bdbfcdd24f2a1018bfe75de872194e52a8 100644 --- a/modules/edf/pro/pages.py +++ b/modules/edf/pro/pages.py @@ -26,7 +26,7 @@ from .compat.weboob_browser_filters_standard import CleanDecimal, CleanText from weboob.browser.filters.json import Dict from weboob.capabilities.bill import DocumentTypes, Subscription, Bill -from weboob.exceptions import ActionNeeded +from .compat.weboob_exceptions import ActionNeeded from weboob.capabilities.profile import Profile diff --git a/modules/ekwateur/browser.py b/modules/ekwateur/browser.py index 21b021472a9582469d35f0d7b91dc807c4e3cdee..157ac2a8d3b545e252ef1ffd911a1b249433b54b 100644 --- a/modules/ekwateur/browser.py +++ b/modules/ekwateur/browser.py @@ -22,8 +22,8 @@ import itertools -from weboob.browser import LoginBrowser, need_login, URL -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, need_login, URL +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import BillsPage, DocumentsPage, LoginPage diff --git a/modules/ekwateur/compat/weboob_exceptions.py b/modules/ekwateur/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ekwateur/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ensap/browser.py b/modules/ensap/browser.py index 2631ad0f6c953d2aecd028cc9a8aaca4b8406007..3e9dc8557493e2fd4238d206619c5003a696b314 100644 --- a/modules/ensap/browser.py +++ b/modules/ensap/browser.py @@ -20,9 +20,9 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, need_login, URL +from weboob.browser.browsers import LoginBrowser, need_login, URL from weboob.browser.profiles import Firefox -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.capabilities.base import find_object from weboob.capabilities.bill import DocumentNotFound from .pages import LoginPage, DocumentsPage, HomePage, LoginControlPage,\ diff --git a/modules/ensap/compat/weboob_exceptions.py b/modules/ensap/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ensap/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/erehsbc/compat/weboob_capabilities_bank.py b/modules/erehsbc/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/erehsbc/compat/weboob_capabilities_bank.py +++ b/modules/erehsbc/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/esalia/compat/weboob_capabilities_bank.py b/modules/esalia/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/esalia/compat/weboob_capabilities_bank.py +++ b/modules/esalia/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/feedly/compat/weboob_exceptions.py b/modules/feedly/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/feedly/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/feedly/google.py b/modules/feedly/google.py index 1360d136579eabf53f6052138b11a19e4a021b5d..ac862611f4dc5f4c61e1241b0deaf5a32f97c111 100644 --- a/modules/feedly/google.py +++ b/modules/feedly/google.py @@ -18,9 +18,9 @@ # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL +from weboob.browser.browsers import LoginBrowser, URL from weboob.browser.pages import HTMLPage, LoggedPage -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.compat import urlparse, parse_qs diff --git a/modules/figgo/browser.py b/modules/figgo/browser.py index 2199b81bd2ae48267b6ee0ac7b32adc29041607e..fa89394f0d864f48fd9fa82b3f41ccf1d1a54a3a 100644 --- a/modules/figgo/browser.py +++ b/modules/figgo/browser.py @@ -21,9 +21,9 @@ from datetime import timedelta -from weboob.browser import LoginBrowser, need_login, URL +from weboob.browser.browsers import LoginBrowser, need_login, URL from weboob.browser.exceptions import ClientError -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.date import new_datetime from .pages import LoginPage, CalendarPage, HomePage, UsersPage diff --git a/modules/figgo/compat/__init__.py b/modules/figgo/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/fortuneo/browser.py b/modules/fortuneo/browser.py index 66040ffc5699d9e7336786ffac42aea60ec644bc..61a270552e64b2a6bc13b611a5f94d79701dbf3c 100644 --- a/modules/fortuneo/browser.py +++ b/modules/fortuneo/browser.py @@ -25,7 +25,7 @@ from datetime import datetime, timedelta from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin -from weboob.exceptions import AuthMethodNotImplemented, BrowserIncorrectPassword, ActionNeeded +from .compat.weboob_exceptions import AuthMethodNotImplemented, BrowserIncorrectPassword, ActionNeeded from .compat.weboob_capabilities_bank import Account, AddRecipientStep, Recipient from weboob.tools.capabilities.bank.transactions import sorted_transactions from weboob.tools.value import Value diff --git a/modules/fortuneo/compat/weboob_capabilities_bank.py b/modules/fortuneo/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/fortuneo/compat/weboob_capabilities_bank.py +++ b/modules/fortuneo/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/fortuneo/compat/weboob_exceptions.py b/modules/fortuneo/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/fortuneo/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/fortuneo/pages/accounts_list.py b/modules/fortuneo/pages/accounts_list.py index 74a6c541b269e199126eeb810bd52705f2c48442..271740ff1f41d2c5d0e98bf2d47b2ed8bb3de775 100644 --- a/modules/fortuneo/pages/accounts_list.py +++ b/modules/fortuneo/pages/accounts_list.py @@ -31,14 +31,14 @@ from weboob.browser.filters.html import Link, Attr from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, RawText, Regexp, Date from weboob.capabilities import NotAvailable -from .compat.weboob_capabilities_bank import Account, Investment, Loan +from .compat.weboob_capabilities_bank import Account, Investment, Loan, AccountOwnership from weboob.capabilities.profile import Person from weboob.browser.pages import HTMLPage, LoggedPage, FormNotFound, CsvPage from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.tools.capabilities.bank.investments import create_french_liquidity from weboob.tools.json import json from weboob.tools.date import parse_french_date -from weboob.exceptions import ActionNeeded, BrowserUnavailable +from .compat.weboob_exceptions import ActionNeeded, BrowserUnavailable from weboob.tools.capabilities.bank.investments import is_isin_valid @@ -71,6 +71,15 @@ class PeaHistoryPage(LoggedPage, HTMLPage): COL_PERF = 6 COL_WEIGHT = 7 + def on_load(self): + err_msgs = [ + "vos informations personnelles n'ayant pas été modifiées récemment, nous vous remercions de bien vouloir les compléter", + "nous vous remercions de mettre à jour et/ou de compléter vos informations personnelles", + ] + text = CleanText('//div[@class="block_cadre"]//div/p')(self.doc) + if any(err_msg in text for err_msg in err_msgs): + raise ActionNeeded(text) + def get_investments(self, account): if account is not None: # the balance is highly dynamic, fetch it along with the investments to grab a snapshot @@ -174,10 +183,10 @@ def get_investments(self, account): inv.label = CleanText(None).filter(cols[self.COL_LABEL]) inv.quantity = self.parse_decimal(cols[self.COL_QUANTITY], True) - inv.unitprice = self.parse_decimal(cols[self.COL_UNITPRICE], False) - inv.unitvalue = self.parse_decimal(cols[self.COL_UNITVALUE], False) + inv.unitprice = self.parse_decimal(cols[self.COL_UNITPRICE], True) + inv.unitvalue = self.parse_decimal(cols[self.COL_UNITVALUE], True) inv.vdate = Date(CleanText(cols[self.COL_DATE], default=NotAvailable), dayfirst=True, default=NotAvailable)(self.doc) - inv.valuation = self.parse_decimal(cols[self.COL_VALUATION], False) + inv.valuation = self.parse_decimal(cols[self.COL_VALUATION], True) inv.diff = self.parse_decimal(cols[self.COL_PERF], True) diff_percent = self.parse_decimal(cols[self.COL_PERF_PERCENT], True) inv.diff_ratio = diff_percent / 100 if diff_percent else NotAvailable @@ -470,6 +479,7 @@ def get_list(self): account.account_label = account_history_page.get_account_label() account.subscription_date = account_history_page.get_subscription_date() account.maturity_date = account_history_page.get_maturity_date() + account.ownership = account_history_page.get_owner() if len(accounts) == 0: global_error_message = page.doc.xpath('//div[@id="as_renouvellementMIFID.do_"]/div[contains(text(), "Bonjour")] ' @@ -502,31 +512,41 @@ def get_list(self): break investment_page = None - if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET, Account.TYPE_LIFE_INSURANCE}: + if account.type in (Account.TYPE_PEA, Account.TYPE_MARKET, Account.TYPE_LIFE_INSURANCE): account._investment_link = Link('./ul/li/a[contains(@id, "portefeuille")]')(cpt) - investment_page = self.browser.open(account._investment_link).page + investment_page = self.browser.location(account._investment_link).page balance = investment_page.get_balance(account.type) - if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: + if account.type in (Account.TYPE_PEA, Account.TYPE_MARKET): self.browser.investments[account.id] = list(self.browser.open(account._investment_link).page.get_investments(account)) else: balance = page.get_balance() if account.type is not Account.TYPE_LOAN: account.coming = page.get_coming() - if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: + if account.type in (Account.TYPE_PEA, Account.TYPE_MARKET): account.currency = investment_page.get_currency() elif balance: account.currency = account.get_currency(balance) - if account.type == Account.TYPE_LIFE_INSURANCE: - # Life Insurance balance uses '.' instead of ',' - account.balance = CleanDecimal.SI().filter(balance) - else: - account.balance = CleanDecimal.French().filter(balance) + + account.balance = CleanDecimal.French().filter(balance) if account.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS): # Need a token sent by SMS to customers account.iban = NotAvailable + if account.type is not Account.TYPE_LOAN: + regexp = re.search(r'(m\. |mme\. )(.+)', CleanText('//span[has-class("mon_espace_nom")]')(self.doc), re.IGNORECASE) + if regexp and len(regexp.groups()) == 2: + gender = regexp.group(1).replace('.', '').rstrip() + name = regexp.group(2) + label = account.label + if re.search(r'(m|mr|me|mme|mlle|mle|ml)\.? (.*)\bou (m|mr|me|mme|mlle|mle|ml)\b(.*)', label, re.IGNORECASE): + account.ownership = AccountOwnership.CO_OWNER + elif re.search(r'{} {}'.format(gender, name), label, re.IGNORECASE): + account.ownership = AccountOwnership.OWNER + else: + account.ownership = AccountOwnership.ATTORNEY + if (account.label, account.id, account.balance) not in [(a.label, a.id, a.balance) for a in accounts]: accounts.append(account) return accounts @@ -557,6 +577,11 @@ def get_subscription_date(self): def get_maturity_date(self): return Date(CleanText(u'//p[@id="c_dateFin"]//strong'), dayfirst=True)(self.doc) + def get_owner(self): + if bool(CleanText('//p[@id="c_emprunteurSecondaire"]')(self.doc)): + return AccountOwnership.CO_OWNER + return AccountOwnership.OWNER + class ProfilePage(LoggedPage, HTMLPage): def get_csv_link(self): @@ -599,4 +624,3 @@ def get_profile(self): class SecurityPage(LoggedPage, HTMLPage): pass - diff --git a/modules/fortuneo/pages/compat/weboob_capabilities_bank.py b/modules/fortuneo/pages/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/fortuneo/pages/compat/weboob_capabilities_bank.py +++ b/modules/fortuneo/pages/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/fortuneo/pages/compat/weboob_exceptions.py b/modules/fortuneo/pages/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/fortuneo/pages/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/fortuneo/pages/login.py b/modules/fortuneo/pages/login.py index e034cf49cb57fe1fd4672fa60615c34b7e771b32..01a551343e87e9937c27e3293b10053fa6731bad 100644 --- a/modules/fortuneo/pages/login.py +++ b/modules/fortuneo/pages/login.py @@ -20,7 +20,7 @@ from weboob.browser.pages import HTMLPage from .compat.weboob_browser_filters_standard import CleanText -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable class LoginPage(HTMLPage): diff --git a/modules/freemobile/browser.py b/modules/freemobile/browser.py index b1f9cd4f069b7c17fd3aeef3c2b45021d768d158..f4be6c71419731a22245189ee1a0ff3300aca67e 100644 --- a/modules/freemobile/browser.py +++ b/modules/freemobile/browser.py @@ -17,9 +17,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL, need_login +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.capabilities.messages import CantSendMessage -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.compat import basestring from .pages import HomePage, LoginPage, HistoryPage, DetailsPage, OptionsPage, ProfilePage diff --git a/modules/freemobile/compat/__init__.py b/modules/freemobile/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/freemobile/compat/weboob_exceptions.py b/modules/freemobile/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/freemobile/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/freemobile/pages/compat/weboob_exceptions.py b/modules/freemobile/pages/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/freemobile/pages/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/freemobile/pages/history.py b/modules/freemobile/pages/history.py index 6878af25ad93e27b7fa990b32b7b414a5cfc6c28..e44fd1b0a39a41e23537bb024f9d246f57216d44 100644 --- a/modules/freemobile/pages/history.py +++ b/modules/freemobile/pages/history.py @@ -29,7 +29,7 @@ from weboob.browser.filters.html import AbsoluteLink, Attr from weboob.capabilities.bill import DocumentTypes, Detail, Bill from weboob.capabilities.base import NotAvailable -from weboob.exceptions import ParseError +from .compat.weboob_exceptions import ParseError from weboob.tools.compat import unicode diff --git a/modules/funmooc/browser.py b/modules/funmooc/browser.py index 27867f782de31874102cb2fcc0b6cff165ec3554..ecf099b721bc7122d0735499914e6b10a813d941 100644 --- a/modules/funmooc/browser.py +++ b/modules/funmooc/browser.py @@ -19,9 +19,9 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.browser.exceptions import HTTPNotFound -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.capabilities.image import Thumbnail from .pages import PageLogin, PageDashboard, PageChapter, PageSection diff --git a/modules/funmooc/compat/weboob_exceptions.py b/modules/funmooc/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/funmooc/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ganassurances/compat/weboob_capabilities_bank.py b/modules/ganassurances/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/ganassurances/compat/weboob_capabilities_bank.py +++ b/modules/ganassurances/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/gmf/browser.py b/modules/gmf/browser.py index a2830bd929d4a3031ef6a880d4c8d11ddd42c41c..c727f3b41f042e9e7b539eff4c4f192ca7d32cb3 100644 --- a/modules/gmf/browser.py +++ b/modules/gmf/browser.py @@ -19,8 +19,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import ( LoginPage, HomePage, AccountsPage, TransactionsInvestmentsPage, AllTransactionsPage, diff --git a/modules/gmf/compat/weboob_capabilities_bank.py b/modules/gmf/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/gmf/compat/weboob_capabilities_bank.py +++ b/modules/gmf/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/gmf/compat/weboob_exceptions.py b/modules/gmf/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/gmf/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/gmf/pages.py b/modules/gmf/pages.py index 370d1fefc9711e44c1f25f650e9b9cb7bd942d8b..2de0174f130cf4b52fa06984df67e11c316ce208 100644 --- a/modules/gmf/pages.py +++ b/modules/gmf/pages.py @@ -34,7 +34,7 @@ from weboob.capabilities.base import NotAvailable from .compat.weboob_tools_captcha_virtkeyboard import SimpleVirtualKeyboard from weboob.tools.capabilities.bank.transactions import FrenchTransaction -from weboob.exceptions import ActionNeeded +from .compat.weboob_exceptions import ActionNeeded class Transaction(FrenchTransaction): diff --git a/modules/groupama/browser.py b/modules/groupama/browser.py index ffaeaf3df2ff6591ccb7411392f599e8da7a1542..786d72131596f67a624dab7a988001b1fcc2ada3 100644 --- a/modules/groupama/browser.py +++ b/modules/groupama/browser.py @@ -20,7 +20,7 @@ import re from weboob.browser.browsers import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .compat.weboob_capabilities_bank import Account from weboob.capabilities.base import empty diff --git a/modules/groupama/compat/weboob_capabilities_bank.py b/modules/groupama/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/groupama/compat/weboob_capabilities_bank.py +++ b/modules/groupama/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/groupama/compat/weboob_exceptions.py b/modules/groupama/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/groupama/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/groupamaes/compat/weboob_capabilities_bank.py b/modules/groupamaes/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/groupamaes/compat/weboob_capabilities_bank.py +++ b/modules/groupamaes/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/happn/browser.py b/modules/happn/browser.py index cf9c81ee80083b0c283571073c806de26a5c0779..43d183ccee48de8dab77ccdd9d74d12c43ba05cb 100644 --- a/modules/happn/browser.py +++ b/modules/happn/browser.py @@ -24,7 +24,7 @@ from weboob.browser.profiles import IPhone from weboob.browser.pages import HTMLPage from .compat.weboob_browser_filters_standard import CleanText -from weboob.exceptions import BrowserIncorrectPassword, ParseError +from .compat.weboob_exceptions import BrowserIncorrectPassword, ParseError from weboob.tools.json import json diff --git a/modules/happn/compat/weboob_exceptions.py b/modules/happn/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/happn/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/happn/module.py b/modules/happn/module.py index 918ff9f391340219ddffb30a25aaaca58bf0c2d9..873d29d663639027bcdf0d7d16132c40b12ff636 100644 --- a/modules/happn/module.py +++ b/modules/happn/module.py @@ -31,7 +31,7 @@ from weboob.capabilities.messages import CapMessages, CapMessagesPost, Thread, Message from weboob.capabilities.dating import CapDating, Optimization from weboob.capabilities.contact import CapContact, Contact, ProfileNode -from weboob.exceptions import BrowserHTTPError +from .compat.weboob_exceptions import BrowserHTTPError from weboob.tools.backend import Module, BackendConfig from weboob.tools.value import Value, ValueBackendPassword from weboob.tools.log import getLogger diff --git a/modules/hsbc/browser.py b/modules/hsbc/browser.py index 6700c3d69d92a72fea5762776294bbd5316fe17b..10673c78aa6896e213e746e6d3a5b2ce00951b96 100644 --- a/modules/hsbc/browser.py +++ b/modules/hsbc/browser.py @@ -25,10 +25,10 @@ from collections import OrderedDict from weboob.tools.date import LinearDateGuesser -from .compat.weboob_capabilities_bank import Account, AccountNotFound +from .compat.weboob_capabilities_bank import Account, AccountNotFound, AccountOwnership from weboob.tools.capabilities.bank.transactions import sorted_transactions, keep_only_card_transactions from weboob.tools.compat import parse_qsl, urlparse -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.browser.exceptions import HTTPNotFound from weboob.capabilities.base import find_object @@ -223,6 +223,12 @@ def iter_account_owners(self): for a in self.accounts_dict[owner].values(): a._owner = owner + # The first space is the PSU owner space + if owner == 0: + a.ownership = AccountOwnership.OWNER + else: + a.ownership = AccountOwnership.ATTORNEY + # go on cards page if there are cards accounts for a in self.accounts_dict[owner].values(): if a.type == Account.TYPE_CARD: @@ -253,6 +259,9 @@ def iter_account_owners(self): for account in owner.values(): if account.id not in self.unique_accounts_dict.keys(): self.unique_accounts_dict[account.id] = account + else: + # If an account is in multiple space, that's mean it is shared between this owners. + self.unique_accounts_dict[account.id].ownership = AccountOwnership.CO_OWNER if self.unique_accounts_dict: for account in self.unique_accounts_dict.values(): diff --git a/modules/hsbc/compat/weboob_capabilities_bank.py b/modules/hsbc/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/hsbc/compat/weboob_capabilities_bank.py +++ b/modules/hsbc/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/hsbc/compat/weboob_exceptions.py b/modules/hsbc/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/hsbc/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/hsbc/pages/account_pages.py b/modules/hsbc/pages/account_pages.py index 84a9a980b25ba2687b937c8f06b861ee212f66aa..13192102784cb2383c4d8bff5792776ac4d98ed7 100644 --- a/modules/hsbc/pages/account_pages.py +++ b/modules/hsbc/pages/account_pages.py @@ -32,7 +32,7 @@ from weboob.capabilities import NotAvailable from .compat.weboob_capabilities_bank import Account, AccountOwnerType from weboob.capabilities.profile import Person -from weboob.exceptions import ActionNeeded, BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import ActionNeeded, BrowserIncorrectPassword, BrowserUnavailable from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.tools.compat import urljoin from .landing_pages import GenericLandingPage diff --git a/modules/hsbc/pages/compat/weboob_capabilities_bank.py b/modules/hsbc/pages/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/hsbc/pages/compat/weboob_capabilities_bank.py +++ b/modules/hsbc/pages/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/hsbc/pages/compat/weboob_exceptions.py b/modules/hsbc/pages/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/hsbc/pages/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/hsbc/pages/investments.py b/modules/hsbc/pages/investments.py index 3f84a161ecdf5aaa8ff26ba9d5af1073d8a4dca4..0ac2b8d5aa303067f50c2a8865514971c929a567 100644 --- a/modules/hsbc/pages/investments.py +++ b/modules/hsbc/pages/investments.py @@ -19,7 +19,7 @@ from weboob.browser.filters.html import TableCell, Link from weboob.browser.filters.json import Dict from weboob.browser.filters.javascript import JSVar -from weboob.exceptions import BrowserUnavailable +from .compat.weboob_exceptions import BrowserUnavailable class LogonInvestmentPage(LoggedPage, HTMLPage): diff --git a/modules/humanis/compat/weboob_capabilities_bank.py b/modules/humanis/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/humanis/compat/weboob_capabilities_bank.py +++ b/modules/humanis/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/ideel/browser.py b/modules/ideel/browser.py index 9d1ec3239d801fda04eb73b689c2e93e6ac69066..f06118702b2465ca096a06e4f4f17ff3294d9050 100644 --- a/modules/ideel/browser.py +++ b/modules/ideel/browser.py @@ -23,11 +23,11 @@ from decimal import Decimal from itertools import count, takewhile -from weboob.browser import URL, LoginBrowser, need_login +from weboob.browser.browsers import URL, LoginBrowser, need_login from weboob.browser.pages import HTMLPage from weboob.capabilities.base import Currency from weboob.capabilities.shop import Item, Order, OrderNotFound, Payment -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.capabilities.bank.transactions import AmericanTransaction as AmTr from weboob.tools.compat import unicode diff --git a/modules/ideel/compat/__init__.py b/modules/ideel/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/ideel/compat/weboob_exceptions.py b/modules/ideel/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ideel/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/imdb/browser.py b/modules/imdb/browser.py index 546a86cd814558f1e50527892cd61240eba65988..9270a198d8e149ac2545ea716538b7e5b8769ef5 100644 --- a/modules/imdb/browser.py +++ b/modules/imdb/browser.py @@ -25,9 +25,9 @@ except ImportError: from html.parser import HTMLParser -from weboob.browser import PagesBrowser, URL +from weboob.browser.browsers import PagesBrowser, URL from weboob.browser.profiles import Wget -from weboob.exceptions import BrowserHTTPNotFound +from .compat.weboob_exceptions import BrowserHTTPNotFound from weboob.capabilities.base import NotAvailable, NotLoaded from weboob.capabilities.cinema import Movie, Person from weboob.tools.compat import unicode diff --git a/modules/imdb/compat/__init__.py b/modules/imdb/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/imdb/compat/weboob_exceptions.py b/modules/imdb/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/imdb/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/infomaniak/browser.py b/modules/infomaniak/browser.py index 8c3f4309f87b5c574f7b08b7a687d53f187bc418..7c00d3cbcbb32be59c5e9b7ae2fe65f9398898fa 100644 --- a/modules/infomaniak/browser.py +++ b/modules/infomaniak/browser.py @@ -19,8 +19,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, SubscriptionsPage, DocumentsPage diff --git a/modules/infomaniak/compat/weboob_exceptions.py b/modules/infomaniak/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/infomaniak/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ing/api/compat/weboob_capabilities_bank.py b/modules/ing/api/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/ing/api/compat/weboob_capabilities_bank.py +++ b/modules/ing/api/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/ing/api/transfer_page.py b/modules/ing/api/transfer_page.py index f12092fa95a85e30755bad6ae90e5a17765a8a42..5743e3ee97a28f70c4f11762a659f5d9382a12c4 100644 --- a/modules/ing/api/transfer_page.py +++ b/modules/ing/api/transfer_page.py @@ -30,9 +30,7 @@ from weboob.browser.elements import method, DictElement, ItemElement from weboob.browser.filters.json import Dict from .compat.weboob_browser_filters_standard import Env, Field, Date -from .compat.weboob_capabilities_bank import ( - Recipient, RecipientInvalidIban, RecipientInvalidOTP, -) +from .compat.weboob_capabilities_bank import Recipient class TransferINGVirtKeyboard(SimpleVirtualKeyboard): @@ -168,13 +166,6 @@ def check_recipient(self, recipient): rcpt = self.doc return rcpt['accountHolderName'] == recipient.label and rcpt['iban'] == recipient.iban - def handle_error(self): - if 'error' in self.doc: - if self.doc['error']['code'] == 'EXTERNAL_ACCOUNT.IBAN_NOT_FRENCH': - # not using the bank message because it is too generic - raise RecipientInvalidIban(message="L'IBAN doit correpondre à celui d'une banque domiciliée en France.") - assert False, 'Recipient error not handled' - class OtpChannelsPage(LoggedPage, JsonPage): def get_sms_info(self): @@ -186,10 +177,4 @@ def get_sms_info(self): class ConfirmOtpPage(LoggedPage, JsonPage): - def handle_error(self): - if 'error' in self.doc: - error_code = self.doc['error']['code'] - if error_code == 'SCA.WRONG_OTP_ATTEMPT': - raise RecipientInvalidOTP(message=self.doc['error']['message']) - - assert False, 'Recipient OTP error not handled' + pass diff --git a/modules/ing/api_browser.py b/modules/ing/api_browser.py index 823de832a7e56dca02cc5dc0a847a0565718e90d..c01d3a72f922e31b55e8fc9154f14f57b4e63916 100644 --- a/modules/ing/api_browser.py +++ b/modules/ing/api_browser.py @@ -25,12 +25,12 @@ import re from weboob.browser.browsers import LoginBrowser, URL, StatesMixin -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded from weboob.browser.exceptions import ClientError from .compat.weboob_capabilities_bank import ( TransferBankError, TransferInvalidAmount, AddRecipientStep, RecipientInvalidOTP, - AddRecipientTimeout, AddRecipientBankError, + AddRecipientTimeout, AddRecipientBankError, RecipientInvalidIban, ) from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.tools.value import Value @@ -436,23 +436,38 @@ def send_sms_to_user(self, recipient, sms_info): raise AddRecipientStep(recipient, Value('code', label='Veuillez saisir le code temporaire envoyé par SMS')) def handle_recipient_error(self, r): + # The bank gives an error message when an error occures. + # But sometimes the message is not relevant. + # So I may replace it by nothing or by a custom message. + # The exception to raise can be coupled with: + # * Nothing: empty message + # * None: message of the bank + # * String: custom message + RECIPIENT_ERROR = { + 'SENSITIVE_OPERATION.SENSITIVE_OPERATION_NOT_FOUND': (AddRecipientTimeout,), + 'SENSITIVE_OPERATION.EXPIRED_TEMPORARY_CODE': (AddRecipientTimeout, None), + 'EXTERNAL_ACCOUNT.EXTERNAL_ACCOUNT_ALREADY_EXISTS': (AddRecipientBankError, None), + 'EXTERNAL_ACCOUNT.ACCOUNT_RESTRICTION': (AddRecipientBankError, None), + 'EXTERNAL_ACCOUNT.EXTERNAL_ACCOUNT_IS_INTERNAL_ACCOUT': (AddRecipientBankError, None), # nice spelling + 'EXTERNAL_ACCOUNT.IBAN_NOT_FRENCH': (RecipientInvalidIban, "L'IBAN doit correpondre à celui d'une banque domiciliée en France."), + 'SCA.WRONG_OTP_ATTEMPT': (RecipientInvalidOTP, None), + 'INPUT_INVALID': (AssertionError, None), # invalid request + } + error_page = r.response.json() if 'error' in error_page: error = error_page['error'] - # the error message may seem generic - # but after testing multiple cases - # it is the only time that it appears - if error['code'] == 'SENSITIVE_OPERATION.SENSITIVE_OPERATION_NOT_FOUND': - raise AddRecipientTimeout() - elif error['code'] in ( - 'EXTERNAL_ACCOUNT.EXTERNAL_ACCOUNT_ALREADY_EXISTS', - # not allowed to add a recipient - 'EXTERNAL_ACCOUNT.ACCOUNT_RESTRICTION', - ): - raise AddRecipientBankError(message=error['message']) + error_exception = RECIPIENT_ERROR.get(error['code']) + if error_exception: + if len(error_exception) == 1: + raise error_exception[0]() + elif error_exception[1] is None: + raise error_exception[0](message=error['message']) + else: + raise error_exception[0](message=error_exception[1]) - assert False, 'Recipient error not handled' + assert False, 'Recipient error "%s" not handled' % error['code'] @need_login def end_sms_recipient(self, recipient, code): @@ -477,8 +492,6 @@ def end_sms_recipient(self, recipient, code): self.handle_recipient_error(e) raise - self.page.handle_error() - @need_login @need_to_be_on_website('api') def new_recipient(self, recipient, **params): @@ -507,8 +520,6 @@ def new_recipient(self, recipient, **params): self.handle_recipient_error(e) raise - self.page.handle_error() - assert self.page.check_recipient(recipient), "The recipients don't match." self.add_recipient_info = self.page.doc diff --git a/modules/ing/browser.py b/modules/ing/browser.py index b648a4e6098a74874e9e7b63b7bb449206dd9407..63acf73bfa46a129e56e5177ae72a65f4e777047 100644 --- a/modules/ing/browser.py +++ b/modules/ing/browser.py @@ -27,7 +27,7 @@ from requests.exceptions import SSLError from weboob.browser.browsers import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserUnavailable, BrowserHTTPNotFound +from .compat.weboob_exceptions import BrowserUnavailable, BrowserHTTPNotFound from weboob.browser.exceptions import ServerError from .compat.weboob_capabilities_bank import Account, AccountNotFound from weboob.capabilities.base import find_object, NotAvailable diff --git a/modules/ing/compat/weboob_capabilities_bank.py b/modules/ing/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/ing/compat/weboob_capabilities_bank.py +++ b/modules/ing/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/ing/compat/weboob_exceptions.py b/modules/ing/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ing/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ing/pages/compat/__init__.py b/modules/ing/pages/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/ing/pages/login.py b/modules/ing/pages/login.py index 1be8b6388086a3e5b6c9444aa8dabef068627456..8e29208b7e49ef58fe9ba062ef971391deeb4aaf 100644 --- a/modules/ing/pages/login.py +++ b/modules/ing/pages/login.py @@ -19,7 +19,7 @@ from io import BytesIO -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded from .compat.weboob_tools_captcha_virtkeyboard import VirtKeyboard from weboob.browser.pages import HTMLPage, LoggedPage from weboob.browser.filters.html import Attr diff --git a/modules/ing/web/compat/weboob_capabilities_bank.py b/modules/ing/web/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/ing/web/compat/weboob_capabilities_bank.py +++ b/modules/ing/web/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/ing/web/compat/weboob_exceptions.py b/modules/ing/web/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ing/web/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ing/web/login.py b/modules/ing/web/login.py index e55dc1b07bac37774256a034a2a4bd06ca458c28..fd2b24b5373af1c9e3d217d5c53603f1b037724c 100644 --- a/modules/ing/web/login.py +++ b/modules/ing/web/login.py @@ -18,7 +18,7 @@ # along with weboob. If not, see . -from weboob.exceptions import ActionNeeded +from .compat.weboob_exceptions import ActionNeeded from weboob.browser.pages import HTMLPage, LoggedPage from .compat.weboob_browser_filters_standard import CleanText diff --git a/modules/kiwibank/browser.py b/modules/kiwibank/browser.py index 63667e391301668512615fa072fa32d8076ec3ff..e6a10c5f30a3e8b3b20bfed785afccaa182489ef 100644 --- a/modules/kiwibank/browser.py +++ b/modules/kiwibank/browser.py @@ -18,8 +18,8 @@ # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, AccountPage, HistoryPage diff --git a/modules/kiwibank/compat/weboob_capabilities_bank.py b/modules/kiwibank/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/kiwibank/compat/weboob_capabilities_bank.py +++ b/modules/kiwibank/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/kiwibank/compat/weboob_exceptions.py b/modules/kiwibank/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/kiwibank/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/lampiris/browser.py b/modules/lampiris/browser.py index 942abe676ecce8c2882da88afbd28cfc78629ef5..25e0ef6de08829ac7bf5b39c09d1448e07a38394 100644 --- a/modules/lampiris/browser.py +++ b/modules/lampiris/browser.py @@ -20,9 +20,9 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.browser.exceptions import ClientError -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, BillsPage diff --git a/modules/lampiris/compat/weboob_exceptions.py b/modules/lampiris/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/lampiris/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/lcl/browser.py b/modules/lcl/browser.py index 2de5a43d5328eae2cde73ee08d6fa3aa6f6e342d..5f23e8fc458cd9200adb38d4b5bb6f696de4c38c 100644 --- a/modules/lcl/browser.py +++ b/modules/lcl/browser.py @@ -24,7 +24,7 @@ from datetime import datetime, timedelta, date from functools import wraps -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin from weboob.browser.exceptions import ServerError from weboob.capabilities.base import NotAvailable @@ -39,7 +39,7 @@ AVPage, AVDetailPage, DiscPage, NoPermissionPage, RibPage, HomePage, LoansPage, TransferPage, AddRecipientPage, RecipientPage, RecipConfirmPage, SmsPage, RecipRecapPage, LoansProPage, Form2Page, DocumentsPage, ClientPage, SendTokenPage, CaliePage, ProfilePage, DepositPage, - AVHistoryPage, AVInvestmentsPage, CardsPage, AVListPage, + AVHistoryPage, AVInvestmentsPage, CardsPage, AVListPage, CalieContractsPage, ) @@ -91,13 +91,19 @@ class LCLBrowser(LoginBrowser, StatesMixin): form2 = URL(r'/outil/UWVI/Routage', Form2Page) send_token = URL('/outil/UWVI/AssuranceVie/envoyerJeton', SendTokenPage) - calie = URL('https://www.my-calie.fr/FO.HoldersWebSite/Disclaimer/Disclaimer.aspx.*', - 'https://www.my-calie.fr/FO.HoldersWebSite/Contract/ContractDetails.aspx.*', - 'https://www.my-calie.fr/FO.HoldersWebSite/Contract/ContractOperations.aspx.*', CaliePage) - - assurancevie = URL('/outil/UWVI/AssuranceVie/accesSynthese', - '/outil/UWVI/AssuranceVie/accesDetail.*', - AVPage) + calie_detail = URL( + r'https://www.my-calie.fr/FO.HoldersWebSite/Disclaimer/Disclaimer.aspx.*', + r'https://www.my-calie.fr/FO.HoldersWebSite/Contract/ContractDetails.aspx.*', + r'https://www.my-calie.fr/FO.HoldersWebSite/Contract/ContractOperations.aspx.*', + CaliePage + ) + calie_contracts = URL(r'https://www.my-calie.fr/FO.HoldersWebSite/Contract/SearchContract.aspx', CalieContractsPage) + + assurancevie = URL( + r'/outil/UWVI/AssuranceVie/accesSynthese', + r'/outil/UWVI/AssuranceVie/accesDetail.*', + AVPage + ) av_list = URL('https://assurance-vie-et-prevoyance.secure.lcl.fr/rest/assurance/synthesePartenaire', AVListPage) avdetail = URL('https://assurance-vie-et-prevoyance.secure.lcl.fr/consultation/epargne', AVDetailPage) @@ -248,10 +254,36 @@ def get_accounts(self): if self.no_perm.is_here(): self.logger.warning('Life insurances are unavailable.') else: + # retrieve life insurances from popups for a in self.page.get_popup_life_insurance(): self.update_accounts(a) - # retrieve life insurance on special lcl life insurance website + # retrieve life insurances from calie website + calie_index = self.page.get_calie_life_insurances_first_index() + if calie_index: + form = self.page.get_form(id="formRedirectPart") + form['INDEX'] = calie_index + form.submit() + # if only one calie insurance, request directly leads to details on CaliePage + if self.calie_detail.is_here(): + self.page.check_error() + a = Account() + a.url = self.url + self.page.fill_account(obj=a) + self.update_accounts(a) + # if several calie insurances, request leads to CalieContractsPage + elif self.calie_contracts.is_here(): + for a in self.page.iter_calie_life_insurance(): + if a.url: + self.location(a.url) + self.page.fill_account(obj=a) + self.update_accounts(a) + else: + self.logger.warning('%s has no url to parse detail to' % a) + # get back to life insurances list page + self.assurancevie.stay_or_go() + + # retrieve life insurances on special lcl life insurance website if self.page.is_website_life_insurance(): self.go_life_insurance_website() for life_insurance in self.page.iter_life_insurance(): @@ -381,21 +413,20 @@ def get_history(self, account): self.logger.warning('This account is limited, there is no available history.') return - self.assurancevie.stay_or_go() - self.go_life_insurance_website() - - if self.calie.is_here(): - # Get back to Synthèse - self.assurancevie.go() - return - - assert self.av_list.is_here(), 'Something went wrong during iter life insurance history' - # Need to be on account details page to do history request - self.av_investments.go(life_insurance_id=account.id) - self.av_history.go() - for tr in self.page.iter_history(): - yield tr - self.go_back_from_life_insurance_website() + if account._is_calie_account: + # TODO build parsing of history page, all-you-can-eat js in it + # follow 'account._history_url' for that + raise NotImplementedError() + else: + self.assurancevie.stay_or_go() + self.go_life_insurance_website() + assert self.av_list.is_here(), 'Something went wrong during iter life insurance history' + # Need to be on account details page to do history request + self.av_investments.go(life_insurance_id=account.id) + self.av_history.go() + for tr in self.page.iter_history(): + yield tr + self.go_back_from_life_insurance_website() @need_login def get_coming(self, account): @@ -453,20 +484,17 @@ def get_investment(self, account): return self.assurancevie.stay_or_go() - self.go_life_insurance_website() - - if self.calie.is_here(): + if account._is_calie_account: + calie_details = self.open(account.url) + for inv in calie_details.page.iter_investment(): + yield inv + else: + self.go_life_insurance_website() + assert self.av_list.is_here(), 'Something went wrong during iter life insurance investments' + self.av_investments.go(life_insurance_id=account.id) for inv in self.page.iter_investment(): yield inv - # Get back to Life Insurance space - self.assurancevie.go() - return - - assert self.av_list.is_here(), 'Something went wrong during iter life insurance investments' - self.av_investments.go(life_insurance_id=account.id) - for inv in self.page.iter_investment(): - yield inv - self.go_back_from_life_insurance_website() + self.go_back_from_life_insurance_website() elif hasattr(account, '_market_link') and account._market_link: self.connexion_bourse() diff --git a/modules/lcl/compat/weboob_capabilities_bank.py b/modules/lcl/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/lcl/compat/weboob_capabilities_bank.py +++ b/modules/lcl/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/lcl/compat/weboob_exceptions.py b/modules/lcl/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/lcl/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/lcl/enterprise/browser.py b/modules/lcl/enterprise/browser.py index 331cb537883b96e01fd66afe8a29d3504f47ecbc..9a516d9add05eb03cc52a346fe5d2960826a6d46 100644 --- a/modules/lcl/enterprise/browser.py +++ b/modules/lcl/enterprise/browser.py @@ -19,7 +19,7 @@ from weboob.browser.browsers import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .compat.weboob_capabilities_bank import AccountOwnerType from .pages import LoginPage, MovementsPage, ProfilePage, PassExpiredPage diff --git a/modules/lcl/enterprise/compat/weboob_capabilities_bank.py b/modules/lcl/enterprise/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/lcl/enterprise/compat/weboob_capabilities_bank.py +++ b/modules/lcl/enterprise/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/lcl/enterprise/compat/weboob_exceptions.py b/modules/lcl/enterprise/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/lcl/enterprise/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/lcl/enterprise/pages.py b/modules/lcl/enterprise/pages.py index 37aacb5cdfe1f173f61d307f8a9306c1d68b052c..75b7cb01768eeb1771cded5d59a981db93807ed2 100644 --- a/modules/lcl/enterprise/pages.py +++ b/modules/lcl/enterprise/pages.py @@ -27,7 +27,7 @@ from .compat.weboob_capabilities_bank import Account from weboob.capabilities.profile import Profile from weboob.capabilities.base import NotAvailable -from weboob.exceptions import BrowserPasswordExpired +from .compat.weboob_exceptions import BrowserPasswordExpired from ..pages import Transaction diff --git a/modules/lcl/pages.py b/modules/lcl/pages.py index a55ef7714d93338be889b4e3b1d3d66e19248627..4d485c84bb06293fc1ee12ce41c9cfa14aaa9177 100644 --- a/modules/lcl/pages.py +++ b/modules/lcl/pages.py @@ -27,8 +27,7 @@ from io import BytesIO from datetime import datetime, timedelta -from weboob.capabilities import NotAvailable -from weboob.capabilities.base import find_object +from weboob.capabilities.base import empty, find_object, NotAvailable from .compat.weboob_capabilities_bank import ( Account, Investment, Recipient, TransferError, TransferBankError, Transfer, ) @@ -36,20 +35,19 @@ from weboob.capabilities.profile import Person, ProfileMissing from weboob.capabilities.contact import Advisor from weboob.browser.elements import method, ListElement, TableElement, ItemElement, DictElement -from weboob.exceptions import ParseError +from .compat.weboob_exceptions import ParseError, ActionNeeded from weboob.browser.exceptions import ServerError from weboob.browser.pages import LoggedPage, HTMLPage, JsonPage, FormNotFound, pagination -from weboob.browser.filters.html import Attr, Link, TableCell, AttributeNotFound +from weboob.browser.filters.html import Attr, Link, TableCell, AttributeNotFound, AbsoluteLink from .compat.weboob_browser_filters_standard import ( CleanText, Field, Regexp, Format, Date, CleanDecimal, Map, AsyncLoad, Async, Env, Slugify, BrowserURL, Eval, Currency, ) from weboob.browser.filters.json import Dict -from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserUnavailable, BrowserIncorrectPassword from weboob.tools.capabilities.bank.transactions import FrenchTransaction from .compat.weboob_tools_captcha_virtkeyboard import MappedVirtKeyboard, VirtKeyboardError -from weboob.tools.compat import unicode -from weboob.tools.compat import urlparse, parse_qs +from weboob.tools.compat import unicode, urlparse, parse_qs, urljoin from weboob.tools.html import html2text from weboob.tools.date import parse_french_date from weboob.tools.capabilities.bank.investments import is_isin_valid @@ -792,6 +790,16 @@ def is_website_life_insurance(self): # because we just need to go on life insurance external website return bool(self.get_routage_url()) + def get_calie_life_insurances_first_index(self): + # indices are associated to calie life insurances to make requests to them + # if only one life insurance, this request directly leads to details on CaliePage + # otherwise, any index will lead to CalieContractsPage, + # so we stop at the first index + for account in self.doc.xpath('//table[@class]/tbody/tr'): + if account.xpath('.//td[has-class("nomContrat")]//a[contains(@class, "redirect")][@href="#"]'): + index = Attr(account.xpath('.//td[has-class("nomContrat")]//a[contains(@class, "redirect")][@href="#"]'), 'id')(self) + return index + @method class get_popup_life_insurance(ListElement): item_xpath = '//table[@class]/tbody/tr' @@ -817,6 +825,7 @@ def condition(self): obj__transfer_id = None obj_number = Field('id') obj__external_website = False + obj__is_calie_account = False def obj_id(self): _id = CleanText('.//td/@id')(self) @@ -875,6 +884,24 @@ def obj__form(self): return form +class CalieContractsPage(LoggedPage, HTMLPage): + @method + class iter_calie_life_insurance(TableElement): + head_xpath = '//table[contains(@id, "MainTable")]//tr[contains(@id, "HeadersRow")]//td[text()]' + item_xpath = '//table[contains(@id, "MainTable")]//tr[contains(@id, "DataRow")]' + + col_number = 'Numéro contrat' # internal contrat number + + class item(ItemElement): + klass = Account + + # internal contrat number, to be replaced by external number in CaliePage.fill_account() + # obj_id is needed here though, to avoid dupicate account errors + obj_id = CleanText(TableCell('number')) + + obj_url = AbsoluteLink('.//a') # need AbsoluteLink since we moved out of basurl domain + + class SendTokenPage(LoggedPage, LCLBasePage): def on_load(self): form = self.get_form('//form') @@ -904,17 +931,17 @@ def get_colnum(self, name): class CaliePage(LoggedPage, HTMLPage): - def on_load(self): - # TODO raise the ActionNeeded when backend handles it. - if self.doc.xpath('//button[@id="acceptDisclaimerButton"]'): - self.logger.warning('Action Needed on website: %s', CleanText('//div[@class="data-header"]')(self.doc)) + def check_error(self): + message = CleanText('//div[contains(@class, "disclaimer-div")]//text()[contains(., "utilisation vaut acceptation")]')(self.doc) + if self.doc.xpath('//button[@id="acceptDisclaimerButton"]') and message: + raise ActionNeeded(message) @method class iter_investment(CalieTableElement): # Careful, contains many nested
# Two first lines are titles, two last are investment sum-ups - item_xpath = '//table[@class="dxgvTable dxgvRBB"]//tr[position() > 2 and position() < last()-1]' - head_xpath = '//table[@class="dxgvTable dxgvRBB"]//tr[1]/td//tr/td[1]' + item_xpath = '//table[@class="dxgvTable dxgvRBB"]//tr[contains(@class, "DataRow")]' + head_xpath = '//table[contains(@id, "MainTable")]//tr[contains(@id, "HeadersRow")]//td[text()]' col_label = 'Support' col_vdate = 'Date de valeur' @@ -922,7 +949,7 @@ class iter_investment(CalieTableElement): col_valuation = 'Valeur dans la devise du support (EUR)' col_unitvalue = 'Valeur unitaire' col_quantity = 'Parts' - col_diff = 'Performance' + col_diff_ratio = 'Performance' col_portfolio_share = 'Répartition (%)' class item(ItemElement): @@ -932,16 +959,40 @@ class item(ItemElement): obj_original_valuation = CleanDecimal(TableCell('original_valuation'), replace_dots=True) obj_valuation = CleanDecimal(TableCell('valuation'), replace_dots=True) obj_vdate = Date(CleanText(TableCell('vdate')), dayfirst=True) - obj_unitvalue = CleanDecimal(TableCell('unitvalue'), replace_dots=True) - obj_quantity = CleanDecimal(TableCell('quantity'), replace_dots=True) + obj_unitvalue = CleanDecimal(TableCell('unitvalue'), replace_dots=True, default=NotAvailable) # displayed with format '123.456,78 EUR' + obj_quantity = CleanDecimal(TableCell('quantity'), replace_dots=True, default=NotAvailable) # displayed with format '1.234,5678 u.' obj_portfolio_share = Eval(lambda x: x / 100, CleanDecimal(TableCell('portfolio_share'))) - obj_diff = CleanDecimal(TableCell('diff')) + + def obj_diff_ratio(self): + _diff_ratio = CleanDecimal(TableCell('diff_ratio'), default=NotAvailable)(self) + if not empty(_diff_ratio): + return Eval(lambda x: x / 100, _diff_ratio)(self) + return NotAvailable # Unfortunately on the Calie space the links to the # invest details return Forbidden even on the website obj_code = NotAvailable obj_code_type = NotAvailable + @method + class fill_account(ItemElement): + obj_number = obj_id = Regexp(CleanText('.'), r'Numéro externe (.{10})') + obj_label = Format( + '%s %s', + Regexp(CleanText('.'), r'Produit (.*) Statut'), + Field('id') + ) + obj_balance = CleanDecimal('//tr[contains(@id, "FooterRow")]', replace_dots=True) + obj_type = Account.TYPE_LIFE_INSURANCE + obj_currency = 'EUR' + obj__external_website = True + obj__is_calie_account = True + obj__transfer_id = None + + def obj__history_url(self): + relative_url = Regexp(Attr('//a[contains(text(), "Opérations")]', 'onclick'), r'href=\'(.*)\'')(self) + return urljoin(self.page.url, relative_url) + class AVDetailPage(LoggedPage, LCLBasePage): def come_back(self): @@ -987,6 +1038,7 @@ def condition(self): obj__market_link = None obj__coming_links = [] obj__transfer_id = None + obj__is_calie_account = False class AVHistoryPage(LoggedPage, JsonPage): diff --git a/modules/ldlc/browser.py b/modules/ldlc/browser.py index fb1102abf5b9b9d80f87fd29d18860962ef34af2..a1b486e1a5a8d1b89ce369d9e779dc967a61ce50 100644 --- a/modules/ldlc/browser.py +++ b/modules/ldlc/browser.py @@ -17,8 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, AbstractBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword, NocaptchaQuestion +from weboob.browser.browsers import LoginBrowser, AbstractBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword, NocaptchaQuestion from .pages import HomePage, LoginPage, ProBillsPage, DocumentsPage diff --git a/modules/ldlc/compat/weboob_exceptions.py b/modules/ldlc/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ldlc/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ldlc/materielnet_pages.py b/modules/ldlc/materielnet_pages.py index 5deb713b6eb55f5ce1145325fef4a463ece50fff..1724671d1b6671bd46770b659b7aa33dcd9334ca 100644 --- a/modules/ldlc/materielnet_pages.py +++ b/modules/ldlc/materielnet_pages.py @@ -27,7 +27,7 @@ from weboob.browser.filters.html import Attr, Link from weboob.capabilities.bill import DocumentTypes, Bill, Subscription from weboob.capabilities.base import NotAvailable -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword class LoginPage(PartialHTMLPage): diff --git a/modules/lendosphere/compat/weboob_capabilities_bank.py b/modules/lendosphere/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/lendosphere/compat/weboob_capabilities_bank.py +++ b/modules/lendosphere/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/lendosphere/compat/weboob_exceptions.py b/modules/lendosphere/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/lendosphere/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/lendosphere/pages.py b/modules/lendosphere/pages.py index 0ea4c454c480881733840b487c91027fd939717c..313ea3249bc15b8be3d8776a1e7789e08803dc25 100644 --- a/modules/lendosphere/pages.py +++ b/modules/lendosphere/pages.py @@ -27,7 +27,7 @@ from weboob.browser.pages import HTMLPage, CsvPage, LoggedPage from weboob.capabilities.base import NotAvailable from .compat.weboob_capabilities_bank import Account, Transaction -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword MAIN_ID = '_lendosphere_' diff --git a/modules/linebourse/api/compat/weboob_capabilities_bank.py b/modules/linebourse/api/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/linebourse/api/compat/weboob_capabilities_bank.py +++ b/modules/linebourse/api/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/linebourse/browser.py b/modules/linebourse/browser.py index b6622d319499da14f60a3d6270dc17c81674f0e6..5366d9a27cff6e05f6bebb89a5450a5f62901d80 100644 --- a/modules/linebourse/browser.py +++ b/modules/linebourse/browser.py @@ -21,8 +21,8 @@ import time -from weboob.browser import LoginBrowser, URL -from weboob.exceptions import BrowserUnavailable +from weboob.browser.browsers import LoginBrowser, URL +from .compat.weboob_exceptions import BrowserUnavailable from .pages import ( MessagePage, InvestmentPage, HistoryPage, BrokenPage, diff --git a/modules/linebourse/compat/weboob_capabilities_bank.py b/modules/linebourse/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/linebourse/compat/weboob_capabilities_bank.py +++ b/modules/linebourse/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/linebourse/compat/weboob_exceptions.py b/modules/linebourse/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/linebourse/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/linebourse/pages.py b/modules/linebourse/pages.py index 83c1a23002c53d987f5fb440685ccac94f87bae6..29815c2600159b939c37389f01914b8f4ebc67db 100644 --- a/modules/linebourse/pages.py +++ b/modules/linebourse/pages.py @@ -32,7 +32,7 @@ from weboob.tools.capabilities.bank.transactions import FrenchTransaction as Transaction from weboob.tools.capabilities.bank.investments import create_french_liquidity from weboob.tools.compat import quote_plus -from weboob.exceptions import ActionNeeded +from .compat.weboob_exceptions import ActionNeeded def MyDecimal(*args, **kwargs): diff --git a/modules/lolix/compat/__init__.py b/modules/lolix/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/lucca/browser.py b/modules/lucca/browser.py index 2792bc406144988c7d532c2f38c03c2494707700..a07316fbe7bf71cef926103af0a27fa4705a8e61 100644 --- a/modules/lucca/browser.py +++ b/modules/lucca/browser.py @@ -21,9 +21,9 @@ from datetime import timedelta -from weboob.browser import LoginBrowser, need_login, URL +from weboob.browser.browsers import LoginBrowser, need_login, URL from weboob.browser.exceptions import ClientError -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.date import new_datetime from .pages import ( diff --git a/modules/lucca/compat/__init__.py b/modules/lucca/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/lucca/compat/weboob_exceptions.py b/modules/lucca/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/lucca/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/materielnet/browser.py b/modules/materielnet/browser.py index 890d446f2c7ff3513ae5b4aa4478b59aada16aa9..1768454c9cc6670b04564fa143a2b974005e38a8 100644 --- a/modules/materielnet/browser.py +++ b/modules/materielnet/browser.py @@ -20,8 +20,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword, NocaptchaQuestion +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword, NocaptchaQuestion from .pages import LoginPage, CaptchaPage, ProfilePage, DocumentsPage, DocumentsDetailsPage diff --git a/modules/materielnet/compat/weboob_exceptions.py b/modules/materielnet/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/materielnet/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/materielnet/pages.py b/modules/materielnet/pages.py index 5deb713b6eb55f5ce1145325fef4a463ece50fff..1724671d1b6671bd46770b659b7aa33dcd9334ca 100644 --- a/modules/materielnet/pages.py +++ b/modules/materielnet/pages.py @@ -27,7 +27,7 @@ from weboob.browser.filters.html import Attr, Link from weboob.capabilities.bill import DocumentTypes, Bill, Subscription from weboob.capabilities.base import NotAvailable -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword class LoginPage(PartialHTMLPage): diff --git a/modules/mediawiki/browser.py b/modules/mediawiki/browser.py index 7da971d929ff177713038d2effc75a440592976d..f425c4f916f676d88413b36a2bc11b6c428ae364 100644 --- a/modules/mediawiki/browser.py +++ b/modules/mediawiki/browser.py @@ -24,7 +24,7 @@ import dateutil.parser from weboob.browser.browsers import DomainBrowser -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.capabilities.content import Revision from weboob.tools.compat import urlsplit, urljoin, basestring diff --git a/modules/mediawiki/compat/__init__.py b/modules/mediawiki/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/mediawiki/compat/weboob_exceptions.py b/modules/mediawiki/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/mediawiki/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/myedenred/browser.py b/modules/myedenred/browser.py index 363ff03ab189270ca1d4f7ff328117f4c988d6bd..c45339b903bde518ed3449670d402c5cb3a20270 100644 --- a/modules/myedenred/browser.py +++ b/modules/myedenred/browser.py @@ -21,8 +21,8 @@ from datetime import timedelta -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.date import LinearDateGuesser from .pages import LoginPage, AccountsPage, AccountDetailsPage, TransactionsPage diff --git a/modules/myedenred/compat/weboob_capabilities_bank.py b/modules/myedenred/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/myedenred/compat/weboob_capabilities_bank.py +++ b/modules/myedenred/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/myedenred/compat/weboob_exceptions.py b/modules/myedenred/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/myedenred/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/myfoncia/browser.py b/modules/myfoncia/browser.py index 3c6f029d839dc06e82e9eb5ce55559e2d8e4e945..55a0386ee41130e27d92ea7a406a07e8b4ac3924 100644 --- a/modules/myfoncia/browser.py +++ b/modules/myfoncia/browser.py @@ -20,8 +20,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, need_login, URL -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, need_login, URL +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, MonBienPage, MesChargesPage diff --git a/modules/myfoncia/compat/weboob_exceptions.py b/modules/myfoncia/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/myfoncia/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/myhabit/browser.py b/modules/myhabit/browser.py index 1f3e4b88c2c6b80cfc50fbfa50837516ff5e8856..8b020b519adb18d31ac3278adc46001c07407408 100644 --- a/modules/myhabit/browser.py +++ b/modules/myhabit/browser.py @@ -23,11 +23,11 @@ from requests.exceptions import Timeout -from weboob.browser import URL, LoginBrowser, need_login +from weboob.browser.browsers import URL, LoginBrowser, need_login from weboob.browser.pages import HTMLPage from weboob.capabilities.base import Currency from weboob.capabilities.shop import Item, Order, OrderNotFound, Payment -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.capabilities.bank.transactions import AmericanTransaction as AmTr from weboob.tools.compat import unicode diff --git a/modules/myhabit/compat/__init__.py b/modules/myhabit/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/myhabit/compat/weboob_exceptions.py b/modules/myhabit/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/myhabit/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/n26/browser.py b/modules/n26/browser.py index 2f5af884b90f544e1543d347a0d54a9bc8f74212..cd46bcb0bde6cffdb414c9be579eabeecfd5cfb7 100644 --- a/modules/n26/browser.py +++ b/modules/n26/browser.py @@ -25,7 +25,7 @@ from weboob.capabilities.base import find_object, NotAvailable from .compat.weboob_capabilities_bank import Account, Transaction, AccountNotFound from .compat.weboob_browser_filters_standard import CleanText -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable from weboob.browser.exceptions import ClientError # Do not use an APIBrowser since APIBrowser sends all its requests bodies as diff --git a/modules/n26/compat/weboob_capabilities_bank.py b/modules/n26/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/n26/compat/weboob_capabilities_bank.py +++ b/modules/n26/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/n26/compat/weboob_exceptions.py b/modules/n26/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/n26/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/nalo/compat/weboob_capabilities_bank.py b/modules/nalo/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/nalo/compat/weboob_capabilities_bank.py +++ b/modules/nalo/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/nef/browser.py b/modules/nef/browser.py index cda7704d6b08664f3b074d6f14a17154da0d169c..f026ec46af5fb777f26d76b9dcc1303ed23355f2 100644 --- a/modules/nef/browser.py +++ b/modules/nef/browser.py @@ -21,8 +21,8 @@ import datetime -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, HomePage, AccountsPage, RecipientsPage, TransactionsPage diff --git a/modules/nef/compat/weboob_capabilities_bank.py b/modules/nef/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/nef/compat/weboob_capabilities_bank.py +++ b/modules/nef/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/nef/compat/weboob_exceptions.py b/modules/nef/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/nef/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/netfinca/browser.py b/modules/netfinca/browser.py index 34afddbdfb2da6553668d77f4de3e439a4a0b85b..b27cdd75b6eba2ca388d8214256e52d22fa4ef99 100644 --- a/modules/netfinca/browser.py +++ b/modules/netfinca/browser.py @@ -20,8 +20,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL -from weboob.exceptions import BrowserUnavailable +from weboob.browser.browsers import LoginBrowser, URL +from .compat.weboob_exceptions import BrowserUnavailable from .pages import InvestmentsPage, AccountsPage diff --git a/modules/netfinca/compat/weboob_capabilities_bank.py b/modules/netfinca/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/netfinca/compat/weboob_capabilities_bank.py +++ b/modules/netfinca/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/netfinca/compat/weboob_exceptions.py b/modules/netfinca/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/netfinca/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/netfinca/pages.py b/modules/netfinca/pages.py index 992042ae156be0cbcdb295d7495e108b09e564f2..0cc638a2a2c9d5558327606c6337c2d59b8ba20d 100644 --- a/modules/netfinca/pages.py +++ b/modules/netfinca/pages.py @@ -71,6 +71,8 @@ def obj_id(self): _id = tablecell.xpath('./div[position()=2]') return CleanText(_id)(self) + obj_number = obj_id + def obj_label(self): tablecell = TableCell('label')(self)[0] label = tablecell.xpath('./div[position()=1]') diff --git a/modules/okc/browser.py b/modules/okc/browser.py index bddc12dcf31fe52c7a3994769de4e2c79ebb6257..bed55c41cd9cdd33b02f11792e727dbab43ff025 100644 --- a/modules/okc/browser.py +++ b/modules/okc/browser.py @@ -23,7 +23,7 @@ from weboob.browser.browsers import DomainBrowser from weboob.browser.pages import HTMLPage from .compat.weboob_browser_filters_standard import CleanText -from weboob.exceptions import BrowserIncorrectPassword, ParseError +from .compat.weboob_exceptions import BrowserIncorrectPassword, ParseError from weboob.tools.json import json __all__ = ['OkCBrowser'] diff --git a/modules/okc/compat/weboob_exceptions.py b/modules/okc/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/okc/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/oney/browser.py b/modules/oney/browser.py index a7f5ee4f532146e391507887673803ffd31f97cb..1f13893666ba996b730441895ea0656789d26b55 100644 --- a/modules/oney/browser.py +++ b/modules/oney/browser.py @@ -17,17 +17,21 @@ # 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 + from datetime import date, timedelta from dateutil.relativedelta import relativedelta from itertools import chain -from weboob.exceptions import BrowserIncorrectPassword -from weboob.browser import LoginBrowser, URL, need_login +from .compat.weboob_capabilities_bank import Account +from .compat.weboob_exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.tools.date import new_date from .pages import ( LoginPage, ClientPage, OperationsPage, ChoicePage, CreditHome, CreditAccountPage, CreditHistory, LastHistoryPage, + ContextInitPage, SendUsernamePage, SendPasswordPage, CheckTokenPage, ) __all__ = ['OneyBrowser'] @@ -36,19 +40,29 @@ class OneyBrowser(LoginBrowser): BASEURL = 'https://www.oney.fr' - login = URL(r'/site/s/login/login.html', LoginPage) + home_login = URL(r'/site/s/login/login.html', + LoginPage) + login = URL(r'https://login.oney.fr/login', + r'https://login.oney.fr/context', + LoginPage) + + send_username = URL(r'https://login.oney.fr/middle/authenticationflowinit', SendUsernamePage) + send_password = URL(r'https://login.oney.fr/middle/completeauthflowstep', SendPasswordPage) + context_init = URL(r'https://login.oney.fr/middle/context', ContextInitPage) - choice = URL(r'/site/s/multimarque/choixsite.html', ChoicePage) + check_token = URL(r'https://login.oney.fr/middle/check_token', CheckTokenPage) + + choice = URL(r'/site/s/multimarque/choixsite.html', ChoicePage) choice_portal = URL(r'/site/s/login/loginidentifiant.html') - client = URL(r'/oney/client', ClientPage) - operations = URL(r'/oney/client', OperationsPage) - card_page = URL(r'/oney/client\?task=Synthese&process=SyntheseMultiCompte&indexSelectionne=(?P\d+)') + client = URL(r'/oney/client', ClientPage) + operations = URL(r'/oney/client', OperationsPage) + card_page = URL(r'/oney/client\?task=Synthese&process=SyntheseMultiCompte&indexSelectionne=(?P\d+)') credit_home = URL(r'/site/s/detailcompte/detailcompte.html', CreditHome) credit_info = URL(r'/site/s/detailcompte/ongletdetailcompte.html', CreditAccountPage) credit_hist = URL(r'/site/s/detailcompte/exportoperations.html', CreditHistory) - last_hist = URL(r'/site/s/detailcompte/ongletdernieresoperations.html', LastHistoryPage) + last_hist = URL(r'/site/s/detailcompte/ongletdernieresoperations.html', LastHistoryPage) has_oney = False has_other = False @@ -57,9 +71,42 @@ class OneyBrowser(LoginBrowser): def do_login(self): self.session.cookies.clear() - self.login.go() + self.home_login.go(method="POST") + context_token = self.page.get_context_token() + assert context_token is not None, "Should not have context_token=None" - self.page.login(self.username, self.password) + self.context_init.go(params={'contextToken': context_token}) + success_url = self.page.get_success_url() + customer_session_id = self.page.get_customer_session_id() + + self.session.headers.update({'Client-id': self.page.get_client_id()}) + + # There is a VK on the website but it does not encode the password + self.login.go() + self.send_username.go(json={ + 'authentication_type': 'LIGHT', + 'authentication_factor': { + 'public_value': self.username, + 'type': 'IAD', + } + }) + + flow_id = self.page.get_flow_id() + + self.send_password.go(json={ + 'flow_id': flow_id, + 'step_type': 'IAD_ACCESS_CODE', + 'value': self.password, + }) + + self.page.check_error() + token = self.page.get_token() + + self.check_token.go(params={'token': token}) + self.location(success_url, params={ + 'token': token, + 'customer_session_id': customer_session_id, + }) if self.choice.is_here(): self.has_other = self.has_oney = True @@ -97,10 +144,16 @@ def get_accounts_list(self): if self.has_other: self.go_site('other') - self.credit_home.stay_or_go() - self.card_name = self.page.get_name() - self.credit_info.go() - accounts.append(self.page.get_account()) + for acc_id in self.page.get_accounts_ids(): + self.credit_home.go(data={'numeroCompte': acc_id}) + label = self.page.get_label() + if 'prêt' in label.lower(): + acc = self.page.get_loan() + else: + self.credit_info.go() + acc = self.page.get_account() + acc.label = label + accounts.append(acc) if self.has_oney: self.go_site('oney') @@ -133,7 +186,7 @@ def _build_hist_form(self, last_months=False): form['anneeFin'] = str(d.year) form['typeOpe'] = 'deux' - form['formatFichier'] = 'xls' # or pdf... great choice + form['formatFichier'] = 'xls' # or pdf... great choice return form @need_login @@ -148,8 +201,10 @@ def iter_history(self, account): for tr in self.page.iter_transactions(seen=set()): yield tr - elif account._site == 'other': - if self.last_hist.go().has_transactions(): + elif account._site == 'other' and account.type != Account.TYPE_LOAN: + self.credit_home.go(data={'numeroCompte': account.id}) + self.last_hist.go() + if self.page.has_transactions(): # transactions are missing from the xls from 2016 to today # so two requests are needed d = date.today() @@ -176,10 +231,12 @@ def iter_coming(self, account): for tr in self.page.iter_transactions(seen=set()): yield tr - elif account._site == 'other': - if self.last_hist.go().has_transactions(): + elif account._site == 'other' and account.type != Account.TYPE_LOAN: + self.credit_home.go(data={'numeroCompte': account.id}) + self.last_hist.go() + if self.page.has_transactions(): self.credit_hist.go(params=self._build_hist_form()) - d = date.today().replace(day=1) # TODO is it the right date? + d = date.today().replace(day=1) # TODO is it the right date? for tr in self.page.iter_history(): if new_date(tr.date) >= d: yield tr diff --git a/modules/oney/compat/weboob_capabilities_bank.py b/modules/oney/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/oney/compat/weboob_capabilities_bank.py +++ b/modules/oney/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/oney/compat/weboob_exceptions.py b/modules/oney/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/oney/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/oney/compat/weboob_tools_captcha_virtkeyboard.py b/modules/oney/compat/weboob_tools_captcha_virtkeyboard.py deleted file mode 100644 index acf6006884305eb2433141003ef74de233657197..0000000000000000000000000000000000000000 --- a/modules/oney/compat/weboob_tools_captcha_virtkeyboard.py +++ /dev/null @@ -1,203 +0,0 @@ - -import weboob.tools.captcha.virtkeyboard as OLD - -# can't import *, __all__ is incomplete... -for attr in dir(OLD): - globals()[attr] = getattr(OLD, attr) - - -try: - __all__ = OLD.__all__ -except AttributeError: - pass - - -class SimpleVirtualKeyboard(object): - """Handle a virtual keyboard where "keys" are distributed on a simple grid. - - Parameters: - :param cols: Column count of the grid - :type cols: int - :param rows: Row count of the grid - :type rows: int - :param image: File-like object to be used as data source - :type image: file - :param convert: Mode to which convert color of pixels, see - :meth:`Image.Image.convert` for more information - :param matching_symbols: symbol that match all case of image grid from left to right and top - to down, European reading way. - :type matching_symbols: iterable - :param matching_symbols_coords: dict mapping matching website symbols to their image coords - (x0, y0, x1, y1) on grid image from left to right and top to - down, European reading way. It's not symbols in the image. - :type matching_symbols_coords: dict[str:4-tuple(int)] - :param browser: Browser of weboob session. - Allow to dump tiles files in same directory than session folder - :type browser: obj(Browser) - - Attributes: - :attribute codesep: Output separator between matching symbols - :type codesep: str - :param margin: Useless image pixel to cut. - See :func:`cut_margin`. - :type margin: 4-tuple(int), same as HTML margin: (top, right, bottom, left). - or 2-tuple(int), (top = bottom, right = left), - or int, top = right = bottom = left - :attribute tile_margin: Useless tile pixel to cut. - See :func:`cut_margin`. - :attribute symbols: Association table between image symbols and md5s - :type symbols: dict[str:str] or dict[str:n-tuple(str)] - :attribute convert: Mode to which convert color of pixels, see - :meth:`Image.Image.convert` for more information - :attribute alter: Allow custom main image alteration. Then overwrite :func:`alter_image`. - :type alter: boolean - """ - - codesep = '' - margin = None - tile_margin = None - symbols = None - convert = None - - def __init__(self, file, cols, rows, matching_symbols=None, matching_symbols_coords=None, browser=None): - self.cols = cols - self.rows = rows - - # Needed even if init is overwrite - self.path = self.build_path(browser) - - # Get self.image - self.load_image(file, self.margin, self.convert) - - # Get self.tiles - self.get_tiles( matching_symbols=matching_symbols, - matching_symbols_coords=matching_symbols_coords) - - # Tiles processing - self.cut_tiles(self.tile_margin) - self.hash_md5_tiles() - - def build_path(self, browser=None): - if browser and browser.responses_dirname: - return browser.responses_dirname - else: - return tempfile.mkdtemp(prefix='weboob_session_') - - def load_image(self, file, margin=None, convert=None): - self.image = Image.open(file) - # Resize image if margin is given - if margin: - self.image = self.cut_margin(self.image, margin) - if convert: - self.image = self.image.convert(convert) - # Give possibility to alter image before get tiles, overwrite :func:`alter_image`. - self.alter_image() - self.width, self.height = self.image.size - - def alter_image(self): - pass - - def cut_margin(self, image, margin): - width, height = image.size - - # Verify the magin value format - if type(margin) is int: - margin = (margin, margin, margin, margin) - elif len(margin) == 2: - margin = (margin[0], margin[1], margin[0], margin[1]) - elif len(margin) == 4: - margin = margin - else: - assert (len(margin) == 3) & (len(margin) > 4), \ - "Margin format is wrong." - - assert ((margin[0] + margin[2]) < height) & ((margin[1] + margin[3]) < width), \ - "Margin is too high, there is not enough pixel to cut." - - image = image.crop((0 + margin[3], - 0 + margin[0], - width - margin[1], - height - margin[2] - )) - return image - - def get_tiles(self, matching_symbols=None, matching_symbols_coords=None): - self.tiles = [] - - # Tiles coords are given - if matching_symbols_coords: - for matching_symbol in matching_symbols_coords: - self.tiles.append(Tile( matching_symbol=matching_symbol, - coords=matching_symbols_coords[matching_symbol] - )) - return - - assert (not self.width%self.cols) & (not self.height%self.rows), \ - "Image width and height are not multiple of cols and rows. Please resize image with attribute `margin`." - - # Tiles coords aren't given, calculate them - self.tileW = self.width // self.cols - self.tileH = self.height // self.rows - - # Matching symbols aren't given, default value is range(columns*rows) - if not matching_symbols: - matching_symbols = ['%s' % i for i in range(self.cols*self.rows)] - - assert len(matching_symbols) == (self.cols*self.rows), \ - "Number of website matching symbols is not equal to the number of cases on the image." - - # Calculate tiles coords for each matching symbol from 1-dimension to 2-dimensions - for index, matching_symbol in enumerate(matching_symbols): - coords = self.get_tile_coords_in_grid(index) - self.tiles.append(Tile(matching_symbol=matching_symbol, coords=coords)) - - def get_tile_coords_in_grid(self, case_index): - # Get the top left pixel coords of the tile - x0 = (case_index % self.cols) * self.tileW - y0 = (case_index // self.cols) * self.tileH - - # Get the bottom right coords of the tile - x1 = x0 + self.tileW - y1 = y0 + self.tileH - - coords = (x0, y0, x1, y1) - return(coords) - - def cut_tiles(self, tile_margin=None): - for tile in self.tiles: - tile.image = self.image.crop(tile.coords) - - # Resize tile if margin is given - if tile_margin: - for tile in self.tiles: - tile.image = self.cut_margin(tile.image, tile_margin) - - def hash_md5_tiles(self): - for tile in self.tiles: - tile.md5 = hashlib.md5(tile.image.tobytes()).hexdigest() - - def dump_tiles(self, path): - for tile in self.tiles: - tile.image.save('{}/{}.png'.format(path, tile.md5)) - - def get_string_code(self, password): - word = [] - - for digit in password: - for tile in self.tiles: - if tile.md5 in self.symbols[digit]: - word.append(tile.matching_symbol) - break - else: - # Dump file only if the symbol is not found - self.dump_tiles(self.path) - raise VirtKeyboardError("Symbol '%s' not found; all symbol hashes are available in %s" - % (digit, self.path)) - return self.codesep.join(word) - -class SplitKeyboard(OLD.SplitKeyboard): - def convert(self, buffer): - return buffer - - def checksum(self, buffer): - return hashlib.md5(self.convert(buffer)).hexdigest() diff --git a/modules/oney/pages.py b/modules/oney/pages.py index 531c308cddd353b76be8aa9f8f123d68961fe608..069b328a75967853acaec2e6d4d15cc830faea86 100644 --- a/modules/oney/pages.py +++ b/modules/oney/pages.py @@ -20,21 +20,19 @@ from __future__ import unicode_literals import re -from io import BytesIO from decimal import Decimal import requests from .compat.weboob_capabilities_bank import Account from weboob.tools.capabilities.bank.transactions import FrenchTransaction, sorted_transactions -from .compat.weboob_tools_captcha_virtkeyboard import MappedVirtKeyboard, VirtKeyboardError -from weboob.browser.pages import HTMLPage, LoggedPage, pagination, XLSPage, PartialHTMLPage +from weboob.browser.pages import HTMLPage, LoggedPage, pagination, XLSPage, PartialHTMLPage, JsonPage from weboob.browser.elements import ListElement, ItemElement, method, DictElement from .compat.weboob_browser_filters_standard import Env, CleanDecimal, CleanText, Field, Format, Currency, Date from weboob.browser.filters.html import Attr from weboob.browser.filters.json import Dict -from weboob.exceptions import BrowserIncorrectPassword - +from .compat.weboob_exceptions import BrowserIncorrectPassword +from weboob.tools.compat import urlparse, parse_qsl class Transaction(FrenchTransaction): PATTERNS = [(re.compile(r'^(?PRetrait .*?) - traité le \d+/\d+$'), FrenchTransaction.TYPE_WITHDRAWAL), @@ -46,109 +44,40 @@ class Transaction(FrenchTransaction): (re.compile(r'^(?P.*?)(, taux de change de(.*)?)? - traité le( (\d+|/\d+)*$|$)'), FrenchTransaction.TYPE_CARD)] # some labels are really badly formed so the regex needs to be this nasty to catch all edge cases -class VirtKeyboard(MappedVirtKeyboard): - symbols = {'0': ('8664b9cdfa66b4c3a1ec99c35a2bf64b', - '9eb80c6e99410eaac32905b2c77e65e5', - '37717277dc2471c8a7bf37e2068a8f01', - '6e3a1ee9bae6f7fdcfc70784e4377b1a', - ), - '1': ('1f36986f9d27dde54ce5b08e8e285476', - '9d0aa7a0a2bbab4f2c01ef1e820cb3f1', - 'a4ef89b1c1741158cac0e20ccb0c06b8', - ), - '2': ('b560b0cce2ca74d3d499d73775152ab7', - 'aa7dfbd005c98c0bd1ebc4135cc196be', - 'de01032b31aa17a9f251f554fe1f765b', - ), - '3': ('d16e426e71fc29b1b55d0fbded99a473', - 'ce25f07ca5df54f6b7934512b65a4653', - ), - '4': ('19c68066e414e08d17c86fc5c4acc949', - 'c43354a7f7739508f76c538d5b3bce26', - '93e9066313113b7219f60fd9fd1c9ace', - ), - '5': ('4b9abf98e30a1475997ec770cbe5e702', - '2059b4aa95c7b3156b171255fa10bbdd', - '1eb285164fae7666c274203f7f429d87', - ), - '6': ('804be4171d61f9cc10e9978c43b1d2a0', - 'a41b091d4a11a318406a5a8bd3ed3837', - 'd51645d63e85c373cbd253b99634fe8c', - 'aa0e99bef5c3b7cc350b4b18b528f31b', - ), - '7': ('8adf951f4eea5f446f714214e101d555', - '7989c1f32113391d7855db195939be56', - '0c4411e5e8ed8732eb1c7ad834b03c37', - '2f8fb9d5aad4b2b17b5b5e5d056db159', - ), - '8': ('568135f3844213c30f2c7880be867d3d', - 'b1a92ad131b163b3e380cf7ed8a7bf53', - 'b0f68949d5af30f4821891062d80ef39', - 'b842758c339e32f41d75df741787137e', - ), - '9': ('a3750995c511ea1492ac244421109e77', - 'eeb3a8ba804f19380dfe94a91a37595b', - '7ff11918f2cbc8ff6191f878b9b7d56c', - 'cb24fe526094ea0a4917bde5d2bf02a1', - ), - } - - color=(0,0,0) - - def __init__(self, page): - img = page.doc.find("//img[@usemap='#cv']") - res = page.browser.open(img.attrib['src']) - MappedVirtKeyboard.__init__(self, BytesIO(res.content), page.doc, img, self.color, 'href', convert='RGB') - - self.check_symbols(self.symbols, page.browser.responses_dirname) - - def check_color(self, pixel): - for p in pixel: - if p >= 0xd5: - return False - - return True - - def get_symbol_coords(self, coords): - # strip borders - x1, y1, x2, y2 = coords - return MappedVirtKeyboard.get_symbol_coords(self, (x1+10, y1+10, x2-10, y2-10)) - - def get_symbol_code(self, md5sum_list): - for md5sum in md5sum_list: - try: - code = MappedVirtKeyboard.get_symbol_code(self,md5sum) - except VirtKeyboardError: - continue - else: - return ''.join(re.findall(r"'(\d+)'", code)[-2:]) - raise VirtKeyboardError('Symbol not found') - - def get_string_code(self, string): - code = '' - for c in string: - code += self.get_symbol_code(self.symbols[c]) - return code +class ContextInitPage(JsonPage): + def get_client_id(self): + return self.doc['context']['client_id'] + + def get_success_url(self): + return self.doc['context']['success_url'] + + def get_customer_session_id(self): + return self.doc['context']['customer_session_id'] + + +class SendUsernamePage(JsonPage): + def get_flow_id(self): + return self.doc['authenticationFlowInit']['flow_id'] + + +class SendPasswordPage(JsonPage): + def get_token(self): + return self.doc['completeAuthFlowStep']['token'] + + def check_error(self): + errors = self.doc['completeAuthFlowStep']['errors'] + if errors: + raise BrowserIncorrectPassword(errors[0]['label']) + + +class CheckTokenPage(JsonPage): + pass class LoginPage(HTMLPage): - def login(self, login, password): - if login.isdigit(): - vk = VirtKeyboard(self) - - form = self.get_form('//form[@id="formulaire-login"]') - code = vk.get_string_code(password) - try: - assert len(code)==10 - except AssertionError: - raise BrowserIncorrectPassword("Wrong number of character") - form['accordirect.identifiant'] = login - form['accordirect.code'] = code - else: - form = self.get_form('//form[@id="formulaire-login-email"]') - form['email.identifiant'] = login - form['email.code'] = password - form.submit() + def get_context_token(self): + parameters = dict(parse_qsl(urlparse(self.url).query)) + return parameters.get('context_token', None) class ChoicePage(LoggedPage, HTMLPage): @@ -239,15 +168,41 @@ def condition(self): def next_page(self): options = self.page.doc.xpath('//select[@id="periode"]//option[@selected="selected"]/preceding-sibling::option[1]') if options: - data = {'numReleve':options[0].values(),'task':'Releve','process':'Releve','eventid':'select','taskid':'','hrefid':'','hrefext':''} + data = { + 'numReleve': options[0].values(), + 'task': 'Releve', + 'process': 'Releve', + 'eventid': 'select', + 'taskid': '', + 'hrefid': '', + 'hrefext': '', + } return requests.Request("POST", self.page.url, data=data) class CreditHome(LoggedPage, HTMLPage): - def get_name(self): - # boulanger/auchan/etc. + def get_accounts_ids(self): + ids = [] + for elem in self.doc.xpath('//li[@id="menu-n2-mesproduits"]//a/@onclick'): + acc_id = re.search(r'afficherDetailCompte\(\'(\d+)\'\)', elem).group(1) + if acc_id not in ids: + ids.append(acc_id) + return ids + + def get_label(self): + # 'Ma carte Alinea', 'Mon Prêt Oney', ... return CleanText('//div[@class="conteneur"]/h1')(self.doc) + @method + class get_loan(ItemElement): + klass = Account + + obj_type = Account.TYPE_LOAN + obj__site = 'other' + obj_label = CleanText('//div[@class="conteneur"]/h1') + obj_number = obj_id = CleanText('//td[contains(text(), "Mon numéro de compte")]/following-sibling::td', replace=[(' ', '')]) + obj_coming = CleanDecimal.US('//td[strong[contains(text(), "Montant de la")]]/following-sibling::td/strong') + class CreditAccountPage(LoggedPage, HTMLPage): @method @@ -257,13 +212,10 @@ class get_account(ItemElement): obj_type = Account.TYPE_CHECKING obj__site = 'other' obj_balance = 0 - obj_id = CleanText('//tr[td[text()="Mon numéro de compte"]]/td[@class="droite"]', replace=[(' ', '')]) + obj_number = obj_id = CleanText('//tr[td[text()="Mon numéro de compte"]]/td[@class="droite"]', replace=[(' ', '')]) obj_coming = CleanDecimal('//div[@id="mod-paiementcomptant"]//tr[td[contains(text(),"débité le")]]/td[@class="droite"]', sign=lambda _: -1, default=0) obj_currency = Currency('//div[@id="mod-paiementcomptant"]//tr[td[starts-with(normalize-space(text()),"Montant disponible")]]/td[@class="droite"]') - def obj_label(self): - return self.page.browser.card_name - class CreditHistory(LoggedPage, XLSPage): # this history doesn't contain the monthly recharges, so the balance isn't consistent with the transactions? diff --git a/modules/onlinenet/browser.py b/modules/onlinenet/browser.py index cf8cbd69b48a963fa719882de8004a7bab5f7319..0a963216f16671b2994b609eb951f7f6c28e205b 100644 --- a/modules/onlinenet/browser.py +++ b/modules/onlinenet/browser.py @@ -18,8 +18,8 @@ # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, ProfilPage, DocumentsPage diff --git a/modules/onlinenet/compat/weboob_exceptions.py b/modules/onlinenet/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/onlinenet/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/openedx/browser.py b/modules/openedx/browser.py index 7719b999132403c829ca35613c0ab6cee9b8d90d..381aba24a1d9a8d5fa09cc7e69b11160243806c6 100644 --- a/modules/openedx/browser.py +++ b/modules/openedx/browser.py @@ -17,10 +17,10 @@ # You should have received a copy of the GNU Affero General Public License # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL, need_login +from weboob.browser.browsers import LoginBrowser, URL, need_login from weboob.browser.pages import RawPage, JsonPage, HTMLPage from weboob.browser.exceptions import ClientError -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword class LoginPage(HTMLPage): def login(self, username, password): diff --git a/modules/openedx/compat/__init__.py b/modules/openedx/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/openedx/compat/weboob_exceptions.py b/modules/openedx/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/openedx/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/orange/browser.py b/modules/orange/browser.py index 34c058be1cd53694886c18fb9e1327885f15bf12..782eefa991e3d0071ce04405f6f4cdcba27d16cd 100644 --- a/modules/orange/browser.py +++ b/modules/orange/browser.py @@ -21,8 +21,8 @@ from requests.exceptions import ConnectTimeout -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable, ActionNeeded, BrowserPasswordExpired +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable, ActionNeeded, BrowserPasswordExpired from .pages import LoginPage, BillsPage from .pages.login import ManageCGI, HomePage, PasswordPage from .pages.bills import SubscriptionsPage, BillsApiProPage, BillsApiParPage, ContractsPage diff --git a/modules/orange/compat/__init__.py b/modules/orange/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/orange/compat/weboob_exceptions.py b/modules/orange/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/orange/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ovh/browser.py b/modules/ovh/browser.py index 2eb5f3c319d3bad20bbf5b294dd09966feb07dd0..24f6cd4f63eca47c2bc6c046b4d8d9812138a1a1 100644 --- a/modules/ovh/browser.py +++ b/modules/ovh/browser.py @@ -20,8 +20,8 @@ from requests.exceptions import HTTPError, TooManyRedirects from datetime import datetime, timedelta -from weboob.browser import LoginBrowser, URL, need_login, StatesMixin -from weboob.exceptions import BrowserIncorrectPassword, BrowserQuestion +from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserQuestion from weboob.tools.value import Value from .pages import LoginPage, ProfilePage, BillsPage diff --git a/modules/ovh/compat/weboob_exceptions.py b/modules/ovh/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ovh/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/ovh/pages.py b/modules/ovh/pages.py index 66e58a0a49fd9d4a751bbfc875b6a3561b23fd49..0774d20686b2384f0e9b2245092ae6f85d6fbbd8 100644 --- a/modules/ovh/pages.py +++ b/modules/ovh/pages.py @@ -24,7 +24,7 @@ from weboob.browser.filters.html import Attr from weboob.browser.filters.json import Dict from weboob.browser.elements import ListElement, ItemElement, method, DictElement -from weboob.exceptions import ActionNeeded, AuthMethodNotImplemented +from .compat.weboob_exceptions import ActionNeeded, AuthMethodNotImplemented class LoginPage(HTMLPage): diff --git a/modules/pastealacon/browser.py b/modules/pastealacon/browser.py index f261e87e31d9d0c27cb9832011f54c939deac747..c2056b2d0124ecabef787767f1e2e3a5a9a06357 100644 --- a/modules/pastealacon/browser.py +++ b/modules/pastealacon/browser.py @@ -25,7 +25,7 @@ from weboob.browser.browsers import PagesBrowser from .compat.weboob_browser_url import URL from weboob.browser.elements import ItemElement, method -from weboob.exceptions import BrowserHTTPNotFound +from .compat.weboob_exceptions import BrowserHTTPNotFound class Spam(Exception): diff --git a/modules/pastealacon/compat/weboob_exceptions.py b/modules/pastealacon/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/pastealacon/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/pastebin/browser.py b/modules/pastebin/browser.py index 81d0c87bb75892179ad6c63674625998851e4659..e2a522046eb2046352c578c4bc26fe1d2d786dd6 100644 --- a/modules/pastebin/browser.py +++ b/modules/pastebin/browser.py @@ -26,7 +26,7 @@ from .compat.weboob_browser_filters_standard import Base, BrowserURL, CleanText, DateTime, Env, Field, Filter, FilterError, RawText from weboob.browser.pages import HTMLPage, RawPage from weboob.capabilities.paste import BasePaste, PasteNotFound -from weboob.exceptions import BrowserHTTPNotFound, BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserHTTPNotFound, BrowserIncorrectPassword, BrowserUnavailable class PastebinPaste(BasePaste): diff --git a/modules/pastebin/compat/weboob_exceptions.py b/modules/pastebin/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/pastebin/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/paypal/browser.py b/modules/paypal/browser.py index 6f565e3fa40d086fe8fc3c7fb7c3d1e096b53147..c8bff6f5ae2a182e5dde2ef44028be82a206d136 100644 --- a/modules/paypal/browser.py +++ b/modules/paypal/browser.py @@ -22,7 +22,7 @@ from dateutil.relativedelta import relativedelta from weboob.tools.compat import basestring -from weboob.exceptions import BrowserHTTPError, BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserHTTPError, BrowserIncorrectPassword, BrowserUnavailable from weboob.browser.browsers import LoginBrowser, need_login from .compat.weboob_browser_url import URL @@ -65,6 +65,7 @@ class Paypal(LoginBrowser): promo = URL('https://www.paypal.com/fr/webapps/mpp/clickthru/paypal-app-promo-2.*', '/fr/webapps/mpp/clickthru.*', PromoPage) account = URL('https://www.paypal.com/businessexp/money', + 'https://www.paypal.com/myaccount/money', 'https://www.paypal.com/webapps/business/money', AccountPage) pro_history = URL('https://\w+.paypal.com/businessexp/transactions/activity\?.*', ProHistoryPage) diff --git a/modules/paypal/compat/weboob_capabilities_bank.py b/modules/paypal/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/paypal/compat/weboob_capabilities_bank.py +++ b/modules/paypal/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/paypal/compat/weboob_exceptions.py b/modules/paypal/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/paypal/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/paypal/pages.py b/modules/paypal/pages.py index 0a83eb689c139a2921d6879b678dc2c70809e957..ff0444837be472ef84213b011ad0d40ad8c25de1 100644 --- a/modules/paypal/pages.py +++ b/modules/paypal/pages.py @@ -24,8 +24,8 @@ from weboob.tools.compat import unicode from .compat.weboob_capabilities_bank import Account -from weboob.capabilities.base import NotAvailable, Currency -from weboob.exceptions import BrowserUnavailable, ActionNeeded +from weboob.capabilities.base import NotAvailable +from .compat.weboob_exceptions import BrowserUnavailable, ActionNeeded from weboob.browser.exceptions import ServerError from weboob.browser.pages import HTMLPage, JsonPage, LoggedPage from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal @@ -93,7 +93,7 @@ def exec_decoder(mtc): cleaner_code = re.sub(r"%s\('([^']+)'\)" % re.escape(decoder_name), exec_decoder, cleaner_code) cookie = re.search(r'xppcts = (\w+);', cleaner_code).group(1) - sessionID = re.search(r"%s'([^']+)'" % re.escape("'&_sessionID='+encodeURIComponent("), cleaner_code).group(1) + sessionID = re.search(r"%s\w+\('([^']+)'" % re.escape("'&_sessionID='+encodeURIComponent("), cleaner_code).group(1) csrf = re.search(r"%s'([^']+)'" % re.escape("'&_csrf='+encodeURIComponent("), cleaner_code).group(1) key, value = re.findall(r"'(\w+)','(\w+)'", cleaner_code)[-1] @@ -106,9 +106,12 @@ def exec_decoder(mtc): get_token_func_declaration = "var " + get_token_func_name + "=" cleaner_code = cleaner_code.replace(get_token_func_declaration, get_token_func_declaration + "window.ADS_JS_TOKEN=") - # Remove the call to an infinite loop - loop_func_name = re.search(r"\(function\(\w+,\s?\w+,\s?\w+,\s?\w+\)\{var\s(\w+)=", cleaner_code).group(1) - cleaner_code = cleaner_code.replace(loop_func_name + "();", "") + # Paypal will try to create an infinite loop to make the parse fail, based on different + # weird things like a check of 'ind\\u0435xOf' vs 'indexOf'. + cleaner_code = cleaner_code.replace(r"'ind\\u0435xOf'", "'indexOf'") + # It also calls "data" which is undefined instead of a return (next call is an infinite + # recursive function). This should theorically not happen if window.domain is correctly set + # to "paypal.com" though. cleaner_code = cleaner_code.replace("data;", "return;") # Add a function that returns the token @@ -166,22 +169,18 @@ def get_account(self, _id): def get_accounts(self): accounts = {} - content = self.doc.xpath('//div[@id="moneyPage" or @id="MoneyPage"]')[0] + content = self.doc.xpath('//section[@id="contents"]')[0] # Multiple accounts - lines = content.xpath('(//div[@class="col-md-8 multi-currency"])[1]/ul/li') + lines = content.xpath('.//ul[@class="multiCurrency-container"][1]/li') for li in lines: account = Account() account.iban = NotAvailable account.type = Account.TYPE_CHECKING - currency_code = CleanText().filter((li.xpath('./span[@class="currencyUnit"]/span') or li.xpath('./span[1]'))[0]) - currency = Currency.get_currency(currency_code) - if not currency: - self.logger.warning('Unable to find currency %r', currency_code) - continue + currency = CleanText().filter(li.xpath('.//span[contains(@class, "multiCurrency-label_alignMiddle")]')[0]) account.id = currency account.currency = currency - account.balance = CleanDecimal(replace_dots=True).filter(li.xpath('./span[@class="amount"]/text()')) + account.balance = CleanDecimal(replace_dots=True).filter(li.xpath('.//span[contains(@class, "multiCurrency-label_right")]/text()')[0]) account.label = u'%s %s*' % (self.browser.username, account.currency) accounts[account.id] = account self.browser.account_currencies.append(account.currency) diff --git a/modules/phpbb/browser.py b/modules/phpbb/browser.py index 27ada9278cb15bc662fbc1e9d0ceab8d6e9016f0..0b9ea4f072043a2a12ef0d5ccf0758b014f18c6f 100644 --- a/modules/phpbb/browser.py +++ b/modules/phpbb/browser.py @@ -20,9 +20,9 @@ import re -from weboob.browser import URL, LoginBrowser, need_login +from weboob.browser.browsers import URL, LoginBrowser, need_login from weboob.capabilities.messages import CantSendMessage -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages.forum import ForumPage, PostingPage, TopicPage from .pages.index import LoginPage diff --git a/modules/phpbb/compat/__init__.py b/modules/phpbb/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/phpbb/compat/weboob_exceptions.py b/modules/phpbb/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/phpbb/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/pixabay/browser.py b/modules/pixabay/browser.py index c16b5130ff922677ec69e4fea0c67be9cd4633ca..145525955b9efe3af94b69c7cb51816035e1f8e8 100644 --- a/modules/pixabay/browser.py +++ b/modules/pixabay/browser.py @@ -18,10 +18,10 @@ # along with this weboob module. If not, see . import re -from weboob.browser import URL +from weboob.browser.browsers import URL from weboob.browser.browsers import LoginBrowser, need_login from weboob.capabilities.image import CapImage -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.compat import quote_plus from .pages import AccountPage, LoginPage, SearchAPI, ViewPage diff --git a/modules/pixabay/compat/__init__.py b/modules/pixabay/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/pixabay/compat/weboob_exceptions.py b/modules/pixabay/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/pixabay/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/playme/browser.py b/modules/playme/browser.py index 7c49b7d7078480ede363931f79965566f50fac5c..8fb3438695c974ab345b9ef84811344848c8890a 100644 --- a/modules/playme/browser.py +++ b/modules/playme/browser.py @@ -20,11 +20,11 @@ import re -from weboob.browser import DomainBrowser +from weboob.browser.browsers import DomainBrowser from weboob.browser.exceptions import ClientError from weboob.browser.pages import HTMLPage from weboob.browser.profiles import Profile -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.json import json diff --git a/modules/playme/compat/__init__.py b/modules/playme/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/playme/compat/weboob_exceptions.py b/modules/playme/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/playme/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/poivy/browser.py b/modules/poivy/browser.py index 3be886e87d10e86c29ff9212008fcfe2bf8ec50d..e9e3357812b1697546aeedbf039602604beab1f8 100644 --- a/modules/poivy/browser.py +++ b/modules/poivy/browser.py @@ -18,8 +18,8 @@ # along with this weboob module. If not, see . from weboob.tools.compat import basestring -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import HomePage, LoginPage, HistoryPage, BillsPage, ErrorPage diff --git a/modules/poivy/compat/weboob_exceptions.py b/modules/poivy/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/poivy/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/poivy/pages.py b/modules/poivy/pages.py index d95190020fc847069e9acc8cc596e112e0aeca12..05b0760d1a3bd691c602314b6c2ff8f92f51a1f2 100644 --- a/modules/poivy/pages.py +++ b/modules/poivy/pages.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this weboob module. If not, see . -from weboob.exceptions import BrowserBanned +from .compat.weboob_exceptions import BrowserBanned from weboob.browser.pages import HTMLPage, LoggedPage, pagination from weboob.browser.elements import ListElement, ItemElement, method from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, Field, DateTime, Format diff --git a/modules/pradoepargne/compat/weboob_capabilities_bank.py b/modules/pradoepargne/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/pradoepargne/compat/weboob_capabilities_bank.py +++ b/modules/pradoepargne/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/redmine/browser.py b/modules/redmine/browser.py index 0d9b644a161ac76920011254e66cf0f9d62ed468..acc2c53e52310a0112ba33c968ca01806d2c2f96 100644 --- a/modules/redmine/browser.py +++ b/modules/redmine/browser.py @@ -21,8 +21,8 @@ import lxml.html from weboob.capabilities.bugtracker import IssueError -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.compat import quote from .pages.index import LoginPage, IndexPage, MyPage, ProjectsPage diff --git a/modules/redmine/compat/__init__.py b/modules/redmine/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/redmine/compat/weboob_exceptions.py b/modules/redmine/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/redmine/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/redmine/module.py b/modules/redmine/module.py index 4a971cd950a536f8b710d671399dedbeb6b0df0f..7cfe20891acd874e7ee5321406949312e782ecc7 100644 --- a/modules/redmine/module.py +++ b/modules/redmine/module.py @@ -24,7 +24,7 @@ Query, Change from weboob.capabilities.collection import CapCollection, Collection, CollectionNotFound from weboob.tools.backend import Module, BackendConfig -from weboob.exceptions import BrowserHTTPNotFound +from .compat.weboob_exceptions import BrowserHTTPNotFound from weboob.tools.compat import basestring, unicode from weboob.tools.value import ValueBackendPassword, Value diff --git a/modules/regionsjob/compat/weboob_exceptions.py b/modules/regionsjob/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/regionsjob/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/regionsjob/pages.py b/modules/regionsjob/pages.py index ca20f176f5703ec5b37261dc6929ced9664c0236..1f8d8ea09b6516d458cef2622f73455652e45a5f 100644 --- a/modules/regionsjob/pages.py +++ b/modules/regionsjob/pages.py @@ -23,7 +23,7 @@ from weboob.browser.filters.html import CleanHTML, Link from weboob.browser.filters.json import Dict from weboob.capabilities.job import BaseJobAdvert -from weboob.exceptions import ParseError +from .compat.weboob_exceptions import ParseError from datetime import date, timedelta from weboob.capabilities import NotAvailable diff --git a/modules/relaiscolis/compat/__init__.py b/modules/relaiscolis/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/relaiscolis/compat/weboob_exceptions.py b/modules/relaiscolis/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/relaiscolis/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/relaiscolis/module.py b/modules/relaiscolis/module.py index d7c2f4cb17f8be29d9ab7ec19259d1155d1e442f..61a6d5a35fac8958ab50d8ec2354c4df781adb3b 100644 --- a/modules/relaiscolis/module.py +++ b/modules/relaiscolis/module.py @@ -23,7 +23,7 @@ from weboob.capabilities.base import NotAvailable from weboob.capabilities.parcel import CapParcel, Parcel, ParcelNotFound from weboob.tools.value import Value -from weboob.exceptions import BrowserQuestion +from .compat.weboob_exceptions import BrowserQuestion from .browser import RelaiscolisBrowser diff --git a/modules/residentadvisor/browser.py b/modules/residentadvisor/browser.py index ae8702e93e62deb245acefa9817743aebf85ef10..6f3c5b93945530a1f9b63f126b56a2adddca3db7 100644 --- a/modules/residentadvisor/browser.py +++ b/modules/residentadvisor/browser.py @@ -18,8 +18,8 @@ # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, EventPage, ListPage, SearchPage diff --git a/modules/residentadvisor/compat/weboob_exceptions.py b/modules/residentadvisor/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/residentadvisor/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/s2e/browser.py b/modules/s2e/browser.py index d2330f65fff1a943a758706b889327cd320f00ff..f8275c2c9844d192292eb6d06f04d78dd9ce18e1 100644 --- a/modules/s2e/browser.py +++ b/modules/s2e/browser.py @@ -23,7 +23,7 @@ import re from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded, NoAccountsException +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded, NoAccountsException from .compat.weboob_capabilities_bank import Investment from weboob.tools.capabilities.bank.investments import is_isin_valid @@ -165,7 +165,8 @@ def update_investments(self, investments): elif self.amfcode_sg.match(inv._link) or self.lyxorfunds.match(inv._link): # Esalia (Société Générale Épargne Salariale) or Lyxor investments # Not all sggestion-ede.com or lyxorfunds.com have available performances. - self.location(inv._link) + # For those requests to work in every case we need the headers from AccountsPage + self.location(inv._link, headers={'Referer': self.accounts.build(slug=self.SLUG)}) inv.performance_history = self.page.get_investment_performances() return investments diff --git a/modules/s2e/compat/weboob_capabilities_bank.py b/modules/s2e/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/s2e/compat/weboob_capabilities_bank.py +++ b/modules/s2e/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/s2e/compat/weboob_exceptions.py b/modules/s2e/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/s2e/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/s2e/pages.py b/modules/s2e/pages.py index 770b3049a4d18b51a44d647f27ccf8de771a425b..2f4e48445d9e78add3be8def9be1971a78f426cd 100644 --- a/modules/s2e/pages.py +++ b/modules/s2e/pages.py @@ -35,7 +35,7 @@ from .compat.weboob_capabilities_bank import Account, Investment, Pocket, Transaction from weboob.capabilities.base import NotAvailable, empty from .compat.weboob_tools_captcha_virtkeyboard import MappedVirtKeyboard -from weboob.exceptions import BrowserUnavailable, ActionNeeded, BrowserQuestion, BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserUnavailable, ActionNeeded, BrowserQuestion, BrowserIncorrectPassword from weboob.tools.value import Value from weboob.tools.compat import urljoin from weboob.tools.capabilities.bank.investments import is_isin_valid diff --git a/modules/sachsen/compat/weboob_exceptions.py b/modules/sachsen/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/sachsen/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/sachsen/pages.py b/modules/sachsen/pages.py index d0fe998fe06eb86eda40b1c40f95c62459f53f6b..fa723304d312e83029c747e71430afa88444ea3a 100644 --- a/modules/sachsen/pages.py +++ b/modules/sachsen/pages.py @@ -25,7 +25,7 @@ from weboob.capabilities.gauge import Gauge, GaugeMeasure, GaugeSensor from weboob.capabilities.base import NotAvailable, NotLoaded -from weboob.exceptions import ParseError +from .compat.weboob_exceptions import ParseError import re diff --git a/modules/societegenerale/browser.py b/modules/societegenerale/browser.py index d52924b51bf57bae7f9d1f044675c9c32c6c307a..af199c5de80dc9391eb39f51749d4a5b01edb3b0 100644 --- a/modules/societegenerale/browser.py +++ b/modules/societegenerale/browser.py @@ -25,7 +25,7 @@ from weboob.browser.browsers import LoginBrowser, URL, need_login, StatesMixin from weboob.capabilities.bill import Document, DocumentTypes -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded, BrowserUnavailable from .compat.weboob_capabilities_bank import Account, TransferBankError, AddRecipientStep, TransactionType, AccountOwnerType from weboob.capabilities.base import find_object, NotAvailable from weboob.browser.exceptions import BrowserHTTPNotFound, ClientError @@ -433,6 +433,10 @@ def new_recipient(self, recipient, **params): return self.end_oob_recipient(recipient, **params) self.add_recipient.go() + if self.main_page.is_here(): + self.page.handle_error() + assert False, 'Should not be on this page.' + self.page.post_iban(recipient) self.page.post_label(recipient) diff --git a/modules/societegenerale/compat/weboob_capabilities_bank.py b/modules/societegenerale/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/societegenerale/compat/weboob_capabilities_bank.py +++ b/modules/societegenerale/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/societegenerale/compat/weboob_exceptions.py b/modules/societegenerale/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/societegenerale/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/societegenerale/pages/accounts_list.py b/modules/societegenerale/pages/accounts_list.py index 1d54e5cf7d20434b41b7f181ffb81873ace931b3..3f7f7650cad6843495e65f5449128556479b3e36 100644 --- a/modules/societegenerale/pages/accounts_list.py +++ b/modules/societegenerale/pages/accounts_list.py @@ -40,7 +40,7 @@ ) from weboob.browser.filters.html import Link, TableCell, Attr from weboob.browser.pages import HTMLPage, XMLPage, JsonPage, LoggedPage, pagination -from weboob.exceptions import BrowserUnavailable, ActionNeeded, NoAccountsException +from .compat.weboob_exceptions import BrowserUnavailable, ActionNeeded, NoAccountsException def MyDecimal(*args, **kwargs): diff --git a/modules/societegenerale/pages/compat/weboob_capabilities_bank.py b/modules/societegenerale/pages/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/societegenerale/pages/compat/weboob_capabilities_bank.py +++ b/modules/societegenerale/pages/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/societegenerale/pages/compat/weboob_exceptions.py b/modules/societegenerale/pages/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/societegenerale/pages/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/societegenerale/pages/login.py b/modules/societegenerale/pages/login.py index 526cd3a1c6bc8ee77a05e30e3b2b519ce3b493da..46cffe08af8cd65d5be3d43a8e8df6aa2e0d13d4 100644 --- a/modules/societegenerale/pages/login.py +++ b/modules/societegenerale/pages/login.py @@ -24,10 +24,11 @@ import re from weboob.tools.json import json -from weboob.exceptions import BrowserUnavailable, BrowserPasswordExpired, ActionNeeded +from .compat.weboob_exceptions import BrowserUnavailable, BrowserPasswordExpired, ActionNeeded from weboob.browser.pages import HTMLPage, JsonPage from .compat.weboob_browser_filters_standard import CleanText from weboob.browser.filters.json import Dict +from .compat.weboob_capabilities_bank import AddRecipientBankError from .base import BasePage from ..captcha import Captcha, TileError @@ -105,6 +106,13 @@ def login(self, login, password): } self.browser.location(self.browser.absurl('/sec/vk/authent.json'), data=data) + def handle_error(self): + error_msg = CleanText('//span[@class="error_msg"]')(self.doc) + if error_msg: + # WARNING: this error occured during a recipient adding + # I don't know if it can happen at another time + raise AddRecipientBankError(message=error_msg) + class LoginPage(JsonPage): def get_error(self): diff --git a/modules/societegenerale/pages/transfer.py b/modules/societegenerale/pages/transfer.py index 852e1a8a7734df215e3001dacd989344b477403a..5f06f0076498e5dcb99919b9ee77d8a39caca6a9 100644 --- a/modules/societegenerale/pages/transfer.py +++ b/modules/societegenerale/pages/transfer.py @@ -33,7 +33,7 @@ from weboob.browser.filters.html import Link, ReplaceEntities from weboob.browser.filters.json import Dict from weboob.tools.json import json -from weboob.exceptions import BrowserUnavailable, ActionNeeded +from .compat.weboob_exceptions import BrowserUnavailable, ActionNeeded from .base import BasePage from .login import MainPage diff --git a/modules/societegenerale/sgpe/browser.py b/modules/societegenerale/sgpe/browser.py index b43a21172c16e7fc2009c7235da5570f9ed981d3..143f682f0beb7e2cd1cf4afa58dba60f476fe682 100644 --- a/modules/societegenerale/sgpe/browser.py +++ b/modules/societegenerale/sgpe/browser.py @@ -25,7 +25,7 @@ from weboob.browser.browsers import LoginBrowser, need_login, StatesMixin from .compat.weboob_browser_url import URL from weboob.browser.exceptions import ClientError -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded, NoAccountsException +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded, NoAccountsException from weboob.capabilities.base import find_object from .compat.weboob_capabilities_bank import ( AccountNotFound, RecipientNotFound, AddRecipientStep, AddRecipientBankError, @@ -472,7 +472,7 @@ def init_transfer(self, account, recipient, transfer): raise TransferBankError(message="La date d'exécution du virement est invalide. Elle doit correspondre aux horaires et aux dates d'ouvertures d'agence.") # update account and recipient info - recipient = find_object(self.iter_recipients(account), iban=recipient.iban, error=RecipientNotFound) + recipient = find_object(self.iter_recipients(account), iban=recipient.iban, id=recipient.id, error=RecipientNotFound) data = [ ('an_codeAction', 'C'), diff --git a/modules/societegenerale/sgpe/compat/weboob_capabilities_bank.py b/modules/societegenerale/sgpe/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/societegenerale/sgpe/compat/weboob_capabilities_bank.py +++ b/modules/societegenerale/sgpe/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/societegenerale/sgpe/compat/weboob_exceptions.py b/modules/societegenerale/sgpe/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/societegenerale/sgpe/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/societegenerale/sgpe/json_pages.py b/modules/societegenerale/sgpe/json_pages.py index b24c3e35ce2e8a1718f767aaacad30d10bc2bc54..130147159ad931e0775a1297844148c66308967b 100644 --- a/modules/societegenerale/sgpe/json_pages.py +++ b/modules/societegenerale/sgpe/json_pages.py @@ -31,7 +31,7 @@ from weboob.capabilities import NotAvailable from .compat.weboob_capabilities_bank import Account, Investment from weboob.capabilities.bill import Document, Subscription, DocumentTypes -from weboob.exceptions import ( +from .compat.weboob_exceptions import ( BrowserUnavailable, NoAccountsException, BrowserIncorrectPassword, BrowserPasswordExpired, AuthMethodNotImplemented, ) diff --git a/modules/societegenerale/sgpe/pages.py b/modules/societegenerale/sgpe/pages.py index d44ba83a88dcf06b9f96f8b5275303810c3f2ade..c6e9ad92a5f2b27aa56d6e3ca8d5120c344d28fe 100644 --- a/modules/societegenerale/sgpe/pages.py +++ b/modules/societegenerale/sgpe/pages.py @@ -33,7 +33,7 @@ from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.capabilities.profile import Profile, Person from weboob.capabilities.bill import Document, Subscription, DocumentTypes -from weboob.exceptions import ActionNeeded, BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import ActionNeeded, BrowserIncorrectPassword, BrowserUnavailable from weboob.tools.json import json from weboob.capabilities.base import NotAvailable diff --git a/modules/sogecartenet/compat/weboob_capabilities_bank.py b/modules/sogecartenet/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/sogecartenet/compat/weboob_capabilities_bank.py +++ b/modules/sogecartenet/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/sogecartenet/compat/weboob_exceptions.py b/modules/sogecartenet/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/sogecartenet/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/sogecartenet/pages.py b/modules/sogecartenet/pages.py index 25336dead5f2232c118cf7c48fc75c0d4e2ecac0..b6e962c235969b2a2dde810fcb660e4c312cba61 100644 --- a/modules/sogecartenet/pages.py +++ b/modules/sogecartenet/pages.py @@ -20,7 +20,7 @@ import requests from weboob.browser.pages import HTMLPage, CsvPage, pagination -from weboob.exceptions import BrowserIncorrectPassword, BrowserPasswordExpired, NoAccountsException +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserPasswordExpired, NoAccountsException from weboob.browser.elements import DictElement, ItemElement, method, TableElement from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, Date, Env from weboob.browser.filters.html import TableCell diff --git a/modules/spirica/browser.py b/modules/spirica/browser.py index cdd06a6e3649266fe83045081c6960c9e1c20883..76f006a36528b66d8c23b5251170d15c90dea6f8 100644 --- a/modules/spirica/browser.py +++ b/modules/spirica/browser.py @@ -19,8 +19,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, AccountsPage, DetailsPage, MaintenancePage diff --git a/modules/spirica/compat/weboob_capabilities_bank.py b/modules/spirica/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/spirica/compat/weboob_capabilities_bank.py +++ b/modules/spirica/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/spirica/compat/weboob_exceptions.py b/modules/spirica/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/spirica/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/spirica/pages.py b/modules/spirica/pages.py index 7df59217a3d3ad42a7c5356311084dc5f9bb937d..30b223f663bf16a5705cbdbe3b72651519cf77d4 100644 --- a/modules/spirica/pages.py +++ b/modules/spirica/pages.py @@ -28,7 +28,7 @@ from weboob.browser.filters.html import Attr, Link, TableCell from .compat.weboob_capabilities_bank import Account, Investment, Transaction from weboob.capabilities.base import NotAvailable, empty -from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserUnavailable, BrowserIncorrectPassword from weboob.tools.compat import urljoin @@ -199,7 +199,7 @@ def obj_portfolio_share(self): path = 'ancestor::tr/preceding-sibling::tr[@data-ri][position() = 1][1]/td[%d]' % (share_idx + 1) profile_share = MyDecimal(path)(self) - assert profile_share + assert not empty(profile_share), 'profile_share is %s' % profile_share profile_share = Eval(lambda x: x / 100, profile_share)(self) return inv_share * profile_share else: diff --git a/modules/suravenir/browser.py b/modules/suravenir/browser.py index f3c86327823f462011b11b52153634a4424efdf2..df746b13aa565dc1efafc2d1d67c3eab264f407b 100644 --- a/modules/suravenir/browser.py +++ b/modules/suravenir/browser.py @@ -20,8 +20,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages import LoginPage, AccountsList, InvestmentList, AccountHistory diff --git a/modules/suravenir/compat/weboob_capabilities_bank.py b/modules/suravenir/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/suravenir/compat/weboob_capabilities_bank.py +++ b/modules/suravenir/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/suravenir/compat/weboob_exceptions.py b/modules/suravenir/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/suravenir/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/t411/browser.py b/modules/t411/browser.py index 5da7429f31c809d8a7d96c9f204512e35e2e92b9..15bb4ee75d88a0c560806524bdf32aa6867d361e 100644 --- a/modules/t411/browser.py +++ b/modules/t411/browser.py @@ -23,7 +23,7 @@ from weboob.browser.browsers import LoginBrowser, need_login from .compat.weboob_browser_url import URL from weboob.browser.profiles import Wget -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages.index import HomePage, LoginPage from .pages.torrents import TorrentPage, SearchPage, DownloadPage diff --git a/modules/t411/compat/weboob_exceptions.py b/modules/t411/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/t411/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/tapatalk/compat/__init__.py b/modules/tapatalk/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/tapatalk/compat/weboob_exceptions.py b/modules/tapatalk/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/tapatalk/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/tapatalk/module.py b/modules/tapatalk/module.py index b161483f85e99782a87cc01bbc9797e63f6b8c72..5002485495a3f1dd2e4ae5c9c23c780d4136b906 100644 --- a/modules/tapatalk/module.py +++ b/modules/tapatalk/module.py @@ -28,7 +28,7 @@ from weboob.tools.backend import Module, BackendConfig from weboob.tools.value import Value, ValueBackendPassword from weboob.capabilities.messages import CapMessages, Thread, Message -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword __all__ = ['TapatalkModule'] diff --git a/modules/themisbanque/compat/weboob_capabilities_bank.py b/modules/themisbanque/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/themisbanque/compat/weboob_capabilities_bank.py +++ b/modules/themisbanque/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/themisbanque/compat/weboob_exceptions.py b/modules/themisbanque/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/themisbanque/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/themisbanque/pages.py b/modules/themisbanque/pages.py index 0908a29f1822096f07738f44dcd46e7d18fd4327..81a67fe509627ff558092b4410907390fae997d9 100644 --- a/modules/themisbanque/pages.py +++ b/modules/themisbanque/pages.py @@ -21,7 +21,7 @@ import re -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.browser.pages import LoggedPage, HTMLPage, pagination, PDFPage from weboob.browser.elements import method, ItemElement, TableElement from .compat.weboob_capabilities_bank import Account diff --git a/modules/ticketscesu/browser.py b/modules/ticketscesu/browser.py index 646b4629cae33fdd211e5a5ddfcbde757d83d87e..0b5f932496bb1b9b6f262acee579bdd5c06cc680 100644 --- a/modules/ticketscesu/browser.py +++ b/modules/ticketscesu/browser.py @@ -20,8 +20,8 @@ from __future__ import unicode_literals -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import ActionNeeded +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import ActionNeeded from .pages import AccountsPage, LoginPage, ProfilePage diff --git a/modules/ticketscesu/compat/weboob_capabilities_bank.py b/modules/ticketscesu/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/ticketscesu/compat/weboob_capabilities_bank.py +++ b/modules/ticketscesu/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/ticketscesu/compat/weboob_exceptions.py b/modules/ticketscesu/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/ticketscesu/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/tinder/browser.py b/modules/tinder/browser.py index a6e342b1e548b99283335de1492bf49a780104b1..825edd6ce9ffda566e34053b16f7746955361fa4 100644 --- a/modules/tinder/browser.py +++ b/modules/tinder/browser.py @@ -24,7 +24,7 @@ from .compat.weboob_browser_filters_standard import CleanText from weboob.browser.pages import HTMLPage from weboob.browser.profiles import IPhone, Android -from weboob.exceptions import BrowserIncorrectPassword, ParseError +from .compat.weboob_exceptions import BrowserIncorrectPassword, ParseError from weboob.tools.json import json diff --git a/modules/tinder/compat/weboob_exceptions.py b/modules/tinder/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/tinder/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/trainline/browser.py b/modules/trainline/browser.py index 0abdc8c679f5a08783636f6fe0e7d1b59b71f754..d912b9d8f8ab2305cf3dfa6d722b4421f7f5a19d 100644 --- a/modules/trainline/browser.py +++ b/modules/trainline/browser.py @@ -22,7 +22,7 @@ from dateutil.relativedelta import relativedelta from weboob.browser.browsers import APIBrowser -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .compat.weboob_browser_filters_standard import CleanDecimal, Date from weboob.browser.exceptions import ClientError from weboob.capabilities.bill import DocumentTypes, Bill, Subscription diff --git a/modules/trainline/compat/weboob_exceptions.py b/modules/trainline/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/trainline/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/twitter/browser.py b/modules/twitter/browser.py index 7143854069f836105fbd8cfd8e777943573bc64e..2ef0735ccd26b96872f47fdc86efc728828af0e5 100644 --- a/modules/twitter/browser.py +++ b/modules/twitter/browser.py @@ -17,8 +17,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this weboob module. If not, see . -from weboob.browser import LoginBrowser, URL, need_login -from weboob.exceptions import BrowserIncorrectPassword +from weboob.browser.browsers import LoginBrowser, URL, need_login +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.capabilities.messages import Message from .pages import LoginPage, LoginErrorPage, ThreadPage, Tweet, TrendsPage,\ TimelinePage, HomeTimelinePage, SearchTimelinePage, SearchPage diff --git a/modules/twitter/compat/weboob_exceptions.py b/modules/twitter/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/twitter/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/twitter/module.py b/modules/twitter/module.py index a6ef0684e4fd4737e84977c35e27303c7325afff..b646511eac162009d380e2581276702c77f2b60b 100644 --- a/modules/twitter/module.py +++ b/modules/twitter/module.py @@ -23,7 +23,7 @@ from weboob.capabilities.messages import CapMessages, Thread, CapMessagesPost from weboob.capabilities.collection import CapCollection, CollectionNotFound, Collection from weboob.capabilities.base import find_object -from weboob.exceptions import BrowserForbidden +from .compat.weboob_exceptions import BrowserForbidden from .browser import TwitterBrowser import itertools diff --git a/modules/vicsec/browser.py b/modules/vicsec/browser.py index 3e2a781ad55ab491c402fadcf0731d44e7169e5c..e34c1a291a7c802e4f43bee788f007e0655b68c9 100644 --- a/modules/vicsec/browser.py +++ b/modules/vicsec/browser.py @@ -23,11 +23,11 @@ from decimal import Decimal from itertools import chain -from weboob.browser import URL, LoginBrowser, need_login +from weboob.browser.browsers import URL, LoginBrowser, need_login from weboob.browser.pages import HTMLPage from weboob.capabilities.base import Currency from weboob.capabilities.shop import Item, Order, Payment -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.capabilities.bank.transactions import AmericanTransaction as AmTr from weboob.tools.compat import unicode diff --git a/modules/vicsec/compat/__init__.py b/modules/vicsec/compat/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/vicsec/compat/weboob_exceptions.py b/modules/vicsec/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/vicsec/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/vicseccard/browser.py b/modules/vicseccard/browser.py index 963e94bd8d3cab41312550bc19e24aa0762b24b5..4576c6c347a0cbc8b7ee5df0db05ab5e99f234ce 100644 --- a/modules/vicseccard/browser.py +++ b/modules/vicseccard/browser.py @@ -25,7 +25,7 @@ from weboob.browser.exceptions import ServerError from weboob.browser.pages import HTMLPage from .compat.weboob_capabilities_bank import Account, AccountNotFound, Transaction -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from weboob.tools.capabilities.bank.transactions import AmericanTransaction as AmTr from weboob.tools.compat import unicode diff --git a/modules/vicseccard/compat/weboob_capabilities_bank.py b/modules/vicseccard/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/vicseccard/compat/weboob_capabilities_bank.py +++ b/modules/vicseccard/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/vicseccard/compat/weboob_exceptions.py b/modules/vicseccard/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/vicseccard/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/vimeo/compat/weboob_exceptions.py b/modules/vimeo/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/vimeo/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/vimeo/pages.py b/modules/vimeo/pages.py index 77fde4c4a7336d333973b43b0a12e3e04870177a..24515a22977f863edeba8f69c88493e7f5095abe 100644 --- a/modules/vimeo/pages.py +++ b/modules/vimeo/pages.py @@ -22,7 +22,7 @@ from weboob.capabilities.image import Thumbnail from weboob.capabilities.collection import Collection -from weboob.exceptions import ParseError +from .compat.weboob_exceptions import ParseError from weboob.browser.elements import ItemElement, ListElement, method, DictElement from weboob.browser.pages import HTMLPage, pagination, JsonPage, XMLPage from .compat.weboob_browser_filters_standard import Regexp, Env, CleanText, DateTime, Duration, Field, BrowserURL diff --git a/modules/wellsfargo/browser.py b/modules/wellsfargo/browser.py index 7adbf75a52f9f21da99a786a47790474c090798e..6b9a3b59d1ba584e19057e7de7b478171d476411 100644 --- a/modules/wellsfargo/browser.py +++ b/modules/wellsfargo/browser.py @@ -26,7 +26,7 @@ from weboob.browser.browsers import URL, LoginBrowser, need_login from .compat.weboob_capabilities_bank import AccountNotFound -from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable +from .compat.weboob_exceptions import BrowserIncorrectPassword, BrowserUnavailable from weboob.tools.compat import unquote from .pages import (ActivityCardPage, ActivityCashPage, CodeRequestPage, CodeSubmitPage, DocumentsPage, LoggedInPage, diff --git a/modules/wellsfargo/compat/weboob_capabilities_bank.py b/modules/wellsfargo/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/wellsfargo/compat/weboob_capabilities_bank.py +++ b/modules/wellsfargo/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/wellsfargo/compat/weboob_exceptions.py b/modules/wellsfargo/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/wellsfargo/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/wiseed/compat/weboob_capabilities_bank.py b/modules/wiseed/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/wiseed/compat/weboob_capabilities_bank.py +++ b/modules/wiseed/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/wiseed/compat/weboob_exceptions.py b/modules/wiseed/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/wiseed/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/wiseed/pages.py b/modules/wiseed/pages.py index 7d83008dfa9f790e884cc15be452d410564dd802..9f32d20f91133cbad018cf2b26f8f4578c532ee1 100644 --- a/modules/wiseed/pages.py +++ b/modules/wiseed/pages.py @@ -23,7 +23,7 @@ from weboob.browser.filters.html import TableCell from .compat.weboob_browser_filters_standard import CleanText, CleanDecimal, Regexp from weboob.browser.elements import method, ItemElement, TableElement -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .compat.weboob_capabilities_bank import Investment from weboob.tools.capabilities.bank.investments import create_french_liquidity diff --git a/modules/yggtorrent/browser.py b/modules/yggtorrent/browser.py index 6b30f05308a41d5cadd0a46ea60be34ced819a48..7548ea0344b5b59afb8ba90f70a12770043df37d 100644 --- a/modules/yggtorrent/browser.py +++ b/modules/yggtorrent/browser.py @@ -22,7 +22,7 @@ from weboob.browser.browsers import LoginBrowser, need_login from .compat.weboob_browser_url import URL from weboob.browser.profiles import Wget -from weboob.exceptions import BrowserIncorrectPassword +from .compat.weboob_exceptions import BrowserIncorrectPassword from .pages.index import HomePage, LoginPage from .pages.torrents import TorrentPage, SearchPage, DownloadPage diff --git a/modules/yggtorrent/compat/weboob_exceptions.py b/modules/yggtorrent/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/yggtorrent/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/yomoni/browser.py b/modules/yomoni/browser.py index 1098de076df4d08332d07f956eb16aa141d22d5b..8b968546f775c714b816ed581123c9e4103a47a0 100644 --- a/modules/yomoni/browser.py +++ b/modules/yomoni/browser.py @@ -29,7 +29,8 @@ from weboob.browser.browsers import APIBrowser from weboob.browser.exceptions import ClientError from .compat.weboob_browser_filters_standard import CleanDecimal, Date -from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded +from weboob.browser.filters.html import ReplaceEntities +from .compat.weboob_exceptions import BrowserIncorrectPassword, ActionNeeded from .compat.weboob_capabilities_bank import Account, Investment, Transaction from weboob.capabilities.base import NotAvailable from weboob.tools.capabilities.bank.investments import is_isin_valid @@ -172,7 +173,13 @@ def iter_history(self, account): self.open('/user/%s/project/%s/activity' % (self.users['userId'], account._project_id), method="OPTIONS") for activity in [acc for acc in self.request('/user/%s/project/%s/activity' % (self.users['userId'], account._project_id), headers=self.request_headers)['activities'] \ if acc['details'] is not None]: - m = re.search(u'([\d\,]+)(?=[\s]+€|[\s]+euro)', activity['details']) + + m = re.search( + r'([\d\,]+)(?=[\s]+€|[\s]+euro)', + ReplaceEntities().filter(activity['details']), + flags=re.UNICODE, + ) + if "Souscription" not in activity['title'] and not m: continue diff --git a/modules/yomoni/compat/weboob_capabilities_bank.py b/modules/yomoni/compat/weboob_capabilities_bank.py index 4d5ed8d992042bb4e08df212e4191efa060bb2e8..74c6887039a2cc313874fbc311b465ac09b881d8 100644 --- a/modules/yomoni/compat/weboob_capabilities_bank.py +++ b/modules/yomoni/compat/weboob_capabilities_bank.py @@ -27,6 +27,10 @@ class RecipientInvalidOTP(AddRecipientError): code = 'invalidOTP' +class TransferInvalidOTP(TransferError): + code = 'invalidOTP' + + class AccountOwnership(object): """ Relationship between the credentials owner (PSU) and the account @@ -43,6 +47,6 @@ class AccountOwnership(object): try: - __all__ += ['AccountOwnership', 'RecipientInvalidOTP'] + __all__ += ['AccountOwnership', 'RecipientInvalidOTP', 'TransferInvalidOTP'] except NameError: pass diff --git a/modules/yomoni/compat/weboob_exceptions.py b/modules/yomoni/compat/weboob_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bdb1af470350d2e26f4114e9b0c6d552af7646f1 --- /dev/null +++ b/modules/yomoni/compat/weboob_exceptions.py @@ -0,0 +1,38 @@ + +import weboob.exceptions as OLD + +# can't import *, __all__ is incomplete... +for attr in dir(OLD): + globals()[attr] = getattr(OLD, attr) + + +try: + __all__ = OLD.__all__ +except AttributeError: + pass + + +class BrowserInteraction(Exception): + pass + + +class BrowserQuestion(BrowserInteraction): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields + + +class DecoupledValidation(BrowserInteraction): + def __init__(self, message='', resource=None, *values): + super(DecoupledValidation, self).__init__(*values) + self.message = message + self.resource = resource + + def __str__(self): + return self.message + + +class AppValidation(DecoupledValidation): + pass diff --git a/modules/youtube/module.py b/modules/youtube/module.py index 5adbe0b69f7a31d988df3404dc74dd6c49ac8a2c..93a111b8b7493b71a734bc91fd6c735f1b944b74 100644 --- a/modules/youtube/module.py +++ b/modules/youtube/module.py @@ -38,7 +38,7 @@ # so apiclient must be imported after from apiclient.discovery import build as ytbuild except ImportError: - raise ImportError("Please install python-googleapi") + raise ImportError("Please install python3-googleapi") __all__ = ['YoutubeModule']