diff --git a/modules/nettokom/__init__.py b/modules/nettokom/__init__.py
new file mode 100644
index 00000000..7b713f05
--- /dev/null
+++ b/modules/nettokom/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012 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 NettoKomBackend
+
+__all__ = ['NettoKomBackend']
diff --git a/modules/nettokom/backend.py b/modules/nettokom/backend.py
new file mode 100644
index 00000000..bfac02e2
--- /dev/null
+++ b/modules/nettokom/backend.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012 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 __future__ import with_statement
+
+from weboob.capabilities.bill import ICapBill, SubscriptionNotFound
+from weboob.tools.backend import BaseBackend, BackendConfig
+from weboob.tools.value import ValueBackendPassword
+
+from .browser import Nettokom
+
+
+__all__ = ['NettoKomBackend']
+
+
+class NettoKomBackend(BaseBackend, ICapBill):
+ NAME = 'nettokom'
+ MAINTAINER = 'Florent Fourcot'
+ EMAIL = 'weboob@flo.fourcot.fr'
+ VERSION = '0.c'
+ LICENSE = 'AGPLv3+'
+ DESCRIPTION = 'Nettokom website'
+ CONFIG = BackendConfig(ValueBackendPassword('login',
+ label='Account ID (phone number)',
+ masked=False,
+ regexp='^(\d{8,13}|)$'),
+ ValueBackendPassword('password',
+ label='Password')
+ )
+ BROWSER = Nettokom
+
+ 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):
+ if not _id.isdigit():
+ raise SubscriptionNotFound()
+ with self.browser:
+ subscription = self.browser.get_subscription(_id)
+ if subscription:
+ return subscription
+ else:
+ raise SubscriptionNotFound()
+
+ def iter_history(self, subscription):
+ with self.browser:
+ for history in self.browser.get_history():
+ yield history
+
+ # The subscription is actually useless, but maybe for the futur...
+ def get_details(self, subscription):
+ with self.browser:
+ for detail in self.browser.get_details():
+ yield detail
diff --git a/modules/nettokom/browser.py b/modules/nettokom/browser.py
new file mode 100644
index 00000000..fa30e02e
--- /dev/null
+++ b/modules/nettokom/browser.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012 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, DetailsPage, BillsPage
+
+__all__ = ['Nettokom']
+
+
+class Nettokom(BaseBrowser):
+ DOMAIN = 'konto.nettokom.de'
+ PROTOCOL = 'https'
+ ENCODING = None # refer to the HTML encoding
+ PAGES = {'.*login.html.*': LoginPage,
+ '.*start.html': HomePage,
+ '.*guthabenverbrauch.html': DetailsPage,
+ '.*/verbindungsnachweis/.*': HistoryPage,
+ '.*verbindungsnachweis.html': BillsPage
+ }
+
+ def __init__(self, *args, **kwargs):
+ BaseBrowser.__init__(self, *args, **kwargs)
+
+ def home(self):
+ self.location('/start.html')
+
+ def is_logged(self):
+ return not self.is_on_page(LoginPage)
+
+ def login(self):
+ assert isinstance(self.username, basestring)
+ assert isinstance(self.password, basestring)
+ assert self.username.isdigit()
+
+ if not self.is_on_page(LoginPage):
+ self.location('/login.html')
+
+ 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('/start.html')
+
+ return self.page.get_list()
+
+ def get_subscription(self, id):
+ assert isinstance(id, basestring)
+
+ if not self.is_on_page(HomePage):
+ self.location('/start.html')
+
+ l = self.page.get_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('/verbindungsnachweis/alle-verbindungen.html')
+ return self.page.get_calls()
+
+ def get_details(self):
+ if not self.is_on_page(DetailsPage):
+ self.location('/guthabenverbrauch.html')
+ return self.page.get_details()
+
+ def iter_bills(self, parentid):
+ if not self.is_on_page(BillsPage):
+ self.location('/verbindungsnachweis.html')
+ return self.page.date_bills()
+
+ def get_bill(self, id):
+ assert isinstance(id, basestring)
+
+ if not self.is_on_page(BillsPage):
+ self.location('/verbindungsnachweis.html')
+ l = self.page.date_bills()
+ for a in l:
+ if a.id == id:
+ return a
+
+# Todo : url depends of file format
+# def download_bill(self, id):
+# assert isinstance(id, basestring)
+# date = id.split('.')[1]
+#
+# return self.readurl('/moncompte/ajax.php?page=facture&mode=html&date=' + date)
diff --git a/modules/nettokom/pages/__init__.py b/modules/nettokom/pages/__init__.py
new file mode 100644
index 00000000..996912ad
--- /dev/null
+++ b/modules/nettokom/pages/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012 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 .homepage import HomePage
+from .history import HistoryPage, DetailsPage, BillsPage
+from .login import LoginPage
+
+__all__ = ['LoginPage', 'HomePage', 'HistoryPage', 'DetailsPage', 'BillsPage']
diff --git a/modules/nettokom/pages/history.py b/modules/nettokom/pages/history.py
new file mode 100644
index 00000000..d00a29de
--- /dev/null
+++ b/modules/nettokom/pages/history.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012 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 datetime import datetime, date, time
+from decimal import Decimal
+
+from weboob.tools.browser import BasePage
+from weboob.capabilities.bill import Detail
+
+
+__all__ = ['HistoryPage', 'DetailsPage', 'BillsPage']
+
+
+class DetailsPage(BasePage):
+
+ def on_loaded(self):
+ self.details = []
+ table = self.document.xpath('//table[@id="reportTable"]')[0]
+
+ for tr in table.xpath('tbody/tr'):
+ detail = Detail()
+ # Skip global category
+ if tr.find('td/a') is not None:
+ continue
+ if tr.attrib["class"] == "totalAmount":
+ continue
+ tds = tr.xpath('td')
+ detail.label = unicode(tds[0].text.strip())
+ detail.infos = unicode(tds[1].text.strip())
+ detail.price = Decimal(tds[2].text.split(' ')[0].replace(',', '.'))
+
+ self.details.append(detail)
+
+ def get_details(self):
+ return self.details
+
+
+def _get_date(detail):
+ return detail.datetime
+
+
+class BillsPage(BasePage):
+ def on_loaded(self):
+ pass
+
+
+class HistoryPage(BasePage):
+
+ def on_loaded(self):
+ self.calls = []
+ for tr in self.document.xpath('//tr'):
+ try:
+ attrib = tr.attrib["class"]
+ except:
+ continue
+ if attrib == "even" or attrib == "odd":
+ label = u''
+ tddate = tr.find('td[@class="middle nowrap"]')
+ for td in tr.xpath('td[@class="long"]'):
+ label += unicode(td.text.strip()) + u' '
+ tdprice = tr.xpath('td[@class="price"]')
+ label += u'(' + unicode(tdprice[0].text.strip()) + u')'
+ price = Decimal(tdprice[1].text.strip().replace(',', '.'))
+ detail = Detail()
+ mydate = date(*reversed([int(x) for x in tddate.text.strip().split(' ')[0].split(".")]))
+ mytime = time(*[int(x) for x in tddate.text.strip().split(' ')[1].split(":")])
+ detail.datetime = datetime.combine(mydate, mytime)
+ detail.label = label
+ detail.price = price
+
+ self.calls.append(detail)
+
+ def get_calls(self):
+ return sorted(self.calls, key=_get_date, reverse=True)
diff --git a/modules/nettokom/pages/homepage.py b/modules/nettokom/pages/homepage.py
new file mode 100644
index 00000000..48bc3e36
--- /dev/null
+++ b/modules/nettokom/pages/homepage.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012 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 Subscription
+from weboob.tools.browser import BasePage
+
+
+__all__ = ['HomePage']
+
+
+class HomePage(BasePage):
+ def on_loaded(self):
+ pass
+
+ def get_list(self):
+ l = []
+ divabo = self.document.xpath('//div[@id="accountSummary"]')[0]
+ owner = divabo.xpath('a/h3')[0].text
+ phone = divabo.xpath('dl/dd')[0].text
+ credit = divabo.xpath('dl/dd')[1].text
+ expiredate = divabo.xpath('dl/dd')[2].text
+ phoneplan = divabo.xpath('dl/dd')[3].text
+ self.browser.logger.debug('Found ' + owner + ' has subscriber')
+ self.browser.logger.debug('Found ' + phone + ' has phone number')
+ self.browser.logger.debug('Found ' + credit + ' has available credit')
+ self.browser.logger.debug('Found ' + expiredate + 'has expire date ')
+ self.browser.logger.debug('Found ' + phoneplan + ' has subscription type')
+
+ subscription = Subscription(phone)
+ subscription.label = unicode(phone + u' - ' + credit + u' - ' + expiredate + u' - ' + phoneplan)
+ subscription.owner = owner
+
+ l.append(subscription)
+
+ return l
diff --git a/modules/nettokom/pages/login.py b/modules/nettokom/pages/login.py
new file mode 100644
index 00000000..dccd5e04
--- /dev/null
+++ b/modules/nettokom/pages/login.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2012 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
+
+__all__ = ['LoginPage']
+
+
+class LoginPage(BasePage):
+ def on_loaded(self):
+ pass
+
+ def login(self, login, password):
+ self.browser.select_form(nr=0)
+ self.browser.set_all_readonly(False)
+ self.browser['quickLoginNumber'] = login
+ self.browser['quickLoginPassword'] = password
+ self.browser.submit(nologin=True)