[Checkins] SVN: Sandbox/luciano/kirbi/src/kirbi/ implemented personal collections and items

Luciano Ramalho luciano at ramalho.org
Sun Aug 19 21:45:34 EDT 2007


Log message for revision 79006:
  implemented personal collections and items
  

Changed:
  U   Sandbox/luciano/kirbi/src/kirbi/app_templates/login.pt
  U   Sandbox/luciano/kirbi/src/kirbi/app_templates/master.pt
  U   Sandbox/luciano/kirbi/src/kirbi/book.py
  U   Sandbox/luciano/kirbi/src/kirbi/collection.py
  A   Sandbox/luciano/kirbi/src/kirbi/collection_templates/addbookitems.pt
  U   Sandbox/luciano/kirbi/src/kirbi/item.py
  U   Sandbox/luciano/kirbi/src/kirbi/pac.py
  U   Sandbox/luciano/kirbi/src/kirbi/static/master.css
  U   Sandbox/luciano/kirbi/src/kirbi/tests/test_doctests.py

-=-
Modified: Sandbox/luciano/kirbi/src/kirbi/app_templates/login.pt
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/app_templates/login.pt	2007-08-19 23:01:03 UTC (rev 79005)
+++ Sandbox/luciano/kirbi/src/kirbi/app_templates/login.pt	2007-08-20 01:45:32 UTC (rev 79006)
@@ -3,7 +3,7 @@
 
 
   <span metal:fill-slot="content_title">Login</span>
-  <span metal:fill-slot="content_search"></span>
+  <span metal:fill-slot="user_menu"></span>
 
   <div metal:fill-slot="content">
 

Modified: Sandbox/luciano/kirbi/src/kirbi/app_templates/master.pt
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/app_templates/master.pt	2007-08-19 23:01:03 UTC (rev 79005)
+++ Sandbox/luciano/kirbi/src/kirbi/app_templates/master.pt	2007-08-20 01:45:32 UTC (rev 79006)
@@ -30,43 +30,53 @@
         <img tal:attributes="src static/circulante-logo-transp-212x51.png"
         title="Circulante.org" width="212" height="51" />
     </a>
-    <ul tal:condition="anonymous">
-        <li><a tal:attributes="href python:view.application_url('pac')">catalog</a></li>
-        <li><a tal:attributes="href python:view.application_url('login')">login</a></li>
+    <ul class="menu">
+      <tal:anon condition="anonymous">
         <li><a tal:attributes="href python:view.application_url('join')">join</a></li>
+      </tal:anon>
+      <tal:auth condition="not:anonymous">
+        <li><a tal:attributes="href string:${view/application_url}/u/${request/principal/id}"
+           ><span tal:replace="request/principal/id" />'s collection</a></li>
+      </tal:auth>
+      <li><a tal:attributes="href python:view.application_url('pac')">collective catalog</a></li>
+      <li class="noborder"><form tal:attributes="action python:view.application_url('pac')"
+          ><input type="text" name="query">&nbsp;<input type="submit" name="submit" value="search"
+          ></form></li>    
     </ul>
-    <ul tal:condition="not:anonymous">
-        <li><a tal:attributes="href python:view.application_url('pac')">catalog</a></li>
-        <li><a tal:attributes="href python:view.application_url('u')">users</a></li>
-        <li><a tal:attributes="href python:view.application_url('pac/addbooks')">add books</a></li>
-        <li><a tal:attributes="href python:view.application_url('logout')">logout</a></li>
-    </ul>
   </div>
   <div metal:define-slot="body">
     <div class="content_menu">
-      <span metal:define-slot="content_menu">
+      <div metal:define-slot="content_menu">
         <span class="content_title">
             <span metal:define-slot="content_title">
             Content title
             </span>
         </span>
         <span class="content_login">
-           <span tal:condition="anonymous">
-              (not logged in)
-           </span>
            <span tal:condition="not:anonymous">
               login: <span tal:replace="view/request/principal/id" />
            </span>
         </span>
-        <span metal:define-slot="content_search">
-          <form class="search" tal:attributes="action python:view.application_url('pac')">
-              <input type="text" name="query">
-              <input type="submit" name="submit" value="search">
-          </form>
+        <span class="user_menu">
+            <span metal:define-slot="user_menu">
+           <ul class="menu">
+              <li tal:condition="anonymous" class="noborder"><form 
+                  tal:attributes="action string:${view/application_url}/login"
+              method="post">login&nbsp;<input
+                    type="text" name="login" id="login" tal:attributes="value
+                    request/login|nothing"/>&nbsp;&nbsp;password&nbsp;<input type="password"
+                    name="password" id="password" />&nbsp;<input type="submit"
+                    name="login_submit" value="log in" /></form></li>
+                <tal:auth condition="not:anonymous">
+                  <li><a tal:attributes="href
+                     string:${view/application_url}/u/${request/principal/id}/addbookitems">add books</a></li>
+                  <li><a tal:attributes="href python:view.application_url('logout')">logout</a></li>
+                </tal:auth>
+            </ul>
+            </span><!--/slot: user_menu-->
         </span>
         
