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

jems jems at ldjm.fr
Fri Mar 2 10:51:00 CET 2012


Signed-off-by: jems <jems at ldjm.fr>
---
 modules/opacwebaloes/__init__.py |   23 +++++
 modules/opacwebaloes/backend.py  |   69 +++++++++++++++
 modules/opacwebaloes/browser.py  |   97 ++++++++++++++++++++
 modules/opacwebaloes/favicon.png |  Bin 0 -> 2401 bytes
 modules/opacwebaloes/pages.py    |  179 ++++++++++++++++++++++++++++++++++++++
 modules/opacwebaloes/test.py     |   27 ++++++
 weboob/capabilities/library.py   |   56 ++++++++++++
 7 files changed, 451 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/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..fdb5dab
--- /dev/null
+++ b/modules/opacwebaloes/backend.py
@@ -0,0 +1,69 @@
+# -*- 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, Book
+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.a'
+    DESCRIPTION = 'Aloes Library software'
+    LICENSE = 'AGPLv3+'
+    CONFIG = BackendConfig(ValueBackendPassword('login',    label='Account ID', regexp='^\d{1,6}\w$', masked=False),
+                           ValueBackendPassword('password', label='Password of account')
+                           )
+    BROWSER = AloesBrowser
+
+    def create_default_browser(self):
+        return self.create_browser(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..15e5869
--- /dev/null
+++ b/modules/opacwebaloes/browser.py
@@ -0,0 +1,97 @@
+# -*- 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.browser import BaseBrowser, BrowserIncorrectPassword
+
+from .pages import SkipPage, LoginPage, HomePage, RentedPage, HistoryPage, BookedPage
+
+
+__all__ = ['AloesBrowser']
+
+
+# Browser
+class AloesBrowser(BaseBrowser):
+    PROTOCOL = 'http'
+    DOMAIN = 'cataloguebm.villeurbanne.fr'
+    ENCODING = 'utf-8'
+    USER_AGENT = BaseBrowser.USER_AGENTS['wget']
+    #DEBUG_HTTP = True
+    DEBUG_HTTP = False
+    PAGES = {
+        'http://cataloguebm.villeurbanne.fr/opacwebaloes/index.aspx': LoginPage,
+        'http://cataloguebm.villeurbanne.fr/opacwebaloes/index.aspx\?IdPage=1': HomePage,
+        'http://cataloguebm.villeurbanne.fr/opacwebaloes/index.aspx\?IdPage=45': RentedPage,
+        'http://cataloguebm.villeurbanne.fr/opacwebaloes/index.aspx\?IdPage=429': HistoryPage,
+        'http://cataloguebm.villeurbanne.fr/opacwebaloes/index.aspx\?IdPage=44': BookedPage
+        }
+
+    def __init__(self, *args, **kwargs):
+        BaseBrowser.__init__(self, *args, **kwargs)
+
+    def is_logged(self):
+        #return self.page and not self.is_on_page(HomePage)
+        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/opacwebaloes/index.aspx' \
+                          % (self.PROTOCOL, self.DOMAIN),
+                          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/opacwebaloes/index.aspx?IdPage=45' \
+                      % (self.PROTOCOL, self.DOMAIN),
+                      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/opacwebaloes/index.aspx?IdPage=44' \
+                          % (self.PROTOCOL, self.DOMAIN),
+                          no_login=True)
+       return self.page.get_list()
+
+    #def get_account(self, id):
+    #    assert isinstance(id, basestring)
+
+    #    l = self.get_accounts_list()
+    #    for a in l:
+    #        if a.id == id:
+    #            return a
+
+    #    return None
+
+    #def get_history(self,account):
+    #    if not self.is_on_page(AccountHistoryPage) :
+    #        self.location('%s://%s%s' % (self.PROTOCOL, self.DOMAIN, account.link_id))
+    #    return self.page.get_operations(account)
+
+    #def get_coming_operations(self, account):
+    #    if not self.is_on_page(AccountComing) or self.page.account.id != account.id:
+    #        self.location('/NS_AVEEC?ch4=%s' % account.link_id)
+    #    return self.page.get_operations()
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..7c399ec
--- /dev/null
+++ b/modules/opacwebaloes/pages.py
@@ -0,0 +1,179 @@
+# -*- 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/>.
+
+import base64
+from datetime import date
+from weboob.capabilities.library import Book
+from weboob.tools.browser import BasePage, BrowserUnavailable
+from weboob.tools.mech import ClientForm
+from logging import error
+import tempfile
+import math
+import random
+
+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
+        
+#class AccountsPage(BasePage):
+    #def get_list(self):
+        #l = []
+        #for a in self.document.getiterator('a'):
+            #link=a.attrib.get('href')
+            #if link is not None and link.startswith("/outil/UWLM/ListeMouvements"):
+                #account = Account()
+                #account.link_id=link+"&mode=45"
+                #parameters=link.split("?").pop().split("&")
+                #for parameter in parameters:
+                    #list=parameter.split("=")
+                    #value=list.pop()
+                    #name=list.pop()
+                    #if name=="agence":
+                        #account.id=value
+                    #elif name=="compte":
+                        #account.id+=value
+                    #elif name=="nature":
+                        #account.type=value
+                #account.label=a.getparent().getprevious().text.strip()
+                #balance=a.text.replace(u"\u00A0",'').replace(' ','').replace('.','').replace('+','').replace(',','.')
+                #account.balance=float(balance)
+                #l.append(account)
+        #return l
+
+#class AccountHistoryPage(BasePage):
+    #def get_operations(self,account):
+        #operations = []
+        #tables=self.document.findall("//table[@class='tagTab pyjama']")
+        #table=None
+        #for i in range(len(tables)):
+            ## Look for the relevant table in the Pro version
+            #header=tables[i].getprevious()
+            #while str(header.tag)=="<built-in function Comment>":
+                #header=header.getprevious()
+            #header=header.find("div")
+            #if header is not None:
+                #header=header.find("span")
+            #if header is not None and \
+               #header.text.strip().startswith("Opérations effectuées".decode('utf-8')):
+                #table=tables[i]
+                #break;
+            ## Look for the relevant table in the Particulier version
+            #header=tables[i].find("thead").find("tr").find("th[@class='titleTab titleTableft']")
+            #if header is not None and\
+               #header.text.strip().startswith("Solde au"):
+                #table=tables[i]
+                #break;
+
+        #for tr in table.iter('tr'):
+            ## skip headers and empty rows
+            #if len(tr.findall("th"))!=0 or\
+               #len(tr.findall("td"))==0:
+                #continue
+            #operation=Operation(len(operations))
+            #mntColumn=0
+            #for td in tr.iter('td'):
+                #value=td.attrib.get('id')
+                #if value is None:
+                    #value=td.attrib.get('class');
+                #if value.startswith("date"):
+                    #operation.date=date(*reversed([int(x) for x in td.text.split('/')]))
+                #elif value.startswith("lib") or value.startswith("opLib"):
+                    ## misclosed A tag requires to grab text from td
+                    #operation.label=u''.join([txt.strip() for txt in td.itertext()])
+                #elif value.startswith("solde") or value.startswith("mnt"):
+                    #mntColumn+=1
+                    #if td.text.strip() != "":
+                        #amount = float(td.text.strip().replace('.','').replace(',','.').replace(u"\u00A0",'').replace(' ',''))
+                        #if value.startswith("soldeDeb") or mntColumn==1:
+                            #amount=-amount
+                        #operation.amount=amount
+            #operations.append(operation)
+        #return operations
+
+
diff --git a/modules/opacwebaloes/test.py b/modules/opacwebaloes/test.py
new file mode 100644
index 0000000..c964225
--- /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):
+        #list(self.backend.iter_accounts())
diff --git a/weboob/capabilities/library.py b/weboob/capabilities/library.py
new file mode 100644
index 0000000..2d02dc7
--- /dev/null
+++ b/weboob/capabilities/library.py
@@ -0,0 +1,56 @@
+# -*- 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 datetime import datetime
+
+from .base import IBaseCap, 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('pages', (int,long,float), size)
+        self.add_field('author', basestring, author)
+        self.add_field('location', basestring, location)
+        #self.add_field('url', basestring, url)
+        #self.add_field('date', datetime, date) # which may be the due date
+        self.add_field('date', basestring, date) # which may be the due date
+        self.add_field('late', basestring, late)
+        
+
+class ICapBook(IBaseCap):
+    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