[Commits] (vajda) - change history now tracks attribute value changes

commits at osafoundation.org commits at osafoundation.org
Mon Aug 30 14:27:18 PDT 2004


Commit by: vajda
Modified files:
chandler/repository/item/Item.py 1.154 1.155
chandler/repository/item/Values.py 1.15 1.16
chandler/repository/persistence/DBContainer.py 1.16 1.17
chandler/repository/persistence/RepositoryError.py 1.1 1.2
chandler/repository/persistence/XMLRepository.py 1.78 1.79
chandler/repository/persistence/XMLRepositoryView.py 1.50 1.51

Log message:
   - change history now tracks attribute value changes
   - added more details to VersionConflictError
   - fixed bug 1777


ViewCVS links:
http://cvs.osafoundation.org/index.cgi/chandler/repository/item/Item.py.diff?r1=text&tr1=1.154&r2=text&tr2=1.155
http://cvs.osafoundation.org/index.cgi/chandler/repository/item/Values.py.diff?r1=text&tr1=1.15&r2=text&tr2=1.16
http://cvs.osafoundation.org/index.cgi/chandler/repository/persistence/DBContainer.py.diff?r1=text&tr1=1.16&r2=text&tr2=1.17
http://cvs.osafoundation.org/index.cgi/chandler/repository/persistence/RepositoryError.py.diff?r1=text&tr1=1.1&r2=text&tr2=1.2
http://cvs.osafoundation.org/index.cgi/chandler/repository/persistence/XMLRepository.py.diff?r1=text&tr1=1.78&r2=text&tr2=1.79
http://cvs.osafoundation.org/index.cgi/chandler/repository/persistence/XMLRepositoryView.py.diff?r1=text&tr1=1.50&r2=text&tr2=1.51

Index: chandler/repository/persistence/DBContainer.py
diff -u chandler/repository/persistence/DBContainer.py:1.16 chandler/repository/persistence/DBContainer.py:1.17
--- chandler/repository/persistence/DBContainer.py:1.16	Wed Jul 28 13:40:38 2004
+++ chandler/repository/persistence/DBContainer.py	Mon Aug 30 14:27:16 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.16 $"
-__date__      = "$Date: 2004/07/28 20:40:38 $"
+__revision__  = "$Revision: 1.17 $"
+__date__      = "$Date: 2004/08/30 21:27:16 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -9,6 +9,7 @@
 from struct import pack, unpack
 
 from repository.item.Access import ACL, ACE
+from repository.item.Item import Item
 from repository.util.UUID import UUID
 from repository.persistence.Repository import Repository
 
@@ -389,18 +390,40 @@
 
 class HistContainer(DBContainer):
 
-    def writeVersion(self, uuid, version, docId, status, parentId=None):
+    def writeVersion(self, uuid, version, docId, status, parentId,
+                     dirtyValues, dirtyRefs):
+
+        if status & Item.DELETED:
+            value = pack('>ll16s', status, docId, parentId._uuid)
 
-        if parentId is not None:
-            value = pack('>li16s', docId, status, parentId._uuid)
         else:
-            value = pack('>li', docId, status)
+            buffer = cStringIO.StringIO()
+
+            buffer.write(pack('>ll', status, docId))
+            for name in dirtyValues:
+                if isinstance(name, unicode):
+                    name = name.encode('utf-8')
+                buffer.write(pack('>l', UUIDext.hash(name)))
+            for name in dirtyRefs:
+                if isinstance(name, unicode):
+                    name = name.encode('utf-8')
+                buffer.write(pack('>l', UUIDext.hash(name)))
+
+            value = buffer.getvalue()
+            buffer.close()
             
         self.put(pack('>l16s', version, uuid._uuid), value)
 
     # has to run within the commit transaction
     def apply(self, fn, oldVersion, newVersion):
 
+        class hashTuple(tuple):
+            def __contains__(self, name):
+                if isinstance(name, unicode):
+                    name = name.encode('utf-8')
+                hash = UUIDext.hash(name)
+                return super(hashTuple, self).__contains__(hash)
+
         try:
             cursor = self.cursor()
 
