added options attribute to query, e.g. for controlling autoassignment; work in progress: loops.expert - starting to implement filters
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2422 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
3b5132b0f6
commit
8660f12f5e
14 changed files with 415 additions and 43 deletions
|
@ -679,6 +679,15 @@ been created during setup.
|
|||
>>> form.presetTypesForAssignment
|
||||
[{'token': 'loops:concept:customer', 'title': u'Customer'}]
|
||||
|
||||
If the node's target is a type concept we don't get any assignments because
|
||||
it does not make much sense to assign resources or other concepts as
|
||||
children to type concepts.
|
||||
|
||||
>>> m112.target = customer
|
||||
>>> form = CreateObjectForm(m112, TestRequest())
|
||||
>>> form.assignments
|
||||
()
|
||||
|
||||
OK, so much about the form - now we want to create a new object based
|
||||
on data provided in this form:
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ 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.query import ConceptQuery
|
||||
from loops.query import ConceptQuery, IQueryConcept
|
||||
from loops.resource import Resource
|
||||
from loops.type import ITypeConcept
|
||||
from loops import util
|
||||
|
@ -265,13 +265,23 @@ class CreateObjectForm(ObjectForm):
|
|||
@property
|
||||
def assignments(self):
|
||||
target = self.virtualTargetObject
|
||||
if (IConcept.providedBy(target) and
|
||||
target.conceptType !=
|
||||
self.loopsRoot.getConceptManager().getTypeConcept()):
|
||||
if self.maybeAssignedAsParent(target):
|
||||
rv = ConceptRelationView(ResourceRelation(target, None), self.request)
|
||||
return (rv,)
|
||||
return ()
|
||||
|
||||
def maybeAssignedAsParent(self, obj):
|
||||
if not IConcept.providedBy(obj):
|
||||
return False
|
||||
if obj.conceptType == self.loopsRoot.getConceptManager().getTypeConcept():
|
||||
return False
|
||||
if 'noassign' in IType(obj).qualifiers:
|
||||
return False
|
||||
adap = adapted(obj)
|
||||
if 'noassign' in getattr(adap, 'options', []):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class CreateObjectPopup(CreateObjectForm):
|
||||
|
||||
|
@ -329,9 +339,7 @@ class CreateConceptForm(CreateObjectForm):
|
|||
@property
|
||||
def assignments(self):
|
||||
target = self.virtualTargetObject
|
||||
if (IConcept.providedBy(target) and
|
||||
target.conceptType !=
|
||||
self.loopsRoot.getConceptManager().getTypeConcept()):
|
||||
if self.maybeAssignedAsParent(target):
|
||||
rv = ConceptRelationView(ConceptRelation(target, None), self.request)
|
||||
return (rv,)
|
||||
return ()
|
||||
|
|
31
common.py
31
common.py
|
@ -38,7 +38,11 @@ from loops.interfaces import ILoopsObject, ILoopsContained, IConcept, IResource
|
|||
from loops.interfaces import IResourceAdapter
|
||||
|
||||
|
||||
# convenience functions
|
||||
|
||||
def adapted(obj, langInfo=None):
|
||||
""" Return adapter based on the object type's type interface.
|
||||
"""
|
||||
t = IType(obj, None)
|
||||
if t is not None:
|
||||
ti = t.typeInterface
|
||||
|
@ -52,6 +56,33 @@ def adapted(obj, langInfo=None):
|
|||
return obj
|
||||
|
||||
|
||||
# helper functions for specifying automatic attribute handling
|
||||
|
||||
def adapterAttributes(*args):
|
||||
attrs = []
|
||||
for arg in args:
|
||||
if isinstance(arg, basestring):
|
||||
attrs.append(arg)
|
||||
elif isinstance(arg, type):
|
||||
attrs.extend(list(arg._adapterAttributes))
|
||||
else:
|
||||
raise ValueError("Argument must be string or class, '%s' is '%s'." %
|
||||
(arg, type(arg)))
|
||||
return tuple(attrs)
|
||||
|
||||
def contextAttributes(*args):
|
||||
attrs = []
|
||||
for arg in args:
|
||||
if isinstance(arg, basestring):
|
||||
attrs.append(arg)
|
||||
elif isinstance(arg, type):
|
||||
attrs.extend(list(arg._contextAttributes))
|
||||
else:
|
||||
raise ValueError("Argument must be string or class, '%s' is '%s'." %
|
||||
(arg, type(arg)))
|
||||
return attrs
|
||||
|
||||
|
||||
# type interface adapters
|
||||
|
||||
class AdapterBase(object):
|
||||
|
|
|
@ -27,7 +27,7 @@ from zope.cachedescriptors.property import Lazy
|
|||
from zope.interface import implements
|
||||
from zope.traversing.api import getName
|
||||
|
||||
from loops.common import AdapterBase
|
||||
from loops.common import AdapterBase, adapterAttributes, contextAttributes
|
||||
from loops.constraint.interfaces import IStaticConstraint
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
@ -42,8 +42,8 @@ hasChildConstraint = 'haschildconstraint'
|
|||
|
||||
class StaticConstraint(AdapterBase):
|
||||
|
||||
_contextAttributes = AdapterBase._contextAttributes + ['relationType', 'cardinality']
|
||||
_adapterAttributes = AdapterBase._adapterAttributes + ('predicates', 'types',)
|
||||
_contextAttributes = contextAttributes(AdapterBase, 'relationType', 'cardinality')
|
||||
_adapterAttributes = adapterAttributes(AdapterBase, 'predicates', 'types')
|
||||
|
||||
implements(IStaticConstraint)
|
||||
|
||||
|
|
|
@ -29,23 +29,32 @@ configuration):
|
|||
>>> t = TestSite(site)
|
||||
>>> concepts, resources, views = t.setup()
|
||||
|
||||
>>> #sorted(concepts)
|
||||
>>> #sorted(resources)
|
||||
>>> len(concepts) + len(resources)
|
||||
38
|
||||
36
|
||||
|
||||
>>> loopsRoot = site['loops']
|
||||
|
||||
|
||||
Type- and Text-based Queries
|
||||
============================
|
||||
Queries
|
||||
=======
|
||||
|
||||
Queries search the loops database (typically but not necessarily the
|
||||
catalog) for objects fulfilling the criteria given. A query returns
|
||||
a set of integer UIDs; thus the results of a query may be efficiently combined
|
||||
with those of other queries using logical operations.
|
||||
|
||||
|
||||
Type- and text-based queries
|
||||
----------------------------
|
||||
|
||||
>>> from loops.expert import query
|
||||
>>> qu = query.Title('ty*')
|
||||
>>> list(qu.apply())
|
||||
[0, 1, 50]
|
||||
[0, 1, 47]
|
||||
|
||||
>>> qu = query.Type('loops:*')
|
||||
>>> len(list(qu.apply()))
|
||||
38
|
||||
36
|
||||
|
||||
>>> qu = query.Type('loops:concept:predicate')
|
||||
>>> len(list(qu.apply()))
|
||||
|
@ -55,9 +64,8 @@ Type- and Text-based Queries
|
|||
>>> list(qu.apply())
|
||||
[1]
|
||||
|
||||
|
||||
Relationship-based Queries
|
||||
==========================
|
||||
Relationship-based queries
|
||||
--------------------------
|
||||
|
||||
In addition to the simple methods of concepts and resources for accessing
|
||||
relations to other objects the expert package provides methods
|
||||
|
@ -67,7 +75,77 @@ syntax (that in turn is based on hurry.query).
|
|||
>>> stateNew = concepts['new']
|
||||
>>> qu = query.Resources(stateNew)
|
||||
>>> list(qu.apply())
|
||||
[66, 71]
|
||||
[25, 27]
|
||||
|
||||
Getting objects
|
||||
---------------
|
||||
|
||||
When a query (or a combination of query terms) has been applied we
|
||||
want to get at the objects resulting from the query.
|
||||
|
||||
The ``getObjects()`` function returns an iterable with the objects.
|
||||
If the ``root`` argument is supplied only objects belonging to the
|
||||
corresponding loops site are returned. In addition a ``checkPermission``
|
||||
argument may be supplied with a function that should be checked for
|
||||
filtering the results; this defaults to ``canListObjects``.
|
||||
|
||||
>>> from loops.expert.query import getObjects
|
||||
>>> objs = getObjects(query.Title('ty*').apply(), root=loopsRoot)
|
||||
>>> sorted(o.title for o in objs)
|
||||
[u'Document Type', u'Type', u'has Type']
|
||||
|
||||
|
||||
Filters
|
||||
=======
|
||||
|
||||
Basically there are two kinds of filters: One is in fact just a query
|
||||
term that is joined ``and`` (``&``) operation to another query;
|
||||
the other one is applied to the objects resulting from applying a
|
||||
query by checking certain attributes or other conditions, thus reducing
|
||||
the number of the resulting objects.
|
||||
|
||||
Which kind of filtering will be used depends on the implementation - this
|
||||
may be an efficiency issue; there are also filters that don't have an
|
||||
equivalent query.
|
||||
|
||||
Example 1: "My Items"
|
||||
---------------------
|
||||
|
||||
Let's assume that jim is the person that corresponds to the logged-in user.
|
||||
We now want to set up a filter that lets pass only objects (resources and
|
||||
concepts) that are direct or indirect children of jim.
|
||||
|
||||
>>> jim = concepts['jim']
|
||||
|
||||
>>> qu = query.Type('loops:resource:textdocument')
|
||||
>>> objs = getObjects(qu.apply())
|
||||
>>> sorted(o.title for o in objs)
|
||||
[u'Doc 001', u'Doc 002', u'Doc 003']
|
||||
|
||||
>>> from loops.expert import filter
|
||||
>>> fltr = filter.Children(jim, recursive=True, includeResources=True)
|
||||
>>> sorted(o.title for o in getObjects((qu & fltr.query()).apply()))
|
||||
[u'Doc 001', u'Doc 003']
|
||||
|
||||
>>> #fltr.check(concepts['d001.txt'])
|
||||
>>> #fltr.check(concepts['d002.txt'])
|
||||
>>> #objs = fltr.apply(objs)
|
||||
>>> #sorted(o.title for o in objs.values())
|
||||
|
||||
|
||||
Organizing Queries and Filters with Query Instances
|
||||
===================================================
|
||||
|
||||
A query instance consists of
|
||||
|
||||
- a base query (a composition of terms)
|
||||
- one or more query filter that will be joined with the base query
|
||||
- a result filter that will be applied to the result set of the
|
||||
preceding steps
|
||||
|
||||
>>> from loops.expert.instance import QueryInstance
|
||||
>>> qi = QueryInstance(qu, fltr)
|
||||
>>> #qi.apply()
|
||||
|
||||
|
||||
Fin de partie
|
||||
|
|
58
expert/filter.py
Normal file
58
expert/filter.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
#
|
||||
# Copyright (c) 2008 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
|
||||
#
|
||||
|
||||
"""
|
||||
Filter query results.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from hurry.query.query import And
|
||||
from BTrees.IOBTree import IOBTree
|
||||
from zope.interface import implements
|
||||
|
||||
from loops.expert.interfaces import IFilter
|
||||
from loops.expert.query import Children as ChildrenQuery
|
||||
|
||||
|
||||
class Filter(object):
|
||||
|
||||
implements(IFilter)
|
||||
|
||||
def __init__(self, **kw):
|
||||
self.kwargs = kw
|
||||
|
||||
def apply(self, objects, queryInstance=None):
|
||||
result = IOBTree()
|
||||
for uid, obj in objects:
|
||||
if self.check(obj, queryInstance):
|
||||
result[uid] = obj
|
||||
return result
|
||||
|
||||
|
||||
class Children(Filter):
|
||||
|
||||
implements(IFilter)
|
||||
|
||||
def __init__(self, parent, **kw):
|
||||
self.parent = parent
|
||||
super(Children, self).__init__(**kw)
|
||||
|
||||
def query(self):
|
||||
return ChildrenQuery(self.parent, **self.kwargs)
|
||||
|
44
expert/instance.py
Normal file
44
expert/instance.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
#
|
||||
# Copyright (c) 2008 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
|
||||
#
|
||||
|
||||
"""
|
||||
Filter query results.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.interface import implements
|
||||
|
||||
from loops.expert.interfaces import IQueryInstance
|
||||
|
||||
|
||||
class QueryInstance(object):
|
||||
|
||||
implements(IQueryInstance)
|
||||
|
||||
def __init__(self, query, *filters, **kw):
|
||||
self.query = query
|
||||
self.filters = filters
|
||||
self.filterQueries = {}
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def apply(self, uidsOnly=False):
|
||||
result = self.query.apply()
|
||||
return result
|
||||
|
76
expert/interfaces.py
Normal file
76
expert/interfaces.py
Normal file
|
@ -0,0 +1,76 @@
|
|||
#
|
||||
# Copyright (c) 2008 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
|
||||
#
|
||||
|
||||
"""
|
||||
Filtering query result sets.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.interface import Interface, Attribute
|
||||
|
||||
|
||||
class IQuery(Interface):
|
||||
""" A query or
|
||||
"""
|
||||
|
||||
def apply():
|
||||
""" Return the result set for this query.
|
||||
"""
|
||||
|
||||
|
||||
class IQueryInstance(Interface):
|
||||
""" A top-level query instance that allows caching of intermediate
|
||||
results when the underlying queries and filters are applied.
|
||||
"""
|
||||
|
||||
query = Attribute('The top-level query (query term).')
|
||||
filters = Attribute('A collection of filters that will be applied '
|
||||
'to the result of the query.')
|
||||
|
||||
def apply(uidsOnly=False):
|
||||
""" Execute the query and apply the filters; return a mapping
|
||||
of UIDs to objects or a set of UIDs only if the ``uidsOnly``
|
||||
argument is set to True.
|
||||
"""
|
||||
|
||||
|
||||
class IFilter(Interface):
|
||||
""" A filter is a query that will be ``and``-connected to a query.
|
||||
"""
|
||||
|
||||
def query():
|
||||
""" Return the query that corresponds to this filter; that will then
|
||||
typically be ``and``-joined to another query.
|
||||
May return None; in this case the ``apply()`` method must be used.
|
||||
"""
|
||||
|
||||
def apply(objects, queryInstance=None):
|
||||
""" Apply the filter to the set of objects specified as a mapping
|
||||
of UIDs to objects.
|
||||
|
||||
If a query instance is given it may be used for caching
|
||||
intermediate results.
|
||||
"""
|
||||
|
||||
def check(obj, queryInstance=None):
|
||||
""" Return True if the object given should be included in the
|
||||
filter's result set, False otherwise.
|
||||
|
||||
If a query instance is given it may be used for caching.
|
||||
"""
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2008 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
|
||||
|
@ -17,23 +17,25 @@
|
|||
#
|
||||
|
||||
"""
|
||||
Generic query functionality for retrieving stuff from a loops database
|
||||
Generic query functionality for retrieving stuff from a loops database.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from BTrees.IIBTree import IITreeSet
|
||||
from BTrees.IFBTree import IFBTree, IFTreeSet
|
||||
|
||||
from BTrees.IOBTree import IOBTree
|
||||
from zope import interface, component
|
||||
from zope.app.intid.interfaces import IIntIds
|
||||
from zope.component import adapts
|
||||
from zope.interface import implements
|
||||
from zope.interface import implements, implementer
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
|
||||
from hurry.query.query import Term
|
||||
from hurry.query.query import Text as BaseText
|
||||
from hurry.query.query import Eq, Between
|
||||
|
||||
from loops.expert.interfaces import IQuery
|
||||
from loops.security.common import canListObject
|
||||
from loops import util
|
||||
|
||||
titleIndex = ('', 'loops_title')
|
||||
|
@ -41,12 +43,15 @@ textIndex = ('', 'loops_text')
|
|||
typeIndex = ('', 'loops_type')
|
||||
|
||||
|
||||
@implementer(IQuery)
|
||||
def Title(value):
|
||||
return BaseText(titleIndex, value)
|
||||
|
||||
@implementer(IQuery)
|
||||
def Text(value):
|
||||
return BaseText(textIndex, value)
|
||||
|
||||
@implementer(IQuery)
|
||||
def Type(value):
|
||||
if value.endswith('*'):
|
||||
v1 = value[:-1]
|
||||
|
@ -55,16 +60,56 @@ def Type(value):
|
|||
return Eq(typeIndex, value)
|
||||
|
||||
|
||||
class Resources(Term):
|
||||
class ConceptMapTerm(Term):
|
||||
|
||||
implements(IQuery)
|
||||
|
||||
def __init__(self, concept, **kw):
|
||||
self.context = concept
|
||||
self.kwargs = kw
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
class Resources(ConceptMapTerm):
|
||||
|
||||
predicates = None
|
||||
|
||||
def apply(self):
|
||||
result = IFTreeSet()
|
||||
#result = IFBTree()
|
||||
for r in self.context.getResources():
|
||||
result.insert(int(util.getUidForObject(r)))
|
||||
#result[int(util.getUidForObject(r))] = 1.0
|
||||
return result
|
||||
|
||||
|
||||
class Children(ConceptMapTerm):
|
||||
|
||||
includeResources = False
|
||||
recursive = False
|
||||
predicates = None
|
||||
|
||||
def apply(self):
|
||||
result = IFTreeSet()
|
||||
self.getRecursive(self.context, result)
|
||||
return result
|
||||
|
||||
def getRecursive(self, c, result):
|
||||
if self.includeResources:
|
||||
for r in c.getResources():
|
||||
uid = int(util.getUidForObject(r))
|
||||
if uid not in result:
|
||||
result.insert(uid)
|
||||
for c in c.getChildren():
|
||||
uid = int(util.getUidForObject(c))
|
||||
if uid not in result:
|
||||
result.insert(uid)
|
||||
if self.recursive:
|
||||
self.getRecursive(c, result)
|
||||
|
||||
|
||||
def getObjects(uids, root=None, checkPermission=canListObject):
|
||||
intIds = component.getUtility(IIntIds)
|
||||
for uid in uids:
|
||||
obj = util.getObjectForUid(uid, intIds)
|
||||
if ((root is None or obj.getLoopsRoot() == root) and
|
||||
(checkPermission is None or checkPermission(obj))):
|
||||
yield obj
|
||||
|
|
|
@ -11,6 +11,8 @@ from cybertools.typology.interfaces import IType
|
|||
from loops import util
|
||||
from loops.concept import Concept
|
||||
from loops.resource import Resource
|
||||
from loops.knowledge.interfaces import IPerson
|
||||
from loops.knowledge.knowledge import Person
|
||||
from loops.knowledge.setup import SetupManager as KnowledgeSetupManager
|
||||
from loops.setup import SetupManager, addObject
|
||||
from loops.tests.setup import TestSite as BaseTestSite
|
||||
|
@ -27,6 +29,8 @@ class TestSite(BaseTestSite):
|
|||
site = self.site
|
||||
loopsRoot = site['loops']
|
||||
|
||||
component.provideAdapter(Person, provides=IPerson)
|
||||
|
||||
component.provideAdapter(KnowledgeSetupManager, name='knowledge')
|
||||
setup = SetupManager(loopsRoot)
|
||||
concepts, resources, views = setup.setup()
|
||||
|
@ -34,6 +38,7 @@ class TestSite(BaseTestSite):
|
|||
tType = concepts.getTypeConcept()
|
||||
tDomain = concepts['domain']
|
||||
tTextDocument = concepts['textdocument']
|
||||
tPerson = concepts['person']
|
||||
|
||||
tCountry = addObject(concepts, Concept, 'country', title=u'Country',
|
||||
type=tType)
|
||||
|
@ -76,18 +81,18 @@ class TestSite(BaseTestSite):
|
|||
dtNote = addObject(concepts, Concept, 'dt_note',
|
||||
title=u'Note', type=tDocumentType)
|
||||
|
||||
d001 = addObject(resources, Resource, 'd001',
|
||||
title=u'Doc 001', type=tTextDocument)
|
||||
jim = addObject(concepts, Concept, 'jim', title=u'Jim', type=tPerson)
|
||||
jim.assignChild(cust1)
|
||||
|
||||
d001 = resources['d001.txt']
|
||||
d001.assignConcept(cust1)
|
||||
d001.assignConcept(stateReleased)
|
||||
d001.assignConcept(dtNote)
|
||||
d002 = addObject(resources, Resource, 'd002',
|
||||
title=u'Doc 002', type=tTextDocument)
|
||||
d002 = resources['d002.txt']
|
||||
d002.assignConcept(cust3)
|
||||
d002.assignConcept(stateNew)
|
||||
d002.assignConcept(dtNote)
|
||||
d003 = addObject(resources, Resource, 'd003',
|
||||
title=u'Doc 003', type=tTextDocument)
|
||||
d003 = resources['d003.txt']
|
||||
d003.assignConcept(cust1)
|
||||
d003.assignConcept(stateNew)
|
||||
d003.assignConcept(dtStudy)
|
||||
|
|
2
external/README.txt
vendored
2
external/README.txt
vendored
|
@ -75,7 +75,7 @@ Writing object information to the external storage
|
|||
type(u'customer', u'Customer', options=u'', viewName=u'')...
|
||||
type(u'query', u'Query', options=u'', typeInterface='loops.query.IQueryConcept',
|
||||
viewName=u'')...
|
||||
concept(u'myquery', u'My Query', u'query', viewName='mystuff.html')...
|
||||
concept(u'myquery', u'My Query', u'query', options=u'', viewName='mystuff.html')...
|
||||
child(u'projects', u'customer', u'standard')...
|
||||
|
||||
|
||||
|
|
18
query.py
18
query.py
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2008 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
|
||||
|
@ -169,12 +169,26 @@ class IQueryConcept(IConceptSchema):
|
|||
default=u'',
|
||||
required=True)
|
||||
|
||||
options = schema.List(
|
||||
title=_(u'Options'),
|
||||
description=_(u'Additional settings.'),
|
||||
value_type=schema.TextLine(),
|
||||
default=[],
|
||||
required=False)
|
||||
|
||||
|
||||
class QueryConcept(AdapterBase):
|
||||
|
||||
implements(IQueryConcept)
|
||||
|
||||
_contextAttributes = list(IQueryConcept) + list(IConcept)
|
||||
_contextAttributes = AdapterBase._contextAttributes + ['viewName']
|
||||
_adapterAttributes = AdapterBase._adapterAttributes + ('options',)
|
||||
|
||||
def getOptions(self):
|
||||
return getattr(self.context, '_options', [])
|
||||
def setOptions(self, value):
|
||||
self.context._options = value
|
||||
options = property(getOptions, setOptions)
|
||||
|
||||
|
||||
TypeInterfaceSourceList.typeInterfaces += (IQueryConcept,)
|
||||
|
|
7
setup.py
7
setup.py
|
@ -203,8 +203,11 @@ def addObject(container, class_, name, **kw):
|
|||
if name in container:
|
||||
return container[name]
|
||||
obj = container[name] = class_()
|
||||
for attr in kw:
|
||||
setattr(obj, attr, kw[attr])
|
||||
for attr, value in kw.items():
|
||||
if attr == 'type':
|
||||
obj.setType(value)
|
||||
else:
|
||||
setattr(obj, attr, value)
|
||||
notify(ObjectCreatedEvent(obj))
|
||||
notify(ObjectModifiedEvent(obj))
|
||||
return obj
|
||||
|
|
5
util.py
5
util.py
|
@ -81,10 +81,11 @@ def toUnicode(text, encoding='UTF-8'):
|
|||
return text
|
||||
|
||||
|
||||
def getObjectForUid(uid):
|
||||
def getObjectForUid(uid, intIds=None):
|
||||
if uid == '*': # wild card
|
||||
return '*'
|
||||
intIds = component.getUtility(IIntIds)
|
||||
if intIds is None:
|
||||
intIds = component.getUtility(IIntIds)
|
||||
return intIds.getObject(int(uid))
|
||||
|
||||
def getUidForObject(obj):
|
||||
|
|
Loading…
Add table
Reference in a new issue