Эх сурвалжийг харах

HUE-4012 [charts] Graph 3rd dimension in stacked bar charts

Enrico Berti 9 жил өмнө
parent
commit
b445746

+ 52 - 32
desktop/core/src/desktop/static/desktop/js/ko.charts.js

@@ -162,6 +162,7 @@
     },
     update: function (element, valueAccessor) {
       var _options = valueAccessor();
+
       var _datum = _options.transformer(_options.datum);
       var _chart = $(element).data("chart");
 
@@ -180,11 +181,12 @@
           }
         }
 
-        if ((_isDiscrete && $(element).data('chart_type') !== 'discrete_bar') || (!_isDiscrete && $(element).data('chart_type') === 'discrete_bar')){
+        var _isPivot = _options.isPivot != null ? _options.isPivot : false;
+
+        if ((_isDiscrete && $(element).data('chart_type') !== 'discrete_bar') || (!_isDiscrete && $(element).data('chart_type') === 'discrete_bar') || (_isPivot && !$(element).data('chart_pivot')) || (!_isPivot && $(element).data('chart_pivot'))){
           ko.bindingHandlers.barChart.init(element, valueAccessor);
         }
         else {
-
           window.setTimeout(function () {
           var _d3 = d3.select($(element).find("svg")[0]);
           _d3.datum(_datum)
@@ -1011,7 +1013,7 @@
           _chart.multibar.stacked(typeof options.stacked != "undefined" ? options.stacked : false);
           _chart.onStateChange(options.onStateChange);
           _chart.onChartUpdate(function () {
-            _d3.selectAll("g.nv-x.nv-axis g text").each(function (d){
+            _d3.selectAll("g.nv-x.nv-axis g text").each(function (d) {
               insertLinebreaks(d, this);
             });
           });
@@ -1031,17 +1033,35 @@
               $(element).find("svg").empty();
             }
             $(element).data('chart_type', 'discrete_bar');
+            $(element).data('chart_pivot', false);
             _chart = nv.models.growingDiscreteBarChart()
-                .x(function (d) {
-                  return d.x
-                })
-                .y(function (d) {
-                  return d.y
-                })
-                .staggerLabels(true)
-                .tooltipContent(function (key, x, y) {
-                  return '<h3>' + key + '</h3><p>' + y + ' on ' + hueUtils.htmlEncode(x) + '</p>'
-                });
+              .x(function (d) {
+                return d.x
+              })
+              .y(function (d) {
+                return d.y
+              })
+              .staggerLabels(true)
+              .tooltipContent(function (key, x, y) {
+                return '<h3>' + key + '</h3><p>' + y + ' on ' + hueUtils.htmlEncode(x) + '</p>'
+              });
+          }
+          else if (_isDiscrete && _isPivot) {
+            if ($(element).find("svg").length > 0 && $(element).find(".nv-discreteBarWithAxes").length > 0) {
+              $(element).find("svg").empty();
+            }
+            $(element).data('chart_type', 'discrete_bar');
+            $(element).data('chart_pivot', true);
+            _chart = nv.models.growingMultiBarChart()
+              .x(function (d) {
+                return d.x
+              })
+              .y(function (d) {
+                return d.y
+              })
+              .tooltipContent(function (key, x, y) {
+                return '<h3>' + key + '</h3><p>' + y + ' on ' + hueUtils.htmlEncode(x) + '</p>'
+              });
           }
           else {
             if ($(element).find("svg").length > 0 && $(element).find(".nv-discreteBarWithAxes").length > 0) {
@@ -1078,23 +1098,23 @@
         _chart.transitionDuration(0);
 
         _chart.yAxis
-            .tickFormat(d3.format("s"));
+          .tickFormat(d3.format("s"));
 
         $(element).data("chart", _chart);
 
         var _d3 = ($(element).find("svg").length > 0) ? d3.select($(element).find("svg")[0]) : d3.select($(element)[0]).append("svg");
         _d3.datum(_datum)
-            .transition().duration(150)
-            .each("end", function () {
-              if (options.onComplete != null) {
-                options.onComplete();
-              }
-              if (isTimeline) {
-                _d3.selectAll("g.nv-x.nv-axis g text").each(function (d){
-                  insertLinebreaks(d, this);
-                });
-              }
-            }).call(_chart);
+          .transition().duration(150)
+          .each("end", function () {
+            if (options.onComplete != null) {
+              options.onComplete();
+            }
+            if (isTimeline) {
+              _d3.selectAll("g.nv-x.nv-axis g text").each(function (d) {
+                insertLinebreaks(d, this);
+              });
+            }
+          }).call(_chart);
 
         if (_chart.selectBars) {
           var _field = (typeof options.field == "function") ? options.field() : options.field;
@@ -1109,7 +1129,7 @@
                 _chart.selectBars({
                   field: item.field(),
                   selected: $.map(item.filter(), function (it) {
-                    return { values: it.value() };
+                    return {values: it.value()};
                   })
                 });
               }
@@ -1133,12 +1153,12 @@
       }, function () {
         var _d3 = ($(element).find("svg").length > 0) ? d3.select($(element).find("svg")[0]) : d3.select($(element)[0]).append("svg");
         _d3.selectAll(".nv-bar").on("click",
-            function (d, i) {
-              if (typeof options.onClick != "undefined") {
-                chartsUpdatingState();
-                options.onClick(d);
-              }
-            });
+          function (d, i) {
+            if (typeof options.onClick != "undefined") {
+              chartsUpdatingState();
+              options.onClick(d);
+            }
+          });
       });
     }
   }

+ 2 - 0
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -613,6 +613,8 @@
     self.chartScope = ko.observable(typeof snippet.chartScope != "undefined" && snippet.chartScope != null ? snippet.chartScope : "world");
     self.chartX = ko.observable(typeof snippet.chartX != "undefined" && snippet.chartX != null ? snippet.chartX : null);
     self.chartX.extend({notify: 'always'});
+    self.chartXPivot = ko.observable(typeof snippet.chartXPivot != "undefined" && snippet.chartXPivot != null ? snippet.chartXPivot : null);
+    self.chartXPivot.extend({notify: 'always'});
     self.chartYSingle = ko.observable(typeof snippet.chartYSingle != "undefined" && snippet.chartYSingle != null ? snippet.chartYSingle : null);
     self.chartYMulti = ko.observableArray(typeof snippet.chartYMulti != "undefined" && snippet.chartYMulti != null ? snippet.chartYMulti : []);
     self.chartData = ko.observableArray(typeof snippet.chartData != "undefined" && snippet.chartData != null ? snippet.chartData : []);

+ 105 - 41
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -1618,15 +1618,22 @@ ${ hueIcons.symbols() }
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART" class="nav-header">${_('value')}</li>
     </ul>
 
-    <div style="overflow-y: auto; max-height: 220px" data-bind="visible: chartType() != '' && (chartType() == ko.HUE_CHARTS.TYPES.BARCHART || chartType() == ko.HUE_CHARTS.TYPES.LINECHART)">
+    <div style="overflow-y: auto; max-height: 220px" data-bind="visible: chartType() != '' && ((chartType() == ko.HUE_CHARTS.TYPES.BARCHART && !chartXPivot()) || chartType() == ko.HUE_CHARTS.TYPES.LINECHART)">
       <ul class="unstyled" data-bind="foreach: result.cleanedNumericMeta">
         <li><label class="checkbox"><input type="checkbox" data-bind="checkedValue: name, checked: $parent.chartYMulti" /> <span data-bind="text: $data.name"></span></label></li>
       </ul>
     </div>
-    <div data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART || chartType() == ko.HUE_CHARTS.TYPES.MAP || chartType() == ko.HUE_CHARTS.TYPES.GRADIENTMAP || chartType() == ko.HUE_CHARTS.TYPES.SCATTERCHART">
+    <div data-bind="visible: (chartType() == ko.HUE_CHARTS.TYPES.BARCHART && chartXPivot()) || chartType() == ko.HUE_CHARTS.TYPES.PIECHART || chartType() == ko.HUE_CHARTS.TYPES.MAP || chartType() == ko.HUE_CHARTS.TYPES.GRADIENTMAP || chartType() == ko.HUE_CHARTS.TYPES.SCATTERCHART">
       <select data-bind="options: chartType() == ko.HUE_CHARTS.TYPES.GRADIENTMAP ? result.cleanedMeta : result.cleanedNumericMeta, value: chartYSingle, optionsText: 'name', optionsValue: 'name', optionsCaption: '${_ko('Choose a column...')}', select2: { width: '100%', placeholder: '${ _ko("Choose a column...") }', update: chartYSingle, dropdownAutoWidth: true}" class="input-medium"></select>
     </div>
 
+    <ul class="nav nav-list" style="border: none; background-color: #FFF" data-bind="visible: chartType() === ko.HUE_CHARTS.TYPES.BARCHART">
+      <li class="nav-header">${_('group')}</li>
+    </ul>
+    <div data-bind="visible: chartType() === ko.HUE_CHARTS.TYPES.BARCHART">
+      <select data-bind="options: result.cleanedMeta, value: chartXPivot, optionsText: 'name', optionsValue: 'name', optionsCaption: '${_ko('Choose a column to pivot...')}', select2: { width: '100%', placeholder: '${ _ko("Choose a column to pivot...") }', update: chartXPivot, dropdownAutoWidth: true}" class="input-medium"></select>
+    </div>
+
     <ul class="nav nav-list" style="border: none; background-color: #FFF" data-bind="visible: chartType() != '' && chartType() == ko.HUE_CHARTS.TYPES.MAP">
       <li class="nav-header">${_('label')}</li>
     </ul>
@@ -1776,7 +1783,7 @@ ${ hueIcons.symbols() }
                       transformer: pieChartDataTransformer, maxWidth: 350, parentSelector: '.chart-container' }, visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART" class="chart"></div>
 
                 <div data-bind="attr:{'id': 'barChart_'+id()}, barChart: {datum: {counts: result.data, sorting: chartSorting(), snippet: $data}, fqs: ko.observableArray([]), hideSelection: true,
