From 8477eb47b949a2d45e20b6b68a5af0c848c09672 Mon Sep 17 00:00:00 2001 From: helmutm Date: Tue, 21 Feb 2006 09:53:41 +0000 Subject: [PATCH] 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 --- README.txt | 23 +++++++++++------ browser/common.py | 4 --- browser/concept.py | 4 +++ concept.py | 62 +++++++++++++++++++++++++++++++++++----------- configure.zcml | 4 +++ interfaces.py | 11 ++++++++ resource.py | 3 ++- target.py | 5 +++- view.py | 4 +-- 9 files changed, 90 insertions(+), 30 deletions(-) diff --git a/README.txt b/README.txt index f9c43b0..f0e70b4 100755 --- a/README.txt +++ b/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: diff --git a/browser/common.py b/browser/common.py index 91a8858..67de75b 100644 --- a/browser/common.py +++ b/browser/common.py @@ -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) diff --git a/browser/concept.py b/browser/concept.py index f179387..86f8912 100644 --- a/browser/concept.py +++ b/browser/concept.py @@ -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)) diff --git a/concept.py b/concept.py index 4280d76..fb6584e 100644 --- a/concept.py +++ b/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: diff --git a/configure.zcml b/configure.zcml index dd98212..0536d19 100644 --- a/configure.zcml +++ b/configure.zcml @@ -74,6 +74,10 @@ permission="zope.ManageContent" interface="zope.app.container.interfaces.IWriteContainer" /> + +