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:
helmutm 2006-03-08 15:24:40 +00:00
parent 73c33a108e
commit dfd398a401
8 changed files with 310 additions and 9 deletions

View file

@ -4,6 +4,17 @@ loops - Linked Objects for Organization and Processing Services
($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
>>> site = placefulSetUp(True)

View file

@ -88,22 +88,21 @@ class Concept(Contained, Persistent):
def getConceptType(self):
typePred = self.getConceptManager().getTypePredicate()
if typePred is None:
return None
parents = self.getParents([typePred])
#typeRelation = ConceptRelation(None, self, cm.getTypePredicate())
#rels = getRelationSingle(self, typeRelation, forSecond=True)
# TODO (?): check for multiple types (->Error)
return parents and parents[0] or None
def setConceptType(self, concept):
current = self.getConceptType()
if current != concept:
typePred = self.getConceptManager().getTypePredicate()
if typePred is None:
raise ValueError('No type predicate found for '
+ zapi.getName(self))
if current is not None:
self.deassignParent(current, [typePred])
self.assignParent(concept, typePred)
#cm = self.getLoopsRoot().getConceptManager()
#typeRelation = ConceptRelation(removeSecurityProxy(concept), self,
# cm.getTypePredicate())
#setRelationSingle(typeRelation, forSecond=True)
conceptType = property(getConceptType, setConceptType)
def __init__(self, title=u''):
@ -196,9 +195,7 @@ class ConceptManager(BTreeContainer):
return zapi.getParent(self)
def getTypePredicate(self):
if self.typePredicate is None:
self.typePredicate = self['hasType']
return self.typePredicate
return self.get('hasType')
def getTypeConcept(self):
if self.typeConcept is None:

View file

@ -234,6 +234,10 @@
<adapter factory="loops.concept.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.NodesExporter" />

162
helpers.txt Executable file
View 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'

View file

@ -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
class IIndexAttributes(Interface):

View file

@ -31,6 +31,9 @@ from zope.i18nmessageid import MessageFactory
from zope.interface import implements
from persistent import Persistent
from cStringIO import StringIO
from textindexng.interfaces import IIndexableContent
from textindexng.content import IndexContentCollector
from cybertools.relation.registry import getRelations
from cybertools.relation.interfaces import IRelatable
@ -163,6 +166,22 @@ class IndexAttributes(object):
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():
return (('loops.resource.Document', _(u'Document')),
('loops.resource.MediaAsset', _(u'Media Asset')),

View file

@ -44,6 +44,7 @@ def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test),
DocFileSuite('../README.txt', optionflags=flags),
DocFileSuite('../helpers.txt', optionflags=flags),
))
if __name__ == '__main__':

93
type.py Normal file
View 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)