search with additional (preset) search criteria

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1615 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2007-03-06 11:01:48 +00:00
parent 3278e2abed
commit d44b721a89
7 changed files with 151 additions and 29 deletions

View file

@ -121,6 +121,9 @@ type manager.
>>> from loops.type import LoopsTypeManager, LoopsType >>> from loops.type import LoopsTypeManager, LoopsType
>>> component.provideAdapter(LoopsTypeManager, (ILoopsObject,), ITypeManager) >>> component.provideAdapter(LoopsTypeManager, (ILoopsObject,), ITypeManager)
>>> from loops.type import TypeConcept
>>> component.provideAdapter(TypeConcept)
>>> from loops.concept import ConceptTypeSourceList >>> from loops.concept import ConceptTypeSourceList
>>> types = ConceptTypeSourceList(cc1) >>> types = ConceptTypeSourceList(cc1)
>>> sorted(t.title for t in types) >>> sorted(t.title for t in types)

View file

@ -225,7 +225,7 @@ class BaseView(GenericView):
return util.KeywordVocabulary(general return util.KeywordVocabulary(general
+ self.listTypesForSearch(('resource',), ('system', 'hidden'),)) + self.listTypesForSearch(('resource',), ('system', 'hidden'),))
# controllling editing # controlling editing
@Lazy @Lazy
def editable(self): def editable(self):

View file

@ -1,4 +1,4 @@
4=================================================================== ===================================================================
loops.search - Provide search functionality for the loops framework loops.search - Provide search functionality for the loops framework
=================================================================== ===================================================================
@ -118,17 +118,21 @@ purposes fairly primitive) catalog and a resource we can search for:
>>> class DummyCat(object): >>> class DummyCat(object):
... implements(ICatalog) ... implements(ICatalog)
... def searchResults(self, **criteria): ... def searchResults(self, **criteria):
... result = []
... name = criteria.get('loops_title') ... name = criteria.get('loops_title')
... if name.endswith('*'): name = name[:-1] ... if name and name.endswith('*'): name = name[:-1]
... type = criteria.get('loops_type', ('resource',)) ... typeToken = criteria.get('loops_type', ('resource',))
... if name: ... if name or typeToken:
... if 'concept' in type[0]: ... if 'concept' in typeToken[0]:
... result = concepts.get(name) ... if name:
... else: ... result = concepts.get(name)
... result = resources.get(name) ... else:
... if result: ... tp = concepts[typeToken[0].split(':')[-1]]
... return [result] ... result = list(tp.getChildren())
... return [] ... else:
... result = resources.get(name)
... if not result: return []
... return type(result) is list and result or [result]
>>> component.provideUtility(DummyCat()) >>> component.provideUtility(DummyCat())
>>> from loops.resource import Resource >>> from loops.resource import Resource
@ -200,6 +204,59 @@ of the concepts' titles:
>>> view.listConcepts() >>> view.listConcepts()
"[['Zope (Topic)', '11']]" "[['Zope (Topic)', '11']]"
Preset Concept Types on Search Forms
------------------------------------
Often we want to include certain types in our search. We can instruct
the search form to include lines for these types by giving these types
a certain qualifier, via the option attribute of the type interface.
Let's start with a new type, the customer type.
>>> customer = concepts['customer'] = Concept('Customer')
>>> customer.conceptType = typeConcept
>>> custType = ITypeConcept(customer)
>>> custType.options
[]
>>> cust1 = concepts['cust1'] = Concept(u'Zope Corporation')
>>> cust2 = concepts['cust2'] = Concept(u'cyberconcepts')
>>> for c in (cust1, cust2): c.conceptType = customer
>>> from cybertools.typology.interfaces import IType
>>> IType(cust1).qualifiers
('concept',)
>>> searchView = Search(search, TestRequest())
>>> list(searchView.presetSearchTypes)
[]
We can now add a 'search' qualifier to the customer type's options
and thus include the customer type in the preset search types.
>>> custType.options = ('qualifier:search',)
>>> IType(cust1).qualifiers
('concept', 'search')
>>> searchView = Search(search, TestRequest())
>>> list(searchView.presetSearchTypes)
[{'token': 'loops:concept:customer', 'title': 'Customer'}]
>>> searchView.conceptsForType('loops:concept:customer')
[{'token': 'none', 'title': u'not selected'},
{'token': '17', 'title': u'Zope Corporation'},
{'token': '18', 'title': u'cyberconcepts'}]
Let's use this new search option for querying:
>>> form = {'search.4.text_selected': u'17'}
>>> resultsView = SearchResults(page, TestRequest(form=form))
>>> results = list(resultsView.results)
>>> results[0].title
u'Zope Corporation'
Automatic Filtering
-------------------
TODO - more to come... TODO - more to come...

View file

