[Checkins] SVN: lovely.relation/trunk/ DataRealtionProperty allows the annotation of the relation

Juergen Kartnaller juergen at kartnaller.at
Wed Sep 12 11:19:13 EDT 2007


Log message for revision 79594:
  DataRealtionProperty allows the annotation of the relation

Changed:
  U   lovely.relation/trunk/CHANGES.txt
  A   lovely.relation/trunk/src/lovely/relation/dataproperty.py
  A   lovely.relation/trunk/src/lovely/relation/dataproperty.txt
  U   lovely.relation/trunk/src/lovely/relation/interfaces.py
  U   lovely.relation/trunk/src/lovely/relation/property.py
  U   lovely.relation/trunk/src/lovely/relation/tests.py

-=-
Modified: lovely.relation/trunk/CHANGES.txt
===================================================================
--- lovely.relation/trunk/CHANGES.txt	2007-09-12 15:04:34 UTC (rev 79593)
+++ lovely.relation/trunk/CHANGES.txt	2007-09-12 15:19:13 UTC (rev 79594)
@@ -5,6 +5,9 @@
 After
 =====
 
+- DataRealtionProperty allows the annotation of the relation
+
+
 2007/09/05 1.1.0a1
 ==================
 

Added: lovely.relation/trunk/src/lovely/relation/dataproperty.py
===================================================================
--- lovely.relation/trunk/src/lovely/relation/dataproperty.py	                        (rev 0)
+++ lovely.relation/trunk/src/lovely/relation/dataproperty.py	2007-09-12 15:19:13 UTC (rev 79594)
@@ -0,0 +1,104 @@
+##############################################################################
+#
+# Copyright (c) 2007 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Relationship Properties
+
+$Id$
+"""
+
+from types import ListType, TupleType
+
+from zope import interface
+
+from zope.annotation.interfaces import IAttributeAnnotatable
+
+from app import O2OStringTypeRelationship
+from property import RelationPropertyOut
+from interfaces import IDataRelationPropertyOut, IDataRelationship
+
+
+class DataRelationship(O2OStringTypeRelationship):
+    interface.implements(IDataRelationship, IAttributeAnnotatable)
+
+    @apply
+    def source():
+        def get(self):
+            if isinstance(self._sources, (ListType, TupleType)):
+                return self._sources[0]
+            return self._sources
+        def set(self, value):
+            self._sources = value
+        return property(get, set)
+
+    @apply
+    def target():
+        def get(self):
+            if isinstance(self._targets, (ListType, TupleType)):
+                return self._targets[0]
+            return self._targets
+        def set(self, value):
+            self._targets = value
+        return property(get, set)
+
+
+class DataRelationPropertyOut(RelationPropertyOut):
+    interface.implements(IDataRelationPropertyOut)
+
+    def __init__(self, manager, name=None, uids=False, relType=None):
+        super(DataRelationPropertyOut, self).__init__(manager,
+                                                      name,
+                                                      uids,
+                                                      relType)
+
+    def new(self, target):
+        return DataRelationship(None, [self._relType], target)
+
+    def __set__(self, inst, value):
+        if self._field.readonly:
+            raise ValueError(self._name, 'field is readonly')
+        if value is None:
+            v = None
+        elif not self._manager.seqOut:
+            if not IDataRelationship.providedBy(value):
+                raise TypeError
+            v = value
+            v.source = inst
+        else:
+            v = value
+            for val in v:
+                if not IDataRelationship.providedBy(val):
+                    raise TypeError
+            for val in v:
+                val.source = inst
+        self._manager.setTargetRelations(inst, v, self._relType)
+        if self._ordered:
+            inst.__dict__['_o_' + self._name] = \
+                        list(self._manager.tokenizeValues(value, 'relations'))
+
+    def __get__(self, inst, klass):
+        if inst is None:
+            return self
+        tokens = self._manager.getSourceRelationTokens(inst, self._relType)
+        if self._ordered:
+            tokens = self._sort(inst, tokens)
+        if not self._uids:
+            tokens = self._manager.resolveValueTokens(tokens, 'sources')
+        tokens = list(tokens)
+        if self._manager.seqOut:
+            return tokens
+        else:
+            try:
+                return tokens[0]
+            except IndexError:
+                return None
+


Property changes on: lovely.relation/trunk/src/lovely/relation/dataproperty.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.relation/trunk/src/lovely/relation/dataproperty.txt
===================================================================
--- lovely.relation/trunk/src/lovely/relation/dataproperty.txt	                        (rev 0)
+++ lovely.relation/trunk/src/lovely/relation/dataproperty.txt	2007-09-12 15:19:13 UTC (rev 79594)
@@ -0,0 +1,160 @@
+======================
+Data Realtion Property
+======================
+
+The data relation property allows us to attach data to the relation.
+
+  >>> from zope import schema, interface
+  >>> from zope.schema.interfaces import IObject
+  >>> class IImage(interface.Interface):
+  ...     name = schema.TextLine(title=u'Name')
+
+  >>> class Image(object):
+  ...     interface.implements(IImage)
+  ...     def __init__(self, name):
+  ...         self.name = name
+  ...     def __repr__(self):
+  ...         return '<image %r>' % self.name
+
+  >>> class IChapter(interface.Interface):
+  ...     document = schema.Object(IObject, title=u'Document')
+  ...     content = schema.Text(title=u'Content')
+
+  >>> class IDocument(interface.Interface):
+  ...     teaser = schema.Object(IImage, title=u'Teaser Image')
+  ...     chapters = schema.List(title=u"Chapters",
+  ...                            value_type=schema.Object(IChapter),
+  ...                            required=False,
+  ...                            default=[])
+
+  >>> class IDocumentChapterRelation(interface.Interface):
+  ...     document = schema.Object(IDocument, title=u'Document')
+
+We need the IntId utility for our relations.
+
+  >>> from zope import component
+  >>> from zope.app.intid.interfaces import IIntIds
+  >>> intids = component.getUtility(IIntIds)
+
+  >>> from lovely.relation.app import O2OStringTypeRelationships
+  >>> from lovely.relation.interfaces import IO2OStringTypeRelationships
+  >>> rels = O2OStringTypeRelationships()
+  >>> component.provideUtility(rels, IO2OStringTypeRelationships)
+
+We setup a relation manager for the document to teaser relation.
+
+  >>> from lovely.relation.property import FieldRelationManager
+  >>> documentTeaser = FieldRelationManager(IDocument['teaser'])
+  >>> documentChapter = FieldRelationManager(
+  ...                                 IDocument['chapters'],
+  ...                                 IDocumentChapterRelation['document'])
+
+And our document class uses a DataRelationProperty to refer to it's teaser.
+
+  >>> from lovely.relation.dataproperty import DataRelationPropertyOut
+  >>> class Document(object):
+  ...     interface.implements(IDocument)
+  ...     teaser = DataRelationPropertyOut(documentTeaser)
+  ...     smallImg = DataRelationPropertyOut(
+  ...                                 documentTeaser, 'Document.smallImg')
+  ...     chapters = DataRelationPropertyOut(documentChapter)
+  ...     def __init__(self, name):
+  ...         self.name = name
+  ...     def __repr__(self):
+  ...         return '<document %r>' % self.name
+
+  >>> from lovely.relation.property import RelationPropertyIn
+  >>> class Chapter(object):
+  ...     interface.implements(IChapter)
+  ...     document = RelationPropertyIn(documentChapter)
+  ...     def __init__(self, content):
+  ...         self.content = content
+  ...     def __repr__(self):
+  ...         return '<chapter %r>' % self.content
+
+Now we create a document and an image.
+
+  >>> doc1 = Document(u'Doc One')
+  >>> img1 = Image(u'Image One')
+
+To assign the image to the teaser property we need to instanciate a new
+relation. We do this by calling new on the class property.
+
+  >>> rel1 = Document.teaser.new(img1)
+  >>> rel1
+  <DataRelationship None>
+
+At this time the relation is not bound to the source.
+
+  >>> rel1.source is None
+  True
+  >>> rel1.target
+  <image u'Image One'>
+
+We can bind it :
+
+  >>> doc1.teaser = rel1
+  >>> doc1.teaser is rel1
+  True
+  >>> rel1.source
+  <document u'Doc One'>
+
+It is now possible to annotate the teaser reference.
+
+  >>> from zope.annotation.interfaces import IAnnotatable
+  >>> IAnnotatable.providedBy(rel1)
+  True
+
+To be able to do that we need something which can be annotated.
+
+  >>> class IImagePositionParameters(interface.Interface):
+  ...      x = schema.Int(title=u'x')
+  ...      y = schema.Int(title=u'y')
+
+  >>> from zope import component
+  >>> class ImagePositionParameters(object):
+  ...     interface.implements(IImagePositionParameters)
+  ...     component.adapts(IAnnotatable)
+  >>> from zope.annotation.factory import factory
+  >>> posFactory = factory(ImagePositionParameters)
+  >>> component.provideAdapter(posFactory)
+
+  >>> params = IImagePositionParameters(rel1)
+  >>> params
+  <ImagePositionParameters ...>
+
+Let's use a second property from the same relation manager but with another
+relation type.
+
+  >>> img2 = Image(u'Image Two')
+  >>> smallImgRel = Document.smallImg.new(img2)
+  >>> smallImgRel
+  <DataRelationship None>
+  >>> doc1.smallImgRel = smallImgRel
+  >>> doc1.smallImgRel is smallImgRel
+  True
+  >>> doc1.teaser is rel1
+  True
+
+
+Use of lists
+------------
+
+  >>> chapter1 = Chapter(u'Chapter One')
+  >>> chapterRel1 = Document.chapters.new(chapter1)
+  >>> chapterRel1
+  <DataRelationship None>
+  >>> doc1.chapters = (chapterRel1, )
+  >>> [c.target for c in doc1.chapters]
+  [<chapter u'Chapter One'>]
+  >>> chapter1.document
+  <document u'Doc One'>
+
+  >>> chapter2 = Chapter(u'Chapter Two')
+  >>> chapterRel2 = Document.chapters.new(chapter2)
+  >>> doc1.chapters = doc1.chapters + [chapterRel2]
+  >>> [c.target for c in doc1.chapters]
+  [<chapter u'Chapter One'>, <chapter u'Chapter Two'>]
+  >>> chapter2.document
+  <document u'Doc One'>
+


Property changes on: lovely.relation/trunk/src/lovely/relation/dataproperty.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: lovely.relation/trunk/src/lovely/relation/interfaces.py
===================================================================
--- lovely.relation/trunk/src/lovely/relation/interfaces.py	2007-09-12 15:04:34 UTC (rev 79593)
+++ lovely.relation/trunk/src/lovely/relation/interfaces.py	2007-09-12 15:19:13 UTC (rev 79594)
@@ -151,3 +151,25 @@
 
 class IO2OStringTypeRelationship(IOneToOneRelationship):
     """A one to one relationship with a string as type"""
+
+
+class IDataRelationship(interface.Interface):
+
+    source = interface.Attribute(
+        """Source pointing in the relationship. Readonly.""")
+
+    target = interface.Attribute(
+        """Target being pointed to in the relationship. Readonly.""")
+
+
+class IDataRelationPropertyOut(interface.Interface):
+    """Extends the relation property to be able to annotate the relation"""
+
+    def new(self, target):
+        """Instantiate a new relation.
+
+        The returned instance implements IDataRelation and must be used for
+        assignement to a data relation property. The relation instance is
+        annotatable to allow data to be added to a relation.
+        """
+

Modified: lovely.relation/trunk/src/lovely/relation/property.py
===================================================================
--- lovely.relation/trunk/src/lovely/relation/property.py	2007-09-12 15:04:34 UTC (rev 79593)
+++ lovely.relation/trunk/src/lovely/relation/property.py	2007-09-12 15:19:13 UTC (rev 79594)
@@ -48,6 +48,9 @@
             self.relType=relType
         self.utilName = utilName
 
+    def _instantiateRelation(self, source, relTypes, target):
+        return O2OStringTypeRelationship(source, relTypes, target)
+
     @property
     def seqIn(self):
         return ISequence.providedBy(self.fIn)
@@ -62,28 +65,22 @@
                                     name=self.utilName)
 
     def getSourceTokens(self, target, relType):
-        util = self.util
-        return util.findSourceTokens(target, relType)
+        return self.util.findSourceTokens(target, relType)
 
     def getTargetTokens(self, source, relType):
-        util = self.util
-        return util.findTargetTokens(source, relType)
+        return self.util.findTargetTokens(source, relType)
 
     def getSourceRelations(self, obj, relType):
-        util = self.util
-        return util.findSourceRelationships(obj, relType)
+        return self.util.findSourceRelationships(obj, relType)
 
     def getTargetRelations(self, obj, relType):
-        util = self.util
-        return util.findTargetRelationships(obj, relType)
+        return self.util.findTargetRelationships(obj, relType)
 
     def getSourceRelationTokens(self, obj, relType):
-        util = self.util
-        return util.findSourceRelationshipTokens(obj, relType)
+        return self.util.findSourceRelationshipTokens(obj, relType)
 
     def getTargetRelationTokens(self, obj, relType):
-        util = self.util
-        return util.findTargetRelationshipTokens(obj, relType)
+        return self.util.findTargetRelationshipTokens(obj, relType)
 
     def setTargets(self, source, targets, relType):
         util = self.util
@@ -106,14 +103,40 @@
                 {'sources': sourceToken,
                  'relations': relType,
                  'targets': tt})
-            self.util.remove(rel.next())
-
+            util.remove(rel.next())
         for addT in list(
-            util.relationIndex.resolveValueTokens(addTT, 'targets')):
-            rel = O2OStringTypeRelationship(source, [relType],
-                                             addT)
-            self.util.add(rel)
+                    util.relationIndex.resolveValueTokens(addTT, 'targets')):
+            rel = self._instantiateRelation(source, [relType], addT)
+            util.add(rel)
 
+    def setTargetRelations(self, source, relations, relType):
+        util = self.util
+        if not self.seqOut:
+            relations = [relations]
+        targets = [rel.target for rel in relations]
+        if targets is not None:
+            newTargetTokens = list(util.relationIndex.tokenizeValues(
+                                                        targets, 'targets'))
+        else:
+            newTargetTokens = []
+        sourceToken = util.relationIndex.tokenizeValues([source],
+                                                        'sources').next()
+        oldTargetTokens = util.findTargetTokens(source, relType)
+        newTT = set(newTargetTokens)
+        oldTT = set(oldTargetTokens)
+        addTT = newTT.difference(oldTT)
+        delTT = oldTT.difference(newTT)
+        for tt in delTT:
+            rel = util.relationIndex.findRelationships(
+                                        {'sources': sourceToken,
+                                         'relations': relType,
+                                         'targets': tt})
+            util.remove(rel.next())
+        rels = [(rel.target, rel) for rel in relations]
+        for token, rel in zip(newTargetTokens, relations):
+            if token in addTT:
+                util.add(rel)
+
     def setSources(self, target, sources, relType):
         util = self.util
         if sources is not None:
@@ -140,11 +163,9 @@
 
         for addT in list(
             util.relationIndex.resolveValueTokens(addST, 'sources')):
-            rel = O2OStringTypeRelationship(addST, [relType],
-                                       target)
+            rel = self._instantiateRelation(addST, [relType], target)
             self.util.add(rel)
 
-
     def tokenizeValues(self, values, index):
         return self.util.relationIndex.tokenizeValues(values, index)
 

Modified: lovely.relation/trunk/src/lovely/relation/tests.py
===================================================================
--- lovely.relation/trunk/src/lovely/relation/tests.py	2007-09-12 15:04:34 UTC (rev 79593)
+++ lovely.relation/trunk/src/lovely/relation/tests.py	2007-09-12 15:19:13 UTC (rev 79594)
@@ -58,6 +58,10 @@
              setUp=setUp, tearDown=tearDown,
              optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
              ),
+        DocFileSuite('dataproperty.txt',
+             setUp=setUp, tearDown=tearDown,
+             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+             ),
         ))
 
 if __name__ == '__main__':



More information about the Checkins mailing list