diff --git a/modules/suravenir/__init__.py b/modules/suravenir/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..30c74dee43b55f08edfee672a29371f0501f3a6b
--- /dev/null
+++ b/modules/suravenir/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2018 Arthur Huillet
+#
+# 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 SuravenirModule
+
+
+__all__ = ['SuravenirModule']
diff --git a/modules/suravenir/browser.py b/modules/suravenir/browser.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc1f03f91f80abea8711a7a6c493505f3409f157
--- /dev/null
+++ b/modules/suravenir/browser.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2018 Arthur Huillet
+#
+# 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, URL, need_login
+from weboob.exceptions import BrowserIncorrectPassword
+
+from .pages import LoginPage, AccountsList, InvestmentList, AccountHistory
+
+__all__ = ['Suravenir']
+
+
+class Suravenir(LoginBrowser):
+ BASEURL = 'https://www.previ-direct.com/'
+
+ login_page = URL('/web/eclient-(?P.*)', LoginPage)
+ accounts_page = URL('/group/eclient-(?P.*)/home$', AccountsList)
+ summary_page = URL('/group/eclient.*tabulateur.tabulation.resume', None)
+ investments_page = URL('/group/eclient.*tabulateur.tabulation.supports', InvestmentList)
+ history_page = URL('/group/eclient.*tabulateur.tabulation.operations', AccountHistory)
+
+# detail_link does not contain the type of page. the suffix for the pages are:
+# résumé
+# _portletespaceClientmonCompte_WAR_portletespaceclient_INSTANCE_Q4n1_tabName=detailsContrat.tabulateur.tabulation.resume
+# mes supports
+# _portletespaceClientmonCompte_WAR_portletespaceclient_INSTANCE_Q4n1_tabName=detailsContrat.tabulateur.tabulation.supports
+# mes opérations
+# _portletespaceClientmonCompte_WAR_portletespaceclient_INSTANCE_Q4n1_tabName=detailsContrat.tabulateur.tabulation.operations
+
+ def __init__(self, broker, *args, **kwargs):
+ self.broker = broker
+ LoginBrowser.__init__(self, *args, **kwargs)
+
+ def do_login(self):
+ self.login_page.stay_or_go(broker=self.broker).login(self.username, self.password)
+
+ if self.login_page.is_here():
+ raise BrowserIncorrectPassword()
+
+ @need_login
+ def get_accounts_list(self):
+ self.accounts_page.stay_or_go(broker=self.broker)
+ return self.page.get_contracts()
+
+ @need_login
+ def iter_investments(self, account):
+ self.location(account._detail_link + '&_portletespaceClientmonCompte_WAR_portletespaceclient_INSTANCE_Q4n1_tabName=detailsContrat.tabulateur.tabulation.supports')
+ return self.page.iter_investments()
+
+ @need_login
+ def iter_history(self, account):
+ self.location(account._detail_link + '&_portletespaceClientmonCompte_WAR_portletespaceclient_INSTANCE_Q4n1_tabName=detailsContrat.tabulateur.tabulation.operations')
+ return self.page.iter_history()
diff --git a/modules/suravenir/favicon.png b/modules/suravenir/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..f7bab69f065badde311dbf629af141c0cbcbe302
Binary files /dev/null and b/modules/suravenir/favicon.png differ
diff --git a/modules/suravenir/module.py b/modules/suravenir/module.py
new file mode 100644
index 0000000000000000000000000000000000000000..e811c4354ff3b5e2c37e87d68b4d21e8a1c7dafa
--- /dev/null
+++ b/modules/suravenir/module.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2018 Arthur Huillet
+#
+# 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.capabilities.base import find_object
+from weboob.capabilities.bank import CapBankWealth, AccountNotFound
+from weboob.tools.backend import Module, BackendConfig
+from weboob.tools.value import ValueBackendPassword
+
+from .browser import Suravenir
+
+
+__all__ = ['SuravenirModule']
+
+
+class SuravenirModule(Module, CapBankWealth):
+ NAME = 'suravenir'
+ MAINTAINER = 'Arthur Huillet'
+ EMAIL = 'arthur.huillet+weboob@free.fr'
+ VERSION = '1.4'
+ LICENSE = 'AGPLv3+'
+ DESCRIPTION = u'Assurance-vie Suravenir à travers différents courtiers (assurancevie.com, linxea, ...)'
+ CONFIG = BackendConfig(
+ ValueBackendPassword('broker', label='Courtier', choices=['assurancevie.com', 'linxea'], masked=False, required=True),
+ ValueBackendPassword('login', label='Identifiant', masked=False, required=True),
+ ValueBackendPassword('password', label='Mot de passe', required=True))
+ BROWSER = Suravenir
+
+ def create_default_browser(self):
+ return self.create_browser(
+ self.config['broker'].get(),
+ self.config['login'].get(),
+ self.config['password'].get()
+ )
+
+ def get_account(self, id):
+ return find_object(self.iter_accounts(), id=id, error=AccountNotFound)
+
+ def iter_accounts(self):
+ return self.browser.get_accounts_list()
+
+ def iter_coming(self, account):
+ raise NotImplementedError()
+
+ def iter_history(self, account):
+ return self.browser.iter_history(account)
+
+ def iter_investment(self, account):
+ return self.browser.iter_investments(account)
+
diff --git a/modules/suravenir/pages.py b/modules/suravenir/pages.py
new file mode 100644
index 0000000000000000000000000000000000000000..502709cc50b43e845f06cc3396c858b6d986d10f
--- /dev/null
+++ b/modules/suravenir/pages.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+# Copyright(C) 2018 Arthur Huillet
+#
+# 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.elements import ListElement, TableElement, ItemElement, method
+from weboob.browser.filters.html import AbsoluteLink, TableCell, Link
+from weboob.browser.filters.standard import CleanText, CleanDecimal, Date
+from weboob.capabilities import NotAvailable
+from weboob.capabilities.bank import Account, Investment, Transaction
+from weboob.browser.pages import HTMLPage, LoggedPage, pagination
+
+
+class LoginPage(HTMLPage):
+ def login(self, login, passwd):
+ form = self.get_form(id='_58_fm')
+ form['_58_login'] = login
+ form['_58_password'] = passwd
+ form.submit()
+
+
+class AccountsList(LoggedPage, HTMLPage):
+ @method
+ class get_contracts(ListElement):
+ item_xpath = '//tr[contains(@class, "results-row")]'
+
+ class item(ItemElement):
+ klass = Account
+
+ obj_label = CleanText('./td[contains(@class, "col-1")]/a')
+ obj_id = CleanText('./td[contains(@class, "col-2")]/a', replace=[(' ', '')])
+ obj_balance = CleanDecimal('./td[contains(@class, "col-3")]', replace_dots=True)
+ obj__detail_link = AbsoluteLink('./td[contains(@class, "col-2")]/a')
+ obj_type = Account.TYPE_LIFE_INSURANCE
+
+
+class InvestmentList(LoggedPage, HTMLPage):
+ @method
+ class iter_investments(TableElement):
+ head_xpath = '//thead[@class="table-columns"]/tr/th/text()'
+ item_xpath = '//tbody[@class="table-data"]/tr[contains(@class, "results-row")]'
+
+ col_ISIN = u"Code ISIN"
+ col_fund = u"Libellé support"
+ col_qty = u"Nb parts"
+ col_date = u"Date VL*"
+ col_unitvalue = u"VL*"
+ col_unitprice = u"Prix de revient"
+ col_perf = u"Perf."
+ col_valuation = u"Solde"
+
+ class item(ItemElement):
+ klass = Investment
+ obj_label = CleanText(TableCell("fund"))
+ obj_description = obj_label
+ obj_code = CleanText(TableCell("ISIN"), default=NotAvailable)
+ obj_code_type = Investment.CODE_TYPE_ISIN
+ obj_quantity = CleanDecimal(TableCell("qty"), replace_dots=True, default=NotAvailable)
+ obj_unitprice = CleanDecimal(TableCell("unitprice"), replace_dots=True, default=NotAvailable)
+ obj_unitvalue = CleanDecimal(TableCell("unitvalue"), replace_dots=True, default=NotAvailable)
+ obj_valuation = CleanDecimal(TableCell("valuation"), replace_dots=True, default=NotAvailable)
+ obj_vdate = Date(CleanText(TableCell("date")), dayfirst=True, default=NotAvailable)
+ obj_diff_percent = CleanDecimal(TableCell("perf"), replace_dots=True, default=NotAvailable)
+
+
+class AccountHistory(LoggedPage, HTMLPage):
+ @pagination
+ @method
+ class iter_history(TableElement):
+ next_page = Link('(//ul[contains(@class, "lfr-pagination-buttons")])[2]/li[@class=" next"]/a[contains(text(), "Suivant")]')
+ head_xpath = '//thead[@class="table-columns"]/tr/th/div/a/text()[1]'
+ item_xpath = '//tbody[@class="table-data"]/tr[contains(@class, "results-row")]'
+
+ col_date = u"Date de l'opération"
+ col_label = u"Libellé de l'opération"
+ col_amount = u"Montant"
+
+ class item(ItemElement):
+ klass = Transaction
+ obj_date = Date(CleanText(TableCell("date")), dayfirst=True, default=NotAvailable)
+ obj_raw = CleanText(TableCell("label"))
+ obj_label = CleanText(TableCell("label"))
+ obj_amount = CleanDecimal(TableCell("amount"), replace_dots=True, default=NotAvailable)
+
+ def obj__transaction_detail(self):
+ return AbsoluteLink((TableCell("label")(self)[0]).xpath('.//a'))
diff --git a/modules/suravenir/test.py b/modules/suravenir/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..84881f16197944b196370bb548c22fe934f3aafe
--- /dev/null
+++ b/modules/suravenir/test.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2018 Arthur Huillet
+#
+# 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.test import BackendTest
+
+
+class SuravenirTest(BackendTest):
+ MODULE = 'suravenir'
+ def test_suravenir(self):
+ l = list(self.backend.iter_accounts())
+ self.assertTrue(len(l) > 0)
+ a = l[0]
+ list(self.backend.iter_history(a))
diff --git a/tools/py3-compatible.modules b/tools/py3-compatible.modules
index 6da804819aefc080d8d0dd43adc55fcc2bd2d81c..e11893825d0247819dc70f4a2485ead12d340b7d 100644
--- a/tools/py3-compatible.modules
+++ b/tools/py3-compatible.modules
@@ -120,6 +120,7 @@ spirica
sprunge
sueurdemetal
supertoinette
+suravenir
tumblr
tvsubtitles
twitter