cBioPortal/iViz

View on GitHub
app/scripts/views/components/survivalChart/template.js

Summary

Maintainability
D
2 days
Test Coverage
/**
 * @author Yichao Sun on 5/18/16.
 */
'use strict';
(function(Vue, dc, iViz, _) {
  Vue.component('survival', {
    template: '<div id={{chartDivId}} ' +
    'class="grid-item grid-item-h-2 grid-item-w-2" ' +
    ':attribute-id="attributes.attr_id" @mouseenter="mouseEnter" ' +
    '@mouseleave="mouseLeave">' +
    '<chart-operations :show-operations="showOperations" ' +
    ':show-download-icon.sync="showDownloadIcon" ' +
    ':has-chart-title="hasChartTitle" :display-name="displayName" ' +
    ':groupid="attributes.group_id" :reset-btn-id="resetBtnId" :chart-ctrl="chartInst" ' +
    ' :chart-id="chartId" ' +
    ':attributes="attributes"></chart-operations>' +
    '<div v-show="!showLoad" :class="{\'show-loading-content\': showLoad}"' +
    'class="dc-chart dc-scatter-plot" align="center" id={{chartId}} ></div>' +
    '<div v-show="showLoad" class="progress-bar-parent-div" :class="{\'show-loading-bar\': showLoad}">' +
    '<progress-bar :div-id="loadingBar.divId" :status="loadingBar.status" :opts="loadingBar.opts" ' +
    ':type="loadingBar.type" :disable="loadingBar.disable" ></progress-bar></div>' +
    '</div>',
    props: [
      'ndx', 'attributes'
    ],
    created: function() {
    },
    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: {},
        showOperations: false,
        fromWatch: false,
        fromFilter: false,
        hasChartTitle: true,
        showLoad: true,
        excludeNa: true,
        hasFilters: false,
        showDownloadIcon: true,
        showingRainbowSurvival: false,
        groups: [],
        invisibleDimension: {},
        loadingBar: {
          status: 0,
          type: 'percentage',
          divId: iViz.util.getDefaultDomId('progressBarId', this.attributes.attr_id),
          opts: {},
          disable: false
        },
        mainDivQtip: ''
      };
    },
    watch: {
      excludeNa: function() {
        if (this.showingRainbowSurvival) {
          this.updateRainbowSurvival();
        } else {
          this.updatePlotGroups(this.hasFilters);
          this.updatePlot();
          this.$dispatch('remove-rainbow-survival');
        }
      },
      showLoad: function(newVal) {
        if (newVal) {
          this.initialInfinityLoadingBar();
        } else {
          this.loadingBar.disable = true;
        }
      }
    },
    events: {
      'show-loader': function() {
        this.showLoad = true;
      },
      'update-special-charts': function(hasFilters) {
        this.showingRainbowSurvival = false;
        this.updatePlotGroups(hasFilters);
        this.updatePlot();
        this.$dispatch('remove-rainbow-survival');
      },
      'closeChart': function() {
        this.invisibleDimension.dispose();
        this.$dispatch('close');
      },
      'addingChart': function(groupId, val) {
        if (this.attributes.group_id === groupId) {
          if (this.attributes.filter.length > 0) {
            if (val) {
              this.invisibleDimension.filterAll();
            } else {
              var filtersMap = {};
              _.each(this.attributes.filter, function(filter) {
                if (filtersMap[filter] === undefined) {
                  filtersMap[filter] = true;
                }
              });
              this.invisibleDimension.filterFunction(function(d) {
                return (filtersMap[d] !== undefined);
              });
            }
          }
        }
      },
      'create-rainbow-survival': function(opts) {
        var _opts = $.extend(true, {}, opts);
        _opts.groups = this.calcCurvesData(
          _opts.groups, _opts.groupType);
        this.groups = $.extend(true, [], _opts.groups);
        if (_opts.subtitle) {
          this.displayName = this.attributes.display_name + _opts.subtitle;
        }
        this.showingRainbowSurvival = true;
        this.updateRainbowSurvival();
      }
    },
    methods: {
      updateRainbowSurvival: function() {
        var groups = $.extend(true, [], this.groups);
        if (this.excludeNa) {
          groups = _.filter(groups, function(group) {
            return group.name !== 'NA';
          });
        }
        this.updatePlot(groups);
      },
      updatePlot: function(groups) {
        // Display name may be changed due to the rainbow survival
        this.displayName = this.attributes.display_name;

        this.chartInst.update(
          groups ? groups : this.groups, this.chartId, this.attributes.attr_id);
        this.checkDownloadableStatus();
        this.updateQtipContent();
        this.showLoad = false;
      },
      updatePlotGroups: function(hasFilters) {
        var _type = this.attributes.group_type;
        var attrId = _type === 'patient' ? 'patient_uid' : 'sample_uid';
        var groupId = this.attributes.group_id;
        var _selectedCases = [];
        var _nonNaCases = [];
        var _allCases = iViz.getCaseUIDs(_type).sort();
        var groups = [];

        this.hasFilters = hasFilters;

        if (this.hasFilters) {
          var filteredClinicalAttrs = {};
          _.each(this.$root.groups, function(group) {
            var _attrId = group.type === 'patient' ? 'patient_uid' : 'sample_uid';
            if (!filteredClinicalAttrs.hasOwnProperty(group.id)) {
              filteredClinicalAttrs[group.id] = {
                attrId: _attrId,
                attrs: [],
                nonNaCases: []
              };
            }
            filteredClinicalAttrs[group.id].attrs = [];

            // Loop through attrList instead of only using attr_id
            // Combination chart has its own attr_id, but the clinical data
            // it's using are listed under attrList
            _.each(_.filter(group.attributes, function(attr) {
              return attr.filter.length > 0;
            }), function(item) {
              filteredClinicalAttrs[group.id].attrs.push(_.pick(item, 'attr_id', 'attrList'));
            });
          });
          if (this.excludeNa) {
            // Find qualified cases in each group.
            _.each(filteredClinicalAttrs, function(group, _groupId) {
              var data_ = iViz.getGroupNdx(_groupId);
              var nonNaCases = [];

              //Check whether case contains NA value on filtered attrs
              var _attrs = {};
              _.each(group.attrs, function(groupAttr) {
                _.each(groupAttr.attrList, function(listItem) {
                  _attrs[listItem] = 1;
                })
              });
              _attrs = Object.keys(_attrs);

              _.each(data_, function(data) {
                var hasNaWithinAttrs = false;
                _.some(_attrs, function(attr) {
                  // All data has been normalized to NA for different NA values
                  if (data[attr] === undefined || data[attr] === 'NA') {
                    hasNaWithinAttrs = true;
                    return true;
                  }
                });

                if (!hasNaWithinAttrs) {
                  var _caseId = data[group.attrId];
                  if (groupId !== _groupId) {
                    if (_type === 'patient') {
                      _caseId = iViz.getPatientUIDs(_caseId);
                    } else {
                      _caseId = iViz.getSampleUIDs(_caseId)
                    }
                  }
                  if (_.isArray(_caseId)) {
                    nonNaCases.push.apply(nonNaCases, _caseId);
                  } else {
                    nonNaCases.push(_caseId);
                  }
                }
              });
              group.nonNaCases = nonNaCases.sort();
            });

            // Find unique data from each group.
            var _list = _.pluck(filteredClinicalAttrs, 'nonNaCases');
            for (var i = 0; i < _list.length; i++) {
              if (i === 0) {
                _nonNaCases = _list[0];
                continue;
              }
              _nonNaCases = iViz.util.intersection(_nonNaCases, _list[i]);
            }
            _selectedCases = iViz.util.intersection(_nonNaCases, _.pluck(this.invisibleDimension.top(Infinity), attrId).sort());
            _allCases = iViz.util.intersection(_nonNaCases, _allCases);
          } else {
            _selectedCases =
              _.pluck(this.invisibleDimension.top(Infinity), attrId).sort();
          }
        }
        if (_selectedCases.length === 0) {
          groups.push({
            id: 0,
            caseIds: _allCases,
            curveHex: '#2986e2',
            name: 'All Patients'
          });
        } else {
          groups = [{
            id: 0,
            caseIds: _selectedCases,
            curveHex: 'red',
            name: 'Selected Patients'
          }, {
            id: 1,
            caseIds: iViz.util.difference(
              _allCases, _selectedCases),
            curveHex: '#2986e2',
            name: 'Unselected Patients'
          }];
        }
        groups = this.calcCurvesData(groups, _type);
        this.groups = groups;
      },
      updateQtipContent: function() {
        if (this.mainDivQtip) {
          var self_ = this;
          var qtipContent = ['<div>'];
          var groups = this.chartInst.getGroups();
          var api = this.mainDivQtip.qtip('api');
          _.each(groups, function(group) {
            qtipContent.push(
              '<div class="category-item" curve-id="' + group.id + '">' +
              '<svg width="12" height="12">' +
              '<rect height="12" width="12" fill="' +
              group.curveHex + '"></rect>' +
              '</svg><span>' + group.name + '</span></div>');
          });
          qtipContent.push('</div>');

          qtipContent.push('<div class="checkbox-div">' +
            '<input type="checkbox" class="checkbox" ' +
            (this.excludeNa ? 'checked' : '') + '><span>' +
            'Exclude patients with NA for any of the selected attribute(s)</span></div>');
          api.set('content.text', qtipContent.join(''));

          // Tender tooltip after updating content
          // Otherwise, api.elements.tooltip will return null.
          api.render();

          var tooltip = api.elements.tooltip;
          tooltip.find('.category-item').click(function() {
            var curveId = $(this).attr('curve-id');
            self_.chartInst.highlightCurve(curveId);
          });
          tooltip.find('.checkbox-div .checkbox').change(function() {
            self_.excludeNa = this.checked;
          });
        }
      },
      mouseEnter: function() {
        this.showOperations = true;
        this.$emit('initMainDivQtip');
      }, mouseLeave: function() {
        this.showOperations = false;
      },
      calcCurvesData: function(groups, groupType) {
        var data_ = iViz.getGroupNdx(this.attributes.group_id);
        var survivalType = this.attributes.group_type;
        _.each(groups, function(group, index) {
          group.id = index;
          group.data = [];

          // If group type is sample, need to convert sample ID to patient ID.
          if (groupType === 'sample') {
            group.caseIds = iViz.util.idMapping(iViz.getCasesMap('sample'),
              group.caseIds);
          }
          _.each(group.caseIds, function(id) {
            //  var _index = iViz.getCaseIndices(survivalType)[id];
            group.data.push(data_[id]);
          });
        });
        return groups;
      },
      initMainDivQtip: function() {
        var self_ = this;
        var chartDivId = self_.chartDivId;
        self_.mainDivQtip = $('#' + chartDivId).qtip({
          id: chartDivId + '-qtip',
          style: {
            classes: 'qtip-light qtip-rounded qtip-shadow forceZindex qtip-max-width dc-survival-chart-qtip'
          },
          show: {event: 'mouseover', delay: 300},
          hide: {fixed: true, delay: 300, event: 'mouseleave'},
          // hide: false,
          position: {
            my: 'left center',
            at: 'center right',
            viewport: $(window)
          },
          content: '<div>Loading...</div>'
        });
        self_.updateQtipContent();
      },
      initialInfinityLoadingBar: function() {
        this.loadingBar.type = 'infinite';
      },
      checkDownloadableStatus: function() {
        if (this.chartInst.downloadIsEnabled()) {
          this.showDownloadIcon = true;
        } else {
          this.showDownloadIcon = false;
        }
      }
    },
    ready: function() {
      var _self = this;
      var attrId =
        this.attributes.group_type === 'patient' ? 'patient_uid' : 'sample_uid';
      this.invisibleDimension = this.ndx.dimension(function(d) {
        return d[attrId];
      });
      var _opts = {
        width: window.iViz.styles.vars.survival.width,
        height: window.iViz.styles.vars.survival.height,
        chartId: this.chartId,
        attrId: this.attributes.attr_id,
        title: this.attributes.display_name,
        type: this.attributes.group_type
      };
      var _type = this.attributes.group_type;

      _self.chartInst = new iViz.view.component.Survival();
      _self.chartInst.setDownloadDataTypes(['pdf', 'svg']);

      var data = iViz.getGroupNdx(this.attributes.group_id);
      if (this.$root.hasfilters) {
        this.updatePlotGroups(true);
      } else {
        var groups = [{
          id: 0,
          name: 'All Patients',
          curveHex: '#2986e2',
          caseIds: iViz.getCaseUIDs(_type)
        }];
        this.groups = this.calcCurvesData(groups, _type);
      }
      _self.chartInst.init(this.groups, data, _opts);
      _self.checkDownloadableStatus();
      _self.showLoad = false;
      _self.$once('initMainDivQtip', _self.initMainDivQtip);
      this.$dispatch('data-loaded', this.attributes.group_id, this.chartDivId);
    }
  });
})(
  window.Vue,
  window.dc,
  window.iViz,
  window._
);