Milimetric has submitted this change and it was merged.

Change subject: Add filters above timeseries graphs in the compare layout
......................................................................


Add filters above timeseries graphs in the compare layout

Bug: T104261
Change-Id: If4aab0e2537c7a08b38cf925546d747c601b78b3
---
M src/app/data-converters/timeseries-data.js
M src/app/startup.js
M src/components/a-b-compare/compare-timeseries.html
A src/components/filter-timeseries/filter-timeseries.html
A src/components/filter-timeseries/filter-timeseries.js
A src/css/066_checkbox.min.css
A src/css/067_list.min.css
M src/layouts/compare/index.html
M src/layouts/compare/index.js
M test/app/timeseries-data.js
10 files changed, 187 insertions(+), 4 deletions(-)

Approvals:
  Milimetric: Verified; Looks good to me, approved



diff --git a/src/app/data-converters/timeseries-data.js 
b/src/app/data-converters/timeseries-data.js
index 3347eb8..9e13fe5 100644
--- a/src/app/data-converters/timeseries-data.js
+++ b/src/app/data-converters/timeseries-data.js
@@ -168,6 +168,55 @@
     };
 
     /**
+     * Filter data by columns
+     * @param {Array} header - column names
+     * @param {Array} patternLabels - label names
+     * @return TimeseriesData
+     */
+    TimeseriesData.prototype.pickColumns = function (header, patternLabels) {
+        var self = this,
+            result = {
+                header: [],
+                colorLabels: [],
+                patternLabels: [],
+                rowsByDate: {}
+            };
+
+        _.forEach(self.rowsByDate, function (row, date) {
+            result.rowsByDate[date] = [];
+            _.forEach(self.rowsByDate[date], function() {
+                result.rowsByDate[date].push([]);
+            });
+        });
+
+        _.forEach(header, function (h, k) {
+            var i = self.header.indexOf(h);
+
+            // make sure the patternLabels also match
+            while (self.patternLabels[i] !== patternLabels[k]) {
+                i = self.header.indexOf(h, i + 1);
+            }
+
+            result.header.push(self.header[i]);
+            result.colorLabels.push(self.colorLabels[i]);
+            result.patternLabels.push(self.patternLabels[i]);
+            _.forEach(result.rowsByDate, function (row, date) {
+                _.forEach(result.rowsByDate[date], function(row2, j) {
+                    
result.rowsByDate[date][j].push(self.rowsByDate[date][j][i]);
+                });
+            });
+        });
+
+        return new TimeseriesData(
+            result.header,
+            result.rowsByDate,
+            result.colorLabels,
+            result.patternLabels,
+            this.duplicateDates
+        );
+    };
+
+    /**
      * Materializes the lodash chainable rowsByDate property and sorts it by 
date
      */
     TimeseriesData.prototype.rowData = function (options) {
diff --git a/src/app/startup.js b/src/app/startup.js
index 99dd92d..6da70f7 100644
--- a/src/app/startup.js
+++ b/src/app/startup.js
@@ -39,6 +39,7 @@
     ko.components.register('compare-sunburst', { require: 
'components/a-b-compare/compare-sunburst' });
     ko.components.register('compare-timeseries', { require: 
'components/a-b-compare/compare-timeseries' });
     ko.components.register('compare-stacked-bars', { require: 
'components/a-b-compare/compare-stacked-bars' });
+    ko.components.register('filter-timeseries', { require: 
'components/filter-timeseries/filter-timeseries' });
 
     // *********** END Funnel Layout Components ************ //
 
diff --git a/src/components/a-b-compare/compare-timeseries.html 
b/src/components/a-b-compare/compare-timeseries.html
index c586dbb..26b5b6b 100644
--- a/src/components/a-b-compare/compare-timeseries.html
+++ b/src/components/a-b-compare/compare-timeseries.html
@@ -1,3 +1 @@
-<!-- slow as can be and still a bit glitchy nvd3-timeseries params="height: 
500, series: series, colors: colors"/-->
-<!-- much faster but glitchy: rickshaw-timeseries params="height: 500, series: 
series, colors: colors"/-->
-<dygraphs-timeseries params="height: 500, data: data, colors: colors, 
annotations: annotations"/>
+<filter-timeseries params="data: data, colors: colors, annotations: 
annotations"></filter-timeseries>
diff --git a/src/components/filter-timeseries/filter-timeseries.html 
b/src/components/filter-timeseries/filter-timeseries.html
new file mode 100644
index 0000000..4686ca3
--- /dev/null
+++ b/src/components/filter-timeseries/filter-timeseries.html
@@ -0,0 +1,19 @@
+<div class="ui basic segment">
+    <div data-bind="foreach: categorizedHeader" class="ui horizontal divided 
list">
+        <div class="item">
+            <h4 data-bind="text: name"></h4>
+            <div data-bind="foreach: columns" class="ui list">
+                <div class="inline field item">
+                    <div class="ui toggle checkbox">
+                        <input type="checkbox" data-bind="attr: {id: 
$data.uniqueId}, checked: selected"/>
+                        <label data-bind="attr: {for: $data.uniqueId}">
+                            <span data-bind="text: title"></span>
+                        </label>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<dygraphs-timeseries params="height: 500, data: filteredData, colors: colors, 
annotations: annotations" />
diff --git a/src/components/filter-timeseries/filter-timeseries.js 
b/src/components/filter-timeseries/filter-timeseries.js
new file mode 100644
index 0000000..0ec4411
--- /dev/null
+++ b/src/components/filter-timeseries/filter-timeseries.js
@@ -0,0 +1,66 @@
+define(function (require) {
+    'use strict';
+
+    var ko = require('knockout'),
+        _ = require('lodash'),
+        templateMarkup = require('text!./filter-timeseries.html');
+
+    function FilterTimeseries (params) {
+        this.data = params.data;
+
+        // turn the header into a list of items that can be selected / 
deselected
+        // make it a computed so if the parent changes the data, this updates
+        this.filteredHeader = ko.computed(function () {
+            var data = ko.unwrap(this.data);
+
+            if (!data) {
+                return [];
+            }
+
+            return data.header.map(function (h, i){
+                return {
+                    index: i,
+                    title: h,
+                    patternLabel: data.patternLabels[i],
+                    name: data.patternLabels[i] + ' - ' + h,
+                    uniqueId: data.patternLabels[i] + ' - ' + h + 
(Math.floor(Math.random() * 100000)),
+                    selected: ko.observable(true)
+                };
+            });
+        }, this);
+
+        // split up by category so we can control the display better
+        this.categorizedHeader = ko.computed(function () {
+            var header = ko.unwrap(this.filteredHeader);
+
+            return _(header).transform(function (categorized, column) {
+                categorized[column.patternLabel] = 
categorized[column.patternLabel] || [];
+                categorized[column.patternLabel].push(column);
+            }, {}).transform(function (categories, columns, category) {
+                categories.push({
+                    name: category,
+                    columns: columns,
+                });
+            }, []).value();
+        }, this);
+
+        this.filteredData = ko.computed(function () {
+            var newHeader = _.filter(this.filteredHeader(), function (header) {
+                return header.selected();
+            });
+
+            return ko.unwrap(this.data).pickColumns(
+                _.pluck(newHeader, 'title'),
+                _.pluck(newHeader, 'patternLabel')
+            );
+        }, this);
+
+        this.colors = params.colors;
+        this.annotations = params.annotations;
+    }
+
+    return {
+        viewModel: FilterTimeseries,
+        template: templateMarkup
+    };
+});
diff --git a/src/css/066_checkbox.min.css b/src/css/066_checkbox.min.css
new file mode 120000
index 0000000..6fccd97
--- /dev/null
+++ b/src/css/066_checkbox.min.css
@@ -0,0 +1 @@
+../bower_modules/semantic/build/minified/modules/checkbox.min.css
\ No newline at end of file
diff --git a/src/css/067_list.min.css b/src/css/067_list.min.css
new file mode 120000
index 0000000..e53d4a2
--- /dev/null
+++ b/src/css/067_list.min.css
@@ -0,0 +1 @@
+../bower_modules/semantic/build/minified/views/list.min.css
\ No newline at end of file
diff --git a/src/layouts/compare/index.html b/src/layouts/compare/index.html
index 2dc7480..d81ca97 100644
--- a/src/layouts/compare/index.html
+++ b/src/layouts/compare/index.html
@@ -35,6 +35,8 @@
         <link href="/src/css/063_button.min.css" rel="stylesheet">
         <link href="/src/css/064_dropdown.min.css" rel="stylesheet">
         <link href="/src/css/065_divider.min.css" rel="stylesheet">
