[Checkins] SVN: Sandbox/luciano/kirbi/src/kirbi/ collection and items implementation

Luciano Ramalho luciano at ramalho.org
Sat Aug 18 21:18:19 EDT 2007


Log message for revision 78978:
  collection and items implementation
  

Changed:
  U   Sandbox/luciano/kirbi/src/kirbi/app.py
  U   Sandbox/luciano/kirbi/src/kirbi/collection.py
  A   Sandbox/luciano/kirbi/src/kirbi/collection_templates/
  A   Sandbox/luciano/kirbi/src/kirbi/collection_templates/index.pt
  U   Sandbox/luciano/kirbi/src/kirbi/ftests/xmlrpc.txt
  U   Sandbox/luciano/kirbi/src/kirbi/interfaces.py
  U   Sandbox/luciano/kirbi/src/kirbi/isbn.py
  U   Sandbox/luciano/kirbi/src/kirbi/item.py
  U   Sandbox/luciano/kirbi/src/kirbi/pac.py
  U   Sandbox/luciano/kirbi/src/kirbi/tests/test_pac.txt
  U   Sandbox/luciano/kirbi/src/kirbi/user.py
  D   Sandbox/luciano/kirbi/src/kirbi/user_templates/index.pt

-=-
Modified: Sandbox/luciano/kirbi/src/kirbi/app.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/app.py	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/app.py	2007-08-19 01:18:18 UTC (rev 78978)
@@ -102,7 +102,7 @@
     grok.context(Interface)
 
 class Login(grok.View):
-    grok.context(Interface)
+    grok.context(Kirbi)
 
     def update(self, login_submit=None, login=None):
         # XXX: need to display some kind of feedback when the login fails
@@ -110,7 +110,8 @@
             and login_submit is not None):
             destination = self.request.get('camefrom')
             if not destination:
-                destination = self.application_url()
+                home = self.context.collections[self.request.principal.id]
+                destination = browser.absoluteURL(home, self.request)
             self.redirect(destination)
 
 class Logout(grok.View):
@@ -185,7 +186,8 @@
         return fmt%blank
 
     def chooseName(self, name, object):
-        name = name or self.nextId('k%04d')
+        prefix = object.__class__.__name__[0].lower()
+        name = name or self.nextId(prefix+'%d')
         # Note: potential concurrency problems of nextId are (hopefully)
         # handled by calling the super.QuickNameChooser
         return super(QuickNameChooser, self).chooseName(name, object)

Modified: Sandbox/luciano/kirbi/src/kirbi/collection.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/collection.py	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/collection.py	2007-08-19 01:18:18 UTC (rev 78978)
@@ -17,6 +17,10 @@
 import grok
 from interfaces import ICollection
 from zope.interface import implements
+from kirbi.interfaces import IBook
+from kirbi.isbn import toISBN13, InvalidISBN
+from kirbi.item import Item
+from kirbi.book import Book
 
 class Collection(grok.Container):
     implements(ICollection)
@@ -24,4 +28,73 @@
         super(Collection, self).__init__()
         self.title = title
         self.private = False ### XXX: implement private collections
+        
+    def addItem(self, item):
+        name = INameChooser(self).chooseName(book.isbn13, book)
+        self[name] = book
+        return book.__name__
+        
+        
+class Index(grok.View):
+    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'
+        self.results = results
 
+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.invalid_isbns = []
+        if isbns is not None:
+            isbns = list(set(isbns.split()))
+            for isbn in isbns:
+                try:
+                    isbn13 = toISBN13(isbn)
+                except InvalidISBN:
+                    self.invalid_isbns.append(isbn)
+                    continue
+                if isbn13 in self.pac:
+                    book = self.pac[isbn13]
+                else:
+                    book = Book(isbn13=isbn13)
+                    self.pac.addBook(book)
+                    item = Item(book.__name__)
+                    self.context.addItem(item)
+
+        if retry_isbns:
+            self.context.retryPending(retry_isbns)
+        # XXX this would be great with AJAX, avoiding the ugly refresh
+        if (not self.invalid_isbns) and (self.pac.getIncomplete()
+                                         or self.pac.getPending()):
+            self.request.response.setHeader("Refresh", "5; url=%s" % self.url())
+
+    def invalidISBNs(self):
+        if self.invalid_isbns:
+            return '\n'.join(self.invalid_isbns)
+        else:
+            return ''
+
+    def sortedByTime(self, isbn_dict):
+        pairs = ((timestamp, isbn) for isbn, timestamp in
+                    isbn_dict.items())
+        return (dict(timestamp=timestamp,isbn=isbn)
+                for timestamp, isbn in sorted(pairs))
+
+    def incompleteIsbns(self):
+        self.pac = self.context.__parent__.__parent__['pac']
+        return list(self.sortedByTime(self.pac.getIncomplete()))
+
+    def pendingIsbns(self):
+        self.pac = self.context.__parent__.__parent__['pac']
+        return list(self.sortedByTime(self.pac.getPending()))
+
+
+    
+

