Mercurial > code > home > repos > homeauto
comparison service/store/store.py @ 411:9fbd2d0193bf
new 'store' service for user inputs
Ignore-this: 7b14beca506dd7f1e38b5214aae1833a
author | drewp@bigasterisk.com |
---|---|
date | Sat, 16 Mar 2019 18:22:57 -0700 |
parents | |
children | 4c68b604c7ec |
comparison
equal
deleted
inserted
replaced
410:a60155ded95f | 411:9fbd2d0193bf |
---|---|
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() |