First pass at data UI, mostly inactive due to time. Adds simple dropdown for datasets.
authordsc <dsc@wikimedia.org>
Wed, 4 Apr 2012 20:04:22 +0000 (13:04 -0700)
committerdsc <dsc@wikimedia.org>
Wed, 4 Apr 2012 20:04:22 +0000 (13:04 -0700)
33 files changed:
lib/base.co
lib/chart/chart-option-model.co
lib/chart/chart-option-view.co
lib/dataset/data-view.co [new file with mode: 0644]
lib/dataset/dataset-model.co [new file with mode: 0644]
lib/dataset/dataset-view.co [new file with mode: 0644]
lib/dataset/datasource-model.co [new file with mode: 0644]
lib/dataset/datasource-ui-view.co [new file with mode: 0644]
lib/dataset/datasource-view.co [new file with mode: 0644]
lib/dataset/index.co [new file with mode: 0644]
lib/dataset/metric-edit-view.co [new file with mode: 0644]
lib/dataset/metric-model.co [new file with mode: 0644]
lib/dataset/metric-view.co [new file with mode: 0644]
lib/graph/graph-display-view.co
lib/graph/graph-edit-view.co
lib/graph/graph-model.co
lib/scaffold/scaffold-model.co
lib/scaffold/scaffold-view.co
lib/server/controllers/datasource.co
lib/server/server.co
lib/template/data.jade [new file with mode: 0644]
lib/template/dataset-metric.jade [new file with mode: 0644]
lib/template/dataset.jade [new file with mode: 0644]
lib/template/datasource-ui.jade [new file with mode: 0644]
lib/template/datasource.jade [new file with mode: 0644]
lib/template/graph-edit.jade
lib/template/metric-edit.jade [new file with mode: 0644]
lib/template/metric.jade [new file with mode: 0644]
lib/util/op.co
www/css/bootstrap-variables.styl [new file with mode: 0644]
www/css/colors.styl
www/css/data.styl [new file with mode: 0644]
www/modules.yaml

index f1f28f7..2ac0400 100644 (file)
@@ -1,5 +1,5 @@
-_  = require 'kraken/util/underscore'
-op = require 'kraken/util/op'
+{ _, op,
+} = require 'kraken/util'
 
 Backbone = require 'backbone'
 