-        
-      </span>
+      </div>
     </div>
     <div class="content">
       <div metal:define-slot="content">

Modified: Sandbox/luciano/kirbi/src/kirbi/book.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/book.py	2007-08-19 23:01:03 UTC (rev 79005)
+++ Sandbox/luciano/kirbi/src/kirbi/book.py	2007-08-20 01:45:32 UTC (rev 79006)
@@ -38,6 +38,9 @@
     """A book record implementation.
 
     >>> alice = Book()
+    >>> IBook.providedBy(alice)
+    True
+
     >>> alice.title = u"Alice's Adventures in Wonderland"
     >>> alice.title
     u"Alice's Adventures in Wonderland"
@@ -255,6 +258,9 @@
     def update(self, **kwargs):
         for key, value in kwargs.items():
             setattr(self,key,value)
+            
+    def getCoverId(self):
+        return self.__name__
 
 class Edit(grok.EditForm):
     grok.require('kirbi.ManageBook')
@@ -267,10 +273,10 @@
     pass
 
 class Index(grok.View):
+    grok.context(IBook)
 
-    def __init__(self, *args):
-        # XXX: Is this super call really needed for a View sub-class?
-        super(Index,self).__init__(*args)
+    def __init__(self, context, request):
+        super(Index, self).__init__(context, request)
 
         # Note: this method was created because calling context properties
         # from the template raises a traversal error
@@ -289,6 +295,6 @@
             self.source_url = None
 
     def coverUrl(self):
-        cover_name = 'covers/large/'+self.context.__name__+'.jpg'
+        cover_name = 'covers/large/'+self.context.getCoverId()+'.jpg'
         return self.static.get(cover_name,
                                self.static['covers/small-placeholder.jpg'])()

Modified: Sandbox/luciano/kirbi/src/kirbi/collection.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/collection.py	2007-08-19 23:01:03 UTC (rev 79005)
+++ Sandbox/luciano/kirbi/src/kirbi/collection.py	2007-08-20 01:45:32 UTC (rev 79006)
@@ -21,8 +21,16 @@
 from kirbi.isbn import toISBN13, InvalidISBN
 from kirbi.item import Item
 from kirbi.book import Book
+from zope.app.container.interfaces import INameChooser
 
 class Collection(grok.Container):
+    """A collection of items (books, disks etc.) belonging to one user.
+    
+    >>> myCollection = Collection('123')
+    >>> ICollection.providedBy(myCollection)
+    True
+    
+    """
     implements(ICollection)
     def __init__(self, title, private=False):
         super(Collection, self).__init__()
@@ -30,27 +38,36 @@
         self.private = False ### XXX: implement private collections
         
     def addItem(self, item):
-        name = INameChooser(self).chooseName(book.isbn13, book)
-        self[name] = book
-        return book.__name__
+        name = INameChooser(self).chooseName('', item)
+        self[name] = item
+        return item.__name__
         
         
 class Index(grok.View):
+    grok.context(Collection)
+
+    def __init__(self, context, request):
+        super(Index, self).__init__(context, request)
+        self.pac = grok.getSite()['pac']
+
     def update(self, query=None):
-        if not query:
-            # XXX: if the query is empty, return all books; this should change
-            # to some limited default search criteria or none at all
-            results = self.context.values()
-            self.results_title = 'All items'
+        results = self.context.values()
+        self.results_title = 'All items'
         self.results = results
+        
+    def coverUrl(self, item):
+        book = self.pac[item.manifestation_id]
+        cover_name = 'covers/large/'+book.__name__+'.jpg'
+        return self.static.get(cover_name,
+                               self.static['covers/small-placeholder.jpg'])()
+        
 
 class AddBookItems(grok.View):
-    grok.context(Collection)
 
     invalid_isbns = []
 
     def update(self, isbns=None, retry_isbns=None):
-        self.pac = self.context.__parent__.__parent__['pac']
+        self.pac = grok.getSite()['pac']
         self.invalid_isbns = []
         if isbns is not None:
             isbns = list(set(isbns.split()))
@@ -65,8 +82,8 @@
                 else:
                     book = Book(isbn13=isbn13)
                     self.pac.addBook(book)
-                    item = Item(book.__name__)
-                    self.context.addItem(item)
+                item = Item(book.__name__)
+                self.context.addItem(item)
 
         if retry_isbns:
             self.context.retryPending(retry_isbns)

Added: Sandbox/luciano/kirbi/src/kirbi/collection_templates/addbookitems.pt
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/collection_templates/addbookitems.pt	                        (rev 0)
+++ Sandbox/luciano/kirbi/src/kirbi/collection_templates/addbookitems.pt	2007-08-20 01:45:32 UTC (rev 79006)
@@ -0,0 +1,131 @@
+<html metal:use-macro="context/@@master/page">
+<head>
+    <title metal:fill-slot="title">
+        Public Catalog
+    </title>
+
+</head>
+<body>
+  <span metal:fill-slot="content_title">Collective catalog</span>
+  <div metal:fill-slot="content">
+  <table><tr>
+    <td valign="top">
+    <form tal:attributes="action view/url" method="post">
+        <p>Type or scan several ISBNs separated by spaces or newlines.<br />
+           Either 10 or 13-digit ISBNs can be used.</p>
+        <p class="error" tal:condition="view/invalidISBNs">
+            The numbers remaining in the field below are not valid ISBN-10
+            or ISBN-13.
+        </p>
+        <textarea name="isbns" rows="20" cols="40"
+            tal:content="view/invalidISBNs">
+        </textarea>
+        <br />
+        <input type="submit" value="Add books">
+        <br />
+    </form>
+    <hr />
+    <p>
+    <form tal:attributes="action python:view.url('importdemo')" method="post">
+        If you have no ISBNs on hand, you can
+        <input type="submit" value="Import a demo collection">
+    </form>
+    </p>
+
+    </td><td valign="top">
+
+    <div tal:condition="view/incompleteIsbns">
+        <h3>New Book Records Without Title</h3>
+        <table class="isbn_list">
+            <tr tal:repeat="item view/incompleteIsbns">
+                <th align="right" tal:content="repeat/item/number" />
+                <td tal:content="item/timestamp" />
+                <td>
+                    <a tal:attributes="href python:view.url(item['isbn'])"
+                            tal:content="item/isbn">9780123456789</a>
+
+                </td>
+            </tr>
+        </table>
+    </div>
+
+    <form tal:condition="view/pendingIsbns" method="post"
+        tal:attributes="action view/url">
+        <h3>Pending Book Searches</h3>
+        <table class="isbn_list">
+            <tr>
+                <th></th>
+                <th>#</th>
+                <th>Created</th>
+                <th>ISBN-13</th>
+                <th>
+                    <a tal:attributes="href string:http://www.worldcatlibraries.org/"
+                        title="WorldCat Libraries Search">
+                        <img tal:attributes="src static/worldcat24.gif" />
+                    </a>
+                </th>
+                <th>
+                    <a tal:attributes="href string:http://www.alibris.com/search/search.cfm"
+                        title="Alibris Book Search">
+                        <img tal:attributes="src static/alibris24.gif" />
+                    </a>
+                </th>
+                <th>
+                    <a tal:attributes="href string:http://books.google.com/"
+                        title="Google Book Search">
+                        <img tal:attributes="src static/googlebook24.gif" />
+                    </a>
+                </th>
+                <th>
+                    <a tal:attributes="href string:http://google.com/"
+                        title="Google Search">
+                        <img tal:attributes="src static/google24.gif" />
+                    </a>
+                </th>
+            </tr>
+            <tr tal:repeat="item view/pendingIsbns">
+                <td>
+                    <input type="checkbox" name="retry_isbns:list"
+                            tal:attributes="value item/isbn" />
+
+                </td>
+                <th align="right" tal:content="repeat/item/number" />
+                <td tal:content="item/timestamp" />
+                <td>
+                    <a tal:attributes="href python:view.url(item['isbn'])"
+                            tal:content="item/isbn">9780123456789</a>
+
+                </td>
+                <td>
+                    <a tal:attributes="href string:http://www.worldcatlibraries.org/search?q=${item/isbn}"
+                        title="WorldCat Libraries Search">
+                        <img tal:attributes="src static/worldcat16.gif" />
+                    </a>
+                </td>
+                <td>
+                    <a tal:attributes="href string:http://www.alibris.com/search/search.cfm?wtit=${item/isbn}"
+                        title="Alibris Book Search">
+                        <img tal:attributes="src static/alibris16.gif" />
+                    </a>
+                </td>
+                <td>
+                    <a tal:attributes="href string:http://books.google.com/books?q=${item/isbn}"
+                        title="Google Book Search">
+                        <img tal:attributes="src static/google16.gif" />
+                    </a>
+                </td>
+                <td>
+                    <a tal:attributes="href string:http://google.com/search?q=${item/isbn}"
+                        title="Google Search">
+                        <img tal:attributes="src static/google16.gif" />
+                    </a>
+
+                </td>
+            </tr>
+        </table>
+        <input type="submit" name="retry" value="Retry">
+    </form>
+
+  </div><!--/content-->
+</body>
+</html>

Modified: Sandbox/luciano/kirbi/src/kirbi/item.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/item.py	2007-08-19 23:01:03 UTC (rev 79005)
+++ Sandbox/luciano/kirbi/src/kirbi/item.py	2007-08-20 01:45:32 UTC (rev 79006)
@@ -15,7 +15,7 @@
 """
 
 import grok