@@ -415,14 +438,22 @@
                 if version > newVersion:
                     break
 
-                if len(value[1]) == 24:
-                    docId, status, parentId = unpack('>li16s', value[1])
+                value = value[1]
+                status, = unpack('>l', value[0:4])
+                value = value[4:]
+
+                if status & Item.DELETED:
+                    docId, parentId = unpack('>l16s', value)
                     parentId = UUID(parentId)
+                    dirties = ()
                 else:
-                    docId, status = unpack('>li', value[1])
+                    docId, = unpack('>l', value[0:4])
                     parentId = None
+                    value = value[4:]
+                    dirties = unpack('>%dl' %(len(value) >> 2), value)
+                    dirties = hashTuple(dirties)
 
-                fn(UUID(uuid), version, docId, status, parentId)
+                fn(UUID(uuid), version, docId, status, parentId, dirties)
 
                 value = cursor.next()
 

Index: chandler/repository/persistence/RepositoryError.py
diff -u chandler/repository/persistence/RepositoryError.py:1.1 chandler/repository/persistence/RepositoryError.py:1.2
--- chandler/repository/persistence/RepositoryError.py:1.1	Mon Jun  7 23:59:08 2004
+++ chandler/repository/persistence/RepositoryError.py	Mon Aug 30 14:27:16 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.1 $"
-__date__      = "$Date: 2004/06/08 06:59:08 $"
+__revision__  = "$Revision: 1.2 $"
+__date__      = "$Date: 2004/08/30 21:27:16 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -11,10 +11,11 @@
 
 
 class VersionConflictError(RepositoryError):
-    "Another thread changed %s and saved those changes before this thread got a chance to do so. These changes conflict with this thread's changes, the item cannot be saved."
+    "Another view changed %s and saved those changes before this view - %s - got a chance to do so. These changes conflict with this thread's changes, the item cannot be saved (0x%0.4x/0x%0.4x)."
 
     def __str__(self):
-        return self.__doc__ %(self.args[0].itsPath)
+        return self.__doc__ %(self.args[0].itsPath, self.args[0].itsView,
+                              self.args[1], self.args[2])
 
     def getItem(self):
         return self.args[0]

Index: chandler/repository/item/Item.py
diff -u chandler/repository/item/Item.py:1.154 chandler/repository/item/Item.py:1.155
--- chandler/repository/item/Item.py:1.154	Sun Aug 29 19:50:28 2004
+++ chandler/repository/item/Item.py	Mon Aug 30 14:27:15 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.154 $"
-__date__      = "$Date: 2004/08/30 02:50:28 $"
+__revision__  = "$Revision: 1.155 $"
+__date__      = "$Date: 2004/08/30 21:27:15 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -1331,8 +1331,9 @@
             if attrDict is not None:
                 assert attribute is not None
                 assert attrDict is not None
+                attrDict._setDirty(attribute)
                 self._invokeMonitors(attribute, attrDict)
-
+                
             self._lastAccess = Item._countAccess()
             if self._status & Item.DIRTY == 0:
                 repository = self.getRepositoryView()

Index: chandler/repository/persistence/XMLRepository.py
diff -u chandler/repository/persistence/XMLRepository.py:1.78 chandler/repository/persistence/XMLRepository.py:1.79
--- chandler/repository/persistence/XMLRepository.py:1.78	Wed Jul 21 14:32:29 2004
+++ chandler/repository/persistence/XMLRepository.py	Mon Aug 30 14:27:16 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.78 $"
-__date__      = "$Date: 2004/07/21 21:32:29 $"
+__revision__  = "$Revision: 1.79 $"
+__date__      = "$Date: 2004/08/30 21:27:16 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -197,19 +197,11 @@
 
     def loadItem(self, version, uuid):
 
-        store = self.store
-        txnStarted = False
-        try:
-            txnStarted = store.startTransaction()
-            docId = store._versions.getDocId(uuid, version)
+        docId = self.store._versions.getDocId(uuid, version)
 
