Commit e77c7951 authored by Nicolas Gattolin's avatar Nicolas Gattolin Committed by Romain Bignon

[amundi] py3 port

- usual forward compat, style fixes
- amundi website no longer use TLS1.0
parent 37824e31
......@@ -18,46 +18,34 @@
# along with weboob. If not, see <>.
import ssl
from .pages import LoginPage, AccountsPage, AccountHistoryPage
from weboob.browser import URL, LoginBrowser, need_login
from import json
from weboob.exceptions import BrowserIncorrectPassword
from weboob.browser.exceptions import ClientError
from .pages import LoginPage, AccountsPage, AccountHistoryPage
class AmundiBrowser(LoginBrowser):
TIMEOUT = 120.0
login = URL('/psf/authenticate', LoginPage)
authorize = URL('/psf/authorize', LoginPage)
accounts = URL('/psf/api/individu/positionFonds\?flagUrlFicheFonds=true&inclurePositionVide=false', AccountsPage)
account_history = URL('/psf/api/individu/operations\?valeurExterne=false&filtreStatutModeExclusion=false&statut=CPTA', AccountHistoryPage)
accounts = URL(r'/psf/api/individu/positionFonds\?flagUrlFicheFonds=true&inclurePositionVide=false', AccountsPage)
account_history = URL(r'/psf/api/individu/operations\?valeurExterne=false&filtreStatutModeExclusion=false&statut=CPTA', AccountHistoryPage)
def __init__(self, website, *args, **kwargs):
self.BASEURL = website
super(AmundiBrowser, self).__init__(*args, **kwargs)
def prepare_request(self, req):
Amundi uses TLS v1.0.
preq = super(AmundiBrowser, self).prepare_request(req)
conn = self.session.adapters['https://'].get_connection(preq.url)
conn.ssl_version = ssl.PROTOCOL_TLSv1
return preq
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(data=json.dumps({'username' : self.username, 'password' : self.password}), \
self.login.go(data=json.dumps({'username': self.username, 'password': self.password}),
headers={'Content-Type': 'application/json;charset=UTF-8'})
self.token = self.authorize.go().get_token()
except ClientError:
......@@ -65,13 +53,15 @@ class AmundiBrowser(LoginBrowser):
def iter_accounts(self):
return self.accounts.go(headers={'X-noee-authorization': ('noeprd %s' % self.token)}).iter_accounts()
return (self.accounts.go(headers={'X-noee-authorization': ('noeprd %s' % self.token)})
def iter_investments(self, account):
return self.accounts.go(headers={'X-noee-authorization': ('noeprd %s' % self.token)})\
return (self.accounts.go(headers={'X-noee-authorization': ('noeprd %s' % self.token)})
def iter_history(self, account):
return self.account_history.go(headers={'X-noee-authorization': ('noeprd %s' % self.token)}).iter_history(account=account)
return (self.account_history.go(headers={'X-noee-authorization': ('noeprd %s' % self.token)})
......@@ -35,59 +35,29 @@ class AmundiModule(Module, CapBankWealth):
EMAIL = ''
VERSION = '1.4'
CONFIG = BackendConfig(ValueBackendPassword('login', label='Identifiant', regexp='\d+', masked=False),
ValueBackendPassword('password', label=u"Mot de passe", regexp='\d+'),
CONFIG = BackendConfig(ValueBackendPassword('login', label='Identifiant', regexp=r'\d+', masked=False),
ValueBackendPassword('password', label=u"Mot de passe", regexp=r'\d+'),
Value('website', label='Type de compte', default='ee',
choices={'ee': 'Amundi Epargne Entreprise',
'tc': 'Amundi Tenue de Compte'}))
choices={'ee': 'Amundi Epargne Entreprise',
'tc': 'Amundi Tenue de Compte'}))
BROWSER = AmundiBrowser
def create_default_browser(self):
w = {'ee': '', 'tc': ''}
return self.create_browser(w[self.config['website'].get()], self.config['login'].get(), self.config['password'].get())
return self.create_browser(w[self.config['website'].get()], self.config['login'].get(),
def get_account(self, id):
Get an account from its ID.
:param id: ID of the account
:type id: :class:`str`
:rtype: :class:`Account`
:raises: :class:`AccountNotFound`
return find_object(self.iter_accounts(), id=id, error=AccountNotFound)
def iter_accounts(self):
Iter accounts.
:rtype: iter[:class:`Account`]
return self.browser.iter_accounts()
def iter_investment(self, account):
Iter investment of a market account
:param account: account to get investments
:type account: :class:`Account`
:rtype: iter[:class:`Investment`]
:raises: :class:`AccountNotFound`
for inv in self.browser.iter_investments(account):
if inv.valuation != 0:
yield inv
def iter_history(self, account):
Iter history of transactions on a specific account.
:param account: account to get history
:type account: :class:`Account`
:rtype: iter[:class:`Transaction`]
:raises: :class:`AccountNotFound`
return self.browser.iter_history(account)
......@@ -17,6 +17,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <>.
from __future__ import unicode_literals
from datetime import datetime
from weboob.browser.elements import ItemElement, method, DictElement
......@@ -60,7 +62,7 @@ class AccountsPage(LoggedPage, JsonPage):
# just the id is a kind of company id so it can be unique on a backend but not unique on multiple backends
return '%s_%s' % (Field('id')(self),
obj_currency = u"EUR"
obj_currency = 'EUR'
def obj_type(self):
return'typeDispositif')(self), Account.TYPE_LIFE_INSURANCE)
......@@ -68,7 +70,7 @@ class AccountsPage(LoggedPage, JsonPage):
def obj_label(self):
return Dict('libelleDispositif')(self).encode('iso-8859-2').decode('utf8')
except (UnicodeEncodeError, UnicodeDecodeError):
except UnicodeError:
return Dict('libelleDispositif')(self).encode('latin1').decode('utf8')
except UnicodeDecodeError:
......@@ -102,8 +104,8 @@ class AccountsPage(LoggedPage, JsonPage):
class AccountHistoryPage(LoggedPage, JsonPage):
def belongs(self, instructions, account):
for ins in instructions:
if 'nomDispositif' in ins and 'codeDispositif' in ins and '%s%s' % (ins['nomDispositif'], ins['codeDispositif']) == \
'%s%s' % (account.label,
if 'nomDispositif' in ins and 'codeDispositif' in ins and '%s%s' % (
ins['nomDispositif'], ins['codeDispositif']) == '%s%s' % (account.label,
return True
return False
......@@ -111,8 +113,9 @@ class AccountHistoryPage(LoggedPage, JsonPage):
amount = 0
for ins in instructions:
if 'nomDispositif' in ins and 'montantNet' in ins and 'codeDispositif' in ins and '%s%s' % (ins['nomDispositif'], ins['codeDispositif']) == \
'%s%s' % (account.label,
if ('nomDispositif' in ins and 'montantNet' in ins and 'codeDispositif' in ins
and '%s%s' % (ins['nomDispositif'], ins['codeDispositif'])
== '%s%s' % (account.label,
amount += ins['montantNet']
return CleanDecimal().filter(amount)
......@@ -125,21 +128,7 @@ class AccountHistoryPage(LoggedPage, JsonPage):
tr.amount = self.get_amount(hist['instructions'], account)
tr.rdate = datetime.strptime(hist['dateComptabilisation'].split('T')[0], '%Y-%m-%d') = tr.rdate
tr.label = hist['libelleOperation'] if 'libelleOperation' in hist else hist['libelleCommunication']
tr.label = hist.get('libelleOperation') or hist['libelleCommunication']
tr.type = Transaction.TYPE_UNKNOWN
# Bypassed because we don't have the ISIN code
# = []
# for ins in hist['instructions']:
# inv = Investment()
# inv.code = NotAvailable
# inv.label = ins['nomFonds']
# inv.description = ' '.join([ins['type'], ins['nomDispositif']])
# inv.vdate = datetime.strptime(ins.get('dateVlReel', ins.get('dateVlExecution')).split('T')[
# 0], '%Y-%m-%d')
# inv.valuation = Decimal(ins['montantNet'])
# inv.quantity = Decimal(ins['nombreDeParts'])
# inv.unitprice = inv.unitvalue = Decimal(ins['vlReel'])
yield tr
......@@ -11,6 +11,7 @@ amazon
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment