From 12eac09b849465a9cba58c50846a8c890ab3cef0 Mon Sep 17 00:00:00 2001 From: Chris Wakefield Date: Mon, 11 Nov 2019 11:39:20 -0600 Subject: [PATCH] Add "View NG-CHM" button on OncoPrint Heatmap tab --- src/config/IAppConfig.ts | 1 + src/config/serverConfigDefaults.ts | 1 + src/pages/resultsView/ResultsViewPageStore.ts | 40 +++++++++++++ .../tools/oncoprinter/Oncoprinter.tsx | 3 +- src/pages/studyView/StudyViewPageStore.ts | 10 ++-- .../oncoprint/ResultsViewOncoprint.tsx | 8 +++ .../oncoprint/controls/ConfirmNgchmModal.tsx | 53 ++++++++++++++++++ .../oncoprint/controls/OncoprintControls.tsx | 29 +++++++++- .../controls/mdandersonlogo260x85.png | Bin 0 -> 18991 bytes 9 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 src/shared/components/oncoprint/controls/ConfirmNgchmModal.tsx create mode 100644 src/shared/components/oncoprint/controls/mdandersonlogo260x85.png diff --git a/src/config/IAppConfig.ts b/src/config/IAppConfig.ts index 6e63daa542f..cae85e728dc 100644 --- a/src/config/IAppConfig.ts +++ b/src/config/IAppConfig.ts @@ -93,6 +93,7 @@ export interface IServerConfig { skin_title: string; skin_authorization_message: string | null; skin_patientview_filter_genes_profiled_all_samples: boolean; + show_mdacc_heatmap: boolean; quick_search_enabled: boolean; default_cross_cancer_study_list: string; // this has a default default_cross_cancer_study_list_name: string; // this has a default diff --git a/src/config/serverConfigDefaults.ts b/src/config/serverConfigDefaults.ts index 7deea8269af..f3d021bfd8f 100644 --- a/src/config/serverConfigDefaults.ts +++ b/src/config/serverConfigDefaults.ts @@ -21,6 +21,7 @@ const ServerConfigDefaults: Partial = { 'https://bioinformatics.mdanderson.org/study2url?studyid=', mdacc_heatmap_study_url: 'https:// bioinformatics.mdanderson.org/TCGA/NGCHMPortal/?', + show_mdacc_heatmap: false, mygene_info_url: 'https://mygene.info/v3/gene/<%= entrezGeneId %>?fields=uniprot', diff --git a/src/pages/resultsView/ResultsViewPageStore.ts b/src/pages/resultsView/ResultsViewPageStore.ts index 2c2d98d391d..d9932a5718f 100644 --- a/src/pages/resultsView/ResultsViewPageStore.ts +++ b/src/pages/resultsView/ResultsViewPageStore.ts @@ -1390,6 +1390,46 @@ export class ResultsViewPageStore { }, }); + + // remoteNgchmUrl queries mdanderson.org to test if there are NGCHMs for one selected + // study. The result is either the full URL to a portal page, or an empty string. + readonly remoteNgchmUrl = remoteData({ + await:()=>[this.studyIds], + invoke:async()=>{ + var result = ''; + + if (this.studyIds.result!.length === 1) { + + const queryData = { + studyid:this.studyIds.result![0], + format:"json" + }; + + var urlResponse; + + try { + urlResponse = await request.get("https://bioinformatics.mdanderson.org/study2url") + .timeout(30000) + .query(queryData) as any; + } catch (err) { + // Just eat the exception. Result will be empty string. + } + + if (urlResponse && urlResponse.body.fileContent) { + const parsedUrlResponse = JSON.parse(urlResponse.body.fileContent.trimEnd()) as any; + + if (parsedUrlResponse.length >= 1) { + // This is faked out for now. study2url needs mods to include site url + result = "https://bioinformatics.mdanderson.org/TCGA/NGCHMPortal?" + parsedUrlResponse[0]; + } + } + } + + return Promise.resolve (result); + } + }); + + readonly molecularProfilesWithData = remoteData({ await: () => [ this.molecularProfilesInStudies, diff --git a/src/pages/staticPages/tools/oncoprinter/Oncoprinter.tsx b/src/pages/staticPages/tools/oncoprinter/Oncoprinter.tsx index 55e4f88ac03..4f1600f2f9e 100644 --- a/src/pages/staticPages/tools/oncoprinter/Oncoprinter.tsx +++ b/src/pages/staticPages/tools/oncoprinter/Oncoprinter.tsx @@ -227,7 +227,8 @@ export default class Oncoprinter extends React.Component }, onClickZoomOut:()=>{ this.oncoprint.setHorzZoom(this.oncoprint.getHorzZoom()*0.7); - } + }, + onClickNGCHM:()=>{} // do nothing in oncoprinter mode }; } diff --git a/src/pages/studyView/StudyViewPageStore.ts b/src/pages/studyView/StudyViewPageStore.ts index f374058b914..4ddb309bb6f 100644 --- a/src/pages/studyView/StudyViewPageStore.ts +++ b/src/pages/studyView/StudyViewPageStore.ts @@ -2256,11 +2256,13 @@ export class StudyViewPageStore { await: () => [this.queriedPhysicalStudyIds], onError: (error => {}), invoke: async () => { - let isSinglePhysicalStudy = this.queriedPhysicalStudyIds.result.length === 1; - if (isSinglePhysicalStudy) { - return await getHeatmapMeta(getMDAndersonHeatmapStudyMetaUrl(this.queriedPhysicalStudyIds.result[0])); + if (AppConfig.serverConfig.show_mdacc_heatmap) { + let isSinglePhysicalStudy = this.queriedPhysicalStudyIds.result.length === 1; + if (isSinglePhysicalStudy) { + return await getHeatmapMeta(getMDAndersonHeatmapStudyMetaUrl(this.queriedPhysicalStudyIds.result[0])); + } } - return []; + return []; // if not enabled or conditions not met, just return default answer } }, []); diff --git a/src/shared/components/oncoprint/ResultsViewOncoprint.tsx b/src/shared/components/oncoprint/ResultsViewOncoprint.tsx index 34fe7e9de2a..4491ad906fa 100644 --- a/src/shared/components/oncoprint/ResultsViewOncoprint.tsx +++ b/src/shared/components/oncoprint/ResultsViewOncoprint.tsx @@ -389,6 +389,11 @@ export default class ResultsViewOncoprint extends React.Component{ this.addHeatmapTracks(this.selectedHeatmapProfile, treatmentIds); }, + onClickNGCHM:()=>{ + window.open(this.props.store.remoteNgchmUrl.result, '_blank'); + }, onClickDownload:(type:string)=>{ switch(type) { case "pdf": diff --git a/src/shared/components/oncoprint/controls/ConfirmNgchmModal.tsx b/src/shared/components/oncoprint/controls/ConfirmNgchmModal.tsx new file mode 100644 index 00000000000..bd03a94e366 --- /dev/null +++ b/src/shared/components/oncoprint/controls/ConfirmNgchmModal.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { Button, Modal } from 'react-bootstrap'; +import {observer} from "mobx-react"; +import {observable} from "mobx"; + +// +// NG-CHM are "Next-Generation Clustered Heat Maps", a highly interactive viewer of +// even very large heat maps, as described here: +// https://bioinformatics.mdanderson.org/public-software/ngchm/ +// Source code for the viewer and other tools can be found at +// https://github.com/MD-Anderson-Bioinformatics +// + +@observer +export default class ConfirmNgchmModal extends React.Component<{ show:boolean, onHide:()=>void, openNgchmWindow:()=>void }, {}> { + + // CSS properties in img tag below should go in .css file? + // margin-right:20px; margin-bottom:20px; + + render() { + return ( + + + Open new tab to MD Anderson NG-CHM? + + +
+

Continue will open a tab or window to a different site (not cBioPortal).

+ MD Anderson Cancer Center logo +

+ The University of Texas MD Anderson Cancer Center has created a + Next-Generation Clustered Heatmap compendium + based on the same dataset or a very similar dataset containing many of the + same TCGA samples. Continue will display a page of NG-CHM based on + this dataset. +

+
+ Read details on the data processing used for this TCGA compendium + here + (opens in a new tab). +
+ + + + + + + ); + } +} diff --git a/src/shared/components/oncoprint/controls/OncoprintControls.tsx b/src/shared/components/oncoprint/controls/OncoprintControls.tsx index 68e1114b74a..9a6db0c9917 100644 --- a/src/shared/components/oncoprint/controls/OncoprintControls.tsx +++ b/src/shared/components/oncoprint/controls/OncoprintControls.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import {observer, Observer} from 'mobx-react'; -import {Button, ButtonGroup} from 'react-bootstrap'; +import {Button, ButtonGroup, Modal} from 'react-bootstrap'; import CustomDropdown from './CustomDropdown'; +import ConfirmNgchmModal from './ConfirmNgchmModal'; import ReactSelect from 'react-select1'; import {MobxPromise} from 'mobxpromise'; import {action, computed, IObservableObject, observable, ObservableMap, reaction, toJS,} from 'mobx'; @@ -65,13 +66,12 @@ export interface IOncoprintControlsHandlers { onChangeSelectedClinicalTracks?: ( attributeIds: (string | SpecialAttribute)[] ) => void; - onClickAddGenesToHeatmap?: () => void; onClickAddTreatmentsToHeatmap?: (treatments: string[]) => void; onSelectHeatmapProfile?: (molecularProfileId: string) => void; onChangeHeatmapGeneInputValue?: (value: string) => void; onChangeHeatmapTreatmentInputValue?: (value: string) => void; - + onClickNGCHM: () => void; onSetHorzZoom: (z: number) => void; onClickZoomIn: () => void; onClickZoomOut: () => void; @@ -116,6 +116,7 @@ export interface IOncoprintControlsState { heatmapGeneInputValue?: string; heatmapTreatmentInputValue?: string; hideHeatmapMenu?: boolean; + ngchmButtonActive?: boolean; customDriverAnnotationBinaryMenuLabel?: string; customDriverAnnotationTiersMenuLabel?: string; @@ -176,6 +177,7 @@ const EVENT_KEY = { downloadOrder: '28', downloadTabular: '29', horzZoomSlider: '30', + viewNGCHM: '31', addTreatmentsToHeatmap: '32', }; @@ -189,6 +191,7 @@ export default class OncoprintControls extends React.Component< @observable private _selectedTreatmentIds: string[] = []; private textareaTreatmentText = ''; @observable treatmentFilter = ''; + @observable showConfirmNgchmModal:boolean = false; constructor(props: IOncoprintControlsProps) { super(props); @@ -444,6 +447,11 @@ export default class OncoprintControls extends React.Component< this.props.handlers.onClickDownload && this.props.handlers.onClickDownload('tabular'); break; + case EVENT_KEY.viewNGCHM: + if (this.props.state.ngchmButtonActive && this.props.handlers.onClickNGCHM) { + this.showConfirmNgchmModal = true; + } + break; } } @@ -695,6 +703,16 @@ export default class OncoprintControls extends React.Component< Add Treatment Response to Heatmap , ]} + + {this.props.state.ngchmButtonActive && + (Open a new tab for NG-CHM with this study from MD Anderson Cancer Center compendium.}> + + ) + }
); } @@ -1309,6 +1327,11 @@ export default class OncoprintControls extends React.Component< {this.getDownloadMenu} {this.getHorzZoomControls} {this.minimapButton} + this.showConfirmNgchmModal=false} + openNgchmWindow={this.props.handlers.onClickNGCHM} + /> ); diff --git a/src/shared/components/oncoprint/controls/mdandersonlogo260x85.png b/src/shared/components/oncoprint/controls/mdandersonlogo260x85.png new file mode 100644 index 0000000000000000000000000000000000000000..1abca50132a2601270bf32f6edaababe251849e6 GIT binary patch literal 18991 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 Q@Bjb+07*qoM6N<$f{I8wH2?qr literal 0 HcmV?d00001