Updates todo and adds notes from various planning meetings.
authordsc <dsc@less.ly>
Wed, 29 Feb 2012 17:03:40 +0000 (09:03 -0800)
committerdsc <dsc@less.ly>
Wed, 29 Feb 2012 17:05:37 +0000 (09:05 -0800)
22 files changed:
docs/notes/graphs.md [moved from docs/graphs.md with 100% similarity]
docs/notes/notes.md [moved from docs/notes.md with 100% similarity]
docs/notes/prototype-sync-up.mediawiki [new file with mode: 0644]
docs/notes/tag-notes.md [new file with mode: 0644]
docs/todo.md
lib/base.co
lib/graph/graph-model.co
lib/graph/graph-view.co
lib/main.co
lib/scaffold/scaffold-model.co
lib/scaffold/scaffold-view.co
lib/server/server.co
lib/template/graph.jade
lib/util/op.co
lib/vis/vis-model.co
lib/vis/vis-view.co
msc/dygraph-options/data.yaml
www/css/colors.styl
www/css/graph.styl
www/graph/test.jade
www/layout.jade
www/modules.yaml

similarity index 100%
rename from docs/graphs.md
rename to docs/notes/graphs.md
similarity index 100%
rename from docs/notes.md
rename to docs/notes/notes.md
diff --git a/docs/notes/prototype-sync-up.mediawiki b/docs/notes/prototype-sync-up.mediawiki
new file mode 100644 (file)
index 0000000..a78bea4
--- /dev/null
@@ -0,0 +1,44 @@
+What data? Agreement to focus on extant reportcard metrics:
+* Visitors
+       ** Mostly comScore data: Unique Visitors, Reach % -- otto is on it; says "super easy" to convert to dygraph format
+       ** Referrer data? otto says it's in the CSVs & totes easy
+* Mobile
+       *** where is ezachte's processed data used to make the pie chart? (otto will email)
+       *** dietz will parse HTML to extract mobile data from http://stats.wikimedia.org/EN/TablesPageViewsMonthlyMobile.htm
+       *** otto will aggregate from ezachte's CSVs after dealing with visitors
+* Editors
+       ** fabz has x6 datasets -- see docs/graph.md for more details
+* Wiki Content
+       ** otto will aggregate last if there is time
+* Pageviews
+       * Mostly done and in data dir already (otto)
+
+UI -- dsc
+* Add fields for graph metadata
+* Cascading graph option presets
+* Add option for a "Benchmark Value" on the graph
+* Datasource UI
+       ** Sources Selector: list datasets via YAML metadata descriptor files
+       ** Metric Selector: select metrics out of a dataset
+       ** Date range selector?
+       ** Configure Metric for Graph: choose color, label, formatting, and transform (low-pri -- can hardcode for now)
+* Graph Edit UI
+       ** Tabbed main container
+       ** Hide callback options
+       ** Redraw button
+       ** Add new tags + tag for "common"/"important" options
+       ** List tags to toggle all matching options
+       ** (work beyond here is low-pri)
+               *** Typeahead filter box for names/tags
+               *** Sort by Importance, Tag, Lexical
+               *** Collapse/uncollapse all
+               *** Reset all options to defaults
+               *** Outline modified options
+
+Other Business
+* Need to write documentation about how to use this and gather feedback
+* dsc to email Moeller and figure out schedule
+* otto to email ezachte to figure out source for mobile graphs on stats.wikimedia.org
+
+
+
diff --git a/docs/notes/tag-notes.md b/docs/notes/tag-notes.md
new file mode 100644 (file)
index 0000000..afb83cf
--- /dev/null
@@ -0,0 +1,24 @@
+
+chart: title, titleHeight, 
+
+axis-appearance
+
+x-axis: xlabel, xLabelHeight, drawXAxis, xAxisHeight, xAxisLabelWidth, xValueParser, drawXGrid,
+
+y-axis: ylabel, y2label, yLabelHeight, avoidMinZero, drawYAxis, includeZero, logscale, yAxisLabelWidth, valueRange, drawYGrid, labelsKMB, digitsAfterDecimal, labelsKMG2, maxNumberWidth, sigFigs, 
+
+axes: axis, axisLabelColor, axisLabelFontSize, axisLabelFormatter, axisLabelWidith, axisLineColor, axisLineWidth, axisTickSize, pixelPerLabel, ticker, dateWindow
+
+line: connectSeparatedPoints, drawPoints, pointSize, strokePattern, strokeWidth, colorSaturation, colorValue, colors, 
+
+graph: fillGraph, stackedGraph, stepPlot, panEdgeFraction, gridLineColor, gridLineWidth, highlightCircleSize, rangeSelectorHeight, rangeSelectorPlotFillColor, rangeSelectorPlotStrokeColor, showRangeSelector, rightGap, timingName, visibility
+
+legend: labelsDiv, labelsDivStyles, labelsDivWidth, labelsSeparateLines, labelsShowZeroValues, legend, showLabelsOnHighlight, 
+
+callback: pointClickCallback, annotationClickHandler, annotationMouseOutHandler, annotationMouseOverHandler, valueFormatter, clickCallback, drawCallback, highlightCallback, underlayCallback, unhighlightCallback, zoomCallback, 
+
+statistics: sigma, wilsonInterval, fillAlpha, customBars, errorBars, fractions, rollPeriod, showRoller, 
+
+data: delimiter, displayAnnotations, 
+
+interactivity: animatedZooms, hideOverlayOnMouseOut, isZoomedIgnoreProgrammaticZoom
index 723a801..2683b03 100644 (file)
@@ -1,28 +1,47 @@
 # todo
 
