view service/store/store.py @ 1401:2ec49c958f91

rule updates Ignore-this: e0e881f196303f76ee6f8a4a16dfad44 darcs-hash:995049f78867de2cbcbe23a0b4a0b3e1afc42a52
author drewp <drewp@bigasterisk.com>
date Tue, 23 Jul 2019 10:15:33 -0700
parents 1431cd58803d
children 92cb3245fdd1
line wrap: on
line source

"""
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 ValuesResource(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)
    post = put
    def _onQueryStringStatement(self, s, p, body):
        subj = URIRef(s)
        pred = URIRef(p)
        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 = 10015
    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'/values', ValuesResource),
    ], masterGraph=masterGraph, dbFile=dbFile, debug=arg['-v']),
                      interface='::')
    log.warn('serving on %s', port)

    reactor.run()