diff --git a/modules/delubac/__init__.py b/modules/delubac/__init__.py
new file mode 100644
index 00000000..456fc62c
--- /dev/null
+++ b/modules/delubac/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2013 Noe Rubinstein
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from .backend import DelubacBackend
+
+
+__all__ = ['DelubacBackend']
diff --git a/modules/delubac/backend.py b/modules/delubac/backend.py
new file mode 100644
index 00000000..91cf7dfe
--- /dev/null
+++ b/modules/delubac/backend.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2013 Noe Rubinstein
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+from itertools import ifilter
+
+from weboob.capabilities.bank import ICapBank
+from weboob.tools.backend import BaseBackend, BackendConfig
+from weboob.tools.value import ValueBackendPassword
+
+from .browser import DelubacBrowser
+
+
+__all__ = ['DelubacBackend']
+
+
+class DelubacBackend(BaseBackend, ICapBank):
+ NAME = 'delubac'
+ DESCRIPTION = u'Banque Delubac & Cie'
+ MAINTAINER = u'Noe Rubinstein'
+ EMAIL = 'nru@budget-insight.com'
+ VERSION = '0.g'
+
+ BROWSER = DelubacBrowser
+
+ CONFIG = BackendConfig(ValueBackendPassword('login', label='Account ID', masked=False),
+ ValueBackendPassword('password', label='Password of account'))
+
+ def create_default_browser(self):
+ return self.create_browser(self.config['login'].get(),
+ self.config['password'].get())
+
+ def iter_accounts(self):
+ return self.browser.iter_accounts()
+
+ def get_account(self, _id):
+ return self.browser.get_account(_id)
+
+ def iter_history(self, account, coming=False):
+ with self.browser:
+ return ifilter(lambda tr: coming == tr._is_coming,
+ self.browser.iter_history(account))
+
+ #def iter_coming(self, account):
+ # return self.iter_history(account, coming=True)
diff --git a/modules/delubac/browser.py b/modules/delubac/browser.py
new file mode 100644
index 00000000..e2c6db73
--- /dev/null
+++ b/modules/delubac/browser.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2013 Noe Rubinstein
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
+
+from .pages import LoginPage, DashboardPage, OperationsPage
+
+
+__all__ = ['DelubacBrowser']
+
+
+class DelubacBrowser(BaseBrowser):
+ PROTOCOL = 'https'
+ DOMAIN = 'vbankonline.delubac.com'
+ ENCODING = None
+
+ @classmethod
+ def page_url(cls, name):
+ return '%s://%s/%s.do' % (cls.PROTOCOL, cls.DOMAIN, name)
+
+ PAGES = {
+ '%s://%s/(simpleIndex|index).do(\;.*)?' % (PROTOCOL, DOMAIN): LoginPage,
+ '%s://%s/tbord.do(\?.*)?' % (PROTOCOL, DOMAIN): DashboardPage,
+ '%s://%s/releve.do' % (PROTOCOL, DOMAIN): OperationsPage,
+ }
+
+ PAGES_REV = {
+ LoginPage: '%s://%s/index.do' % (PROTOCOL, DOMAIN),
+ DashboardPage: '%s://%s/tbord.do' % (PROTOCOL, DOMAIN),
+ OperationsPage: '%s://%s/releve.do' % (PROTOCOL, DOMAIN),
+ }
+
+ def stay_or_go(self, page, **kwargs):
+ if not self.is_on_page(page):
+ self.location(self.PAGES_REV[page], **kwargs)
+
+ assert self.is_on_page(page)
+
+ def login(self):
+ assert isinstance(self.username, basestring)
+ assert isinstance(self.password, basestring)
+
+ self.stay_or_go(LoginPage, no_login=True)
+
+ self.page.login(self.username, self.password)
+
+ self.location(self.page_url('loginSecurite')+'?_top')
+
+ if not self.is_logged():
+ raise BrowserIncorrectPassword()
+
+ def is_logged(self):
+ return not self.is_on_page(LoginPage)
+
+ def iter_accounts(self):
+ self.stay_or_go(DashboardPage)
+ return self.page.iter_accounts()
+
+ def get_account(self, _id):
+ self.stay_or_go(DashboardPage)
+ return self.page.get_account(_id)
+
+ def iter_history(self, account):
+ self.location(account._url)
+ assert self.is_on_page(OperationsPage)
+
+ return self.page.iter_history()
diff --git a/modules/delubac/pages.py b/modules/delubac/pages.py
new file mode 100644
index 00000000..b365136b
--- /dev/null
+++ b/modules/delubac/pages.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2013 Noe Rubinstein
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+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
+
+
+__all__ = ['LoginPage', 'DashboardPage', 'OperationsPage']
+
+
+class LoginPage(BasePage):
+
+ def login(self, username, password):
+ self.browser.select_form(name="frmLogin")
+ self.browser['username'] = username
+ self.browser['password'] = password
+ self.browser.find_control('lang').readonly = False
+ self.browser['lang'] = 'fr'
+ self.browser.submit(nologin=True)
+
+
+class DashboardPage(BasePage):
+ def iter_accounts(self):
+ for line in self._accounts():
+ yield self._get_account(line)
+
+ def get_account(self, _id):
+ xpath = './/a[@href="tbord.do?id=%s"]' % _id
+ account = next(a for a in self._accounts() if a.xpath(xpath))
+ return self._get_account(account)
+
+ def _accounts(self):
+ return self.document.getroot().cssselect('.tbord_account')
+
+ _FIELD_XPATH = './/span[@class="%s"]//text()'
+ _URL_XPATH = './/span[@class="accountLabel"]//a/@href'
+
+ def _get_account(self, line):
+ def get_field(field):
+ return unicode(line.xpath(self._FIELD_XPATH % field)[0]).strip()
+
+ account = Account()
+ account._url = unicode(line.xpath(self._URL_XPATH)[0])
+ account.id = account._url.replace("tbord.do?id=", "")
+ account.balance = FrenchTransaction.clean_amount(
+ get_field('accountTotal'))
+ account.label = get_field('accountLabel2')
+ account.currency = Account.TXT2CUR.get(get_field('accountDev'),
+ Account.CUR_UNKNOWN)
+
+ return account
+
+
+class OperationsPage(BasePage):
+ _LINE_XPATH = '//tr[starts-with(@class,"PL_LIGLST_")]'
+
+ def iter_history(self):
+ i = 0
+ for line in self.document.xpath(self._LINE_XPATH):
+ i += 1
+ operation = Transaction(i)
+
+ date = line.xpath('.//td[@class="nlb d"]')[0].text_content().strip()
+ raw = line.xpath('.//td[@class="t"]')[0].text_content().strip()
+
+ amounts = line.xpath('.//td[@class="n"]')
+ [debit, credit] = [amount.text_content().strip()
+ for amount in line.xpath('.//td[@class="n"]')]
+
+ operation.parse(date=date, raw=raw)
+ operation.set_amount(credit, debit)
+
+ yield operation
+
+
+class Transaction(FrenchTransaction):
+ PATTERNS = [(re.compile('^(?:Vir(?:ement)?|VRT) (?P.*)', re.I),
+ FrenchTransaction.TYPE_TRANSFER),
+ (re.compile('^CARTE(?: ETR.)? ' +
+ '(?P\d{2})/(?P\d{2})/(?P\d{4}) ' +
+ '(?P.*)'),
+ FrenchTransaction.TYPE_CARD),
+ (re.compile('^CHQ (?P.*)$'),
+ FrenchTransaction.TYPE_CHECK),
+ (re.compile('^RCH (?P.*)'),
+ FrenchTransaction.TYPE_DEPOSIT)]
+
+ _is_coming = False
diff --git a/modules/delubac/test.py b/modules/delubac/test.py
new file mode 100644
index 00000000..4caa804d
--- /dev/null
+++ b/modules/delubac/test.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2013 Noe Rubinstein
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.tools.test import BackendTest
+
+
+class DelubacTest(BackendTest):
+ BACKEND = 'delubac'
+
+ def test_delubac(self):
+ l = list(self.backend.iter_accounts())
+ if len(l) > 0:
+ a = l[0]
+ list(self.backend.iter_history(a))