-- [graph options]
-    - special 'auto' value for width & height to fit to page-size
-        - non-lib option: redraw on page resize
-    - hide callbacks & handlers
+- cascading presets
+- base preset
+- pass label element
+- tags -> hidden detection: callbacks, deprecated, type=function
+- title element to replace title option
 
 
-## features
 
-- Bookmarkable Graphs
-    - Load graph from URL
-    - Update URL when graph changes
-- Ability to merge & filter datasets
-- Discoverable & Self-Describing Data Sources
-    - ToC endpoint
-    - Metadata endpoint for each datasource/dataset
-    - (Both could be static files -- "endpoint" just means a convention to the URLs)
-- Persist graph settings as a preset on the server
-- Dashboard Configuration
-    - list of graphs w/ configuration identifiers
+##### ohai #####
+
+[Other Business]
+- Need to write documentation about how to use this and gather feedback
+- **dsc** to email Moeller and figure out schedule
+- **otto** to email ezachte to figure out source for mobile graphs on stats.wikimedia.org
 
 
 ## ux
 
+- Add fields for graph metadata (name, desc, slug, w/h, dataset)
+- Cascading graph option presets
+- Add option for a "Benchmark Value" on the graph
+- [Datasource UI]
+    - Sources Selector: list datasets via YAML metadata descriptor files
+    - Metric Selector: select metrics out of a dataset
+    - Date range selector?
+    - Configure Metric for Graph: choose color, label, formatting, and transform (low-pri -- can hardcode for now)
+- [Graph Edit UI]
+    - Tabbed main container
+    - Hide callback options
+    - Redraw button
+    - Add new tags + tag for "common"/"important" options
+    - List tags to toggle all matching options
+    - (work beyond here is low-pri)
+        - non-lib option: redraw graph on page resize
+        - Typeahead filter box for names/tags
+        - Sort by Importance, Tag, Lexical
+        - Collapse/uncollapse all
+        - Reset all options to defaults
+        - Outline modified options
+
+### later
+
 - Filter/Sort attributes, datasets, etc
 - Improve graph color defaults
 - Stacked bar charts seem to be something a lot of us are into, but dygraphs won't be doing that. (tho d3 does)
 - Outline all changed options
 
 
+## features
+
+- Ability to merge & filter datasets
+- Discoverable & Self-Describing Data Sources
+    - ToC endpoint
+    - Metadata endpoint for each datasource/dataset
+    - (Both could be static files -- "endpoint" just means a convention to the URLs)
+- Persist graph settings as a preset on the server
+- Dashboard Configuration
+    - list of graphs w/ configuration identifiers
+
+
 
 
 ## notes
