Mercurial > code > home > repos > homeauto
changeset 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 | a60155ded95f |
children | 91162a54553c |
files | service/store/Dockerfile service/store/db.nquads service/store/index.html service/store/makefile service/store/requirements.txt service/store/store.py |
diffstat | 6 files changed, 144 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/store/Dockerfile Sat Mar 16 18:22:57 2019 -0700 @@ -0,0 +1,12 @@ +FROM bang6:5000/base_x86 + +WORKDIR /opt + +COPY requirements.txt ./ +RUN pip install -r requirements.txt + +COPY *.py *.html ./ + +EXPOSE 11014 + +CMD [ "python", "./store.py" ]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/store/db.nquads Sat Mar 16 18:22:57 2019 -0700 @@ -0,0 +1,2 @@ +<http://e/1> <http://e/2> "3.5"^^<http://float> <http://projects.bigasterisk.com/room/stored> . +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/store/index.html Sat Mar 16 18:22:57 2019 -0700 @@ -0,0 +1,12 @@ +<!doctype html> +<html> + <head> + <title>store</title> + <meta charset="utf-8" /> + </head> + <body> + + table of statements, creation times, creators, which UI they used + + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/store/makefile Sat Mar 16 18:22:57 2019 -0700 @@ -0,0 +1,22 @@ +JOB=store +PORT=10014 + +TAG=bang6:5000/${JOB}_x86:latest + +push_x86: + docker push ${TAG} + +build_x86: + rm -rf tmp_ctx + mkdir -p tmp_ctx + cp -a Dockerfile ../../lib/*.py *.py *.txt *.html tmp_ctx + docker build --network=host -t ${TAG} tmp_ctx + rm -rf tmp_ctx + +build_image: build_x86 push_x86 + +shell: + docker run --rm -it --cap-add SYS_PTRACE --net=host $(TAG) /bin/sh + +local_run: build_x86 + docker run --rm -it --net=host -v `pwd`/index.html:/opt/index.html -v `pwd`:/opt/homeauto_store bang6:5000/store_x86:latest python ./store.py -v
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/store/requirements.txt Sat Mar 16 18:22:57 2019 -0700 @@ -0,0 +1,4 @@ +rdflib==4.2.2 +cyclone +https://projects.bigasterisk.com/rdfdb/rdfdb-0.6.0.tar.gz +rdflib-jsonld==0.3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/store/store.py Sat Mar 16 18:22:57 2019 -0700 @@ -0,0 +1,92 @@ +""" +persistent store of rdf statements, meant for stmts from users. + +API is not typical rdf: putting statments replaces existing (s,o) +matches so there can be only one object at a time. Putting the special +object :unset clears the statement. +""" + +import sys, logging +from docopt import docopt +from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler +from rdfdb.patch import Patch +from rdflib import Namespace, URIRef, Literal, Graph +from rdflib.parser import StringInputSource +from twisted.internet import reactor +from twisted.python.filepath import FilePath +import cyclone.web + +ROOM = Namespace('http://projects.bigasterisk.com/room/') + +logging.basicConfig() +log = logging.getLogger() + +CTX = ROOM['stored'] + +class OutputPage(cyclone.web.RequestHandler): + def put(self): + arg = self.request.arguments + if arg.get('s') and arg.get('p'): + self._onQueryStringStatement(arg['s'][-1], arg['p'][-1], self.request.body) + else: + self._onGraphBodyStatements(self.request.body, self.request.headers) + + def _onQueryStringStatement(self, s, p, body): + subj = URIRef(arg['s'][-1]) + pred = URIRef(arg['p'][-1]) + turtleLiteral = self.request.body + try: + obj = Literal(float(turtleLiteral)) + except ValueError: + obj = Literal(turtleLiteral) + self._onStatements([(subj, pred, obj)]) + + def _onGraphBodyStatements(self, body, headers): + # maybe quads only so we can track who made the input and from what interface? + # Or your input of triples gets wrapped in a new quad in here? + g = Graph() + g.parse(StringInputSource(body), format='nt') + if not g: + raise ValueError("expected graph body") + self._onStatements(list(g.triples((None, None, None)))) + + def _onStatements(self, stmts): + g = self.settings.masterGraph + for s, p, o in stmts: + patch = g.getObjectPatch(CTX, s, p, o) + if o == ROOM['unset']: + patch = Patch(delQuads=patch.delQuads) + g.patch(patch) + nquads = g.serialize(None, format='nquads') + self.settings.dbFile.setContent(nquads) + +if __name__ == '__main__': + arg = docopt(""" + Usage: store.py [options] + + -v Verbose + """) + log.setLevel(logging.WARN) + if arg['-v']: + from twisted.python import log as twlog + twlog.startLogging(sys.stdout) + log.setLevel(logging.DEBUG) + + masterGraph = PatchableGraph() + dbFile = FilePath('/opt/homeauto_store/db.nquads') + if dbFile.exists(): + masterGraph._graph.parse(dbFile.open(), format='nquads') + + port = 10014 + reactor.listenTCP(port, cyclone.web.Application([ + (r"/()", cyclone.web.StaticFileHandler, + {"path": ".", "default_filename": "index.html"}), + (r"/graph", CycloneGraphHandler, {'masterGraph': masterGraph}), + (r"/graph/events", CycloneGraphEventsHandler, + {'masterGraph': masterGraph}), + (r'/output', OutputPage), + ], masterGraph=masterGraph, dbFile=dbFile, debug=arg['-v']), + interface='::') + log.warn('serving on %s', port) + + reactor.run()