UNPKG

ucsc-xena-client

Version:

UCSC Xena Client. Functional genomics visualizations.

421 lines (362 loc) 16.4 kB
'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _PureComponent2 = require('../PureComponent'); var _PureComponent3 = _interopRequireDefault(_PureComponent2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var React = require('react'); var _ = require('../underscore_ext'); var CohortOrDisease = require('../views/CohortOrDisease'); var VariableSelect = require('../views/VariableSelect'); var GhostVariableSelect = require('../views/GhostVariableSelect'); var getStepperState = require('./getStepperState'); var _require = require('../models/datasetJoins'), getColSpec = _require.getColSpec; var _require2 = require('../heatmapColors'), defaultColorClass = _require2.defaultColorClass; var uuid = require('../uuid'); var Rx = require('../rx'); var parsePos = require('../parsePos'); var parseGeneSignature = require('../parseGeneSignature'); var parseInput = require('../parseInput'); var _require3 = require('../models/fieldSpec'), signatureField = _require3.signatureField; /*function toWordList(str) { // Have to wrap trim because it takes a 2nd param. return _.filter(_.map(str.split(/,| |\n|\t/), s => s.trim(), _.identity)); }*/ var typeWidth = { matrix: 136, chrom: 200 }; // 'features' is a problem here, because they are not unique across datasets. // How do we look up features w/o a dataset? function getValueType(dataset, features, fields) { var type = dataset.type, valuetype = _.getIn(features, [fields[0], 'valuetype']); if (type === 'mutationVector') { return 'mutation'; } if (type === 'genomicSegment') { return 'segmented'; } if (type === 'clinicalMatrix') { return valuetype === 'category' ? 'coded' : 'float'; } return 'float'; } function getFieldType(dataset, features, fields, probes, pos) { if (dataset.type === 'mutationVector') { return dataset.dataSubType.search(/SV|structural/i) !== -1 ? 'SV' : 'mutation'; } if (dataset.type === 'genomicSegment') { return 'segmented'; } if (dataset.type === 'clinicalMatrix') { return 'clinical'; } // We treat probes in chrom view (pos) as geneProbes return probes ? 'probes' : fields.length > 1 && !pos ? 'genes' : 'geneProbes'; } function sigFields(fields, _ref) { var genes = _ref.genes, weights = _ref.weights; return { missing: genes.filter(function (p, i) { return !fields[i]; }), genes: fields.filter(function (p) { return p; }), weights: weights.filter(function (p, i) { return fields[i]; }) }; } // XXX duplicated in VariableSelect. var getAssembly = function getAssembly(datasets, dsID) { return _.getIn(datasets, [dsID, 'assembly'], _.getIn(datasets, [dsID, 'probemapMeta', 'assembly'])); }; // XXX handle position in all genomic datatypes? function columnSettings(datasets, features, dsID, input, fields, probes) { var meta = datasets[dsID], pos = parsePos(input.trim(), getAssembly(datasets, dsID)), sig = parseGeneSignature(input.trim()), fieldType = getFieldType(meta, features[dsID], fields, probes, pos), fieldsInput = sig ? sig.genes : parseInput(input), normalizedFields = pos ? [pos.chrom + ':' + pos.baseStart + '-' + pos.baseEnd] : (['segmented', 'mutation', 'SV'].indexOf(fieldType) !== -1 ? [fields[0]] : fields).map(function (f, i) { return f ? f : fieldsInput[i] + " (unknown)"; }); // My god, this is a disaster. if (sig) { var _sigFields = sigFields(fields, sig), missing = _sigFields.missing, genes = _sigFields.genes, weights = _sigFields.weights, missingLabel = _.isEmpty(missing) ? '' : ' (missing terms: ' + missing.join(', ') + ')'; return signatureField('signature' + missingLabel, { signature: ['geneSignature', dsID, genes, weights], missing: missing, fieldType: 'probes', defaultNormalization: meta.colnormalization, colorClass: defaultColorClass, fields: [input], dsID: dsID }); } return _extends({}, fieldType === 'geneProbes' ? { showIntrons: true } : {}, { fields: normalizedFields, fetchType: 'xena', valueType: getValueType(meta, features[dsID], fields), fieldType: fieldType, dsID: dsID, defaultNormalization: meta.colnormalization, // XXX this assumes fields[0] doesn't appear in features if ds is genomic //fieldLabel: _.getIn(features, [dsID, fields[0], 'longtitle'], fields.join(', ')), fieldLabel: _.getIn(features, [dsID, fields[0], 'longtitle']) || normalizedFields.join(', '), colorClass: defaultColorClass, assembly: meta.assembly || _.getIn(meta, ['probemapMeta', 'assembly']) }); } // Configuration for first and second variable select cards that are displayed during wizard. var variableSelectConfig = { 'FIRST_COLUMN': { helpText: { 'Genotypic': 'Add a gene (e.g. RB1) or position (e.g. chr19p), and select a dataset.', 'Phenotypic': 'Add a phenotype (e.g. sample type, age).' }, pos: 1, title: 'First Variable' }, 'SECOND_COLUMN': { helpText: { 'Genotypic': 'Add a gene (e.g. RB1) or position (e.g. chr19p), and select a dataset.', 'Phenotypic': 'Add a phenotype (e.g. sample type, age).' }, pos: 2, title: 'Second Variable' } }; function wizardColumns(wizardMode, stepperState, cohortSelectProps, datasetSelectProps, width) { if (wizardMode) { if (stepperState === 'COHORT') { return [React.createElement(CohortOrDisease, _extends({ key: 'c1' }, cohortSelectProps)), React.createElement(GhostVariableSelect, _extends({ key: 'c2', width: width }, variableSelectConfig.FIRST_COLUMN)), React.createElement(GhostVariableSelect, _extends({ key: 'c3', width: width }, variableSelectConfig.SECOND_COLUMN))]; } if (stepperState === 'FIRST_COLUMN') { return [React.createElement(VariableSelect, _extends({ key: 'c2' }, variableSelectConfig[stepperState], datasetSelectProps)), React.createElement(GhostVariableSelect, _extends({ key: 'c3', width: width }, variableSelectConfig.SECOND_COLUMN))]; } if (stepperState === 'SECOND_COLUMN') { return [React.createElement(VariableSelect, _extends({ key: 'c3' }, variableSelectConfig[stepperState], datasetSelectProps))]; } } return []; } var preferredLabels = { 'gene expression': 'Gene Expression', 'copy number': 'Copy Number', 'simple somatic mutation': 'Somatic Mutation' }; var activeHubs = function activeHubs(hubs) { return _.keys(hubs).filter(function (hub) { return hubs[hub].user; }); }; var cohortName = function cohortName(cohort) { return _.get(cohort, 'name'); }; var getCohortPreferred = function getCohortPreferred(table, cohort) { return _.get(table, cohortName(cohort)); }; function getPreferedDatasets(cohort, cohortPreferred, hubs, datasets) { var active = activeHubs(hubs), // Only include datasets on active hubs & real existing datasets (more reliable against mistakes in the .json file) preferred = _.pick(getCohortPreferred(cohortPreferred, cohort), function (ds) { return _.contains(active, JSON.parse(ds).host) && _.has(datasets, ds); }); // filter out key used to support geneset/pathway view preferred = _.pick(preferred, function (ds, key) { return key !== "copy number for pathway view"; }); // Use isEmpty to handle 1) no configured preferred datasets or 2) preferred dataset list // is empty after filtering by active hubs. return _.isEmpty(preferred) ? [] : _.keys(preferred).map(function (type) { return { dsID: preferred[type], label: preferredLabels[type] }; }); } function getPreferredPhenotypes(cohort, cohortPreferredPhenotypes, hubs) { var active = activeHubs(hubs), preferred = _.filter(getCohortPreferred(cohortPreferredPhenotypes, cohort), function (_ref2) { var dsID = _ref2.dsID; return _.contains(active, JSON.parse(dsID).host); }); return _.isEmpty(preferred) ? [] : preferred; } var consolidateFeatures = function consolidateFeatures(featureSet) { return _.reduce(featureSet, function (all, features, dsID) { var strippedFeatures = _.toArray(_.mapObject(features, function (f) { return _.extend(f, { dsID: dsID, label: f.longtitle || f.name }); })); return all.concat(strippedFeatures); }, []); }; var sortFeatures = function sortFeatures(features) { return _.sortBy(features, function (f) { return f.label.toUpperCase(); }); }; var removeSampleID = function removeSampleID(features) { return _.filter(features, function (f) { return f.name !== "sampleID"; }); }; var computeSettings = _.curry(function (datasets, features, inputFields, width, dataset, matches) { var ds = datasets[dataset]; var settings = columnSettings(datasets, features, dataset, inputFields, matches.fields, matches.type === 'probes'), colSpec = getColSpec([settings], datasets), columnLabel = (ds.dataSubType && !ds.dataSubType.match(/phenotype/i) ? ds.dataSubType + ' - ' : '') + (ds.dataSubType && ds.dataSubType.match(/phenotype/i) ? '' : ds.label); return _.assoc(colSpec, 'width', _.contains(['mutationVector', 'segmented'], ds.type) ? typeWidth.chrom : typeWidth.matrix, 'dataset', ds, 'columnLabel', columnLabel, 'user', { columnLabel: columnLabel, fieldLabel: colSpec.fieldLabel }); }); // 1) if appState.editing, then set editing state, and render editor. // 2) if wizard mode // add cohort editor, or // add 1st column editor, or // add 2nd column editor function addWizardColumns(Component) { var _class, _temp; return _temp = _class = function (_PureComponent) { _inherits(_class, _PureComponent); function _class(props) { _classCallCheck(this, _class); var _this = _possibleConstructorReturn(this, (_class.__proto__ || Object.getPrototypeOf(_class)).call(this, props)); _this.onCancel = function () { _this.props.callback(['edit-column', null]); }; _this.onCohortSelect = function (cohort) { _this.props.callback(['cohort', cohort, typeWidth.matrix]); }; _this.onDatasetSelect = function (posOrId, input, datasetList, fieldList) { var _this$props = _this.props, _this$props$wizard = _this$props.wizard, datasets = _this$props$wizard.datasets, features = _this$props$wizard.features, defaultWidth = _this$props.appState.defaultWidth, isPos = _.isNumber(posOrId), settingsList = _.mmap(datasetList, fieldList, computeSettings(datasets, features, input, defaultWidth)); _this.props.callback(['add-column', posOrId].concat(_toConsumableArray(settingsList.map(function (settings, i) { return { id: !i && !isPos ? posOrId : uuid(), settings: settings }; })))); }; var editing = props.editing; _this.state = { editing: editing }; return _this; } _createClass(_class, [{ key: 'componentWillMount', value: function componentWillMount() { var callback = this.props.callback; this.sub = Rx.Observable.of(true).concat(Rx.Observable.fromEvent(window, 'resize')).debounceTime(200).subscribe(function () { return callback(['viewportWidth', document.documentElement.clientWidth]); }); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.sub.unsubscribe(); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(newProps) { var editing = newProps.editing; // XXX set timeout here for flipping back, when done. this.setState({ editing: editing }); // XXX If we had a cohort but lost it (e.g. due to change in servers), // and the columnEdit is closed: open it. // if (!this.state.openColumnEdit && // this.props.appState.cohort[0] && // !newProps.appState.cohort[0]) { // // this.setState({openColumnEdit: true}); // } } }, { key: 'addColumns', value: function addColumns() { var _props = this.props, children = _props.children, appState = _props.appState, wizard = _props.wizard, cohort = appState.cohort, wizardMode = appState.wizardMode, defaultWidth = appState.defaultWidth, servers = appState.servers, cohorts = wizard.cohorts, cohortPreferred = wizard.cohortPreferred, cohortMeta = wizard.cohortMeta, cohortPhenotype = wizard.cohortPhenotype, datasets = wizard.datasets, features = wizard.features, stepperState = getStepperState(appState), editing = appState.editing, preferred = cohortPreferred && getPreferedDatasets(cohort, cohortPreferred, servers, datasets), preferredPhenotypes = cohortPhenotype && getPreferredPhenotypes(cohort, cohortPhenotype, servers), width = defaultWidth, cohortSelectProps = { cohorts: cohorts, cohortMeta: cohortMeta, onSelect: this.onCohortSelect, width: width }, datasetSelectProps = { datasets: datasets, features: features && sortFeatures(removeSampleID(consolidateFeatures(features))), preferred: preferred, basicFeatures: preferredPhenotypes, onSelect: this.onDatasetSelect, width: width }, columns = React.Children.toArray(children), cancelIcon = React.createElement( 'i', { className: 'material-icons', onClick: this.onCancel }, 'cancel' ), withEditor = columns.map(function (el) { return editing === el.props.id ? React.createElement(VariableSelect, _extends({ key: editing, actionKey: editing, pos: editing, fields: appState.columns[editing].fieldSpecs[0].fields, dataset: appState.columns[editing].fieldSpecs[0].dsID, title: 'Edit Variable' }, datasetSelectProps, { colId: el.props.label, controls: cancelIcon })) : el; }), withNewColumns = _.flatmap(withEditor, function (el, i) { return editing === i ? [el, React.createElement(VariableSelect, _extends({ key: i, actionKey: i, pos: i, title: 'Add Variable' }, datasetSelectProps, { controls: cancelIcon }))] : [el]; }); return withNewColumns.concat(wizardColumns(wizardMode, stepperState, cohortSelectProps, datasetSelectProps, width)); } }, { key: 'render', value: function render() { var _props2 = this.props, children = _props2.children, _props2$appState = _props2.appState, editing = _props2$appState.editing, wizardMode = _props2$appState.wizardMode, columns = editing != null || wizardMode ? this.addColumns() : children; return React.createElement( Component, this.props, columns ); } }]); return _class; }(_PureComponent3.default), _class.displayName = 'SpreadsheetWizardColumns', _temp; } module.exports = addWizardColumns;