changeset 812:5ff9d9e264bb

Patch.fromDiff and its usage in graphfile changes Ignore-this: 650eaba5a71a5e838d14af5110b04e34
author drewp@bigasterisk.com
date Sat, 29 Sep 2012 21:00:34 +0000
parents b19cd005a491
children 6f984ce851e2
files light9/rdfdb/graphfile.py light9/rdfdb/patch.py light9/rdfdb/rdflibpatch.py
diffstat 3 files changed, 111 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/light9/rdfdb/graphfile.py	Sat Sep 29 20:59:13 2012 +0000
+++ b/light9/rdfdb/graphfile.py	Sat Sep 29 21:00:34 2012 +0000
@@ -3,6 +3,7 @@
 from twisted.internet.inotify import IN_CLOSE_WRITE, IN_MOVED_FROM
 from rdflib import Graph
 from light9.rdfdb.patch import Patch
+from light9.rdfdb.rdflibpatch import inContext
 
 log = logging.getLogger()
 
@@ -40,17 +41,48 @@
             log.error("syntax error in %s" % self.path)
             return
 
-        adds = [(s, p, o, self.uri) for s, p, o in new - old]
-        dels = [(s, p, o, self.uri) for s, p, o in old - new]
+        old = inContext(old, self.uri)
+        new = inContext(new, self.uri)
+        print "old %s new %s" % (old, new)
+
+        p = Patch.fromDiff(old, new)
+        if p:
+            self.patch(p, dueToFileChange=True)
 
-        print "file dels"
-        for s  in dels:
-            print s
-        print "file adds"
-        for s in adds:
-            print s
-        print ""
+    def dirty(self, graph):
+        """
+        there are new contents for our file
+        
+        graph is the rdflib.Graph that contains the contents of the
+        file. It is allowed to change. Note that dirty() will probably
+        do the save later when the graph might be different.
+        
+        after a timer has passed, write it out. Any scheduling issues
+        between files? i don't think so. the timer might be kind of
+        huge, and then we might want to take a hint from a client that
+        it's a good time to save the files that it was editing, like
+        when the mouse moves out of the client's window and might be
+        going towards a text file editor
+        
+        """
+        log.info("%s dirty, %s stmt" % (self.uri, len(graph)))
 
+        self.graphToWrite = graph
+        if self.writeCall:
+            self.writeCall.reset(self.flushDelay)
+        else:
+            self.writeCall = reactor.callLater(self.flushDelay, self.flush)
+
+    def flush(self):
+        self.writeCall = None
+
+        tmpOut = self.path + ".rdfdb-temp"
+        f = open(tmpOut, 'w')
+        t1 = time.time()
+        self.graphToWrite.serialize(destination=f, format='n3')
+        serializeTime = time.time() - t1
+        f.close()
+        self.lastWriteTimestamp = os.path.getmtime(tmpOut)
+        os.rename(tmpOut, self.path)
+        iolog.info("rewrote %s in %.1f ms", self.path, serializeTime * 1000)
         
-        if adds or dels:
-            self.patch(Patch(addQuads=adds, delQuads=dels))
--- a/light9/rdfdb/patch.py	Sat Sep 29 20:59:13 2012 +0000
+++ b/light9/rdfdb/patch.py	Sat Sep 29 21:00:34 2012 +0000
@@ -1,5 +1,5 @@
 import json, unittest
-from rdflib import ConjunctiveGraph, URIRef
+from rdflib import ConjunctiveGraph, URIRef, URIRef as U
 from light9.rdfdb.rdflibpatch import graphFromNQuad, graphFromQuads, serializeQuad
 
 ALLSTMTS = (None, None, None)
@@ -28,6 +28,24 @@
             if 'senderUpdateUri' in body:
                 self.senderUpdateUri = body['senderUpdateUri']
 
+    @classmethod
+    def fromDiff(cls, oldGraph, newGraph):
+        """
+        make a patch that changes oldGraph to newGraph
+        """
+        old = set(oldGraph.quads(ALLSTMTS))
+        new = set(newGraph.quads(ALLSTMTS))
+        return cls(addQuads=list(new - old), delQuads=list(old - new))
+
+    def __nonzero__(self):
+        """
+        does this patch do anything to a graph?
+        """
+        if self._jsonRepr and self._jsonRepr.strip():
+            raise NotImplementedError()
+        return bool(self._addQuads or self._delQuads or
+                    self._addGraph or self._delGraph)
+
     @property
     def addQuads(self):
         if self._addQuads is None:
@@ -97,3 +115,44 @@
                 else:
                     adds.add(q)
         return Patch(delQuads=dels, addQuads=adds)
+
+    def getContext(self):
+        """assumes that all the edits are on the same context"""
+        ctx = None
+        for q in self.addQuads + self.delQuads:
+            if ctx is None:
+                ctx = q[3]
+
+            if ctx != q[3]:
+                raise ValueError("patch applies to multiple contexts, at least %r and %r" % (ctx, q[3]))
+        return ctx
+
+stmt1 = U('http://a'), U('http://b'), U('http://c'), U('http://ctx1')
+
+class TestPatchFromDiff(unittest.TestCase):
+    def testEmpty(self):
+        g = ConjunctiveGraph()
+        p = Patch.fromDiff(g, g)
+        self.assert_(not p)
+
+    def testNonEmpty(self):
+        g1 = ConjunctiveGraph()
+        g2 = graphFromQuads([stmt1])
+        p = Patch.fromDiff(g1, g2)
+        self.assert_(p)
+
+    def testNoticesAdds(self):
+        g1 = ConjunctiveGraph()
+        g2 = graphFromQuads([stmt1])
+        p = Patch.fromDiff(g1, g2)
+        self.assertEqual(p.addQuads, [stmt1])
+        self.assertEqual(p.delQuads, [])
+
+    def testNoticesDels(self):
+        g1 = graphFromQuads([stmt1])
+        g2 = ConjunctiveGraph()
+        p = Patch.fromDiff(g1, g2)
+        self.assertEqual(p.addQuads, [])
+        self.assertEqual(p.delQuads, [stmt1])
+        
+        
--- a/light9/rdfdb/rdflibpatch.py	Sat Sep 29 20:59:13 2012 +0000
+++ b/light9/rdfdb/rdflibpatch.py	Sat Sep 29 21:00:34 2012 +0000
@@ -67,6 +67,14 @@
                                 c.n3())
     return out
 
+def inContext(graph, newContext):
+    """
+    make a ConjunctiveGraph where all the triples in the given graph
+    are in newContext
+    """
+    return graphFromQuads([(s,p,o,newContext) for s,p,o in graph])
+    
+
 class TestGraphFromQuads(unittest.TestCase):
     nqOut = '<http://example.com/> <http://example.com/> <http://example.com/> <http://example.com/> .\n'
     def testSerializes(self):
@@ -82,7 +90,6 @@
         self.assertEqual(out.strip(), self.nqOut.strip())
         
 
-
 stmt1 = U('http://a'), U('http://b'), U('http://c'), U('http://ctx1')
 stmt2 = U('http://a'), U('http://b'), U('http://c'), U('http://ctx2')
 class TestPatchQuads(unittest.TestCase):