cBioPortal/iViz

View on GitHub
app/scripts/views/components/barChart/barChartTemplate.js

Summary

Maintainability
D
1 day
Test Coverage
/**
 * Created by Karthik Kalletla on 4/6/16.
 */
'use strict';
(function(Vue, d3, dc, iViz, _, $, cbio) {
  Vue.component('barChart', {
    template: '<div id={{chartDivId}} ' +
    'class="grid-item grid-item-w-2 grid-item-h-1 bar-chart" ' +
    ':attribute-id="attributes.attr_id" @mouseenter="mouseEnter" ' +
    ':layout-number="attributes.layout" ' +
    '@mouseleave="mouseLeave">' +
    '<chart-operations :show-log-scale="settings.showLogScale"' +
    ':show-operations="showOperations" :groupid="attributes.group_id" ' +
    ':chart-initialed = "!failedToInit"' +
    ':show-survival-icon.sync="showSurvivalIcon"' +
    ':reset-btn-id="resetBtnId" :chart-ctrl="barChart" ' +
    ':chart-id="chartId" :show-log-scale="showLogScale" ' +
    ':attributes="attributes"' +
    ':filters.sync="attributes.filter"></chart-operations>' +
    '<div class="dc-chart dc-bar-chart" align="center" ' +
    'style="float:none !important;" id={{chartId}} >' +
    '<div v-if="failedToInit" class="error-panel" align="center" style="padding-top: 10%;">' +
    '<error v-if="failedToInit" :message="errorMessage"></error>' +
    '</div></div>' +
    '<span class="text-center chart-title-span" ' +
    'id="{{chartId}}-title">{{displayName}}</span>' +
    '</div>',
    props: [
      'ndx', 'attributes', 'showedSurvivalPlot'
    ],
    data: function() {
      return {
        chartDivId:
          iViz.util.getDefaultDomId('chartDivId', this.attributes.attr_id),
        resetBtnId:
          iViz.util.getDefaultDomId('resetBtnId', this.attributes.attr_id),
        chartId:
          iViz.util.getDefaultDomId('chartId', this.attributes.attr_id),
        displayName: this.attributes.display_name,
        chartInst: {},
        barChart: {},
        showOperations: false,
        filtersUpdated: false,
        showSurvivalIcon: false,
        data: {},
        settings: {
          width: 400,
          height: 180,
          showLogScale: false,
          transitionDuration: iViz.opts.dc.transitionDuration
        },
        failedToInit: false,
        errorMessage: '',
        opts: {},
        numOfSurvivalCurveLimit: iViz.opts.numOfSurvivalCurveLimit || 20,
        addingChart: false
      };
    }, watch: {
      'attributes.filter': function(newVal) {
        if (this.filtersUpdated) {
          this.filtersUpdated = false;
        } else {
          this.filtersUpdated = true;
          if (newVal.length === 0) {
            this.chartInst.filterAll();
            this.$dispatch('update-filters', true);
          }
        }
        this.barChart.resetBarColor();
      },
      'showedSurvivalPlot': function() {
        this.updateShowSurvivalIcon();
      }
    }, events: {
      closeChart: function() {
        if (!this.failedToInit) {
          dc.deregisterChart(this.chartInst, this.attributes.group_id);
          this.chartInst.dimension().dispose();
        }
        this.$dispatch('close');
      },
      changeLogScale: function(logScaleChecked) {
        $('#' + this.chartId).find('svg').remove();
        this.chartInst.filterAll();
        this.$dispatch('update-filters', true);
        dc.deregisterChart(this.chartInst, this.attributes.group_id);
        this.initChart(logScaleChecked);
        this.chartInst.render();
      },
      addingChart: function(groupId, val) {
        if (this.attributes.group_id === groupId) {
          if (this.attributes.filter.length > 0) {
            if (val) {
              this.addingChart = val;
              this.chartInst.filter(null);
            } else {
              var filter_ = new dc.filters.RangedFilter(this.attributes.filter[0], this.attributes.filter[1]);
              this.chartInst.filter(filter_);
              this.addingChart = val;
            }
          }
        }
      },
      getRainbowSurvival: function() {
        var groups = [];
        var categories = this.barChart.getCurrentCategories('key');
        _.each(categories, function(category) {
          groups.push({
            name: category.name,
            caseIds: category.caseIds,
            curveHex: category.color
          });
        });
        this.barChart.colorBars(categories);
        this.$dispatch('create-rainbow-survival', {
          attrId: this.attributes.attr_id,
          subtitle: ' (' + this.attributes.display_name + ')',
          groups: groups,
          groupType: this.attributes.group_type
        });
      },
      resetBarColor: function(exceptionAttrIds) {
        if (!this.failedToInit &&
          _.isArray(exceptionAttrIds) && exceptionAttrIds.indexOf(this.attributes.attr_id) === -1) {
          this.barChart.resetBarColor();
        }
      }
    },
    methods: {
      updateShowSurvivalIcon: function() {
        if (this.showedSurvivalPlot) {
          // Disable rainbow survival if only one group present
          if (this.barChart.getCurrentCategories().length < 2 ||
            this.barChart.getCurrentCategories().length > this.numOfSurvivalCurveLimit) {
            this.showSurvivalIcon = false;
          } else {
            this.showSurvivalIcon = true;
          }
        } else {
          this.showSurvivalIcon = false;
        }
      },
      processBarchartData: function(_data) {
        var _self = this;
        var _dataIssue = false;
        var smallerOutlier = {};
        var greaterOutlier = {};
        var dataMetaKeys = {}; // Fast index unique dataMeta instead of using _.unique

        this.data.meta = _.map(_.filter(_.pluck(
          _data, this.opts.attrId), function(d) {
          if (isNaN(d) && !(_.isString(d) && (d.indexOf('>') !== -1 || d.indexOf('<') !== -1))) {
            _self.data.hasNA = true;
            d = 'NA';
          }
          return d !== 'NA';
        }), function(d) {
          var number = d;
          var smallerOutlierPattern = new RegExp('^<|(>=?)$');
          var greaterOutlierPattern = new RegExp('^>|(<=?)$');
          if (isNaN(d)) {
            if (smallerOutlierPattern.test(number)) {
              smallerOutlier[number.replace(/[^0-9.]/g, '')] = 1;
            } else if (greaterOutlierPattern.test(number)) {
              greaterOutlier[number.replace(/[^0-9.]/g, '')] = 1;
            } else {
              _dataIssue = true;
            }
          } else {
            number = parseFloat(d);
          }
          dataMetaKeys[number] = true;
          return number;
        });

        smallerOutlier = Object.keys(smallerOutlier);
        greaterOutlier = Object.keys(greaterOutlier);

        if (_dataIssue) {
          this.errorMessage = iViz.util.getDataErrorMessage('dataInvalid');
          this.failedToInit = true;
        } else {
          // for scientific small number
          if (this.data.meta[Math.ceil((this.data.meta.length * (1 / 2)))] < 0.001 &&
            this.data.meta[Math.ceil((this.data.meta.length * (1 / 2)))] > 0) {
            this.data.smallDataFlag = true;
            this.data.exponents = cbio.util.getDecimalExponents(this.data.meta);
            var findExtremeExponentResult = cbio.util.findExtremes(this.data.exponents);
            this.data.minExponent = findExtremeExponentResult[0];
            this.data.maxExponent = findExtremeExponentResult[1];
          } else {
            this.data.smallDataFlag = false;
          }

          var findExtremeResult = cbio.util.findExtremes(this.data.meta);
          if (iViz.util.isAgeClinicalAttr(this.attributes.attr_id) && _.min(this.data.meta) < 18 && (findExtremeResult[1] - findExtremeResult[0]) / 2 > 18) {
            this.data.min = 18;
          } else {
            this.data.min = findExtremeResult[0];
          }
          this.data.max = findExtremeResult[1];

          // noGrouping is true when number of different values less than or equal to 5. 
          // In this case, the chart sets data value as ticks' value directly. 
          this.data.noGrouping = false;
          if (Object.keys(dataMetaKeys).length <= 5 && this.data.meta.length > 0) {// for data less than 6 points
            this.data.noGrouping = true;
            this.data.uniqueSortedData = _.unique(findExtremeResult[3]);// use sorted value as ticks directly
          }

          if (smallerOutlier.length > 0) {
            this.data.min = Number(_.max(smallerOutlier));
            this.data.smallerOutlier = this.data.min;
          }

          if (greaterOutlier.length > 0) {
            this.data.max = Number(_.min(greaterOutlier));
            this.data.greaterOutlier = this.data.max;
          }

          this.data.attrId = this.attributes.attr_id;
          this.data.groupType = this.attributes.group_type;

          // logScale and noGroup cannot be true at same time
          // logScale and smallDataFlag cannot be true at same time
          if (((this.data.max - this.data.min) > 1000) && (this.data.min > 1) && !this.data.noGrouping) {
            this.settings.showLogScale = true;
          }
          this.barChart = new iViz.view.component.BarChart();
          this.barChart.setDownloadDataTypes(['tsv', 'pdf', 'svg']);
          this.initChart(this.settings.showLogScale);
          this.updateShowSurvivalIcon();
        }
        this.$dispatch('data-loaded', this.attributes.group_id, this.chartDivId);
      },
      mouseEnter: function() {
        this.showOperations = true;
      }, mouseLeave: function() {
        this.showOperations = false;
      }, initChart: function(logScaleChecked) {
        this.opts = _.extend(this.opts, {
          logScaleChecked: logScaleChecked
        });

        this.chartInst = this.barChart.init(this.ndx, this.data, this.opts);
        var self_ = this;
        this.chartInst.on('filtered', function(_chartInst, _filter) {
          // TODO : Right now we are manually checking for brush mouseup event.
          // This should be updated one latest dc.js is released
          // https://github.com/dc-js/dc.js/issues/627
          if (!self_.addingChart) {
            if (self_.filtersUpdated) {
              self_.filtersUpdated = false;
            } else {
              self_.chartInst.select('.brush').on('mouseup', function() {
                self_.filtersUpdated = true;
                if (typeof _filter !== 'undefined' && _filter !== null &&
                  _filter.length > 1 && self_.chartInst.hasFilter()) {
                  self_.attributes.filter = self_.barChart.rangeFilter(logScaleChecked, _filter);
                  self_.$dispatch('update-filters');
                } else if (self_.attributes.filter.length > 0) {
                  self_.attributes.filter = [];
                  self_.$dispatch('update-filters');
                }
              });
            }
          }
        });
      }
    },
    ready: function() {
      var _self = this;
      var _data = [];
      this.settings.width = window.iViz.styles.vars.barchart.width;
      this.settings.height = window.iViz.styles.vars.barchart.height;

      this.opts = _.extend(this.opts, {
        groupType: this.attributes.group_type,
        attrId: this.attributes.attr_id,
        displayName: this.attributes.display_name,
        chartDivId: this.chartDivId,
        chartId: this.chartId,
        groupid: this.attributes.group_id,
        width: this.settings.width,
        height: this.settings.height
      });

      
      _data = iViz.getGroupNdx(this.opts.groupid);
      _self.processBarchartData(_data);
    }
  });
})(
  window.Vue,
  window.d3,
  window.dc,
  window.iViz,
  window._,
  window.$ || window.jQuery,
  window.cbio
);