[weboob] [PATCH 1/1] add library capability and backend and application for archimede software aloes http://www.archimed.fr/aloes/presentation-et-avantages-12.html (second try)

jems jems at ldjm.fr
Tue Mar 13 00:48:58 CET 2012


Signed-off-by: jems <jems at ldjm.fr>
---
 modules/opacwebaloes/__init__.py        |   23 ++++
 modules/opacwebaloes/backend.py         |   71 +++++++++++++
 modules/opacwebaloes/browser.py         |   77 ++++++++++++++
 modules/opacwebaloes/favicon.png        |  Bin 0 -> 2401 bytes
 modules/opacwebaloes/pages.py           |   97 ++++++++++++++++++
 modules/opacwebaloes/test.py            |   27 +++++
 weboob/applications/library/__init__.py |   23 ++++
 weboob/applications/library/library.py  |  170 +++++++++++++++++++++++++++++++
 weboob/capabilities/library.py          |   58 +++++++++++
 9 files changed, 546 insertions(+), 0 deletions(-)
 create mode 100644 modules/opacwebaloes/__init__.py
 create mode 100644 modules/opacwebaloes/backend.py
 create mode 100644 modules/opacwebaloes/browser.py
 create mode 100644 modules/opacwebaloes/favicon.png
 create mode 100644 modules/opacwebaloes/pages.py
 create mode 100644 modules/opacwebaloes/test.py
 create mode 100644 weboob/applications/library/__init__.py
 create mode 100644 weboob/applications/library/library.py
 create mode 100644 weboob/capabilities/library.py

