From c18ba9072c481b1e54efc65d8a83f8345a480acf Mon Sep 17 00:00:00 2001 From: Romain Bignon Date: Fri, 26 Dec 2014 11:40:54 +0100 Subject: [PATCH] support full list of accounts (closes #1360) --- modules/banquepopulaire/browser.py | 19 +++++-- modules/banquepopulaire/pages.py | 85 ++++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 21 deletions(-) diff --git a/modules/banquepopulaire/browser.py b/modules/banquepopulaire/browser.py index e24030d7..4d66e9cd 100644 --- a/modules/banquepopulaire/browser.py +++ b/modules/banquepopulaire/browser.py @@ -22,7 +22,7 @@ import urllib from weboob.deprecated.browser import Browser, BrowserIncorrectPassword, BrokenPageError -from .pages import LoginPage, IndexPage, AccountsPage, CardsPage, TransactionsPage, \ +from .pages import LoginPage, IndexPage, AccountsPage, AccountsFullPage, CardsPage, TransactionsPage, \ UnavailablePage, RedirectPage, HomePage, Login2Page @@ -37,7 +37,8 @@ class BanquePopulaire(Browser): 'https://[^/]+/cyber/internet/StartTask.do\?taskInfoOID=mesComptes.*': AccountsPage, 'https://[^/]+/cyber/internet/StartTask.do\?taskInfoOID=maSyntheseGratuite.*': AccountsPage, 'https://[^/]+/cyber/internet/StartTask.do\?taskInfoOID=accueilSynthese.*': AccountsPage, - 'https://[^/]+/cyber/internet/ContinueTask.do\?.*dialogActionPerformed=EQUIPEMENT_COMPLET.*': AccountsPage, + 'https://[^/]+/cyber/internet/ContinueTask.do\?.*dialogActionPerformed=EQUIPEMENT_COMPLET.*': AccountsFullPage, + 'https://[^/]+/cyber/internet/ContinueTask.do\?.*dialogActionPerformed=VUE_COMPLETE.*': AccountsPage, 'https://[^/]+/cyber/internet/ContinueTask.do\?.*dialogActionPerformed=ENCOURS_COMPTE.*': CardsPage, 'https://[^/]+/cyber/internet/ContinueTask.do\?.*dialogActionPerformed=SELECTION_ENCOURS_CARTE.*': TransactionsPage, 'https://[^/]+/cyber/internet/ContinueTask.do\?.*dialogActionPerformed=SOLDE.*': TransactionsPage, @@ -109,14 +110,22 @@ class BanquePopulaire(Browser): for a in self.page.iter_accounts(next_pages): yield a - for next_page in next_pages: - if not self.is_on_page(AccountsPage): + while len(next_pages) > 0: + next_page = next_pages.pop() + + if not self.is_on_page(AccountsFullPage): self.go_on_accounts_list() + # If there is an action needed to go to the "next page", do it. + if 'prevAction' in next_page: + params = self.page.get_params() + params['dialogActionPerformed'] = next_page.pop('prevAction') + params['token'] = self.page.build_token(self.page.get_token()) + self.location('/cyber/internet/ContinueTask.do', urllib.urlencode(params)) next_page['token'] = self.page.build_token(self.page.get_token()) self.location('/cyber/internet/ContinueTask.do', urllib.urlencode(next_page)) - for a in self.page.iter_accounts(): + for a in self.page.iter_accounts(next_pages): yield a def get_account(self, id): diff --git a/modules/banquepopulaire/pages.py b/modules/banquepopulaire/pages.py index 59c2cbf8..42d87ce2 100644 --- a/modules/banquepopulaire/pages.py +++ b/modules/banquepopulaire/pages.py @@ -86,6 +86,24 @@ class BasePage(_BasePage): token += token[i] return token + def get_params(self): + params = {} + for field in self.document.xpath('//input'): + params[field.attrib['name']] = field.attrib.get('value', '') + return params + + def get_button_actions(self): + actions = {} + for script in self.document.xpath('//script'): + if script.text is None: + continue + + for id, action, strategy in re.findall(r'''attEvt\(window,"(?P[^"]+)","click","sab\('(?P[^']+)','(?P[^']+)'\);"''', script.text, re.MULTILINE): + actions[id] = {'dialogActionPerformed': action, + 'validationStrategy': strategy, + } + return actions + class RedirectPage(BasePage): """ @@ -276,14 +294,18 @@ class HomePage(BasePage): class AccountsPage(BasePage): - ACCOUNT_TYPES = {u'Mes comptes d\'épargne': Account.TYPE_SAVINGS, - u'Mon épargne': Account.TYPE_SAVINGS, - u'Placements': Account.TYPE_SAVINGS, - u'Mes comptes': Account.TYPE_CHECKING, - u'Comptes en euros': Account.TYPE_CHECKING, - u'Mes emprunts': Account.TYPE_LOAN, - u'Financements': Account.TYPE_LOAN, - u'Mes services': None, # ignore this kind of accounts (no bank ones) + ACCOUNT_TYPES = {u'Mes comptes d\'épargne': Account.TYPE_SAVINGS, + u'Mon épargne': Account.TYPE_SAVINGS, + u'Placements': Account.TYPE_SAVINGS, + u'Liste complète de mon épargne': Account.TYPE_SAVINGS, + u'Mes comptes': Account.TYPE_CHECKING, + u'Comptes en euros': Account.TYPE_CHECKING, + u'Liste complète de mes comptes': Account.TYPE_CHECKING, + u'Mes emprunts': Account.TYPE_LOAN, + u'Financements': Account.TYPE_LOAN, + u'Mes services': None, # ignore this kind of accounts (no bank ones) + u'Équipements': None, # ignore this kind of accounts (no bank ones) + u'Synthèse': None, # ignore this title } def is_error(self): @@ -307,12 +329,11 @@ class AccountsPage(BasePage): def iter_accounts(self, next_pages): account_type = Account.TYPE_UNKNOWN - params = {} - for field in self.document.xpath('//input'): - params[field.attrib['name']] = field.attrib.get('value', '') + params = self.get_params() + actions = self.get_button_actions() for div in self.document.getroot().cssselect('div.btit'): - if div.text is None: + if div.text in (None, u'Synthèse'): continue account_type = self.ACCOUNT_TYPES.get(div.text.strip(), Account.TYPE_UNKNOWN) @@ -320,6 +341,15 @@ class AccountsPage(BasePage): # ignore services accounts continue + # Go to the full list of this kind of account, if any. + btn = div.getparent().xpath('.//button/span[text()="Suite"]') + if len(btn) > 0: + btn = btn[0].getparent() + _params = params.copy() + _params.update(actions[btn.attrib['id']]) + next_pages.append(_params) + continue + currency = None for th in div.getnext().xpath('.//thead//th'): m = re.match('.*\((\w+)\)$', th.text) @@ -361,9 +391,24 @@ class AccountsPage(BasePage): if len(tds) >= 5 and len(tds[self.COL_COMING].xpath('.//a')) > 0: _params = account._params.copy() _params['dialogActionPerformed'] = 'ENCOURS_COMPTE' + + # If there is an action needed before going to the cards page, save it. + m = re.search('dialogActionPerformed=([\w_]+)', self.url) + if m: + _params['prevAction'] = m.group(1) next_pages.append(_params) yield account + # Needed to preserve navigation. + btn = self.document.xpath('.//button/span[text()="Retour"]') + if len(btn) > 0: + btn = btn[0].getparent() + _params = params.copy() + _params.update(actions[btn.attrib['id']]) + self.browser.openurl('/cyber/internet/ContinueTask.do', urllib.urlencode(_params)) + +class AccountsFullPage(AccountsPage): + pass class CardsPage(BasePage): COL_ID = 0 @@ -372,10 +417,8 @@ class CardsPage(BasePage): COL_DATE = 3 COL_AMOUNT = 4 - def iter_accounts(self): - params = {} - for field in self.document.xpath('//input'): - params[field.attrib['name']] = field.attrib.get('value', '') + def iter_accounts(self, next_pages): + params = self.get_params() account = None for tr in self.document.xpath('//table[@id="TabCtes"]/tbody/tr'): @@ -387,6 +430,7 @@ class CardsPage(BasePage): yield account account = Account() account.id = id.replace(' ', '') + account.type = Account.TYPE_CARD account.balance = account.coming = Decimal('0') account._next_debit = datetime.date.today() account._prev_debit = datetime.date(2000,1,1) @@ -426,6 +470,15 @@ class CardsPage(BasePage): if account is not None: yield account + # Needed to preserve navigation. + btn = self.document.xpath('.//button/span[text()="Retour"]') + if len(btn) > 0: + btn = btn[0].getparent() + actions = self.get_button_actions() + _params = params.copy() + _params.update(actions[btn.attrib['id']]) + self.browser.openurl('/cyber/internet/ContinueTask.do', urllib.urlencode(_params)) + class Transaction(FrenchTransaction): PATTERNS = [(re.compile('^RET DAB (?P.*?) RETRAIT (DU|LE) (?P
\d{2})(?P\d{2})(?P\d+).*'),