[Checkins] SVN: Produts.RecentItemsIndex/trunk/ Cleanups.
Tres Seaver
tseaver at palladion.com
Tue Mar 16 18:48:08 EDT 2010
Log message for revision 110004:
Cleanups.
Changed:
U Produts.RecentItemsIndex/trunk/COPYRIGHT.txt
U Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/index.py
U Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/tests/test_index.py
-=-
Modified: Produts.RecentItemsIndex/trunk/COPYRIGHT.txt
===================================================================
--- Produts.RecentItemsIndex/trunk/COPYRIGHT.txt 2010-03-16 22:21:06 UTC (rev 110003)
+++ Produts.RecentItemsIndex/trunk/COPYRIGHT.txt 2010-03-16 22:48:08 UTC (rev 110004)
@@ -1,4 +1,4 @@
-Copyright (c) 2004 Zope Corporation and Contributors.
+Copyright (c) 2004, 2010 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Modified: Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/index.py
===================================================================
--- Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/index.py 2010-03-16 22:21:06 UTC (rev 110003)
+++ Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/index.py 2010-03-16 22:48:08 UTC (rev 110004)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2004 Zope Corporation and Contributors.
+# Copyright (c) 2004, 2010 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -28,15 +28,16 @@
from App.special_dtml import DTMLFile
from App.class_init import InitializeClass
from OFS.SimpleItem import SimpleItem
-from Products.PluginIndexes.interfaces import IPluggableIndex
+from Products.PluginIndexes.interfaces import IUniqueValueIndex
from Products.ZCatalog.Lazy import LazyMap
from zLOG import LOG
from zLOG import WARNING
-
+
_marker = []
def _getSourceValue(obj, attrname):
- """Return the data to be indexed for obj"""
+ """ Return the data to be indexed for obj.
+ """
value = getattr(obj, attrname)
try:
# Try calling it
@@ -46,10 +47,10 @@
return value
class RecentItemsIndex(SimpleItem):
- """Recent items index"""
+ """ Recent items index.
+ """
+ implements(IUniqueValueIndex)
- implements(IPluggableIndex)
-
meta_type = 'Recent Items Index'
# class default; instances get Length() in their clear()
@@ -58,38 +59,45 @@
manage_options = (
{'label': 'Overview', 'action': 'manage_main'},
)
-
+
manage_main = DTMLFile('www/manageIndex', globals())
-
+
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.manage_zcatalog_indexes)
-
- def __init__(
- self, id, field_name=None, date_name=None, max_length=None,
- guard_roles=None, guard_permission=None, extra=None, caller=None):
- """Recent items index constructor
-
+
+ def __init__(self,
+ id,
+ field_name=None,
+ date_name=None,
+ max_length=None,
+ guard_roles=None,
+ guard_permission=None,
+ extra=None,
+ caller=None,
+ ):
+ """ Recent items index constructor
+
id -- Zope id for index in
-
+
field_name -- Name of attribute used to classify the objects. A
recent item list is created for each value of this field indexed.
If this value is omitted, then a single recent item list for all
cataloged objects is created.
-
+
date_name -- Name of attribute containing a date which specifies the
object's age.
-
+
max_length -- Maximum length of each recent items list.
-
+
guard_roles -- A list of one or more roles that must be granted the
guard permission in order for an object to be indexed. Ignored if
no guard_permission value is given.
-
+
guard_permission -- The permission that must be granted to the
guard roles for an object in order for it to be indexed. Ignored if
no guard_roles value is given.
-
- extra and caller are used by the wonderous ZCatalog addIndex
+
+ extra and caller are used by the wonderous ZCatalog addIndex
machinery. You can ignore them, unfortunately I can't 8^/
"""
self.id = id
@@ -107,84 +115,32 @@
else:
self.guard_permission = self.guard_roles = None
self.clear()
-
- ## Index specific methods ##
+ ## IPluggableIndex implementation.
+ def getEntryForObject(self, docid, default=None):
+ """ See IPluggableIndex.
+ """
+ try:
+ fieldvalue = self._rid2value[docid]
+ except KeyError:
+ return default
+ for date, rid in self._value2items[fieldvalue].keys():
+ if rid == docid:
+ return {'value':fieldvalue, 'date':date}
+ # If we get here then _rid2values is inconsistent with _value2items
+ LOG('RecentItemsIndex', WARNING,
+ 'Field value found for item %s, but no date. '
+ 'This should not happen.' % docid)
+ return default
+
def getIndexSourceNames(self):
""" See IPluggableIndex.
"""
return (self.field_name,)
- def indexSize(self):
+ def index_object(self, docid, obj, theshold=None):
""" See IPluggableIndex.
"""
- return len(self._value2items)
-
- def getItemCounts(self):
- """Return a dict of field value => item count"""
- counts = {}
- for value, items in self._value2items.items():
- counts[value] = len(items)
- return counts
-
- def query(self, value=None, limit=None, merge=1):
- """Return a lazy sequence of catalog brains like a catalog search
- that coorespond to the most recent items for the value(s) given.
- If value is omitted, then the most recent for all values are returned.
- The result are returned in order, newest first. An integer value
- can be specified in limit which restricts the maximum number of
- results if desired. If no limit is specified, the indexes'
- maximum length is used as the limit
- """
- catalog = aq_parent(aq_inner(self))
- if value is None and self.field_name is not None:
- # Query all values
- value = list(self._value2items.keys())
- elif value is not None and self.field_name is None:
- # Ignore value given if there is no classifier field
- value = None
- if isinstance(value, (types.TupleType, types.ListType)):
- # Query for multiple values
- results = []
- for fieldval in value:
- try:
- itempairs = self._value2items[fieldval].keys()
- except KeyError:
- pass
- else:
- results.extend(itempairs)
- results.sort()
- if merge:
- results = [rid for date, rid in results]
- else:
- # Create triples expected by mergeResults()
- results = [(date, rid, catalog.__getitem__)
- for date, rid in results]
- else:
- # Query for single value
- try:
- items = self._value2items[value]
- except KeyError:
- results = []
- else:
- if merge:
- results = items.values()
- else:
- # Create triples expected by mergeResults()
- results = [(date, rid, catalog.__getitem__)
- for date, rid in items.keys()]
- results.reverse()
- if limit is not None:
- results = results[:limit]
- if merge:
- return LazyMap(catalog.__getitem__, results, len(results))
- else:
- return results
-
- ## Pluggable Index API ##
-
- def index_object(self, docid, obj, theshold=None):
- """Add document to index"""
if self.guard_permission is not None and self.guard_roles:
allowed_roles = rolesForPermissionOn(self.guard_permission, obj)
for role in allowed_roles:
@@ -208,11 +164,11 @@
return 0
datevalue = datevalue.timeTime()
entry = self.getEntryForObject(docid)
- if (entry is None or fieldvalue != entry['value']
+ if (entry is None or fieldvalue != entry['value']
or datevalue != entry['date']):
# XXX Note that setting the date older than a previously pruned
# object will result in an incorrect index state. This may
- # present a problem if dates are changed arbitrarily
+ # present a problem if dates are changed arbitrarily
if entry is None:
self.numObjects.change(1)
else:
@@ -237,16 +193,16 @@
try:
del self._rid2value[oldrid]
except KeyError:
- LOG('RecentItemsIndex', WARNING,
+ LOG('RecentItemsIndex', WARNING,
'Could not unindex field value for %s.' % oldrid)
return 1
else:
# Index is up to date, nothing to do
return 0
-
+
def unindex_object(self, docid):
- """Remove docid from the index. If docid is not in the index,
- do nothing"""
+ """ See IPluggableIndex.
+ """
try:
fieldvalue = self._rid2value[docid]
except KeyError:
@@ -261,53 +217,119 @@
del self._value2items[fieldvalue]
return 1
return 1
-
+
def _apply_index(self, request, cid=''):
- """We do not play in normal catalog queries"""
+ """ See IPluggableIndex.
+ """
return None
-
- def getEntryForObject(self, docid, default=None):
- """Return a dict containing the field value and date for
- docid, or default if it is not in the index. The returned dict
- has the keys 'value' and 'date'
+
+ def indexSize(self):
+ """ See IPluggableIndex.
"""
- try:
- fieldvalue = self._rid2value[docid]
- except KeyError:
- return default
- for date, rid in self._value2items[fieldvalue].keys():
- if rid == docid:
- return {'value':fieldvalue, 'date':date}
- # If we get here then _rid2values is inconsistent with _value2items
- LOG('RecentItemsIndex', WARNING,
- 'Field value found for item %s, but no date. '
- 'This should not happen.' % docid)
- return default
-
+ return len(self._value2items)
+
+ def clear(self):
+ """ See IPluggableIndex.
+ """
+ self.numObjects = Length()
+ # Mapping field value => top items
+ self._value2items = OOBTree()
+ # Mapping indexed rid => field value for unindexing
+ self._rid2value = IOBTree()
+
+ # IUniqueValueIndex implementation.
def hasUniqueValuesFor(self, name):
- """Return true if the index holds the unique values for name"""
+ """ See IUniqueValueIndex.
+ """
return name == self.field_name
-
- def uniqueValues(self, name=None):
- """Return the unique field values indexed"""
+
+ def uniqueValues(self, name=None, withLengths=0):
+ """ See IUniqueValueIndex.
+ """
+ if withLengths:
+ return self.getItemCounts().items()
+
if name is None:
name = self.field_name
if name == self.field_name:
return self._value2items.keys()
else:
return []
+
+ ## Index specific methods ##
+ def getItemCounts(self):
+ """ Return a mapping of field values => item counts.
+ """
+ counts = {}
+ for value, items in self._value2items.items():
+ counts[value] = len(items)
+ return counts
+
+ def query(self, value=None, limit=None, merge=1):
+ """ Return a lazy sequence of catalog brains like a catalog search.
+
+ Brains coorespond to the most recent items for the value(s) given.
+
+ If 'value' is omitted, return the most recent for all values.
+
+ The results are returned in order, newest first.
- ## ZCatalog ZMI methods ##
+ 'limit', if passed, must be an integer value restricting the maximum
+ number of results.
+
+ If no limit is specified, the indexes' maximum length is used as
+ the limit.
- def clear(self):
- """reinitialize the index"""
- self.numObjects = Length()
- # Mapping field value => top items
- self._value2items = OOBTree()
- # Mapping indexed rid => field value for unindexing
- self._rid2value = IOBTree()
-
+ 'merge' is a flag: if true, return a lazy map of the brains. If
+ false, return a sequence of (value, rid, fetch) tuples which can
+ be merged later.
+ """
+ catalog = aq_parent(aq_inner(self))
+ if value is None and self.field_name is not None:
+ # Query all values
+ value = list(self._value2items.keys())
+ elif value is not None and self.field_name is None:
+ # Ignore value given if there is no classifier field
+ value = None
+ if isinstance(value, (types.TupleType, types.ListType)):
+ # Query for multiple values
+ results = []
+ for fieldval in value:
+ try:
+ itempairs = self._value2items[fieldval].keys()
+ except KeyError:
+ pass
+ else:
+ results.extend(itempairs)
+ results.sort()
+ if merge:
+ results = [rid for date, rid in results]
+ else:
+ # Create triples expected by mergeResults()
+ results = [(date, rid, catalog.__getitem__)
+ for date, rid in results]
+ else:
+ # Query for single value
+ try:
+ items = self._value2items[value]
+ except KeyError:
+ results = []
+ else:
+ if merge:
+ results = items.values()
+ else:
+ # Create triples expected by mergeResults()
+ results = [(date, rid, catalog.__getitem__)
+ for date, rid in items.keys()]
+ results.reverse()
+ if limit is not None:
+ results = results[:limit]
+ if merge:
+ return LazyMap(catalog.__getitem__, results, len(results))
+ else:
+ return results
+
InitializeClass(RecentItemsIndex)
addIndexForm = DTMLFile('www/addIndex', globals())
Modified: Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/tests/test_index.py
===================================================================
--- Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/tests/test_index.py 2010-03-16 22:21:06 UTC (rev 110003)
+++ Produts.RecentItemsIndex/trunk/Products/RecentItemsIndex/tests/test_index.py 2010-03-16 22:48:08 UTC (rev 110004)
@@ -28,8 +28,8 @@
max_length=10,
*args, **kw):
catalog = self._makeCatalog()
- index = self._getTargetClass()(id, field_name, date_name, max_length,
- *args, **kw)
+ klass = self._getTargetClass()
+ index = klass(id, field_name, date_name, max_length, *args, **kw)
return index.__of__(catalog)
def _makeDoc(self, **kw):
@@ -100,6 +100,17 @@
index = self._makeOne()
verifyObject(IPluggableIndex, index)
+ def test_class_conforms_to_IUniqueValueIndex(self):
+ from zope.interface.verify import verifyClass
+ from Products.PluginIndexes.interfaces import IUniqueValueIndex
+ verifyClass(IUniqueValueIndex, self._getTargetClass())
+
+ def test_instance_conforms_to_IUniqueValueIndex(self):
+ from zope.interface.verify import verifyObject
+ from Products.PluginIndexes.interfaces import IUniqueValueIndex
+ index = self._makeOne()
+ verifyObject(IUniqueValueIndex, index)
+
def test_construct_with_extra(self):
# Simulate instantiating from ZCatalog
class extra:
More information about the checkins
mailing list