Changeset - 36f58b2aa8ef
[Not reviewed]
default
0 3 0
Drew Perttula - 9 years ago 2016-06-05 03:21:31
drewp@bigasterisk.com
browser syncedgraph sends patches back to server
Ignore-this: eb8d3f018ff97f7389c4af3efa62fd9
3 files changed with 64 insertions and 16 deletions:
0 comments (0 inline, 0 general)
bin/rdfdb
Show inline comments
 
@@ -414,15 +414,16 @@ class WebsocketClient(cyclone.websocket.
 
    def connectionLost(self, reason):
 
        log.info("bye ws client %r", self.wsClient)
 
        self.settings.db.clientErrored(
 
            Failure(WebsocketDisconnect(reason)), self.wsClient)
 

	
 
    def messageReceived(self, message):
 
        log.info("got message from %s: %s", self.connectionId, message)
 
        # how
 
        self.sendMessage(message)
 
        log.info("got message from %r: %s", self.wsClient, message)
 
        p = Patch(jsonRepr=message)
 
        p.senderUpdateUri = self.wsClient.updateUri
 
        self.settings.db.patch(p)
 

	
 
liveClients = set()
 
def sendToLiveClients(d=None, asJson=None):
 
    j = asJson or json.dumps(d)
 
    for c in liveClients:
 
        c.sendMessage(j)
light9/web/adjustable.coffee
Show inline comments
 
@@ -26,27 +26,24 @@ class Adjustable
 
  getTarget: () -> # vec2 of pixels
 
    @config.getTarget()
 
            
 
  subscribe: (onChange) ->
 
    # change could be displayValue or center or target. This likely
 
    # calls onChange right away if there's any data yet.
 
    setInterval((() => onChange()), 100)
 
    throw new Error('not implemented')
 

	
 
  startDrag: () ->
 
    # todo
 
    @dragStartValue = @_getValue()
 
    # override
 

	
 
  continueDrag: (pos) ->
 
    # pos is vec2 of pixels relative to the drag start
 
    
 
    # todo
 
    newValue = @dragStartValue + pos.e(0) * .1
 
    graph.patchObject(@_subj, @_pred, graph.Literal(newValue), @_ctx)
 
    # override
 

	
 
  endDrag: () ->
 
    0
 
    # override
 

	
 
  _editorCoordinates: () -> # vec2 of mouse relative to <l9-t-editor>
 
    ev = d3.event.sourceEvent
 

	
 
    if ev.target.tagName == "LIGHT9-TIMELINE-EDITOR"
 
      rootElem = ev.target
 
@@ -112,7 +109,9 @@ class window.AdjustableFloatObject exten
 
      onChange()
 
    
 
  continueDrag: (pos) ->
 
    # pos is vec2 of pixels relative to the drag start
 
    
 
    newValue = @config.getValueForPos(@_editorCoordinates())
 
    @config.graph.patchObject(@config.subj, @config.pred, @config.graph.Literal(newValue), @_ctx)
 
    @config.graph.patchObject(@config.subj, @config.pred,
 
                              @config.graph.LiteralRoundedFloat(newValue),
 
                              @config.ctx)
light9/web/graph.coffee
Show inline comments
 
log = console.log
 

	
 
# Patch is {addQuads: <quads>, delQuads: <quads>}
 
# <quads> is [{subject: s, ...}, ...]
 

	
 
patchSizeSummary = (patch) ->
 
  '-' + patch.delQuads.length + ' +' + patch.addQuads.length
 

	
 
# partial port of autodepgraphapi.py
 
class GraphWatchers
 
  constructor: ->
 
    @handlersSp = {} # {s: {p: [handlers]}}
 
  subscribe: (s, p, o, onChange) -> # return subscription handle
 
    if o? then throw Error('not implemented')
 
@@ -33,20 +36,42 @@ class GraphWatchers
 
        cb({delQuads: [quad], addQuads: []})
 
    for quad in patch.addQuads
 
      for cb in @matchingHandlers(quad)
 
        cb({delQuads: [], addQuads: [quad]})
 

	
 

	
 
jsonPatch = (jsPatch, cb) ->
 
  out = {patch: {adds: '', deletes: ''}}
 

	
 
  writeDels = (cb) ->
 
    writer = N3.Writer({ format: 'N-Quads' })
 
    writer.addTriples(jsPatch.delQuads)
 
    writer.end((err, result) ->
 
      out.patch.deletes = result
 
      cb())
 

	
 
  writeAdds = (cb) ->
 
    writer = N3.Writer({ format: 'N-Quads' })
 
    writer.addTriples(jsPatch.addQuads)
 
    writer.end((err, result) ->
 
      out.patch.adds = result
 
      cb())
 
    
 
  async.parallel([writeDels, writeAdds], (err) ->
 
      cb(JSON.stringify(out))
 
    )
 

	
 
class window.SyncedGraph
 
  # Note that applyPatch is the only method to write to the graph, so
 
  # it can fire subscriptions.
 

	
 
  constructor: (@patchSenderUrl, prefixes) ->
 
    @graph = N3.Store()
 
    @_addPrefixes(prefixes)
 
    @_watchers = new GraphWatchers()
 
    @patchesToSend = []
 
    @newConnection()
 

	
 
  newConnection: ->
 
    fullUrl = 'ws://' + window.location.host + @patchSenderUrl
 
    @ws = new WebSocket(fullUrl)
 

	
 
@@ -81,21 +106,45 @@ class window.SyncedGraph
 
                      patch.delQuads.push(quad)
 
                    else
 
                      cb()
 
      
 
    async.parallel([parseAdds, parseDels], ((err) => @applyPatch(patch)))
 
      
 
  _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) =>
 
        jsonPatch(patch, (json) =>
 
          log('send patch to server, ' + json.length + ' bytes')
 
          @ws.send(json)
 
          cb(null)
 
      )
 

	
 
    async.eachSeries(@patchesToSend, sendOne, () =>
 
        @patchesToSend = []
 
      )
 
    
 
      
 
  _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),
 
                          "http://www.w3.org/2001/XMLSchema#decimal")
 

	
 
  toJs: (literal) ->
 
    # incomplete
 
    parseFloat(N3.Util.getLiteralValue(literal))
 

	
 
  loadTrig: (trig, cb) -> # for debugging
 
    patch = {delQuads: [], addQuads: []}
 
