rewrite with browser2

This commit is contained in:
Romain Bignon 2014-04-30 08:27:48 +02:00
commit d1fc1888c6
3 changed files with 89 additions and 109 deletions

View file

@ -18,6 +18,7 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.capabilities.base import find_object
from weboob.capabilities.bank import ICapBank, AccountNotFound
from weboob.tools.backend import BaseBackend, BackendConfig
from weboob.tools.value import ValueBackendPassword
@ -45,18 +46,10 @@ class BanqueAccordBackend(BaseBackend, ICapBank):
self.config['password'].get())
def iter_accounts(self):
with self.browser:
return self.browser.get_accounts_list()
return self.browser.get_accounts_list()
def get_account(self, _id):
with self.browser:
account = self.browser.get_account(_id)
if account:
return account
else:
raise AccountNotFound()
return find_object(self.browser.get_accounts_list(), id=_id, error=AccountNotFound)
def iter_history(self, account):
with self.browser:
return self.browser.iter_history(account)
return self.browser.iter_history(account)

View file

@ -18,9 +18,8 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import urllib
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
from weboob.tools.browser2 import LoginBrowser, need_login, URL
from weboob.tools.browser import BrowserIncorrectPassword
from .pages import LoginPage, IndexPage, AccountsPage, OperationsPage
@ -28,80 +27,53 @@ from .pages import LoginPage, IndexPage, AccountsPage, OperationsPage
__all__ = ['BanqueAccordBrowser']
class BanqueAccordBrowser(BaseBrowser):
PROTOCOL = 'https'
DOMAIN = 'www.banque-accord.fr'
ENCODING = None
class BanqueAccordBrowser(LoginBrowser):
BASEURL = 'https://www.banque-accord.fr/site/s/'
TIMEOUT = 20.0
PAGES = {
'https://www.banque-accord.fr/site/s/login/login.html': LoginPage,
'https://www.banque-accord.fr/site/s/detailcompte/detailcompte.html': IndexPage,
'https://www.banque-accord.fr/site/s/detailcompte/ongletdetailcompte.html': AccountsPage,
'https://www.banque-accord.fr/site/s/detailcompte/ongletdernieresoperations.html': OperationsPage,
}
login = URL('login/login.html', LoginPage)
index = URL('detailcompte/detailcompte.html', IndexPage)
accounts = URL('detailcompte/ongletdetailcompte.html', AccountsPage)
operations = URL('detailcompte/ongletdernieresoperations.html', OperationsPage)
def is_logged(self):
return self.page is not None and not self.is_on_page(LoginPage)
def home(self):
if self.is_logged():
self.location('/site/s/detailcompte/detailcompte.html')
else:
self.login()
def login(self):
def do_login(self):
"""
Attempt to log in.
Note: this method does nothing if we are already logged in.
"""
assert isinstance(self.username, basestring)
assert isinstance(self.password, basestring)
if self.is_logged():
return
self.location('/site/s/login/login.html', no_login=True)
assert self.is_on_page(LoginPage)
self.login.go()
self.page.login(self.username, self.password)
if not self.is_logged():
if not self.index.is_here():
raise BrowserIncorrectPassword()
@need_login
def get_accounts_list(self):
if not self.is_on_page(IndexPage):
self.location('https://www.banque-accord.fr/site/s/detailcompte/detailcompte.html')
self.index.stay_or_go()
for a in self.page.get_list():
post = {'numeroCompte': a.id,}
self.location('/site/s/detailcompte/detailcompte.html', urllib.urlencode(post))
self.index.go(data=post)
a.balance = self.page.get_loan_balance()
if a.balance is not None:
a.type = a.TYPE_LOAN
else:
self.location('/site/s/detailcompte/ongletdetailcompte.html')
self.accounts.go()
a.balance = self.page.get_balance()
a.type = a.TYPE_CARD
yield a
def get_account(self, id):
assert isinstance(id, basestring)
if not self.is_on_page(IndexPage):
self.home()
for a in self.get_accounts_list():
if a.id == id:
return a
return None
@need_login
def iter_history(self, account):
if account.type != account.TYPE_CARD:
return iter([])
post = {'numeroCompte': account.id}
self.location('/site/s/detailcompte/detailcompte.html', urllib.urlencode(post))
self.location('/site/s/detailcompte/ongletdernieresoperations.html')
self.index.go(data=post)
self.operations.go()
assert self.is_on_page(OperationsPage)
return self.page.get_history()

View file

