Skip to content
Commits on Source (3)
# -*- coding: utf-8 -*-
# Copyright(C) 2012-2019 Budget-Insight
#
# 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 <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from .module import NetfincaModule
__all__ = ['NetfincaModule']
# -*- coding: utf-8 -*-
# Copyright(C) 2012-2019 Budget-Insight
#
# 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 <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from weboob.browser import LoginBrowser, URL
from weboob.exceptions import BrowserUnavailable
from .pages import InvestmentsPage, AccountsPage
class NetfincaBrowser(LoginBrowser):
accounts = URL(r'/netfinca-titres/servlet/com.netfinca.frontcr.synthesis.HomeSynthesis', AccountsPage)
investments = URL(r'/netfinca-titres/servlet/com.netfinca.frontcr.account.WalletVal\?nump=(?P<nump_id>.*)', InvestmentsPage)
def do_login(self):
raise BrowserUnavailable()
def iter_accounts(self):
self.accounts.stay_or_go()
return self.page.get_accounts()
def iter_investments(self, account):
self.accounts.stay_or_go()
nump_id = self.page.get_nump_id(account)
self.investments.go(nump_id=nump_id)
for invest in self.page.get_investments(account_currency=account.currency):
yield invest
liquidity = self.page.get_liquidity()
if liquidity:
yield liquidity
# -*- coding: utf-8 -*-
# Copyright(C) 2012-2019 Budget-Insight
#
# 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 <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from weboob.tools.backend import Module
from .browser import NetfincaBrowser
__all__ = ['NetfincaModule']
class NetfincaModule(Module):
NAME = 'netfinca'
DESCRIPTION = 'netfinca website'
MAINTAINER = 'Martin Sicot'
EMAIL = 'martin.sicot@budget-insight.com'
LICENSE = 'LGPLv3+'
VERSION = '1.4'
BROWSER = NetfincaBrowser
# -*- coding: utf-8 -*-
# Copyright(C) 2012-2019 Budget-Insight
#
# 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 <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
import re
from decimal import Decimal
import datetime
from weboob.browser.pages import HTMLPage, LoggedPage
from weboob.browser.elements import method, ItemElement, TableElement
from weboob.browser.filters.standard import CleanText, CleanDecimal, Currency
from weboob.browser.filters.html import TableCell, Attr
from weboob.capabilities.bank import Investment, Account
from weboob.capabilities.base import NotAvailable
from weboob.tools.capabilities.bank.investments import is_isin_valid, create_french_liquidity
class AccountsPage(LoggedPage, HTMLPage):
# UTF8 tag in the meta div, but that's wrong
ENCODING = 'iso-8859-1'
@method
class get_accounts(TableElement):
head_xpath = '//table[contains(@class,"tableau_comptes_details")]//th'
# There is not 'tbody' balise in the table, we have to get all tr and get out thead and tfoot ones
item_xpath = '//table[contains(@class,"tableau_comptes_details")]//tr[not(ancestor::thead) and not(ancestor::tfoot)]'
col_id = col_label = 'Comptes'
col_owner = 'Titulaire du compte'
col_balance = re.compile(r'.*Valorisation totale.*')
class item(ItemElement):
klass = Account
obj_owner = CleanText(TableCell('owner'))
obj_type = Account.TYPE_MARKET
def obj_id(self):
tablecell = TableCell('id')(self)[0]
id = tablecell.xpath('./div[position()=2]')
return CleanText().filter(id)
def obj_label(self):
tablecell = TableCell('label')(self)[0]
label = tablecell.xpath('./div[position()=1]')
return CleanText(label)(self)
def obj_balance(self):
tablecell = TableCell('balance')(self)[0]
b = tablecell.xpath('./span[@class="intraday"]')
balance = CleanDecimal(replace_dots=True).filter(b)
return Decimal(balance)
def obj_currency(self):
tablecell = TableCell('balance')(self)[0]
text = tablecell.xpath('./span/text()')[0]
regex = '[0-9,.]* (.*)'
currency = Currency().filter(re.search(regex, text).group(1))
return currency
def get_nump_id(self, account):
# Return an element needed in the request in order to access investments details
attr = Attr('//td[contains(@id, "wallet-%s")]' % account.id, 'onclick')(self.doc)
return re.search('([0-9]+:[0-9]+)', attr).group(1)
class InvestmentsPage(LoggedPage, HTMLPage):
# UTF8 tag in the meta div, but that's wrong
ENCODING = 'iso-8859-1'
@method
class get_investments(TableElement):
item_xpath = '//table[@id="tableValeurs"]/tbody/tr[starts-with(@id, "ContentDetPosInLine")]'
head_xpath = '//table[@id="tableValeurs"]/thead//th'
col_label = col_code = 'Valeur / Isin'
col_quantity = ['Quantité', 'Qté']
col_unitvalue = col_vdate = 'Cours'
col_valuation = ['Valorisation totale', 'Val. totale']
col_unitprice = 'Prix de revient'
col_diff = '+/- Value latente'
# Due to a bug in TableCell, column's number match with tdcell-1
# Had to use <following-sibling::td[position()=1]> each time in xpath to get the right cell
# @todo : Correct the TableCell class and this module
class item(ItemElement):
klass = Investment
obj_diff = CleanDecimal(TableCell('diff', colspan=True), replace_dots=True)
obj_unitprice = CleanDecimal(TableCell('unitprice', colspan=True), replace_dots=True)
obj_valuation = CleanDecimal(TableCell('valuation', colspan=True), replace_dots=True)
def obj_quantity(self):
tablecell = TableCell('quantity', colspan=True)(self)[0]
return CleanDecimal(tablecell.xpath('./span'), replace_dots=True)(self)
def obj_label(self):
tablecell = TableCell('label', colspan=True)(self)[0]
label = CleanText(tablecell.xpath('./following-sibling::td[@class=""]/div/a')[0])(self)
return label
def obj_code(self):
# We try to get the code from <a> div. If we didn't find code in url,
# we try to find it in the cell text
tablecell = TableCell('label', colspan=True)(self)[0]
# url find try
url = tablecell.xpath('./following-sibling::td[position()=1]/div/a')[0].attrib['href']
code_match = re.search(r'sico=([A-Z0-9]*)', url)
if code_match:
if is_isin_valid(code_match.group(1)):
return code_match.group(1)
# cell text find try
text = CleanText(tablecell.xpath('./following-sibling::td[position()=1]/div')[0])(self)
for code in text.split(' '):
if is_isin_valid(code):
return code
return NotAvailable
def obj_code_type(self):
if is_isin_valid(self.obj_code()):
return Investment.CODE_TYPE_ISIN
return NotAvailable
def obj_unitvalue(self):
currency, unitvalue = self.original_unitvalue()
if currency == self.env['account_currency']:
return unitvalue
return NotAvailable
def obj_original_currency(self):
currency, unitvalue = self.original_unitvalue()
if currency != self.env['account_currency']:
return currency
def obj_original_unitvalue(self):
currency, unitvalue = self.original_unitvalue()
if currency != self.env['account_currency']:
return unitvalue
def obj_vdate(self):
tablecell = TableCell('vdate', colspan=True)(self)[0]
vdate_scrapped = tablecell.xpath('./preceding-sibling::td[position()=1]//span/text()')[0]
# Scrapped date could be a schedule time (00:00) or a date (01/01/1970)
vdate = NotAvailable
if ':' in vdate_scrapped:
today = datetime.date.today()
h, m = [int(x) for x in vdate_scrapped.split(':')]
hour = datetime.time(hour=h, minute=m)
vdate = datetime.datetime.combine(today, hour)
elif '/' in vdate_scrapped:
vdate = datetime.datetime.strptime(vdate_scrapped, '%d/%m/%y')
return vdate
# extract unitvalue and currency
def original_unitvalue(self):
tablecell = TableCell('unitvalue', colspan=True)(self)[0]
text = tablecell.xpath('./text()')[0]
regex = '[0-9,]* (.*)'
currency = Currency().filter(re.search(regex, text).group(1))
return currency, CleanDecimal(replace_dots=True).filter(text)
def get_liquidity(self):
liquidity_element = self.doc.xpath('//td[contains(text(), "Solde espèces en euros")]//following-sibling::td[position()=1]')
assert len(liquidity_element) <= 1
if liquidity_element:
valuation = CleanDecimal(replace_dots=True).filter(liquidity_element[0])
return create_french_liquidity(valuation)