Work in progress: new ConceptRelation implementation that uses concepts as predicates

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1091 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-02-21 09:53:41 +00:00
parent 19e4431cfb
commit 8477eb47b9
9 changed files with 90 additions and 30 deletions

View file

@ -41,12 +41,15 @@ top-level loops container and a concept manager:
Now we want to relate the second concept to the first one.
In order to do this we first have to provide a relation registry. For
testing we use a simple dummy implementation.
testing we use a simple dummy implementation. As relationships are
based on predicates that are themselves concepts we also need a
default predicate concept; the default name for this is 'standard'.
>>> from cybertools.relation.interfaces import IRelationRegistry
>>> from cybertools.relation.registry import DummyRelationRegistry
>>> from zope.app.testing import ztapi
>>> ztapi.provideUtility(IRelationRegistry, DummyRelationRegistry())
>>> concepts['standard'] = Concept('Default Predicate')
Now we can assign the concept c2 as a child to c1 (using the standard
ConceptRelation):
@ -74,8 +77,10 @@ We can now ask our concepts for their related child and parent concepts:
Each concept should have a concept type; this is in fact provided by a
relation to a special kind of concept object with the magic name 'type'.
This type object is its own type:
This type object is its own type. The type relations themselves are of
a special predicate 'hasType'.
>>> concepts['hasType'] = Concept(u'Type Predicate')
>>> concepts['type'] = Concept(u'Type')
>>> typeObject = concepts['type']
>>> typeObject.setConceptType(typeObject)
@ -99,6 +104,13 @@ This type object is its own type:
>>> cc1.getConceptType().title
u'Topic'
We get a list of types using the ConceptTypeSourceList:
>>> from loops.concept import ConceptTypeSourceList
>>> types = ConceptTypeSourceList(cc1)
>>> sorted(t.title for t in types)
[u'Topic', u'Type', u'Unknown Type']
Concept Views
-------------
@ -108,9 +120,6 @@ Concept Views
>>> sorted([c.title for c in view.children()])
[u'Zope 3']
The concept view allows to get a list of terms (sort of vocabulary) that
can be used to show the objects in a listing:
The concept view allows updating the underlying context object:
>>> cc3 = Concept(u'loops for Zope 3')
@ -388,9 +397,9 @@ objects.) The source is basically a source list:
>>> from loops.target import TargetSourceList
>>> source = TargetSourceList(m111)
>>> len(source)
8
1
>>> sorted([zapi.getName(s) for s in source])
[u'cc1', u'cc2', u'cc3', u'cc4', u'doc1', u'topic', u'type', u'unknown']
[u'doc1']
The form then uses a sort of browser view providing the ITerms interface
based on this source list:

View file

@ -27,7 +27,6 @@ from zope.app.dublincore.interfaces import ICMFDublinCore
from zope.app.form.browser.interfaces import ITerms
from zope.cachedescriptors.property import Lazy
from zope.interface import implements
#from zope.schema.vocabulary import SimpleTerm
from zope.security.proxy import removeSecurityProxy
class BaseView(object):
@ -85,9 +84,6 @@ class LoopsTerms(object):
def getTerm(self, value):
return BaseView(value, self.request)
#token = self.loopsRoot.getLoopsUri(value)
#title = value.title or zapi.getName(value)
#return SimpleTerm(value, token, title)
def getValue(self, token):
return self.loopsRoot.loopsTraverse(token)

View file

@ -25,8 +25,10 @@ $Id$
from zope.app import zapi
from zope.app.catalog.interfaces import ICatalog
from zope.app.dublincore.interfaces import ICMFDublinCore
from zope.app.event.objectevent import ObjectCreatedEvent
from zope.app.form.browser.interfaces import ITerms
from zope.cachedescriptors.property import Lazy
from zope.event import notify
from zope.interface import implements
from zope.publisher.interfaces import BadRequest
from zope import schema
@ -90,6 +92,8 @@ class ConceptView(BaseView):
concept = Concept(title)
container = self.loopsRoot.getConceptManager()
container[name] = concept
# TODO: notify ObjectCreatedEvent() (?)
#notify(ObjectCreatedEvent(removeSecurityProxy(concept)))
assignAs = self.request.get('assignAs', 'child')
if assignAs == 'child':
self.context.assignChild(removeSecurityProxy(concept))

View file

