#!bin/python """ live web display of all existing subs with pictures, mainly for dragging them into CC """ from run_local import log import sys, optparse, logging, json, subprocess, datetime from dateutil.tz import tzlocal from twisted.internet import reactor, defer import cyclone.web, cyclone.httpclient, cyclone.websocket from rdflib import RDF, URIRef, Literal import pyjade.utils from light9.rdfdb.syncedgraph import SyncedGraph from light9.rdfdb.patch import Patch from light9.namespaces import L9, DCTERMS from light9 import networking, showconfig from lib.cycloneerr import PrettyErrorHandler liveClients = set() def sendToLiveClients(d=None, asJson=None): j = asJson or json.dumps(d) for c in liveClients: c.sendMessage(j) class Live(cyclone.websocket.WebSocketHandler): def connectionMade(self, *args, **kwargs): log.info("websocket opened") liveClients.add(self) self.settings.onNewClient() def connectionLost(self, reason): log.info("websocket closed") liveClients.remove(self) def messageReceived(self, message): log.info("got message %s" % message) self.sendMessage(message) class EffectsUpdates(cyclone.websocket.WebSocketHandler): def connectionMade(self, *args, **kwargs): self.connected = True self.settings.graph.addHandler(self.onGraphChange) def connectionLost(self, reason): self.connected = False def onGraphChange(self): if not self.connected: return graph = self.settings.graph classes = [] for e in graph.subjects(RDF.type, L9['EffectClass']): classes.append({'uri': e, 'label': graph.label(e), 'code': graph.value(e, L9['code'])}) print "sendMessage", classes self.sendMessage({'classes': classes}) class Static(PrettyErrorHandler, cyclone.web.StaticFileHandler): def get(self, path, *args, **kw): if path in ['', 'effects']: return self.respondStaticJade("light9/subserver/%s.jade" % (path or 'index')) if path.endswith(".js"): return self.responseStaticCoffee( 'light9/subserver/%s' % path.replace(".js", ".coffee")) # potential security hole cyclone.web.StaticFileHandler.get(self, path, *args, **kw) def respondStaticJade(self, src): html = pyjade.utils.process(open(src).read()) self.write(html) def responseStaticCoffee(self, src): self.write(subprocess.check_output([ '/usr/bin/coffee', '--compile', '--print', src])) class Snapshot(PrettyErrorHandler, cyclone.web.RequestHandler): @defer.inlineCallbacks def post(self): about = URIRef(self.get_argument("about")) response = yield cyclone.httpclient.fetch(networking.vidref.path("snapshot"), method="POST", timeout=1) snapUri = URIRef(json.loads(response.body)['snapshot']) # vidref could write about when it was taken, etc. would it be # better for us to tell vidref where to attach the result in # the graph, and then it doesn't even have to return anything? ctx = showconfig.showUri() + "/snapshots" self.settings.graph.patch(Patch(addQuads=[ (about, L9['image'], snapUri, ctx), (snapUri, DCTERMS['created'], Literal(datetime.datetime.now(tzlocal())), ctx), ])) self.write(json.dumps({'snapshot': snapUri})) def newestImage(subject): newest = (None, None) for img in graph.objects(subject, L9['image']): created = graph.value(img, DCTERMS['created']) if created > newest[0]: newest = (created, img) return newest[1] if __name__ == "__main__": parser = optparse.OptionParser() parser.add_option("-v", "--verbose", action="store_true", help="logging.DEBUG") (options, args) = parser.parse_args() log.setLevel(logging.DEBUG if options.verbose else logging.INFO) graph = SyncedGraph(networking.rdfdb.url, "subserver") d = {} def updateSubs(): subs = [] for sub in sorted(graph.subjects(RDF.type, L9['Submaster'])): rec = {'uri' : sub} rec['isLocal'] = graph.contains((sub, RDF.type, L9['LocalSubmaster'])) rec['label'] = graph.label(sub) rec['img'] = newestImage(sub) subs.append(rec) d.clear() d.update({'subs': subs}) d['chases'] = [] for chase in sorted(graph.subjects(RDF.type, L9['Chase'])): fakeLabel = chase.rsplit('/', 1)[-1] d['chases'].append({ 'uri': chase, 'label': fakeLabel, }) sendToLiveClients(d=d) def onNewClient(): sendToLiveClients(d=d) graph.addHandler(updateSubs) port = 8052 reactor.listenTCP(port, cyclone.web.Application(handlers=[ (r'/live', Live), (r'/effectsUpdates', EffectsUpdates), (r'/snapshot', Snapshot), (r'/static/(.*)', cyclone.web.StaticFileHandler, {'path': 'static/'}), (r'/(.*)', Static, {"path" : "light9/subserver", "default_filename" : "index.jade"}), ], debug=True, graph=graph, onNewClient=onNewClient)) log.info("serving on %s" % port) reactor.run()