diff --git a/modules/ldlc/browser.py b/modules/ldlc/browser.py
index 63fcb98c68a04ad8c6818b0aa190a262d5ed558b..9935d53a3c91aec02dc16f16aa13127115ae8b5e 100644
--- a/modules/ldlc/browser.py
+++ b/modules/ldlc/browser.py
@@ -17,11 +17,29 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this weboob module. If not, see .
-
-from weboob.browser import LoginBrowser, URL, need_login
+from weboob.browser import LoginBrowser, AbstractBrowser, URL, need_login
from weboob.exceptions import BrowserIncorrectPassword
-from .pages import LoginPage, HomePage, ParBillsPage, ProBillsPage
+from .pages import HomePage, LoginPage, ProBillsPage, DocumentsPage
+
+
+class LdlcParBrowser(AbstractBrowser):
+ PARENT = 'materielnet'
+ BASEURL = 'https://secure2.ldlc.com'
+
+ documents = URL(r'/fr-fr/Orders/PartialCompletedOrdersHeader', DocumentsPage)
+
+ def __init__(self, *args, **kwargs):
+ super(LdlcParBrowser, self).__init__(*args, **kwargs)
+ self.lang = 'fr-fr/'
+
+ @need_login
+ def iter_documents(self, subscription):
+ json_response = self.location('/fr-fr/Orders/CompletedOrdersPeriodSelection').json()
+
+ for data in json_response:
+ for doc in self.location('/fr-fr/Orders/PartialCompletedOrdersHeader', data=data).page.get_documents(subid=subscription.id):
+ yield doc
class LdlcBrowser(LoginBrowser):
@@ -38,22 +56,7 @@ def do_login(self):
@need_login
def get_subscription_list(self):
- return self.home.stay_or_go().get_list()
-
-
-class LdlcParBrowser(LdlcBrowser):
- BASEURL = 'https://secure.ldlc.com'
-
- bills = URL(r'/Account/CommandListingPage.aspx', ParBillsPage)
-
- @need_login
- def iter_documents(self, subscription):
- self.bills.stay_or_go()
- for value in self.page.get_range():
- self.bills.go(data={'ctl00$ctl00$cphMainContent$cphMainContent$ddlDate': value, '__EVENTTARGET': 'ctl00$cphMainContent$ddlDate'})
-
- for bill in self.page.iter_documents(subid=subscription.id):
- yield bill
+ return self.home.stay_or_go().get_subscriptions()
class LdlcProBrowser(LdlcBrowser):
diff --git a/modules/ldlc/materielnet_pages.py b/modules/ldlc/materielnet_pages.py
new file mode 120000
index 0000000000000000000000000000000000000000..aae0d1773082edcf82027f576df742ec4ff3c3c0
--- /dev/null
+++ b/modules/ldlc/materielnet_pages.py
@@ -0,0 +1 @@
+../materielnet/pages.py
\ No newline at end of file
diff --git a/modules/ldlc/module.py b/modules/ldlc/module.py
index c66ec87726804f87ad6d834680ed52b182e79135..3ddb0d1a5d00fbd1b47081f141f4bfbd058c235a 100644
--- a/modules/ldlc/module.py
+++ b/modules/ldlc/module.py
@@ -17,10 +17,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this weboob module. If not, see .
+from __future__ import unicode_literals
-from weboob.capabilities.bill import CapDocument, Subscription, Bill, SubscriptionNotFound, DocumentNotFound
-from weboob.capabilities.base import find_object
-from weboob.tools.backend import Module, BackendConfig
+from weboob.capabilities.bill import CapDocument, Bill
+from weboob.capabilities.base import empty
+from weboob.tools.backend import AbstractModule, BackendConfig
from weboob.tools.value import ValueBackendPassword, Value
from .browser import LdlcParBrowser, LdlcProBrowser
@@ -29,10 +30,10 @@
__all__ = ['LdlcModule']
-class LdlcModule(Module, CapDocument):
+class LdlcModule(AbstractModule, CapDocument):
NAME = 'ldlc'
- DESCRIPTION = u'ldlc website'
- MAINTAINER = u'Vincent Paredes'
+ DESCRIPTION = 'ldlc website'
+ MAINTAINER = 'Vincent Paredes'
EMAIL = 'vparedes@budget-insight.com'
LICENSE = 'LGPLv3+'
VERSION = '1.6'
@@ -41,34 +42,21 @@ class LdlcModule(Module, CapDocument):
Value('website', label='Site web', default='part',
choices={'pro': 'Professionnels', 'part': 'Particuliers'}))
+ PARENT = 'materielnet'
+
def create_default_browser(self):
if self.config['website'].get() == 'part':
self.BROWSER = LdlcParBrowser
+ return self.create_browser(self.config['login'].get(), self.config['password'].get(), weboob=self.weboob)
else:
self.BROWSER = LdlcProBrowser
-
- return self.create_browser(self.config['login'].get(), self.config['password'].get())
-
- def iter_subscription(self):
- return self.browser.get_subscription_list()
-
- def get_subscription(self, _id):
- return find_object(self.iter_subscription(), id=_id, error=SubscriptionNotFound)
-
- def get_document(self, _id):
- subid = _id.rsplit('_', 1)[0]
- subscription = self.get_subscription(subid)
-
- return find_object(self.iter_documents(subscription), id=_id, error=DocumentNotFound)
-
- def iter_documents(self, subscription):
- if not isinstance(subscription, Subscription):
- subscription = self.get_subscription(subscription)
- return self.browser.iter_documents(subscription)
+ return self.create_browser(self.config['login'].get(), self.config['password'].get())
def download_document(self, bill):
if not isinstance(bill, Bill):
bill = self.get_document(bill)
+ if empty(bill.url):
+ return
if self.config['website'].get() == 'part':
return self.browser.open(bill.url).content
else:
diff --git a/modules/ldlc/pages.py b/modules/ldlc/pages.py
index 9e0003bf8c60e1e5c739131deea63db7a435130e..c9694d9162b46d133effe32e78630babf6107adb 100644
--- a/modules/ldlc/pages.py
+++ b/modules/ldlc/pages.py
@@ -19,12 +19,16 @@
from __future__ import unicode_literals
-from weboob.browser.pages import HTMLPage, LoggedPage
-from weboob.browser.filters.standard import CleanDecimal, CleanText, Env, Format, QueryValue, TableCell, Currency
+from weboob.browser.pages import HTMLPage, LoggedPage, PartialHTMLPage
+from weboob.browser.filters.standard import (
+ CleanDecimal, CleanText, Env, Format, QueryValue, TableCell, Currency, Regexp, Async, Date, Field,
+)
from weboob.browser.elements import ListElement, ItemElement, method, TableElement
-from weboob.browser.filters.html import Attr
-from weboob.capabilities.bill import Bill, Subscription
+from weboob.browser.filters.html import Attr, Link
+from weboob.capabilities import NotAvailable
+from weboob.capabilities.bill import Bill, Subscription, DocumentTypes
from weboob.tools.date import parse_french_date
+from .materielnet_pages import MyAsyncLoad
class HiddenFieldPage(HTMLPage):
@@ -35,7 +39,7 @@ def get_ctl00_actScriptManager_HiddenField(self):
class HomePage(LoggedPage, HTMLPage):
@method
- class get_list(ListElement):
+ class get_subscriptions(ListElement):
item_xpath = '//div[@id="divAccueilInformationClient"]//div[@id="divInformationClient"]'
class item(ItemElement):
klass = Subscription
@@ -45,56 +49,41 @@ class item(ItemElement):
obj_label = CleanText('.//div[@id="divlblTitleFirstNameLastName"]//span')
-class LoginPage(HiddenFieldPage):
- def login(self, username, password, website):
- form = self.get_form(id='aspnetForm')
- if website == 'part':
- form["ctl00$ctl00$cphMainContent$cphMainContent$txbMail"] = username
- form["ctl00$ctl00$cphMainContent$cphMainContent$txbPassword"] = password
- form["__EVENTTARGET"] = "ctl00$ctl00$cphMainContent$cphMainContent$butConnexion"
- form["ctl00_ctl00_actScriptManager_HiddenField"] = self.get_ctl00_actScriptManager_HiddenField()
- else:
- form["ctl00$cphMainContent$txbMail"] = username
- form["ctl00$cphMainContent$txbPassword"] = password
- form["__EVENTTARGET"] = "ctl00$cphMainContent$butConnexion"
- form["ctl00_ctl00_actScriptManager_HiddenField"] = self.get_ctl00_actScriptManager_HiddenField()
+class LoginPage(HTMLPage):
+ def login(self, username, password):
+ form = self.get_form(xpath='//form[contains(@action, "/Login/Login")]')
+ form['Email'] = username
+ form['Password'] = password
form.submit()
def get_error(self):
return CleanText('//span[contains(text(), "Identifiants incorrects")]')(self.doc)
-class BillsPage(LoggedPage, HiddenFieldPage):
- def get_range(self):
- for value in self.doc.xpath('//div[@class="commandListing content clearfix"]//select/option/@value'):
- yield value
-
-
-class ParBillsPage(BillsPage):
+class DocumentsPage(LoggedPage, PartialHTMLPage):
@method
- class iter_documents(TableElement):
- ignore_duplicate = True
- item_xpath = '//table[@id="TopListing"]/tr[position()>1]'
- head_xpath = '//table[@id="TopListing"]/tr[@class="TopListingHeader"]/td'
-
- col_id = 'N° de commande'
- col_date = 'Date'
- col_price = 'Montant TTC'
+ class get_documents(ListElement):
+ item_xpath = '//div[@class="dsp-row"]'
class item(ItemElement):
klass = Bill
- obj_id = Format('%s_%s', Env('subid'), CleanText(TableCell('id')))
- obj_url = Attr('./td[@class="center" or @class="center pdf"]/a', 'href')
+ load_details = Link('.//a[contains(text(), "détails")]') & MyAsyncLoad
+
+ obj_id = Format('%s_%s', Env('subid'), Field('label'))
+ obj_url = Async('details') & Link('//a[span[contains(text(), "Télécharger la facture")]]', default=NotAvailable)
+ obj_date = Date(CleanText('./div[contains(@class, "cell-date")]'), dayfirst=True)
obj_format = 'pdf'
- obj_price = CleanDecimal(TableCell('price'), replace_dots=True)
- obj_currency = Currency(TableCell('price'))
+ obj_label = Regexp(CleanText('./div[contains(@class, "cell-nb-order")]'), r' (.*)')
+ obj_type = DocumentTypes.BILL
+ obj_price = CleanDecimal(CleanText('./div[contains(@class, "cell-value")]'), replace_dots=(' ', '€'))
+ obj_currency = 'EUR'
- def obj_date(self):
- return parse_french_date(CleanText(TableCell('date'))(self)).date()
- def condition(self):
- return CleanText().filter(self.el.xpath('.//td')[-1]) != "" and len(self.el.xpath('./td[@class="center" or @class="center pdf"]/a/@href')) == 1
+class BillsPage(LoggedPage, HiddenFieldPage):
+ def get_range(self):
+ for value in self.doc.xpath('//div[@class="commandListing content clearfix"]//select/option/@value'):
+ yield value
class ProBillsPage(BillsPage):
diff --git a/modules/materielnet/browser.py b/modules/materielnet/browser.py
index b1695a62f542763c0b231912e809d28c70beae6a..e86cf501b1244e94069ed3da3bf46293c43455b3 100644
--- a/modules/materielnet/browser.py
+++ b/modules/materielnet/browser.py
@@ -21,28 +21,37 @@
from weboob.browser import LoginBrowser, URL, need_login
from weboob.exceptions import BrowserIncorrectPassword
-from .pages import LoginPage, CaptchaPage, ProfilPage, DocumentsPage, DocumentsDetailsPage
+from .pages import LoginPage, CaptchaPage, ProfilePage, DocumentsPage, DocumentsDetailsPage
+
+
+class MyURL(URL):
+ def go(self, *args, **kwargs):
+ kwargs['lang'] = self.browser.lang
+ return super(MyURL, self).go(*args, **kwargs)
class MaterielnetBrowser(LoginBrowser):
BASEURL = 'https://secure.materiel.net'
- login = URL(r'/Login/Login', LoginPage)
+ login = MyURL(r'/(?P.*)Login/Login', LoginPage)
captcha = URL('/pm/client/captcha.html', CaptchaPage)
- profil = URL(r'/Account/InformationsSection',
- r'/pro/Account/InformationsSection', ProfilPage)
- documents = URL(r'/Orders/PartialCompletedOrdersHeader',
- r'/pro/Orders/PartialCompletedOrdersHeader', DocumentsPage)
- document_details = URL(r'/Orders/PartialCompletedOrderContent',
- r'/pro/Orders/PartialCompletedOrderContent', DocumentsDetailsPage)
+ profile = MyURL(r'/(?P.*)Account/InformationsSection',
+ r'/pro/Account/InformationsSection', ProfilePage)
+ documents = MyURL(r'/(?P.*)Orders/PartialCompletedOrdersHeader',
+ r'/pro/Orders/PartialCompletedOrdersHeader', DocumentsPage)
+ document_details = MyURL(r'/(?P.*)Orders/PartialCompletedOrderContent',
+ r'/pro/Orders/PartialCompletedOrderContent', DocumentsDetailsPage)
def __init__(self, *args, **kwargs):
super(MaterielnetBrowser, self).__init__(*args, **kwargs)
self.is_pro = None
+ self.lang = ''
def par_or_pro_location(self, url, *args, **kwargs):
if self.is_pro:
url = '/pro' + url
+ elif self.lang:
+ url = '/' + self.lang[:-1] + url
return super(MaterielnetBrowser, self).location(url, *args, **kwargs)
@@ -63,7 +72,7 @@ def do_login(self):
@need_login
def get_subscription_list(self):
- return self.par_or_pro_location('/Account/InformationsSection').page.get_list()
+ return self.par_or_pro_location('/Account/InformationsSection').page.get_subscriptions()
@need_login
def iter_documents(self, subscription):
diff --git a/modules/materielnet/pages.py b/modules/materielnet/pages.py
index 36f0fb09ed2884925e96516d813d4af0ea45c079..2fd24b719423f28e09c29af2f5d2cd0f4fa64bc3 100644
--- a/modules/materielnet/pages.py
+++ b/modules/materielnet/pages.py
@@ -58,18 +58,22 @@ def get_error(self):
return CleanText('//div[@class="captcha-block"]/p[1]/text()')(self.doc)
-class ProfilPage(LoggedPage, HTMLPage):
+class ProfilePage(LoggedPage, HTMLPage):
@method
- class get_list(ListElement):
+ class get_subscriptions(ListElement):
class item(ItemElement):
klass = Subscription
obj_subscriber = Format('%s %s', Attr('//input[@id="FirstName"]', 'value'), Attr('//input[@id="LastName"]', 'value'))
- obj_id = Env('subid')
- obj_label = obj_id
- def parse(self, el):
- self.env['subid'] = self.page.browser.username
+ def obj_id(self):
+ if 'Materielnet' in self.page.browser.__class__.__name__:
+ filter_id = CleanText('//p[@class="NumCustomer"]/span')
+ else: # ldlc
+ filter_id = Regexp(CleanText('//span[@class="nclient"]'), r'Nº client : (.*)')
+
+ return filter_id(self)
+ obj_label = obj_id
class MyAsyncLoad(Filter):