@ -64,6 +64,20 @@ class Search(BaseView):
self.maxRowNum = n self.maxRowNum = n
return n return n
@Lazy
def presetSearchTypes(self):
""" Return a list of concept type info dictionaries (see BaseView)
that should be displayed on a separate search parameter row.
"""
#return ITypeManager(self.context).listTypes(include=('search',))
return self.listTypesForSearch(include=('search',))
def conceptsForType(self, token):
noSelection = dict(token='none', title=u'not selected')
result = sorted(ConceptQuery(self).query(type=token), key=lambda x: x.title)
return [noSelection] + [dict(title=o.title, token=util.getUidForObject(o))
for o in result]
def initDojo(self): def initDojo(self):
self.registerDojo() self.registerDojo()
cm = self.controller.macros cm = self.controller.macros
@ -106,18 +120,33 @@ class SearchResults(BaseView):
@Lazy @Lazy
def results(self): def results(self):
request = self.request form = self.request.form
type = request.get('search.1.text', 'loops:*') type = form.get('search.1.text', 'loops:*')
text = request.get('search.2.text') text = form.get('search.2.text')
useTitle = request.get('search.2.title') useTitle = form.get('search.2.title')
useFull = request.get('search.2.full') useFull = form.get('search.2.full')
conceptType = request.get('search.3.type', 'loops:concept:*') conceptType = form.get('search.3.type', 'loops:concept:*')
conceptTitle = request.get('search.3.text') conceptTitle = form.get('search.3.text')
conceptUid = request.get('search.3.text_selected') conceptUid = form.get('search.3.text_selected')
result = FullQuery(self).query(text=text, type=type, result = FullQuery(self).query(text=text, type=type,
useTitle=useTitle, useFull=useFull, useTitle=useTitle, useFull=useFull,
conceptTitle=conceptTitle, conceptUid=conceptUid, conceptTitle=conceptTitle, conceptUid=conceptUid,
conceptType= conceptType) conceptType=conceptType)
rowNum = 4
while rowNum < 10:
addCriteria = form.get('search.%i.text_selected' % rowNum)
rowNum += 1
if not addCriteria:
break
if addCriteria == 'none':
continue
addSelection = FullQuery(self).query(text=text, type=type,
useTitle=useTitle, useFull=useFull,
conceptUid=addCriteria)
if result:
result = [r for r in result if r in addSelection]
else:
result = addSelection
result = sorted(result, key=lambda x: x.title.lower()) result = sorted(result, key=lambda x: x.title.lower())
return self.viewIterator(result) return self.viewIterator(result)

View file

@ -84,6 +84,7 @@
<div metal:define-macro="search_row" id="1.1.row" <div metal:define-macro="search_row" id="1.1.row"
tal:define="rowNum item/rowNum; tal:define="rowNum item/rowNum;
basicIdPrefix idPrefix;
idPrefix string:$idPrefix.$rowNum; idPrefix string:$idPrefix.$rowNum;
namePrefix string:search.$rowNum; namePrefix string:search.$rowNum;
param search_param | item/searchParam" param search_param | item/searchParam"
@ -164,8 +165,36 @@
<tr> <tr>
<td metal:use-macro="macros/minus"/> <td metal:use-macro="macros/minus"/>
<td colspan="3"> <td colspan="3">
<h3>Search via related concepts</h3> <h3 i18n:translate="">Search via related concepts</h3>
</td> </td>
<tr tal:repeat="type item/presetSearchTypes">
<tal:preset define="rowNum item/rowNum;
idPrefix string:$basicIdPrefix.$rowNum;
namePrefix string:search.$rowNum;">
<td></td>
<td>
<span i18n:translate="">Type: </span>
<b tal:content="type/title" />
<input type="hidden" name="type" value=""
tal:attributes="name string:$namePrefix.type;
value type/token" />
</td>
<td>
<label for="text"
tal:attributes="for string:$idPrefix.text">Select value:</label>
</td>
<td>
<select name="text_selected"
tal:attributes="name string:$namePrefix.text_selected;
id string:$idPrefix.text;">
<tal:concepts repeat="concept python: item.conceptsForType(type['token'])">
<option tal:attributes="value concept/token"
tal:content="concept/title">Zope Corp</option>
</tal:concepts>
</select>
</td>
</tal:preset>
</tr>
<tr> <tr>
<td></td> <td></td>
<td> <td>

View file

@ -85,6 +85,9 @@ class LoopsType(BaseType):
# check typeProvider for additional qualifiers: # check typeProvider for additional qualifiers:
if self.typeProvider in (self.typeConcept, self.predicateType,): if self.typeProvider in (self.typeConcept, self.predicateType,):
qu.append('system') qu.append('system')
addQualifiers = self.optionsDict.get('qualifier')
if addQualifiers:
qu.extend(addQualifiers.split(','))
# how to set a type to 'hidden'? # how to set a type to 'hidden'?
return tuple(qu) return tuple(qu)

13
util.py
View file

@ -64,6 +64,13 @@ def nl2br(text):
else: # gracefully handle Mac line endings else: # gracefully handle Mac line endings
return '<br />\n'.join(text.split('\r')) return '<br />\n'.join(text.split('\r'))
def toUnicode(text):
if type(text) is not unicode:
return text.decode('UTF-8')
else:
return text
def getObjectForUid(uid): def getObjectForUid(uid):
if uid == '*': # wild card if uid == '*': # wild card
return '*' return '*'
@ -76,9 +83,3 @@ def getUidForObject(obj):
intIds = component.getUtility(IIntIds) intIds = component.getUtility(IIntIds)
return str(intIds.queryId(obj)) return str(intIds.queryId(obj))
def toUnicode(text):
if type(text) is not unicode:
return text.decode('UTF-8')
else:
return text