From d26bbc50a855fd5f5c9d8ec1bc8b700e083a4acb Mon Sep 17 00:00:00 2001 From: Romain Bignon Date: Wed, 21 Jan 2015 13:14:37 +0100 Subject: [PATCH] support of ASV accounts --- modules/ing/browser.py | 23 +++++++++-- modules/ing/pages/__init__.py | 4 +- modules/ing/pages/accounts_list.py | 64 ++++++++++++++++++++++++++---- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/modules/ing/browser.py b/modules/ing/browser.py index 68b7f47f..ddee1a55 100644 --- a/modules/ing/browser.py +++ b/modules/ing/browser.py @@ -18,13 +18,14 @@ # along with weboob. If not, see . import re import hashlib +import time from weboob.browser import LoginBrowser, URL, need_login from weboob.exceptions import BrowserIncorrectPassword, ParseError from weboob.capabilities.bank import Account, TransferError from .pages import AccountsList, LoginPage, TitrePage, TitreHistory,\ - TransferPage, TransferConfirmPage, BillsPage, StopPage + TransferPage, TransferConfirmPage, BillsPage, StopPage, TitreDetails __all__ = ['IngBrowser'] @@ -52,6 +53,7 @@ class IngBrowser(LoginBrowser): transferpage = URL('/protected/pages/cc/transfer/transferManagement.jsf', TransferPage) dotransferpage = URL('/general\?command=DisplayDoTransferCommand', TransferPage) valtransferpage = URL('/protected/pages/cc/transfer/create/transferCreateValidation.jsf', TransferConfirmPage) + titredetails = URL('/general\?command=display.*', TitreDetails) # CapBank-Market starttitre = URL('/general\?command=goToAccount&zone=COMPTE', TitrePage) titrepage = URL('https://bourse.ingdirect.fr/priv/portefeuille-TR.php', TitrePage) @@ -226,7 +228,17 @@ class IngBrowser(LoginBrowser): "javax.faces.ViewState": account._jid, "cptnbr": account._id } - self.accountspage.go(data=data) + + # On ASV pages, data maybe not available. + for i in range(4): + time.sleep(2**i) + self.accountspage.go(data=data) + + if not self.page.has_error(): + break + + if self.page.is_asv: + return self.starttitre.go() self.where = u"titre" @@ -237,12 +249,15 @@ class IngBrowser(LoginBrowser): raise NotImplementedError() self.go_investments(account) - self.titrerealtime.go() + if self.where == u'titre': + self.titrerealtime.go() return self.page.iter_investments() def get_history_titre(self, account): self.go_investments(account) - self.titrehistory.go() + + if self.where == u'titre': + self.titrehistory.go() return self.page.iter_history() ############# CapBill ############# diff --git a/modules/ing/pages/__init__.py b/modules/ing/pages/__init__.py index e246da20..25805a21 100644 --- a/modules/ing/pages/__init__.py +++ b/modules/ing/pages/__init__.py @@ -18,7 +18,7 @@ # along with weboob. If not, see . -from .accounts_list import AccountsList +from .accounts_list import AccountsList, TitreDetails from .login import LoginPage, StopPage from .transfer import TransferPage, TransferConfirmPage from .bills import BillsPage @@ -28,6 +28,6 @@ from .titre import TitrePage, TitreHistory class AccountPrelevement(AccountsList): pass -__all__ = ['AccountsList', 'LoginPage', +__all__ = ['AccountsList', 'LoginPage', 'TitreDetails', 'AccountPrelevement', 'TransferPage', 'TransferConfirmPage', 'BillsPage', 'StopPage', 'TitrePage', 'TitreHistory'] diff --git a/modules/ing/pages/accounts_list.py b/modules/ing/pages/accounts_list.py index 2831480a..ecdacfa9 100644 --- a/modules/ing/pages/accounts_list.py +++ b/modules/ing/pages/accounts_list.py @@ -20,13 +20,15 @@ from datetime import date, timedelta import datetime +from decimal import Decimal import re -from weboob.capabilities.bank import Account +from weboob.capabilities.bank import Account, Investment from weboob.capabilities.base import NotAvailable from weboob.browser.pages import HTMLPage, LoggedPage from weboob.browser.elements import ListElement, ItemElement, method -from weboob.browser.filters.standard import CleanText, CleanDecimal, Filter, Field, MultiFilter, Date, Lower +from weboob.browser.filters.standard import CleanText, CleanDecimal, Filter, Field, MultiFilter, \ + Date, Lower, Regexp, Async, AsyncLoad, Slugify from weboob.browser.filters.html import Attr from weboob.tools.capabilities.bank.transactions import FrenchTransaction @@ -57,7 +59,8 @@ class AddPref(MultiFilter): class AddType(Filter): types = {u'Courant': Account.TYPE_CHECKING, u'Livret A': Account.TYPE_SAVINGS, u'Orange': Account.TYPE_SAVINGS, u'Durable': Account.TYPE_SAVINGS, - u'Titres': Account.TYPE_MARKET, u'PEA': Account.TYPE_MARKET} + u'Titres': Account.TYPE_MARKET, u'PEA': Account.TYPE_MARKET, + u'Direct Vie': Account.TYPE_MARKET} def filter(self, label): for key, acc_type in self.types.items(): @@ -112,9 +115,10 @@ class INGCategory(Filter): class AccountsList(LoggedPage, HTMLPage): - def __init__(self, browser, response, *args, **kwargs): - super(AccountsList, self).__init__(browser, response, *args, **kwargs) - self.i = 0 + i = 0 + + def has_error(self): + return self.doc.xpath('//div[has-class("alert-warning")]') > 0 @method class get_list(ListElement): @@ -166,8 +170,7 @@ class AccountsList(LoggedPage, HTMLPage): item_xpath = '//table' def get_history_jid(self): - span = self.doc.xpath('//span[@id="index:panelASV"]') - if len(span) > 1: + if self.is_asv: # Assurance Vie, we do not support this kind of account. return None @@ -182,3 +185,48 @@ class AccountsList(LoggedPage, HTMLPage): nomore = self.doc.getroot().cssselect('.no-more-transactions') return (len(nomore) > 0) + + @property + def is_asv(self): + span = self.doc.xpath('//span[@id="index:panelASV"]') + return len(span) > 0 + + @method + class iter_investments(ListElement): + item_xpath = '//div[has-class("asv_fond")]' + + class item(ItemElement): + klass = Investment + + # ASV.popup('/general?command=displayAVEuroEpargne') + load_details = Attr('.//div[has-class("asv_fond_view")]//a', 'onclick') & Regexp(pattern="'(.*)'") & AsyncLoad + + obj_label = CleanText('.//span[has-class("asv_cat_lbl")]') + # XXX I would like to do that... but 1. CleanText doesn't deal with default 2. default values can't be filters yet + #obj_code = Async('details') & CleanText('//li[contains(text(), "Code ISIN")]/span') | (Field('label') & Format('XX%s', Slugify)) + obj_code = Async('details') & CleanText('//li[contains(text(), "Code ISIN")]/span') + obj_id = obj_code + obj_description = Async('details') & CleanText('//h5') + obj_quantity = CleanDecimal('.//dl[contains(dt/text(), "Nombre de parts")]/dd', replace_dots=True) + obj_unitvalue = CleanDecimal('.//dl[contains(dt/text(), "Valeur de part")]/dd', replace_dots=True) + obj_valuation = CleanDecimal('.//dl[has-class("ligne-montant")]/dd', replace_dots=True) + + def obj_unitprice(self): + if 'eurossima' in self.el.get('class'): + return self.obj.unitvalue + + percent = CleanDecimal('.//dl[has-class("ligne-pmvalue")]/dd', replace_dots=True)(self) + return (self.obj.unitvalue / (1 + percent/Decimal('100.0'))).quantize(Decimal('1.00')) + + def obj_diff(self): + return (self.obj.valuation - (self.obj.quantity * self.obj.unitprice)).quantize(Decimal('1.00')) + + def validate(self, obj): + if not obj.id: + obj.id = obj.code = 'XX' + Slugify(self.obj_label)(self) + + return True + + +class TitreDetails(LoggedPage, HTMLPage): + pass