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:
parent
3278e2abed
commit
d44b721a89
7 changed files with 151 additions and 29 deletions
|
@ -121,6 +121,9 @@ type manager.
|
|||
>>> from loops.type import LoopsTypeManager, LoopsType
|
||||
>>> component.provideAdapter(LoopsTypeManager, (ILoopsObject,), ITypeManager)
|
||||
|
||||
>>> from loops.type import TypeConcept
|
||||
>>> component.provideAdapter(TypeConcept)
|
||||
|
||||
>>> from loops.concept import ConceptTypeSourceList
|
||||
>>> types = ConceptTypeSourceList(cc1)
|
||||
>>> sorted(t.title for t in types)
|
||||
|
|
|
@ -225,7 +225,7 @@ class BaseView(GenericView):
|
|||
return util.KeywordVocabulary(general
|
||||
+ self.listTypesForSearch(('resource',), ('system', 'hidden'),))
|
||||
|
||||
# controllling editing
|
||||
# controlling editing
|
||||
|
||||
@Lazy
|
||||
def editable(self):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
4===================================================================
|
||||
===================================================================
|
||||
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):
|
||||
... implements(ICatalog)
|
||||
... def searchResults(self, **criteria):
|
||||
... result = []
|
||||
... name = criteria.get('loops_title')
|
||||
... if name.endswith('*'): name = name[:-1]
|
||||
... type = criteria.get('loops_type', ('resource',))
|
||||
... if name:
|
||||
... if 'concept' in type[0]:
|
||||
... result = concepts.get(name)
|
||||
... else:
|
||||
... result = resources.get(name)
|
||||
... if result:
|
||||
... return [result]
|
||||
... return []
|
||||
... if name and name.endswith('*'): name = name[:-1]
|
||||
... typeToken = criteria.get('loops_type', ('resource',))
|
||||
... if name or typeToken:
|
||||
... if 'concept' in typeToken[0]:
|
||||
... if name:
|
||||
... result = concepts.get(name)
|
||||
... else:
|
||||
... tp = concepts[typeToken[0].split(':')[-1]]
|
||||
... result = list(tp.getChildren())
|
||||
... else:
|
||||
... result = resources.get(name)
|
||||
... if not result: return []
|
||||
... return type(result) is list and result or [result]
|
||||
>>> component.provideUtility(DummyCat())
|
||||
|
||||
>>> from loops.resource import Resource
|
||||
|
@ -200,6 +204,59 @@ of the concepts' titles:
|
|||
>>> view.listConcepts()
|
||||
"[['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...
|
||||
|
||||
|
||||
|
|
|
@ -64,6 +64,20 @@ class Search(BaseView):
|
|||
self.maxRowNum = 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):
|
||||
self.registerDojo()
|
||||
cm = self.controller.macros
|
||||
|
@ -106,18 +120,33 @@ class SearchResults(BaseView):
|
|||
|
||||
@Lazy
|
||||
def results(self):
|
||||
request = self.request
|
||||
type = request.get('search.1.text', 'loops:*')
|
||||
text = request.get('search.2.text')
|
||||
useTitle = request.get('search.2.title')
|
||||
useFull = request.get('search.2.full')
|
||||
conceptType = request.get('search.3.type', 'loops:concept:*')
|
||||
conceptTitle = request.get('search.3.text')
|
||||
conceptUid = request.get('search.3.text_selected')
|
||||
form = self.request.form
|
||||
type = form.get('search.1.text', 'loops:*')
|
||||
text = form.get('search.2.text')
|
||||
useTitle = form.get('search.2.title')
|
||||
useFull = form.get('search.2.full')
|
||||
conceptType = form.get('search.3.type', 'loops:concept:*')
|
||||
conceptTitle = form.get('search.3.text')
|
||||
conceptUid = form.get('search.3.text_selected')
|
||||
result = FullQuery(self).query(text=text, type=type,
|
||||
useTitle=useTitle, useFull=useFull,
|
||||
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())
|
||||
return self.viewIterator(result)
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
|
||||
<div metal:define-macro="search_row" id="1.1.row"
|
||||
tal:define="rowNum item/rowNum;
|
||||
basicIdPrefix idPrefix;
|
||||
idPrefix string:$idPrefix.$rowNum;
|
||||
namePrefix string:search.$rowNum;
|
||||
param search_param | item/searchParam"
|
||||
|
@ -164,8 +165,36 @@
|
|||
<tr>
|
||||
<td metal:use-macro="macros/minus"/>
|
||||
<td colspan="3">
|
||||
<h3>Search via related concepts</h3>
|
||||
<h3 i18n:translate="">Search via related concepts</h3>
|
||||
</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>
|
||||
<td></td>
|
||||
<td>
|
||||
|
|
3
type.py
3
type.py
|
@ -85,6 +85,9 @@ class LoopsType(BaseType):
|
|||
# check typeProvider for additional qualifiers:
|
||||
if self.typeProvider in (self.typeConcept, self.predicateType,):
|
||||
qu.append('system')
|
||||
addQualifiers = self.optionsDict.get('qualifier')
|
||||
if addQualifiers:
|
||||
qu.extend(addQualifiers.split(','))
|
||||
# how to set a type to 'hidden'?
|
||||
return tuple(qu)
|
||||
|
||||
|
|
13
util.py
13
util.py
|
@ -64,6 +64,13 @@ def nl2br(text):
|
|||
else: # gracefully handle Mac line endings
|
||||
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):
|
||||
if uid == '*': # wild card
|
||||
return '*'
|
||||
|
@ -76,9 +83,3 @@ def getUidForObject(obj):
|
|||
intIds = component.getUtility(IIntIds)
|
||||
return str(intIds.queryId(obj))
|
||||
|
||||
|
||||
def toUnicode(text):
|
||||
if type(text) is not unicode:
|
||||
return text.decode('UTF-8')
|
||||
else:
|
||||
return text
|
||||
|
|
Loading…
Add table
Reference in a new issue