comparison lib/patchablegraph.py @ 233:4ebb5cc30002

server/browser graph sync. cut dependency on the WS version. merge some changes between arduino/pi code. Ignore-this: cf7d20d54e134e8ff33a9ee405610846
author drewp@bigasterisk.com
date Sat, 30 Jan 2016 06:40:00 -0800
parents c1b98006f56e
children 8d89da1915df
comparison
equal deleted inserted replaced
232:4e91f3ec460b 233:4ebb5cc30002
8 4. The client that got the graph holds and maintains a copy. The 8 4. The client that got the graph holds and maintains a copy. The
9 client may merge together multiple graphs. 9 client may merge together multiple graphs.
10 5. Client queries its graph with low-level APIs or client-side sparql. 10 5. Client queries its graph with low-level APIs or client-side sparql.
11 6. When the graph changes, the client knows and can update itself at 11 6. When the graph changes, the client knows and can update itself at
12 low or high granularity. 12 low or high granularity.
13
14
15 See also:
16 * http://iswc2007.semanticweb.org/papers/533.pdf RDFSync: efficient remote synchronization of RDF
17 models
18 * https://www.w3.org/2009/12/rdf-ws/papers/ws07 Supporting Change Propagation in RDF
19 * https://www.w3.org/DesignIssues/lncs04/Diff.pdf Delta: an ontology for the distribution of
20 differences between RDF graphs
21
13 """ 22 """
14 import sys, json 23 import sys, json, logging
15 import cyclone.sse 24 import cyclone.sse
16 sys.path.append("/my/proj/light9") 25 sys.path.append("/my/proj/light9")
17 from light9.rdfdb.grapheditapi import GraphEditApi 26 from light9.rdfdb.grapheditapi import GraphEditApi
18 from rdflib import ConjunctiveGraph 27 from rdflib import ConjunctiveGraph
19 from light9.rdfdb.rdflibpatch import patchQuads 28 from light9.rdfdb.rdflibpatch import patchQuads
20 from rdflib_jsonld.serializer import from_rdf 29 from rdflib_jsonld.serializer import from_rdf
21 from cycloneerr import PrettyErrorHandler 30 from cycloneerr import PrettyErrorHandler
31
32 log = logging.getLogger('patchablegraph')
22 33
23 def writeGraphResponse(req, graph, acceptHeader): 34 def writeGraphResponse(req, graph, acceptHeader):
24 if acceptHeader == 'application/nquads': 35 if acceptHeader == 'application/nquads':
25 req.set_header('Content-type', 'application/nquads') 36 req.set_header('Content-type', 'application/nquads')
26 graph.serialize(req, format='nquads') 37 graph.serialize(req, format='nquads')
44 return json.dumps({'patch': { 55 return json.dumps({'patch': {
45 'adds': from_rdf(_graphFromQuads2(p.addQuads)), 56 'adds': from_rdf(_graphFromQuads2(p.addQuads)),
46 'deletes': from_rdf(_graphFromQuads2(p.delQuads)), 57 'deletes': from_rdf(_graphFromQuads2(p.delQuads)),
47 }}) 58 }})
48 59
60 def graphAsJson(g):
61 # This is not the same as g.serialize(format='json-ld')! That
62 # version omits literal datatypes.
63 return json.dumps(from_rdf(g))
64
49 class PatchableGraph(GraphEditApi): 65 class PatchableGraph(GraphEditApi):
50 """ 66 """
51 Master graph that you modify with self.patch, and we get the 67 Master graph that you modify with self.patch, and we get the
52 updates to all current listeners. 68 updates to all current listeners.
53 """ 69 """
66 addQuads=p.addQuads, 82 addQuads=p.addQuads,
67 perfect=False) # true? 83 perfect=False) # true?
68 for ob in self._observers: 84 for ob in self._observers:
69 ob(patchAsJson(p)) 85 ob(patchAsJson(p))
70 86
87 def asJsonLd(self):
88 return graphAsJson(self._graph)
89
71 def addObserver(self, onPatch): 90 def addObserver(self, onPatch):
72 self._observers.append(onPatch) 91 self._observers.append(onPatch)
73 92
74 def removeObserver(self, onPatch): 93 def removeObserver(self, onPatch):
75 try: 94 try:
100 def __init__(self, application, request, masterGraph): 119 def __init__(self, application, request, masterGraph):
101 cyclone.sse.SSEHandler.__init__(self, application, request) 120 cyclone.sse.SSEHandler.__init__(self, application, request)
102 self.masterGraph = masterGraph 121 self.masterGraph = masterGraph
103 122
104 def bind(self): 123 def bind(self):
105 self.sendEvent( 124 graphJson = self.masterGraph.asJsonLd()
106 message=self.masterGraph.serialize(None, format='json-ld', 125 log.debug("send fullGraph event: %s", graphJson)
107 indent=None), 126 self.sendEvent(message=graphJson, event='fullGraph')
108 event='fullGraph')
109 self.masterGraph.addObserver(self.onPatch) 127 self.masterGraph.addObserver(self.onPatch)
110 128
111 def onPatch(self, patchJson): 129 def onPatch(self, patchJson):
112 # throttle and combine patches here- ideally we could see how 130 # throttle and combine patches here- ideally we could see how
113 # long the latency to the client is to make a better rate choice 131 # long the latency to the client is to make a better rate choice