diff --git a/modules/poivy/__init__.py b/modules/poivy/__init__.py new file mode 100644 index 00000000..9fccbea1 --- /dev/null +++ b/modules/poivy/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Florent Fourcot +# +# 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 PoivyBackend + +__all__ = ['PoivyBackend'] diff --git a/modules/poivy/backend.py b/modules/poivy/backend.py new file mode 100644 index 00000000..00821ce8 --- /dev/null +++ b/modules/poivy/backend.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Florent Fourcot +# +# 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.capabilities.bill import ICapBill, Subscription, SubscriptionNotFound, Detail +from weboob.capabilities.base import Currency +from weboob.tools.backend import BaseBackend, BackendConfig +from weboob.tools.value import ValueBackendPassword + +from .browser import PoivyBrowser + + +__all__ = ['PoivyBackend'] + + +class PoivyBackend(BaseBackend, ICapBill): + NAME = 'poivy' + MAINTAINER = u'Florent Fourcot' + EMAIL = 'weboob@flo.fourcot.fr' + VERSION = '0.h' + LICENSE = 'AGPLv3+' + DESCRIPTION = 'Poivy website' + CONFIG = BackendConfig(ValueBackendPassword('login', + label='login', + masked=False), + ValueBackendPassword('password', + label='Password') + ) + BROWSER = PoivyBrowser + + def create_default_browser(self): + return self.create_browser(self.config['login'].get(), + self.config['password'].get()) + + def iter_subscription(self): + for subscription in self.browser.get_subscription_list(): + yield subscription + + def get_subscription(self, _id): + with self.browser: + subscription = self.browser.get_subscription(_id) + if subscription: + return subscription + else: + raise SubscriptionNotFound() + + def iter_bills_history(self, subscription): + with self.browser: + for history in self.browser.get_history(): + yield history + + # No details on the website + def get_details(self, subscription): + raise NotImplementedError() + + def get_balance(self, subscription): + if not isinstance(subscription, Subscription): + subscription = self.get_subscription(subscription) + balance = Detail() + balance.id = "%s-balance" % subscription.id + balance.price = subscription._balance + balance.label = u"Balance %s" % subscription.id + balance.currency = Currency.CUR_EUR + return balance diff --git a/modules/poivy/browser.py b/modules/poivy/browser.py new file mode 100644 index 00000000..8e659f8e --- /dev/null +++ b/modules/poivy/browser.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Fourcot Florent +# +# 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 HomePage, LoginPage, HistoryPage, BillsPage + +__all__ = ['PoivyBrowser'] + + +class PoivyBrowser(BaseBrowser): + DOMAIN = 'www.poivy.com' + PROTOCOL = 'https' + ENCODING = None # refer to the HTML encoding + PAGES = {'.*login': LoginPage, + '.*buy_credit.*': HomePage, + '.*/recent_calls': HistoryPage, + '.*purchases': BillsPage + } + + def __init__(self, *args, **kwargs): + BaseBrowser.__init__(self, *args, **kwargs) + + def home(self): + self.location('/login') + + def is_logged(self): + return not self.is_on_page(LoginPage) + + def login(self): + assert isinstance(self.username, basestring) + assert isinstance(self.password, basestring) + + if not self.is_on_page(LoginPage): + self.location('/login') + + self.page.login(self.username, self.password) + + if self.is_on_page(LoginPage): + raise BrowserIncorrectPassword() + + def get_subscription_list(self): + if not self.is_on_page(HomePage): + self.location('/buy_credit') + + return self.page.get_list() + + def get_subscription(self, id): + assert isinstance(id, basestring) + + l = self.get_subscription_list() + for a in l: + if a.id == id: + return a + + return None + + def get_history(self): + if not self.is_on_page(HistoryPage): + self.location('/recent_calls') + return self.page.get_calls() + + def iter_bills(self, parentid): + if not self.is_on_page(BillsPage): + self.location('/purchases') + return self.page.date_bills() + + def get_bill(self, id): + assert isinstance(id, basestring) + + l = self.iter_bills(id) + for a in l: + if a.id == id: + return a diff --git a/modules/poivy/pages.py b/modules/poivy/pages.py new file mode 100644 index 00000000..45d16145 --- /dev/null +++ b/modules/poivy/pages.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Florent Fourcot +# +# 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 BasePage +from weboob.capabilities.base import Currency +from weboob.capabilities.bill import Subscription, Detail +from decimal import Decimal +from datetime import datetime, date, time + +import re + +__all__ = ['LoginPage', 'HomePage', 'HistoryPage', 'BillsPage'] + + +class LoginPage(BasePage): + def on_loaded(self): + pass + + def login(self, login, password): + # Form without name + self.browser.select_form(nr=1) + self.browser.set_all_readonly(False) + self.browser['login[username]'] = login.encode('iso-8859-1') + self.browser['login[password]'] = password.encode('iso-8859-1') + self.browser.submit(nologin=True) + + +class HomePage(BasePage): + def on_loaded(self): + pass + + def get_list(self): + spanabo = self.document.xpath('//span[@class="welcome-text"]/b')[0] + owner = spanabo.text_content() + credit = self.document.xpath('//span[@class="balance"]')[0].text_content() + + subscription = Subscription(owner) + subscription.label = u"Poivy - %s - %s" % (owner, credit) + subscription._balance = Decimal(re.sub(u'[^\d\-\.]', '', credit)) + + return [subscription] + + +class HistoryPage(BasePage): + months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + + def on_loaded(self): + pass + + def get_calls(self): + table = self.document.xpath('//table/tbody')[0] + for tr in table.xpath('tr'): + tds = tr.xpath('td') + + rawdate = tds[0].text_content() + splitdate = rawdate.split('-') + month_no = self.months.index(splitdate[1]) + 1 + mydate = date(int(splitdate[2]), month_no, int(splitdate[0])) + + rawtime = tds[1].text_content() + mytime = time(*[int(x) for x in rawtime.split(":")]) + + price = re.sub(u'[^\d\-\.]', '', tds[6].text) + detail = Detail() + detail.datetime = datetime.combine(mydate, mytime) + detail.label = u"%s from %s to %s - %s" % (tds[2].text, tds[3].text, tds[4].text, tds[5].text) + detail.price = Decimal(price) + detail.currency = Currency.CUR_EUR + + yield detail + + +class BillsPage(BasePage): + def on_loaded(self): + pass