-            # None -> not found, 0 -> deleted
-            if docId: 
-                return self.getDocument(docId)
-
-        finally:
-            if txnStarted:
-                store.abortTransaction()
+        # None -> not found, 0 -> deleted
+        if docId: 
+            return self.getDocument(docId)
 
         return None
             
@@ -558,7 +550,8 @@
             self.repository._env.lock_put(lock)
         return None
 
-    def saveItem(self, xml, uuid, version, currPN, origPN, status):
+    def saveItem(self, xml, uuid, version, currPN, origPN, status,
+                 dirtyValues, dirtyRefs):
         
         doc = XmlDocument()
         doc.setContent(xml)
@@ -575,12 +568,14 @@
         if status & Item.DELETED:
             parent, name = origPN
             self._versions.setDocVersion(uuid, version, 0)
-            self._history.writeVersion(uuid, version, 0, status, parent)
+            self._history.writeVersion(uuid, version, 0, status,
+                                       parent, [], [])
             self.writeName(version, parent, name, None)
 
         else:
             self._versions.setDocVersion(uuid, version, docId)
-            self._history.writeVersion(uuid, version, docId, status)
+            self._history.writeVersion(uuid, version, docId, status,
+                                       None, dirtyValues, dirtyRefs)
 
             if origPN is not None:
                 parent, name = origPN

Index: chandler/repository/item/Values.py
diff -u chandler/repository/item/Values.py:1.15 chandler/repository/item/Values.py:1.16
--- chandler/repository/item/Values.py:1.15	Sun Aug 29 19:50:28 2004
+++ chandler/repository/item/Values.py	Mon Aug 30 14:27:15 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.15 $"
-__date__      = "$Date: 2004/08/30 02:50:28 $"
+__revision__  = "$Revision: 1.16 $"
+__date__      = "$Date: 2004/08/30 21:27:15 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -15,6 +15,15 @@
         super(Values, self).__init__()
         self._setItem(item)
 
+    def clear(self):
+
+        try:
+            del self._flags
+        except AttributeError:
+            pass
+
+        super(Values, self).clear()
+
     def _getItem(self):
 
         return self._item
@@ -68,9 +77,9 @@
 
     def _setFlag(self, key, flag):
 
-        if '_flags' in self.__dict__:
+        try:
             self._flags[key] = self._flags.get(key, 0) | flag
-        else:
+        except AttributeError:
             self._flags = { key: flag }
 
     def _clearFlag(self, key, flag):
@@ -81,17 +90,17 @@
 
     def _setFlags(self, key, flags):
 
-        if '_flags' in self.__dict__:
+        try:
             self._flags[key] = flags
-        else:
+        except AttributeError:
             self._flags = { key: flags }
 
     def _getFlags(self, key, default=0):
 
-        if '_flags' in self.__dict__:
+        try:
             return self._flags.get(key, default)
-
-        return default
+        except AttributeError:
+            return default
 
     def _isReadOnly(self, key):
 
@@ -113,13 +122,40 @@
 
         self._setFlag(key, Values.MONITORED)
 
+    def _setDirty(self, key):
+
+        self._setFlag(key, Values.DIRTY)
+
     def _clearTransient(self, key):
 
-        self._flags[key] &= ~Values.TRANSIENT
+        try:
+            self._flags[key] &= ~Values.TRANSIENT
+        except AttributeError:
+            pass
 
     def _clearMonitored(self, key):
 
-        self._flags[key] &= ~Values.MONITORED
+        try:
+            self._flags[key] &= ~Values.MONITORED
+        except AttributeError:
+            pass
+
+    def _getDirties(self):
+
+        try:
+            return [ key for key, flags in self._flags.iteritems()
+                     if flags & Values.DIRTY ]
+        except AttributeError:
+            return []
+
+    def _clearDirties(self):
+
+        try:
+            for key, flags in self._flags.iteritems():
+                if flags & Values.DIRTY:
+                    self._flags[key] &= ~Values.DIRTY
+        except AttributeError:
+            pass
 
     def _xmlValues(self, generator, withSchema, version, mode):
 