diff --git a/modules/opacwebaloes/__init__.py b/modules/opacwebaloes/__init__.py
new file mode 100644
index 0000000..bb0ece2
--- /dev/null
+++ b/modules/opacwebaloes/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2011 Jeremy Monnet
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+from .backend import AloesBackend
+
+__all__ = ['AloesBackend']
diff --git a/modules/opacwebaloes/backend.py b/modules/opacwebaloes/backend.py
new file mode 100644
index 0000000..bec9fdb
--- /dev/null
+++ b/modules/opacwebaloes/backend.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2011  Jeremy Monnet
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+from __future__ import with_statement
+
+from weboob.capabilities.library import ICapBook
+from weboob.tools.backend import BaseBackend, BackendConfig
+from weboob.tools.value import ValueBackendPassword, Value
+
+from .browser import AloesBrowser
+
+
+__all__ = ['AloesBackend']
+
+
+class AloesBackend(BaseBackend, ICapBook):
+    NAME = 'opacwebaloes'
+    MAINTAINER = u'Jeremy Monnet'
+    EMAIL = 'jmonnet at gmail.com'
+    VERSION = '0.b'
+    DESCRIPTION = 'Aloes Library software'
+    LICENSE = 'AGPLv3+'
+    CONFIG = BackendConfig(Value('login',    label='Account ID', regexp='^\d{1,6}\w$'),
+                           ValueBackendPassword('password', label='Password of account'),
+                           Value('baseurl',    label='Base URL')
+                           )
+    BROWSER = AloesBrowser
+
+    def create_default_browser(self):
+        return self.create_browser(self.config['baseurl'].get(),
+                                   self.config['login'].get(),
+                                   self.config['password'].get())
+
+    def get_rented(self):
+        for book in self.browser.get_rented_books_list():
+            yield book 
+
+    def get_booked(self):
+        for book in self.browser.get_booked_books_list():
+            yield book
+
+    def iter_books(self):
+        self.browser.login()
+        for book in self.get_booked():
+            yield book
+        for book in self.get_rented():
+            yield book
+
+    def get_book(self, _id):
+        raise NotImplementedError()
+
+    def search_books(self, _string):
+        raise NotImplementedError()
+
diff --git a/modules/opacwebaloes/browser.py b/modules/opacwebaloes/browser.py
new file mode 100644
index 0000000..a91ed83
--- /dev/null
+++ b/modules/opacwebaloes/browser.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2012  Jeremy Monnet
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
+
+from .pages import LoginPage, HomePage, RentedPage, HistoryPage, BookedPage
+
+
+__all__ = ['AloesBrowser']
+
+
+# Browser
+class AloesBrowser(BaseBrowser):
+    PROTOCOL = 'http'
+    ENCODING = 'utf-8'
+    USER_AGENT = BaseBrowser.USER_AGENTS['desktop_firefox']
+    #DEBUG_HTTP = True
+    DEBUG_HTTP = False
+    PAGES = {
+        'http://.*/index.aspx': LoginPage,
+        'http://.*/index.aspx\?IdPage=1': HomePage,
+        'http://.*/index.aspx\?IdPage=45': RentedPage,
+        'http://.*/index.aspx\?IdPage=429': HistoryPage,
+        'http://.*/index.aspx\?IdPage=44': BookedPage
+        }
+
+    def __init__(self, baseurl, *args, **kwargs):
+        self.BASEURL=baseurl
+        BaseBrowser.__init__(self, *args, **kwargs)
+
+    def is_logged(self):
+        return self.page and not self.is_on_page(LoginPage)
+        #return True
+
+    def login(self):
+        assert isinstance(self.username, basestring)
+        assert isinstance(self.password, basestring)
+        if not self.is_on_page(HomePage):
+            self.location('%s://%s/index.aspx' \
+                          % (self.PROTOCOL, self.BASEURL),
+                          no_login=True)
+        if  not self.page.login(self.username, self.password) or \
+            not self.is_logged() or \
+            (self.is_on_page(LoginPage) and self.page.is_error()) :
+            raise BrowserIncorrectPassword()
+
+  
+    def get_rented_books_list(self):
+        if not self.is_on_page(RentedPage):
+            self.location('%s://%s/index.aspx?IdPage=45' \
+                      % (self.PROTOCOL, self.BASEURL),
+                      no_login=True)
+        return self.page.get_list()
+
+    def get_booked_books_list(self):
+        if not self.is_on_page(BookedPage):
+           self.location('%s://%s/index.aspx?IdPage=44' \
+                          % (self.PROTOCOL, self.BASEURL),
+                          no_login=True)
+        return self.page.get_list()
diff --git a/modules/opacwebaloes/favicon.png b/modules/opacwebaloes/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4a865bb897b88000137c6b48759c0cde740db62
GIT binary patch
literal 2401
zcmV-n37+<eP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igh@
z5HL8Dbsy~j000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM at dakSAh-}000QQNkl<Z
zc-rlodvH|c8ODF-oV_O5Yyu=i0zx(<3P=!!OAv`nf#F&*wGvtpP}+K#wxgY5)v?x2
zN2kol*r5Wg6hzvh6>Ljup`C&kw2%-fSR at eGkQ;=sS(43V?|Ux&<1o7{$i=!#WtaEA
zbH4BFp7WgddEU$S9l>mULL-e$B+cz605<`+3BXMN(%KA{8z`GLj20n8u-#9%u at 5b*
zrjG}RHX|#Zo5kpbBgmRjh-fpW7X_N?pQlX-<Mi~Tp9bu%egaoG$m-f7pn-RH8){!9
zJv6}NuwfsV#nbQA0-$)pgz9_J#{-;upW?8)i#oRlP0{ezbfh=|Oz{8=v}f|{`yT@!
zy8`(4x>6i~a14N;{}-&@|7mn6 at b7c!l?Zk91vuW>4S*U{(%|i11VBHVm6jf_4uCI0
zx>%eB)VV!6Gn~EY1%Sz617K8MuC51lp7a7>!kkiEK0h;x3Q at X4$Y%rT1;DKF4$xqC
zQyU0EN at sqboe4@p^n at CauX<63b8Fmjeo58;d;>!YeJr}SnTp9SiXDCA+QZ0-K<8OA
z9Y-vjIiA6WT_x;o$WO|B7yvSjMnpjeL9`mK3-U&z!jD&c%6FDJF&N?-(*}b^VQzpz
zHNe<B4|5)A<K9=Mv9C2hITZMA<#^1JM4j7%(j89v!<$VCf7tjDk1aoO$-`e_3W(kC
z_ToJ}Jn=+wDA4T-(kJV at 7mUeo6217`A*N1_)ft)wZ)_{#cUv7CcV?ri0wWwfET7)M
zLVF8>1_n`M(dcH@*On$80Fx;4QuSfoH3wxN+KfpP!jZRivt&_Y+_~@nXd+v;4~si@
z at MwPy9_`O<-|A&dfgYAe7j*OF)FZs~(H)7UK!>Z3RWr(CE_%B?X>b4j`<f79n#*5y
z4!PvvFSO@(jbU#~UiA3MilgLbec>=I10ldC0<^Srajd;FRwR}r4}iNS#h2K>-&%CV
z`TJV)d1QMz{)iN-TC;+Q at O(31iW1)Ii;16PHzf^#VYm9?j{kEc_o~+n%nFiWRP->d
zL0Lh<GF79f$cK6Gp=X$K``{Qnd6uLBkeLymXY_fm-f(q!ErewKU5NtBE@?}g254G0
zr%pAoXKw>X9$l!P%rGRa6YTTH=NY!FtBY-i*GfaDEqYvF3nmT#QMw&fImN7*YsoUj
z9y)VsK~ex{Zq1B49&^XFxpQwQc5gOwB!K{kdUo~DgT=UdebI{0QkI((0IF-_v$+SV
znyz}y(y=F*G~`@V1J>2tmiT_a#@DJDm>qjNQjR%VCMVLiY%4;$Ao;7Ddy0jX&6l13
z^|BUL&OL~DK6L(a-w1wts5rp at 2xcti{nQ_Kv9e-9bkH(xK*9kydOU|Ww#A3OH80ij
z@{1ocwycv3tBlnmGorMc*B}2E>nitSKu4_8Z(_rN;fc=#MR73M+1UjAVRqHE@%Y3$
zSY3Mr<DiUd`jLyPvWxBFD(#t{jiqR)m%Appb^HaL3YXB~E#Q346IMii{=sNEyw-%>
zN7MsE$-$YnLrk9XGC%n01oAVj(X==jGzf+aR4uRIH=9dP6yXZB^7#@y2ddtvNT};y
z<N>-c*|Tft=?Tz&x;qvPrVk?Acs${M91cr7`@&e(Z!Tfc*P6L|at9 at jUUKXaWLd!7
zX`#Vq<M5eW-g<2q&i3r2h_Z0uF0oKxJzKVHqpE5aZyjrlE|T=V)0(nv^0?YpGL*4o
z>C<d_eLpjX4vL1v%zFw_c8UCw;$Kzjc<iws>LF3pokPktku_sb>H{E34#0#WA7Ig<
zmF!qNH#(LvDL*NK_~imX%*Skf6j0c?b2X}}QM0TvI-Y%BQObevnf)(8$ieG9%bRcR
zB-3C(?h5H4qo0|~>8V8p;c|P0!@<o6;$Y6XYp__2j99-dI at a^2W&*YD)b#)WgJ}+`
za*{P`KSYiw6uV+L+2##N87hdEFB9`ITOZ~p&&@%SM7~!(n#gIrn<JW~l+gp>YTM=I
zNDYd77)|rh>G836>$}v4^ksCpD at 5<xO at tf!QqKdTR4|qUegHH at W98KGaiW;BWH=d9
z2BxkD#6)om2M+v$apT76eY!qBlQwNX4~&X$&sqG{9XH&<s$?*h|Mc1>ma%T_a#U5L
zv~(<4Rue`+V1K(CK@>0**)Wx4gQ}tSMQ)g_wZiB5WJZqE05ED~fJ284^2yP5f>we1
zcYF{xFVgPEzt2UezUNEt5!dVida67aQ5sD9nGbO~>sj_d6-9Y=`t at jZC}G?|T^l5u
z5zEM2tP=-d9BM=Di=cSIH@*j4XraJ*z|W+~tN8ODzm3b|<Ca1@%m2EEI!|}pTrI2+
zX%8T~g9HzrOG?x*Icyk<Y($#+KG*LHU-YXnIa0&;3HR}<wN+RwMnam(og)WP-FlX9
zZh!BR`C3Fp4Jjy{A++H6)t;bCxS^NGX&+*iUUti815x^jHY0`tYjp2|Xg6UTl8IzD
zVaT%}W*E?96~Q2~e_17!bAHVypPas4092zYjUbqL_0=`ZpEr#S8>^W&e-u)Nf!(cV
zc+cGt-GTObObyDQ0+N7emaaH=R9-Hd7EGhzOc!4nv+DYJ0D!7?AV)p~EyTp~r<gx)
z6!Yee!kS^C|A0(9-XK9#ysE<e at 4TNdx34T6%EZEa9J$%tIj}!YmxsG&zjA#k at LBnS
zrn!*}<(SQ<Sn at y>Cr)&5*Ock3Sn&;V?0Uo1+1`aee14r!6nM9}4Z$d)$tsg?EdWuV
z=Ct01o9P(LKb at zd4VsxHLl|AqAAcx9uQ$jEr<+p805)u{;f3e_etksnTBspP#i&Xn
z6&1zQAFH9g{Uo>CQp|z{3osZZvTbJWxo0K-A0LRV`A?lX96=J;d$<+BD6(VMF=mf1
zMhokj at wXjy*Ua(%8vs|LOC+=%Q7T3dGEtS2x{6yC-&kQVr8`150k{c3`q+N~-&bYs
TENKIt00000NkvXXu0mjf3RQQI

literal 0
HcmV?d00001

diff --git a/modules/opacwebaloes/pages.py b/modules/opacwebaloes/pages.py
new file mode 100644
index 0000000..435cf7b
--- /dev/null
+++ b/modules/opacwebaloes/pages.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2012  Jeremy Monnet
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+from datetime import date
+from weboob.capabilities.library import Book
+from weboob.tools.browser import BasePage, BrowserUnavailable
+from weboob.tools.mech import ClientForm
+
+class SkipPage(BasePage):
+    pass
+
+class HomePage(BasePage):
+    pass
+
+class RentedPage(BasePage):
+    # TODO, table limited to 20 items, need to use pagination
+    def get_list(self):
+        l = []
+	for tr in self.document.getroot().xpath('//tr[contains(@id, "ctl00_ContentPlaceHolder1_ctl00_ctl07_COMPTE_PRET_1_1_GrillePrets_ctl00__")]'):
+            book = Book(tr[1].text, name=tr[4].text, author=tr[5].text, date=tr[6].text, late="N")
+            l.append(book)
+        for tr in self.document.getroot().xpath('//tr[contains(@id, "ctl00_ContentPlaceHolder1_ctl00_ctl08_COMPTE_RETARD_0_1_GrilleRetards_ctl00__")]'):
+            book = Book(tr[0].text, name=tr[3].text, author=tr[4].text, date=tr[5].text, late="Y")
+            l.append(book)
+        return l
+
+      
+
+class HistoryPage(BasePage):
+    pass
+
+class BookedPage(BasePage):
+    # TODO, table limited to 20 items, need to use pagination
+    def get_list(self):
+        l = []
+	for tr in self.document.getroot().xpath('//tr[contains(@id, "ctl00_ContentPlaceHolder1_ctl00_ctl09_COMPTE_INFOS_0_GrilleInfos_ctl00__0")]'):
+            username=tr[1].text+"_"+tr[0].text
+	for tr in self.document.getroot().xpath('//tr[contains(@id, "ctl00_ContentPlaceHolder1_ctl00_ctl10_COMPTE_RESA_1_1_GrilleResas_ctl00__")]'):
+            # if all the books booked are available, there are only 7 columns.
+            # if (at least ?) one book is still not available, yous can cancel, and the first column does contain the checkbox. So 8 columns.
+            if (len(tr) == 7):
+                book = Book(username+str(len(l)), name=tr[2].text, author=tr[3].text, date=tr[5].text, late="N")
+            if (len(tr) == 8):
+                book = Book(username+str(len(l)), name=tr[3].text, author=tr[4].text, date=tr[6].text, late="N")
+            l.append(book)
+        return l
+
+class LoginPage(BasePage):
+    def login(self, login, passwd):
+        self.browser.select_form(
+            predicate=lambda x: x.attrs.get('id','')=='aspnetForm')
+        self.browser.form.set_all_readonly(False)
+        self.browser['ctl00$ContentPlaceHolder1$ctl00$ctl04$ctl00$TextSaisie'] = login
+        self.browser['ctl00$ContentPlaceHolder1$ctl00$ctl04$ctl00$TextPass'] = passwd
+        self.browser['ctl00_ScriptManager1_TSM']="%3B%3BSystem.Web.Extensions%2C%20Version%3D1.0.61025.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3D31bf3856ad364e35%3Afr-FR%3A1f0f78f9-0731-4ae9-b308-56936732ccb8%3Aea597d4b%3Ab25378d2%3BTelerik.Web.UI%2C%20Version%3D2009.3.1314.20%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3D121fae78165ba3d4%3Afr-FR%3Aec1048f9-7413-49ac-913a-b3b534cde186%3A16e4e7cd%3Aed16cbdc%3Af7645509%3A24ee1bba%3A19620875%3A874f8ea2%3A33108d14%3Abd8f85e4"
+        self.browser.controls.append(ClientForm.TextControl('text', 'RadAJAXControlID', {'value': ''}))
+        self.browser['RadAJAXControlID']="ctl00_ContentPlaceHolder1_ctl00_ctl04_ctl00_RadAjaxPanelConnexion"
+        self.browser.controls.append(ClientForm.TextControl('text', 'ctl00$ScriptManager1', {'value': ''}))
+        self.browser['ctl00$ScriptManager1']="ctl00$ContentPlaceHolder1$ctl00$ctl04$ctl00$ctl00$ContentPlaceHolder1$ctl00$ctl04$ctl00$RadAjaxPanelConnexionPanel|"
+        self.browser.controls.append(ClientForm.TextControl('text', '__EVENTTARGET', {'value': ''}))
+        self.browser.controls.append(ClientForm.TextControl('text', '__EVENTARGUMENT', {'value': ''}))
+        self.browser.controls.append(ClientForm.TextControl('text', 'ctl00$ContentPlaceHolder1$ctl00$ctl04$ctl00$btnImgConnexion.x', {'value': ''}))
+        self.browser['ctl00$ContentPlaceHolder1$ctl00$ctl04$ctl00$btnImgConnexion.x']="76"
+        self.browser.controls.append(ClientForm.TextControl('text', 'ctl00$ContentPlaceHolder1$ctl00$ctl04$ctl00$btnImgConnexion.y', {'value': ''}))
+        self.browser['ctl00$ContentPlaceHolder1$ctl00$ctl04$ctl00$btnImgConnexion.y']="10"
+        
+        try:
+            self.browser.submit()      
+        except BrowserUnavailable:
+            # Login is not valid
+            return False
+        return True
+
+    def is_error(self):
+        for text in self.document.find('body').itertext():
+            text=text.strip()
+            # Login seems valid, but password does not
+            needle='Echec lors de l\'authentification'
+            if text.startswith(needle.decode('utf-8')):
+                return True
+        return False
diff --git a/modules/opacwebaloes/test.py b/modules/opacwebaloes/test.py
new file mode 100644
index 0000000..d105c76
--- /dev/null
+++ b/modules/opacwebaloes/test.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2011 Jeremy Monnet
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+from weboob.tools.test import BackendTest
+
+class AloestTest(BackendTest):
+    BACKEND = 'aloes'
+
+    def test_aloes(self):
+        pass
diff --git a/weboob/applications/library/__init__.py b/weboob/applications/library/__init__.py
new file mode 100644
index 0000000..463e89d
--- /dev/null
+++ b/weboob/applications/library/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2011 Jérémy Monnet
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+from .library import Biboob
+
+__all__ = ['Biboob']
diff --git a/weboob/applications/library/library.py b/weboob/applications/library/library.py
new file mode 100644
index 0000000..06e10b4
--- /dev/null
+++ b/weboob/applications/library/library.py
@@ -0,0 +1,170 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2009-2012  Jeremy Monnet
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+from weboob.capabilities.library import ICapBook, Book
+from weboob.tools.application.repl import ReplApplication
+from weboob.tools.application.formatters.iformatter import IFormatter
+
+
+__all__ = ['Biboob']
+
+class RentedListFormatter(IFormatter):
+    MANDATORY_FIELDS = ('id', 'date', 'author', 'name', 'late')
+
+    count = 0
+
+    def flush(self):
+        self.count = 0
+
+    def format_dict(self, item):
+        self.count += 1
+
+        if self.interactive:
+            backend = item['id'].split('@', 1)[1]
+            id = '#%d (%s)' % (self.count, backend)
+        else:
+            id = item['id']
+	
+        return u'%s %s %s %s %s %s %s' % (self.BOLD, id, self.NC, item['author'], item['name'], item['date'], item['late'])
+
+class Biboob(ReplApplication):
+    APPNAME = 'biboob'
+    VERSION = '0.b'
+    COPYRIGHT = 'Copyright(C) 2010-2012 Jeremy Monnet'
+    CAPS = ICapBook
+    DESCRIPTION = "Console application allowing to list your books rented or booked at the library, " \
+                  "book and search new ones, get your booking history (if available)." 
+    EXTRA_FORMATTERS = {'rented_list':   RentedListFormatter,
+                        }
+    DEFAULT_FORMATTER = 'table'
+    COMMANDS_FORMATTERS = {'ls':          'rented_list',
+                           'list':        'rented_list',
+                          }
+                          
+    COLLECTION_OBJECTS = (Book, )
+
+    def do_list(self, line):
+        """
+        list
+
+        List books.
+        """
+        self.options.count = None
+        return self.do_ls(line)
+
+    #def complete_history(self, text, line, *ignored):
+        #args = line.split(' ')
+        #if len(args) == 2:
+            #return self._complete_account()
+
+    #def do_history(self, id):
+        #"""
+        #history ID
+
+        #Display old operations.
+        #"""
+        #id, backend_name = self.parse_id(id)
+        #if not id:
+            #print >>sys.stderr, 'Error: please give an account ID (hint: use list command)'
+            #return 2
+        #names = (backend_name,) if backend_name is not None else None
+
+        #def do(backend):
+            #account = backend.get_account(id)
+            #return backend.iter_history(account)
+
+        #for backend, operation in self.do(do, backends=names):
+            #self.format(operation)
+        #self.flush()
+
+    #def complete_coming(self, text, line, *ignored):
+        #args = line.split(' ')
+        #if len(args) == 2:
+            #return self._complete_account()
+
+    #def do_coming(self, id):
+        #"""
+        #coming ID
+
+        #Display all future operations.
+        #"""
+        #id, backend_name = self.parse_id(id)
+        #names = (backend_name,) if backend_name is not None else None
+
+        #def do(backend):
+            #account = backend.get_account(id)
+            #return backend.iter_operations(account)
+
+        #for backend, operation in self.do(do, backends=names):
+            #self.format(operation)
+        #self.flush()
+
+    #def complete_transfer(self, text, line, *ignored):
+        #args = line.split(' ')
+        #if len(args) == 2:
+            #return self._complete_account()
+        #if len(args) == 3:
+            #return self._complete_account(args[1])
+
+    #def do_transfer(self, line):
+        #"""
+        #transfer ACCOUNT [RECIPIENT AMOUNT [REASON]]
+
+        #Make a transfer beetwen two account
+        #- ACCOUNT    the source account
+        #- RECIPIENT  the recipient
+        #- AMOUNT     amount to transfer
+        #- REASON     reason of transfer
+
+        #If you give only the ACCOUNT parameter, it lists all the
+        #available recipients for this account.
+        #"""
+        #id_from, id_to, amount, reason = self.parse_command_args(line, 4, 1)
+
+        #id_from, backend_name_from = self.parse_id(id_from)
+        #if not id_to:
+            #self.objects = []
+            #self.set_formatter('recipient_list')
+            #self.set_formatter_header(u'Available recipients')
+            #names = (backend_name_from,) if backend_name_from is not None else None
+            #for backend, recipient in self.do('iter_transfer_recipients', id_from, backends=names):
+                #self.format(recipient)
+                #self.add_object(recipient)
+            #self.flush()
+            #return 0
+
+        #id_to, backend_name_to = self.parse_id(id_to)
+
+        #try:
+            #amount = float(amount)
+        #except (TypeError,ValueError):
+            #print >>sys.stderr, 'Error: please give a decimal amount to transfer'
+            #return 2
+
+        #if backend_name_from != backend_name_to:
+            #print >>sys.stderr, "Transfer between different backends is not implemented"
+            #return 4
+        #else:
+            #backend_name = backend_name_from
+
+        #names = (backend_name,) if backend_name is not None else None
+
+        #for backend, transfer in self.do('transfer', id_from, id_to, amount, reason, backends=names):
+            #self.format(transfer)
+        #self.flush()
diff --git a/weboob/capabilities/library.py b/weboob/capabilities/library.py
new file mode 100644
index 0000000..7881f92
--- /dev/null
+++ b/weboob/capabilities/library.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2010-2012 Jeremy Monnet
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+from datetime import datetime
+
+from .collection import ICapCollection, CollectionNotFound
+from .base import CapBaseObject, NotLoaded
+
+__all__ = ['ICapBook', 'Book']
+
+class Book(CapBaseObject):
+    def __init__(self, id, name=NotLoaded, author=NotLoaded, pages=NotLoaded, url=NotLoaded, date=NotLoaded, location=NotLoaded, late=NotLoaded):
+        CapBaseObject.__init__(self, id)
+        self.add_field('name', basestring, name)
+        self.add_field('author', basestring, author)
+        self.add_field('location', basestring, location)
+        self.add_field('date', datetime, date) # which may be the due date
+        self.add_field('late', basestring, late)
+
+class ICapBook(ICapCollection):
+  
+    def iter_resources(self, objs, split_path):
+        if Book in objs:
+            if len(split_path) > 0:
+                raise CollectionNotFound(split_path)
+
+            return self.iter_books()
+
+    def iter_books(self, pattern):
+        raise NotImplementedError()
+
+    def get_book(self, _id):
+        raise NotImplementedError()
+
+    def get_booked(self, _id):
+        raise NotImplementedError()
+
+    def get_rented(self, _id):
+        raise NotImplementedError()
+
+    def search_books(self, _string):
+        raise NotImplementedError()
-- 
1.7.2.5




More information about the weboob mailing list