411
|
1 """
|
|
2 persistent store of rdf statements, meant for stmts from users.
|
|
3
|
|
4 API is not typical rdf: putting statments replaces existing (s,o)
|
|
5 matches so there can be only one object at a time. Putting the special
|
|
6 object :unset clears the statement.
|
|
7 """
|
|
8
|
|
9 import sys, logging
|
|
10 from docopt import docopt
|
|
11 from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler
|
|
12 from rdfdb.patch import Patch
|
|
13 from rdflib import Namespace, URIRef, Literal, Graph
|
|
14 from rdflib.parser import StringInputSource
|
|
15 from twisted.internet import reactor
|
|
16 from twisted.python.filepath import FilePath
|
|
17 import cyclone.web
|
|
18
|
|
19 ROOM = Namespace('http://projects.bigasterisk.com/room/')
|
|
20
|
|
21 logging.basicConfig()
|
|
22 log = logging.getLogger()
|
|
23
|
|
24 CTX = ROOM['stored']
|
|
25
|
|
26 class OutputPage(cyclone.web.RequestHandler):
|
|
27 def put(self):
|
|
28 arg = self.request.arguments
|
|
29 if arg.get('s') and arg.get('p'):
|
|
30 self._onQueryStringStatement(arg['s'][-1], arg['p'][-1], self.request.body)
|
|
31 else:
|
|
32 self._onGraphBodyStatements(self.request.body, self.request.headers)
|
|
33
|
|
34 def _onQueryStringStatement(self, s, p, body):
|
|
35 subj = URIRef(arg['s'][-1])
|
|
36 pred = URIRef(arg['p'][-1])
|
|
37 turtleLiteral = self.request.body
|
|
38 try:
|
|
39 obj = Literal(float(turtleLiteral))
|
|
40 except ValueError:
|
|
41 obj = Literal(turtleLiteral)
|
|
42 self._onStatements([(subj, pred, obj)])
|
|
43
|
|
44 def _onGraphBodyStatements(self, body, headers):
|
|
45 # maybe quads only so we can track who made the input and from what interface?
|
|
46 # Or your input of triples gets wrapped in a new quad in here?
|
|
47 g = Graph()
|
|
48 g.parse(StringInputSource(body), format='nt')
|
|
49 if not g:
|
|
50 raise ValueError("expected graph body")
|
|
51 self._onStatements(list(g.triples((None, None, None))))
|
|
52
|
|
53 def _onStatements(self, stmts):
|
|
54 g = self.settings.masterGraph
|
|
55 for s, p, o in stmts:
|
|
56 patch = g.getObjectPatch(CTX, s, p, o)
|
|
57 if o == ROOM['unset']:
|
|
58 patch = Patch(delQuads=patch.delQuads)
|
|
59 g.patch(patch)
|
|
60 nquads = g.serialize(None, format='nquads')
|
|
61 self.settings.dbFile.setContent(nquads)
|
|
62
|
|
63 if __name__ == '__main__':
|
|
64 arg = docopt("""
|
|
65 Usage: store.py [options]
|
|
66
|
|
67 -v Verbose
|
|
68 """)
|
|
69 log.setLevel(logging.WARN)
|
|
70 if arg['-v']:
|
|
71 from twisted.python import log as twlog
|
|
72 twlog.startLogging(sys.stdout)
|
|
73 log.setLevel(logging.DEBUG)
|
|
74
|
|
75 masterGraph = PatchableGraph()
|
|
76 dbFile = FilePath('/opt/homeauto_store/db.nquads')
|
|
77 if dbFile.exists():
|
|
78 masterGraph._graph.parse(dbFile.open(), format='nquads')
|
|
79
|
|
80 port = 10014
|
|
81 reactor.listenTCP(port, cyclone.web.Application([
|
|
82 (r"/()", cyclone.web.StaticFileHandler,
|
|
83 {"path": ".", "default_filename": "index.html"}),
|
|
84 (r"/graph", CycloneGraphHandler, {'masterGraph': masterGraph}),
|
|
85 (r"/graph/events", CycloneGraphEventsHandler,
|
|
86 {'masterGraph': masterGraph}),
|
|
87 (r'/output', OutputPage),
|
|
88 ], masterGraph=masterGraph, dbFile=dbFile, debug=arg['-v']),
|
|
89 interface='::')
|
|
90 log.warn('serving on %s', port)
|
|
91
|
|
92 reactor.run()
|