diff --git a/src/pages/studyView/StudyViewPageStore.ts b/src/pages/studyView/StudyViewPageStore.ts index 1dce27ca2eb..684e9b2b0ba 100644 --- a/src/pages/studyView/StudyViewPageStore.ts +++ b/src/pages/studyView/StudyViewPageStore.ts @@ -88,6 +88,8 @@ import { getFilteredAndCompressedDataIntervalFilters, getUniqueKeyFromMolecularProfileIds, getMolecularProfileIdsFromUniqueKey, + getClinicalDataBySamples, + updateSavedUserPreferenceChartIds, } from './StudyViewUtils'; import MobxPromise from 'mobxpromise'; import { SingleGeneQuery } from 'shared/lib/oql/oql-parser'; @@ -2198,62 +2200,6 @@ export class StudyViewPageStore { return this.clinicalDataBinPromises[uniqueKey]; } - private async getClinicalDataBySamples(samples: Sample[]) { - let clinicalData: { - [sampleId: string]: { [attributeId: string]: string }; - } = {}; - - let sampleClinicalData = await defaultClient.fetchClinicalDataUsingPOST( - { - clinicalDataType: 'SAMPLE', - clinicalDataMultiStudyFilter: { - identifiers: _.map(samples, sample => { - return { - entityId: sample.sampleId, - studyId: sample.studyId, - }; - }), - } as ClinicalDataMultiStudyFilter, - } - ); - - _.forEach(sampleClinicalData, item => { - clinicalData[item.uniqueSampleKey] = { - ...(clinicalData[item.uniqueSampleKey] || {}), - [item.clinicalAttributeId]: item.value, - }; - }); - - let patientClinicalData = await defaultClient.fetchClinicalDataUsingPOST( - { - clinicalDataType: ClinicalDataTypeEnum.PATIENT, - clinicalDataMultiStudyFilter: { - identifiers: _.map(samples, sample => { - return { - entityId: sample.patientId, - studyId: sample.studyId, - }; - }), - } as ClinicalDataMultiStudyFilter, - } - ); - - const patientSamplesMap = _.groupBy( - samples, - sample => sample.uniquePatientKey - ); - - _.forEach(patientClinicalData, item => { - (patientSamplesMap[item.uniquePatientKey] || []).forEach(sample => { - clinicalData[sample.uniqueSampleKey] = { - ...(clinicalData[sample.uniqueSampleKey] || {}), - [item.clinicalAttributeId]: item.value, - }; - }); - }); - return clinicalData; - } - readonly molecularProfiles = remoteData({ await: () => [this.queriedPhysicalStudyIds], invoke: async () => { @@ -3198,7 +3144,9 @@ export class StudyViewPageStore { toJS(this.studyIds) ); if (userSettings) { - return userSettings.chartSettings || []; + return updateSavedUserPreferenceChartIds( + userSettings.chartSettings + ); } } return []; @@ -4734,9 +4682,7 @@ export class StudyViewPageStore { invoke: async () => { let sampleClinicalDataMap: { [attributeId: string]: { [attributeId: string]: string }; - } = await this.getClinicalDataBySamples( - this.selectedSamples.result - ); + } = await getClinicalDataBySamples(this.selectedSamples.result); return _.reduce( this.selectedSamples.result, (acc, next) => { @@ -4998,7 +4944,7 @@ export class StudyViewPageStore { @autobind public async getDownloadDataPromise() { - let sampleClinicalDataMap = await this.getClinicalDataBySamples( + let sampleClinicalDataMap = await getClinicalDataBySamples( this.selectedSamples.result ); diff --git a/src/pages/studyView/StudyViewUtils.spec.ts b/src/pages/studyView/StudyViewUtils.spec.ts index 5d4551afe39..2a84df25123 100644 --- a/src/pages/studyView/StudyViewUtils.spec.ts +++ b/src/pages/studyView/StudyViewUtils.spec.ts @@ -54,6 +54,7 @@ import { getBinName, getGroupedClinicalDataByBins, getFilteredAndCompressedDataIntervalFilters, + updateSavedUserPreferenceChartIds, } from 'pages/studyView/StudyViewUtils'; import { DataBin, @@ -82,6 +83,7 @@ import { } from 'shared/lib/Colors'; import { IStudyViewDensityScatterPlotDatum } from './charts/scatterPlot/StudyViewDensityScatterPlot'; import { shallow } from 'enzyme'; +import { ChartUserSetting } from './StudyViewPageStore'; describe('StudyViewUtils', () => { const emptyStudyViewFilter: StudyViewFilter = { @@ -3328,4 +3330,62 @@ describe('StudyViewUtils', () => { ); }); }); + + describe('updateChartIds', () => { + const chartSetting1: ChartUserSetting = { + id: 'SAMPLE_CANCER_TYPE', + chartType: 'PIE_CHART', + layout: { + x: 0, + y: 0, + w: 1, + h: 1, + }, + patientAttribute: false, + }; + const chartSetting2: ChartUserSetting = { + id: 'SAMPLE_SAMPLE_TYPE', + chartType: 'PIE_CHART', + layout: { + x: 0, + y: 0, + w: 1, + h: 1, + }, + patientAttribute: false, + }; + + it('should return correct chart settings', () => { + assert.deepEqual( + updateSavedUserPreferenceChartIds([chartSetting1]), + [{ ...chartSetting1, id: 'CANCER_TYPE' }] + ); + assert.deepEqual( + updateSavedUserPreferenceChartIds([ + { ...chartSetting1, id: 'CANCER_TYPE' }, + ]), + [{ ...chartSetting1, id: 'CANCER_TYPE' }] + ); + assert.deepEqual( + updateSavedUserPreferenceChartIds([ + chartSetting1, + chartSetting2, + ]), + [ + { ...chartSetting1, id: 'CANCER_TYPE' }, + { ...chartSetting2, id: 'SAMPLE_TYPE' }, + ] + ); + assert.deepEqual( + updateSavedUserPreferenceChartIds([ + { ...chartSetting1, id: 'CANCER_TYPE' }, + { ...chartSetting2, id: 'SAMPLE_TYPE' }, + ]), + [ + { ...chartSetting1, id: 'CANCER_TYPE' }, + { ...chartSetting2, id: 'SAMPLE_TYPE' }, + ] + ); + }); + }); }); diff --git a/src/pages/studyView/StudyViewUtils.tsx b/src/pages/studyView/StudyViewUtils.tsx index a9324525b73..c348ecf9f52 100644 --- a/src/pages/studyView/StudyViewUtils.tsx +++ b/src/pages/studyView/StudyViewUtils.tsx @@ -16,6 +16,7 @@ import { PatientIdentifier, Sample, ClinicalData, + ClinicalDataMultiStudyFilter, } from 'shared/api/generated/CBioPortalAPI'; import * as React from 'react'; import { buildCBioPortalPageUrl } from '../../shared/api/urls'; @@ -2417,3 +2418,103 @@ export function getFilteredAndCompressedDataIntervalFilters( : undefined; return { start, end } as any; } + +export async function getClinicalDataBySamples(samples: Sample[]) { + let clinicalData: { + [sampleId: string]: { [attributeId: string]: string }; + } = {}; + + let sampleClinicalData = await defaultClient.fetchClinicalDataUsingPOST({ + clinicalDataType: 'SAMPLE', + clinicalDataMultiStudyFilter: { + identifiers: _.map(samples, sample => { + return { + entityId: sample.sampleId, + studyId: sample.studyId, + }; + }), + } as ClinicalDataMultiStudyFilter, + }); + + _.forEach(sampleClinicalData, item => { + clinicalData[item.uniqueSampleKey] = { + ...(clinicalData[item.uniqueSampleKey] || {}), + [item.clinicalAttributeId]: item.value, + }; + }); + + let patientClinicalData = await defaultClient.fetchClinicalDataUsingPOST({ + clinicalDataType: ClinicalDataTypeEnum.PATIENT, + clinicalDataMultiStudyFilter: { + identifiers: _.map(samples, sample => { + return { + entityId: sample.patientId, + studyId: sample.studyId, + }; + }), + } as ClinicalDataMultiStudyFilter, + }); + + const patientSamplesMap = _.groupBy( + samples, + sample => sample.uniquePatientKey + ); + + _.forEach(patientClinicalData, item => { + (patientSamplesMap[item.uniquePatientKey] || []).forEach(sample => { + clinicalData[sample.uniqueSampleKey] = { + ...(clinicalData[sample.uniqueSampleKey] || {}), + [item.clinicalAttributeId]: item.value, + }; + }); + }); + return clinicalData; +} + +export function updateSavedUserPreferenceChartIds( + chartSettings: ChartUserSetting[] +): ChartUserSetting[] { + const customChartRegex = /^CUSTOM_FILTERS_\d+/i; + const chartIdWithDataTypeRegex = /^(?:PATIENT_|SAMPLE_)([a-zA-Z0-9_]+)/i; + let numberOfClinicalAttributeCharts = 0; + let numberOfChartRequiringUpdates = 0; + const specialChartKeySet = _.reduce( + UniqueKey, + (acc, next) => { + acc[next] = true; + return acc; + }, + {} as { [id: string]: boolean } + ); + + chartSettings.forEach(chartSetting => { + let customChartmatch = chartSetting.id.match(customChartRegex); + if ( + !customChartmatch && + specialChartKeySet[chartSetting.id] === undefined + ) { + numberOfClinicalAttributeCharts++; + let match = chartSetting.id.match(chartIdWithDataTypeRegex); + if (!!match) { + numberOfChartRequiringUpdates++; + } + } + }); + + // Some of the clinical attributes id contains SAMPLE_ or PATIENT_ prefix. Updating them would not show those charts. + // Only way is to check if the chart ids requires update is to see if the + // number of clinical attribute charts shown is same as the number of chart ids requiring updates + if (numberOfClinicalAttributeCharts === numberOfChartRequiringUpdates) { + return chartSettings.map(chartSetting => { + let match = chartSetting.id.match(chartIdWithDataTypeRegex); + if (!!match) { + return { + ...chartSetting, + id: match[1], + }; + } + return chartSetting; + }); + } + return chartSettings; +}