Mercurial > code > home > repos > homeauto
changeset 712:d98c3ffe7144
new graph output for browsers, with autorefresh
Ignore-this: e4ff4dbed311b238d90988a1891ef640
author | drewp@bigasterisk.com |
---|---|
date | Mon, 03 Feb 2020 23:47:23 -0800 |
parents | e404420a2343 |
children | 02f876016f27 |
files | lib/patchablegraph/patchablegraph.py lib/patchablegraph/tasks.py |
diffstat | 2 files changed, 83 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/patchablegraph/patchablegraph.py Mon Feb 03 23:46:29 2020 -0800 +++ b/lib/patchablegraph/patchablegraph.py Mon Feb 03 23:47:23 2020 -0800 @@ -20,44 +20,21 @@ differences between RDF graphs """ -import json, logging, itertools +import json, logging, itertools, html from greplin import scales from rdfdb.grapheditapi import GraphEditApi from rdflib import ConjunctiveGraph +from rdflib.namespace import NamespaceManager from rdflib.parser import StringInputSource from rdflib_jsonld.serializer import from_rdf import cyclone.sse - from cycloneerr import PrettyErrorHandler from rdfdb.patch import Patch from rdfdb.rdflibpatch import patchQuads, inGraph log = logging.getLogger('patchablegraph') -def _writeGraphForBrowser(req, graph): - # We think this is a browser, so respond with a live graph view - # (todo) - req.set_header('Content-type', 'text/plain') - lines = graph.serialize(None, format='nquads').splitlines() - lines.sort() - req.write(b'\n'.join(lines)) - - -def _writeGraphResponse(req, graph, acceptHeader: str): - if acceptHeader == 'application/nquads': - req.set_header('Content-type', 'application/nquads') - graph.serialize(req, format='nquads') - elif acceptHeader == 'application/ld+json': - req.set_header('Content-type', 'application/ld+json') - graph.serialize(req, format='json-ld', indent=2) - else: - if acceptHeader.startswith('text/html'): - _writeGraphForBrowser(req, graph) - return - req.set_header('Content-type', 'application/x-trig') - graph.serialize(req, format='trig') - # forked from /my/proj/light9/light9/rdfdb/rdflibpatch.py def _graphFromQuads2(q): g = ConjunctiveGraph() @@ -146,14 +123,91 @@ _sendFullGraph = scales.PmfStat('serve/events/sendFull') _sendPatch = scales.PmfStat('serve/events/sendPatch') + class CycloneGraphHandler(PrettyErrorHandler, cyclone.web.RequestHandler): - def initialize(self, masterGraph): + def initialize(self, masterGraph: PatchableGraph): self.masterGraph = masterGraph def get(self): with self.masterGraph._sendSimpleGraph.time(): - _writeGraphResponse(self, self.masterGraph, - self.request.headers.get('accept', '')) + self._writeGraphResponse() + + def _writeGraphResponse(self): + acceptHeader = self.request.headers.get( + 'Accept', + # see https://github.com/fiorix/cyclone/issues/20 + self.request.headers.get('accept', '')) + + if acceptHeader == 'application/nquads': + self.set_header('Content-type', 'application/nquads') + self.masterGraph.serialize(self, format='nquads') + elif acceptHeader == 'application/ld+json': + self.set_header('Content-type', 'application/ld+json') + self.masterGraph.serialize(self, format='json-ld', indent=2) + else: + if acceptHeader.startswith('text/html'): + self._writeGraphForBrowser() + return + self.set_header('Content-type', 'application/x-trig') + self.masterGraph.serialize(self, format='trig') + + def _writeGraphForBrowser(self): + # We think this is a browser, so respond with a live graph view + # (todo) + self.set_header('Content-type', 'text/html') + + self.write(b''' + <html><body><pre>''') + + ns = NamespaceManager(self.masterGraph._graph) + # maybe these could be on the PatchableGraph instance + ns.bind('ex', 'http://example.com/') + ns.bind('', 'http://projects.bigasterisk.com/room/') + ns.bind("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#") + ns.bind("xsd", "http://www.w3.org/2001/XMLSchema#") + + for s, p, o, g in sorted(self.masterGraph._graph.quads()): + g = g.identifier + nquadLine = f'{s.n3(ns)} {p.n3(ns)} {o.n3(ns)} {g.n3(ns)} .\n' + self.write(html.escape(nquadLine).encode('utf8')) + + self.write(b''' + </pre> + <p> + <a href="#">[refresh]</a> + <label><input type="checkbox"> Auto-refresh</label> + </p> + <script> + + if (new URL(window.location).searchParams.get('autorefresh') == 'on') { + document.querySelector("input").checked = true; + setTimeout(() => { + requestAnimationFrame(() => { + window.location.replace(window.location.href); + }); + }, 2000); + } + + document.querySelector("a").addEventListener("click", (ev) => { + ev.preventDefault(); + window.location.replace(window.location.href); + + }); + document.querySelector("input").addEventListener("change", (ev) => { + if (document.querySelector("input").checked) { + const u = new URL(window.location); + u.searchParams.set('autorefresh', 'on'); + window.location.replace(u.href); + } else { + const u = new URL(window.location); + u.searchParams.delete('autorefresh'); + window.location.replace(u.href); + } + }); + + </script> + </body></html> + ''') class CycloneGraphEventsHandler(cyclone.sse.SSEHandler):
--- a/lib/patchablegraph/tasks.py Mon Feb 03 23:46:29 2020 -0800 +++ b/lib/patchablegraph/tasks.py Mon Feb 03 23:47:23 2020 -0800 @@ -20,5 +20,5 @@ f'--net=host ' f'-v `pwd`:/opt ' f'bang:5000/patchable_graph_browser_test ' - f'/bin/bash' #f'python3 browser_test.py', + f'/bin/bash', #f'python3 browser_test.py', pty=True)