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']