Changeset - 77eaccba654e
[Not reviewed]
default
0 6 0
Drew Perttula - 8 years ago 2017-06-10 07:17:20
drewp@bigasterisk.com
try to fix timeline sending empty ctx to rdfdb
Ignore-this: f778183e90f073b9914d9b6736560d99
6 files changed with 33 insertions and 14 deletions:
0 comments (0 inline, 0 general)
light9/rdfdb/rdflibpatch.py
Show inline comments
 
@@ -61,24 +61,25 @@ def inGraph(spoc, graph):
 

	
 
# some of the following workarounds may be fixed in https://github.com/RDFLib/rdflib/issues/299
 
def graphFromQuads(q):
 
    g = ConjunctiveGraph()
 
    #g.addN(q) # no effect on nquad output
 
    for s,p,o,c in q:
 
        #g.get_context(c).add((s,p,o)) # kind of works with broken rdflib nquad serializer code; you need this for json_ld serialize to work :(
 
        g.store.add((s,p,o), c) # no effect on nquad output
 
    return g
 

	
 
def graphFromNQuad(text):
 
    g1 = ConjunctiveGraph()
 
    # text might omit ctx on some lines. rdflib just puts in a bnode, which shows up later.
 
    g1.parse(data=text, format='nquads')
 
    return g1
 

	
 
from rdflib.plugins.serializers.nt import _quoteLiteral
 
def serializeQuad(g):
 
    """
 
    replacement for graph.serialize(format='nquads')
 

	
 
    Still broken in rdflib 4.2.2: graph.serialize(format='nquads')
 
    returns empty string for my graph in
 
    TestGraphFromQuads.testSerializes.
 
    """
light9/web/effects/effects.coffee
Show inline comments
 
Polymer
 
  is: "light9-effects"
 
  properties: 
 
    graph: {type: Object}
 
    effectClasses: { type: Array }
 
  ready: ->
 
    @graph.runHandler(@getClasses.bind(@))
 
    @graph.runHandler(@getClasses.bind(@), 'getClasses')
 

	
 
  getClasses: ->
 
    U = (x) => @graph.Uri(x)
 
    @effectClasses = _.sortBy(@graph.subjects(U('rdf:type'), U(':Effect')))
 

	
 
Polymer
 
  is: "light9-effect-class"
 
  properties: 
 
    graph: {type: Object}
 
    uri: {type: String}
 
    
 
  onAdd: ->
light9/web/graph.coffee
Show inline comments
 
@@ -62,24 +62,27 @@ class Handler
 
  constructor: (@func, @label) ->
 
    @patterns = [] # s,p,o,g quads that should trigger the next run
 
    @innerHandlers = [] # Handlers requested while this one was running
 
  
 
class AutoDependencies
 
  constructor: () ->
 
    @handlers = new Handler(null) # tree of all known Handlers (at least those with non-empty patterns). Top node is not a handler.
 
    @handlerStack = [@handlers] # currently running
 
    
 
  runHandler: (func, label) ->
 
    # what if we have this func already? duplicate is safe?
 

	
 
    if not label?
 
      throw new Error("missing label")
 

	
 
    h = new Handler(func, label)
 
    @handlerStack[@handlerStack.length - 1].innerHandlers.push(h)
 
    console.time("handler #{label}")
 
    @_rerunHandler(h, null)
 
    console.timeEnd("handler #{label}")
 
    
 
  _rerunHandler: (handler, patch) ->
 
    handler.patterns = []
 
    @handlerStack.push(handler)
 
    try
 
      handler.func(patch)
 
    catch e
 
