provide system qualifier for type type and predicate, check it when searching; + some minor refactorings

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1605 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2007-03-02 10:48:06 +00:00
parent a2932828fb
commit 6e3e08c781
15 changed files with 146 additions and 88 deletions

View file

@ -140,6 +140,13 @@ class BaseView(GenericView):
def value(self): def value(self):
return self.context return self.context
@Lazy
def uniqueId(self):
return util.getUidForObject(self.context)
#return zapi.getUtility(IRelationRegistry).getUniqueIdForObject(self.context)
# type stuff
@Lazy @Lazy
def type(self): def type(self):
return IType(self.context) return IType(self.context)
@ -174,35 +181,47 @@ class BaseView(GenericView):
for o in objs: for o in objs:
yield BaseView(o, request) yield BaseView(o, request)
# type listings
def listTypes(self, include=None, exclude=None, sortOn='title'):
types = [dict(token=t.token, title=t.title)
for t in ITypeManager(self.context).listTypes(include, exclude)]
if sortOn:
types.sort(key=lambda x: x[sortOn])
return types
def resourceTypes(self): def resourceTypes(self):
return util.KeywordVocabulary([(t.token, t.title) return util.KeywordVocabulary(self.listTypes(('resource',) ('hidden',)))
for t in ITypeManager(self.context).listTypes(('resource',)) #if t.factory == Resource]) # ? if necessary -> type.qualifiers
if t.factory == Resource])
def conceptTypes(self): def conceptTypes(self):
return util.KeywordVocabulary([(t.token, t.title) return util.KeywordVocabulary(self.listTypes(('concept',), ('hidden',)))
for t in ITypeManager(self.context).listTypes(('concept',))])
def listTypesForSearch(self, include=None, exclude=None, sortOn='title'):
types = [dict(token=t.tokenForSearch, title=t.title)
for t in ITypeManager(self.context).listTypes(include, exclude)]
if sortOn:
types.sort(key=lambda x: x[sortOn])
return types
def typesForSearch(self): def typesForSearch(self):
general = [('loops:resource:*', 'Any Resource'), general = [('loops:resource:*', 'Any Resource'),
('loops:concept:*', 'Any Concept'),] ('loops:concept:*', 'Any Concept'),]
return util.KeywordVocabulary(general + sorted([(t.tokenForSearch, t.title) return util.KeywordVocabulary(general
for t in ITypeManager(self.context).types]) + self.listTypesForSearch(exclude=('system', 'hidden',))
+ [('loops:*', 'Any')]) + [('loops:*', 'Any')])
def conceptTypesForSearch(self): def conceptTypesForSearch(self):
general = [('loops:concept:*', 'Any Concept'),] general = [('loops:concept:*', 'Any Concept'),]
return util.KeywordVocabulary(general + [(t.tokenForSearch, t.title) return util.KeywordVocabulary(general
for t in ITypeManager(self.context).listTypes(('concept',))]) + self.listTypesForSearch(('concept',), ('system', 'hidden',),))
def resourceTypesForSearch(self): def resourceTypesForSearch(self):
general = [('loops:resource:*', 'Any Resource'),] general = [('loops:resource:*', 'Any Resource'),]
return util.KeywordVocabulary(general + [(t.tokenForSearch, t.title) return util.KeywordVocabulary(general
for t in ITypeManager(self.context).listTypes(('resource',))]) + self.listTypesForSearch(('resource',), ('system', 'hidden'),))
@Lazy # controllling editing
def uniqueId(self):
return zapi.getUtility(IRelationRegistry).getUniqueIdForObject(self.context)
@Lazy @Lazy
def editable(self): def editable(self):

View file

