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

View file

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