@@ -110,26 +159,25 @@ class window.SyncedGraph
 
                    
 
  quads: () -> # for debugging
 
    [q.subject, q.predicate, q.object, q.graph] for q in @graph.find()
 

	
 
  applyAndSendPatch: (patch, cb) ->
 
    @applyPatch(patch)
 
    console.log('patch to server:')
 
    console.log('  delete:', JSON.stringify(patch.delQuads))
 
    console.log('  add:', JSON.stringify(patch.addQuads))
 
    # post to server
 
    console.log('queue patch to server ', patchSizeSummary(patch))
 
    @patchesToSend.push(patch)
 
    @_continueSending()
 

	
 
  applyPatch: (patch) ->
 
    # In most cases you want applyAndSendPatch.
 
    # 
 
    # This is the only method that writes to the graph!
 
    for quad in patch.delQuads
 
      @graph.removeTriple(quad)
 
    for quad in patch.addQuads
 
      @graph.addTriple(quad)
 
    log('applied patch -' + patch.delQuads.length + ' +' + patch.addQuads.length)
 
    log('applied patch locally', patchSizeSummary(patch))
 
    @_watchers.graphChanged(patch)
 

	
 
  getObjectPatch: (s, p, newObject, g) ->
 
    # send a patch which removes existing values for (s,p,*,c) and
 
    # adds (s,p,newObject,c). Values in other graphs are not affected.
 
    existing = @graph.findByIRI(s, p, null, g)
0 comments (0 inline, 0 general)