From 10dc63e4fa68fb761b5a6721114efd4a51931d4b Mon Sep 17 00:00:00 2001 From: Romain Bignon Date: Mon, 9 Apr 2012 11:21:28 +0200 Subject: [PATCH] support display of history and coming transactions --- modules/societegenerale/backend.py | 13 ++- modules/societegenerale/browser.py | 19 ++-- modules/societegenerale/pages/__init__.py | 6 +- .../societegenerale/pages/accounts_list.py | 94 ++++++++++++++++++- 4 files changed, 116 insertions(+), 16 deletions(-) diff --git a/modules/societegenerale/backend.py b/modules/societegenerale/backend.py index b7ad32b0..2c5f6d87 100644 --- a/modules/societegenerale/backend.py +++ b/modules/societegenerale/backend.py @@ -51,7 +51,6 @@ class SocieteGeneraleBackend(BaseBackend, ICapBank): yield account def get_account(self, _id): - print _id if not _id.isdigit(): raise AccountNotFound() with self.browser: @@ -60,3 +59,15 @@ class SocieteGeneraleBackend(BaseBackend, ICapBank): return account else: raise AccountNotFound() + + def iter_history(self, account): + with self.browser: + for tr in self.browser.iter_history(account._link_id): + if not tr._coming: + yield tr + + def iter_coming(self, account): + with self.browser: + for tr in self.browser.iter_history(account._link_id): + if tr._coming: + yield tr diff --git a/modules/societegenerale/browser.py b/modules/societegenerale/browser.py index 8cac7f3d..92c1bf08 100644 --- a/modules/societegenerale/browser.py +++ b/modules/societegenerale/browser.py @@ -19,7 +19,9 @@ from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword -from .pages import LoginPage, AccountsList, BadLoginPage + +from .pages.accounts_list import AccountsList, AccountHistory +from .pages.login import LoginPage, BadLoginPage __all__ = ['SocieteGenerale'] @@ -34,7 +36,7 @@ class SocieteGenerale(BaseBrowser): 'https://particuliers.societegenerale.fr/.*': LoginPage, 'https://.*.societegenerale.fr//acces/authlgn.html': BadLoginPage, '.*restitution/cns_listeprestation.html': AccountsList, -# '.*restitution/cns_detailCav.html.*': AccountHistory, + '.*restitution/cns_detailCav.html.*': AccountHistory, } def __init__(self, *args, **kwargs): @@ -79,12 +81,11 @@ class SocieteGenerale(BaseBrowser): return None - def get_history(self, account): - raise NotImplementedError() + def iter_history(self, url): + self.location(url) - #if not self.is_on_page(AccountHistory) or self.page.account.id != account.id: - # self.location(account._link_id) - #return self.page.get_operations() + assert self.is_on_page(AccountHistory) + #self.location(self.page.get_part_url()) - def transfer(self, from_id, to_id, amount, reason=None): - raise NotImplementedError() + #assert self.is_on_page(AccountHistoryPart) + return self.page.iter_transactions() diff --git a/modules/societegenerale/pages/__init__.py b/modules/societegenerale/pages/__init__.py index fb752cd3..96817e16 100644 --- a/modules/societegenerale/pages/__init__.py +++ b/modules/societegenerale/pages/__init__.py @@ -18,14 +18,12 @@ # along with weboob. If not, see . -from .accounts_list import AccountsList +from .accounts_list import AccountsList, AccountHistory from .login import LoginPage, BadLoginPage -class AccountPrelevement(AccountsList): - pass - __all__ = ['LoginPage', 'BadLoginPage', 'AccountsList', + 'AccountHistory', ] diff --git a/modules/societegenerale/pages/accounts_list.py b/modules/societegenerale/pages/accounts_list.py index 0ee17d07..85a4fd04 100644 --- a/modules/societegenerale/pages/accounts_list.py +++ b/modules/societegenerale/pages/accounts_list.py @@ -18,11 +18,19 @@ # along with weboob. If not, see . +from urlparse import parse_qs, urlparse +from lxml.etree import XML +from cStringIO import StringIO from decimal import Decimal import re from weboob.capabilities.bank import Account -from weboob.tools.browser import BasePage +from weboob.tools.capabilities.bank.transactions import FrenchTransaction +from weboob.tools.browser import BasePage, BrokenPageError + + +__all__ = ['AccountsList', 'AccountHistory'] + class AccountsList(BasePage): LINKID_REGEXP = re.compile(".*ch4=(\w+).*") @@ -38,7 +46,7 @@ class AccountsList(BasePage): for td in tr.getiterator('td'): if td.attrib.get('headers', '') == 'TypeCompte': a = td.find('a') - account.label = a.find("span").text + account.label = unicode(a.find("span").text) account._link_id = a.get('href', '') elif td.attrib.get('headers', '') == 'NumeroCompte': @@ -60,3 +68,85 @@ class AccountsList(BasePage): l.append(account) return l + +class Transaction(FrenchTransaction): + PATTERNS = [(re.compile(r'^CARTE \w+ RETRAIT DAB.* (?P
\d{2})/(?P\d{2}) (?P\d+)H(?P\d+) (?P.*)'), + FrenchTransaction.TYPE_WITHDRAWAL), + (re.compile(r'^(?PCARTE) \w+ (?P
\d{2})/(?P\d{2}) (?P.*)'), + FrenchTransaction.TYPE_CARD), + (re.compile(r'^(?P(COTISATION|PRELEVEMENT|TELEREGLEMENT|TIP)) (?P.*)'), + FrenchTransaction.TYPE_ORDER), + (re.compile(r'^(?PVIR(EMEN)?T? \w+) (?P.*)'), + FrenchTransaction.TYPE_TRANSFER), + (re.compile(r'^(CHEQUE) (?P.*)'), FrenchTransaction.TYPE_CHECK), + (re.compile(r'^(FRAIS) (?P.*)'), FrenchTransaction.TYPE_BANK), + (re.compile(r'^(?PECHEANCEPRET)(?P.*)'), + FrenchTransaction.TYPE_LOAN_PAYMENT), + (re.compile(r'^(?PREMISE CHEQUES)(?P.*)'), + FrenchTransaction.TYPE_DEPOSIT), + ] + +class AccountHistory(BasePage): + def get_part_url(self): + for script in self.document.getiterator('script'): + if script.text is None: + continue + + m = re.search('var listeEcrCavXmlUrl="(.*)";', script.text) + if m: + return m.group(1) + + raise BrokenPageError('Unable to find link to history part') + + def iter_transactions(self): + url = self.get_part_url() + while 1: + d = XML(self.browser.readurl(url)) + el = d.xpath('//dataBody')[0] + s = StringIO(el.text) + doc = self.browser.get_document(s) + + for tr in self._iter_transactions(doc): + yield tr + + el = d.xpath('//dataHeader')[0] + if int(el.find('suite').text) != 1: + return + + url = urlparse(url) + p = parse_qs(url.query) + url = self.browser.buildurl(url.path, n10_nrowcolor=0, + operationNumberPG=el.find('operationNumber').text, + operationTypePG=el.find('operationType').text, + pageNumberPG=el.find('pageNumber').text, + sign=p['sign'][0], + src=p['src'][0]) + + + def _iter_transactions(self, doc): + for i, tr in enumerate(self.parser.select(doc.getroot(), 'tr')): + t = Transaction(i) + t.parse(date=tr.xpath('./td[@headers="Date"]')[0].text, + raw=tr.attrib['title'].strip()) + t.set_amount(*reversed([el.text for el in tr.xpath('./td[@class="right"]')])) + t._coming = tr.xpath('./td[@headers="AVenir"]')[0].text + yield t + + +class _AccountHistory(BasePage): + def iter_operations(self): + for tr in self.document.xpath('//table[@id="tableCompte"]//tr'): + if len(tr.xpath('td[@class="debit"]')) == 0: + continue + + id = tr.find('td').find('input').attrib['value'] + op = Transaction(id) + op.parse(date=tr.findall('td')[1].text, + raw=tr.findall('td')[2].text.replace(u'\xa0', u'')) + + debit = tr.xpath('.//td[@class="debit"]')[0].text + credit = tr.xpath('.//td[@class="credit"]')[0].text + + op.set_amount(credit, debit) + + yield op