From d85b92783c3f331cf1c473569433ab263351ac24 Mon Sep 17 00:00:00 2001 From: Vincent A Date: Sun, 16 Sep 2018 13:52:37 +0200 Subject: [PATCH] [nalo] new CapBankWealth module --- modules/nalo/__init__.py | 26 ++++++++++ modules/nalo/browser.py | 77 +++++++++++++++++++++++++++++ modules/nalo/module.py | 54 +++++++++++++++++++++ modules/nalo/pages.py | 94 ++++++++++++++++++++++++++++++++++++ tools/py3-compatible.modules | 1 + 5 files changed, 252 insertions(+) create mode 100644 modules/nalo/__init__.py create mode 100644 modules/nalo/browser.py create mode 100644 modules/nalo/module.py create mode 100644 modules/nalo/pages.py diff --git a/modules/nalo/__init__.py b/modules/nalo/__init__.py new file mode 100644 index 0000000000..4b0d1c8d2b --- /dev/null +++ b/modules/nalo/__init__.py @@ -0,0 +1,26 @@ +# -*- 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 Affero 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 Affero General Public License for more details. +# +# 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 .module import NaloModule + + +__all__ = ['NaloModule'] diff --git a/modules/nalo/browser.py b/modules/nalo/browser.py new file mode 100644 index 0000000000..1b1aa5ec84 --- /dev/null +++ b/modules/nalo/browser.py @@ -0,0 +1,77 @@ +# -*- 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 Affero 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 Affero General Public License for more details. +# +# 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 weboob.browser import LoginBrowser, need_login, URL +from weboob.capabilities.bank import Investment + +from .pages import LoginPage, AccountsPage, AccountPage, InvestPage + + +class NaloBrowser(LoginBrowser): + BASEURL = 'https://nalo.fr' + + login = URL(r'/api/v1/login', LoginPage) + accounts = URL(r'/api/v1/projects/mine/without_details', AccountsPage) + history = URL(r'/api/v1/projects/(?P\d+)/history') + account = URL(r'/api/v1/projects/(?P\d+)', AccountPage) + invests = URL(r'https://app.nalo.fr/scripts/data/data.json', InvestPage) + + token = None + + def do_login(self): + self.login.go(json={ + 'email': self.username, + 'password': self.password, + 'userToken': False, + }) + self.token = self.page.get_token() + + def build_request(self, *args, **kwargs): + if 'json' in kwargs: + kwargs.setdefault('headers', {})['Accept'] = 'application/json' + if self.token: + kwargs.setdefault('headers', {})['Authorization'] = 'Token %s' % self.token + return super(NaloBrowser, self).build_request(*args, **kwargs) + + @need_login + def iter_accounts(self): + self.accounts.go() + return self.page.iter_accounts() + + @need_login + def iter_history(self, account): + self.history.go(id=account.id) + return self.page.iter_history() + + @need_login + def iter_investment(self, account): + self.account.go(id=account.id) + key = self.page.get_invest_key() + + self.invests.go() + data = self.page.get_invest(*key) + for item in data: + inv = Investment() + inv.code = item['isin'] + inv.label = item['name'] + inv.portfolio_share = item['share'] + inv.valuation = account.balance * inv.portfolio_share + yield inv diff --git a/modules/nalo/module.py b/modules/nalo/module.py new file mode 100644 index 0000000000..d1aefccbb5 --- /dev/null +++ b/modules/nalo/module.py @@ -0,0 +1,54 @@ +# -*- 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 Affero 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 Affero General Public License for more details. +# +# 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 weboob.tools.backend import Module, BackendConfig +from weboob.tools.value import ValueBackendPassword +from weboob.capabilities.bank import CapBankWealth + +from .browser import NaloBrowser + + +__all__ = ['NaloModule'] + + +class NaloModule(Module, CapBankWealth): + NAME = 'nalo' + DESCRIPTION = 'Nalo' + MAINTAINER = 'Vincent A' + EMAIL = 'dev@indigo.re' + LICENSE = 'AGPLv3+' + VERSION = '1.4' + + BROWSER = NaloBrowser + + CONFIG = BackendConfig( + ValueBackendPassword('login', label='E-mail', masked=False, regexp='.+@.+'), + ValueBackendPassword('password', label='Mot de passe'), + ) + + def create_default_browser(self): + return self.create_browser(self.config['login'].get(), self.config['password'].get()) + + def iter_accounts(self): + return self.browser.iter_accounts() + + def iter_investment(self, account): + return self.browser.iter_investment(account) diff --git a/modules/nalo/pages.py b/modules/nalo/pages.py new file mode 100644 index 0000000000..5f8b64fe07 --- /dev/null +++ b/modules/nalo/pages.py @@ -0,0 +1,94 @@ +# -*- 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 Affero 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 Affero General Public License for more details. +# +# 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 decimal import Decimal + +from weboob.browser.pages import LoggedPage, JsonPage +from weboob.browser.elements import method, DictElement, ItemElement +from weboob.browser.filters.json import Dict +from weboob.browser.filters.standard import Eval +from weboob.capabilities.bank import Account + + +def float_to_decimal(v): + return Decimal(str(v)) + + +class LoginPage(JsonPage): + def get_token(self): + return self.doc['detail']['token'] + + +class AccountsPage(LoggedPage, JsonPage): + ENCODING = 'utf-8' # chardet is shit + + @method + class iter_accounts(DictElement): + item_xpath = 'detail' + + class item(ItemElement): + klass = Account + + obj_id = Eval(str, Dict('id')) + obj_label = Dict('name') + obj_balance = Eval(float_to_decimal, Dict('current_value')) + obj_valuation_diff = Eval(float_to_decimal, Dict('absolute_performance')) + obj_currency = 'EUR' + obj_type = Account.TYPE_LIFE_INSURANCE + + +class AccountPage(LoggedPage, JsonPage): + def get_invest_key(self): + return self.doc['detail']['project_kind'], self.doc['detail']['risk_level'] + + def get_kind(self): + return self.doc['detail']['project_kind'] + + def get_risk(self): + return self.doc['detail']['risk_level'] + + +class HistoryPage(LoggedPage, JsonPage): + pass + + +class InvestPage(LoggedPage, JsonPage): + ENCODING = 'utf-8' + + def get_invest(self, kind, risk): + for pk in self.doc['portfolios']: + if pk['kind'] == kind: + break + else: + assert False + + for p in pk['target_portfolios']: + if p['risk_id'] == risk: + break + else: + assert False + + for line in p['lines']: + yield { + 'isin': line['isin'], + 'name': line['name'], + 'share': float_to_decimal(line['weight']) / 100, + } diff --git a/tools/py3-compatible.modules b/tools/py3-compatible.modules index 71b7e2d0a9..a773dba022 100644 --- a/tools/py3-compatible.modules +++ b/tools/py3-compatible.modules @@ -96,6 +96,7 @@ meteofrance monster myfoncia n26 +nalo nectarine nova oney -- GitLab