Patch for syncing using hashes



So currently if a data source doesn't have mtimes, the synchronization
pretty much just gives up.

I have attached a patch to compute whether both sides of the
synchronization have been modified. If they haven't been both modified
we can safely assume that the modified one is the one that we want. If
they have been both modified we retain the original behavior.

This is a fairly large change if something isn't quite right with the
patch let me know but this is my best guess from what's available (I
wish there was a better way to grab the sink uid from inside the compare
function).

The reason I would like to see this in trunk is that I am attempting to
implement synchronization for iPhone databases and there is no mtime
information available. Syncing without this fix is pretty frustrating.

-Matt


2008-09-26  Matt Colyer <>

    * conduit/MappingDB.py: Added a way to find a row based on two
objects and the sink.
    * conduit/datatypes/*.py: Forced get_hash to string values and
changed compare to use the hashes (when no mtimes present).
    * conduit/modules/EvolutionModule/EvolutionModule.py: Changed
compare to use new hash functionality. Fixed Note not return a proper
rid. 

diff --git a/conduit/MappingDB.py b/conduit/MappingDB.py
index 9c07edb..1bf6b91 100644
--- a/conduit/MappingDB.py
+++ b/conduit/MappingDB.py
@@ -94,6 +94,32 @@ class MappingDB:
             os.unlink(filename)
             self._open_db_and_check_structure(filename)
 
+    def get_mapping_from_objects(self, sourceDataLUID, sinkDataLUID, sinkUID):
+        
+        sql = "SELECT * FROM mappings WHERE sourceDataLUID = ? AND sinkDataLUID = ? AND sinkUID = ?"
+        res = self._db.select_one(sql, (sourceDataLUID, sinkDataLUID, sinkUID))
+        
+        #a mapping is always returned relative to the source -> sink
+        #order in which it was called.
+        if (res[5] == sinkUID):
+            m = Mapping(
+                    res[0],
+                    sourceUID=res[1],
+                    sourceRid=conduit.datatypes.Rid(res[2],res[3],res[4]),
+                    sinkUID=res[5],
+                    sinkRid=conduit.datatypes.Rid(res[6],res[7],res[8])
+                    )
+        else:
+            m = Mapping(
+                    res[0],
+                    sourceUID=res[5],
+                    sourceRid=conduit.datatypes.Rid(res[6],res[7],res[8]),
+                    sinkUID=res[1],
+                    sinkRid=conduit.datatypes.Rid(res[2],res[3],res[4])
+                    )
+
+        return m
+
     def get_mapping(self, sourceUID, dataLUID, sinkUID):
         """
         pass
diff --git a/conduit/datatypes/Bookmark.py b/conduit/datatypes/Bookmark.py
index b5b4467..65007c0 100644
--- a/conduit/datatypes/Bookmark.py
+++ b/conduit/datatypes/Bookmark.py
@@ -23,7 +23,7 @@ class Bookmark(DataType.DataType):
         return self.uri
         
     def get_hash(self):
-        return hash( (self.get_title(), self.get_uri()) )
+        return str(hash( (self.get_title(), self.get_uri()) ))
 
     def get_bookmark_string(self):
         return self.__str__()
diff --git a/conduit/datatypes/Contact.py b/conduit/datatypes/Contact.py
index c40d041..983025a 100644
--- a/conduit/datatypes/Contact.py
+++ b/conduit/datatypes/Contact.py
@@ -86,6 +86,6 @@ class Contact(DataType.DataType):
         return "Name: %s" % self.get_name()
         
     def get_hash(self):
-        return hash(self.get_vcard_string())
+        return str(hash(self.get_vcard_string()))
     
 
diff --git a/conduit/datatypes/DataType.py b/conduit/datatypes/DataType.py
index f4e35dc..1b9153d 100644
--- a/conduit/datatypes/DataType.py
+++ b/conduit/datatypes/DataType.py
@@ -39,7 +39,7 @@ class DataType(object):
         """
         return self._name_
 
-    def compare(self, B):
+    def compare(self, B, sinkUID=None):
         """
         Comparison function to be overridden by datatypes who support two
         way synchronisation. 
@@ -55,13 +55,20 @@ class DataType(object):
         """
         log.debug("COMPARE: %s <----> %s " % (self.get_UID(), B.get_UID()))
 
+        m = None
+        if sinkUID:
+            m = conduit.GLOBALS.mappingDB.get_mapping_from_objects(self.get_UID(), B.get_UID(), sinkUID)
+
         if self.get_rid() == B.get_rid():
             return conduit.datatypes.COMPARISON_EQUAL
 
         mtime1 = self.get_mtime()
         mtime2 = B.get_mtime()
 
-        if mtime1 == None or mtime2 == None:
+        # resolve conflicts with hashes if only one side has changed and mtimes are not useful
+        if (mtime1 == None or mtime2 == None) and m and m.get_sink_rid().get_hash() == B.get_hash():
+            return conduit.datatypes.COMPARISON_NEWER
+        elif mtime1 == None or mtime2 == None:
             return conduit.datatypes.COMPARISON_UNKNOWN
 
         if mtime1 > mtime2:
diff --git a/conduit/datatypes/Email.py b/conduit/datatypes/Email.py
index 79790fa..d7b695a 100644
--- a/conduit/datatypes/Email.py
+++ b/conduit/datatypes/Email.py
@@ -93,5 +93,5 @@ class Email(DataType.DataType):
         DataType.DataType.__setstate__(self, data)
 
     def get_hash(self):
-        return hash( self.get_email_string() )
+        return str(hash( self.get_email_string() ))
         
diff --git a/conduit/datatypes/Event.py b/conduit/datatypes/Event.py
index 6b2d53e..66d805a 100644
--- a/conduit/datatypes/Event.py
+++ b/conduit/datatypes/Event.py
@@ -26,4 +26,4 @@ class Event(DataType.DataType):
         DataType.DataType.__setstate__(self, data)
         
     def get_hash(self):
-        return hash(self.get_ical_string())
+        return str(hash(self.get_ical_string()))
diff --git a/conduit/datatypes/File.py b/conduit/datatypes/File.py
index 7cad661..f908b8e 100644
--- a/conduit/datatypes/File.py
+++ b/conduit/datatypes/File.py
@@ -314,7 +314,7 @@ class File(DataType.DataType):
         # they change.
         tagstr = "".join(self.get_tags())
         #FIXME: self.get_size() does not seem reliable
-        return hash(tagstr)
+        return str(hash(tagstr))
                        
     def get_filename(self):
         """
diff --git a/conduit/datatypes/Note.py b/conduit/datatypes/Note.py
index 93509ba..b4c01c6 100644
--- a/conduit/datatypes/Note.py
+++ b/conduit/datatypes/Note.py
@@ -21,7 +21,7 @@ class Note(DataType.DataType):
         return self.contents
         
     def get_hash(self):
-        return hash( (self.get_title(), self.get_contents()) )
+        return str(hash( (self.get_title(), self.get_contents()) ))
 
     def get_note_string(self):
         return self.__str__()
diff --git a/conduit/datatypes/Photo.py b/conduit/datatypes/Photo.py
index c26d2c9..5beca93 100644
--- a/conduit/datatypes/Photo.py
+++ b/conduit/datatypes/Photo.py
@@ -84,7 +84,7 @@ class Photo(File.File):
         file_hash = File.File.get_hash(self)       
         hash_data = "%s%s%s" % (file_hash, self.get_photo_size(),
                 self.get_caption())
-        return hash(hash_data)
+        return str(hash(hash_data))
         
     def __getstate__(self):
         data = File.File.__getstate__(self)
diff --git a/conduit/datatypes/Setting.py b/conduit/datatypes/Setting.py
index 2e9b720..d28f061 100644
--- a/conduit/datatypes/Setting.py
+++ b/conduit/datatypes/Setting.py
@@ -22,4 +22,4 @@ class Setting(DataType.DataType):
         DataType.DataType.__setstate__(self, data)
 
     def get_hash(self):
-        return hash( (self.key,self.value) )
+        return str(hash( (self.key,self.value) ))
diff --git a/conduit/datatypes/Text.py b/conduit/datatypes/Text.py
index 4fbae08..f31bdd9 100644
--- a/conduit/datatypes/Text.py
+++ b/conduit/datatypes/Text.py
@@ -30,5 +30,5 @@ class Text(DataType.DataType):
         DataType.DataType.__setstate__(self, data)
             
     def get_hash(self):
-        return hash(self.text)
+        return str(hash(self.text))
 
diff --git a/conduit/modules/EvolutionModule/EvolutionModule.py b/conduit/modules/EvolutionModule/EvolutionModule.py
index 95bfe0c..1612750 100644
--- a/conduit/modules/EvolutionModule/EvolutionModule.py
+++ b/conduit/modules/EvolutionModule/EvolutionModule.py
@@ -72,7 +72,7 @@ class EvoBase(DataProvider.TwoWay):
                     rid = self._update_object(LUID, obj)
                     return rid
                 else:
-                    comp = obj.compare(existing)
+                    comp = obj.compare(existing, "%s-%s" % (self.__class__.__name__, self.get_UID()))
                     # only update if newer
                     if comp != conduit.datatypes.COMPARISON_NEWER:
                         raise Exceptions.SynchronizeConflictError(comp, obj, existing)
@@ -352,7 +352,8 @@ class EvoMemoTwoWay(EvoBase):
         
         if uid != None:
             mtime = datetime.datetime.fromtimestamp(obj.get_modified())
-            return conduit.datatypes.Rid(uid=uid, mtime=mtime, hash=mtime)
+            note = self._get_object(uid)
+            return note.get_rid()
         else:
             raise Exceptions.SyncronizeError("Error creating memo")
 

Attachment: signature.asc
Description: This is a digitally signed message part



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]