@ -42,29 +42,31 @@ class AdapterBase(object):
adapts(IConcept) adapts(IConcept)
_attributes = ('context', '__parent__', ) _adapterAttributes = ('context', '__parent__', )
_schemas = list(IConcept) _contextAttributes = list(IConcept)
def __init__(self, context): def __init__(self, context):
self.context = context # to get the permission stuff right self.context = context
self.__parent__ = context self.__parent__ = context # to get the permission stuff right
def __getattr__(self, attr): def __getattr__(self, attr):
self.checkAttr(attr) self.checkAttr(attr)
return getattr(self.context, '_' + attr, None) return getattr(self.context, '_' + attr, None)
def __setattr__(self, attr, value): def __setattr__(self, attr, value):
if attr in self._attributes: if attr in self._adapterAttributes:
object.__setattr__(self, attr, value) object.__setattr__(self, attr, value)
else: else:
self.checkAttr(attr) self.checkAttr(attr)
setattr(self.context, '_' + attr, value) setattr(self.context, '_' + attr, value)
def checkAttr(self, attr): def checkAttr(self, attr):
if attr not in self._schemas: if attr not in self._contextAttributes:
raise AttributeError(attr) raise AttributeError(attr)
def __eq__(self, other): def __eq__(self, other):
if other is None:
return False
return self.context == other.context return self.context == other.context
@ -72,7 +74,7 @@ class ResourceAdapterBase(AdapterBase):
adapts(IResource) adapts(IResource)
_schemas = list(IResourceAdapter) _contextAttributes = list(IResourceAdapter)
# other adapters # other adapters

View file

@ -149,10 +149,6 @@ class Concept(Contained, Persistent):
def deassignChild(self, child, predicates=None): def deassignChild(self, child, predicates=None):
registry = zapi.getUtility(IRelationRegistry) registry = zapi.getUtility(IRelationRegistry)
#relations = []
#for rs in relationships:
# relations.extend(registry.query(first=self, second=concept,
# relationship=rs))
for rel in self.getChildRelations(predicates, child): for rel in self.getChildRelations(predicates, child):
registry.unregister(rel) registry.unregister(rel)
@ -192,6 +188,7 @@ class ConceptManager(BTreeContainer):
typeConcept = None typeConcept = None
typePredicate = None typePredicate = None
defaultPredicate = None defaultPredicate = None
predicateType = None
def getLoopsRoot(self): def getLoopsRoot(self):
return zapi.getParent(self) return zapi.getParent(self)
@ -209,19 +206,27 @@ class ConceptManager(BTreeContainer):
self.defaultPredicate = self['standard'] self.defaultPredicate = self['standard']
return self.defaultPredicate return self.defaultPredicate
def getPredicateType(self):
if self.predicateType is None:
dp = self.getDefaultPredicate()
self.predicateType = dp.conceptType
return self.predicateType
def getViewManager(self): def getViewManager(self):
return self.getLoopsRoot().getViewManager() return self.getLoopsRoot().getViewManager()
# adapters and similar components # adapters and similar components
class ConceptSourceList(object): class xxxConceptSourceList(object):
# seems to be obsolete
implements(schema.interfaces.IIterableSource) implements(schema.interfaces.IIterableSource)
def __init__(self, context): def __init__(self, context):
#self.context = context self.context = context
self.context = removeSecurityProxy(context) #self.context = removeSecurityProxy(context)
root = self.context.getLoopsRoot() root = self.context.getLoopsRoot()
self.concepts = root.getConceptManager() self.concepts = root.getConceptManager()

View file

@ -78,7 +78,8 @@
<require <require
permission="zope.View" permission="zope.View"
attributes="getTypePredicate getDefaultPredicate getTypeConcept" /> attributes="getTypePredicate getDefaultPredicate getTypeConcept
getPredicateType" />
</class> </class>

View file

@ -130,7 +130,7 @@ So let's check the type of the type object:
>>> type_type.tokenForSearch >>> type_type.tokenForSearch
'loops:concept:type' 'loops:concept:type'
>>> type_type.qualifiers >>> type_type.qualifiers
('concept',) ('concept', 'system')
Now we register another type ('topic') and assign it to cc1: Now we register another type ('topic') and assign it to cc1:
@ -151,7 +151,7 @@ lazy properties, one should always get a new adapter:
>>> cc1_type.typeProvider == topic >>> cc1_type.typeProvider == topic
True True
>>> topic_type.qualifiers >>> topic_type.qualifiers
('concept',) ('concept', 'system')
>>> topic_type.defaultContainer >>> topic_type.defaultContainer
<loops.concept.ConceptManager object ...> <loops.concept.ConceptManager object ...>
>>> topic_type.factory >>> topic_type.factory

View file