@ -18,11 +18,13 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from decimal import Decimal
from decimal import Decimal, InvalidOperation
import re
from cStringIO import StringIO
from weboob.capabilities.bank import Account
from weboob.tools.browser import BasePage, BrokenPageError
from weboob.tools.browser2.page import HTMLPage, method, ListElement, ItemElement, LoggedPage
from weboob.tools.browser2.filters import ParseError, CleanText, Regexp, Attr, CleanDecimal, Env
from weboob.tools.captcha.virtkeyboard import MappedVirtKeyboard, VirtKeyboardError
from weboob.tools.capabilities.bank.transactions import FrenchTransaction
@ -46,9 +48,9 @@ class VirtKeyboard(MappedVirtKeyboard):
color=(0,0,0)
def __init__(self, page):
img = page.document.find("//img[@usemap='#cv']")
img_file = page.browser.openurl(img.attrib['src'])
MappedVirtKeyboard.__init__(self, img_file, page.document, img, self.color, 'href', convert='RGB')
img = page.doc.find("//img[@usemap='#cv']")
res = page.browser.open(img.attrib['src'])
MappedVirtKeyboard.__init__(self, StringIO(res.content), page.doc, img, self.color, 'href', convert='RGB')
self.check_symbols(self.symbols, page.browser.responses_dirname)
@ -70,7 +72,7 @@ class VirtKeyboard(MappedVirtKeyboard):
except VirtKeyboardError:
continue
else:
return ''.join(re.findall("'(\d+)'", code)[-2:])
return ''.join(re.findall(r"'(\d+)'", code)[-2:])
raise VirtKeyboardError('Symbol not found')
def get_string_code(self, string):
@ -79,39 +81,44 @@ class VirtKeyboard(MappedVirtKeyboard):
code += self.get_symbol_code(self.symbols[c])
return code
class LoginPage(BasePage):
class LoginPage(HTMLPage):
def login(self, login, password):
vk = VirtKeyboard(self)
form = self.document.xpath('//form[@id="formulaire-login"]')[0]
form = self.get_form('//form[@id="formulaire-login"]')
code = vk.get_string_code(password)
assert len(code)==10, BrokenPageError("Wrong number of character.")
self.browser.location(self.browser.buildurl(form.attrib['action'], identifiant=login, code=code), no_login=True)
assert len(code)==10, ParseError("Wrong number of character.")
form['identifiant'] = login
form['code'] = code
form.submit()
class IndexPage(BasePage):
def get_list(self):
for line in self.document.xpath('//li[@id="menu-n2-mesproduits"]//li//a'):
if line.get('onclick') is None:
continue
account = Account()
account.id = line.get('onclick').split("'")[1]
account.label = self.parser.tocleanstring(line)
yield account
class IndexPage(LoggedPage, HTMLPage):
@method
class get_list(ListElement):
item_xpath = '//li[@id="menu-n2-mesproduits"]//li//a'
class item(ItemElement):
klass = Account
obj_id = Regexp(Attr('.', 'onclick'), r"^[^']+'([^']+)'.*", r"\1")
obj_label = CleanText('.')
def condition(self):
return self.el.get('onclick') is not None
def get_loan_balance(self):
xpath = '//table//td/strong[contains(text(), "Montant emprunt")]/../../td[2]'
try:
return - Decimal(FrenchTransaction.clean_amount(self.parser.tocleanstring(self.document.xpath(xpath)[0])))
except IndexError:
return - CleanDecimal(xpath, replace_dots=False)(self.doc)
except InvalidOperation:
return None
def get_card_name(self):
return self.parser.tocleanstring(self.document.xpath('//h1')[0])
return CleanText('//h1[1]')(self.doc)
class AccountsPage(BasePage):
class AccountsPage(LoggedPage, HTMLPage):
def get_balance(self):
balance = Decimal('0.0')
for line in self.document.xpath('//div[@class="detail"]/table//tr'):
for line in self.doc.xpath('//div[@class="detail"]/table//tr'):
try:
left = line.xpath('./td[@class="gauche"]')[0]
right = line.xpath('./td[@class="droite"]')[0]
@ -119,35 +126,43 @@ class AccountsPage(BasePage):
#useless line
continue
if len(left.xpath('./span[@class="precision"]')) == 0 and (left.text is None or not 'total' in left.text.lower()):
if len(left.xpath('./span[@class="precision"]')) == 0 or \
(left.text is None or
not 'total' in left.text.lower() or
u'prélevé' in left.xpath('./span[@class="precision"]')[0].text.lower()):
continue
balance -= Decimal(FrenchTransaction.clean_amount(right.text))
balance -= CleanDecimal('.', replace_dots=False)(right)
return balance
class OperationsPage(BasePage):
def get_history(self):
for tr in self.document.xpath('//div[contains(@class, "mod-listeoperations")]//table/tbody/tr'):
cols = tr.findall('td')
class Transaction(FrenchTransaction):
PATTERNS = [(re.compile(ur'^(?P<text>.*?) - traité le \d+/\d+$'), FrenchTransaction.TYPE_CARD)]
date = self.parser.tocleanstring(cols[0])
raw = self.parser.tocleanstring(cols[1])
label = re.sub(u' - traité le \d+/\d+', '', raw)
class OperationsPage(LoggedPage, HTMLPage):
@method
class get_history(ListElement):
item_xpath = '//div[contains(@class, "mod-listeoperations")]//table/tbody/tr'
debit = self.parser.tocleanstring(cols[3])
if len(debit) > 0:
t = FrenchTransaction(0)
t.parse(date, raw)
t.label = label
t.set_amount(debit)
yield t
class credit(ItemElement):
klass = Transaction
obj_type = Transaction.TYPE_CARD
obj_date = Transaction.Date('./td[1]')
obj_raw = Transaction.Raw('./td[2]')
obj_amount = Env('amount')
amount = self.parser.tocleanstring(cols[2])
if len(amount) > 0:
t = FrenchTransaction(0)
t.parse(date, raw)
t.label = label
t.set_amount(amount)
t.amount = - t.amount
yield t
def condition(self):
self.env['amount'] = Transaction.Amount('./td[4]')(self.el)
return self.env['amount'] > 0
class debit(ItemElement):
klass = Transaction
obj_type = Transaction.TYPE_CARD
obj_date = Transaction.Date('./td[1]')
obj_raw = Transaction.Raw('./td[2]')
obj_amount = Env('amount')
def condition(self):
self.env['amount'] = Transaction.Amount('', './td[3]')(self.el)
return self.env['amount'] < 0