index f3766f4..3f96421 100644 (file)
@@ -12,8 +12,17 @@ BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{
     ctorName : 'BaseModel'
     
     initialize: ->
-        _.bindAll this, ...@__bind__ if @__bind__.length
         @__super__ = @constructor.__super__
+        _.bindAll this, ...@__bind__ if @__bind__.length
+    
+    serialize: (v) ->
+        if v!?
+            v = ''
+        else if _.isBoolean v
+            v =  Number v
+        else if _.isObject v
+            v = JSON.stringify v
+        String v
     
     /**
      * Like `.toJSON()` in that it should return a plain object with no functions,
@@ -22,7 +31,10 @@ BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{
      * @returns {Object}
      */
     toKVObject: ->
-        _.collapseObject @toJSON()
+        kvo = _.collapseObject @toJSON()
+        for k, v in kvo
+            kvo[k] = @serialize v
+        kvo
     
     /**
      * Serialize the model into a `www-form-encoded` string suitable for use as
@@ -85,6 +97,7 @@ BaseView = exports.BaseView = Backbone.View.extend do # {{{
         _.bindAll this, ...@__bind__ if @__bind__.length
         @__super__ = @constructor.__super__
         
+        @build()
         @model.view = this
         @$el.data { @model, view:this }
         @model.on 'change',  @render, this
index 2728efa..372fb66 100644 (file)
@@ -4,6 +4,8 @@ _ = require 'kraken/underscore'
 { Field, FieldList, FieldView, Scaffold,
 } = require 'kraken/scaffold'
 
+IGNORED_TAGS = exports.IGNORED_TAGS = <[ callback deprecated debugging ]>
+
 
 
 class exports.TagSet extends Array
@@ -48,6 +50,12 @@ GraphOption = exports.GraphOption = Field.extend do # {{{
         # Notify Tag indexer of category when created, to ensure all category-tags
         # get indices with colors :P
         KNOWN_TAGS.update @getCategory()
+        
+        # Ignore functions/callbacks and, ahem, hidden tags.
+        type = @get 'type', '' .toLowerCase()
+        tags = @get 'tags', []
+        if _.str.include(type, 'function') or _.intersection tags, IGNORED_TAGS .length
+            @set 'ignore', true
     
     
     # Wrapper to ensure @set('tags') is called, as tags.push()
index 4087cf5..3d87cb0 100644 (file)
@@ -58,8 +58,10 @@ GraphOptionsScaffold = exports.GraphOptionsScaffold = Scaffold.extend do # {{{
     ctorName       : 'GraphOptionsScaffold'
     tagName        : 'form'
     className      : 'options scaffold'
+    template       : require 'kraken/template/graph-scaffold'
     collectionType : GraphOptionList
     subviewType    : GraphOptionView
+    $fields        : '.fields'
     
     # GraphView will set this
     ready          : false
@@ -74,7 +76,7 @@ GraphOptionsScaffold = exports.GraphOptionsScaffold = Scaffold.extend do # {{{
         # console.log "#this.render() -> .isotope()"
         @__super__.render ...
         return this unless @ready
-        @$el.isotope do
+        @$el.find '.options.control-group' .isotope do
             itemSelector    : '.field.option'
             layoutMode      : 'masonry'
             masonry         : columnWidth : 10
index 43962e4..a90bd2a 100644 (file)
@@ -1,3 +1,4 @@
+Seq = require 'seq'
 { _, op,
 } = require 'kraken/util'
 { BaseView, BaseModel, BaseList,
 { VisView, VisModel,
 } = require 'kraken/vis'
 
-root = do -> this
+root = this
+CHART_OPTIONS_SPEC = []
+ROOT_VIS_DATA      = {}
+ROOT_VIS_OPTIONS   = {}
+
 
 # Create the Graph Scaffold
 main = ->
+    # TODO: create a preset manager
+    # Remove chart options from data so we don't have to deepcopy
+    ROOT_VIS_OPTIONS := delete root.ROOT_VIS_DATA.options
+    
+    # Bind to URL changes
     History.Adapter.bind window, 'statechange', ->
-        console.log 'StateChange!', String root.location
+        console.log 'StateChange!', String(root.location)
     
-    graph = root.graph = new VisView do
-        graph_spec : CHART_OPTIONS_SPEC
     
     # If we got querystring args, apply them to the graph
-    if root.location?.search.slice 1
-        g = _.uncollapseObject _.fromKVPairs that
-        # yarr, have to do options separately or everything goes to shit
-        options = delete g.options
-        graph.model.set g, {+silent}
-        graph.chartOptions options, {+silent}
+    data = {}
+    if String(root.location).split '?' .1
+        data = _.uncollapseObject _.fromKVPairs that.replace('#', '%23')
         
-        # Flush all changes once the DOM settles
-        _.defer do
-            -> graph.scaffold.invoke 'change'
-            50
+        # # yarr, have to do options separately or everything goes to shit
+        # options = delete data.options
+        # graph.model.set data, {+silent}
+        # graph.chartOptions options, {+silent}
+        # # Flush all changes once the DOM settles
+        # _.delay do
+        #     ->
+        #         graph.change()
+        #         # graph.scaffold.invoke 'change'
+        #     50
+    
+    
+    vis   = root.vis   = new VisModel data
+    graph = root.graph = new VisView do
+        graph_spec : root.CHART_OPTIONS_SPEC
+        model      : vis
     
     $ '#content .inner' .append graph.el
 
 
-jQuery.ajax do
-    url      : '/graph/dygraph-options.json',
-    dataType : 'json'
-    success : (data) ->
-        root.CHART_OPTIONS_SPEC = data
-        jQuery main
-    error : (err) -> console.error err
+
+# Load data files
+Seq([   <[ CHART_OPTIONS_SPEC /graph/dygraph-options.json ]>,
+        <[ ROOT_VIS_DATA /graph/root ]>
+])
+.parEach_ (next, [key, url]) ->
+    jQuery.ajax do
+        url : url,
+        dataType : 'json'
+        success : (data) ->
+            root[key] = data
+            next.ok()
+        error : (err) -> console.error err
+.seq ->
+    console.log 'All data loaded!'
+    jQuery main
 
index 8142295..65814ff 100644 (file)
@@ -14,6 +14,7 @@ Field = exports.Field = BaseModel.extend do # {{{
     
     
     initialize: ->
+        _.bindAll this, ...(_.functions this .filter -> _.startsWith(it, 'parse'))
         @set 'value', @get('default'), {+silent} if not @has 'value'
         # console.log "#this.initialize!"
         # @on 'all', (evt) ~> console.log "#this.trigger(#evt)"
@@ -38,7 +39,6 @@ Field = exports.Field = BaseModel.extend do # {{{
         type = _ (type or @get 'type').toLowerCase()
         for t of <[ Integer Float Boolean Object Array Function ]>
             if type.startsWith t.toLowerCase()
-                # console.log "parse#t ->", @["parse#t"] unless @["parse#t"]
                 return @["parse#t"]
         @parseString
     
@@ -64,7 +64,7 @@ Field = exports.Field = BaseModel.extend do # {{{
     
     parseFunction: (fn) ->
         if fn and _.startswith String(fn), 'function'
-            try eval "(#fn)" catch err
+            try eval "(#fn)" catch err then null
         else
             null
     
@@ -90,21 +90,14 @@ Field = exports.Field = BaseModel.extend do # {{{
     
     /* * * Serializers * * */
     serializeValue: ->
-        v = @getValue()
-        if v!?
-            v = ''
-        else if _.isBoolean v
-            v =  Number v
-        else if _.isArray(v) or _.isObject(v)
-            v = JSON.stringify v
-        String v
+        @serialize @getValue()
     
     toJSON: ->
         {id:@id} import do
             _.clone(@attributes) import { value:@getValue(), def:@get('default') }
     
     toKVObject: ->
-        { "#{@id}": @serializeValue() }
+        { "#{@id}":@serializeValue() }
     
     toString: -> "(#{@id}: #{@serializeValue()})"
 # }}}
@@ -118,16 +111,17 @@ FieldList = exports.FieldList = BaseList.extend do # {{{
      * Collects a map of fields to their values, excluding those set to `null` or their default.
      * @returns {Object}
      */
-    values: (keepDefaults=false) ->
+    values: (opts={}) ->
+        opts = {-keepDefaults, -serialize} import opts
         _.synthesize do
-            if keepDefaults then @models else @models.filter -> not it.isDefault()
-            -> [ it.get('name'), it.getValue() ]
+            if opts.keepDefaults then @models else @models.filter -> not it.isDefault()
+            -> [ it.get('name'), if opts.serialize then it.serializeValue() else it.getValue() ]
     
     toJSON: ->
         @values()
     
     toKVObject: ->
-        _.collapseObject @toJSON()
+        _.collapseObject @values false, true
     
     toKVPairs: (item_delim='&', kv_delim='=') ->
         _.toKVPairs @toKVObject(), item_delim, kv_delim
index 8f2c770..5e4cbfb 100644 (file)
@@ -28,7 +28,7 @@ FieldView = exports.FieldView = BaseView.extend do # {{{
         @trigger 'update', this
     
     render: ->
-        return @remove() if @model.get 'hidden', false
+        return @remove() if @model.get 'ignore', false
         return BaseView::render ... if @template
         
         name  = @model.get 'name'
@@ -74,6 +74,7 @@ Scaffold = exports.Scaffold = BaseView.extend do # {{{
     
     addOne: (field) ->
         # console.log "[S] #this.addOne!", @__super__
+        fields = if @$fields then @$el.find that else @$el
         _.remove @subviews, field.view if field.view
         
         # avoid duplicating event propagation
@@ -85,7 +86,7 @@ Scaffold = exports.Scaffold = BaseView.extend do # {{{
         SubviewType = @subviewType
         view = new SubviewType model:field
         @subviews.push view
-        @$el.append view.render().el unless field.get 'hidden'
+        fields.append view.render().el unless field.get 'ignore'
         view.on 'update', @change.bind(this, field)
         
         @render()
index db5df45..3281595 100755 (executable)
@@ -2,6 +2,7 @@
 
 fs       = require 'fs'
 path     = require 'path'
+{existsSync:exists} = path
 {exec, spawn} = require 'child_process'
 
 _        = require 'underscore'
@@ -65,6 +66,9 @@ app.configure ->
     # Allow "spoofing" HTTP methods that IE doesn't support
     app.use express.methodOverride()
     
+    # Route to the web services
+    app.use app.router
+    
     # Transparently recompile modules that have changed
     app.use compiler do
         enabled : <[ coco jade-browser stylus yaml ]>
@@ -95,9 +99,6 @@ app.configure ->
     app.use express.static VAR
     app.use express.static STATIC
     
-    # Route to the web services
-    app.use app.router
-    
     # Serve directory listings
     app.use express.directory WWW<