diff --git a/modules/creditcooperatif/backend.py b/modules/creditcooperatif/backend.py
index 13b6c5c1..6d539d5d 100644
--- a/modules/creditcooperatif/backend.py
+++ b/modules/creditcooperatif/backend.py
@@ -20,7 +20,7 @@
from weboob.capabilities.bank import ICapBank, AccountNotFound
from weboob.tools.backend import BaseBackend, BackendConfig
-from weboob.tools.value import ValueBackendPassword, Value
+from weboob.tools.value import ValueBackendPassword
from .browser import CreditCooperatif
@@ -35,18 +35,14 @@ class CreditCooperatifBackend(BaseBackend, ICapBank):
VERSION = '0.e'
DESCRIPTION = u'Credit Cooperatif French bank website'
LICENSE = 'AGPLv3+'
- auth_type = {"weak" : "Code confidentiel",
- "strong": "Sesame"}
- CONFIG = BackendConfig(Value('auth_type', label='Authentication type', choices=auth_type, default="strong"),
- ValueBackendPassword('login', label='Account ID', masked=False),
+ CONFIG = BackendConfig(ValueBackendPassword('login', label='Account ID', masked=False),
ValueBackendPassword('password', label='Password or one time pin'))
BROWSER = CreditCooperatif
def create_default_browser(self):
return self.create_browser(self.config['login'].get(),
- self.config['password'].get(),
- strong_auth=self.config['auth_type'].get() == "strong")
+ self.config['password'].get())
def iter_accounts(self):
with self.browser:
diff --git a/modules/creditcooperatif/browser.py b/modules/creditcooperatif/browser.py
index 58723b6e..37d421ed 100644
--- a/modules/creditcooperatif/browser.py
+++ b/modules/creditcooperatif/browser.py
@@ -17,9 +17,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see .
+import urllib
+
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
-from .pages import LoginPage, AccountsPage, TransactionsPage, ComingTransactionsPage
+from .pages import LoginPage, LoggedPage, AccountsPage, TransactionsPage, TransactionsJSONPage, ComingTransactionsPage
__all__ = ['CreditCooperatif']
@@ -28,22 +30,17 @@ __all__ = ['CreditCooperatif']
class CreditCooperatif(BaseBrowser):
PROTOCOL = 'https'
ENCODING = 'iso-8859-15'
- DOMAIN = "www.coopanet.com"
- PAGES = {'https://www.coopanet.com/banque/sso/.*': LoginPage,
- 'https://www.coopanet.com/banque/cpt/incoopanetj2ee.do.*': AccountsPage,
- 'https://www.coopanet.com/banque/cpt/cpt/situationcomptes.do\?lnkReleveAction=X&numeroExterne=.*': TransactionsPage,
- 'https://www.coopanet.com/banque/cpt/cpt/relevecompte.do\?tri_page=.*': TransactionsPage,
- 'https://www.coopanet.com/banque/cpt/cpt/situationcomptes.do\?lnkOpCB=X&numeroExterne=.*': ComingTransactionsPage
+ DOMAIN = "www.credit-cooperatif.coop"
+ PAGES = {'https://www.credit-cooperatif.coop/portail/particuliers/login.do': LoginPage,
+ 'https://www.credit-cooperatif.coop/portail/particuliers/authentification.do': LoggedPage,
+ 'https://www.credit-cooperatif.coop/portail/particuliers/mescomptes/synthese.do': AccountsPage,
+ 'https://www.credit-cooperatif.coop/portail/particuliers/mescomptes/relevedesoperations.do': TransactionsPage,
+ 'https://www.credit-cooperatif.coop/portail/particuliers/mescomptes/relevedesoperationsjson.do': (TransactionsJSONPage, 'json'),
+ 'https://www.credit-cooperatif.coop/portail/particuliers/mescomptes/synthese/operationsencourslien.do': ComingTransactionsPage,
}
- def __init__(self, *args, **kwargs):
- self.strong_auth = kwargs.pop('strong_auth', False)
- BaseBrowser.__init__(self, *args, **kwargs)
-
def home(self):
- self.location("/banque/sso/")
-
- assert self.is_on_page(LoginPage)
+ self.location("/portail/particuliers/mescomptes/synthese.do")
def is_logged(self):
return not self.is_on_page(LoginPage)
@@ -56,7 +53,6 @@ class CreditCooperatif(BaseBrowser):
assert isinstance(self.username, basestring)
assert isinstance(self.password, basestring)
- assert isinstance(self.strong_auth, bool)
if self.is_logged():
return
@@ -64,13 +60,19 @@ class CreditCooperatif(BaseBrowser):
if not self.is_on_page(LoginPage):
self.home()
- self.page.login(self.username, self.password, self.strong_auth)
+ self.page.login(self.username, self.password)
+
+ if self.is_on_page(LoggedPage):
+ error = self.page.get_error()
+ if error is not None:
+ raise BrowserIncorrectPassword(error)
if not self.is_logged():
raise BrowserIncorrectPassword()
def get_accounts_list(self):
- self.location(self.buildurl('/banque/cpt/incoopanetj2ee.do?ssomode=ok'))
+ if not self.is_on_page(AccountsPage):
+ self.location('/portail/particuliers/mescomptes/synthese.do')
return self.page.get_list()
@@ -84,24 +86,25 @@ class CreditCooperatif(BaseBrowser):
return None
def get_history(self, account):
- self.location('/banque/cpt/cpt/situationcomptes.do?lnkReleveAction=X&numeroExterne='+ account.id)
+ data = {'accountExternalNumber': account.id}
+ self.location('/portail/particuliers/mescomptes/relevedesoperations.do', urllib.urlencode(data))
- while 1:
- assert self.is_on_page(TransactionsPage)
+ data = {'iDisplayLength': 400,
+ 'iDisplayStart': 0,
+ 'iSortCol_0': 0,
+ 'iSortingCols': 1,
+ 'sColumns': '',
+ 'sEcho': 1,
+ 'sSortDir_0': 'asc',
+ }
+ self.location('/portail/particuliers/mescomptes/relevedesoperationsjson.do', urllib.urlencode(data))
- for tr in self.page.get_history():
- yield tr
-
- next_url = self.page.get_next_url()
- if next_url is None:
- return
-
- self.location(next_url)
+ return self.page.get_transactions()
def get_coming(self, account):
- self.location('/banque/cpt/cpt/situationcomptes.do?lnkOpCB=X&numeroExterne='+ account.id)
+ data = {'accountExternalNumber': account.id}
+ self.location('/portail/particuliers/mescomptes/synthese/operationsencourslien.do', urllib.urlencode(data))
assert self.is_on_page(ComingTransactionsPage)
- for ctr in self.page.get_history():
- yield ctr
+ return self.page.get_transactions()
diff --git a/modules/creditcooperatif/pages.py b/modules/creditcooperatif/pages.py
index c6f4f772..02574ea0 100644
--- a/modules/creditcooperatif/pages.py
+++ b/modules/creditcooperatif/pages.py
@@ -20,8 +20,8 @@
from decimal import Decimal
import re
-import time
+from weboob.tools.json import json
from weboob.tools.browser import BasePage
from weboob.capabilities.bank import Account
from weboob.tools.capabilities.bank.transactions import FrenchTransaction
@@ -29,64 +29,57 @@ from weboob.tools.capabilities.bank.transactions import FrenchTransaction
__all__ = ['LoginPage', 'AccountsPage', 'TransactionsPage', 'ComingTransactionsPage']
+
class LoginPage(BasePage):
- def login(self, login, pin, strong_auth):
- form_nb = 1 if strong_auth else 0
- indentType = "RENFORCE" if strong_auth else "MDP"
-
- self.browser.select_form(name='loginCoForm', nr=form_nb)
- self.browser['codeUtil'] = login
- self.browser['motPasse'] = pin
-
- assert self.browser['identType'] == indentType
+ def login(self, login, password):
+ self.browser.select_form(predicate=lambda form: form.attrs.get('id', '') == 'AuthForm')
+ self.browser['j_username'] = login.encode('iso-8859-15')
+ self.browser['j_password'] = password.encode('iso-8859-15')
self.browser.submit(nologin=True)
+class LoggedPage(BasePage):
+ def get_error(self):
+ div = self.document.xpath('//div[@class="errorForm-msg"]')
+ if len(div) == 0:
+ return None
+
+ msg = u', '.join([li.text.strip() for li in div[0].xpath('.//li')])
+ return re.sub('[\r\n\t\xa0]+', ' ', msg)
+
class AccountsPage(BasePage):
ACCOUNT_TYPES = {u'COMPTE NEF': Account.TYPE_CHECKING}
- CPT_ROW_ID = 0
- CPT_ROW_NAME = 1
- CPT_ROW_NATURE = 2
- CPT_ROW_BALANCE = 3
- CPT_ROW_ENCOURS = 4
-
- def is_error(self):
- for par in self.document.xpath('//p[@class=acctxtnoirlien]'):
- if par.text is not None and u"La page demandée ne peut pas être affichée." in par.text:
- return True
-
- return False
-
def get_list(self):
- for trCompte in self.document.xpath('//table[@id="compte"]/tbody/tr'):
- tds = trCompte.findall('td')
-
+ for table in self.document.getroot().cssselect('table.table-synthese'):
account = Account()
+ labels = table.xpath('.//ul[@class="nClient"]/li')
+ account_type_str = table.xpath('.//h2[@class="tt_compte"]')[0].text.strip()
- account.id = tds[self.CPT_ROW_ID].text.strip()
- account.label = tds[self.CPT_ROW_NAME].text.strip()
+ account.id = re.sub(u'[^0-9]', '', labels[-1].text)
+ account.label = u' '.join([account_type_str, labels[0].text.strip()])
+ account.type = self.ACCOUNT_TYPES.get(account_type_str, Account.TYPE_UNKNOWN)
- account_type_str = "".join([td.text for td in tds[self.CPT_ROW_NATURE].xpath('.//td[@class="txt"]')]).strip()
+ balance = table.xpath('.//td[@class="sum_solde"]//span')[-1].text
+ account.balance = Decimal(FrenchTransaction.clean_amount(balance))
+ account.currency = account.get_currency(balance)
- account.type = self.ACCOUNT_TYPES.get(account_type_str, Account.TYPE_UNKNOWN)
-
- account.balance = Decimal(FrenchTransaction.clean_amount(tds[self.CPT_ROW_BALANCE].find("a").text))
- account.coming = Decimal(FrenchTransaction.clean_amount( tds[self.CPT_ROW_ENCOURS].find("a").text))
- account.currency = account.get_currency(tds[self.CPT_ROW_BALANCE].find("a").text)
yield account
- return
-
class Transaction(FrenchTransaction):
- PATTERNS = [(re.compile('^RETRAIT DAB (?P.*?).*'),
+ PATTERNS = [(re.compile('^(?PRETRAIT DAB) (?P\d{2})-(?P\d{2})-([\d\-]+)'),
FrenchTransaction.TYPE_WITHDRAWAL),
- (re.compile('^(?P.*) RETRAIT DU (?P\d{2})(?P\d{2})(?P\d{2}) .*'),
+ (re.compile('^RETRAIT DAB (?P\d{2})-(?P\d{2})-([\d\-]+) (?P.*)'),
FrenchTransaction.TYPE_WITHDRAWAL),
- (re.compile('^CARTE \d+ .*'), FrenchTransaction.TYPE_CARD),
- (re.compile('^VIR(EMENT)? (?P.*)'), FrenchTransaction.TYPE_TRANSFER),
- (re.compile('^PRLV (?P.*)'), FrenchTransaction.TYPE_ORDER),
+ (re.compile('^CARTE (?P\d{2})(?P\d{2}) \d+ (?P.*)'),
+ FrenchTransaction.TYPE_CARD),
+ (re.compile('^VIR COOPA (?P\d{2})/(?P\d{2}) (?P.*)'),
+ FrenchTransaction.TYPE_TRANSFER),
+ (re.compile('^VIR(EMENT|EMT)? (?P.*?)(- .*)?$'),
+ FrenchTransaction.TYPE_TRANSFER),
+ (re.compile('^(PRLV|PRELEVEMENT) (?P.*?)(- .*)?$'),
+ FrenchTransaction.TYPE_ORDER),
(re.compile('^CHEQUE.*'), FrenchTransaction.TYPE_CHECK),
- (re.compile('^(AGIOS /|FRAIS) (?P.*)'), FrenchTransaction.TYPE_BANK),
+ (re.compile('^(AGIOS /|FRAIS) (?P.*)'),FrenchTransaction.TYPE_BANK),
(re.compile('^ABONNEMENT (?P.*)'), FrenchTransaction.TYPE_BANK),
(re.compile('^REMISE (?P.*)'), FrenchTransaction.TYPE_DEPOSIT),
(re.compile('^(?P.*)( \d+)? QUITTANCE .*'),
@@ -96,72 +89,46 @@ class Transaction(FrenchTransaction):
]
class TransactionsPage(BasePage):
- def get_next_url(self):
- # can be 'Suivant' or ' Suivant'
- next = self.document.xpath("//a[normalize-space(text()) = 'Suivant']")
+ pass
- if not next:
- return None
-
- return next[0].attrib["href"]
-
- TR_DATE = 0
- TR_TEXT = 2
- TR_DEBIT = 3
- TR_CREDIT = 4
- def get_history(self):
- for tr in self.document.xpath('//table[@id="operation"]/tbody/tr'):
- tds = tr.findall('td')
-
- def get_content(td):
- ret = "".join([ttd.text if ttd.text else "" for ttd in td.xpath(".//td")])
- return ret.replace(u"\xa0", " ").strip()
-
- date = get_content(tds[self.TR_DATE])
- raw = get_content(tds[self.TR_TEXT])
-
- debit = get_content(tds[self.TR_DEBIT])
- credit = get_content(tds[self.TR_CREDIT])
-
- t = Transaction(date+""+raw)
- t.parse(date, re.sub(r'[ ]+', ' ', raw))
- t.set_amount(credit, debit)
+class TransactionsJSONPage(BasePage):
+ ROW_DATE = 0
+ ROW_TEXT = 2
+ ROW_CREDIT = -1
+ ROW_DEBIT = -2
+ def get_transactions(self):
+ for tr in self.document['aaData']:
+ t = Transaction(0)
+ t.parse(tr[self.ROW_DATE], tr[self.ROW_TEXT])
+ t.set_amount(tr[self.ROW_CREDIT], tr[self.ROW_DEBIT])
yield t
class ComingTransactionsPage(BasePage):
- COM_TR_COMMENT = 0
- COM_TR_DATE = 1
- COM_TR_TEXT = 2
- COM_TR_VALUE = 3
+ ROW_REF = 0
+ ROW_TEXT = 1
+ ROW_DATE = 2
+ ROW_CREDIT = -1
+ ROW_DEBIT = -2
- def get_history(self):
- comment = None
- for tr in self.document.xpath('//table[@id="operation"]/tbody/tr'):
- tds = tr.findall('td')
+ def get_transactions(self):
+ data = []
+ for script in self.document.xpath('//script'):
+ txt = script.text
+ if txt is None:
+ continue
- def get_content(td):
- ret = td.text
- return ret.replace(u"\xa0", " ").strip()
+ pattern = 'var jsonData ='
+ start = txt.find(pattern)
+ if start < 0:
+ continue
- raw = get_content(tds[self.COM_TR_TEXT])
-
- if comment is None:
- comment = get_content(tds[self.COM_TR_COMMENT])
- raw = "%s (%s) " % (raw, comment)
-
- debit = get_content(tds[self.COM_TR_VALUE])
- date = get_content(tds[self.COM_TR_DATE])
-
- if comment is not None:
- #date is 'JJ/MM'. add '/YYYY'
- date += comment[comment.rindex("/"):]
- else:
- date += "/%d" % time.localtime().tm_year
-
-
- t = Transaction(date+""+raw)
- t.parse(date, re.sub(r'[ ]+', ' ', raw))
- t.set_amount("", debit)
+ txt = txt[start+len(pattern):start+txt[start:].find(';')]
+ data = json.loads(txt)
+ break
+ for tr in data:
+ t = Transaction(0)
+ t.parse(tr[self.ROW_DATE], tr[self.ROW_TEXT])
+ t.set_amount(tr[self.ROW_CREDIT], tr[self.ROW_DEBIT])
yield t