-                      transformer: multiSerieDataTransformer, stacked: false, showLegend: true},  stacked: true, showLegend: true, visible: chartType() == ko.HUE_CHARTS.TYPES.BARCHART" class="chart"></div>
+                      transformer: multiSerieDataTransformer, stacked: false, showLegend: true, isPivot: typeof chartXPivot() !== 'undefined'},  stacked: true, showLegend: true, visible: chartType() == ko.HUE_CHARTS.TYPES.BARCHART" class="chart"></div>
 
                 <div data-bind="attr:{'id': 'lineChart_'+id()}, lineChart: {datum: {counts: result.data, sorting: chartSorting(), snippet: $data},
                       transformer: multiSerieDataTransformer, showControls: false, enableSelection: false }, visible: chartType() == ko.HUE_CHARTS.TYPES.LINECHART" class="chart"></div>
@@ -2659,52 +2666,109 @@ ${ hueIcons.symbols() }
     if (rawDatum.snippet.chartX() != null && rawDatum.snippet.chartYMulti().length > 0) {
       var _plottedSerie = 0;
 
-      rawDatum.snippet.result.meta().forEach(function (meta) {
-        if (rawDatum.snippet.chartYMulti().indexOf(meta.name) > -1) {
-          var col = meta.name;
-          var _idxValue = -1;
-          var _idxLabel = -1;
-          var _isXDate = false;
-          rawDatum.snippet.result.meta().forEach(function (icol, idx) {
-            if (icol.name == rawDatum.snippet.chartX()) {
-              _isXDate = icol.type.toUpperCase().indexOf('DATE') > -1;
-              _idxLabel = idx;
-            }
-            if (icol.name == col) {
-              _idxValue = idx;
-            }
-          });
+      if (typeof rawDatum.snippet.chartXPivot() !== 'undefined') {
+        var _idxValue = -1;
+        var _idxLabel = -1;
+        var _isXDate = false;
+
+        rawDatum.snippet.result.meta().forEach(function (icol, idx) {
+          if (icol.name == rawDatum.snippet.chartX()) {
+            _isXDate = icol.type.toUpperCase().indexOf('DATE') > -1;
+            _idxLabel = idx;
+          }
+          if (icol.name == rawDatum.snippet.chartYSingle()) {
+            _idxValue = idx;
+          }
+        });
 
-          if (_idxValue > -1) {
-            var _data = [];
+        rawDatum.snippet.result.meta().forEach(function (meta, cnt) {
+          if (rawDatum.snippet.chartXPivot() === meta.name) {
+            var _idxPivot = cnt;
             var colors = HueColors.cuiD3Scale();
-            $(rawDatum.counts()).each(function (cnt, item) {
-              _data.push({
-                series: _plottedSerie,
-                x: _isXDate ? moment(item[_idxLabel]) : hueUtils.html2text(item[_idxLabel]),
-                y: item[_idxValue] * 1,
-                color: colors[cnt % colors.length],
-                obj: item
+            var pivotValues = $.map(rawDatum.counts(), function (p) {
+              return p[_idxPivot];
+            });
+            pivotValues = pivotValues.filter(function (item, pos) {
+              return pivotValues.indexOf(item) === pos;
+            });
+            pivotValues.forEach(function (val) {
+              var _data = [];
+              $(rawDatum.counts()).each(function (cnt, item) {
+                if (item[_idxPivot] === val) {
+                  _data.push({
+                    x: _isXDate ? moment(item[_idxLabel]) : hueUtils.html2text(item[_idxLabel]),
+                    y: item[_idxValue] * 1,
+                    color: colors[cnt % colors.length],
+                    obj: item
+                  });
+                }
               });
+              if (rawDatum.sorting == "asc") {
+                _data.sort(function (a, b) {
+                  return a.y - b.y
+                });
+              }
+              if (rawDatum.sorting == "desc") {
+                _data.sort(function (a, b) {
+                  return b.y - a.y
+                });
+              }
+              _datum.push({
+                key: val,
+                values: _data
+              });
+            });
+          }
+        });
+      }
+      else {
+        rawDatum.snippet.result.meta().forEach(function (meta) {
+          if (rawDatum.snippet.chartYMulti().indexOf(meta.name) > -1) {
+            var col = meta.name;
+            var _idxValue = -1;
+            var _idxLabel = -1;
+            var _isXDate = false;
+            rawDatum.snippet.result.meta().forEach(function (icol, idx) {
+              if (icol.name == rawDatum.snippet.chartX()) {
+                _isXDate = icol.type.toUpperCase().indexOf('DATE') > -1;
+                _idxLabel = idx;
+              }
+              if (icol.name == col) {
+                _idxValue = idx;
+              }
             });
-            if (rawDatum.sorting == "asc") {
-              _data.sort(function (a, b) {
-                return a.y - b.y
+
+            if (_idxValue > -1) {
+              var _data = [];
+              var colors = HueColors.cuiD3Scale();
+              $(rawDatum.counts()).each(function (cnt, item) {
+                _data.push({
+                  series: _plottedSerie,
+                  x: _isXDate ? moment(item[_idxLabel]) : hueUtils.html2text(item[_idxLabel]),
+                  y: item[_idxValue] * 1,
+                  color: colors[cnt % colors.length],
+                  obj: item
+                });
               });
-            }
-            if (rawDatum.sorting == "desc") {
-              _data.sort(function (a, b) {
-                return b.y - a.y
+              if (rawDatum.sorting == "asc") {
+                _data.sort(function (a, b) {
+                  return a.y - b.y
+                });
+              }
+              if (rawDatum.sorting == "desc") {
+                _data.sort(function (a, b) {
+                  return b.y - a.y
+                });
+              }
+              _datum.push({
+                key: col,
+                values: _data
               });
+              _plottedSerie++;
             }
-            _datum.push({
-              key: col,
-              values: _data
-            });
-            _plottedSerie++;
           }
-        }
-      });
+        });
+      }
     }
     return _datum;
   }