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