@ -155,23 +155,30 @@ class IConceptView(Interface):
""" Used for accessing a concept via a node's target attribute""" """ Used for accessing a concept via a node's target attribute"""
class IConceptManager(ILoopsObject, IContainer): #class IConceptManager(ILoopsObject, IContainer):
class IConceptManager(ILoopsObject):
""" A manager/container for concepts. """ A manager/container for concepts.
""" """
contains(IConcept) contains(IConcept)
def getTypeConcept():
""" Return the concept that provides the type object,
i.e. the type of all types.
"""
def getTypePredicate(): def getTypePredicate():
""" Return the concept that provides the type predicate. """ Return the concept that provides the type predicate.
""" """
def getTypeConcept():
""" Return the concept that provides the type object.
"""
def getDefaultPredicate(): def getDefaultPredicate():
""" Return the concept that provides the default (standard) predicate. """ Return the concept that provides the default (standard) predicate.
""" """
def getPredicateType():
""" Return the concept that provides the predicate type object,
i.e. the type of all predicates.
"""
class IConceptManagerContained(Interface): class IConceptManagerContained(Interface):
containers(IConceptManager) containers(IConceptManager)

View file

@ -100,7 +100,7 @@ class Topic(AdapterBase, KnowledgeAdapterMixin):
""" """
implements(IKnowledgeElement) implements(IKnowledgeElement)
_attributes = ('context', '__parent__', 'parent') _adapterAttributes = ('context', '__parent__', 'parent')
def getParent(self): def getParent(self):
parents = self.context.getParents((self.standardPred,)) parents = self.context.getParents((self.standardPred,))

View file

@ -72,8 +72,8 @@ class Person(AdapterBase, BasePerson):
implements(IPerson) implements(IPerson)
_attributes = ('context', '__parent__', 'userId', 'phoneNumbers') _adapterAttributes = ('context', '__parent__', 'userId', 'phoneNumbers')
_schemas = list(IPerson) + list(IConcept) _contextAttributes = list(IPerson) + list(IConcept)
def getUserId(self): def getUserId(self):
return getattr(self.context, '_userId', None) return getattr(self.context, '_userId', None)
@ -162,8 +162,8 @@ class Address(AdapterBase):
implements(IAddress) implements(IAddress)
_attributes = ('context', '__parent__', 'lines') _adapterAttributes = ('context', '__parent__', 'lines')
_schemas = list(IAddress) + list(IConcept) _contextAttributes = list(IAddress) + list(IConcept)
def getLines(self): def getLines(self):
return getattr(self.context, '_lines', []) return getattr(self.context, '_lines', [])

View file

@ -39,6 +39,6 @@ class Task(AdapterBase):
implements(ITask) implements(ITask)
_attributes = ('context', '__parent__',) _adapterAttributes = ('context', '__parent__',)
_schemas = list(ITask) + list(IConcept) _contextAttributes = list(ITask) + list(IConcept)

View file