+        <link href="/src/css/066_checkbox.min.css" rel="stylesheet">
+        <link href="/src/css/067_list.min.css" rel="stylesheet">
         <!-- end semantic ui -->
 
         <link href="/src/css/068_nvd3.css" rel="stylesheet">
diff --git a/src/layouts/compare/index.js b/src/layouts/compare/index.js
index b78c1fc..7f7ce48 100644
--- a/src/layouts/compare/index.js
+++ b/src/layouts/compare/index.js
@@ -19,7 +19,10 @@
                 // above, and group them into bundles here.
                 'sunburst': ['components/visualizers/sunburst/sunburst'],
                 'stacked-bars': 
['components/visualizers/stacked-bars/stacked-bars'],
-                'dygraphs-timeseries': 
['components/visualizers/dygraphs-timeseries/dygraphs-timeseries'],
+                'filter-timeseries': [
+                    'components/filter-timeseries',
+                    
'components/visualizers/dygraphs-timeseries/dygraphs-timeseries'
+                ],
             }
         }
     };
diff --git a/test/app/timeseries-data.js b/test/app/timeseries-data.js
index f1493db..cc5f210 100644
--- a/test/app/timeseries-data.js
+++ b/test/app/timeseries-data.js
@@ -175,5 +175,48 @@
                 t1.merge(t2);
             }).toThrow('Can not be merged: has multiple rows per date.');
         });
