From 0d1bb5a722d561ba23bfac29daa7e14b44edfa3e Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 20 May 2011 10:57:23 +0200 Subject: [PATCH] Remove now obsolete loops.search package. --- search/README.txt | 239 ---------------------------- search/__init__.py | 4 - search/browser.py | 295 ---------------------------------- search/configure.zcml | 33 ---- search/search.pt | 358 ------------------------------------------ search/tests.py | 26 --- 6 files changed, 955 deletions(-) delete mode 100755 search/README.txt delete mode 100644 search/__init__.py delete mode 100644 search/browser.py delete mode 100644 search/configure.zcml delete mode 100644 search/search.pt delete mode 100755 search/tests.py diff --git a/search/README.txt b/search/README.txt deleted file mode 100755 index fa33f30..0000000 --- a/search/README.txt +++ /dev/null @@ -1,239 +0,0 @@ -=================================================================== -loops.search - Provide search functionality for the loops framework -=================================================================== - - ($Id$) - - -Let's do some basic set up - - >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown - >>> site = placefulSetUp(True) - - >>> from zope import component, interface - >>> from zope.interface import implements - -and setup a simple loops site with a concept manager and some concepts -(with all the type machinery, what in real life is done via standard -ZCML setup): - - >>> from loops.concept import Concept - >>> from loops.type import ConceptType, TypeConcept - >>> from loops.interfaces import ITypeConcept - >>> from loops.base import Loops - >>> from loops.expert.testsetup import TestSite - >>> t = TestSite(site) - >>> concepts, resources, views = t.setup() - - >>> loopsRoot = site['loops'] - >>> query = concepts['query'] - >>> topic = concepts['topic'] - -In addition we create a concept that holds the search page and a node -(page) that links to this concept: - - >>> search = concepts['search'] = Concept(u'Search') - >>> search.conceptType = query - - >>> from loops.view import Node - >>> page = views['page'] = Node('Search Page') - >>> page.target = search - -Search views ------------- - -Now we are ready to create a search view object: - - >>> from zope.publisher.browser import TestRequest - >>> from loops.expert.browser.search import Search - >>> searchView = Search(search, TestRequest()) - -The search view provides values for identifying the search form itself -and the parameter rows; the rowNum is auto-incremented, so it should be -accessed exactly once per row: - - >>> searchView.rowNum - 1 - >>> searchView.rowNum - 2 - -The search view provides vocabularies for types that allow the selection -of types to search for; this needs an ITypeManager adapter registered via -zcml in real life: - - >>> from loops.type import LoopsTypeManager - >>> component.provideAdapter(LoopsTypeManager) - - >>> t = searchView.typesForSearch() - >>> len(t) - 14 - >>> t.getTermByToken('loops:resource:*').title - 'Any Resource' - - >>> t = searchView.conceptTypesForSearch() - >>> len(t) - 11 - >>> t.getTermByToken('loops:concept:*').title - 'Any Concept' - -To execute the search in the context of a node we have to set up a node -view for our page. The submitReplacing method returns a JavaScript call -that will replace the results part on the search page; as this registers -the dojo library with the view's controller we also have to supply -a controller attribute for the search view. - - >>> from loops.browser.node import NodeView - >>> request = TestRequest() - >>> pageView = NodeView(page, request) - - >>> from cybertools.browser.liquid.controller import Controller - >>> searchView.controller = Controller(searchView, request) - - >>> searchView.submitReplacing('1.results', '1.search.form', pageView) - 'submitReplacing("1.results", "1.search.form", - "http://127.0.0.1/loops/views/page/.target80/@@searchresults.html");...' - -Basic (text/title) search -------------------------- - -The searchresults.html view, i.e. the SearchResults view class provides the -result set of the search via its `results` property. - -Before accessing the `results` property we have to prepare a -resource we can search for and index it in the catalog. - - >>> from loops.resource import Resource - >>> rplone = resources['plone'] = Resource() - - >>> from zope.app.catalog.interfaces import ICatalog - >>> from loops import util - >>> catalog = component.getUtility(ICatalog) - >>> catalog.index_doc(int(util.getUidForObject(rplone)), rplone) - - >>> from loops.expert.browser.search import SearchResults - >>> form = {'search.2.title': True, 'search.2.text': u'plone'} - >>> request = TestRequest(form=form) - >>> resultsView = SearchResults(page, request) - - >>> results = list(resultsView.results) - >>> len(results) - 1 - >>> results[0].context == rplone - True - - >>> form = {'search.2.title': True, 'search.2.text': u'foo'} - >>> request = TestRequest(form=form) - >>> resultsView = SearchResults(page, request) - >>> len(list(resultsView.results)) - 0 - -Search via related concepts ---------------------------- - -We first have to prepare some test concepts (topics); we also assign our test -resource (rplone) from above to one of the topics: - - >>> czope = concepts['zope'] = Concept(u'Zope') - >>> czope2 = concepts['zope2'] = Concept(u'Zope 2') - >>> czope3 = concepts['zope3'] = Concept(u'Zope 3') - >>> cplone = concepts['plone'] = Concept(u'Plone') - >>> for c in (czope, czope2, czope3, cplone): - ... c.conceptType = topic - ... catalog.index_doc(int(util.getUidForObject(c)), c) - >>> czope.assignChild(czope2) - >>> czope.assignChild(czope3) - >>> czope2.assignChild(cplone) - >>> rplone.assignConcept(cplone) - -Now we can fill our search form and execute the query; note that all concepts -found are listed, plus all their children and all resources associated -with them: - - >>> uid = util.getUidForObject(concepts['zope']) - >>> form = {'search.3.type': 'loops:concept:topic', 'search.3.text': uid} - >>> request = TestRequest(form=form) - >>> resultsView = SearchResults(page, request) - >>> results = list(resultsView.results) - >>> len(results) - 5 - >>> results[0].context.__name__ - u'plone' - - >>> uid = util.getUidForObject(concepts['zope3']) - >>> form = {'search.3.type': 'loops:concept:topic', 'search.3.text': uid} - >>> request = TestRequest(form=form) - >>> resultsView = SearchResults(page, request) - >>> results = list(resultsView.results) - >>> len(results) - 1 - >>> results[0].context.__name__ - u'zope3' - -To support easy entry of concepts to search for we can preselect the available -concepts (optionally restricted to a certain type) by entering text parts -of the concepts' titles: - - >>> form = {'searchType': 'loops:concept:topic', 'name': u'zope'} - >>> request = TestRequest(form=form) - >>> view = Search(page, request) - >>> view.listConcepts() - u"{identifier: 'id', items: [{label: 'Zope (Topic)', name: 'Zope', id: '85'}, {label: 'Zope 2 (Topic)', name: 'Zope 2', id: '87'}, {label: 'Zope 3 (Topic)', name: 'Zope 3', id: '89'}]}" - -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'] - >>> custType = ITypeConcept(customer) - >>> custType.options - [] - - >>> cust1 = concepts['cust1'] - >>> cust2 = concepts['cust2'] - >>> for c in (cust1, cust2): - ... c.conceptType = customer - ... catalog.index_doc(int(util.getUidForObject(c)), c) - - >>> 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': u'Customer'}] - - >>> searchView.conceptsForType('loops:concept:customer') - [{'token': 'none', 'title': u'not selected'}, - {'token': '58', 'title': u'Customer 1'}, - {'token': '60', 'title': u'Customer 2'}, - {'token': '62', 'title': u'Customer 3'}] - -Let's use this new search option for querying: - - >>> form = {'search.4.text_selected': u'58'} - >>> resultsView = SearchResults(page, TestRequest(form=form)) - >>> results = list(resultsView.results) - >>> results[0].title - u'Customer 1' - - -Automatic Filtering -------------------- - -TODO - more to come... - diff --git a/search/__init__.py b/search/__init__.py deleted file mode 100644 index 4bc90fb..0000000 --- a/search/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -$Id$ -""" - diff --git a/search/browser.py b/search/browser.py deleted file mode 100644 index 982a278..0000000 --- a/search/browser.py +++ /dev/null @@ -1,295 +0,0 @@ -# -# Copyright (c) 2011 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -""" -Definition of basic view classes and other browser related stuff for the -loops.search package. - -$Id: browser.py 4160 2011-02-07 16:53:08Z helmutm $ -""" - -from zope import interface, component -from zope import traversing -from zope.app.pagetemplate import ViewPageTemplateFile -from zope.cachedescriptors.property import Lazy -from zope.formlib.namedtemplate import NamedTemplate, NamedTemplateImplementation -from zope.i18nmessageid import MessageFactory - -from cybertools.ajax import innerHtml -from cybertools.relation.interfaces import IRelationRegistry -from cybertools.stateful.interfaces import IStateful, IStatesDefinition -from cybertools.typology.interfaces import ITypeManager -from loops.browser.common import BaseView -from loops.browser.node import NodeView -from loops.common import adapted, AdapterBase -#from loops.expert.browser.search import searchMacrosTemplate as search_template -from loops.expert.concept import ConceptQuery, FullQuery -from loops.interfaces import IResource -from loops.organize.personal.browser.filter import FilterView -from loops import util -from loops.util import _ - - -#search_template = ViewPageTemplateFile('search.pt') - - -class Search(BaseView): - - maxRowNum = 0 - - @Lazy - def search_macros(self): - return self.controller.getTemplateMacros('search', search_template) - - @Lazy - def macro(self): - return self.search_macros['search'] - - @property - def rowNum(self): - """ Return the rowNum to be used for identifying the current search - parameter row. - """ - n = self.request.get('loops.rowNum', 0) - if n: # if given directly we don't use the calculation - return n - n = (self.maxRowNum or self.request.get('loops.maxRowNum', 0)) + 1 - 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): - result = ConceptQuery(self).query(type=token) - fv = FilterView(self.context, self.request) - result = fv.apply(result) - result.sort(key=lambda x: x.title) - noSelection = dict(token='none', title=u'not selected') - return [noSelection] + [dict(title=adapted(o, self.languageInfo).title, - token=util.getUidForObject(o)) - for o in result] - - def initDojo(self): - self.registerDojo() - cm = self.controller.macros - jsCall = ('dojo.require("dojo.parser");' - 'dojo.require("dijit.form.FilteringSelect");' - 'dojo.require("dojox.data.QueryReadStore");') - cm.register('js-execute', jsCall, jsCall=jsCall) - - def listConcepts(self, filterMethod=None): - """ Used for dijit.FilteringSelect. - """ - request = self.request - request.response.setHeader('Content-Type', 'text/plain; charset=UTF-8') - title = request.get('name') - if title == '*': - title = None - types = request.get('searchType') - data = [] - if title or types: - if title is not None: - title = title.replace('(', ' ').replace(')', ' ').replace(' -', ' ') - #title = title.split(' ', 1)[0] - if not types: - types = ['loops:concept:*'] - if not isinstance(types, (list, tuple)): - types = [types] - for type in types: - result = self.executeQuery(title=title or None, type=type, - exclude=('hidden',)) - fv = FilterView(self.context, self.request) - result = fv.apply(result) - for o in result: - if o.getLoopsRoot() == self.loopsRoot: - adObj = adapted(o, self.languageInfo) - if filterMethod is not None and not filterMethod(adObj): - continue - name = self.getRowName(adObj) - if title and title.endswith('*'): - title = title[:-1] - sort = ((title and name.startswith(title) and '0' or '1') - + name.lower()) - if o.conceptType is None: - raise ValueError('Concept Type missing for %r.' % name) - data.append({'label': self.getRowLabel(adObj, name), - 'name': name, - 'id': util.getUidForObject(o), - 'sort': sort}) - data.sort(key=lambda x: x['sort']) - if not title: - data.insert(0, {'label': '', 'name': '', 'id': ''}) - json = [] - for item in data[:100]: - json.append("{label: '%s', name: '%s', id: '%s'}" % - (item['label'], item['name'], item['id'])) - json = "{identifier: 'id', items: [%s]}" % ', '.join(json) - #print '***', json - return json - - def executeQuery(self, **kw): - return ConceptQuery(self).query(**kw) - - def getRowName(self, obj): - return obj.getLongTitle() - - def getRowLabel(self, obj, name=None): - if isinstance(obj, AdapterBase): - obj = obj.context - if name is None: - name = obj.title - return '%s (%s)' % (name, obj.conceptType.title) - - @Lazy - def statesDefinitions(self): - return [component.getUtility(IStatesDefinition, name=n) - for n in self.globalOptions('organize.stateful.resource', ())] - - @Lazy - def selectedStates(self): - result = {} - for k, v in self.request.form.items(): - if k.startswith('state.') and v: - result[k] = v - return result - - def submitReplacing(self, targetId, formId, view): - self.registerDojo() - return 'submitReplacing("%s", "%s", "%s"); return false;' % ( - targetId, formId, - '%s/.target%s/@@searchresults.html' % (view.url, self.uniqueId)) - - @Lazy - def results(self): - form = self.request.form - type = form.get('search.1.text', 'loops:*') - text = form.get('search.2.text') - if text is not None: - text = util.toUnicode(text, encoding='ISO8859-15') # IE hack!!! - 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') - #if conceptTitle is not None: - # conceptTitle = util.toUnicode(conceptTitle, encoding='ISO8859-15') - conceptUid = form.get('search.3.text') - result = FullQuery(self).query(text=text, type=type, - useTitle=useTitle, useFull=useFull, - #conceptTitle=conceptTitle, - conceptUid=conceptUid, - 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 = [r for r in result if self.checkStates(r)] - fv = FilterView(self.context, self.request) - result = fv.apply(result) - result = sorted(result, key=lambda x: x.title.lower()) - return self.viewIterator(result) - - def checkStates(self, obj): - if not IResource.providedBy(obj): - return True - for std, states in self.selectedStates.items(): - if std.startswith('state.resource.'): - std = std[len('state.resource.'):] - else: - continue - stf = component.queryAdapter(obj, IStateful, name=std) - if stf is None: - continue - for state in states: - if stf.state == state: - break - else: - return False - return True - - -#class SearchResults(BaseView): -class SearchResults(NodeView): - """ Provides results as inner HTML """ - - @Lazy - def search_macros(self): - return self.controller.getTemplateMacros('search', search_template) - - @Lazy - def macro(self): - return self.search_macros['search_results'] - - def __call__(self): - return innerHtml(self) - - @Lazy - def results(self): - form = self.request.form - type = form.get('search.1.text', 'loops:*') - text = form.get('search.2.text') - if text is not None: - text = util.toUnicode(text, encoding='ISO8859-15') # IE hack!!! - 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') - #if conceptTitle is not None: - # conceptTitle = util.toUnicode(conceptTitle, encoding='ISO8859-15') - conceptUid = form.get('search.3.text') - result = FullQuery(self).query(text=text, type=type, - useTitle=useTitle, useFull=useFull, - #conceptTitle=conceptTitle, - conceptUid=conceptUid, - 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 - fv = FilterView(self.context, self.request) - result = fv.apply(result) - result = sorted(result, key=lambda x: x.title.lower()) - return self.viewIterator(result) - diff --git a/search/configure.zcml b/search/configure.zcml deleted file mode 100644 index cb773cb..0000000 --- a/search/configure.zcml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - diff --git a/search/search.pt b/search/search.pt deleted file mode 100644 index a7d0a91..0000000 --- a/search/search.pt +++ /dev/null @@ -1,358 +0,0 @@ - - - - - -
-
-
- - - - - - -

Type(s) to search for

- - - - - - -   - - - -
- - - - - - -

Text-based search

- - - - - - - -    - - - - - - -   - - -
- - - - - - -

Search via related concepts

- - - - - - Type: - - - - - - - - - - - - - - - -    - - - - -   - - - -
-
- -
- - -
- - - - - - -

Restrict to objects with certain states

- - - - Workflow - States - - - - - - - -   - - - -
- - - -   - diff --git a/search/tests.py b/search/tests.py deleted file mode 100755 index 4edec1b..0000000 --- a/search/tests.py +++ /dev/null @@ -1,26 +0,0 @@ -# $Id$ - -import unittest, doctest -from zope.testing.doctestunit import DocFileSuite -from zope.interface.verify import verifyClass - -#from loops.search.interfaces import ... - - -class Test(unittest.TestCase): - "Basic tests for the loops.search package." - - def testBase(self): - pass - - -def test_suite(): - flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS - return unittest.TestSuite(( - unittest.makeSuite(Test), - DocFileSuite('README.txt', - optionflags=flags,), - )) - -if __name__ == '__main__': - unittest.main(defaultTest='test_suite')