@@ -142,7 +178,8 @@
 
             if persist:
                 flags = self._getFlags(key)
-                persist = flags & self.TRANSIENT == 0
+                persist = flags & Values.TRANSIENT == 0
+                flags &= Values.SAVEMASK
 
             if persist:
                 if attribute is not None:
@@ -169,9 +206,11 @@
 
 
     READONLY  = 0x0001         # value is read-only
-    TRANSIENT = 0x0002         # value is transient
-    MONITORED = 0x0004         # value is monitored
-    
+    MONITORED = 0x0002         # value is monitored
+    DIRTY     = 0x0100         # value is dirty
+    TRANSIENT = 0x0200         # value is transient
+    SAVEMASK  = 0x00ff         # save these flags
+
 
 class References(Values):
 
@@ -206,7 +245,7 @@
 
         for key, value in self.iteritems():
             if item.getAttributeAspect(key, 'persist', default=True):
-                flags = self._getFlags(key)
+                flags = self._getFlags(key) & Values.SAVEMASK
                 attrs = {}
                 if flags:
                     attrs['flags'] = str(flags)

Index: chandler/repository/persistence/XMLRepositoryView.py
diff -u chandler/repository/persistence/XMLRepositoryView.py:1.50 chandler/repository/persistence/XMLRepositoryView.py:1.51
--- chandler/repository/persistence/XMLRepositoryView.py:1.50	Wed Aug 25 18:00:53 2004
+++ chandler/repository/persistence/XMLRepositoryView.py	Mon Aug 30 14:27:16 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.50 $"
-__date__      = "$Date: 2004/08/26 01:00:53 $"
+__revision__  = "$Revision: 1.51 $"
+__date__      = "$Date: 2004/08/30 21:27:16 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -197,15 +197,17 @@
                 if newVersion > self.version:
                     histNotifications = RepositoryNotifications()
                     
-                    def unload(uuid, version, docId, status, parent):
+                    def unload(uuid, version, docId, status, parent, dirties):
 
                         if status & Item.DELETED:
                             histNotifications.history(uuid, 'deleted',
                                                       parent=parent)
                         elif status & Item.NEW:
-                            histNotifications.history(uuid, 'added')
+                            histNotifications.history(uuid, 'added',
+                                                      dirties=dirties)
                         else:
-                            histNotifications.history(uuid, 'changed')
+                            histNotifications.history(uuid, 'changed',
+                                                      dirties=dirties)
 
                         item = self._registry.get(uuid)
                         if (item is not None and
@@ -247,6 +249,8 @@
                     item._version = newVersion
                 item._status &= ~(Item.NEW | Item.DIRTY |
                                   Item.MERGED | Item.SAVED)
+                item._values._clearDirties()
+                item._references._clearDirties()
             del self._log[:]
 
         if newVersion > self.version:
@@ -324,7 +328,9 @@
 
         store.saveItem(xml, uuid, newVersion,
                        (item.itsParent.itsUUID, item._name), origPN,
-                       item._status)
+                       item._status,
+                       item._values._getDirties(),
+                       item._references._getDirties())
 
         if item._status & item.ADIRTY:
             for name, acl in item._acls.iteritems():
@@ -341,13 +347,13 @@
 
     def _mergeItems(self, items, oldVersion, newVersion, history):
 
-        def check(uuid, version, docId, status, parentId):
+        def check(uuid, version, docId, status, parentId, dirties):
             item = items.get(uuid)
             if item is not None:
                 newDirty = item.getDirty()
                 oldDirty = status & item.DIRTY
                 if newDirty & oldDirty:
-                    raise VersionConflictError, item
+                    raise VersionConflictError, (item, newDirty, oldDirty)
                 else:
                     if (newDirty == item.VDIRTY or oldDirty == item.VDIRTY or
                         newDirty == item.RDIRTY or oldDirty == item.RDIRTY or



More information about the Commits mailing list