Commit 3d03dd0f authored by Florian Duguet's avatar Florian Duguet Committed by Romain Bignon

[ldlc + materielnet] ldlc is now an abstract module of materielnet

update materielnet because some features are different between them

Closes: 40300@sibi
Closes: 10942@zendesk
parent 5e800964
......@@ -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 <http://www.gnu.org/licenses/>.
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 @@ class LdlcBrowser(LoginBrowser):
@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):
......
../materielnet/pages.py
\ No newline at end of file
......@@ -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 <http://www.gnu.org/licenses/>.
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 @@ from .browser import LdlcParBrowser, LdlcProBrowser
__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:
......
......@@ -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 @@ class HiddenFieldPage(HTMLPage):
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 HomePage(LoggedPage, HTMLPage):
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):
......
......@@ -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<lang>.*)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<lang>.*)Account/InformationsSection',
r'/pro/Account/InformationsSection', ProfilePage)
documents = MyURL(r'/(?P<lang>.*)Orders/PartialCompletedOrdersHeader',
r'/pro/Orders/PartialCompletedOrdersHeader', DocumentsPage)
document_details = MyURL(r'/(?P<lang>.*)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 @@ class MaterielnetBrowser(LoginBrowser):
@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):
......
......@@ -58,18 +58,22 @@ class CaptchaPage(HTMLPage):
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):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment