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.
|
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
|
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.interfaces import IRelationRegistry
|
||||||
>>> from cybertools.relation.registry import DummyRelationRegistry
|
>>> from cybertools.relation.registry import DummyRelationRegistry
|
||||||
>>> from zope.app.testing import ztapi
|
>>> from zope.app.testing import ztapi
|
||||||
>>> ztapi.provideUtility(IRelationRegistry, DummyRelationRegistry())
|
>>> ztapi.provideUtility(IRelationRegistry, DummyRelationRegistry())
|
||||||
|
>>> concepts['standard'] = Concept('Default Predicate')
|
||||||
|
|
||||||
Now we can assign the concept c2 as a child to c1 (using the standard
|
Now we can assign the concept c2 as a child to c1 (using the standard
|
||||||
ConceptRelation):
|
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
|
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'.
|
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')
|
>>> concepts['type'] = Concept(u'Type')
|
||||||
>>> typeObject = concepts['type']
|
>>> typeObject = concepts['type']
|
||||||
>>> typeObject.setConceptType(typeObject)
|
>>> typeObject.setConceptType(typeObject)
|
||||||
|
@ -99,6 +104,13 @@ This type object is its own type:
|
||||||
>>> cc1.getConceptType().title
|
>>> cc1.getConceptType().title
|
||||||
u'Topic'
|
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
|
Concept Views
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
@ -108,9 +120,6 @@ Concept Views
|
||||||
>>> sorted([c.title for c in view.children()])
|
>>> sorted([c.title for c in view.children()])
|
||||||
[u'Zope 3']
|
[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:
|
The concept view allows updating the underlying context object:
|
||||||
|
|
||||||
>>> cc3 = Concept(u'loops for Zope 3')
|
>>> cc3 = Concept(u'loops for Zope 3')
|
||||||
|
@ -388,9 +397,9 @@ objects.) The source is basically a source list:
|
||||||
>>> from loops.target import TargetSourceList
|
>>> from loops.target import TargetSourceList
|
||||||
>>> source = TargetSourceList(m111)
|
>>> source = TargetSourceList(m111)
|
||||||
>>> len(source)
|
>>> len(source)
|
||||||
8
|
1
|
||||||
>>> sorted([zapi.getName(s) for s in source])
|
>>> 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
|
The form then uses a sort of browser view providing the ITerms interface
|
||||||
based on this source list:
|
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.app.form.browser.interfaces import ITerms
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
#from zope.schema.vocabulary import SimpleTerm
|
|
||||||
from zope.security.proxy import removeSecurityProxy
|
from zope.security.proxy import removeSecurityProxy
|
||||||
|
|
||||||
class BaseView(object):
|
class BaseView(object):
|
||||||
|
@ -85,9 +84,6 @@ class LoopsTerms(object):
|
||||||
|
|
||||||
def getTerm(self, value):
|
def getTerm(self, value):
|
||||||
return BaseView(value, self.request)
|
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):
|
def getValue(self, token):
|
||||||
return self.loopsRoot.loopsTraverse(token)
|
return self.loopsRoot.loopsTraverse(token)
|
||||||
|
|
|
@ -25,8 +25,10 @@ $Id$
|
||||||
from zope.app import zapi
|
from zope.app import zapi
|
||||||
from zope.app.catalog.interfaces import ICatalog
|
from zope.app.catalog.interfaces import ICatalog
|
||||||
from zope.app.dublincore.interfaces import ICMFDublinCore
|
from zope.app.dublincore.interfaces import ICMFDublinCore
|
||||||
|
from zope.app.event.objectevent import ObjectCreatedEvent
|
||||||
from zope.app.form.browser.interfaces import ITerms
|
from zope.app.form.browser.interfaces import ITerms
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
from zope.event import notify
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from zope.publisher.interfaces import BadRequest
|
from zope.publisher.interfaces import BadRequest
|
||||||
from zope import schema
|
from zope import schema
|
||||||
|
@ -90,6 +92,8 @@ class ConceptView(BaseView):
|
||||||
concept = Concept(title)
|
concept = Concept(title)
|
||||||
container = self.loopsRoot.getConceptManager()
|
container = self.loopsRoot.getConceptManager()
|
||||||
container[name] = concept
|
container[name] = concept
|
||||||
|
# TODO: notify ObjectCreatedEvent() (?)
|
||||||
|
#notify(ObjectCreatedEvent(removeSecurityProxy(concept)))
|
||||||
assignAs = self.request.get('assignAs', 'child')
|
assignAs = self.request.get('assignAs', 'child')
|
||||||
if assignAs == 'child':
|
if assignAs == 'child':
|
||||||
self.context.assignChild(removeSecurityProxy(concept))
|
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 import DyadicRelation
|
||||||
from cybertools.relation.registry import getRelations
|
from cybertools.relation.registry import getRelations
|
||||||
from cybertools.relation.registry import getRelationSingle, setRelationSingle
|
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 IConcept, IConceptRelation, IConceptView
|
||||||
from interfaces import IConceptManager, IConceptManagerContained
|
from interfaces import IConceptManager, IConceptManagerContained
|
||||||
|
@ -50,12 +50,18 @@ class ConceptRelation(DyadicRelation):
|
||||||
"""
|
"""
|
||||||
implements(IConceptRelation)
|
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):
|
def getPredicateName(self):
|
||||||
""" A special relation between two concepts, the parent specifying
|
baseName = super(ConceptRelation, self).getPredicateName()
|
||||||
the type of the child.
|
id = zapi.getUtility(IRelationRegistry).getUniqueIdForObject(self.predicate)
|
||||||
"""
|
return '.'.join((baseName, str(id)))
|
||||||
implements(IConceptRelation)
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceRelation(DyadicRelation):
|
class ResourceRelation(DyadicRelation):
|
||||||
|
@ -68,7 +74,7 @@ class ResourceRelation(DyadicRelation):
|
||||||
|
|
||||||
class Concept(Contained, Persistent):
|
class Concept(Contained, Persistent):
|
||||||
|
|
||||||
implements(IConcept, IConceptManagerContained)
|
implements(IConcept, IConceptManagerContained, IRelatable)
|
||||||
|
|
||||||
proxyInterface = IConceptView
|
proxyInterface = IConceptView
|
||||||
|
|
||||||
|
@ -78,11 +84,16 @@ class Concept(Contained, Persistent):
|
||||||
title = property(getTitle, setTitle)
|
title = property(getTitle, setTitle)
|
||||||
|
|
||||||
def getConceptType(self):
|
def getConceptType(self):
|
||||||
rel = getRelationSingle(self, TypeRelation)
|
cm = self.getLoopsRoot().getConceptManager()
|
||||||
return rel and rel.second or None
|
typeRelation = ConceptRelation(None, self, cm.getTypePredicate())
|
||||||
|
rel = getRelationSingle(self, typeRelation, forSecond=True)
|
||||||
|
return rel and rel.first or None
|
||||||
def setConceptType(self, concept):
|
def setConceptType(self, concept):
|
||||||
if self.getConceptType() != 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)
|
conceptType = property(getConceptType, setConceptType)
|
||||||
|
|
||||||
def __init__(self, title=u''):
|
def __init__(self, title=u''):
|
||||||
|
@ -95,14 +106,14 @@ class Concept(Contained, Persistent):
|
||||||
|
|
||||||
def getChildren(self, relationships=None):
|
def getChildren(self, relationships=None):
|
||||||
if relationships is None:
|
if relationships is None:
|
||||||
relationships = [ConceptRelation]
|
relationships = [ConceptRelation(self, None)]
|
||||||
rels = getRelations(first=self, relationships=relationships)
|
rels = getRelations(first=self, relationships=relationships)
|
||||||
return [r.second for r in rels]
|
return [r.second for r in rels]
|
||||||
# TODO: sort...
|
# TODO: sort...
|
||||||
|
|
||||||
def getParents(self, relationships=None):
|
def getParents(self, relationships=None):
|
||||||
if relationships is None:
|
if relationships is None:
|
||||||
relationships = [ConceptRelation]
|
relationships = [ConceptRelation(None, self)]
|
||||||
rels = getRelations(second=self, relationships=relationships)
|
rels = getRelations(second=self, relationships=relationships)
|
||||||
return [r.first for r in rels]
|
return [r.first for r in rels]
|
||||||
|
|
||||||
|
@ -117,7 +128,7 @@ class Concept(Contained, Persistent):
|
||||||
|
|
||||||
def deassignChildren(self, concept, relationships=None):
|
def deassignChildren(self, concept, relationships=None):
|
||||||
if relationships is None:
|
if relationships is None:
|
||||||
relationships = [ConceptRelation]
|
relationships = [ConceptRelation(self, None)]
|
||||||
registry = zapi.getUtility(IRelationRegistry)
|
registry = zapi.getUtility(IRelationRegistry)
|
||||||
relations = []
|
relations = []
|
||||||
for rs in relationships:
|
for rs in relationships:
|
||||||
|
@ -159,9 +170,28 @@ class ConceptManager(BTreeContainer):
|
||||||
|
|
||||||
implements(IConceptManager, ILoopsContained)
|
implements(IConceptManager, ILoopsContained)
|
||||||
|
|
||||||
|
typeConcept = None
|
||||||
|
typePredicate = None
|
||||||
|
defaultPredicate = None
|
||||||
|
|
||||||
def getLoopsRoot(self):
|
def getLoopsRoot(self):
|
||||||
return zapi.getParent(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):
|
def getViewManager(self):
|
||||||
return self.getLoopsRoot().getViewManager()
|
return self.getLoopsRoot().getViewManager()
|
||||||
|
|
||||||
|
@ -202,10 +232,12 @@ class ConceptTypeSourceList(object):
|
||||||
@Lazy
|
@Lazy
|
||||||
def conceptTypes(self):
|
def conceptTypes(self):
|
||||||
result = []
|
result = []
|
||||||
typeObject = self.concepts.get('type')
|
cm = self.concepts
|
||||||
|
typeObject = cm.getTypeConcept()
|
||||||
unknownType = self.concepts.get('unknown')
|
unknownType = self.concepts.get('unknown')
|
||||||
if typeObject is not None:
|
if typeObject is not None:
|
||||||
types = typeObject.getParents((TypeRelation,))
|
typeRelation = ConceptRelation(None, typeObject, cm.getTypePredicate())
|
||||||
|
types = typeObject.getChildren((typeRelation,))
|
||||||
if typeObject not in types:
|
if typeObject not in types:
|
||||||
result.append(typeObject)
|
result.append(typeObject)
|
||||||
if unknownType is not None and unknownType not in types:
|
if unknownType is not None and unknownType not in types:
|
||||||
|
|
|
@ -74,6 +74,10 @@
|
||||||
permission="zope.ManageContent"
|
permission="zope.ManageContent"
|
||||||
interface="zope.app.container.interfaces.IWriteContainer" />
|
interface="zope.app.container.interfaces.IWriteContainer" />
|
||||||
|
|
||||||
|
<require
|
||||||
|
permission="zope.View"
|
||||||
|
attributes="getTypePredicate getDefaultPredicate getTypeConcept" />
|
||||||
|
|
||||||
</content>
|
</content>
|
||||||
|
|
||||||
<interface
|
<interface
|
||||||
|
|
|
@ -141,6 +141,17 @@ class IConceptManager(ILoopsObject, IContainer):
|
||||||
"""
|
"""
|
||||||
contains(IConcept)
|
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):
|
class IConceptManagerContained(Interface):
|
||||||
containers(IConceptManager)
|
containers(IConceptManager)
|
||||||
|
|
|
@ -30,6 +30,7 @@ from zope.interface import implements
|
||||||
from persistent import Persistent
|
from persistent import Persistent
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from cybertools.relation.registry import getRelations
|
from cybertools.relation.registry import getRelations
|
||||||
|
from cybertools.relation.interfaces import IRelatable
|
||||||
|
|
||||||
from interfaces import IResource
|
from interfaces import IResource
|
||||||
from interfaces import IDocument, IDocumentSchema, IDocumentView
|
from interfaces import IDocument, IDocumentSchema, IDocumentView
|
||||||
|
@ -40,7 +41,7 @@ from interfaces import ILoopsContained
|
||||||
|
|
||||||
class Resource(Contained, Persistent):
|
class Resource(Contained, Persistent):
|
||||||
|
|
||||||
implements(IResource, IResourceManagerContained)
|
implements(IResource, IResourceManagerContained, IRelatable)
|
||||||
|
|
||||||
_title = u''
|
_title = u''
|
||||||
def getTitle(self): return self._title
|
def getTitle(self): return self._title
|
||||||
|
|
|
@ -112,7 +112,10 @@ class TargetSourceList(object):
|
||||||
self.context = removeSecurityProxy(context)
|
self.context = removeSecurityProxy(context)
|
||||||
root = self.context.getLoopsRoot()
|
root = self.context.getLoopsRoot()
|
||||||
self.resources = root.getResourceManager()
|
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):
|
def __iter__(self):
|
||||||
for obj in self.resources.values():
|
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 persistent import Persistent
|
||||||
from cybertools.relation import DyadicRelation
|
from cybertools.relation import DyadicRelation
|
||||||
from cybertools.relation.registry import getRelations
|
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 IView, INode, INodeConfigSchema
|
||||||
from interfaces import IViewManager, INodeContained
|
from interfaces import IViewManager, INodeContained
|
||||||
|
@ -46,7 +46,7 @@ from interfaces import ITargetRelation
|
||||||
|
|
||||||
class View(object):
|
class View(object):
|
||||||
|
|
||||||
implements(IView, INodeContained)
|
implements(IView, INodeContained, IRelatable)
|
||||||
|
|
||||||
_title = u''
|
_title = u''
|
||||||
def getTitle(self): return self._title
|
def getTitle(self): return self._title
|
||||||
|
|
Loading…
Add table
Reference in a new issue