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:
parent
3a65566beb
commit
629de87686
12 changed files with 153 additions and 32 deletions
2
base.py
2
base.py
|
@ -59,7 +59,7 @@ class Loops(Folder):
|
|||
def getLoopsRoot(self):
|
||||
return self
|
||||
|
||||
def getAllParents(self, collectGrants=False):
|
||||
def getAllParents(self, collectGrants=False, ignoreTypes=False):
|
||||
return Jeep()
|
||||
|
||||
def getConceptManager(self):
|
||||
|
|
|
@ -281,6 +281,8 @@ class ConceptView(BaseView):
|
|||
for r in self.context.getChildRelations(sort=None))
|
||||
if sort:
|
||||
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:
|
||||
if criteria:
|
||||
if not self.checkCriteria(r, criteria):
|
||||
|
@ -294,6 +296,7 @@ class ConceptView(BaseView):
|
|||
break
|
||||
if skip:
|
||||
continue
|
||||
if fv.check(r.context):
|
||||
yield r
|
||||
|
||||
def checkCriteria(self, relation, criteria):
|
||||
|
@ -352,10 +355,13 @@ class ConceptView(BaseView):
|
|||
yield self.childViewFactory(r, self.request)
|
||||
|
||||
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()
|
||||
for r in rels:
|
||||
#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)
|
||||
|
||||
def unique(self, rels):
|
||||
|
|
|
@ -49,14 +49,15 @@ from cybertools.composer.schema.browser.common import schema_macros, schema_edit
|
|||
from cybertools.composer.schema.schema import FormState
|
||||
from cybertools.stateful.interfaces import IStateful
|
||||
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.concept import Concept, ConceptRelation, ResourceRelation
|
||||
from loops.interfaces import IConcept, IConceptSchema
|
||||
from loops.interfaces import IResource, IResourceManager, IDocument
|
||||
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.organize.personal.browser.filter import FilterView
|
||||
from loops.query import ConceptQuery, IQueryConcept
|
||||
from loops.resource import Resource
|
||||
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]
|
||||
|
||||
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')
|
||||
result = sorted(ConceptQuery(self).query(type=token), key=lambda x: x.title)
|
||||
predicateUid = self.defaultPredicateUid
|
||||
return ([noSelection] +
|
||||
[dict(title=o.title,
|
||||
|
|
|
@ -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
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -151,16 +151,19 @@ class Concept(Contained, Persistent):
|
|||
def getConceptManager(self):
|
||||
return self.getLoopsRoot().getConceptManager()
|
||||
|
||||
def getAllParents(self, collectGrants=False, result=None):
|
||||
def getAllParents(self, collectGrants=False, result=None, ignoreTypes=False):
|
||||
if result is None:
|
||||
result = Jeep()
|
||||
for rel in self.getParentRelations():
|
||||
if (ignoreTypes and
|
||||
rel.predicate == self.getConceptManager().getTypePredicate()):
|
||||
continue
|
||||
obj = rel.first
|
||||
uid = util.getUidForObject(obj)
|
||||
pi = result.get(uid)
|
||||
if pi is None:
|
||||
result[uid] = ParentInfo(obj, [rel])
|
||||
obj.getAllParents(collectGrants, result)
|
||||
obj.getAllParents(collectGrants, result, ignoreTypes)
|
||||
elif rel not in pi.relations:
|
||||
pi.relations.append(rel)
|
||||
return result
|
||||
|
|
|
@ -30,6 +30,7 @@ from zope.traversing.api import getName, getParent
|
|||
|
||||
from loops.browser.node import NodeView
|
||||
from loops.expert.concept import ConceptQuery, FullQuery
|
||||
from loops.organize.personal.browser.filter import FilterView
|
||||
from loops import util
|
||||
from loops.util import _
|
||||
|
||||
|
@ -59,4 +60,7 @@ class SearchResults(NodeView):
|
|||
type = self.globalOptions('expert.quicksearch')
|
||||
result = FullQuery(self).query(text=text, type=type,
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
ZCML setup):
|
||||
|
||||
>>> from loops.concept import Concept
|
||||
>>> from loops.setup import addAndConfigureObject
|
||||
|
||||
>>> from loops.organize.setup import SetupManager
|
||||
>>> component.provideAdapter(SetupManager, name='organize')
|
||||
>>> from loops.organize.personal.setup import SetupManager
|
||||
|
@ -114,6 +117,46 @@ Let's now trigger the saving of a favorite.
|
|||
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
|
||||
=============
|
||||
|
||||
|
|
|
@ -94,6 +94,6 @@ class PortletConfigurator(ViewConfigurator):
|
|||
title=_(u'Filters'),
|
||||
subMacro=personal_macros.macros['filters_portlet'],
|
||||
priority=195,
|
||||
url=absoluteURL(self.context, self.request) + '/@@filters.html',
|
||||
#url=absoluteURL(self.context, self.request) + '/@@filters.html',
|
||||
))
|
||||
return [filters]
|
||||
|
|
|
@ -28,6 +28,7 @@ from zope.cachedescriptors.property import Lazy
|
|||
|
||||
from cybertools.browser.configurator import ViewConfigurator, MacroViewProperty
|
||||
from loops.browser.node import NodeView
|
||||
from loops.concept import Concept
|
||||
from loops.organize.party import getPersonForUser
|
||||
from loops.organize.personal.interfaces import IFilters
|
||||
from loops import util
|
||||
|
@ -46,6 +47,16 @@ class FilterView(NodeView):
|
|||
def person(self):
|
||||
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
|
||||
def filters(self):
|
||||
records = self.loopsRoot.getRecordManager()
|
||||
|
@ -64,6 +75,7 @@ class FilterView(NodeView):
|
|||
yield dict(url=self.getUrlForTarget(obj),
|
||||
uid=uid,
|
||||
title=obj.title,
|
||||
typeTitle=obj.getType().title,
|
||||
description=obj.description,
|
||||
object=obj)
|
||||
|
||||
|
@ -83,5 +95,40 @@ class FilterView(NodeView):
|
|||
id = self.request.get('id')
|
||||
if not id:
|
||||
return
|
||||
self.filters.deactivate(id)
|
||||
#self.filters.deactivate(id)
|
||||
self.filters.remove(id, self.person)
|
||||
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)]
|
||||
|
||||
|
|
|
@ -7,15 +7,13 @@
|
|||
string:${view/virtualTargetUrl}/removeFavorite.html?id=${item/uid};
|
||||
title string:Remove from favorites"
|
||||
i18n:attributes="title">X</a> </span>
|
||||
<a href=""
|
||||
tal:attributes="href item/url;
|
||||
<a tal:attributes="href item/url;
|
||||
title item/description"
|
||||
tal:content="item/title">Some object</a>
|
||||
</div>
|
||||
<div id="addFavorite" class="action"
|
||||
tal:condition="targetUid">
|
||||
<a href="addFavorite.html"
|
||||
i18n:translate=""
|
||||
<a i18n:translate=""
|
||||
tal:attributes="href
|
||||
string:${view/virtualTargetUrl}/addFavorite.html?id=$targetUid;
|
||||
title string:Add current object to favorites"
|
||||
|
@ -30,18 +28,17 @@
|
|||
<div tal:repeat="item view/listFilters">
|
||||
<span style="float:right" class="delete-item"> <a href="removeFilter.html"
|
||||
tal:attributes="href
|
||||
string:${view/url}/deactivateFilter.html?id=${item/uid};
|
||||
string:${view/virtualTargetUrl}/deactivateFilter.html?id=${item/uid};
|
||||
title string:Deactivate filter"
|
||||
i18n:attributes="title">X</a> </span>
|
||||
<a href=""
|
||||
tal:attributes="href item/url;
|
||||
title item/description"
|
||||
tal:content="item/title">Some object</a>
|
||||
<a tal:attributes="href item/url;
|
||||
title item/description">
|
||||
<span tal:content="item/title">Some object</span>
|
||||
(<i i18n:translate="" tal:content="item/typeTitle">Type</i>)</a>
|
||||
</div>
|
||||
<div id="addFilter" class="action"
|
||||
tal:condition="targetUid">
|
||||
<a href="addFilter.html"
|
||||
i18n:translate=""
|
||||
<a i18n:translate=""
|
||||
tal:attributes="href
|
||||
string:${view/virtualTargetUrl}/addFilter.html?id=$targetUid;
|
||||
title string:Use current object as filter"
|
||||
|
|
|
@ -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
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -63,8 +63,9 @@ class Filters(object):
|
|||
data = {}
|
||||
return self.context.saveUserTrack(uid, 0, personUid, data)
|
||||
|
||||
def remove(self, id):
|
||||
def remove(self, uid, person):
|
||||
changed = False
|
||||
personUid = util.getUidForObject(person)
|
||||
for t in self.context.query(userName=personUid, taskId=uid):
|
||||
changed = True
|
||||
self.context.removeTrack(t)
|
||||
|
|
20
resource.py
20
resource.py
|
@ -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
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -25,7 +25,6 @@ $Id$
|
|||
from cStringIO import StringIO
|
||||
from persistent import Persistent
|
||||
from zope import component, schema
|
||||
from zope.app import zapi
|
||||
from zope.app.container.btree import BTreeContainer
|
||||
from zope.app.container.contained import Contained
|
||||
from zope.app.file.image import Image
|
||||
|
@ -74,7 +73,10 @@ class ResourceManager(BTreeContainer):
|
|||
implements(IResourceManager, ILoopsContained)
|
||||
|
||||
def getLoopsRoot(self):
|
||||
return zapi.getParent(self)
|
||||
return getParent(self)
|
||||
|
||||
def getConceptManager(self):
|
||||
return self.getLoopsRoot().getConceptManager()
|
||||
|
||||
def getViewManager(self):
|
||||
return self.getLoopsRoot().getViewManager()
|
||||
|
@ -166,17 +168,23 @@ class Resource(Image, Contained):
|
|||
contentType = property(getContentType, setContentType)
|
||||
|
||||
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()
|
||||
for rel in self.getConceptRelations():
|
||||
if (ignoreTypes and
|
||||
rel.predicate == self.getConceptManager().getTypePredicate()):
|
||||
continue
|
||||
obj = rel.first
|
||||
uid = util.getUidForObject(obj)
|
||||
pi = result.setdefault(uid, ParentInfo(obj))
|
||||
if rel not in pi.relations:
|
||||
pi.relations.append(rel)
|
||||
obj.getAllParents(collectGrants, result)
|
||||
obj.getAllParents(collectGrants, result, ignoreTypes)
|
||||
return result
|
||||
|
||||
# concept relations
|
||||
|
|
|
@ -37,6 +37,7 @@ from loops.browser.common import BaseView
|
|||
from loops.browser.node import NodeView
|
||||
from loops.common import adapted, AdapterBase
|
||||
from loops.expert.concept import ConceptQuery, FullQuery
|
||||
from loops.organize.personal.browser.filter import FilterView
|
||||
from loops import util
|
||||
from loops.util import _
|
||||
|
||||
|
@ -77,8 +78,11 @@ class Search(BaseView):
|
|||
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')
|
||||
result = sorted(ConceptQuery(self).query(type=token), key=lambda x: x.title)
|
||||
return [noSelection] + [dict(title=adapted(o, self.languageInfo).title,
|
||||
token=util.getUidForObject(o))
|
||||
for o in result]
|
||||
|
@ -112,6 +116,8 @@ class Search(BaseView):
|
|||
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)
|
||||
|
@ -206,6 +212,8 @@ class SearchResults(NodeView):
|
|||
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)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue