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 32c95bbf881..b537972e531 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 00000000000..1abca50132a Binary files /dev/null and b/src/shared/components/oncoprint/controls/mdandersonlogo260x85.png differ