-from interfaces import IItem, ILease
+from interfaces import IItem, IBook
 from zope.interface import Interface, implements, invariant
 from zope import schema
 from datetime import datetime
@@ -24,14 +24,36 @@
     """An exemplar of a book.
     
     See note at interfaces.IItem.
+    
+    >>> it = Item('')
+    >>> IItem.providedBy(it)
+    True
+    
+    Now let's make it provide IBook.
+    
+    >>> from kirbi.book import Book
+    >>> book = Book('Any Book')
+    >>> it.manifestation = book
+    
+    >>> IBook.providedBy(it)
+    True
+    
     """
 
-    implements(IItem)
+    implements(IItem, IBook)
     
-    def __init__(self, manifestation, description=u'', catalog_datetime=None):
+    def __init__(self, manifestation_id, description=u'', catalog_datetime=None):
         super(Item, self).__init__()
-        self.manifestation = manifestation
+        self.manifestation_id = manifestation_id
+        if manifestation_id:
+            self.manifestation = grok.getSite()['pac'].get(manifestation_id)
         self.description = description
         if catalog_datetime is None:
             self.catalog_datetime = datetime.now()
-            
\ No newline at end of file
+            
+    def getCoverId(self):
+        return self.manifestation.__name__
+            
+    def __getattr__(self,name):
+        # XXX: this looks too easy... feels like cheating. Is it sane?
+        return getattr(self.manifestation, name)
\ No newline at end of file

