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:
helmutm 2006-02-22 09:44:29 +00:00
parent 8477eb47b9
commit 07b62baa25
6 changed files with 222 additions and 105 deletions

View file

@ -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())

View file

@ -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

View file

@ -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)

View file

@ -22,6 +22,8 @@
<tr>
<th>&nbsp;</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>

View file

@ -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

View file

@ -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.
"""