filtering functionality basically working, including search and standard concept listings

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@4160 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2011-02-07 16:53:08 +00:00
parent 3a65566beb
commit 629de87686
12 changed files with 153 additions and 32 deletions

View file

@ -59,7 +59,7 @@ class Loops(Folder):
def getLoopsRoot(self): def getLoopsRoot(self):
return self return self
def getAllParents(self, collectGrants=False): def getAllParents(self, collectGrants=False, ignoreTypes=False):
return Jeep() return Jeep()
def getConceptManager(self): def getConceptManager(self):

View file

@ -281,6 +281,8 @@ class ConceptView(BaseView):
for r in self.context.getChildRelations(sort=None)) for r in self.context.getChildRelations(sort=None))
if sort: if sort:
rels = sorted(rels, key=lambda r: (r.order, r.title.lower())) rels = sorted(rels, key=lambda r: (r.order, r.title.lower()))
from loops.organize.personal.browser.filter import FilterView
fv = FilterView(self.context, self.request)
for r in rels: for r in rels:
if criteria: if criteria:
if not self.checkCriteria(r, criteria): if not self.checkCriteria(r, criteria):
@ -294,7 +296,8 @@ class ConceptView(BaseView):
break break
if skip: if skip:
continue continue
yield r if fv.check(r.context):
yield r
def checkCriteria(self, relation, criteria): def checkCriteria(self, relation, criteria):
result = True result = True
@ -352,11 +355,14 @@ class ConceptView(BaseView):
yield self.childViewFactory(r, self.request) yield self.childViewFactory(r, self.request)
def resources(self): def resources(self):
from loops.browser.resource import ResourceRelationView
from loops.organize.personal.browser.filter import FilterView
fv = FilterView(self.context, self.request)
rels = self.context.getResourceRelations() rels = self.context.getResourceRelations()
for r in rels: for r in rels:
#yield self.childViewFactory(r, self.request, contextIsSecond=True) #yield self.childViewFactory(r, self.request, contextIsSecond=True)
from loops.browser.resource import ResourceRelationView if fv.check(r.first):
yield ResourceRelationView(r, self.request, contextIsSecond=True) yield ResourceRelationView(r, self.request, contextIsSecond=True)
def unique(self, rels): def unique(self, rels):
result = Jeep() result = Jeep()

View file

@ -49,14 +49,15 @@ from cybertools.composer.schema.browser.common import schema_macros, schema_edit
from cybertools.composer.schema.schema import FormState from cybertools.composer.schema.schema import FormState
from cybertools.stateful.interfaces import IStateful from cybertools.stateful.interfaces import IStateful
from cybertools.typology.interfaces import IType, ITypeManager from cybertools.typology.interfaces import IType, ITypeManager
from loops.browser.node import NodeView
from loops.browser.concept import ConceptRelationView
from loops.common import adapted from loops.common import adapted
from loops.concept import Concept, ConceptRelation, ResourceRelation from loops.concept import Concept, ConceptRelation, ResourceRelation
from loops.interfaces import IConcept, IConceptSchema from loops.interfaces import IConcept, IConceptSchema
from loops.interfaces import IResource, IResourceManager, IDocument from loops.interfaces import IResource, IResourceManager, IDocument
from loops.interfaces import IFile, IExternalFile, INote, ITextDocument from loops.interfaces import IFile, IExternalFile, INote, ITextDocument
from loops.browser.node import NodeView
from loops.browser.concept import ConceptRelationView
from loops.i18n.browser import I18NView from loops.i18n.browser import I18NView
from loops.organize.personal.browser.filter import FilterView
from loops.query import ConceptQuery, IQueryConcept from loops.query import ConceptQuery, IQueryConcept
from loops.resource import Resource from loops.resource import Resource
from loops.schema.field import relation_macros from loops.schema.field import relation_macros
@ -196,8 +197,11 @@ class ObjectForm(NodeView):
return [dict(title=t.title, token=t.tokenForSearch) for t in types] return [dict(title=t.title, token=t.tokenForSearch) for t in types]
def conceptsForType(self, token): 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') noSelection = dict(token='none', title=u'not selected')
result = sorted(ConceptQuery(self).query(type=token), key=lambda x: x.title)
predicateUid = self.defaultPredicateUid predicateUid = self.defaultPredicateUid
return ([noSelection] + return ([noSelection] +
[dict(title=o.title, [dict(title=o.title,

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2010 Helmut Merz helmutm@cy55.de # Copyright (c) 2011 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -151,16 +151,19 @@ class Concept(Contained, Persistent):
def getConceptManager(self): def getConceptManager(self):
return self.getLoopsRoot().getConceptManager() return self.getLoopsRoot().getConceptManager()
def getAllParents(self, collectGrants=False, result=None): def getAllParents(self, collectGrants=False, result=None, ignoreTypes=False):
if result is None: if result is None:
result = Jeep() result = Jeep()
for rel in self.getParentRelations(): for rel in self.getParentRelations():
if (ignoreTypes and
rel.predicate == self.getConceptManager().getTypePredicate()):
continue
obj = rel.first obj = rel.first
uid = util.getUidForObject(obj) uid = util.getUidForObject(obj)
pi = result.get(uid) pi = result.get(uid)
if pi is None: if pi is None:
result[uid] = ParentInfo(obj, [rel]) result[uid] = ParentInfo(obj, [rel])
obj.getAllParents(collectGrants, result) obj.getAllParents(collectGrants, result, ignoreTypes)
elif rel not in pi.relations: elif rel not in pi.relations:
pi.relations.append(rel) pi.relations.append(rel)
return result return result

View file

@ -30,6 +30,7 @@ from zope.traversing.api import getName, getParent
from loops.browser.node import NodeView from loops.browser.node import NodeView
from loops.expert.concept import ConceptQuery, FullQuery from loops.expert.concept import ConceptQuery, FullQuery
from loops.organize.personal.browser.filter import FilterView
from loops import util from loops import util
from loops.util import _ from loops.util import _
@ -59,4 +60,7 @@ class SearchResults(NodeView):
type = self.globalOptions('expert.quicksearch') type = self.globalOptions('expert.quicksearch')
result = FullQuery(self).query(text=text, type=type, result = FullQuery(self).query(text=text, type=type,
useTitle=True, useFull=True,) useTitle=True, useFull=True,)
fv = FilterView(self.context, self.request)
result = fv.apply(result)
result.sort(key=lambda x: x.title)
return self.viewIterator(result) return self.viewIterator(result)

View file

@ -14,6 +14,9 @@ and set up a simple loops site with a concept manager and some concepts
(with all the type machinery, what in real life is done via standard (with all the type machinery, what in real life is done via standard
ZCML setup): ZCML setup):
>>> from loops.concept import Concept
>>> from loops.setup import addAndConfigureObject
>>> from loops.organize.setup import SetupManager >>> from loops.organize.setup import SetupManager
>>> component.provideAdapter(SetupManager, name='organize') >>> component.provideAdapter(SetupManager, name='organize')
>>> from loops.organize.personal.setup import SetupManager >>> from loops.organize.personal.setup import SetupManager
@ -114,6 +117,46 @@ Let's now trigger the saving of a favorite.
1 1
Filters - Show only Certain Parts of the Concept Map
====================================================
>>> baseFilters = records['filters']
>>> from loops.organize.personal.filter import Filters
>>> component.provideAdapter(Filters)
Let's prepare some concepts and assignments to be used for filtering.
>>> dGeneral = addAndConfigureObject(concepts, Concept, 'general',
... conceptType=concepts['domain'])
>>> dProjects = concepts['projects'] # created in global setup
>>> dGeneral.assignResource(resources['d001.txt'])
>>> dProjects.assignResource(resources['d002.txt'])
Now we can define a simple filter that blocks certain concepts and resources.
>>> from loops.organize.personal.interfaces import IFilters
>>> filters = IFilters(baseFilters)
>>> filters.add(dProjects, johnC)
'0000001'
We access the filters via a filter view.
>>> from loops.organize.personal.browser.filter import FilterView
>>> fv = FilterView(home, TestRequest())
>>> fv.person = johnC
>>> fv.check(resources['d001.txt'])
False
>>> fv.check(resources['d002.txt'])
True
>>> fv.check(resources['d003.txt'])
True
>>> [r.__name__ for r in fv.apply(resources.values())]
[u'd002.txt', u'd003.txt']
Fin de partie Fin de partie
============= =============

View file

@ -94,6 +94,6 @@ class PortletConfigurator(ViewConfigurator):
title=_(u'Filters'), title=_(u'Filters'),
subMacro=personal_macros.macros['filters_portlet'], subMacro=personal_macros.macros['filters_portlet'],
priority=195, priority=195,
url=absoluteURL(self.context, self.request) + '/@@filters.html', #url=absoluteURL(self.context, self.request) + '/@@filters.html',
)) ))
return [filters] return [filters]

View file

@ -28,6 +28,7 @@ from zope.cachedescriptors.property import Lazy
from cybertools.browser.configurator import ViewConfigurator, MacroViewProperty from cybertools.browser.configurator import ViewConfigurator, MacroViewProperty
from loops.browser.node import NodeView from loops.browser.node import NodeView
from loops.concept import Concept
from loops.organize.party import getPersonForUser from loops.organize.party import getPersonForUser
from loops.organize.personal.interfaces import IFilters from loops.organize.personal.interfaces import IFilters
from loops import util from loops import util
@ -46,6 +47,16 @@ class FilterView(NodeView):
def person(self): def person(self):
return getPersonForUser(self.context, self.request) return getPersonForUser(self.context, self.request)
@Lazy
def targetUid(self):
target = self.virtualTargetObject
if (target and isinstance(target, Concept) and
target.conceptType != self.conceptManager.getTypeConcept()):
return util.getUidForObject(target)
else:
return None
@Lazy @Lazy
def filters(self): def filters(self):
records = self.loopsRoot.getRecordManager() records = self.loopsRoot.getRecordManager()
@ -64,6 +75,7 @@ class FilterView(NodeView):
yield dict(url=self.getUrlForTarget(obj), yield dict(url=self.getUrlForTarget(obj),
uid=uid, uid=uid,
title=obj.title, title=obj.title,
typeTitle=obj.getType().title,
description=obj.description, description=obj.description,
object=obj) object=obj)
@ -83,5 +95,40 @@ class FilterView(NodeView):
id = self.request.get('id') id = self.request.get('id')
if not id: if not id:
return return
self.filters.deactivate(id) #self.filters.deactivate(id)
self.filters.remove(id, self.person)
self.request.response.redirect(self.virtualTargetUrl) self.request.response.redirect(self.virtualTargetUrl)
@Lazy
def filterStructure(self):
result = {}
for item in self.listFilters():
obj = item['object']
result.setdefault(obj.getType(), set([])).add(obj)
return result
def check(self, obj):
fs = self.filterStructure
if not fs:
return True
for objs in fs.values():
if obj in objs: # the filtered object itself is always shown
return True
checked = {}
parents = [pi.object for pi in obj.getAllParents(ignoreTypes=True)]
if isinstance(obj, Concept):
parents.insert(0, obj)
for p in parents:
for t, v in fs.items():
if checked.get(t): # type already present and set to True
continue
if p.conceptType == t: # type has to be checked
checked[t] = (p in v)
for x in checked.values():
if not x:
return False
return True
def apply(self, seq):
return [obj for obj in seq if self.check(obj)]

View file

@ -7,15 +7,13 @@
string:${view/virtualTargetUrl}/removeFavorite.html?id=${item/uid}; string:${view/virtualTargetUrl}/removeFavorite.html?id=${item/uid};
title string:Remove from favorites" title string:Remove from favorites"
i18n:attributes="title">X</a>&nbsp;</span> i18n:attributes="title">X</a>&nbsp;</span>
<a href="" <a tal:attributes="href item/url;
tal:attributes="href item/url;
title item/description" title item/description"
tal:content="item/title">Some object</a> tal:content="item/title">Some object</a>
</div> </div>
<div id="addFavorite" class="action" <div id="addFavorite" class="action"
tal:condition="targetUid"> tal:condition="targetUid">
<a href="addFavorite.html" <a i18n:translate=""
i18n:translate=""
tal:attributes="href tal:attributes="href
string:${view/virtualTargetUrl}/addFavorite.html?id=$targetUid; string:${view/virtualTargetUrl}/addFavorite.html?id=$targetUid;
title string:Add current object to favorites" title string:Add current object to favorites"
@ -30,18 +28,17 @@
<div tal:repeat="item view/listFilters"> <div tal:repeat="item view/listFilters">
<span style="float:right" class="delete-item">&nbsp;<a href="removeFilter.html" <span style="float:right" class="delete-item">&nbsp;<a href="removeFilter.html"
tal:attributes="href tal:attributes="href
string:${view/url}/deactivateFilter.html?id=${item/uid}; string:${view/virtualTargetUrl}/deactivateFilter.html?id=${item/uid};
title string:Deactivate filter" title string:Deactivate filter"
i18n:attributes="title">X</a>&nbsp;</span> i18n:attributes="title">X</a>&nbsp;</span>
<a href="" <a tal:attributes="href item/url;
tal:attributes="href item/url; title item/description">
title item/description" <span tal:content="item/title">Some object</span>
tal:content="item/title">Some object</a> (<i i18n:translate="" tal:content="item/typeTitle">Type</i>)</a>
</div> </div>
<div id="addFilter" class="action" <div id="addFilter" class="action"
tal:condition="targetUid"> tal:condition="targetUid">
<a href="addFilter.html" <a i18n:translate=""
i18n:translate=""
tal:attributes="href tal:attributes="href
string:${view/virtualTargetUrl}/addFilter.html?id=$targetUid; string:${view/virtualTargetUrl}/addFilter.html?id=$targetUid;
title string:Use current object as filter" title string:Use current object as filter"

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2010 Helmut Merz helmutm@cy55.de # Copyright (c) 2011 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -63,8 +63,9 @@ class Filters(object):
data = {} data = {}
return self.context.saveUserTrack(uid, 0, personUid, data) return self.context.saveUserTrack(uid, 0, personUid, data)
def remove(self, id): def remove(self, uid, person):
changed = False changed = False
personUid = util.getUidForObject(person)
for t in self.context.query(userName=personUid, taskId=uid): for t in self.context.query(userName=personUid, taskId=uid):
changed = True changed = True
self.context.removeTrack(t) self.context.removeTrack(t)

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de # Copyright (c) 2011 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -25,7 +25,6 @@ $Id$
from cStringIO import StringIO from cStringIO import StringIO
from persistent import Persistent from persistent import Persistent
from zope import component, schema from zope import component, schema
from zope.app import zapi
from zope.app.container.btree import BTreeContainer from zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained from zope.app.container.contained import Contained
from zope.app.file.image import Image from zope.app.file.image import Image
@ -74,7 +73,10 @@ class ResourceManager(BTreeContainer):
implements(IResourceManager, ILoopsContained) implements(IResourceManager, ILoopsContained)
def getLoopsRoot(self): def getLoopsRoot(self):
return zapi.getParent(self) return getParent(self)
def getConceptManager(self):
return self.getLoopsRoot().getConceptManager()
def getViewManager(self): def getViewManager(self):
return self.getLoopsRoot().getViewManager() return self.getLoopsRoot().getViewManager()
@ -166,17 +168,23 @@ class Resource(Image, Contained):
contentType = property(getContentType, setContentType) contentType = property(getContentType, setContentType)
def getLoopsRoot(self): def getLoopsRoot(self):
return zapi.getParent(self).getLoopsRoot() return getParent(self).getLoopsRoot()
def getAllParents(self, collectGrants=False): def getConceptManager(self):
return self.getLoopsRoot().getConceptManager()
def getAllParents(self, collectGrants=False, ignoreTypes=False):
result = Jeep() result = Jeep()
for rel in self.getConceptRelations(): for rel in self.getConceptRelations():
if (ignoreTypes and
rel.predicate == self.getConceptManager().getTypePredicate()):
continue
obj = rel.first obj = rel.first
uid = util.getUidForObject(obj) uid = util.getUidForObject(obj)
pi = result.setdefault(uid, ParentInfo(obj)) pi = result.setdefault(uid, ParentInfo(obj))
if rel not in pi.relations: if rel not in pi.relations:
pi.relations.append(rel) pi.relations.append(rel)
obj.getAllParents(collectGrants, result) obj.getAllParents(collectGrants, result, ignoreTypes)
return result return result
# concept relations # concept relations

View file

@ -37,6 +37,7 @@ from loops.browser.common import BaseView
from loops.browser.node import NodeView from loops.browser.node import NodeView
from loops.common import adapted, AdapterBase from loops.common import adapted, AdapterBase
from loops.expert.concept import ConceptQuery, FullQuery from loops.expert.concept import ConceptQuery, FullQuery
from loops.organize.personal.browser.filter import FilterView
from loops import util from loops import util
from loops.util import _ from loops.util import _
@ -77,8 +78,11 @@ class Search(BaseView):
return self.listTypesForSearch(include=('search',)) return self.listTypesForSearch(include=('search',))
def conceptsForType(self, token): 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') noSelection = dict(token='none', title=u'not selected')
result = sorted(ConceptQuery(self).query(type=token), key=lambda x: x.title)
return [noSelection] + [dict(title=adapted(o, self.languageInfo).title, return [noSelection] + [dict(title=adapted(o, self.languageInfo).title,
token=util.getUidForObject(o)) token=util.getUidForObject(o))
for o in result] for o in result]
@ -112,6 +116,8 @@ class Search(BaseView):
for type in types: for type in types:
result = self.executeQuery(title=title or None, type=type, result = self.executeQuery(title=title or None, type=type,
exclude=('hidden',)) exclude=('hidden',))
fv = FilterView(self.context, self.request)
result = fv.apply(result)
for o in result: for o in result:
if o.getLoopsRoot() == self.loopsRoot: if o.getLoopsRoot() == self.loopsRoot:
adObj = adapted(o, self.languageInfo) adObj = adapted(o, self.languageInfo)
@ -206,6 +212,8 @@ class SearchResults(NodeView):
result = [r for r in result if r in addSelection] result = [r for r in result if r in addSelection]
else: else:
result = addSelection result = addSelection
fv = FilterView(self.context, self.request)
result = fv.apply(result)
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)