Files @ 243afbd7a117
Branch filter:

Location: light9/bin/subserver - annotation

drewp@bigasterisk.com
tighter ui
Ignore-this: 7318243eb22e7cf19d6464610bd5a67f
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
dd896321faee
dd896321faee
dd896321faee
33a5a98e9bf1
dd896321faee
dd896321faee
33a5a98e9bf1
dd896321faee
dd896321faee
dd896321faee
bb4d1e9b30c1
473db8bebb8f
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
bb4d1e9b30c1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
6fd81bd4b4a7
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
448fe9f81501
33a5a98e9bf1
33a5a98e9bf1
448fe9f81501
891f2d75c686
891f2d75c686
33a5a98e9bf1
a4632a7b2e17
891f2d75c686
891f2d75c686
891f2d75c686
891f2d75c686
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
bb4d1e9b30c1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
cfc748f4ad2e
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
dd896321faee
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
e703b3434dbd
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
8b95d2865643
dd896321faee
8b95d2865643
8b95d2865643
8b95d2865643
8b95d2865643
dd896321faee
8b95d2865643
8b95d2865643
33a5a98e9bf1
8b95d2865643
891f2d75c686
891f2d75c686
891f2d75c686
891f2d75c686
891f2d75c686
891f2d75c686
891f2d75c686
891f2d75c686
891f2d75c686
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
bb4d1e9b30c1
33a5a98e9bf1
e703b3434dbd
33a5a98e9bf1
33a5a98e9bf1
448fe9f81501
dd896321faee
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
33a5a98e9bf1
#!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['Effect']):
            classes.append({'uri': e,
                            'label': graph.label(e),
                            'code': graph.value(e, L9['code'])})
        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 = networking.subServer.port
    reactor.listenTCP(port, cyclone.web.Application(handlers=[
        (r'/live', Live),
        (r'/effectsUpdates', EffectsUpdates),
        (r'/snapshot', Snapshot),
        (r'/(.*)', Static,
         {"path" : "light9/subserver",
          "default_filename" : "index.jade"}),
        ], debug=True, graph=graph, onNewClient=onNewClient))
    log.info("serving on %s" % port)
    reactor.run()