Copied: Sandbox/luciano/kirbi/src/kirbi/collection_templates/index.pt (from rev 78945, Sandbox/luciano/kirbi/src/kirbi/user_templates/index.pt)
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/collection_templates/index.pt	                        (rev 0)
+++ Sandbox/luciano/kirbi/src/kirbi/collection_templates/index.pt	2007-08-19 01:18:18 UTC (rev 78978)
@@ -0,0 +1,47 @@
+<html metal:use-macro="context/@@master/page">
+<head>
+
+</head>
+<body>
+  <span metal:fill-slot="content_title">
+    <span tal:replace="view/context/title" />'s collection
+  </span>
+  <span metal:fill-slot="content_actions">
+    <form class="search" action=".">
+        <input type="text" name="query">
+        <input type="submit" name="submit" value="search my collection">
+    </form>
+  </span>
+  <div metal:fill-slot="content">
+
+    <p><a tal:attributes="href python:view.url('addbookitem')">
+            Add Book
+    </a></p>
+    <p><a tal:attributes="href python:view.url('addbookitems')">
+            Add Several Books
+    </a></p>
+
+    <h3 tal:content="view/results_title">999 items matched the query</h3>
+    <table tal:condition="view/results">
+        <tr tal:repeat="item view/results">
+            <th align="right" tal:content="repeat/item/number" />
+            <td align="center">
+                <img class="cover"
+                     tal:attributes="src python:view.coverUrl(item)"
+                     height="53" />
+            </td>
+            <td>
+                <dl>
+                    <dt><a tal:attributes="href python:view.url(item)"
+                        tal:content="item/filing_title">title goes here</a>
+                    </dt>
+                    <dd tal:content="item/creatorsLine">
+                    </dd>
+                </dl>
+            </td>
+        </tr>
+    </table>
+
+  </div>
+</body>
+</html>

Modified: Sandbox/luciano/kirbi/src/kirbi/ftests/xmlrpc.txt
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/ftests/xmlrpc.txt	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/ftests/xmlrpc.txt	2007-08-19 01:18:18 UTC (rev 78978)
@@ -14,7 +14,7 @@
 Now we use the proxy to add books to the PAC::
 
     >>> pac.add(dict(title="One Flew Over the Cuckoo's Nest"))
-    'k0001'
+    'b1'
     >>> pac.add(dict(isbn13='9780684833392'))
     '9780684833392'
     >>> pac.add(dict(isbn13='9780486273471'))
@@ -22,7 +22,7 @@
     >>> pac.add(dict(title=u'Utopia', isbn13='9780140449105'))
     '9780140449105'
     >>> sorted(pac.list())
-    ['9780140449105', '9780486273471', '9780684833392', 'k0001']
+    ['9780140449105', '9780486273471', '9780684833392', 'b1']
 
 The second and third books added have ISBN but no title, so they are added to
 the incomplete attribute of the PAC, to be retrieved by Kirbifetch::

Modified: Sandbox/luciano/kirbi/src/kirbi/interfaces.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/interfaces.py	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/interfaces.py	2007-08-19 01:18:18 UTC (rev 78978)
@@ -37,6 +37,9 @@
 
 class InvalidISBN(schema.ValidationError):
     """This is not a valid ISBN-10 or ISBN-13"""
+    ### XXX: There is another exception class with the same name in
+    ### isbn.py. I'd like to avoid the duplication, but how to do it
+    ### without making the isbn.py module depend on schema.ValidationError?
 
 def validateISBN(isbn):
     if not isValidISBN(isbn):
