From 1fb506ccb26c05d661366f2754fde5bfcbe152bc Mon Sep 17 00:00:00 2001 From: Adam Abeshouse Date: Fri, 13 Aug 2021 18:36:51 +0200 Subject: [PATCH] Specify unprofiled in any or unprofiled in every for results view filtering Signed-off-by: Adam Abeshouse --- src/pages/resultsView/ResultsViewPageStore.ts | 69 ++++++++++++---- .../comparison/ResultsViewComparisonUtils.ts | 7 +- .../resultsView/settings/SettingsMenu.tsx | 81 +++++++++++++++---- .../AnnotationFilteringSettings.ts | 2 +- .../driverAnnotations/SettingsMenu.tsx | 5 -- 5 files changed, 126 insertions(+), 38 deletions(-) diff --git a/src/pages/resultsView/ResultsViewPageStore.ts b/src/pages/resultsView/ResultsViewPageStore.ts index d38328cd7ee..8bc5a2b8f97 100644 --- a/src/pages/resultsView/ResultsViewPageStore.ts +++ b/src/pages/resultsView/ResultsViewPageStore.ts @@ -521,7 +521,9 @@ export type ModifyQueryParams = { interface IResultsViewExclusionSettings { setExcludeGermlineMutations: (value: boolean) => void; - setHideUnprofiledSamples: (value: boolean) => void; + setHideUnprofiledSamples: ( + value: IAnnotationFilterSettings['hideUnprofiledSamples'] + ) => void; } /* fields and methods in the class below are ordered based on roughly @@ -701,17 +703,24 @@ export class ResultsViewPageStore @computed public get hideUnprofiledSamples() { - return this.urlWrapper.query.hide_unprofiled_samples === 'true'; + const value = this.urlWrapper.query.hide_unprofiled_samples; + if (value === 'any' || value === 'totally') { + return value; + } else { + return false; + } } @action.bound - public setHideUnprofiledSamples(e: boolean) { + public setHideUnprofiledSamples( + e: IAnnotationFilterSettings['hideUnprofiledSamples'] + ) { this.urlWrapper.updateURL({ - hide_unprofiled_samples: e.toString(), + hide_unprofiled_samples: (e || false).toString(), }); } - public set hideUnprofiledSamples(include: boolean) { + public set hideUnprofiledSamples(include: 'any' | 'totally' | false) { this.setHideUnprofiledSamples(include); } @@ -3810,11 +3819,23 @@ export class ResultsViewPageStore }); readonly filteredSamples = remoteData({ - await: () => [this.samples, this.unprofiledSampleKeyToSample], + await: () => [ + this.samples, + this.unprofiledSampleKeyToSample, + this.totallyUnprofiledSamples, + ], invoke: () => { if (this.hideUnprofiledSamples) { - const unprofiledSampleKeys = this.unprofiledSampleKeyToSample - .result!; + let unprofiledSampleKeys: { [key: string]: Sample }; + if (this.hideUnprofiledSamples === 'any') { + unprofiledSampleKeys = this.unprofiledSampleKeyToSample + .result!; + } else if (this.hideUnprofiledSamples === 'totally') { + unprofiledSampleKeys = _.keyBy( + this.totallyUnprofiledSamples.result!, + s => s.uniqueSampleKey + ); + } return Promise.resolve( this.samples.result!.filter( s => !(s.uniqueSampleKey in unprofiledSampleKeys) @@ -3837,19 +3858,29 @@ export class ResultsViewPageStore // Samples that are unprofiled for at least one (gene, profile) const genes = this.genes.result!; const coverageInfo = this.coverageInformation.result!; - const queryProfileIds = this.selectedMolecularProfiles.result!.map( - p => p.molecularProfileId + const studyToSelectedMolecularProfileIds = _.mapValues( + _.groupBy( + this.selectedMolecularProfiles.result!, + p => p.studyId + ), + profiles => profiles.map(p => p.molecularProfileId) ); return Promise.resolve( this.samples.result!.filter(sample => { + // Only look at profiles for this sample's study - doesn't + // make sense to look at profiles for other studies, which + // the sample certainly is not part of. + const profileIds = + studyToSelectedMolecularProfileIds[sample.studyId]; + // Sample that is unprofiled for some gene return _.some(genes, gene => { // for some profile return !_.every( isSampleProfiledInMultiple( sample.uniqueSampleKey, - queryProfileIds, + profileIds, coverageInfo, gene.hugoGeneSymbol ) @@ -3870,19 +3901,29 @@ export class ResultsViewPageStore invoke: () => { const genes = this.genes.result!; const coverageInfo = this.coverageInformation.result!; - const queryProfileIds = this.selectedMolecularProfiles.result!.map( - p => p.molecularProfileId + const studyToSelectedMolecularProfileIds = _.mapValues( + _.groupBy( + this.selectedMolecularProfiles.result!, + p => p.studyId + ), + profiles => profiles.map(p => p.molecularProfileId) ); return Promise.resolve( this.unprofiledSamples.result!.filter(sample => { + // Only look at profiles for this sample's study - doesn't + // make sense to look at profiles for other studies, which + // the sample certainly is not part of. + const profileIds = + studyToSelectedMolecularProfileIds[sample.studyId]; + // Among unprofiled samples, pick out samples that are unprofiled for EVERY gene ...(gene x profile) return _.every(genes, gene => { // for EVERY profile return !_.some( isSampleProfiledInMultiple( sample.uniqueSampleKey, - queryProfileIds, + profileIds, coverageInfo, gene.hugoGeneSymbol ) diff --git a/src/pages/resultsView/comparison/ResultsViewComparisonUtils.ts b/src/pages/resultsView/comparison/ResultsViewComparisonUtils.ts index 5cdf6176a84..a2cfc39a267 100644 --- a/src/pages/resultsView/comparison/ResultsViewComparisonUtils.ts +++ b/src/pages/resultsView/comparison/ResultsViewComparisonUtils.ts @@ -74,7 +74,7 @@ export function getAlteredVsUnalteredGroups( unalteredAndProfiledSamples: Sample[], totallyUnprofiledSamples: Sample[], queryContainsOql: boolean, - hideUnprofiledSamples: boolean + hideUnprofiledSamples: 'any' | 'totally' | false ): SessionGroupData[] { const ret = []; if (alteredSamples.length > 0) { @@ -106,7 +106,10 @@ export function getAlteredVsUnalteredGroups( color: UNALTERED_COLOR, }); } - if (totallyUnprofiledSamples.length > 0 && !hideUnprofiledSamples) { + if ( + totallyUnprofiledSamples.length > 0 && + hideUnprofiledSamples !== 'totally' + ) { ret.push({ name: UNPROFILED_GROUP_NAME, description: `${ diff --git a/src/pages/resultsView/settings/SettingsMenu.tsx b/src/pages/resultsView/settings/SettingsMenu.tsx index 7d247435bb6..1b845f916aa 100644 --- a/src/pages/resultsView/settings/SettingsMenu.tsx +++ b/src/pages/resultsView/settings/SettingsMenu.tsx @@ -19,6 +19,8 @@ enum EVENT_KEY { hidePutativePassengers = '0', showGermlineMutations = '1', hideUnprofiledSamples = '1.1', + hideAnyUnprofiledSamples = '1.2', + hideTotallyUnprofiledSamples = '1.3', dataTypeSample = '2', dataTypePatient = '3', @@ -63,8 +65,17 @@ export default class SettingsMenu extends React.Component< .props.store.driverAnnotationSettings.includeVUS; break; case EVENT_KEY.hideUnprofiledSamples: - this.props.store.hideUnprofiledSamples = !this.props.store - .hideUnprofiledSamples; + if (!this.props.store.hideUnprofiledSamples) { + this.props.store.hideUnprofiledSamples = 'any'; + } else { + this.props.store.hideUnprofiledSamples = false; + } + break; + case EVENT_KEY.hideAnyUnprofiledSamples: + this.props.store.hideUnprofiledSamples = 'any'; + break; + case EVENT_KEY.hideTotallyUnprofiledSamples: + this.props.store.hideUnprofiledSamples = 'totally'; break; case EVENT_KEY.showGermlineMutations: this.props.store.includeGermlineMutations = !this.props.store @@ -161,20 +172,58 @@ export default class SettingsMenu extends React.Component< {this.props.resultsView && ( -
- +
+
+ +
+
+
+ + +
+
)}
diff --git a/src/shared/alterationFiltering/AnnotationFilteringSettings.ts b/src/shared/alterationFiltering/AnnotationFilteringSettings.ts index 07b16d3f4b2..6e4bc43b907 100644 --- a/src/shared/alterationFiltering/AnnotationFilteringSettings.ts +++ b/src/shared/alterationFiltering/AnnotationFilteringSettings.ts @@ -18,7 +18,7 @@ export interface IExclusionSettings { includeGermlineMutations: boolean; includeSomaticMutations: boolean; includeUnknownStatusMutations: boolean; - hideUnprofiledSamples?: boolean; + hideUnprofiledSamples?: 'any' | 'totally' | false; } export interface DriverAnnotationSettings { diff --git a/src/shared/components/driverAnnotations/SettingsMenu.tsx b/src/shared/components/driverAnnotations/SettingsMenu.tsx index 288197cc3e3..9df9887f97d 100644 --- a/src/shared/components/driverAnnotations/SettingsMenu.tsx +++ b/src/shared/components/driverAnnotations/SettingsMenu.tsx @@ -22,7 +22,6 @@ enum EVENT_KEY { showSomaticMutations = '4', showUnknownStatusMutations = '5', showUnknownTier = '6', - hideUnprofiledSamples = '7', toggleAllMutationStatus = '8', toggleAllDriverAnnotation = '9', toggleAllDriverTiers = '10', @@ -82,10 +81,6 @@ export default class SettingsMenu extends React.Component< .props.store.driverAnnotationSettings .includeUnknownOncogenicity; break; - case EVENT_KEY.hideUnprofiledSamples: - this.props.store.hideUnprofiledSamples = !this.props.store - .hideUnprofiledSamples; - break; case EVENT_KEY.showGermlineMutations: this.props.store.includeGermlineMutations = !this.props.store .includeGermlineMutations;