@ -28,6 +28,7 @@ from zope import traversing
from zope.app.catalog.interfaces import ICatalog from zope.app.catalog.interfaces import ICatalog
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from cybertools.typology.interfaces import IType
from loops.interfaces import IConcept from loops.interfaces import IConcept
from loops.common import AdapterBase from loops.common import AdapterBase
from loops.type import TypeInterfaceSourceList from loops.type import TypeInterfaceSourceList
@ -59,7 +60,7 @@ class BaseQuery(object):
def loopsRoot(self): def loopsRoot(self):
return self.context.context.getLoopsRoot() return self.context.context.getLoopsRoot()
def queryConcepts(self, title=None, type=None): def queryConcepts(self, title=None, type=None, **kw):
if type.endswith('*'): if type.endswith('*'):
start = type[:-1] start = type[:-1]
end = start + '\x7f' end = start + '\x7f'
@ -71,16 +72,23 @@ class BaseQuery(object):
else: else:
result = cat.searchResults(loops_type=(start, end)) result = cat.searchResults(loops_type=(start, end))
result = set(r for r in result if r.getLoopsRoot() == self.loopsRoot) result = set(r for r in result if r.getLoopsRoot() == self.loopsRoot)
if 'exclude' in kw:
r1 = set()
for r in result:
qur = IType(r).qualifiers
if not [qux for qux in kw['exclude'] if qux in qur]:
r1.add(r)
result = r1
return result return result
def queryConceptsWithChildren(self, title=None, type=None, uid=None): def queryConceptsWithChildren(self, title=None, type=None, uid=None, **kw):
if title: # there are a few characters that the index doesn't like if title: # there are a few characters that the index doesn't like
title = title.replace('(', ' ').replace(')', ' ') title = title.replace('(', ' ').replace(')', ' ')
if not title and not uid and (type is None or '*' in type): if not title and not uid and (type is None or '*' in type):
return None return None
result = set() result = set()
if not uid: if not uid:
queue = list(self.queryConcepts(title=title, type=type)) queue = list(self.queryConcepts(title=title, type=type, **kw))
else: else:
queue = [util.getObjectForUid(uid)] queue = [util.getObjectForUid(uid)]
concepts = [] concepts = []
@ -100,7 +108,7 @@ class BaseQuery(object):
class FullQuery(BaseQuery): class FullQuery(BaseQuery):
def query(self, text=None, type=None, useTitle=True, useFull=False, def query(self, text=None, type=None, useTitle=True, useFull=False,
conceptTitle=None, conceptUid=None, conceptType=None): conceptTitle=None, conceptUid=None, conceptType=None, **kw):
result = set() result = set()
rc = self.queryConceptsWithChildren(title=conceptTitle, uid=conceptUid, rc = self.queryConceptsWithChildren(title=conceptTitle, uid=conceptUid,
type=conceptType) type=conceptType)
@ -136,10 +144,10 @@ class ConceptQuery(BaseQuery):
""" Find concepts of type `type` whose title starts with `title`. """ Find concepts of type `type` whose title starts with `title`.
""" """
def query(self, title=None, type=None): def query(self, title=None, type=None, **kw):
if title and not title.endswith('*'): if title and not title.endswith('*'):
title += '*' title += '*'
return self.queryConcepts(title=title, type=type) return self.queryConcepts(title=title, type=type, **kw)
# QueryConcept: concept objects that allow querying the database. # QueryConcept: concept objects that allow querying the database.
@ -161,7 +169,7 @@ class QueryConcept(AdapterBase):
implements(IQueryConcept) implements(IQueryConcept)
_schemas = list(IQueryConcept) + list(IConcept) _contextAttributes = list(IQueryConcept) + list(IConcept)
TypeInterfaceSourceList.typeInterfaces += (IQueryConcept,) TypeInterfaceSourceList.typeInterfaces += (IQueryConcept,)

View file

@ -200,10 +200,9 @@ class FileAdapter(ResourceAdapterBase):
""" """
implements(IFile) implements(IFile)
_schemas = list(IFile) + list(IBaseResource)
# let the adapter handle the data attribute: _contextAttributes = list(IFile) + list(IBaseResource)
_attributes = ResourceAdapterBase._attributes + ('data',) _adapterAttributes = ResourceAdapterBase._adapterAttributes + ('data',)
def setData(self, data): self.context.data = data def setData(self, data): self.context.data = data
def getData(self): return self.context.data def getData(self): return self.context.data
@ -251,7 +250,7 @@ class DocumentAdapter(ResourceAdapterBase):
""" """
# let the adapter handle the data attribute: # let the adapter handle the data attribute:
_attributes = ResourceAdapterBase._attributes + ('data',) _adapterAttributes = ResourceAdapterBase._adapterAttributes + ('data',)
def setData(self, data): self.context._data = data.replace('\r', '') def setData(self, data): self.context._data = data.replace('\r', '')
def getData(self): return self.context._data def getData(self): return self.context._data
@ -263,7 +262,7 @@ class TextDocumentAdapter(DocumentAdapter):
""" """
implements(IDocument) implements(IDocument)
_schemas = list(IDocument) + list(IBaseResource) _contextAttributes = list(IDocument) + list(IBaseResource)
class NoteAdapter(DocumentAdapter): class NoteAdapter(DocumentAdapter):
@ -271,7 +270,7 @@ class NoteAdapter(DocumentAdapter):
""" """
implements(INote) implements(INote)
_schemas = list(INote) + list(IBaseResource) _contextAttributes = list(INote) + list(IBaseResource)
# other adapters # other adapters

View file

@ -78,13 +78,13 @@ zcml in real life:
>>> t = searchView.typesForSearch() >>> t = searchView.typesForSearch()
>>> len(t) >>> len(t)
11 9
>>> t.getTermByToken('loops:resource:*').title >>> t.getTermByToken('loops:resource:*').title
'Any Resource' 'Any Resource'
>>> t = searchView.conceptTypesForSearch() >>> t = searchView.conceptTypesForSearch()
>>> len(t) >>> len(t)
5 3
>>> t.getTermByToken('loops:concept:*').title >>> t.getTermByToken('loops:concept:*').title
'Any Concept' 'Any Concept'

View file

@ -77,11 +77,11 @@ class Search(BaseView):
request.response.setHeader('Content-Type', 'text/plain; charset=UTF-8') request.response.setHeader('Content-Type', 'text/plain; charset=UTF-8')
title = request.get('searchString', '').replace('(', ' ').replace(')', ' ') title = request.get('searchString', '').replace('(', ' ').replace(')', ' ')
type = request.get('searchType') or 'loops:concept:*' type = request.get('searchType') or 'loops:concept:*'
result = ConceptQuery(self).query(title=title, type=type) result = ConceptQuery(self).query(title=title, type=type, exclude=('system',))
registry = component.getUtility(IRelationRegistry) #registry = component.getUtility(IRelationRegistry)
# simple way to provide JSON format: # simple way to provide JSON format:
return str(sorted([[`o.title`[2:-1] + ' (%s)' % `o.conceptType.title`[2:-1], return str(sorted([[`o.title`[2:-1] + ' (%s)' % `o.conceptType.title`[2:-1],
`registry.getUniqueIdForObject(o)`] `int(util.getUidForObject(o))`]
for o in result for o in result
if o.getLoopsRoot() == self.loopsRoot])).replace('\\\\x', '\\x') if o.getLoopsRoot() == self.loopsRoot])).replace('\\\\x', '\\x')
#return str(sorted([[`o.title`[2:-1], `traversing.api.getName(o)`[2:-1]] #return str(sorted([[`o.title`[2:-1], `traversing.api.getName(o)`[2:-1]]

57
type.py
View file