+
+        it('should filter by column', function () {
+            var header = [
+                    'bounce-rate',
+                    'not-attempted-rate',
+                    'success-rate',
+                    'failure-rate',
+                    'bounce-rate',
+                    'not-attempted-rate',
+                    'success-rate',
+                    'failure-rate'
+                ],
+                rowsByDate = {
+                    '2015-04-01': [[1, 2, 3, 4, 5, 6, 7, 8]],
+                    '2015-04-02': [[9, 10, 11, 12, 13, 14, 15, 16]],
+                    '2015-04-03': [[17, 18, 19, 20, 21, 22, 23, 24]],
+                    '2015-04-04': [[25, 26, 27, 28, 29, 30, 31, 32]]
+                },
+                colorLabels = [
+                    'bounce-rate',
+                    'not-attempted-rate',
+                    'success-rate',
+                    'failure-rate',
+                    'bounce-rate',
+                    'not-attempted-rate',
+                    'success-rate',
+                    'failure-rate'
+                ],
+                patternLabels = ['VE', 'VE', 'VE', 'VE', 'WT', 'WT', 'WT', 
'WT'],
+                ts = new TimeseriesData(header, rowsByDate, colorLabels, 
patternLabels),
+                filteredTs = ts.pickColumns(
+                    ['success-rate', 'failure-rate'], ['VE', 'WT']);
+
+            expect(filteredTs.header).toEqual(['success-rate', 
'failure-rate']);
+            expect(filteredTs.rowsByDate).toEqual({
+                '2015-04-01': [[3, 8]],
+                '2015-04-02': [[11, 16]],
+                '2015-04-03': [[19, 24]],
+                '2015-04-04': [[27, 32]]
+                       });
+            expect(filteredTs.colorLabels).toEqual(['success-rate', 
'failure-rate']);
+            expect(filteredTs.patternLabels).toEqual(['VE', 'WT']);
+        });
     });
 });

-- 
To view, visit https://gerrit.wikimedia.org/r/231424
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: If4aab0e2537c7a08b38cf925546d747c601b78b3
Gerrit-PatchSet: 9
Gerrit-Project: analytics/dashiki
Gerrit-Branch: master
Gerrit-Owner: Milimetric <[email protected]>
Gerrit-Reviewer: Bmansurov <[email protected]>
Gerrit-Reviewer: Milimetric <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to