@@ -19,9 +19,7 @@ BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{
     
     constructor : function BaseModel
         @__class__      = @constructor
-        @__super__      = @constructor.__super__
-        @__superclass__ = @__super__.constructor
-        # Backbone.NestedModel ...
+        @__superclass__ = @..__super__.constructor
         Backbone.Model ...
         @trigger 'create', this
     
@@ -50,7 +48,7 @@ BaseModel = exports.BaseModel = Backbone.Model.extend do # {{{
     #         if _.str.contains key, '.'
     #             _.setNested @attributes, key, value, opts
     #         else
-    #             @__super__.set.call this, key, value, opts
+    #             Backbone.Model::set.call this, key, value, opts
     #     
     #     this
     # 
@@ -126,8 +124,14 @@ BaseList = exports.BaseList = Backbone.Collection.extend do # {{{
     __bind__ : []
     
     
+    
+    constructor : function BaseList
+        @__class__      = @constructor
+        @__superclass__ = @..__super__.constructor
+        Backbone.Collection ...
+        @trigger 'create', this
+    
     initialize : ->
-        @__super__ = @constructor.__super__
         _.bindAll this, ...@__bind__ if @__bind__.length
     
     
@@ -151,13 +155,27 @@ BaseList = exports.BaseList = Backbone.Collection.extend do # {{{
 BaseView = exports.BaseView = Backbone.View.extend do # {{{
     ctorName : 'BaseView'
     
-    # A list of method-names to bind on initialize; set this on a subclass to override.
+    /**
+     * A list of method-names to bind on initialize; set this on a subclass to override.
+     * @type Array<String>
+     */
     __bind__ : []
     
+    /**
+     * @type Array<BaseView>
+     */
+    subviews : []
+    
     
     
+    constructor : function BaseView
+        @__class__      = @constructor
+        @__superclass__ = @..__super__.constructor
+        @subviews       = []
+        Backbone.View ...
+        @trigger 'create', this
+    
     initialize: ->
-        @__super__ = @constructor.__super__
         _.bindAll this, ...@__bind__ if @__bind__.length
         
         @model.view = this
index e5277f8..7cf2bc7 100644 (file)
@@ -48,6 +48,9 @@ ChartOption = exports.ChartOption = Field.extend do # {{{
     IGNORED_TAGS : <[ callback deprecated debugging ]>
     
     
+    constructor: function ChartOption
+        Field ...
+    
     initialize : ->
         # console.log "#this.initialize!"
         Field::initialize ...
@@ -113,6 +116,10 @@ ChartOptionList = exports.ChartOptionList = FieldList.extend do # {{{
     ctorName   : 'ChartOptionList'
     model      : ChartOption
     
+    
+    constructor: function ChartOptionList
+        FieldList ...
+    
     /**
      * Override to omit defaults from URL.
      */
index fa68de9..369c198 100644 (file)
@@ -28,8 +28,11 @@ ChartOptionView = exports.ChartOptionView = FieldView.extend do # {{{
         'click .collapsed'                   : 'onClick'
     
     
+    constructor: function ChartOptionView
+        FieldView ...
+    
     render: ->
-        @__super__.render ...
+        FieldView::render ...
         @$el.addClass 'collapsed' if @isCollapsed
         this
     
@@ -60,12 +63,14 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{
     template       : require 'kraken/template/chart-scaffold'
     collectionType : ChartOptionList
     subviewType    : ChartOptionView
-    fields        : '.fields'
+    fields         : '.fields'
     
     # GraphView will set this
     ready          : false
     
     
+    constructor: function ChartOptionScaffold
+        Scaffold ...
     
     initialize : ->
         @render = _.debounce @render.bind(this), DEBOUNCE_RENDER
@@ -73,7 +78,7 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{
     
     render: ->
         # console.log "#this.render() -> .isotope()"
-        # @__super__.render ...
+        # Scaffold::render ...
         return this unless @ready
         container = if @fields then @$el.find @fields else @$el
         container
@@ -94,7 +99,7 @@ ChartOptionScaffold = exports.ChartOptionScaffold = Scaffold.extend do # {{{
      * layout after collapse events.
      */
     addOne: (field) ->
-        view = @__super__.addOne ...
+        view = Scaffold::addOne ...
         view.on 'change:collapse render', @render
         view
     
diff --git a/lib/dataset/data-view.co b/lib/dataset/data-view.co
new file mode 100644 (file)
index 0000000..75439f9
--- /dev/null
@@ -0,0 +1,63 @@
+Seq = require 'seq'
+{ _, op,
+} = require 'kraken/util'
+{ BaseView,
+} = require 'kraken/base'
+{ DataSetView,
+} = require 'kraken/dataset/dataset-view'
+{ MetricEditView,
+} = require 'kraken/dataset/metric-edit-view'
+
+
+/**
+ * @class
+ */
+DataView = exports.DataView = BaseView.extend do # {{{
+    ctorName       : 'DataView'
+    tagName        : 'section'
+    className      : 'data-ui'
+    template       : require 'kraken/template/data'
+    
+    data : {}
+    
+    
+    
+    constructor: function DataView
+        BaseView ...
+    
+    initialize: ->
+        @graph_id = @options.graph_id
+        BaseView::initialize ...
+        
+        @load()
+        
+        # @subviews.push @dataset_view = new DataSetView {@model, @graph_id}
+        # @$el.append @dataset_view.render().el
+        # @dataset_view.on 'edit-metric', @editMetric, this
+        # 
+        # @subviews.push @metric_edit_view  = new MetricEditView  {dataset:@model, @graph_id}
+        # @$el.append @metric_edit_view.render().hide().el
+        
+    
+    toTemplateLocals: ->
+        attrs = _.clone @model.attributes
+        { $, _, op, @model, view:this, @data, @graph_id } import attrs
+    
+    
+    load: ->
+        $.getJSON '/datasources/all', (@data) ~>
+            @ready = true
+            @render()
+            @trigger 'ready', this
+    
+    # Don't rebuild HTML, simply notify subviews
+    render: ->
+        BaseView::render ...
+        
+        # _.invoke @subviews, 'render'
+        this
+    
+    editMetric: (metric) ->
+        @metric_edit_view.editMetric metric
+    
+# }}}
diff --git a/lib/dataset/dataset-model.co b/lib/dataset/dataset-model.co
new file mode 100644 (file)
index 0000000..1b40c21
--- /dev/null
@@ -0,0 +1,61 @@
+Seq = require 'seq'
+ColorBrewer = require 'colorbrewer'
+
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList,
+} = require 'kraken/base'
+{ Metric, MetricList,
+} = require 'kraken/dataset/metric-model'
+{ DataSource, DataSourceList,
+} = require 'kraken/dataset/datasource-model'
+
+
+
+/**
+ * @class
+ */
+DataSet = exports.DataSet = BaseModel.extend do # {{{
+    ctorName : 'DataSet'
+    urlRoot : '/datasets'
+    
+    /**
+     * @type DataSourceList
+     */
+    sources : []
+    
+    /**
+     * @type MetricList
+     */
+    metrics : []
+    
+    
+    constructor: function DataSet
+        BaseModel ...
+    
+    initialize : ->
+        BaseModel::initialize ...
+        @sources = new DataSourceList @attributes.sources
+        @metrics = new MetricList     @attributes.metrics
+    
+    
+    defaults : ->
+        sources : [] # XXX: needed? metrics now implies this info
+        metrics : []
+        # lines   : []
+    
+    
+    /**
+     * @returns {Array} The reified dataset, materialized to an array of data-series arrays.
+     */
+    getData: ->
+        '/data/datasources/non_mobile_pageviews_by.timestamp.language.csv'
+    
+    newMetric: ->
+        index = @metrics.length
+        @metrics.add m = new Metric { index, color:ColorBrewer.Spectral[11][index] }
+        m
+    
+    
+# }}}
+
diff --git a/lib/dataset/dataset-view.co b/lib/dataset/dataset-view.co
new file mode 100644 (file)
index 0000000..eadaff0
--- /dev/null
@@ -0,0 +1,111 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseView,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+DataSetView = exports.DataSetView = BaseView.extend do # {{{
+    ctorName  : 'DataSetView'
+    tagName   : 'section'
+    className : 'dataset-ui dataset'
+    template  : require 'kraken/template/dataset'
+    
+    events:
+        'click .new-metric-button'       : 'newMetric'
+        'click .metrics .dataset-metric' : 'editMetric'
+    
+    views_by_cid : {}
+    active_view : null
+    
+    
+    constructor: function DataSetView
+        BaseView ...
+    
+    initialize: ->
+        @graph_id = @options.graph_id
+        BaseView::initialize ...
+        @views_by_cid = {}
+        @model.metrics.on 'add', @addMetric, this
+    
+    
+    newMetric: ->
+        # triggers 'add' on @model.metrics
+        @model.newMetric()
+        false
+    
+    addMetric: (metric) ->
+        console.log "#this.addMetric!", metric
+        if metric.view
+            _.remove @subviews, metric.view
+            delete @views_by_cid[metric.cid]
+        
+        @subviews.push view = new DataSetMetricView {model:metric, @graph_id}
+        @views_by_cid[metric.cid] = view
+        @$el.find '.metrics' .append view.render().el
+        
+        # @render()
+        view
+    
+    editMetric: (metric) ->
+        console.log "#this.editMetric!", metric
+        if metric instanceof [jQuery.Event, Event]
+            metric = $ metric.currentTarget .data 'model'
+        view = @views_by_cid[metric.cid]
+        console.log '  --> metric:', metric, 'view:', view
+        
+        @$el.find '.metrics' .removeClass 'metric-active'
+        view.$el.addClass 'metric-active'
+        view.$el.find '.activity-arrow' .css 'font-size', 2+view.$el.height()
+        @trigger 'edit-metric', metric
+    
+# }}}
+
+
+
+/**
+ * @class
+ */
+DataSetMetricView = exports.DataSetMetricView = BaseView.extend do # {{{
+    ctorName  : 'DataSetMetricView'
+    tagName   : 'tr'
+    className : 'dataset-metric metric'
+    template  : require 'kraken/template/dataset-metric'
+    
+    
+    
+    constructor: function DataSetMetricView
+        BaseView ...
+    
+    initialize: ->
+        @graph_id = @options.graph_id
+        BaseView::initialize ...
+    
+    
+    toTemplateLocals: ->
+        m = @model.toJSON()
+        m import
+            graph_id : @graph_id
+            viewClasses : _.compact([
+                if @model.isOk() then 'valid'   else 'invalid',
+                if m.visible     then 'visible' else 'hidden',
+                'disabled' if m.disabled,
+            ]).map( -> "metric-#it" ).join ' '
+            source :
+                if m.source_id and m.source_col_name
+                    "#{m.source_id}.#{m.source_col_name}"
+                else
+                    'No source'
+            timespan :
+                if _.every ts = m.timespan, op.ok
+                    "#{ts.start} to #{ts.end} by #{ts.step}"
+                else
+                    '&mdash;'
+        
+        # XXX: Icons/classes for visible/disabled?
+        { $, _, op, @model, view:this } import m
+    
+# }}}
+
diff --git a/lib/dataset/datasource-model.co b/lib/dataset/datasource-model.co
new file mode 100644 (file)
index 0000000..ef23d98
--- /dev/null
@@ -0,0 +1,47 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList, BaseView,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+DataSource = exports.DataSource = BaseModel.extend do # {{{
+    ctorName : 'DataSource'
+    urlRoot  : '/datasources'
+    
+    
+    constructor: function DataSource
+        BaseModel ...
+    
+    initialize: ->
+        BaseModel::initialize ...
+        
+    
+    defaults: ->
+        {}
+    
+    url: ->
+        "/data/#{@id}.json"
+    
+# }}}
+
+
+
+/**
+ * @class
+ */
+DataSourceList = exports.DataSourceList = BaseList.extend do # {{{
+    ctorName : 'DataSourceList'
+    urlRoot  : '/datasources'
+    model    : DataSource
+    
+    constructor: function DataSourceList
+        BaseList ...
+    
+    initialize : ->
+        BaseList::initialize ...
+    
+    
+# }}}
diff --git a/lib/dataset/datasource-ui-view.co b/lib/dataset/datasource-ui-view.co
new file mode 100644 (file)
index 0000000..25d52d1
--- /dev/null
@@ -0,0 +1,32 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList, BaseView,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+DataSourceUIView = exports.DataSourceUIView = BaseView.extend do # {{{
+    __bind__       : <[  ]>
+    ctorName       : 'DataSourceUIView'
+    tagName        : 'section'
+    className      : 'datasource-ui'
+    template       : require 'kraken/template/datasource-ui'
+    
+    
+    constructor: function DataSourceUIView
+        BaseView ...
+    
+    initialize: ->
+        @graph_id = @options.graph_id
+        BaseView::initialize ...
+    
+    toTemplateLocals: ->
+        locals = @model.toJSON()
+        locals import
+            graph_id         : @graph_id
+            source_summary   : 'Source Summary'
+            metric_summary   : 'Metric Summary'
+            timespan_summary : 'Timespan Summary'
+# }}}
diff --git a/lib/dataset/datasource-view.co b/lib/dataset/datasource-view.co
new file mode 100644 (file)
index 0000000..e7d6aa2
--- /dev/null
@@ -0,0 +1,25 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList, BaseView,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+DataSourceView = exports.DataSourceView = BaseView.extend do # {{{
+    __bind__       : <[  ]>
+    ctorName       : 'DataSourceView'
+    tagName        : 'section'
+    className      : 'datasource'
+    template       : require 'kraken/template/datasource'
+    
+    
+    
+    constructor: function DataSourceView
+        BaseView ...
+    
+    initialize: ->
+        BaseView::initialize ...
+    
+# }}}
diff --git a/lib/dataset/index.co b/lib/dataset/index.co
new file mode 100644 (file)
index 0000000..56d3e89
--- /dev/null
@@ -0,0 +1,14 @@
+metric_model       = require 'kraken/dataset/metric-model'
+metric_view        = require 'kraken/dataset/metric-view'
+metric_edit_view   = require 'kraken/dataset/metric-edit-view'
+datasource_model   = require 'kraken/dataset/datasource-model'
+datasource_view    = require 'kraken/dataset/datasource-view'
+datasource_ui_view = require 'kraken/dataset/datasource-ui-view'
+dataset_model      = require 'kraken/dataset/dataset-model'
+dataset_view       = require 'kraken/dataset/dataset-view'
+data_view          = require 'kraken/dataset/data-view'
+
+exports import metric_model     import metric_view      import metric_edit_view     \
+        import datasource_model import datasource_view  import datasource_ui_view   \
+        import dataset_model    import dataset_view                                 \
+                                import data_view
diff --git a/lib/dataset/metric-edit-view.co b/lib/dataset/metric-edit-view.co
new file mode 100644 (file)
index 0000000..b397ff5
--- /dev/null
@@ -0,0 +1,55 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseView,
+} = require 'kraken/base'
+{ Metric,
+} = require 'kraken/dataset/metric-model'
+{ DataSourceUIView,
+} = require 'kraken/dataset/datasource-ui-view'
+
+
+
+/**
+ * @class
+ */
+MetricEditView = exports.MetricEditView = BaseView.extend do # {{{
+    ctorName       : 'MetricEditView'
+    tagName        : 'section'
+    className      : 'metric-edit-ui'
+    template       : require 'kraken/template/metric-edit'
+    
+    graph_id           : null
+    dataset            : null
+    datasource_ui_view : null
+    
+    
+    constructor: function MetricEditView
+        BaseView ...
+    
+    initialize: ->
+        this import @options.{graph_id, dataset}
+        @model or= new Metric
+        BaseView::initialize ...
+        @subviews.push @datasource_ui_view = new DataSourceUIView {@model, @graph_id}
+        @$el.find '.metric-datasource' .append @datasource_ui_view.render().el
+    
+    
+    toTemplateLocals: ->
+        locals = BaseView::toTemplateLocals ...
+        locals import {@graph_id}
+    
+    build: ->
+        BaseView::build ...
+        if @datasource_ui_view
+            @$el.find '.metric-datasource' .append @datasource_ui_view.render().el
+        this
+    
+    editMetric: (metric) ->
+        console.log "#this.editMetric!", metric
+        @model = metric
+        @render()
+        @show()
+        this
+    
+# }}}
+
diff --git a/lib/dataset/metric-model.co b/lib/dataset/metric-model.co
new file mode 100644 (file)
index 0000000..63a88a0
--- /dev/null
@@ -0,0 +1,73 @@
+{ _, op,
+} = require 'kraken/util'
+{ BaseModel, BaseList,
+} = require 'kraken/base'
+
+
+/**
+ * @class
+ */
+Metric = exports.Metric = BaseModel.extend do # {{{
+    ctorName : 'Metric'
+    urlRoot  : '/metrics'
+    
+    /**
+     * Data source of the Metric.
+     * @type DataSource
+     */
+    source : null
+    
+    
+    constructor: function Metric
+        BaseModel ...
+    
+    initialize : ->
+        BaseModel::initialize ...
+    
+    
+    defaults : ->
+        index           : 0
+        label           : 'New Metric'
+        type            : 'int'
+        timespan        : { start:null, stop:null, step:null }
+        disabled        : false
+        
+        # DataSource
+        source_id       : null
+        source_col_idx  : -1
+        source_col_name : ''
+        
+        # Chart Options
+        color           : null
+        visible         : true
+        format_value    : null
+        format_axis     : null
+        
+        scale           : 1.0
+        transforms      : []
+    
+    
+    /**
+     * Check whether the metric has aiight-looking values so we don't
+     * attempt to graph unconfigured crap.
+     */
+    isOk: ->
+        (label = @get('label')) and label is not 'New Metric'
+        and @get('source_id')   and @get('source_col_idx') >= 0
+        and _.every @get('timespan'), op.ok
+    
+    
+    
+# }}}
+
+
+/**
+ * @class
+ */
+MetricList = exports.MetricList = BaseList.extend do # {{{
+    ctorName : 'MetricList'
+    urlRoot  : '/metrics'
+    model    : Metric
+    
+    constructor: function MetricList then BaseList ...
+# }}}
diff --git a/lib/dataset/metric-view.co b/lib/dataset/metric-view.co
new file mode 100644 (file)
index 0000000..e69de29
index 8d24a78..cdbdfd3 100644 (file)
@@ -21,6 +21,9 @@ GraphDisplayView = exports.GraphDisplayView = BaseView.extend do # {{{
     #     'submit .value' : 'update'
     
     
+    constructor: function GraphDisplayView
+        BaseView ...
+    
     initialize: ->
         @model or= new Graph
         BaseView::initialize ...
index 49dca93..c95a6e8 100644 (file)
@@ -7,7 +7,7 @@ _ = require 'kraken/util/underscore'
 } = require 'kraken/chart'
 { Graph,
 } = require 'kraken/graph/graph-model'
-{ DataSetView,
+{ DataView, DataSetView, DataSet,
 } = require 'kraken/dataset'
 
 root = do -> this
@@ -45,29 +45,33 @@ GraphEditView = exports.GraphEditView = BaseView.extend do # {{{
         'keypress form.details input[type="text"]' : 'onKeypress'
         'keypress form.options .value'             : 'onKeypress'
         'submit   form.details'                    : 'onDetailsSubmit'
+        'change   select'                          : 'onDetailsSubmit'
         'submit   form.options'                    : 'onOptionsSubmit'
         'change   input[type="checkbox"]'          : 'onOptionsSubmit'
     
-    subviews: []
-    ready: false
+    data  : {}
+    ready : false
     
     
+    constructor: function GraphEditView
+        BaseView ...
     
     initialize : (o={}) ->
-        @subviews = []
+        @data = {}
         @model or= new Graph
+        @id = _.domize 'graph', (@model.id or @model.get('slug') or @model.cid)
         BaseView::initialize ...
         # console.log "#this.initialize!"
         
         for name of @__debounce__
             @[name] = _.debounce @[name], DEBOUNCE_RENDER
         
-        @id = _.domize 'graph', (@model.get('slug') or @model.id&nb