Modified: Sandbox/luciano/kirbi/src/kirbi/pac.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/pac.py	2007-08-19 23:01:03 UTC (rev 79005)
+++ Sandbox/luciano/kirbi/src/kirbi/pac.py	2007-08-20 01:45:32 UTC (rev 79006)
@@ -102,7 +102,7 @@
     grok.context(Pac)
 
     def coverUrl(self, book):
-        cover_name = 'covers/large/'+book.isbn13+'.jpg'
+        cover_name = 'covers/large/'+book.__name__+'.jpg'
         return self.static.get(cover_name,
                                self.static['covers/small-placeholder.jpg'])()
 

Modified: Sandbox/luciano/kirbi/src/kirbi/static/master.css
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/static/master.css	2007-08-19 23:01:03 UTC (rev 79005)
+++ Sandbox/luciano/kirbi/src/kirbi/static/master.css	2007-08-20 01:45:32 UTC (rev 79006)
@@ -34,18 +34,39 @@
     text-decoration: none;
 }
 .top ul {
-    display: inline;
     position: absolute;
-    right: 10px;
     top: 0px;
 }
-.top li {
+ul.menu {
     display: inline;
+    right: 10px;
+}
+
+.menu li {
+    display: inline;
     line-height: 1.5;
     margin-right: 10px;
     padding-bottom: 1px;
     border-bottom: 1px solid yellow;
 }
+.menu li.noborder {
+    border-bottom: none;
+}
+
+.menu form {
+    display: inline;
+}
+
+.menu input {
+    display: inline;
+}
+
+form.login {
+    display: inline;
+    position: absolute;
+    right: 10px;
+}
+
 h1, h2, h3, dt, .content_title {
     font-family: sans-serif;
     font-weight: bold;
@@ -73,16 +94,16 @@
     font-family: sans-serif;
 }
 
+span.user_menu {
+    position: absolute;
+    right: 10px;
+    font-family: sans-serif;
+}
+
 div.content {
     margin: 20px;
 }
 
-form.search {
-    display: inline;
-    position: absolute;
-    right: 10px;
-}
-
 img.cover {
     border: 1px solid lightgray;
     margin-right: 10px;

Modified: Sandbox/luciano/kirbi/src/kirbi/tests/test_doctests.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/tests/test_doctests.py	2007-08-19 23:01:03 UTC (rev 79005)
+++ Sandbox/luciano/kirbi/src/kirbi/tests/test_doctests.py	2007-08-20 01:45:32 UTC (rev 79006)
@@ -2,7 +2,7 @@
 import unittest
 
 def test_suite():
-    modules = ['app', 'pac', 'book', 'user']
+    modules = ['app','book','collection','interfaces','isbn','item','user']
     suite = unittest.TestSuite()
     for module in modules:
         module_name = 'kirbi.' + module



More information about the Checkins mailing list