[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