Mercurial > code > home > repos > homeauto
changeset 1412:302063bfb8ff
reasoning web page uses rdf/browse/graphView for inputs and outputs now
Ignore-this: 64b7275ee149f631b606320444a3478b
darcs-hash:1ec4d67abed3d43997fae5f10d4bc23ee1a6b2d5
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Wed, 24 Jul 2019 00:36:16 -0700 |
parents | 21d0cd98ef7a |
children | 14802ffc9e44 |
files | service/reasoning/actions.py service/reasoning/index.html service/reasoning/reasoning.py |
diffstat | 3 files changed, 244 insertions(+), 115 deletions(-) [+] |
line wrap: on
line diff
--- a/service/reasoning/actions.py Wed Jul 24 00:34:41 2019 -0700 +++ b/service/reasoning/actions.py Wed Jul 24 00:36:16 2019 -0700 @@ -19,13 +19,29 @@ self.payload = None self.foafAgent = None self.nextCall = None + self.lastErr = None self.numRequests = 0 + def report(self): + return { + 'url': self.url, + 'urlAbbrev': self.url + .replace('http%3A%2F%2Fprojects.bigasterisk.com%2Froom%2F', ':') + .replace('http://projects.bigasterisk.com/room/', ':') + .replace('.vpn-home.bigasterisk.com', '.vpn-home'), + 'payload': self.payload, + 'numRequests': self.numRequests, + 'lastChangeTime': round(self.lastChangeTime, 2), + 'lastErr': str(self.lastErr) if self.lastErr is not None else None, + } + def setPayload(self, payload, foafAgent): - if self.numRequests > 0 and (self.payload == payload or self.foafAgent == foafAgent): + if self.numRequests > 0 and (self.payload == payload and + self.foafAgent == foafAgent): return self.payload = payload self.foafAgent = foafAgent + self.lastChangeTime = time.time() self.makeRequest() def makeRequest(self): @@ -52,13 +68,13 @@ log.debug(" PUT %s ok", self.url) self.lastErr = None self.currentRequest = None - self.nextCall = reactor.callLater(3, self.makeRequest) + self.nextCall = reactor.callLater(30, self.makeRequest) def onError(self, err): self.lastErr = err log.debug(' PUT %s failed: %s', self.url, err) self.currentRequest = None - self.nextCall = reactor.callLater(5, self.makeRequest) + self.nextCall = reactor.callLater(50, self.makeRequest) class HttpPutOutputs(object): """these grow forever""" @@ -155,6 +171,7 @@ obj = deviceGraph.value(defaultDesc, ROOM['defaultObject']) defaultStmts.add((s, p, obj)) + log.debug('defaultStmts %s %s %s', s, p, obj) self._putDevices(deviceGraph, defaultStmts) def _oneShotPostActions(self, deviceGraph, inferred): @@ -242,3 +259,27 @@ def _put(self, url, payload, agent=None): assert isinstance(payload, bytes) self.putOutputs.put(url, payload, agent) + +import cyclone.sse + +class PutOutputsTable(cyclone.sse.SSEHandler): + def __init__(self, application, request): + cyclone.sse.SSEHandler.__init__(self, application, request) + self.actions = self.settings.reasoning.actions + + def bind(self, *args, **kwargs): + self.bound = True + self.loop() + + def unbind(self): + self.bound = False + + def loop(self): + if not self.bound: + return + + self.sendEvent(message=json.dumps({ + 'puts': [row.report() for _, row in + sorted(self.actions.putOutputs.state.items())], + }), event='update') + reactor.callLater(1, self.loop)
--- a/service/reasoning/index.html Wed Jul 24 00:34:41 2019 -0700 +++ b/service/reasoning/index.html Wed Jul 24 00:36:16 2019 -0700 @@ -2,143 +2,218 @@ <html> <head> <title>reasoning</title> - <script src="/lib/polymer/1.0.9/webcomponentsjs/webcomponents.js"></script> + <meta charset="utf-8" /> + <script src="/lib/polymer/1.0.9/webcomponentsjs/webcomponents.min.js"></script> + <script src="/lib/require/require-2.3.3.js"></script> + <script src="/rdf/common_paths_and_ns.js"></script> + <script> + window.NS = { + room: "http://projects.bigasterisk.com/room/", + dev: "http://projects.bigasterisk.com/device/", + dcterms: "http://purl.org/dc/terms/", + rdfs: "http://www.w3.org/2000/01/rdf-schema#", + map: "http://bigasterisk.com/map#", + rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + }; + </script> + <link rel="import" href="/rdf/streamed-graph.html"> + <link rel="import" href="/lib/polymer/1.0.9/polymer/polymer.html"> + + <meta name="mobile-web-app-capable" content="yes"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <style type="text/css" media="all"> /* <![CDATA[ */ + @import url('https://fonts.googleapis.com/css?family=Allerta|Dosis|Jura&display=swap'); body { - font-family: sans-serif; - font-size: 12px; + background: black; + color: white; + font-family: 'Allerta', sans-serif; + font-size: 12px; + } + a { + color: #b1b1fd; + text-shadow: 1px 1px 0px #0400ff94; + text-decoration-color: #00007714; + } + #subjectRequest { + width: 50em; + } + .pane > h2 { + background: #3f738a61; + border-top-left-radius: 23px; + border-top-right-radius: 23px; + border-top: 3px solid #2a4b58; + padding: 14px 0 5px 11px; + + margin-top: 10px; } pre { - font-family: sans-serif; - } - pre div { - border-bottom: 1px solid #ccc; + font-family: sans-serif; } - .pred { - background: #e7e6f8; - } - .obj { - background: #ccf + pre div { + border-bottom: 1px solid #ccc; } - .pane { - position: relative; - display: flex; - flex-direction: column; - } - .pane pre { - overflow: auto; - flex-grow: 1; - } - /* ]]> */ + + + + #out > section { background: #1d23314a; } + #out2 > section { background: #4222134a; } + /* ]]> */ </style> <link rel="import" href="/supdebug/bang/service-rows/main.html"> </head> - <body layout vertical fit> + <body> + + <link rel="import" href="/rdf/rdf-uri.html"> + + <h1>reasoning service</h1> - <div style="flex: 0 0 auto"> + <div class="pane"> <h2>Service</h2> <service-rows name-substrs="reasoning"></service-rows> </div> - - <div class="pane"> - <h2>Input</h2> - <div><input id="inputQ"></div> - <div style="max-height: 600px; margin-right: 30px; overflow: auto"> - <pre id="input"></pre> - </div> - </div> <div class="pane"> + <h2>Input</h2> + + <streamed-graph id="inGraph" url="/sse_collector/graph/home"></streamed-graph> + <div id="out"></div> + <script type="module" src="/rdf/streamed_graph_view.js"></script> + + </div> + + <div class="pane"> <h2>Rules</h2> - <div style="max-height: 300px; margin-right: 30px; overflow: auto"> + <div style=""> <pre id="rules"></pre> </div> + <label><input id="auto" type="checkbox"> auto refresh</label> + + <script src="//bigasterisk.com/lib/jquery-2.0.3.min.js"></script> + <script type="text/javascript"> + // <![CDATA[ + $(function () { + + function update() { + $.get("rules", function (txt) { + $("#rules").empty().text(txt); + }); + } + function loop() { + update(); + if ($("input#auto").is(":checked")) { + setTimeout(loop, 2000); + } + } + loop(); + $("input#auto").click(loop); + }); + // ]]> + </script> </div> <div class="pane"> <h2>Output</h2> - <div><input id="outputQ"></div> - <div style="max-height: 300px; margin-right: 30px; overflow: auto"> - <pre id="output"></pre> - </div> - </div> + + <streamed-graph id="outGraph" url="graph/output/events"></streamed-graph> + <div id="out2"></div> + + + <script type="module"> + import { render } from '/lib/lit-html/1.0.0/lit-html.js'; + import { graphView } from '/rdf/browse/graphView.js'; + + const sg = document.querySelector('#outGraph'); + + const out = document.querySelector('#out2'); + const startPainting = () => { + if (!sg.graph || !sg.graph.graph) { + setTimeout(startPainting, 100); + return; + } + + let dirty = true; - <div> - <label><input id="auto" type="checkbox"> auto refresh</label> + const repaint = () => { + if (!dirty) { + return; + } + render(graphView(sg.graph.graph), out); + dirty = false; + }; + + sg.addEventListener('graph-changed', (ev) => { + dirty = true; + requestAnimationFrame(repaint); + }); + repaint(); + }; + setTimeout(startPainting, 10); + + </script> + + </div> - <script src="//bigasterisk.com/lib/jquery-2.0.3.min.js"></script> - <script> - window.NS = { - room: "http://projects.bigasterisk.com/room/", - dev: "http://projects.bigasterisk.com/device/", - dcterms: "http://purl.org/dc/terms/", - rdfs: "http://www.w3.org/2000/01/rdf-schema#", - map: "http://bigasterisk.com/map#", - rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - }; - </script> - <link rel="import" href="/rdf/rdf-uri.html"> - <script type="text/javascript"> - // <![CDATA[ - $(function () { + <div class="pane"> - function makeAddStmts(elem, q) { - return function (stmts) { - elem.empty(); - $.each(stmts, function (i, stmt) { - var s = BigastUri.compactUri(stmt[0]); - var p = BigastUri.compactUri(stmt[1]); - var o = BigastUri.compactUri(stmt[2]); - - q = q.toLowerCase() - if (q && - s.toLowerCase().indexOf(q) == -1 && - p.toLowerCase().indexOf(q) == -1 && - o.toLowerCase().indexOf(q) == -1) { - return; + <h2>put outputs</h2> + <style> + .recent2 { background: #ffff55; } + .recent10 { background: #ffff88; } + .recent60 { background: #ffffdd; } + #putOutputs th, #putOutputs td { text-align: left; padding-left: 5px; } + </style> + <table id="putOutputs"> + <thead> + <tr> + <th>url</th> + <th>numReq</th> + <th>changed</th> + <th>payload</th> + <th>lastErr</th> + </tr> + </thead> + <tbody id="putRows"> + </tbody> + </table> + <script> + window.addEventListener('load', () => { + const es = new EventSource('putOutputs'); + es.addEventListener('update', (ev) => { + const rows = document.querySelector('#putRows'); + rows.innerHTML = ''; + JSON.parse(ev.data).puts.forEach((row) => { + const tr = document.createElement('tr'); + for (let attr of [ + 'urlAbbrev', + 'numRequests', + 'lastChangeTime', + 'payload', + 'lastErr', + ]) { + const td = document.createElement('td'); + let value = row[attr]; + if (attr == 'lastChangeTime') { + const secAgo = Math.round(Date.now() / 1000 - row.lastChangeTime); + value = `-${secAgo} sec`; + if (secAgo < 2) { tr.classList.add('recent2'); } + else if (secAgo < 10) { tr.classList.add('recent10'); } + else if (secAgo < 60) { tr.classList.add('recent60'); } + } + td.innerText = value; + tr.appendChild(td); } - - elem.append( - $("<div>") - .append($("<span>").text(s)) - .append(" ") - .append($("<span>").addClass("pred").text(p)) - .append(" ") - .append($("<span>").addClass("obj").text(o))); - }) - } - } + rows.appendChild(tr); + }); + }); + }); + </script> + </div> - function updateIn() { - $.get("lastInputGraph", makeAddStmts($("#input"), $("#inputQ").val())); - } - function updateOut() { - $.get("lastOutputGraph", makeAddStmts($("#output"), $("#outputQ").val())); - } - - function update() { - updateIn(); - updateOut(); - $.get("rules", function (txt) { - $("#rules").empty().text(txt); - }); - } - $("#inputQ").on('keyup', updateIn); - function loop() { - update(); - if ($("input#auto").is(":checked")) { - setTimeout(loop, 2000); - } - } - loop(); - $("input#auto").click(loop); - }); - // ]]> - </script> </body> </html>
--- a/service/reasoning/reasoning.py Wed Jul 24 00:34:41 2019 -0700 +++ b/service/reasoning/reasoning.py Wed Jul 24 00:36:16 2019 -0700 @@ -31,11 +31,12 @@ from greplin.scales.cyclonehandler import StatsHandler from inference import infer, readRules -from actions import Actions +from actions import Actions, PutOutputsTable from inputgraph import InputGraph from escapeoutputstatements import unquoteOutputStatements - + from standardservice.logsetup import log, verboseLogging +from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler ROOM = Namespace("http://projects.bigasterisk.com/room/") @@ -56,8 +57,9 @@ self.rulesN3 = "(not read yet)" self.inferred = Graph() # gets replaced in each graphChanged call + self.outputGraph = PatchableGraph() # copy of inferred, for now - self.inputGraph = InputGraph([], self.graphChanged) + self.inputGraph = InputGraph([], self.graphChanged) self.inputGraph.updateFileData() @STATS.updateRules.time() @@ -81,6 +83,7 @@ self.inferred = Graph() self.inferred.add((ROOM['reasoner'], ROOM['ruleParseError'], Literal(traceback.format_exc()))) + self.copyOutput() raise return [(ROOM['reasoner'], ROOM['ruleParseTime'], Literal(ruleParseTime))], ruleParseTime @@ -127,6 +130,11 @@ ruleParseSec * 1000, inferSec * 1000, putResultsTime * 1000)) + if not oneShot: + self.copyOutput() + + def copyOutput(self): + self.outputGraph.setToGraph((s,p,o,ROOM['inferred']) for s,p,o in self.inferred) def _makeInferred(self, inputGraph): t1 = time.time() @@ -141,7 +149,7 @@ return out, inferenceTime - + class Index(cyclone.web.RequestHandler): def get(self): self.set_header("Content-Type", "text/html") @@ -185,7 +193,7 @@ traceback.print_exc() log.error(e) raise - + # for reuse class GraphResource(cyclone.web.RequestHandler): def get(self, which): @@ -257,8 +265,13 @@ (r"/", Index), (r"/immediateUpdate", ImmediateUpdate), (r"/oneShot", OneShot), + (r'/putOutputs', PutOutputsTable), (r'/(jquery.min.js)', Static), (r'/(lastInput|lastOutput)Graph', GraphResource), + + (r"/graph/output", CycloneGraphHandler, {'masterGraph': reasoning.outputGraph}), + (r"/graph/output/events", CycloneGraphEventsHandler, {'masterGraph': reasoning.outputGraph}), + (r'/ntGraphs', NtGraphs), (r'/rules', Rules), (r'/status', Status), @@ -269,7 +282,7 @@ def configLogging(arg): log.setLevel(WARN) - + if arg['-i'] or arg['-r'] or arg['-o'] or arg['-v']: log.handlers[0].setFormatter(ColoredFormatter("%(log_color)s%(levelname)-8s %(name)-6s %(filename)-12s:%(lineno)-3s %(funcName)-20s%(reset)s %(white)s%(message)s", datefmt=None,