First version of a searches system, where queries may be defined on every root class. The portlet and query pages have been deeply revised. Ajax is now used for displaying query results and appy-specific ajax-based navigation is used for refs as well as for queries in portal_catalog.

This commit is contained in:
Gaetan Delannay 2009-11-03 15:02:18 +01:00
parent 2b907fee32
commit 1c0744da85
12 changed files with 371 additions and 253 deletions

View file

@ -1,9 +1,10 @@
# ------------------------------------------------------------------------------
import re, os, os.path
from appy.gen.utils import FieldDescr
from appy.gen.utils import FieldDescr, SomeObjects
from appy.gen.plone25.mixins import AbstractMixin
from appy.gen.plone25.mixins.FlavourMixin import FlavourMixin
from appy.gen.plone25.wrappers import AbstractWrapper
from appy.gen.plone25.descriptors import ArchetypesClassDescriptor
_PY = 'Please specify a file corresponding to a Python interpreter ' \
'(ie "/usr/bin/python").'
@ -97,24 +98,46 @@ class ToolMixin(AbstractMixin):
def showPortlet(self):
return not self.portal_membership.isAnonymousUser()
def executeQuery(self, queryName, flavourNumber):
if queryName.find(',') != -1:
def executeQuery(self, contentType, flavourNumber=1, searchName=None,
startNumber=0):
'''Executes a query on a given p_contentType (or several, separated
with commas) in Plone's portal_catalog. Portal types are from the
flavour numbered p_flavourNumber. If p_searchName is specified, it
corresponds to a search defined on p_contentType: additional search
criteria will be added to the query. We will retrieve objects from
p_startNumber.'''
# Is there one or several content types ?
if contentType.find(',') != -1:
# Several content types are specified
portalTypes = queryName.split(',')
portalTypes = contentType.split(',')
if flavourNumber != 1:
portalTypes = ['%s_%d' % (pt, flavourNumber) \
for pt in portalTypes]
else:
portalTypes = queryName
portalTypes = contentType
params = {'portal_type': portalTypes, 'batch': True}
res = self.portal_catalog.searchResults(**params)
batchStart = self.REQUEST.get('b_start', 0)
res = self.getProductConfig().Batch(res,
self.getNumberOfResultsPerPage(), int(batchStart), orphan=0)
return res
# Manage additional criteria from a search when relevant
if searchName:
# In this case, contentType must contain a single content type.
appyClass = self.getAppyClass(contentType)
# Find the search
search = ArchetypesClassDescriptor.getSearch(appyClass, searchName)
for fieldName, fieldValue in search.fields.iteritems():
appyType = getattr(appyClass, fieldName)
attrName = fieldName
if (appyType.type == 'String') and appyType.isMultiValued():
attrName = 'get%s%s' % (fieldName[0].upper(), fieldName[1:])
params[attrName] = fieldValue
brains = self.portal_catalog.searchResults(**params)
print 'Number of results per page is', self.getNumberOfResultsPerPage()
print 'StartNumber is', startNumber
res = SomeObjects(brains, self.getNumberOfResultsPerPage(), startNumber)
res.brainsToObjects()
print 'Res?', res.totalNumber, res.batchSize, res.startNumber
return res.__dict__
def getResultColumnsNames(self, queryName):
contentTypes = queryName.strip(',').split(',')
def getResultColumnsNames(self, contentType):
contentTypes = contentType.strip(',').split(',')
resSet = None # Temporary set for computing intersections.
res = [] # Final, sorted result.
flavour = None
@ -137,12 +160,12 @@ class ToolMixin(AbstractMixin):
res.append(fieldName)
return res
def getResultColumns(self, anObject, queryName):
def getResultColumns(self, anObject, contentType):
'''What columns must I show when displaying a list of root class
instances? Result is a list of tuples containing the name of the
column (=name of the field) and a FieldDescr instance.'''
res = []
for fieldName in self.getResultColumnsNames(queryName):
for fieldName in self.getResultColumnsNames(contentType):
if fieldName == 'workflowState':
# We do not return a FieldDescr instance if the attributes is
# not a *real* attribute but the workfow state.
@ -297,4 +320,20 @@ class ToolMixin(AbstractMixin):
for msg in jsMessages:
res += 'var %s = "%s";\n' % (msg, self.translate(msg))
return res
def getSearches(self, contentType):
'''Returns the searches that are defined for p_contentType.'''
appyClass = self.getAppyClass(contentType)
return [s.__dict__ for s in \
ArchetypesClassDescriptor.getSearches(appyClass)]
def getQueryUrl(self, contentType, flavourNumber, searchName):
'''This method creates the URL that allows to perform an ajax GET
request for getting queried objects from a search named p_searchName
on p_contentType from flavour numbered p_flavourNumber.'''
return self.getAppFolder().absolute_url() + '/skyn/ajax?objectUid=%s' \
'&page=macros&macro=queryResult&contentType=%s&flavourNumber=%s' \
'&searchName=%s&startNumber=' % (self.UID(), contentType,
flavourNumber, searchName)
# ------------------------------------------------------------------------------

View file

@ -10,7 +10,7 @@ import os, os.path, sys, types, mimetypes
import appy.gen
from appy.gen import String
from appy.gen.utils import FieldDescr, GroupDescr, PhaseDescr, StateDescr, \
ValidationErrors, sequenceTypes, RefObjects
ValidationErrors, sequenceTypes, SomeObjects
from appy.gen.plone25.descriptors import ArchetypesClassDescriptor
from appy.gen.plone25.utils import updateRolesForPermission, getAppyRequest
@ -210,7 +210,7 @@ class AbstractMixin:
for uid in toDelete:
sortedUids.remove(uid)
# Prepare the result
res = RefObjects()
res = SomeObjects()
res.totalNumber = res.batchSize = len(sortedUids)
if batchNeeded:
res.batchSize = appyType['maxPerPage']
@ -218,7 +218,7 @@ class AbstractMixin:
res.startNumber = startNumber
# Get the needed referred objects
i = res.startNumber
# Is is possible and more efficient to perform a single query in
# Is it possible and more efficient to perform a single query in
# uid_catalog and get the result in the order of specified uids?
while i < (res.startNumber + res.batchSize):
if i >= res.totalNumber: break
@ -245,7 +245,7 @@ class AbstractMixin:
startNumber=startNumber).__dict__
else:
# Note Pagination is not yet implemented for backward ref.
return RefObjects(self.getBRefs(fieldName)).__dict__
return SomeObjects(self.getBRefs(fieldName)).__dict__
def getAppyRefIndex(self, fieldName, obj):
'''Gets the position of p_obj within Ref field named p_fieldName.'''