@@ -118,37 +121,41 @@ class window.SyncedGraph
 
  # 
 
  # Note that _applyPatch is the only method to write to the graph, so
 
  # it can fire subscriptions.
 

	
 
  constructor: (@patchSenderUrl, @prefixes, @setStatus) ->
 
    # patchSenderUrl is the /syncedGraph path of an rdfdb server.
 
    # prefixes can be used in Uri(curie) calls.
 
    @_watchers = new GraphWatchers() # old
 
    @_autoDeps = new AutoDependencies() # replaces GraphWatchers
 
    @clearGraph()
 

	
 
    if @patchSenderUrl
 
      @_client = new RdfDbClient(@patchSenderUrl, @clearGraph.bind(@),
 
      @_client = new RdfDbClient(@patchSenderUrl, @_clearGraphOnNewConnection.bind(@),
 
                                 @_applyPatch.bind(@), @setStatus)
 
    
 
  clearGraph: -> # for debugging
 
  clearGraph: ->
 
    # just deletes the statements; watchers are unaffected.
 
    if @graph?
 
      @_applyPatch({addQuads: [], delQuads: @graph.find()})
 

	
 
    # if we had a Store already, this lets N3.Store free all its indices/etc
 
    @graph = N3.Store()
 
    @_addPrefixes(@prefixes)
 
    @cachedFloatValues = new Map();
 
    
 

	
 
  _clearGraphOnNewConnection: -> # must not send a patch to the server!
 
    log('graph: clearGraphOnNewConnection')
 
    @clearGraph()
 
    log('graph: clearGraphOnNewConnection done')
 
      
 
  _addPrefixes: (prefixes) ->
 
    @graph.addPrefixes(prefixes)
 
        
 
  Uri: (curie) ->
 
    N3.Util.expandPrefixedName(curie, @graph._prefixes)
 

	
 
  Literal: (jsValue) ->
 
    N3.Util.createLiteral(jsValue)
 

	
 
  LiteralRoundedFloat: (f) ->
 
    N3.Util.createLiteral(d3.format(".3f")(f),
 
@@ -166,24 +173,31 @@ class window.SyncedGraph
 
                    patch.addQuads.push(quad)
 
                  else
 
                    @_applyPatch(patch)
 
                    @_addPrefixes(prefixes)
 
                    cb() if cb
 
                    
 
  quads: () -> # for debugging
 
    [q.subject, q.predicate, q.object, q.graph] for q in @graph.find()
 

	
 
  applyAndSendPatch: (patch) ->
 
    if !Array.isArray(patch.addQuads) || !Array.isArray(patch.delQuads)
 
      throw new Error("corrupt patch: #{patch}")
 

	
 
    for qs in [patch.addQuads, patch.delQuads]
 
      for q in qs
 
        if not q.graph?
 
          throw new Error("corrupt patch: #{q}")
 

	
 
    log('graph: patch', patch)
 
    @_applyPatch(patch)
 
    @_client.sendPatch(patch) if @_client
 

	
 
  _applyPatch: (patch) ->
 
    # In most cases you want applyAndSendPatch.
 
    # 
 
    # This is the only method that writes to @graph!
 
    @cachedFloatValues.clear()
 
    for quad in patch.delQuads
 
      @graph.removeTriple(quad)
 
    for quad in patch.addQuads
 
      @graph.addTriple(quad)
light9/web/live/live.coffee
Show inline comments
 
@@ -148,25 +148,25 @@ Polymer
 
          @graph.LiteralRoundedFloat(row[2])
 
        else
 
          @graph.Literal(row[2])
 
      settingType = if row[1] in scaledAttributeTypes then U(':scaledValue') else U(':value')
 
      addQuads.push(quad(setting, settingType, value))
 
      
 
    patch = {addQuads: addQuads, delQuads: []}
 
    log('save', patch)
 
    @graph.applyAndSendPatch(patch)
 
    @newEffectName = ''
 

	
 
  onGraph: ->
 
    @graph.runHandler(@update.bind(@))
 
    @graph.runHandler(@update.bind(@), 'controls')
 
  resendAll: ->
 
    for llc in @getElementsByTagName("light9-live-control")
 
      llc.resend()
 
  clearAll: ->
 
    for llc in @getElementsByTagName("light9-live-control")
 
      llc.clear()
 
    
 
  update: ->
 
    U = (x) => @graph.Uri(x)
 

	
 
    @set('devices', [])
 
    for dc in _.sortBy(@graph.subjects(U('rdf:type'), U(':DeviceClass')))
light9/web/rdfdbclient.coffee
Show inline comments
 
@@ -43,72 +43,72 @@ parseJsonPatch = (jsonPatch, cb) ->
 
  parseDels = (cb) =>
 
    parser = N3.Parser()
 
    parser.parse input.patch.deletes, (error, quad, prefixes) =>
 
                  if (quad)
 
                    patch.delQuads.push(quad)
 
                  else
 
                    cb()
 

	
 
  async.parallel([parseAdds, parseDels], ((err) => cb(patch)))
 

	
 
class window.RdfDbClient
 
  # Send and receive patches from rdfdb
 
  constructor: (@patchSenderUrl, @clearGraph, @applyPatch, @setStatus) ->
 
  constructor: (@patchSenderUrl, @clearGraphOnNewConnection, @applyPatch, @setStatus) ->
 
    @_patchesToSend = []
 
    @_lastPingMs = -1
 
    @_patchesReceived = 0
 
    @_patchesSent = 0
 

	
 
    @_reconnectionTimeout = null
 
    @_newConnection()
 

	
 
  _updateStatus: ->
 
    ws = (if not @ws? then 'no' else switch @ws.readyState
 
      when @ws.CONNECTING then 'connecting'
 
      when @ws.OPEN then 'open'
 
      when @ws.CLOSING then 'closing'
 
      when @ws.CLOSED then 'close'
 
      )
 

	
 
    ping = if @_lastPingMs > 0 then @_lastPingMs else '...'
 
    @setStatus("#{ws};
 
      #{@_patchesReceived} recv;
 
      #{@_patchesSent} sent;
 
      #{@_patchesToSend.length} pending;
 
      #{ping}ms")
 
 
 
  sendPatch: (patch) ->
 
    console.log('queue patch to server ', patchSizeSummary(patch))
 
    console.log('rdfdbclient: queue patch to server ', patchSizeSummary(patch))
 
    @_patchesToSend.push(patch)
 
    @_updateStatus()
 
    @_continueSending()           
 

	
 
  _newConnection: ->
 
    wsOrWss = window.location.protocol.replace('http', 'ws')
 
    fullUrl = wsOrWss + '//' + window.location.host + @patchSenderUrl
 
    @ws.close() if @ws?
 
    @ws = new WebSocket(fullUrl)
 

	
 
    @ws.onopen = =>
 
      log('connected to', fullUrl)
 
      log('rdfdbclient: connected to', fullUrl)
 
      @_updateStatus()
 
      @clearGraph()
 
      @clearGraphOnNewConnection()
 
      @_pingLoop()
 

	
 
    @ws.onerror = (e) =>
 
      log('ws error ' + e)
 
      log('rdfdbclient: ws error ' + e)
 
      @ws.onclose()
 

	
 
    @ws.onclose = =>
 
      log('ws close')
 
      log('rdfdbclient: ws close')
 
      @_updateStatus()
 
      clearTimeout(@_reconnectionTimeout) if @_reconnectionTimeout?
 
      @_reconnectionTimeout = setTimeout(@_newConnection.bind(@), 1000)
 

	
 
    @ws.onmessage = @_onMessage.bind(@)
 

	
 
  _pingLoop: () ->
 
    if @ws.readyState == @ws.OPEN
 
      @ws.send('PING')
 
      @_lastPingMs = -Date.now()
 
      
 
      clearTimeout(@_pingLoopTimeout) if @_pingLoopTimeout?
 
@@ -125,23 +125,23 @@ class window.RdfDbClient
 
    @_updateStatus()
 

	
 
  _continueSending: ->
 
    if @ws.readyState != @ws.OPEN
 
      setTimeout(@_continueSending.bind(@), 500)
 
      return
 

	
 
    # we could call this less often and coalesce patches together to optimize
 
    # the dragging cases.
 

	
 
    sendOne = (patch, cb) =>
 
        toJsonPatch(patch, (json) =>
 
          log('send patch to server, ' + json.length + ' bytes')
 
          log('rdfdbclient: send patch to server, ' + json.length + ' bytes')
 
          @ws.send(json)
 
          @_patchesSent++
 
          @_updateStatus()
 
          cb(null)
 
      )
 

	
 
    async.eachSeries(@_patchesToSend, sendOne, () =>
 
        @_patchesToSend = []
 
        @_updateStatus()
 
      )
light9/web/timeline/timeline.coffee
Show inline comments
 
@@ -276,25 +276,25 @@ Polymer
 
      
 

	
 
Polymer
 
  is: 'light9-timeline-time-zoomed'
 
  behaviors: [ Polymer.IronResizableBehavior ]
 
  properties:
 
    graph: { type: Object, notify: true }
 
    selection: { type: Object, notify: true }
 
    dia: { type: Object, notify: true }
 
    song: { type: String, notify: true }
 
    zoomInX: { type: Object, notify: true }
 
    rows: { value: [0...ROW_COUNT] }
 
    zoom: { type: Object, notify: true, observer: 'onZoom' }
 
    zoom: { type: Object, notify: true, observer: 'onZoom' } # viewState.zoomSpec
 
    zoomFlattened: { type: Object, notify: true }
 
  onZoom: ->
 
    updateZoomFlattened = ->
 
      log('updateZoomFlattened')
 
      @zoomFlattened = ko.toJS(@zoom)
 
    ko.computed(updateZoomFlattened.bind(@))
 
  ready: ->
 

	
 
  attached: ->
 
    root = @closest('light9-timeline-editor')
 

	
 
  onDrop: (effect, pos) ->
 
@@ -355,24 +355,25 @@ Polymer
 
        quad(@song, U(':note'), newNote)
 
        quad(newNote, RDF + 'type', U(':Note'))
 
        quad(newNote, U(':originTime'), @graph.LiteralRoundedFloat(dropTime))
 
        quad(newNote, U(':effectClass'), effect)
 
        quad(newNote, U(':curve'), newCurve)
 
        quad(newCurve, RDF + 'type', U(':Curve'))
 
        quad(newCurve, U(':attr'), U(':strength'))
 
      ]        
 
    pointQuads = []
 

	
 
    desiredWidthX = @offsetWidth * .3
 
    desiredWidthT = @zoomInX.invert(desiredWidthX) - @zoomInX.invert(0)
 
    desiredWidthT = Math.min(desiredWidthT, @zoom.duration() - dropTime)
 
    
 
    for i in [0...4]
 
      pt = points[i]
 
      pointQuads.push(quad(newCurve, U(':point'), pt))
 
      pointQuads.push(quad(pt, U(':time'), @graph.LiteralRoundedFloat(i/3 * desiredWidthT)))
 
      pointQuads.push(quad(pt, U(':value'), @graph.LiteralRoundedFloat(i == 1 or i == 2)))
 

	
 
    patch = {
 
      delQuads: []
 
      addQuads: curveQuads.concat(pointQuads)
 
      }
 
    @graph.applyAndSendPatch(patch)
 
@@ -621,45 +622,48 @@ Polymer
 
    'onColorScale(graph, uri, colorScale)'
 
    ]
 
  displayed: ->
 
    @querySelector('light9-color-picker').displayed()
 
  onColorScale: ->
 
    U = (x) => @graph.Uri(x)
 
    if @colorScale == @colorScaleFromGraph
 
      return
 
    @editAttr(@song, @uri, U(':colorScale'), @graph.Literal(@colorScale))
 

	
 
  editAttr: (song, note, attr, value) ->
 
    U = (x) => @graph.Uri(x)
 
    if not song?
 
      log("can't edit inline attr yet, no song")
 
      return
 
    quad = (s, p, o) => {subject: s, predicate: p, object: o, graph: song}
 

	
 
    existingColorScaleSetting = null
 
    for setting in @graph.objects(note, U(':setting'))
 
      ea = @graph.uriValue(setting, U(':effectAttr'))
 
      if ea == attr
 
        existingColorScaleSetting = setting
 
        
 
    if existingColorScaleSetting
 
      @graph.patchObject(existingColorScaleSetting, U(':value'), value, song)
 
    else
 
      setting = @graph.nextNumberedResource(note + 'set')
 
      patch = {delQuads: [], addQuads: [
 
        quad(note, U(':setting'), setting)
 
        quad(setting, U(':effectAttr'), attr)
 
        quad(setting, U(':value'), value)
 
        ]}
 
      @graph.applyAndSendPatch(patch)
 
    
 
  addHandler: ->
 
    @graph.runHandler(@update.bind(@))
 
    @graph.runHandler(@update.bind(@), "update inline attrs #{@uri}")
 
    
 
  update: ->
 
    #console.time('attrs update')
 
    U = (x) => @graph.Uri(x)
 
    @effect = @graph.uriValue(@uri, U(':effectClass'))
 
    @effectLabel = @graph.stringValue(@effect, U('rdfs:label')) or (@effect.replace(/.*\//, ''))
 
    @noteLabel = @uri.replace(/.*\//, '')
 

	
 
    existingColorScaleSetting = null
 
    for setting in @graph.objects(@uri, U(':setting'))
 
      ea = @graph.uriValue(setting, U(':effectAttr'))
 
      value = @graph.stringValue(setting, U(':value'))
0 comments (0 inline, 0 general)