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:
parent
19e4431cfb
commit
8477eb47b9
9 changed files with 90 additions and 30 deletions
23
README.txt
23
README.txt
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
62
concept.py
62
concept.py
|
@ -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:
|
||||
|
|
|
@ -74,6 +74,10 @@
|
|||
permission="zope.ManageContent"
|
||||
interface="zope.app.container.interfaces.IWriteContainer" />
|
||||
|
||||
<require
|
||||
permission="zope.View"
|
||||
attributes="getTypePredicate getDefaultPredicate getTypeConcept" />
|
||||
|
||||
</content>
|
||||
|
||||
<interface
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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():
|
||||
|
|
4
view.py
4
view.py
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue