From f69da38af95d46d4e46b2c40a64530b6dadafcb2 Mon Sep 17 00:00:00 2001 From: Vincent Paredes Date: Fri, 31 Jul 2015 16:00:23 +0200 Subject: [PATCH] supporting bill capability for orange --- modules/orange/bill/__init__.py | 0 modules/orange/bill/browser.py | 55 ++++++++++++++++++++++ modules/orange/bill/pages/__init__.py | 23 +++++++++ modules/orange/bill/pages/bills.py | 55 ++++++++++++++++++++++ modules/orange/bill/pages/login.py | 28 +++++++++++ modules/orange/module.py | 67 ++++++++++++++++++++++++--- 6 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 modules/orange/bill/__init__.py create mode 100644 modules/orange/bill/browser.py create mode 100644 modules/orange/bill/pages/__init__.py create mode 100644 modules/orange/bill/pages/bills.py create mode 100644 modules/orange/bill/pages/login.py diff --git a/modules/orange/bill/__init__.py b/modules/orange/bill/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modules/orange/bill/browser.py b/modules/orange/bill/browser.py new file mode 100644 index 00000000..fae5efdd --- /dev/null +++ b/modules/orange/bill/browser.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2012-2014 Vincent Paredes +# +# 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.browser import LoginBrowser, URL, need_login +from weboob.exceptions import BrowserIncorrectPassword +from .pages import LoginPage, BillsPage + +__all__ = ['OrangeBillBrowser'] + + +class OrangeBillBrowser(LoginBrowser): + loginpage = URL('https://id.orange.fr/auth_user/bin/auth_user.cgi', LoginPage) + billspage = URL('https://m.espaceclientv3.orange.fr/\?page=factures-archives', + 'https://.*.espaceclientv3.orange.fr/\?page=factures-archives', + BillsPage) + + def do_login(self): + assert isinstance(self.username, basestring) + assert isinstance(self.password, basestring) + + self.loginpage.stay_or_go().login(self.username, self.password) + + self.billspage.go() + if self.loginpage.is_here(): + raise BrowserIncorrectPassword() + + def get_nb_remaining_free_sms(self): + raise NotImplementedError() + + def post_message(self, message, sender): + raise NotImplementedError() + + @need_login + def get_subscription_list(self): + return self.billspage.stay_or_go().get_list() + + @need_login + def iter_bills(self, subscription): + return self.billspage.stay_or_go().get_bills(subid=subscription.id) diff --git a/modules/orange/bill/pages/__init__.py b/modules/orange/bill/pages/__init__.py new file mode 100644 index 00000000..a158b5bb --- /dev/null +++ b/modules/orange/bill/pages/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2010-2011 Vincent Paredes +# +# 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 .login import LoginPage +from .bills import BillsPage + +__all__ = ['LoginPage', BillsPage] diff --git a/modules/orange/bill/pages/bills.py b/modules/orange/bill/pages/bills.py new file mode 100644 index 00000000..b2b47262 --- /dev/null +++ b/modules/orange/bill/pages/bills.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2010-2011 Vincent Paredes +# +# 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 . + +import re + +from weboob.browser.pages import HTMLPage +from weboob.capabilities.bill import Subscription +from weboob.browser.elements import ListElement, ItemElement, method +from weboob.browser.filters.standard import CleanDecimal, CleanText, Env, Format, Date +from weboob.browser.filters.html import Attr +from weboob.capabilities.bill import Bill + + +class BillsPage(HTMLPage): + @method + class get_list(ListElement): + class item(ItemElement): + klass = Subscription + + obj_label = CleanText('(//li[@class="n1 menuUsage toHighlight"])[1]') + obj_subscriber = CleanText('//div[@class="blocCompte blocPrincipal"]//h2/a') + obj_id = Env('id') + + def parse(self, el): + self.env['id'] = re.sub(r'[^\d\-\.]', '', el.xpath('(//li[@class="n1 menuUsage toHighlight"])[1]//a')[0].text) + + @method + class get_bills(ListElement): + item_xpath = '//ul[@class="liste fe_clearfix factures"]/li' + + class item(ItemElement): + klass = Bill + + obj__url = Attr('.//span[@class="telecharger pdf"]/a', 'href') + obj_id = Format('%s.%s', Env('subid'), CleanDecimal(CleanText('.//span[@class="date magic_gras magic_font13"]'))) + obj_date = Date(CleanText('.//span[@class="date magic_gras magic_font13"]')) + obj_format = u"pdf" + obj_price = CleanDecimal('span[@class="montant"]', replace_dots=True) + diff --git a/modules/orange/bill/pages/login.py b/modules/orange/bill/pages/login.py new file mode 100644 index 00000000..1708f747 --- /dev/null +++ b/modules/orange/bill/pages/login.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2010-2011 Vincent Paredes +# +# 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.browser.pages import HTMLPage + +class LoginPage(HTMLPage): + def login(self, login, password): + form = self.get_form(xpath='//form[@id="AuthentForm"]') + form['credential'] = login + form['password'] = password + form.submit() + diff --git a/modules/orange/module.py b/modules/orange/module.py index 8f60c11d..859e1eb8 100644 --- a/modules/orange/module.py +++ b/modules/orange/module.py @@ -18,18 +18,38 @@ # along with weboob. If not, see . +from weboob.capabilities.bill import CapBill, Subscription, Bill, SubscriptionNotFound, BillNotFound from weboob.capabilities.messages import CantSendMessage, CapMessages, CapMessagesPost +from weboob.capabilities.base import find_object from weboob.capabilities.account import CapAccount, StatusField from weboob.tools.backend import Module, BackendConfig from weboob.tools.value import ValueBackendPassword, Value from .browser import OrangeBrowser +from .bill.browser import OrangeBillBrowser __all__ = ['OrangeModule'] +# We need to have a switcher, CapMessages use a browser1 and +# CapBill use a browser2 +# This will be remove when CapMessages use a browser2 +def browser_switcher(b): + def set_browser(func): + def func_wrapper(*args, **kwargs): + self = args[0] + if self._browser is None or type(self._browser) != b: + self.BROWSER = b + try: + self._browser = self._browsers[b] + except KeyError: + self._browsers[b] = self.create_default_browser() + self._browser = self._browsers[b] + return func(*args, **kwargs) + return func_wrapper + return set_browser -class OrangeModule(Module, CapAccount, CapMessages, CapMessagesPost): +class OrangeModule(Module, CapAccount, CapMessages, CapMessagesPost, CapBill): NAME = 'orange' MAINTAINER = u'Lucas Nussbaum' EMAIL = 'lucas@lucas-nussbaum.net' @@ -38,21 +58,54 @@ class OrangeModule(Module, CapAccount, CapMessages, CapMessagesPost): LICENSE = 'AGPLv3+' CONFIG = BackendConfig(Value('login', label='Login'), ValueBackendPassword('password', label='Password'), - Value('phonenumber', Label='Phone number') + Value('phonenumber', label='Phone number', default='') ) - BROWSER = OrangeBrowser ACCOUNT_REGISTER_PROPERTIES = None + BROWSER = OrangeBrowser + + + def __init__(self, *args, **kwargs): + self._browsers = dict() + super(OrangeModule, self).__init__(*args, **kwargs) def create_default_browser(self): return self.create_browser(self.config['login'].get(), self.config['password'].get()) + @browser_switcher(OrangeBrowser) def get_account_status(self): - with self.browser: - return (StatusField('nb_remaining_free_sms', 'Number of remaining free SMS', + return (StatusField('nb_remaining_free_sms', 'Number of remaining free SMS', self.browser.get_nb_remaining_free_sms()),) + @browser_switcher(OrangeBrowser) def post_message(self, message): if not message.content.strip(): raise CantSendMessage(u'Message content is empty.') - with self.browser: - self.browser.post_message(message, self.config['phonenumber'].get()) + self.browser.post_message(message, self.config['phonenumber'].get()) + + @browser_switcher(OrangeBillBrowser) + def iter_subscription(self): + return self.browser.get_subscription_list() + + @browser_switcher(OrangeBillBrowser) + def get_subscription(self, _id): + return find_object(self.iter_subscription(), id=_id, error=SubscriptionNotFound) + + @browser_switcher(OrangeBillBrowser) + def get_bill(self, _id): + subid = _id.split('.')[0] + subscription = self.get_subscription(subid) + + return find_object(self.iter_bills(subscription), id=_id, error=BillNotFound) + + @browser_switcher(OrangeBillBrowser) + def iter_bills(self, subscription): + if not isinstance(subscription, Subscription): + subscription = self.get_subscription(subscription) + return self.browser.iter_bills(subscription) + + @browser_switcher(OrangeBillBrowser) + def download_bill(self, bill): + if not isinstance(bill, Bill): + bill = self.get_bill(bill) + return self.browser.open(bill._url).content +