@ -35,7 +35,7 @@ from persistent import Persistent
from cybertools.relation import DyadicRelation
from cybertools.relation.registry import getRelations
from cybertools.relation.registry import getRelationSingle, setRelationSingle
from cybertools.relation.interfaces import IRelationRegistry
from cybertools.relation.interfaces import IRelationRegistry, IRelatable
from interfaces import IConcept, IConceptRelation, IConceptView
from interfaces import IConceptManager, IConceptManagerContained
@ -50,12 +50,18 @@ class ConceptRelation(DyadicRelation):
"""
implements(IConceptRelation)
def __init__(self, first, second, predicate=None):
super(ConceptRelation, self).__init__(first, second)
if predicate is None:
context = first is not None and first or second
cm = context.getLoopsRoot().getConceptManager()
predicate = cm.getDefaultPredicate()
self.predicate = predicate
class TypeRelation(DyadicRelation):
""" A special relation between two concepts, the parent specifying
the type of the child.
"""
implements(IConceptRelation)
def getPredicateName(self):
baseName = super(ConceptRelation, self).getPredicateName()
id = zapi.getUtility(IRelationRegistry).getUniqueIdForObject(self.predicate)
return '.'.join((baseName, str(id)))
class ResourceRelation(DyadicRelation):
@ -68,7 +74,7 @@ class ResourceRelation(DyadicRelation):
class Concept(Contained, Persistent):
implements(IConcept, IConceptManagerContained)
implements(IConcept, IConceptManagerContained, IRelatable)
proxyInterface = IConceptView
@ -78,11 +84,16 @@ class Concept(Contained, Persistent):
title = property(getTitle, setTitle)
def getConceptType(self):
rel = getRelationSingle(self, TypeRelation)
return rel and rel.second or None
cm = self.getLoopsRoot().getConceptManager()
typeRelation = ConceptRelation(None, self, cm.getTypePredicate())
rel = getRelationSingle(self, typeRelation, forSecond=True)
return rel and rel.first or None
def setConceptType(self, concept):
if self.getConceptType() != concept:
setRelationSingle(TypeRelation(self, removeSecurityProxy(concept)))
cm = self.getLoopsRoot().getConceptManager()
typeRelation = ConceptRelation(removeSecurityProxy(concept), self,
cm.getTypePredicate())
setRelationSingle(typeRelation, forSecond=True)
conceptType = property(getConceptType, setConceptType)
def __init__(self, title=u''):
@ -95,14 +106,14 @@ class Concept(Contained, Persistent):
def getChildren(self, relationships=None):
if relationships is None:
relationships = [ConceptRelation]
relationships = [ConceptRelation(self, None)]
rels = getRelations(first=self, relationships=relationships)
return [r.second for r in rels]
# TODO: sort...
def getParents(self, relationships=None):
if relationships is None:
relationships = [ConceptRelation]
relationships = [ConceptRelation(None, self)]
rels = getRelations(second=self, relationships=relationships)
return [r.first for r in rels]
@ -117,7 +128,7 @@ class Concept(Contained, Persistent):
def deassignChildren(self, concept, relationships=None):
if relationships is None:
relationships = [ConceptRelation]
relationships = [ConceptRelation(self, None)]
registry = zapi.getUtility(IRelationRegistry)
relations = []
for rs in relationships:
@ -159,9 +170,28 @@ class ConceptManager(BTreeContainer):
implements(IConceptManager, ILoopsContained)
typeConcept = None
typePredicate = None
defaultPredicate = None
def getLoopsRoot(self):
return zapi.getParent(self)
def getTypePredicate(self):
if self.typePredicate is None:
self.typePredicate = self['hasType']
return self.typePredicate
def getTypeConcept(self):
if self.typeConcept is None:
self.typeConcept = self['type']
return self.typeConcept
def getDefaultPredicate(self):
if self.defaultPredicate is None:
self.defaultPredicate = self['standard']
return self.defaultPredicate
def getViewManager(self):
return self.getLoopsRoot().getViewManager()
@ -202,10 +232,12 @@ class ConceptTypeSourceList(object):
@Lazy
def conceptTypes(self):
result = []
typeObject = self.concepts.get('type')
cm = self.concepts
typeObject = cm.getTypeConcept()
unknownType = self.concepts.get('unknown')
if typeObject is not None:
types = typeObject.getParents((TypeRelation,))
typeRelation = ConceptRelation(None, typeObject, cm.getTypePredicate())
types = typeObject.getChildren((typeRelation,))
if typeObject not in types:
result.append(typeObject)
if unknownType is not None and unknownType not in types:

View file

@ -74,6 +74,10 @@
permission="zope.ManageContent"
interface="zope.app.container.interfaces.IWriteContainer" />
<require
permission="zope.View"
attributes="getTypePredicate getDefaultPredicate getTypeConcept" />
</content>
<interface

View file

@ -141,6 +141,17 @@ class IConceptManager(ILoopsObject, IContainer):
"""
contains(IConcept)
def getTypePredicate():
""" Return the concept that provides the type predicate.
"""
def getTypeConcept():
""" Return the concept that provides the type object.
"""
def getDefaultPredicate():
""" Return the concept that provides the default (standard) predicate.
"""
class IConceptManagerContained(Interface):
containers(IConceptManager)

View file

@ -30,6 +30,7 @@ from zope.interface import implements
from persistent import Persistent
from cStringIO import StringIO
from cybertools.relation.registry import getRelations
from cybertools.relation.interfaces import IRelatable
from interfaces import IResource
from interfaces import IDocument, IDocumentSchema, IDocumentView
@ -40,7 +41,7 @@ from interfaces import ILoopsContained
class Resource(Contained, Persistent):
implements(IResource, IResourceManagerContained)
implements(IResource, IResourceManagerContained, IRelatable)
_title = u''
def getTitle(self): return self._title

View file

@ -112,7 +112,10 @@ class TargetSourceList(object):
self.context = removeSecurityProxy(context)
root = self.context.getLoopsRoot()
self.resources = root.getResourceManager()
self.concepts = root.getConceptManager()
# concepts will only be included when we have some really
# queryable source with a corresponding user interface:
#self.concepts = root.getConceptManager()
self.concepts = {}
def __iter__(self):
for obj in self.resources.values():

View file

@ -36,7 +36,7 @@ from zope.security.proxy import removeSecurityProxy
from persistent import Persistent
from cybertools.relation import DyadicRelation
from cybertools.relation.registry import getRelations
from cybertools.relation.interfaces import IRelationRegistry
from cybertools.relation.interfaces import IRelationRegistry, IRelatable
from interfaces import IView, INode, INodeConfigSchema
from interfaces import IViewManager, INodeContained
@ -46,7 +46,7 @@ from interfaces import ITargetRelation
class View(object):
implements(IView, INodeContained)
implements(IView, INodeContained, IRelatable)
_title = u''
def getTitle(self): return self._title