@ -22,13 +22,14 @@ Type management stuff.
$Id$ $Id$
""" """
from zope.app import zapi from zope import component, schema
from zope.component import adapts from zope.component import adapts
from zope.interface import implements from zope.interface import implements
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope.dottedname.resolve import resolve from zope.dottedname.resolve import resolve
from zope import schema
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
from zope.traversing.api import getName
from cybertools.typology.type import BaseType, TypeManager from cybertools.typology.type import BaseType, TypeManager
from cybertools.typology.interfaces import ITypeManager from cybertools.typology.interfaces import ITypeManager
from loops.interfaces import ILoopsObject, IConcept, IResource from loops.interfaces import ILoopsObject, IConcept, IResource
@ -44,7 +45,8 @@ class LoopsType(BaseType):
adapts(ILoopsObject) adapts(ILoopsObject)
factoryMapping = dict(concept=Concept, resource=Resource, document=Document) factoryMapping = dict(concept=Concept, resource=Resource)
#document=Document)
containerMapping = dict(concept='concepts', resource='resources') containerMapping = dict(concept='concepts', resource='resources')
@Lazy @Lazy
@ -60,27 +62,31 @@ class LoopsType(BaseType):
@Lazy @Lazy
def tokenForSearch(self): def tokenForSearch(self):
tp = self.typeProvider tp = self.typeProvider
typeName = tp is None and 'unknown' or str(zapi.getName(tp)) typeName = tp is None and 'unknown' or str(getName(tp))
return ':'.join(('loops', self.qualifiers[0], typeName,)) return ':'.join(('loops', self.qualifiers[0], typeName,))
@Lazy @Lazy
def typeInterface(self): def typeInterface(self):
adapter = zapi.queryAdapter(self.typeProvider, ITypeConcept) adapter = component.queryAdapter(self.typeProvider, ITypeConcept)
if adapter is not None: if adapter is not None:
return removeSecurityProxy(adapter.typeInterface) return removeSecurityProxy(adapter.typeInterface)
else: else:
conceptType = self.typeProvider if self.typeProvider is self.typeConcept:
typeConcept = self.root.getConceptManager().getTypeConcept() return ITypeConcept # typeConcept always is a type concept
if conceptType is typeConcept:
return ITypeConcept
return None return None
@Lazy @Lazy
def qualifiers(self): def qualifiers(self):
ti = self.typeInterface ti = self.typeInterface
if ti is None or not issubclass(ti, IResourceAdapter): if ti is None or not issubclass(ti, IResourceAdapter):
return ('concept',) qu = ['concept',]
return ('resource',) else:
qu = ['resource',]
# check typeProvider for additional qualifiers:
if self.typeProvider in (self.typeConcept, self.predicateType,):
qu.append('system')
# how to set a type to 'hidden'?
return tuple(qu)
@Lazy @Lazy
def factory(self): def factory(self):
@ -91,10 +97,6 @@ class LoopsType(BaseType):
def defaultContainer(self): def defaultContainer(self):
return self.root[self.containerMapping.get(self.qualifiers[0], 'concept')] return self.root[self.containerMapping.get(self.qualifiers[0], 'concept')]
@Lazy
def root(self):
return self.context.getLoopsRoot()
@Lazy @Lazy
def typeProvider(self): def typeProvider(self):
# TODO: unify this type attribute naming... # TODO: unify this type attribute naming...
@ -115,6 +117,20 @@ class LoopsType(BaseType):
result['default'].append(opt) result['default'].append(opt)
return result return result
# general infos
@Lazy
def root(self):
return self.context.getLoopsRoot()
@Lazy
def typeConcept(self):
return self.root.getConceptManager().getTypeConcept()
@Lazy
def predicateType(self):
return self.root.getConceptManager().getPredicateType()
class LoopsTypeInfo(LoopsType): class LoopsTypeInfo(LoopsType):
""" The type info class used by the type manager for listing types. """ The type info class used by the type manager for listing types.
@ -144,13 +160,10 @@ class ConceptTypeInfo(LoopsTypeInfo):
class ResourceType(LoopsType): class ResourceType(LoopsType):
""" The 'old-style' resource type - different classes (Document, MediaAsset) """ The 'old-style' resource type - different classes (Document, MediaAsset)
per type. Will be replaced by new style types that are governed by per type. Are replaced by new style types that are governed by
type concepts as is already the case for concepts. type concepts as is already the case for concepts.
""" """
#adapts(IResource)
#typeTitles = {'MediaAsset': u'Media Asset'}
typeTitles = {} typeTitles = {}
typeInterface = None typeInterface = None
@ -165,8 +178,6 @@ class ResourceType(LoopsType):
@Lazy @Lazy
def token(self): def token(self):
return '.'.join((self.factory.__module__, self.className)) return '.'.join((self.factory.__module__, self.className))
#cn = self.className
#return '/'.join(('.loops/resources', cn.lower(),))
@Lazy @Lazy
def tokenForSearch(self): def tokenForSearch(self):
@ -240,7 +251,7 @@ class TypeConcept(AdapterBase):
implements(ITypeConcept) implements(ITypeConcept)
_schemas = list(ITypeConcept) + list(IConcept) _contextAttributes = list(ITypeConcept) + list(IConcept)
def getTypeInterface(self): def getTypeInterface(self):
ti = getattr(self.context, '_typeInterface', None) ti = getattr(self.context, '_typeInterface', None)
@ -255,10 +266,8 @@ class TypeConcept(AdapterBase):
def getOptions(self): def getOptions(self):
return getattr(self.context, '_options', []) return getattr(self.context, '_options', [])
#return super(TypeConcept, self).options or []
def setOptions(self, value): def setOptions(self, value):
self.context._options = value self.context._options = value
#super(TypeConcept, self).options = value
options = property(getOptions, setOptions) options = property(getOptions, setOptions)

12
util.py
View file

@ -36,8 +36,16 @@ _ = MessageFactory('zope') # it's easier not use a special i18n domain...
class KeywordVocabulary(vocabulary.SimpleVocabulary): class KeywordVocabulary(vocabulary.SimpleVocabulary):
def __init__(self, items, *interfaces): def __init__(self, items, *interfaces):
terms = [vocabulary.SimpleTerm(token, token, title) """ ``items`` may be a tuple of (token, title) or a dictionary
for token, title in items] with corresponding elements named 'token' and 'title'.
"""
terms = []
for t in items:
if type(t) is dict:
token, title = t['token'], t['title']
else:
token, title = t
terms.append(vocabulary.SimpleTerm(token, token, title))
super(KeywordVocabulary, self).__init__(terms, *interfaces) super(KeywordVocabulary, self).__init__(terms, *interfaces)