Work in progress: concept relations using predicates...
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1092 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
8477eb47b9
commit
07b62baa25
6 changed files with 222 additions and 105 deletions
32
README.txt
32
README.txt
|
@ -49,7 +49,7 @@ default predicate concept; the default name for this is 'standard'.
|
|||
>>> from cybertools.relation.registry import DummyRelationRegistry
|
||||
>>> from zope.app.testing import ztapi
|
||||
>>> ztapi.provideUtility(IRelationRegistry, DummyRelationRegistry())
|
||||
>>> concepts['standard'] = Concept('Default Predicate')
|
||||
>>> concepts['standard'] = Concept('parent')
|
||||
|
||||
Now we can assign the concept c2 as a child to c1 (using the standard
|
||||
ConceptRelation):
|
||||
|
@ -58,20 +58,13 @@ ConceptRelation):
|
|||
|
||||
We can now ask our concepts for their related child and parent concepts:
|
||||
|
||||
>>> sc1 = cc1.getChildren()
|
||||
>>> len(sc1)
|
||||
1
|
||||
>>> cc2 in sc1
|
||||
True
|
||||
>>> [zapi.getName(c) for c in cc1.getChildren()]
|
||||
[u'cc2']
|
||||
>>> len(cc1.getParents())
|
||||
0
|
||||
>>> [zapi.getName(p) for p in cc2.getParents()]
|
||||
[u'cc1']
|
||||
|
||||
>>> pc2 = cc2.getParents()
|
||||
>>> len(pc2)
|
||||
1
|
||||
|
||||
>>> cc1 in pc2
|
||||
True
|
||||
>>> len(cc2.getChildren())
|
||||
0
|
||||
|
||||
|
@ -80,7 +73,7 @@ relation to a special kind of concept object with the magic name '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['hasType'] = Concept(u'has type')
|
||||
>>> concepts['type'] = Concept(u'Type')
|
||||
>>> typeObject = concepts['type']
|
||||
>>> typeObject.setConceptType(typeObject)
|
||||
|
@ -117,9 +110,18 @@ Concept Views
|
|||
>>> from loops.browser.concept import ConceptView
|
||||
>>> view = ConceptView(cc1, TestRequest())
|
||||
|
||||
>>> sorted([c.title for c in view.children()])
|
||||
>>> children = list(view.children())
|
||||
>>> [c.title for c in children]
|
||||
[u'Zope 3']
|
||||
|
||||
The token attribute provided with the items returned by the children() and
|
||||
parents() methods identifies identifies not only the item itself but
|
||||
also the relationship to the context object using a combination
|
||||
of URIs to item and the predicate of the relationship:
|
||||
|
||||
>>> [c.token for c in children]
|
||||
['.loops/concepts/cc2:.loops/concepts/standard']
|
||||
|
||||
The concept view allows updating the underlying context object:
|
||||
|
||||
>>> cc3 = Concept(u'loops for Zope 3')
|
||||
|
@ -133,7 +135,7 @@ The concept view allows updating the underlying context object:
|
|||
|
||||
>>> view = ConceptView(cc1,
|
||||
... TestRequest(action='remove', qualifier='children',
|
||||
... tokens=['.loops/concepts/cc2']))
|
||||
... tokens=['.loops/concepts/cc2:.loops/concepts/standard']))
|
||||
>>> view.update()
|
||||
True
|
||||
>>> sorted(c.title for c in cc1.getChildren())
|
||||
|
|
|
@ -32,8 +32,8 @@ from zope.security.proxy import removeSecurityProxy
|
|||
class BaseView(object):
|
||||
|
||||
def __init__(self, context, request):
|
||||
self.context = context
|
||||
#self.context = removeSecurityProxy(context)
|
||||
#self.context = context
|
||||
self.context = removeSecurityProxy(context)
|
||||
self.request = request
|
||||
|
||||
@Lazy
|
||||
|
@ -64,6 +64,15 @@ class BaseView(object):
|
|||
def value(self):
|
||||
return self.context
|
||||
|
||||
@Lazy
|
||||
def typeTitle(self):
|
||||
return self.context.conceptType.title
|
||||
|
||||
@Lazy
|
||||
def typeUrl(self):
|
||||
return zapi.absoluteURL(self.context.conceptType, self.request)
|
||||
|
||||
|
||||
|
||||
class LoopsTerms(object):
|
||||
""" Provide the ITerms interface, e.g. for usage in selection
|
||||
|
|
|
@ -40,12 +40,22 @@ from loops.browser.common import BaseView, LoopsTerms
|
|||
class ConceptView(BaseView):
|
||||
|
||||
def children(self):
|
||||
ch = self.context.getChildren()
|
||||
return ch and self.viewIterator(ch) or []
|
||||
for r in self.context.getChildRelations():
|
||||
yield ConceptRelationView(r, self.request, contextIsSecond=True)
|
||||
|
||||
def parents(self):
|
||||
p = self.context.getParents()
|
||||
return p and self.viewIterator(p) or []
|
||||
for r in self.context.getParentRelations():
|
||||
yield ConceptRelationView(r, self.request)
|
||||
#rels = self.context.getParentRelations()
|
||||
#result = []
|
||||
#for r in rels:
|
||||
# p = r.first
|
||||
#if p is None: # this should not be necessary
|
||||
# print 'Warning: parents() got a None first on', \
|
||||
# zapi.getName(self.context), zapi.getName(r.predicate)
|
||||
# continue
|
||||
# p.predicate = r.predicate
|
||||
#return result and self.viewIterator(result) or []
|
||||
|
||||
def viewIterator(self, objs):
|
||||
request = self.request
|
||||
|
@ -61,6 +71,10 @@ class ConceptView(BaseView):
|
|||
return True
|
||||
tokens = self.request.get('tokens', [])
|
||||
for token in tokens:
|
||||
parts = token.split(':')
|
||||
token = parts[0]
|
||||
if len(parts) > 1:
|
||||
relToken = parts[1]
|
||||
concept = self.loopsRoot.loopsTraverse(token)
|
||||
if action == 'assign':
|
||||
assignAs = self.request.get('assignAs', 'child')
|
||||
|
@ -71,11 +85,12 @@ class ConceptView(BaseView):
|
|||
else:
|
||||
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
|
||||
elif action == 'remove':
|
||||
predicate = self.loopsRoot.loopsTraverse(relToken)
|
||||
qualifier = self.request.get('qualifier')
|
||||
if qualifier == 'parents':
|
||||
self.context.deassignParents(concept)
|
||||
self.context.deassignParents(concept, [predicate])
|
||||
elif qualifier == 'children':
|
||||
self.context.deassignChildren(concept)
|
||||
self.context.deassignChildren(concept, [predicate])
|
||||
else:
|
||||
raise(BadRequest, 'Illegal qualifier: %s.' % qualifier)
|
||||
else:
|
||||
|
@ -102,12 +117,6 @@ class ConceptView(BaseView):
|
|||
else:
|
||||
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
|
||||
|
||||
#def getVocabularyForRelated(self): # obsolete
|
||||
# source = ConceptSourceList(self.context)
|
||||
# terms = zapi.getMultiAdapter((source, self.request), ITerms)
|
||||
# for candidate in source:
|
||||
# yield terms.getTerm(candidate)
|
||||
|
||||
def search(self):
|
||||
request = self.request
|
||||
if request.get('action') != 'search':
|
||||
|
@ -120,3 +129,50 @@ class ConceptView(BaseView):
|
|||
result = self.loopsRoot.getConceptManager().values()
|
||||
return self.viewIterator(result)
|
||||
|
||||
|
||||
class ConceptRelationView(object):
|
||||
|
||||
def __init__(self, relation, request, contextIsSecond=False):
|
||||
if contextIsSecond:
|
||||
self.context = relation.second
|
||||
self.other = relation.first
|
||||
else:
|
||||
self.context = relation.first
|
||||
self.other = relation.second
|
||||
self.predicate = relation.predicate
|
||||
self.conceptType = self.context.conceptType
|
||||
self.request = request
|
||||
|
||||
@Lazy
|
||||
def loopsRoot(self):
|
||||
return self.context.getLoopsRoot()
|
||||
|
||||
@Lazy
|
||||
def url(self):
|
||||
return zapi.absoluteURL(self.context, self.request)
|
||||
|
||||
@Lazy
|
||||
def title(self):
|
||||
return self.context.title
|
||||
|
||||
@Lazy
|
||||
def token(self):
|
||||
return ':'.join((self.loopsRoot.getLoopsUri(self.context),
|
||||
self.loopsRoot.getLoopsUri(self.predicate)))
|
||||
|
||||
@Lazy
|
||||
def typeTitle(self):
|
||||
return self.conceptType.title
|
||||
|
||||
@Lazy
|
||||
def typeUrl(self):
|
||||
return zapi.absoluteURL(self.conceptType, self.request)
|
||||
|
||||
@Lazy
|
||||
def predicateTitle(self):
|
||||
return self.predicate.title
|
||||
|
||||
@Lazy
|
||||
def predicateUrl(self):
|
||||
return zapi.absoluteURL(self.predicate, self.request)
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
<tr>
|
||||
<th> </th>
|
||||
<th i18n:translate="label_title">Title</th>
|
||||
<th i18n:translate="label_predicate">Predicate</th>
|
||||
<th i18n:translate="label_type">Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -37,6 +39,23 @@
|
|||
Title
|
||||
</a>
|
||||
</td>
|
||||
<tal:relation condition="item/predicateTitle|nothing">
|
||||
<td>
|
||||
<a tal:content="item/predicateTitle" href="#"
|
||||
tal:attributes="href
|
||||
string:${item/predicateUrl}/@@SelectedManagementView.html">
|
||||
Type
|
||||
</a>
|
||||
</td>
|
||||
</tal:relation>
|
||||
<td>
|
||||
<a tal:condition="item/typeTitle | nothing"
|
||||
tal:content="item/typeTitle" href="#"
|
||||
tal:attributes="href
|
||||
string:${item/typeUrl}/@@SelectedManagementView.html">
|
||||
Title
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
126
concept.py
126
concept.py
|
@ -45,13 +45,10 @@ from interfaces import ISearchableText
|
|||
|
||||
# relation classes
|
||||
|
||||
class ConceptRelation(DyadicRelation):
|
||||
""" A relation between concept objects.
|
||||
"""
|
||||
implements(IConceptRelation)
|
||||
class BaseRelation(DyadicRelation):
|
||||
|
||||
def __init__(self, first, second, predicate=None):
|
||||
super(ConceptRelation, self).__init__(first, second)
|
||||
super(BaseRelation, self).__init__(first, second)
|
||||
if predicate is None:
|
||||
context = first is not None and first or second
|
||||
cm = context.getLoopsRoot().getConceptManager()
|
||||
|
@ -59,12 +56,18 @@ class ConceptRelation(DyadicRelation):
|
|||
self.predicate = predicate
|
||||
|
||||
def getPredicateName(self):
|
||||
baseName = super(ConceptRelation, self).getPredicateName()
|
||||
baseName = super(BaseRelation, self).getPredicateName()
|
||||
id = zapi.getUtility(IRelationRegistry).getUniqueIdForObject(self.predicate)
|
||||
return '.'.join((baseName, str(id)))
|
||||
|
||||
|
||||
class ResourceRelation(DyadicRelation):
|
||||
class ConceptRelation(BaseRelation):
|
||||
""" A relation between concept objects.
|
||||
"""
|
||||
implements(IConceptRelation)
|
||||
|
||||
|
||||
class ResourceRelation(BaseRelation):
|
||||
""" A relation between a concept and a resource object.
|
||||
"""
|
||||
implements(IConceptRelation)
|
||||
|
@ -84,16 +87,23 @@ class Concept(Contained, Persistent):
|
|||
title = property(getTitle, setTitle)
|
||||
|
||||
def getConceptType(self):
|
||||
cm = self.getLoopsRoot().getConceptManager()
|
||||
typeRelation = ConceptRelation(None, self, cm.getTypePredicate())
|
||||
rel = getRelationSingle(self, typeRelation, forSecond=True)
|
||||
return rel and rel.first or None
|
||||
typePred = self.getConceptManager().getTypePredicate()
|
||||
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):
|
||||
if self.getConceptType() != concept:
|
||||
cm = self.getLoopsRoot().getConceptManager()
|
||||
typeRelation = ConceptRelation(removeSecurityProxy(concept), self,
|
||||
cm.getTypePredicate())
|
||||
setRelationSingle(typeRelation, forSecond=True)
|
||||
current = self.getConceptType()
|
||||
if current != concept:
|
||||
typePred = self.getConceptManager().getTypePredicate()
|
||||
if current is not None:
|
||||
self.deassignParents(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''):
|
||||
|
@ -102,66 +112,74 @@ class Concept(Contained, Persistent):
|
|||
def getLoopsRoot(self):
|
||||
return zapi.getParent(self).getLoopsRoot()
|
||||
|
||||
def getConceptManager(self):
|
||||
return self.getLoopsRoot().getConceptManager()
|
||||
|
||||
# concept relations
|
||||
|
||||
def getChildren(self, relationships=None):
|
||||
if relationships is None:
|
||||
relationships = [ConceptRelation(self, None)]
|
||||
rels = getRelations(first=self, relationships=relationships)
|
||||
return [r.second for r in rels]
|
||||
def getChildRelations(self, predicates=None, second=None):
|
||||
predicates = predicates is None and ['*'] or predicates
|
||||
relationships = [ConceptRelation(self, None, p) for p in predicates]
|
||||
# TODO: sort...
|
||||
return getRelations(first=self, second=second, relationships=relationships)
|
||||
|
||||
def getParents(self, relationships=None):
|
||||
if relationships is None:
|
||||
relationships = [ConceptRelation(None, self)]
|
||||
rels = getRelations(second=self, relationships=relationships)
|
||||
return [r.first for r in rels]
|
||||
def getChildren(self, predicates=None):
|
||||
return [r.second for r in self.getChildRelations(predicates)]
|
||||
|
||||
def assignChild(self, concept, relationship=ConceptRelation):
|
||||
def getParentRelations (self, predicates=None, first=None):
|
||||
predicates = predicates is None and ['*'] or predicates
|
||||
relationships = [ConceptRelation(None, self, p) for p in predicates]
|
||||
# TODO: sort...
|
||||
return getRelations(first=first, second=self, relationships=relationships)
|
||||
|
||||
def getParents(self, predicates=None):
|
||||
return [r.first for r in self.getParentRelations(predicates)]
|
||||
|
||||
def assignChild(self, concept, predicate=None):
|
||||
if predicate is None:
|
||||
predicate = self.getConceptManager().getDefaultPredicate()
|
||||
registry = zapi.getUtility(IRelationRegistry)
|
||||
rel = relationship(self, concept)
|
||||
rel = ConceptRelation(self, concept, predicate)
|
||||
registry.register(rel)
|
||||
# TODO (?): avoid duplicates
|
||||
|
||||
def assignParent(self, concept, relationship=ConceptRelation):
|
||||
concept.assignChild(self, relationship)
|
||||
def assignParent(self, concept, predicate=None):
|
||||
concept.assignChild(self, predicate)
|
||||
|
||||
def deassignChildren(self, concept, relationships=None):
|
||||
if relationships is None:
|
||||
relationships = [ConceptRelation(self, None)]
|
||||
def deassignChildren(self, concept, predicates=None):
|
||||
registry = zapi.getUtility(IRelationRegistry)
|
||||
relations = []
|
||||
for rs in relationships:
|
||||
relations.extend(registry.query(first=self, second=concept,
|
||||
relationship=rs))
|
||||
for rel in relations:
|
||||
#relations = []
|
||||
#for rs in relationships:
|
||||
# relations.extend(registry.query(first=self, second=concept,
|
||||
# relationship=rs))
|
||||
for rel in self.getChildRelations(predicates, concept):
|
||||
registry.unregister(rel)
|
||||
|
||||
def deassignParents(self, concept, relationships=None):
|
||||
concept.deassignChildren(self, relationships)
|
||||
def deassignParents(self, concept, predicates=None):
|
||||
concept.deassignChildren(self, predicates)
|
||||
|
||||
# resource relations
|
||||
|
||||
def getResources(self, relationships=None):
|
||||
if relationships is None:
|
||||
relationships = [ResourceRelation]
|
||||
rels = getRelations(first=self, relationships=relationships)
|
||||
return [r.second for r in rels]
|
||||
def getResourceRelations(self, predicates=None):
|
||||
predicates = predicates is None and ['*'] or predicates
|
||||
relationships = [ResourceRelation(self, None, p) for p in predicates]
|
||||
# TODO: sort...
|
||||
return getRelations(first=self, relationships=relationships)
|
||||
|
||||
def assignResource(self, resource, relationship=ResourceRelation):
|
||||
def getResources(self, predicates=None):
|
||||
return [r.second for r in self.getResourceRelations(predicates)]
|
||||
|
||||
def assignResource(self, resource, predicate=None):
|
||||
if predicate is None:
|
||||
predicate = self.getConceptManager().getDefaultPredicate()
|
||||
registry = zapi.getUtility(IRelationRegistry)
|
||||
registry.register(relationship(self, resource))
|
||||
registry.register(ResourceRelation(self, resource, predicate))
|
||||
# TODO (?): avoid duplicates
|
||||
|
||||
def deassignResource(self, resource, relationships=None):
|
||||
if relationships is None:
|
||||
relationships = [ResourceRelation]
|
||||
def deassignResource(self, resource, predicates=None):
|
||||
registry = zapi.getUtility(IRelationRegistry)
|
||||
relations = registry.query(first=self, second=resource,
|
||||
relationships=relationships)
|
||||
for rel in relations:
|
||||
registry.unregister(relation)
|
||||
for rel in self.getResourceRelations(predicates):
|
||||
registry.unregister(rel)
|
||||
|
||||
|
||||
# concept manager
|
||||
|
|
|
@ -79,56 +79,69 @@ class IConcept(ILoopsObject, IPotentialTarget):
|
|||
source="loops.conceptTypeSource",
|
||||
required=False)
|
||||
|
||||
def getChildren(relationships=None):
|
||||
def getChildren(predicates=None):
|
||||
""" Return a sequence of concepts related to self as child concepts,
|
||||
possibly restricted to the relationships (typically a list of
|
||||
relation classes) given.
|
||||
optionally restricted to the predicates given.
|
||||
"""
|
||||
|
||||
def getParents(relationships=None):
|
||||
def getChildRelations(predicates=None):
|
||||
""" Return a sequence of relations to other concepts assigned to self
|
||||
as child concepts, optionally restricted to the predicates given.
|
||||
"""
|
||||
|
||||
def getParents(predicates=None):
|
||||
""" Return a tuple of concepts related to self as parent concepts,
|
||||
possibly restricted to the relationships (typically a list of
|
||||
relation classes) given.
|
||||
optionally restricted to the predicates given.
|
||||
"""
|
||||
|
||||
def assignChild(concept, relationship):
|
||||
""" Assign an existing concept to self using the relationship given.
|
||||
def getParentRelations(predicates=None):
|
||||
""" Return a sequence of relations to other concepts assigned to self
|
||||
as child concepts, optionally restricted to the predicates given.
|
||||
"""
|
||||
|
||||
def assignChild(concept, predicate):
|
||||
""" Assign an existing concept to self using the predicate given.
|
||||
The assigned concept will be a child concept of self.
|
||||
|
||||
The relationship defaults to ConceptRelation.
|
||||
The predicate defaults to the concept manager's default predicate.
|
||||
"""
|
||||
|
||||
def assignParent(concept, relationship):
|
||||
""" Assign an existing concept to self using the relationship given.
|
||||
def assignParent(concept, predicate):
|
||||
""" Assign an existing concept to self using the predicate given.
|
||||
The assigned concept will be a parent concept of self.
|
||||
|
||||
The relationship defaults to ConceptRelation.
|
||||
The predicate defaults to the concept manager's default predicate.
|
||||
"""
|
||||
|
||||
def deassignChildren(concept, relationships=None):
|
||||
def deassignChildren(concept, predicates=None):
|
||||
""" Remove the child concept relations to the concept given from self,
|
||||
optionally restricting them to the relationships given.
|
||||
optionally restricting them to the predicates given.
|
||||
"""
|
||||
|
||||
def deassignParents(concept, relationships=None):
|
||||
def deassignParents(concept, predicates=None):
|
||||
""" Remove the child concept relations to the concept given from self,
|
||||
optionally restricting them to the relationships given.
|
||||
optionally restricting them to the predicates given.
|
||||
"""
|
||||
|
||||
def getResources(relationships=None):
|
||||
def getResources(predicates=None):
|
||||
""" Return a sequence of resources assigned to self,
|
||||
possibly restricted to the relationships given.
|
||||
optionally restricted to the predicates given.
|
||||
"""
|
||||
|
||||
def assignResource(resource, relationship):
|
||||
""" Assign an existing resource to self using the relationship given.
|
||||
def getResourceRelations(predicates=None):
|
||||
""" Return a sequence of relations to resources assigned to self,
|
||||
optionally restricted to the predicates given.
|
||||
"""
|
||||
|
||||
def assignResource(resource, predicate):
|
||||
""" Assign an existing resource to self using the predicate given.
|
||||
|
||||
The relationship defaults to ConceptResourceRelation.
|
||||
"""
|
||||
|
||||
def deassignResource(resource, relationships=None):
|
||||
def deassignResource(resource, predicates=None):
|
||||
""" Remove the relations to the resource given from self, optionally
|
||||
restricting them to the relationships given.
|
||||
restricting them to the predicates given.
|
||||
"""
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue