Provide typology stuff for concepts
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1119 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
73c33a108e
commit
dfd398a401
8 changed files with 310 additions and 9 deletions
11
README.txt
11
README.txt
|
@ -4,6 +4,17 @@ loops - Linked Objects for Organization and Processing Services
|
||||||
|
|
||||||
($Id$)
|
($Id$)
|
||||||
|
|
||||||
|
The loops platform is built up of three parts:
|
||||||
|
|
||||||
|
(1) concepts: simple interconnected objects usually representing
|
||||||
|
meta-information
|
||||||
|
(2) resources: (possibly large) atomic objects like documents and media assets
|
||||||
|
(3) views: objects (usually hierarchically organized nodes) providing
|
||||||
|
access to and presenting concepts or resources
|
||||||
|
|
||||||
|
Note that there is another doctest file called helpers.txt that deals
|
||||||
|
with lower-level aspects like type or state management.
|
||||||
|
|
||||||
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||||
>>> site = placefulSetUp(True)
|
>>> site = placefulSetUp(True)
|
||||||
|
|
||||||
|
|
15
concept.py
15
concept.py
|
@ -88,22 +88,21 @@ class Concept(Contained, Persistent):
|
||||||
|
|
||||||
def getConceptType(self):
|
def getConceptType(self):
|
||||||
typePred = self.getConceptManager().getTypePredicate()
|
typePred = self.getConceptManager().getTypePredicate()
|
||||||
|
if typePred is None:
|
||||||
|
return None
|
||||||
parents = self.getParents([typePred])
|
parents = self.getParents([typePred])
|
||||||
#typeRelation = ConceptRelation(None, self, cm.getTypePredicate())
|
|
||||||
#rels = getRelationSingle(self, typeRelation, forSecond=True)
|
|
||||||
# TODO (?): check for multiple types (->Error)
|
# TODO (?): check for multiple types (->Error)
|
||||||
return parents and parents[0] or None
|
return parents and parents[0] or None
|
||||||
def setConceptType(self, concept):
|
def setConceptType(self, concept):
|
||||||
current = self.getConceptType()
|
current = self.getConceptType()
|
||||||
if current != concept:
|
if current != concept:
|
||||||
typePred = self.getConceptManager().getTypePredicate()
|
typePred = self.getConceptManager().getTypePredicate()
|
||||||
|
if typePred is None:
|
||||||
|
raise ValueError('No type predicate found for '
|
||||||
|
+ zapi.getName(self))
|
||||||
if current is not None:
|
if current is not None:
|
||||||
self.deassignParent(current, [typePred])
|
self.deassignParent(current, [typePred])
|
||||||
self.assignParent(concept, typePred)
|
self.assignParent(concept, typePred)
|
||||||
#cm = self.getLoopsRoot().getConceptManager()
|
|
||||||
#typeRelation = ConceptRelation(removeSecurityProxy(concept), self,
|
|
||||||
# cm.getTypePredicate())
|
|
||||||
#setRelationSingle(typeRelation, forSecond=True)
|
|
||||||
conceptType = property(getConceptType, setConceptType)
|
conceptType = property(getConceptType, setConceptType)
|
||||||
|
|
||||||
def __init__(self, title=u''):
|
def __init__(self, title=u''):
|
||||||
|
@ -196,9 +195,7 @@ class ConceptManager(BTreeContainer):
|
||||||
return zapi.getParent(self)
|
return zapi.getParent(self)
|
||||||
|
|
||||||
def getTypePredicate(self):
|
def getTypePredicate(self):
|
||||||
if self.typePredicate is None:
|
return self.get('hasType')
|
||||||
self.typePredicate = self['hasType']
|
|
||||||
return self.typePredicate
|
|
||||||
|
|
||||||
def getTypeConcept(self):
|
def getTypeConcept(self):
|
||||||
if self.typeConcept is None:
|
if self.typeConcept is None:
|
||||||
|
|
|
@ -234,6 +234,10 @@
|
||||||
|
|
||||||
<adapter factory="loops.concept.IndexAttributes" />
|
<adapter factory="loops.concept.IndexAttributes" />
|
||||||
<adapter factory="loops.resource.IndexAttributes" />
|
<adapter factory="loops.resource.IndexAttributes" />
|
||||||
|
<adapter factory="loops.resource.IndexableResource" />
|
||||||
|
|
||||||
|
<adapter factory="loops.type.ConceptType" />
|
||||||
|
<adapter factory="loops.type.LoopsTypeManager" />
|
||||||
|
|
||||||
<adapter factory="loops.external.NodesLoader" />
|
<adapter factory="loops.external.NodesLoader" />
|
||||||
<adapter factory="loops.external.NodesExporter" />
|
<adapter factory="loops.external.NodesExporter" />
|
||||||
|
|
162
helpers.txt
Executable file
162
helpers.txt
Executable file
|
@ -0,0 +1,162 @@
|
||||||
|
===============================================================
|
||||||
|
loops - Linked Objects for Organization and Processing Services
|
||||||
|
===============================================================
|
||||||
|
|
||||||
|
This file documents and tests a bunch of facilities that support the
|
||||||
|
loops framework, mostly provided by sub-packages of the cybertools
|
||||||
|
package.
|
||||||
|
|
||||||
|
($Id$)
|
||||||
|
|
||||||
|
Let's first do some basic imports
|
||||||
|
|
||||||
|
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||||
|
>>> site = placefulSetUp(True)
|
||||||
|
|
||||||
|
>>> from zope.app import zapi
|
||||||
|
>>> from zope.app.tests import ztapi
|
||||||
|
>>> from zope.interface import Interface
|
||||||
|
>>> from zope.publisher.browser import TestRequest
|
||||||
|
|
||||||
|
and setup a simple loops site with its manager objects,
|
||||||
|
|
||||||
|
>>> from loops import Loops
|
||||||
|
>>> site['loops'] = Loops()
|
||||||
|
>>> loopsRoot = site['loops']
|
||||||
|
|
||||||
|
>>> from loops.concept import ConceptManager, Concept
|
||||||
|
>>> loopsRoot['concepts'] = ConceptManager()
|
||||||
|
>>> concepts = loopsRoot['concepts']
|
||||||
|
|
||||||
|
>>> from loops.resource import ResourceManager, Document, MediaAsset
|
||||||
|
>>> loopsRoot['resources'] = ResourceManager()
|
||||||
|
>>> resources = loopsRoot['resources']
|
||||||
|
|
||||||
|
some concepts,
|
||||||
|
|
||||||
|
>>> cc1 = Concept(u'Zope')
|
||||||
|
>>> concepts['cc1'] = cc1
|
||||||
|
>>> cc1.title
|
||||||
|
u'Zope'
|
||||||
|
|
||||||
|
>>> cc2 = Concept(u'Zope 3')
|
||||||
|
>>> concepts['cc2'] = cc2
|
||||||
|
>>> cc2.title
|
||||||
|
u'Zope 3'
|
||||||
|
|
||||||
|
and resources:
|
||||||
|
|
||||||
|
>>> doc1 = Document(u'Zope Info')
|
||||||
|
>>> resources['doc1'] = doc1
|
||||||
|
>>> doc1.title
|
||||||
|
u'Zope Info'
|
||||||
|
|
||||||
|
>>> img1 = MediaAsset(u'An Image')
|
||||||
|
>>> resources['img1'] = img1
|
||||||
|
|
||||||
|
Finally, we also need a relation registry:
|
||||||
|
|
||||||
|
>>> from cybertools.relation.interfaces import IRelationRegistry
|
||||||
|
>>> from cybertools.relation.registry import DummyRelationRegistry
|
||||||
|
>>> from zope.app.testing import ztapi
|
||||||
|
>>> ztapi.provideUtility(IRelationRegistry, DummyRelationRegistry())
|
||||||
|
|
||||||
|
Type management with typology
|
||||||
|
=============================
|
||||||
|
|
||||||
|
The type of an object may be determined by adapting it to the IType
|
||||||
|
interface. The loops framework provides an adapter (LoopsType) for this
|
||||||
|
purpose:
|
||||||
|
|
||||||
|
>>> from cybertools.typology.interfaces import IType
|
||||||
|
>>> from loops.interfaces import IConcept
|
||||||
|
>>> from loops.type import ConceptType
|
||||||
|
>>> ztapi.provideAdapter(IConcept, IType, ConceptType)
|
||||||
|
>>> cc1_type = IType(cc1)
|
||||||
|
|
||||||
|
As we have not yet associated a type with one of our content objects we get
|
||||||
|
|
||||||
|
>>> cc1_type.typeProvider is None
|
||||||
|
True
|
||||||
|
>>> cc1_type.title
|
||||||
|
u'Unknown Type'
|
||||||
|
>>> cc1_type.token
|
||||||
|
'.unknown'
|
||||||
|
|
||||||
|
So we create two special concepts: one ('hasType') as the predicate signifying
|
||||||
|
a type relation, and the other ('type') as the one and only type concept:
|
||||||
|
|
||||||
|
>>> concepts['hasType'] = Concept(u'has type')
|
||||||
|
>>> concepts['type'] = Concept(u'Type')
|
||||||
|
>>> typeObject = concepts['type']
|
||||||
|
|
||||||
|
Assigning a type to a concept is a core functionality of concepts as
|
||||||
|
concept types are themselves concepts; and we assign the type object to
|
||||||
|
itself (as it in fact is of type 'type'):
|
||||||
|
|
||||||
|
>>> typeObject.conceptType = typeObject
|
||||||
|
|
||||||
|
So let's check the type of the type object:
|
||||||
|
|
||||||
|
>>> type_type = IType(typeObject)
|
||||||
|
>>> type_type.typeProvider is typeObject
|
||||||
|
True
|
||||||
|
>>> type_type.title
|
||||||
|
u'Type'
|
||||||
|
>>> type_type.token
|
||||||
|
'.loops/concepts/type'
|
||||||
|
>>> type_type.tokenForSearch
|
||||||
|
'loops:concept:type'
|
||||||
|
|
||||||
|
Now we register another type ('topic') and assign it to cc1:
|
||||||
|
|
||||||
|
>>> concepts['topic'] = Concept(u'Topic')
|
||||||
|
>>> topic = concepts['topic']
|
||||||
|
>>> topic.conceptType = typeObject
|
||||||
|
>>> cc1.conceptType = topic
|
||||||
|
|
||||||
|
Note: as these kind of usually short-living adapters makes heavy use of
|
||||||
|
lazy properties, one should always get a new adapter:
|
||||||
|
|
||||||
|
>>> topic_type = IType(topic)
|
||||||
|
>>> cc1_type = IType(cc1)
|
||||||
|
>>> topic_type == type_type
|
||||||
|
True
|
||||||
|
>>> cc1_type == topic_type
|
||||||
|
False
|
||||||
|
>>> cc1_type.typeProvider == topic
|
||||||
|
True
|
||||||
|
|
||||||
|
Can we find out somehow which types are available? This is the time to look
|
||||||
|
for a type manager. This could be a utility; but in the loops package it
|
||||||
|
is again an adapter, now for the loops root object. Nevertheless one can
|
||||||
|
get a type manager from all loops objects, always with the same context:
|
||||||
|
|
||||||
|
>>> from cybertools.typology.interfaces import ITypeManager
|
||||||
|
>>> from loops.interfaces import ILoopsObject
|
||||||
|
>>> from loops.type import LoopsTypeManager
|
||||||
|
>>> ztapi.provideAdapter(ILoopsObject, ITypeManager, LoopsTypeManager)
|
||||||
|
>>> typeManager = ITypeManager(loopsRoot)
|
||||||
|
>>> typeManager.context == ITypeManager(cc1).context == loopsRoot
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> types = typeManager.types
|
||||||
|
>>> sorted(t.token for t in types)
|
||||||
|
['.loops/concepts/topic', '.loops/concepts/type']
|
||||||
|
|
||||||
|
>>> typeManager.getType('.loops/concepts/topic') == cc1_type
|
||||||
|
True
|
||||||
|
|
||||||
|
Index attributes adapter
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
>>> from loops.concept import IndexAttributes
|
||||||
|
>>> idx = IndexAttributes(cc2)
|
||||||
|
>>> idx.type()
|
||||||
|
'loops:concept:unknown'
|
||||||
|
|
||||||
|
>>> from loops.resource import IndexAttributes
|
||||||
|
>>> idx = IndexAttributes(doc1)
|
||||||
|
>>> idx.type()
|
||||||
|
'loops:resource:Document'
|
||||||
|
|
|
@ -476,6 +476,20 @@ class IConceptRelation(IRelation):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# type and type manager interfaces - probably obsolete
|
||||||
|
|
||||||
|
# class ILoopsType(IType):
|
||||||
|
# """ Each loops object is of a certain type providing this interface.
|
||||||
|
# Usually implemented as an adapter.
|
||||||
|
# """
|
||||||
|
|
||||||
|
|
||||||
|
# class ILoopsTypeManager(ITypeManager):
|
||||||
|
# """ The loops type manager, probably implemented by an adapter to
|
||||||
|
# the loops root object or the loops root object itself.
|
||||||
|
# """
|
||||||
|
|
||||||
|
|
||||||
# interfaces for catalog indexes
|
# interfaces for catalog indexes
|
||||||
|
|
||||||
class IIndexAttributes(Interface):
|
class IIndexAttributes(Interface):
|
||||||
|
|
19
resource.py
19
resource.py
|
@ -31,6 +31,9 @@ from zope.i18nmessageid import MessageFactory
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from persistent import Persistent
|
from persistent import Persistent
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
|
||||||
|
from textindexng.interfaces import IIndexableContent
|
||||||
|
from textindexng.content import IndexContentCollector
|
||||||
from cybertools.relation.registry import getRelations
|
from cybertools.relation.registry import getRelations
|
||||||
from cybertools.relation.interfaces import IRelatable
|
from cybertools.relation.interfaces import IRelatable
|
||||||
|
|
||||||
|
@ -163,6 +166,22 @@ class IndexAttributes(object):
|
||||||
return ':'.join(('loops:resource', context.__class__.__name__))
|
return ':'.join(('loops:resource', context.__class__.__name__))
|
||||||
|
|
||||||
|
|
||||||
|
class IndexableResource(object):
|
||||||
|
|
||||||
|
implements(IIndexableContent)
|
||||||
|
adapts(IResource)
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
def indexableContent(self, fields):
|
||||||
|
context = self.context
|
||||||
|
icc = IndexContentCollector()
|
||||||
|
icc.addBinary(fields[0], context.data, context.contentType, language='de')
|
||||||
|
return icc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getResourceTypes():
|
def getResourceTypes():
|
||||||
return (('loops.resource.Document', _(u'Document')),
|
return (('loops.resource.Document', _(u'Document')),
|
||||||
('loops.resource.MediaAsset', _(u'Media Asset')),
|
('loops.resource.MediaAsset', _(u'Media Asset')),
|
||||||
|
|
|
@ -44,6 +44,7 @@ def test_suite():
|
||||||
return unittest.TestSuite((
|
return unittest.TestSuite((
|
||||||
unittest.makeSuite(Test),
|
unittest.makeSuite(Test),
|
||||||
DocFileSuite('../README.txt', optionflags=flags),
|
DocFileSuite('../README.txt', optionflags=flags),
|
||||||
|
DocFileSuite('../helpers.txt', optionflags=flags),
|
||||||
))
|
))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
93
type.py
Normal file
93
type.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 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
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Type management stuff.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.app import zapi
|
||||||
|
from zope.component import adapts
|
||||||
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
from cybertools.typology.type import BaseType, TypeManager
|
||||||
|
from loops.interfaces import ILoopsObject, IConcept
|
||||||
|
|
||||||
|
|
||||||
|
class LoopsType(BaseType):
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def title(self):
|
||||||
|
tp = self.typeProvider
|
||||||
|
return tp is None and u'Unknown Type' or tp.title
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def token(self):
|
||||||
|
tp = self.typeProvider
|
||||||
|
return tp is None and '.unknown' or self.root.getLoopsUri(tp)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def tokenForSearch(self):
|
||||||
|
tp = self.typeProvider
|
||||||
|
typeName = tp is None and 'unknown' or str(zapi.getName(tp))
|
||||||
|
return ':'.join(('loops:concept', typeName,))
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def root(self):
|
||||||
|
return self.context.getLoopsRoot()
|
||||||
|
|
||||||
|
|
||||||
|
class LoopsTypeInfo(LoopsType):
|
||||||
|
""" A common class the instances of which are used as a generic type info.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, typeProvider):
|
||||||
|
self.typeProvider = self.context = typeProvider
|
||||||
|
|
||||||
|
|
||||||
|
class ConceptType(LoopsType):
|
||||||
|
""" The type adapter for concept objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
adapts(IConcept)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def typeProvider(self):
|
||||||
|
return self.context.conceptType
|
||||||
|
|
||||||
|
|
||||||
|
class LoopsTypeManager(TypeManager):
|
||||||
|
|
||||||
|
adapts(ILoopsObject)
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context.getLoopsRoot()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def types(self):
|
||||||
|
cm = self.context.getConceptManager()
|
||||||
|
to = cm.getTypeConcept()
|
||||||
|
tp = cm.getTypePredicate()
|
||||||
|
if to is None or tp is None:
|
||||||
|
return ()
|
||||||
|
result = to.getChildren([tp])
|
||||||
|
if to not in result:
|
||||||
|
result.append(to)
|
||||||
|
return tuple(LoopsTypeInfo(c) for c in result)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue