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
|
>>> 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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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...
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
3
type.py
3
type.py
|
@ -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
13
util.py
|
@ -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
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue