UNPKG

ucsc-xena-client

Version:

UCSC Xena Client. Functional genomics visualizations.

365 lines (307 loc) 12.4 kB
'use strict'; // Helper methods needed by multiple controllers. var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 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); } } var Rx = require('../rx'); var xenaQuery = require('../xenaQuery'); var _ = require('../underscore_ext'); var _require = require('./errors'), reifyErrors = _require.reifyErrors, collectResults = _require.collectResults; var fetch = require('../fieldFetch'); var kmModel = require('../models/km'); var _require2 = require('../models/datasetJoins'), getColSpec = _require2.getColSpec; var _require3 = require('../models/fieldSpec'), signatureField = _require3.signatureField; var defaultServers = require('../defaultServers'); var publicServers = defaultServers.publicServers; var gaEvents = require('../gaEvents'); // pick up signature fetch require('../models/signatures'); var datasetResults = function datasetResults(resps) { return collectResults(resps, function (servers) { return _.object(_.flatmap(servers, function (s) { return _.map(s.datasets, function (d) { return [d.dsID, d]; }); })); }); }; function datasetQuery(servers, cohort) { return Rx.Observable.zipArray(_.map(servers, function (server) { return reifyErrors(xenaQuery.datasetList(server, [cohort.name]).map(function (datasets) { return { server: server, datasets: datasets }; }), { host: server }); })).flatMap(datasetResults); } function fetchDatasets(serverBus, servers, cohort) { serverBus.next(['datasets', datasetQuery(servers, cohort)]); } var MAX_SAMPLES = 50 * 1000; var allSamples = _.curry(function (cohort, max, server) { return xenaQuery.cohortSamples(server, cohort, max === Infinity ? null : max); }); function unionOfGroup(gb) { return _.union.apply(_, _toConsumableArray(_.map(gb, function (_ref) { var _ref2 = _slicedToArray(_ref, 1), v = _ref2[0]; return v; }))); } function logSampleSources(cohortResps) { var havingSamples = cohortResps.filter(function (_ref3) { var _ref4 = _slicedToArray(_ref3, 1), v = _ref4[0]; return v.length > 0; }).map(function (_ref5) { var _ref6 = _slicedToArray(_ref5, 3), server = _ref6[2]; return server; }), types = new Set(havingSamples.map(function (s) { return s === defaultServers.servers.localHub ? 'localhost' : // counting all ucsc-hosted hubs as public s.indexOf('.xenahubs.net') !== -1 ? 'public' : 'private'; })); if (types.has('localhost')) { // user has samples on localhost hub gaEvents('hubs', 'localhost'); } if (types.has('private')) { // user has samples on private hub that isn't localhost gaEvents('hubs', 'private'); } if (types.size > 1 && (types.has('localhost') || types.has('private'))) { // user is joining samples across hubs, and not all of them // are xena public hubs. gaEvents('hubs', 'cohort join'); } } // Performance of this is probably poor, esp. due to underscore's horrible // n^2 set operations. function cohortHasPrivateSamples(cohortResps) { var _$groupBy = _.groupBy(cohortResps, function (_ref7) { var _ref8 = _slicedToArray(_ref7, 3), server = _ref8[2]; return _.contains(publicServers, server); }), pub = _$groupBy['true'], priv = _$groupBy['false'], pubSamps = unionOfGroup(pub), privSamps = unionOfGroup(priv); return _.difference(privSamps, pubSamps).length > 0; } function filterSamples(sampleFilter, samples) { return sampleFilter ? _.intersection(sampleFilter, samples) : samples; } // For the cohort, query all servers, // return a stream per-cohort, each of which returns an event // [cohort, [sample, ...]]. // By not combining them here, we can uniformly handle errors, below. var cohortSamplesQuery = function cohortSamplesQuery(servers, max, _ref9) { var name = _ref9.name, sampleFilter = _ref9.sampleFilter; return _.map(servers, allSamples(name, max)).map(function (resp, j) { return resp.map(function (samples) { return [filterSamples(sampleFilter, samples), samples.length >= max, servers[j]]; }); }); }; var collateSamples = _.curry(function (cohorts, max, resps) { var serverOver = _.any(resps, function (_ref10) { var _ref11 = _slicedToArray(_ref10, 2), over = _ref11[1]; return over; }), cohortSamples = unionOfGroup(resps || []).slice(0, max), cohortOver = cohortSamples.length >= max, hasPrivateSamples = cohortHasPrivateSamples(resps); logSampleSources(resps); return { samples: cohortSamples, over: serverOver || cohortOver, hasPrivateSamples: hasPrivateSamples }; }); // reifyErrors should be pass the server name, but in this expression we don't have it. function samplesQuery(servers, cohort, max) { return Rx.Observable.zipArray(cohortSamplesQuery(servers, max, cohort).map(reifyErrors)).flatMap(function (resps) { return collectResults(resps, collateSamples(cohort, max)); }); } function fetchSamples(serverBus, servers, cohort, allowOverSamples) { serverBus.next(['samples', samplesQuery(servers, cohort, allowOverSamples ? Infinity : MAX_SAMPLES)]); } function fetchColumnData(serverBus, samples, id, settings) { // XXX Note that the widget-data-xxx slots are leaked in the groupBy // in main.js. We need a better mechanism. // if (Math.random() > 0.5) { // testing error handling serverBus.next([['widget-data', id], fetch(settings, samples)]); // } else { // serverBus.onNext([['widget-data', id], Rx.Observable.throw(new Error('Injected error'))]); // } } function resetZoom(state) { var count = _.getIn(state, ['cohortSamples', 'length'], 0); return _.updateIn(state, ["zoom"], function (z) { return _.merge(z, { count: count, index: 0 }); }); } var setCohortRelatedFields = function setCohortRelatedFields(state, cohort) { return _.assoc(state, 'cohort', cohort, 'hasPrivateSamples', false, 'cohortSamples', [], 'columns', {}, 'columnOrder', [], 'data', {}, 'survival', null, 'km', _.assoc(state.km, ['id'], null)); }; // This adds or overwrites a 'sample' column in the state. // Called from setCohort, the column data will be fetched after // the sample list returns from the server. function addSampleColumn(state, width) { if (!_.get(state.cohort, ['name'])) { return state; } var field = signatureField('samples', { columnLabel: 'Sample ID', valueType: 'coded', signature: ['samples'] }), newOrder = _.has(state.columns, 'samples') ? state.columnOrder : [].concat(_toConsumableArray(state.columnOrder), ['samples']), colSpec = getColSpec([field], {}), settings = _.assoc(colSpec, 'width', Math.round(width == null ? 136 : width), 'user', _.pick(colSpec, ['columnLabel', 'fieldLabel'])), newState = _.assocIn(state, ['columns', 'samples'], settings, ['columnOrder'], newOrder); return _.assocIn(newState, ['data', 'samples', 'status'], 'loading'); } var setWizardAndMode = function setWizardAndMode(state) { return _.assocIn(state, ['wizardMode'], true, ['mode'], 'heatmap'); }; var setCohort = _.curry(function (cohort, width, state) { return addSampleColumn(setWizardAndMode(resetZoom(setCohortRelatedFields(state, cohort))), width); }); var userServers = function userServers(state) { return _.keys(state.servers).filter(function (h) { return state.servers[h].user; }); }; var fetchCohortData = function fetchCohortData(serverBus, state) { var user = userServers(state); if (state.cohort) { fetchDatasets(serverBus, user, state.cohort); fetchSamples(serverBus, user, state.cohort, state.allowOverSamples); } }; var unionOfResults = function unionOfResults(resps) { return collectResults(resps, function (results) { return _.union.apply(_, _toConsumableArray(results)); }); }; function cohortQuery(servers) { return Rx.Observable.zipArray(_.map(servers, function (s) { return reifyErrors(xenaQuery.allCohorts(s), { host: s }); })).flatMap(unionOfResults); } function fetchCohorts(serverBus, state, newState) { var _ref12 = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}, force = _ref12.force; var user = userServers(state), newUser = userServers(newState); if (force || !_.listSetsEqual(user, newUser)) { serverBus.next(['cohorts', cohortQuery(newUser)]); } } function updateWizard(serverBus, state, newState) { var opts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; fetchCohorts(serverBus, state, newState, opts); var user = userServers(newState); // If there's a bookmark on wizard mode step 2, will we fail // to load the dataset? if (newState.cohort && (opts.force || newState.cohort.name !== _.get(state.cohort, 'name'))) { fetchDatasets(serverBus, user, newState.cohort); } } var clearWizardCohort = function clearWizardCohort(state) { return _.assocIn(state, ['wizard', 'datasets'], undefined, ['wizard', 'features'], undefined); }; // // survival fields // var hasSurvFields = function hasSurvFields(vars) { return !!_.some(_.values(kmModel.survivalOptions), function (option) { return vars[option.ev] && vars[option.tte] && vars[option.patient]; }); }; var probeFieldSpec = function probeFieldSpec(_ref13) { var dsID = _ref13.dsID, name = _ref13.name; return { dsID: dsID, fetchType: 'xena', // maybe take from dataset meta instead of hard-coded valueType: 'float', fieldType: 'probes', fields: [name] }; }; var codedFieldSpec = function codedFieldSpec(_ref14) { var dsID = _ref14.dsID, name = _ref14.name; return { dsID: dsID, fetchType: 'xena', // maybe take from dataset meta instead of hard-coded valueType: 'coded', fieldType: 'clinical', fields: [name] }; }; function mapToObj(keys, fn) { return _.object(keys, _.map(keys, fn)); } function survivalFields(cohort, datasets, features) { var vars = kmModel.pickSurvivalVars(features), fields = {}; if (hasSurvFields(vars)) { fields['patient'] = getColSpec([codedFieldSpec(vars.patient)], datasets); _.values(kmModel.survivalOptions).forEach(function (option) { if (vars[option.ev] && vars[option.tte]) { fields[option.ev] = getColSpec([probeFieldSpec(vars[option.ev])], datasets); fields[option.tte] = getColSpec([probeFieldSpec(vars[option.tte])], datasets); } }); if (_.has(fields, 'ev') && _.keys(fields).length > 3) { delete fields.ev; delete fields.tte; } } return fields; } // If field set has changed, re-fetch. function fetchSurvival(serverBus, state) { var _Rx$Observable; var _state$wizard = state.wizard, datasets = _state$wizard.datasets, features = _state$wizard.features, _state$spreadsheet = state.spreadsheet, cohort = _state$spreadsheet.cohort, survival = _state$spreadsheet.survival, cohortSamples = _state$spreadsheet.cohortSamples, fields = survivalFields(cohort, datasets, features), survFields = _.keys(fields), refetch = _.some(survFields, function (f) { return !_.isEqual(fields[f], _.getIn(survival, [f, 'field'])); }), queries = _.map(survFields, function (key) { return fetch(fields[key], cohortSamples); }), collate = function collate(data) { return mapToObj(survFields, function (k, i) { return { field: fields[k], data: data[i] }; }); }; refetch && serverBus.next(['km-survival-data', (_Rx$Observable = Rx.Observable).zipArray.apply(_Rx$Observable, _toConsumableArray(queries)).map(collate)]); } module.exports = { fetchCohortData: fetchCohortData, fetchCohorts: fetchCohorts, fetchColumnData: fetchColumnData, fetchDatasets: fetchDatasets, fetchSamples: fetchSamples, fetchSurvival: fetchSurvival, resetZoom: resetZoom, setCohort: setCohort, userServers: userServers, updateWizard: updateWizard, clearWizardCohort: clearWizardCohort, datasetQuery: datasetQuery };