From f688786e35425d93c2f26a1734f7d6d3e44703b1 Mon Sep 17 00:00:00 2001 From: Florent Date: Fri, 13 Apr 2012 15:44:13 +0200 Subject: [PATCH] First implementation of nettokom website --- modules/nettokom/__init__.py | 23 ++++++ modules/nettokom/backend.py | 75 ++++++++++++++++++++ modules/nettokom/browser.py | 109 +++++++++++++++++++++++++++++ modules/nettokom/pages/__init__.py | 25 +++++++ modules/nettokom/pages/history.py | 91 ++++++++++++++++++++++++ modules/nettokom/pages/homepage.py | 51 ++++++++++++++ modules/nettokom/pages/login.py | 35 +++++++++ 7 files changed, 409 insertions(+) create mode 100644 modules/nettokom/__init__.py create mode 100644 modules/nettokom/backend.py create mode 100644 modules/nettokom/browser.py create mode 100644 modules/nettokom/pages/__init__.py create mode 100644 modules/nettokom/pages/history.py create mode 100644 modules/nettokom/pages/homepage.py create mode 100644 modules/nettokom/pages/login.py 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)