Cataloging objects in BFG
One way to catalog the objects in a BFG application is to use subscribers .
The procedure followed to catalog is :
Let us see how we can catalog objects.
One way to catalog the objects in a BFG application is to use subscribers .
The procedure followed to catalog is :
- Add a catalog file in the root that has a method to catalog the objects.
- In models.py import the catalog method you have written and call it in the appmaker() .
- Implement zope events interfaces that you require in events.py (you can use any name)
- Then write the functions which you want to execute on firing of above mentioned/decalred events.
- Now use subscriber tag in configure.zcml and register the subscriber function to the events declared.
Let us see how we can catalog objects.
- catalog.py
Create a python file called catalog.py in the root. Add a method populate_catalog and place the indexes that are required.
- import datetime
- import time
- from zope.interface import providedBy
- from zope.component import queryAdapter
- from zope.interface.declarations import Declaration
- from repoze.bfg.traversal import model_path
- from repoze.bfg.traversal import find_interface
- from repoze.catalog.catalog import Catalog
- from repoze.catalog.indexes.field import CatalogFieldIndex
- from repoze.catalog.indexes.keyword import CatalogKeywordIndex
- from repoze.catalog.indexes.text import CatalogTextIndex
- from repoze.catalog.indexes.path import CatalogPathIndex
- from repoze.catalog.indexes.path2 import CatalogPathIndex2
- from repoze.catalog.document import DocumentMap
- from repoze.bfg.security import principals_allowed_by_permission
- from leavemnt.interfaces import IRoot
- from leavemnt.interfaces import ISearchText
- from leavemnt.interfaces import IVirtualData
- from leavemnt.interfaces import ILeave
- from leavemnt.interfaces import IProfile
- from leavemnt.utils import coarse_datetime_repr
- from leavemnt.utils import find_users
- def get_search_text(object, default):
- adapter = queryAdapter(object, ISearchText)
- if adapter is None:
- return default
- return adapter()
- def get_creator(object, default):
- return getattr(object, 'creator', default)
- def _get_date(object, default, name):
- date = getattr(object, 'modified', None)
- if date is None:
- return default
- # we can't store datetimes directly in the catalog because they
- # can't be compared with anything
- timetime = time.mktime(date.timetuple())
- # creation "minute" actually to prevent too-granular storage
- date = int(str(int(timetime))[:-2])
- return date
- def get_modified_date(object, default):
- return _get_date(object, default, 'modified')
- def get_created_date(object, default):
- print _get_date(object, default, 'created')
- return _get_date(object, default, 'created')
- def get_interfaces(object, default):
- # we unwind all derived and immediate interfaces using spec.flattened()
- # (providedBy would just give us the immediate interfaces)
- provided_by = list(providedBy(object))
- spec = Declaration(provided_by)
- ifaces = list(spec.flattened())
- return ifaces
- def get_security_state(obj, default):
- return getattr(obj, 'security_state', default)
- def populate_catalog(site):
- catalog = site.catalog = Catalog()
- catalog['text'] = CatalogTextIndex(get_search_text)
- catalog['interfaces'] = CatalogKeywordIndex(get_interfaces)
- catalog['creator'] = CatalogFieldIndex(get_creator)
- catalog['modified'] = CatalogFieldIndex(get_modified_date)
- catalog['created'] = CatalogFieldIndex(get_created_date)
- catalog['security_state']= CatalogFieldIndex(get_security_state)
- catalog.document_map = DocumentMap()
- def find_catalog(context):
- return find_interface(context, IRoot).catalog
- In models.py
Then in the appmaker function , make a site instance , and pass it as a parameter to the populate_catalog method.
- def appmaker(root):
- if not root.has_key(SITE_ID):
- #site = root['site'] = Site()
- site = Site()
- root[SITE_ID] = site
- ## TODO: cleanup catalog creation.
- populate_catalog(site)
- leavefolder = LeaveFolder()
- site['leavefolder'] = leavefolder
- data = DefaultInitialData()
- transaction.commit()
- return root[SITE_ID]
Then the site will contain the catalog mechanism. For every subsequent creation, modification and deletion there should be a mechanism to update the catalog.
- subscribers.py
- import datetime
- from zope.component import queryAdapter
- from repoze.bfg.traversal import model_path
- from repoze.folder.interfaces import IFolder
- from leavemnt.catalog import find_catalog
- from leavemnt.interfaces import IMetadata
- def postorder(startnode):
- def visit(node):
- if IFolder.providedBy(node):
- for child in node.values():
- for result in visit(child):
- yield result
- yield node
- return visit(startnode)
- def index_content(object, event):
- """ Index content (an IObjectAddedEvent subscriber) """
- catalog = find_catalog(object)
- if catalog is not None:
- for node in postorder(object):
- path = model_path(node)
- docid = catalog.document_map.add(path)
- catalog.index_doc(docid, node)
- adapter = queryAdapter(node, IMetadata)
- if adapter is not None:
- metadata = adapter()
- catalog.document_map.add_metadata(docid, metadata)
- def unindex_content(object, event):
- """ Unindex content (an IObjectWillBeRemovedEvent subscriber) """
- catalog = find_catalog(object)
- if catalog is not None:
- path = model_path(object)
- num, docids = catalog.search(path=path)
- for docid in docids:
- catalog.unindex_doc(docid)
- catalog.document_map.remove_docid(docid)
- def reindex_content(object, event):
- """ Reindex a single piece of content (non-recursive); an
- IObjectModifed event subscriber """
- catalog = find_catalog(object)
- if catalog is not None:
- path = model_path(object)
- docid = catalog.document_map.docid_for_address(path)
- catalog.reindex_doc(docid, object)
- #catalog.document_map.remove_metadata(docid)
- adapter = queryAdapter(object, IMetadata)
- if adapter is not None:
- metadata = adapter()
- catalog.document_map.add_metadata(docid, metadata)
-
- def set_modified(object, event):
- """ Set the modified date on a single piece of content
- unconditionally (non-recursive); an IObjectModified event
- subscriber"""
- now = datetime.datetime.now()
- object.modified = now
- def set_created(object, event):
- """ Add modified and created attributes to nodes which do not yet
- have them (recursively); an IObjectWillBeAddedEvent subscriber"""
- now = datetime.datetime.now()
- for node in postorder(object):
- if not getattr(node, 'modified', None):
- node.modified = now
- if not getattr(node, 'created', None):
- node.created = now
When ever the event subscribed in configure.zcml is fired, approriate methods (in subscribers.py) is called.
- configure.zcml
-
-
-
-
-
-
- name="static"
- path="templates/static"
- />
-
-
- for="leavemnt.interfaces.ILeave
- repoze.folder.interfaces.IObjectWillBeRemovedEvent"
- handler=".subscribers.unindex_content" />
-
-
- for="leavemnt.interfaces.ILeave
- repoze.folder.interfaces.IObjectAddedEvent"
- handler=".subscribers.index_content" />
-
- for="leavemnt.interfaces.ILeave
- .interfaces.IObjectModifiedEvent"
- handler=".subscribers.reindex_content" />
-
- for="*
- repoze.folder.interfaces.IObjectWillBeAddedEvent"
- handler=".subscribers.set_created" />
-
- for="leavemnt.interfaces.IProfile
- repoze.folder.interfaces.IObjectWillBeRemovedEvent"
- handler=".subscribers.unindex_content" />
-
-
- for="leavemnt.interfaces.IProfile
- repoze.folder.interfaces.IObjectAddedEvent"
- handler=".subscribers.index_content" />
-
- for="leavemnt.interfaces.IProfile
- .interfaces.IObjectModifiedEvent"
- handler=".subscribers.reindex_content" />
-
- for="*
- .interfaces.IObjectModifiedEvent"
- handler=".subscribers.set_modified" />
-
- events.py
- from leavemnt.interfaces import IObjectModifiedEvent
- from leavemnt.interfaces import IObjectWillBeModifiedEvent
- from zope.interface import implements
- class ObjectModifiedEvent(object):
- implements(IObjectModifiedEvent)
- def __init__(self, object):
- print object
- self.object = object
- class ObjectWillBeModifiedEvent(object):
- implements(IObjectWillBeModifiedEvent)
- def __init__(self, object):
- self.object = object