@@ -115,16 +118,16 @@
     
     """
     
-    manifestation_id = schema.TextLine(title=u"Book id",
-                    description=u"The id of the book of which this is a copy.",
+    manifestation_id = schema.ASCII(title=u"Book id",
+                    description=u"Id of the book of which this item is a copy.",
                     required=True)
     description = schema.Text(title=u"Description",
                     description=(u"Details of this copy, such as autographs,"
                                  u"marks, damage etc."),
                             required=False)
     #XXX: This should be filled automatically.
-    catalog_date = schema.Date(title=u"Catalog date",
-                    description=u"Date when added to your collection.",
+    catalog_datetime = schema.Datetime(title=u"Catalog date",
+                    description=u"Datetime when added to your collection.",
                             required=False)
     
 class ICollection(Interface):

Modified: Sandbox/luciano/kirbi/src/kirbi/isbn.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/isbn.py	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/isbn.py	2007-08-19 01:18:18 UTC (rev 78978)
@@ -14,6 +14,12 @@
 """Kirbi ISBN handling functions
 """
 
+class InvalidISBN(ValueError):
+    """This is not a valid ISBN-10 or ISBN-13"""
+    ### XXX: There is another exception class with the same name in
+    ### interfaces.py. I'd like to avoid the duplication, but how to do it
+    ### without making this module depend on schema.ValidationError?
+
 def filterDigits(input):
     """ Strip the input of all non-digits, but retain last X if present. """
     input = input.strip()
@@ -94,7 +100,7 @@
     if len(digits) > 10:
         digits = filterDigits(digits)
     if len(digits) != 10:
-        raise ValueError, '%s is not a valid ISBN-10'
+        raise InvalidISBN, '%s is not a valid ISBN-10'
     else:
         digits = '978' + digits[:-1]
         return digits + checksumEAN(digits)
@@ -103,16 +109,21 @@
     if len(digits) > 13:
         digits = filterDigits(digits)
     if len(digits) != 13:
-        raise ValueError, '%s is not a valid ISBN-13'
+        raise InvalidISBN, '%s is not a valid ISBN-13'
     if digits.startswith('978'):
         digits = digits[3:-1]
         return digits + checksumISBN10(digits)
     elif digits.startswith('979'):
-        raise ValueError, '%s is a valid ISBN-13 but has no ISBN-10 equivalent'       
+        raise InvalidISBN, '%s is a valid ISBN-13 but has no ISBN-10 equivalent'       
     else:
-        raise ValueError, '%s is not a valid ISBN-13 (wrong prefix)'
+        raise InvalidISBN, '%s is not a valid ISBN-13 (wrong prefix)'
+    
+def toISBN13(digits):
+    digits = filterDigits(digits)
+    if isValidISBN13(digits): return digits
+    else:
+        return convertISBN10toISBN13(digits)
 
-
 # Note: ISBN group identifiers related to languages
 # http://www.isbn-international.org/en/identifiers/allidentifiers.html
 # http://www.loc.gov/standards/iso639-2/php/code_list.php

Modified: Sandbox/luciano/kirbi/src/kirbi/item.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/item.py	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/item.py	2007-08-19 01:18:18 UTC (rev 78978)
@@ -18,6 +18,7 @@
 from interfaces import IItem, ILease
 from zope.interface import Interface, implements, invariant
 from zope import schema
+from datetime import datetime
 
 class Item(grok.Container):
     """An exemplar of a book.
@@ -27,6 +28,10 @@
 
     implements(IItem)
     
-    def __init__(self, manifestation_id):
-        super(IItem, self).__init__()
-        
+    def __init__(self, manifestation, description=u'', catalog_datetime=None):
+        super(Item, self).__init__()
+        self.manifestation = manifestation
+        self.description = description
+        if catalog_datetime is None:
+            self.catalog_datetime = datetime.now()
+            
\ No newline at end of file

Modified: Sandbox/luciano/kirbi/src/kirbi/pac.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/pac.py	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/pac.py	2007-08-19 01:18:18 UTC (rev 78978)
@@ -193,7 +193,6 @@
                                          or self.context.getPending()):
             self.request.response.setHeader("Refresh", "5; url=%s" % self.url())
 
-
     def invalidISBNs(self):
         if self.invalid_isbns:
             return '\n'.join(self.invalid_isbns)

Modified: Sandbox/luciano/kirbi/src/kirbi/tests/test_pac.txt
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/tests/test_pac.txt	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/tests/test_pac.txt	2007-08-19 01:18:18 UTC (rev 78978)
@@ -11,13 +11,13 @@
     >>> from kirbi.book import Book
     >>> pac = Pac()
     >>> pac.addBook(Book(u'Zero'))
-    u'k0001'
+    u'b1'
     >>> pac.addBook(Book(isbn13='978-0670030583'))
     u'9780670030583'
     >>> pac.addBook(Book(u'A Tale of Two Cities', isbn13='978-0141439600'))
     u'9780141439600'
     >>> sorted(pac)
-    [u'9780141439600', u'9780670030583', u'k0001']
+    [u'9780141439600', u'9780670030583', u'b1']
 
 One of the books has ISBN but no Title, so it's put in the fetch queue::
     # XXX: revise this test to the new Pac API

Modified: Sandbox/luciano/kirbi/src/kirbi/user.py
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/user.py	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/user.py	2007-08-19 01:18:18 UTC (rev 78978)
@@ -99,9 +99,6 @@
         else:
             return self.login
 
-class Index(grok.View):
-    grok.context(User)
-
 class PrincipalInfoAdapter(grok.Adapter):
     grok.context(User)
     grok.implements(IPrincipalInfo)

Deleted: Sandbox/luciano/kirbi/src/kirbi/user_templates/index.pt
===================================================================
--- Sandbox/luciano/kirbi/src/kirbi/user_templates/index.pt	2007-08-19 00:02:12 UTC (rev 78977)
+++ Sandbox/luciano/kirbi/src/kirbi/user_templates/index.pt	2007-08-19 01:18:18 UTC (rev 78978)
@@ -1,24 +0,0 @@
-<html metal:use-macro="context/@@master/page">
-<head>
-    <title metal:fill-slot="title">
-        Public Catalog
-    </title>
-
-</head>
-<body>
-  <span metal:fill-slot="content_title">
-    <span tal:replace="view/context/__name__" />'s collection
-  </span>
-  <span metal:fill-slot="content_actions">
-    <form class="search" action=".">
-        <input type="text" name="query">
-        <input type="submit" name="submit" value="search my books">
-    </form>
-  </span>
-  <div metal:fill-slot="content">
-
-    User's stuff
-
-  </div>
-</body>
-</html>



More information about the Checkins mailing list