From 0c08047f7609131e74b2912cb68a3f30805defc7 Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Mon, 15 Sep 2025 16:04:07 -0400 Subject: [PATCH 1/9] Expression caching fixed to return only the gene(s) requested --- main/js/smartCache/main.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/main/js/smartCache/main.js b/main/js/smartCache/main.js index 6016fd6a..294a9276 100644 --- a/main/js/smartCache/main.js +++ b/main/js/smartCache/main.js @@ -440,19 +440,18 @@ function CacheInterface(nameOfDb) { for (geneObj of interfaceData) { //First element in geneObj is the gene name gene = geneObj[0] - //Second element in geneObj is map TCGA barcodes to TCGA expression records - expressionMap = geneObj[1] - for(expressionObj of expressionMap) { - //First element in expressionObj is patient's TCGA barcode - let barcode = expressionObj[0]; - //If there is a subset of barcodes being requested, apply that filter - if(listOfBarcodes && listOfBarcodes.includes(barcode)) { - //Second element in expressionObj is the mRNA_Seq information for current patient - tmp.push(expressionObj[1]); - } - //If no subset of barcodes is being requested, then do not apply a filter - else if(!listOfBarcodes) { - tmp.push(expressionObj[1]) + //Only append expression data if a certain gene is being requested + if(listOfGenes.includes(gene)) { + //Second element in geneObj is map TCGA barcodes to TCGA expression records + expressionMap = geneObj[1] + for(expressionObj of expressionMap) { + //First element in expressionObj is patient's TCGA barcode + let barcode = expressionObj[0]; + //Append patient's expression data if barcode is in the requested subset or if no barcode filter is applied + if((listOfBarcodes && listOfBarcodes.includes(barcode)) || (!listOfBarcodes)) { + //Second element in expressionObj is the mRNA_Seq information for current patient + tmp.push(expressionObj[1]); + } } } } From 8ed55984719a335136a0ca179923f658e2034ecf Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Tue, 7 Apr 2026 20:04:49 -0400 Subject: [PATCH 2/9] Added error handling for data Firebrowse could not fetch --- main/js/dataAcquisition/fetch.js | 21 ++++- main/js/smartCache/main.js | 147 ++++++++++++++++++------------- 2 files changed, 105 insertions(+), 63 deletions(-) diff --git a/main/js/dataAcquisition/fetch.js b/main/js/dataAcquisition/fetch.js index 0014f937..061c6991 100644 --- a/main/js/dataAcquisition/fetch.js +++ b/main/js/dataAcquisition/fetch.js @@ -163,7 +163,11 @@ firebrowse.fetch = async function(endpoint, params, groupBy) { ProgressBar.setPercentage(i/paramsMatrix.length*100, "Fetching " + expectedKey); // Run a fetch and then collect the data into one common object. await _fetchFromFireBrowse(endpoint, paramsForThisCall, expectedKey) - .then(x => {results[expectedKey].push(...x[expectedKey])}); + .then(x => { + // Verify retrieved data has expectedKey as a field + if(expectedKey in x) + results[expectedKey].push(...x[expectedKey]) + }); } ProgressBar.cleanUp(); return results; @@ -205,7 +209,12 @@ firebrowse.fetchClinicalFH = async function({cohorts, genes, barcodes, pageNum}) groupBy.push({key: "tcga_participant_barcode", length: 50}); } const data = await firebrowse.fetch("/Samples/Clinical_FH", params, groupBy); - return data.Clinical_FH; + // Check that firebrowse.fetch() returned properly formatted data + let data_field = "Clinical_FH" + if(data_field in data_field) + return data[data_field]; + else + throw new Error("Clinical data could not be fetched from Firebrowse"); }; @@ -242,8 +251,12 @@ firebrowse.fetchCounts = async function(cohorts) { data_type: "mrnaseq", totals: "true", }; - const fetchResponse = await firebrowse.fetch("/Metadata/Counts", params); - return fetchResponse.Counts; + const data = await firebrowse.fetch("/Metadata/Counts", params); + let data_field = "Counts" + if(data_field in data_field) + return data[data_field]; + else + window.alert("Could not fetch cohort counts data"); }; firebrowse.fetchMutationMAF = async function ({cohorts, genes}) { diff --git a/main/js/smartCache/main.js b/main/js/smartCache/main.js index 294a9276..5658d770 100644 --- a/main/js/smartCache/main.js +++ b/main/js/smartCache/main.js @@ -381,19 +381,28 @@ function CacheInterface(nameOfDb) { //Iterate over each cohort, gene combination for (let cohort in interface) { for (let gene in interface[cohort]) { - //Fetch expression data for requested cohort, gene, and barcodes with Firebrowse fetch call - let expressionData = await firebrowse.fetchmRNASeq({cohorts: [cohort], genes: [gene]}) - for(let index = 0; index < expressionData.length; index++) { - try { - let obj = expressionData[index]; - //Call add() and saveToDB() to cache the fetched data - await cacheGE.add(obj.cohort, obj.tcga_participant_barcode, gene, obj); - await cacheGE.saveToDB(obj.cohort, obj.tcga_participant_barcode, gene, obj); - } catch(err) { - //If an error occurred, print an error message and end execution - console.error('Failed, skipping for cohort.', err); - return undefined - } + try { + //Fetch expression data for requested cohort, gene, and barcodes with Firebrowse fetch call + await firebrowse.fetchmRNASeq({ + cohorts: [cohort], + genes: [gene]} + ).then((expressionData) => { + for(let index = 0; index < expressionData.length; index++) { + try { + let obj = expressionData[index]; + //Call add() and saveToDB() to cache the fetched data + cacheGE.add(obj.cohort, obj.tcga_participant_barcode, gene, obj); + cacheGE.saveToDB(obj.cohort, obj.tcga_participant_barcode, gene, obj); + } catch(err) { + //If an error occurred, print an error message and end execution + console.error('Failed, skipping for cohort.', err); + return undefined + } + } + }) + } catch(err) { + error_message = `${err.message} for ${cohort} cohort and ${gene} gene` + handleDataFetchError(error_message); } } } @@ -581,19 +590,28 @@ function CacheInterface(nameOfDb) { //Iterate over each cohort, gene combination for (let cohort in interface) { for (let gene of interface[cohort]) { - //Fetch expression data for requested cohort, gene, and barcodes with Firebrowse fetch call - let rawMutationData = await firebrowse.fetchMutationMAF({cohorts: [cohort], genes: [gene]}); - let mutationData = await formatMutationData(cohort, gene, rawMutationData); - for(let index = 0; index < mutationData.length; index++) { - try { - let obj = mutationData[index]; - await cacheMU.add(obj.cohort, obj.tcga_participant_barcode, gene, obj); // Add patient to mutation data caching interface - await cacheMU.saveToDB(obj.cohort, obj.tcga_participant_barcode, gene, obj); // Save patient to caching interface database - } catch(err) { - //If an error occurred, print an error message and end execution - console.error('Failed, skipping for cohort.', err); - return undefined - } + try { + //Fetch expression data for requested cohort, gene, and barcodes with Firebrowse fetch call + firebrowse.fetchMutationMAF({ + cohorts: [cohort], + genes: [gene]} + ).then((raw_mutation_data) => { + let mutationData = formatMutationData(cohort, gene, raw_mutation_data); + for(let index = 0; index < mutationData.length; index++) { + try { + let obj = mutationData[index]; + cacheMU.add(obj.cohort, obj.tcga_participant_barcode, gene, obj); // Add patient to mutation data caching interface + cacheMU.saveToDB(obj.cohort, obj.tcga_participant_barcode, gene, obj); // Save patient to caching interface database + } catch(err) { + //If an error occurred, print an error message and end execution + console.error('Failed, skipping for cohort.', err); + return undefined; + } + } + }); + } catch(err) { + error_message = `${err.message} for ${cohort} cohort and ${gene} gene` + handleDataFetchError(error_message); } } } @@ -680,25 +698,31 @@ function CacheInterface(nameOfDb) { let expr = "TP53" //For each cohort, query the mRNASeq data for that cohort for (let cohort in interface) { - //Get mRNASeq data with hardcoded gene - /*Currently uses low-level firebrowse function to fetch mRNASeq data; - replace with appropriate gene expression caching method!*/ - let expressionData = await firebrowse.fetchmRNASeq({ - cohorts: [cohort], - genes: [expr], - }) - //Iterate over each JSON object in fetched expression data and save to cache - for(let index = 0; index < expressionData.length; index++) { - let obj = expressionData[index]; - try { - //Call add() and saveToDB() to cache the fetched data - await cacheBAR.add(obj.cohort, obj.tcga_participant_barcode); - await cacheBAR.saveToDB(obj.cohort, obj.tcga_participant_barcode, obj); - } catch(err) { - //If an error occurred, print an error message and end execution - console.error('Failed, skipping for cohort.', err); - return undefined - } + try { + //Get mRNASeq data with hardcoded gene + /*Currently uses low-level firebrowse function to fetch mRNASeq data; + replace with appropriate gene expression caching method!*/ + firebrowse.fetchmRNASeq({ + cohorts: [cohort], + genes: [expr] + }).then((expression_data) => { + //Iterate over each JSON object in fetched expression data and save to cache + for(let index = 0; index < expression_data.length; index++) { + let obj = expression_data[index]; + try { + //Call add() and saveToDB() to cache the fetched data + cacheBAR.add(obj.cohort, obj.tcga_participant_barcode); + cacheBAR.saveToDB(obj.cohort, obj.tcga_participant_barcode, obj); + } catch(err) { + //If an error occurred, print an error message and end execution + console.error('Failed, skipping for cohort.', err); + return undefined + } + } + }) + } catch(err) { + error_message = `${err.message} for ${cohort} cohort` + handleDataFetchError(error_message) } } } @@ -758,20 +782,25 @@ function CacheInterface(nameOfDb) { .trigger('change.select2'); for (let cohort in interface) { let getBarcodesInACohort = barcodesByCohort.filter(cohortEle => (cohortEle.cohort == cohort))[0].barcodes - let clinicalData = await firebrowse.fetchClinicalFH({ - cohorts: [cohort], - barcodes: getBarcodesInACohort, - }).then((clinicalData) => { - // Iterate over each patient's clinical data - for(let index = 0; index < clinicalData.length; index++) { - let obj = clinicalData[index]; - cacheCLIN.add(cohort=obj.cohort, barcode=obj); // Add clinical data to interface map by mimicking parameters for barcode caching - cacheCLIN.saveToDB(obj.cohort, obj.tcga_participant_barcode, obj); // Save clinical data to interface - } - }).catch(error => { - console.error('Failed, skipping for cohort.', error); - return undefined - }); + try { + await firebrowse.fetchClinicalFH({ + cohorts: [cohort], + barcodes: getBarcodesInACohort + }).then((clinicalData) => { + // Iterate over each patient's clinical data + for(let index = 0; index < clinicalData.length; index++) { + let obj = clinicalData[index]; + cacheCLIN.add(cohort=obj.cohort, barcode=obj); // Add clinical data to interface map by mimicking parameters for barcode caching + cacheCLIN.saveToDB(obj.cohort, obj.tcga_participant_barcode, obj); // Save clinical data to interface + } + }).catch(error => { + console.error('Failed, skipping for cohort.', error); + return undefined + }); + } catch(err) { + error_message = `${err.message} for ${cohort} cohort` + handleDataFetchError(error_message) + } } $( '.clinicalMultipleSelection, .pathwayMultipleSelection' From a45af95a05ee0c1911f6de3a25181c224bd531eb Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Tue, 7 Apr 2026 20:06:13 -0400 Subject: [PATCH 3/9] Generalized handler for empty cohort to extend to error messages --- main/js/afterSubmit.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main/js/afterSubmit.js b/main/js/afterSubmit.js index f32f5ed3..1ce2d3e2 100644 --- a/main/js/afterSubmit.js +++ b/main/js/afterSubmit.js @@ -86,7 +86,8 @@ const buildPlots = async function() { } else { //Set screen text to indicate that no plots were generated due to lack of patient barcodes - handleEmptyCohort(); + message = "The gene mutation and/or metadata filter(s) produced a cohort with no patients. To see figures, please change the gene mutation and/or metadata filter(s)."; + handleDataFetchError(message); return null; } expressionData = (expressionData || []).filter( @@ -533,14 +534,13 @@ let downloadClinicalData = function(cohortID, clinicalData, barcodes_clin) { } } -let handleEmptyCohort = function () { - let empty_cohort_message = "The gene mutation and/or metadata filter(s) produced a cohort with no patients. To see figures, please change the gene mutation and/or metadata filter(s)." +let handleDataFetchError = function (message) { // Remove the loaders from heatmap, violin, and survival tabs document.getElementById("heatmapLoaderDiv").classList.remove("loader"); document.getElementById("violinLoaderDiv").classList.remove("loader"); document.getElementById("survivalLoaderDiv").classList.remove("loader"); // Set text of each plot tab - d3.select("#heatmapLoaderDiv").html(empty_cohort_message); - d3.select("#violinLoaderDiv").html(empty_cohort_message); - d3.select("#survivalLoaderDiv").html(empty_cohort_message); + d3.select("#heatmapLoaderDiv").html(message); + d3.select("#violinLoaderDiv").html(message); + d3.select("#survivalLoaderDiv").html(message); }; \ No newline at end of file From 3b5db9d930d301a527e4f958b1dfcaabf2297d14 Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Mon, 13 Apr 2026 11:31:06 -0400 Subject: [PATCH 4/9] Added graceful failure to Firebrowse data request errors --- main/js/dataAcquisition/fetch.js | 22 ++-- main/js/smartCache/main.js | 170 +++++++++++++++---------------- 2 files changed, 97 insertions(+), 95 deletions(-) diff --git a/main/js/dataAcquisition/fetch.js b/main/js/dataAcquisition/fetch.js index 919a9ace..54346e53 100644 --- a/main/js/dataAcquisition/fetch.js +++ b/main/js/dataAcquisition/fetch.js @@ -28,7 +28,7 @@ const _fetchFromFireBrowse = async function(endpoint, params, expectedKey) { const json = await response.json(); return json; } catch(error) { - console.log(`${expectedKey} is empty, returning an object with empty ${expectedKey} `); + console.warn(`${expectedKey} is empty, returning an object with empty ${expectedKey} `); return minimalJson; } }; @@ -211,7 +211,7 @@ firebrowse.fetchClinicalFH = async function({cohorts, genes, barcodes, pageNum}) const data = await firebrowse.fetch("/Samples/Clinical_FH", params, groupBy); // Check that firebrowse.fetch() returned properly formatted data let data_field = "Clinical_FH" - if(data_field in data_field) + if(data_field in data) return data[data_field]; else throw new Error("Clinical data could not be fetched from Firebrowse"); @@ -253,10 +253,10 @@ firebrowse.fetchCounts = async function(cohorts) { }; const data = await firebrowse.fetch("/Metadata/Counts", params); let data_field = "Counts" - if(data_field in data_field) + if(data_field in data) return data[data_field]; else - window.alert("Could not fetch cohort counts data"); + throw new Error("Cohort counts could not be fetched from Firebrowse"); }; firebrowse.fetchMutationMAF = async function ({cohorts, genes}) { @@ -288,7 +288,11 @@ firebrowse.fetchMutationMAF = async function ({cohorts, genes}) { else { data = await firebrowse.fetch("/Analyses/Mutation/MAF", params, groupBy); } - return data.MAF; + let data_field = "MAF" + if(data_field in data) + return data[data_field]; + else + throw new Error("Could not fetch MAF mutation data from Firehose"); }; @@ -337,7 +341,13 @@ firebrowse.fetchmRNASeq = async function({cohorts, genes, barcodes}) { groupBy.push({key: "tcga_participant_barcode", length: 400}); } const data = await firebrowse.fetch("/Samples/mRNASeq", params, groupBy); - return data.mRNASeq; + //let data_field = "mRNASeq" + let data_field = "test" + if(data_field in data) + return data[data_field]; + else + throw new Error("Could not fetch mRNASeq expression data from Firehose"); + }; // Prevent any changes to this object. diff --git a/main/js/smartCache/main.js b/main/js/smartCache/main.js index 796dfac1..1650282b 100644 --- a/main/js/smartCache/main.js +++ b/main/js/smartCache/main.js @@ -383,26 +383,22 @@ function CacheInterface(nameOfDb) { for (let gene in interface[cohort]) { try { //Fetch expression data for requested cohort, gene, and barcodes with Firebrowse fetch call - await firebrowse.fetchmRNASeq({ - cohorts: [cohort], - genes: [gene]} - ).then((expressionData) => { - for(let index = 0; index < expressionData.length; index++) { - try { - let obj = expressionData[index]; - //Call add() and saveToDB() to cache the fetched data - cacheGE.add(obj.cohort, obj.tcga_participant_barcode, gene, obj); - cacheGE.saveToDB(obj.cohort, obj.tcga_participant_barcode, gene, obj); - } catch(err) { - //If an error occurred, print an error message and end execution - console.error('Failed, skipping for cohort.', err); - return undefined - } + let expressionData = await firebrowse.fetchmRNASeq({cohorts: [cohort], genes: [gene]}) + for(let index = 0; index < expressionData.length; index++) { + try { + let obj = expressionData[index]; + //Call add() and saveToDB() to cache the fetched data + await cacheGE.add(obj.cohort, obj.tcga_participant_barcode, gene, obj); + await cacheGE.saveToDB(obj.cohort, obj.tcga_participant_barcode, gene, obj); + } catch(err) { + //If an error occurred, print an error message and end execution + console.error('Failed, skipping for cohort.', err); + return undefined } - }) + } } catch(err) { - error_message = `${err.message} for ${cohort} cohort and ${gene} gene` - handleDataFetchError(error_message); + message = `Could not fetch/store genetic data from Firebrowse for cohort ${cohort} and gene ${gene}`; + handleDataFetchError(message); } } } @@ -449,18 +445,19 @@ function CacheInterface(nameOfDb) { for (geneObj of interfaceData) { //First element in geneObj is the gene name gene = geneObj[0] - //Only append expression data if a certain gene is being requested - if(listOfGenes.includes(gene)) { - //Second element in geneObj is map TCGA barcodes to TCGA expression records - expressionMap = geneObj[1] - for(expressionObj of expressionMap) { - //First element in expressionObj is patient's TCGA barcode - let barcode = expressionObj[0]; - //Append patient's expression data if barcode is in the requested subset or if no barcode filter is applied - if((listOfBarcodes && listOfBarcodes.includes(barcode)) || (!listOfBarcodes)) { - //Second element in expressionObj is the mRNA_Seq information for current patient - tmp.push(expressionObj[1]); - } + //Second element in geneObj is map TCGA barcodes to TCGA expression records + expressionMap = geneObj[1] + for(expressionObj of expressionMap) { + //First element in expressionObj is patient's TCGA barcode + let barcode = expressionObj[0]; + //If there is a subset of barcodes being requested, apply that filter + if(listOfBarcodes && listOfBarcodes.includes(barcode)) { + //Second element in expressionObj is the mRNA_Seq information for current patient + tmp.push(expressionObj[1]); + } + //If no subset of barcodes is being requested, then do not apply a filter + else if(!listOfBarcodes) { + tmp.push(expressionObj[1]) } } } @@ -592,26 +589,22 @@ function CacheInterface(nameOfDb) { for (let gene of interface[cohort]) { try { //Fetch expression data for requested cohort, gene, and barcodes with Firebrowse fetch call - firebrowse.fetchMutationMAF({ - cohorts: [cohort], - genes: [gene]} - ).then((raw_mutation_data) => { - let mutationData = formatMutationData(cohort, gene, raw_mutation_data); - for(let index = 0; index < mutationData.length; index++) { - try { - let obj = mutationData[index]; - cacheMU.add(obj.cohort, obj.tcga_participant_barcode, gene, obj); // Add patient to mutation data caching interface - cacheMU.saveToDB(obj.cohort, obj.tcga_participant_barcode, gene, obj); // Save patient to caching interface database - } catch(err) { - //If an error occurred, print an error message and end execution - console.error('Failed, skipping for cohort.', err); - return undefined; - } + let rawMutationData = await firebrowse.fetchMutationMAF({cohorts: [cohort], genes: [gene]}); + let mutationData = await formatMutationData(cohort, gene, rawMutationData); + for(let index = 0; index < mutationData.length; index++) { + try { + let obj = mutationData[index]; + await cacheMU.add(obj.cohort, obj.tcga_participant_barcode, gene, obj); // Add patient to mutation data caching interface + await cacheMU.saveToDB(obj.cohort, obj.tcga_participant_barcode, gene, obj); // Save patient to caching interface database + } catch(err) { + //If an error occurred, print an error message and end execution + console.error('Failed, skipping for cohort.', err); + return undefined } - }); + } } catch(err) { - error_message = `${err.message} for ${cohort} cohort and ${gene} gene` - handleDataFetchError(error_message); + message = `Could not fetch/store mutation data from Firebrowse for cohort ${cohort} and gene ${gene}`; + handleDataFetchError(message) } } } @@ -696,35 +689,33 @@ function CacheInterface(nameOfDb) { async function executeQueriesBAR(interface) { //Harcode gene to query mRNASeq data for let expr = "TP53" - //For each cohort, query the mRNASeq data for that cohort - for (let cohort in interface) { - try { + //For each cohort, query the mRNASeq data for that cohort + for (let cohort in interface) { + try { //Get mRNASeq data with hardcoded gene /*Currently uses low-level firebrowse function to fetch mRNASeq data; replace with appropriate gene expression caching method!*/ - firebrowse.fetchmRNASeq({ + let expressionData = await firebrowse.fetchmRNASeq({ cohorts: [cohort], - genes: [expr] - }).then((expression_data) => { - //Iterate over each JSON object in fetched expression data and save to cache - for(let index = 0; index < expression_data.length; index++) { - let obj = expression_data[index]; - try { - //Call add() and saveToDB() to cache the fetched data - cacheBAR.add(obj.cohort, obj.tcga_participant_barcode); - cacheBAR.saveToDB(obj.cohort, obj.tcga_participant_barcode, obj); - } catch(err) { - //If an error occurred, print an error message and end execution - console.error('Failed, skipping for cohort.', err); - return undefined - } - } + genes: [expr], }) - } catch(err) { - error_message = `${err.message} for ${cohort} cohort` - handleDataFetchError(error_message) + //Iterate over each JSON object in fetched expression data and save to cache + for(let index = 0; index < expressionData.length; index++) { + let obj = expressionData[index]; + try { + //Call add() and saveToDB() to cache the fetched data + await cacheBAR.add(obj.cohort, obj.tcga_participant_barcode); + await cacheBAR.saveToDB(obj.cohort, obj.tcga_participant_barcode, obj); + } catch(err) { + //If an error occurred, print an error message and end execution + console.error('Failed, skipping for cohort.', err); + return undefined + } + }} catch(err) { + message = `Could not fetch/store barcode data from Firebrowse for cohort ${cohort}`; + handleDataFetchError(message); + } } - } } //missingInterface is an array of cohorts whose barcodes have not yet been cached @@ -789,26 +780,27 @@ function CacheInterface(nameOfDb) { ) .prop('disabled', true) .trigger('change.select2'); - for (let cohort in interface) { - let getBarcodesInACohort = barcodesByCohort.filter(cohortEle => (cohortEle.cohort == cohort))[0].barcodes - try { - await firebrowse.fetchClinicalFH({ + + for (let cohort in interface) { + try { + let getBarcodesInACohort = barcodesByCohort.filter(cohortEle => (cohortEle.cohort == cohort))[0].barcodes + let clinicalData = await firebrowse.fetchClinicalFH({ cohorts: [cohort], - barcodes: getBarcodesInACohort - }).then((clinicalData) => { - // Iterate over each patient's clinical data - for(let index = 0; index < clinicalData.length; index++) { - let obj = clinicalData[index]; - cacheCLIN.add(cohort=obj.cohort, barcode=obj); // Add clinical data to interface map by mimicking parameters for barcode caching - cacheCLIN.saveToDB(obj.cohort, obj.tcga_participant_barcode, obj); // Save clinical data to interface - } - }).catch(error => { + barcodes: getBarcodesInACohort, + }) + // Iterate over each patient's clinical data + for(let index = 0; index < clinicalData.length; index++) { + try { + let obj = clinicalData[index]; + cacheCLIN.add(cohort = obj.cohort, barcode = obj); // Add clinical data to interface map by mimicking parameters for barcode caching + cacheCLIN.saveToDB(obj.cohort, obj.tcga_participant_barcode, obj); // Save clinical data to interface + } catch(err) { console.error('Failed, skipping for cohort.', error); return undefined - }); - } catch(err) { - error_message = `${err.message} for ${cohort} cohort` - handleDataFetchError(error_message) + }; + }} catch(err) { + message = `Could not fetch/store clinical data from Firebrowse for cohort ${cohort}`; + handleDataFetchError(message); } } $( @@ -816,7 +808,7 @@ function CacheInterface(nameOfDb) { ) .prop('disabled', false) .trigger('change.select2'); - } + } let [missingInterface, hasInterface] = constructQueriesCLIN(listOfCohorts, this.interface) From 5edc9ff91ea215574c5270e6825c24364d82db2e Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Mon, 20 Apr 2026 11:49:54 -0400 Subject: [PATCH 5/9] Caching creating old error message --- main/js/afterSubmit.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main/js/afterSubmit.js b/main/js/afterSubmit.js index 1ce2d3e2..356d29dc 100644 --- a/main/js/afterSubmit.js +++ b/main/js/afterSubmit.js @@ -37,7 +37,9 @@ const buildPlots = async function() { if (isEmpty(selectedTumorTypes) || isEmpty(allSelectedGenes) ) { console.log("user did not provide enough information for query"); - window.alert("Please select at least one tumor type and gene."); + // window.alert("Please select at least one tumor type and gene."); + let message = "Please select at least one tumor type and gene to generate plots."; + handleDataFetchError(message); return null; } From 7171aaa9b7c8066231759463996dbda3bbf6e9ee Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Tue, 21 Apr 2026 12:12:36 -0400 Subject: [PATCH 6/9] Resolved data fetch error --- main/js/dataAcquisition/fetch.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main/js/dataAcquisition/fetch.js b/main/js/dataAcquisition/fetch.js index 54346e53..ccc224d3 100644 --- a/main/js/dataAcquisition/fetch.js +++ b/main/js/dataAcquisition/fetch.js @@ -341,8 +341,7 @@ firebrowse.fetchmRNASeq = async function({cohorts, genes, barcodes}) { groupBy.push({key: "tcga_participant_barcode", length: 400}); } const data = await firebrowse.fetch("/Samples/mRNASeq", params, groupBy); - //let data_field = "mRNASeq" - let data_field = "test" + let data_field = "mRNASeq" if(data_field in data) return data[data_field]; else From 8882c4f775eba0433e3556d99813abf0db87df5e Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Tue, 21 Apr 2026 12:23:45 -0400 Subject: [PATCH 7/9] Changed Firebrowse error message formatting to red text --- main/js/afterSubmit.js | 7 +++---- main/js/dataAcquisition/fetch.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/main/js/afterSubmit.js b/main/js/afterSubmit.js index 356d29dc..75f7e625 100644 --- a/main/js/afterSubmit.js +++ b/main/js/afterSubmit.js @@ -37,7 +37,6 @@ const buildPlots = async function() { if (isEmpty(selectedTumorTypes) || isEmpty(allSelectedGenes) ) { console.log("user did not provide enough information for query"); - // window.alert("Please select at least one tumor type and gene."); let message = "Please select at least one tumor type and gene to generate plots."; handleDataFetchError(message); return null; @@ -542,7 +541,7 @@ let handleDataFetchError = function (message) { document.getElementById("violinLoaderDiv").classList.remove("loader"); document.getElementById("survivalLoaderDiv").classList.remove("loader"); // Set text of each plot tab - d3.select("#heatmapLoaderDiv").html(message); - d3.select("#violinLoaderDiv").html(message); - d3.select("#survivalLoaderDiv").html(message); + d3.select("#heatmapLoaderDiv").html(message).style("color", "#8B0000").style("font-size", "16px"); + d3.select("#violinLoaderDiv").html(message).style("color", "#8B0000").style("font-size", "16px"); + d3.select("#survivalLoaderDiv").html(message).style("color", "#8B0000").style("font-size", "16px"); }; \ No newline at end of file diff --git a/main/js/dataAcquisition/fetch.js b/main/js/dataAcquisition/fetch.js index ccc224d3..75c7c272 100644 --- a/main/js/dataAcquisition/fetch.js +++ b/main/js/dataAcquisition/fetch.js @@ -210,7 +210,7 @@ firebrowse.fetchClinicalFH = async function({cohorts, genes, barcodes, pageNum}) } const data = await firebrowse.fetch("/Samples/Clinical_FH", params, groupBy); // Check that firebrowse.fetch() returned properly formatted data - let data_field = "Clinical_FH" + let data_field = "Clinical_FH"; if(data_field in data) return data[data_field]; else From 69bb5aebdc910f1d600e8d2205b3e2a245a11ac4 Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Tue, 21 Apr 2026 12:32:05 -0400 Subject: [PATCH 8/9] Restructured handleDataFetchError --- main/js/afterSubmit.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/main/js/afterSubmit.js b/main/js/afterSubmit.js index 75f7e625..ad2ae4aa 100644 --- a/main/js/afterSubmit.js +++ b/main/js/afterSubmit.js @@ -541,7 +541,9 @@ let handleDataFetchError = function (message) { document.getElementById("violinLoaderDiv").classList.remove("loader"); document.getElementById("survivalLoaderDiv").classList.remove("loader"); // Set text of each plot tab - d3.select("#heatmapLoaderDiv").html(message).style("color", "#8B0000").style("font-size", "16px"); - d3.select("#violinLoaderDiv").html(message).style("color", "#8B0000").style("font-size", "16px"); - d3.select("#survivalLoaderDiv").html(message).style("color", "#8B0000").style("font-size", "16px"); + error_message_font_size = "16px"; + error_message_font_color = "#8B0000"; + d3.select("#heatmapLoaderDiv").html(message).style("color", error_message_font_color).style("font-size", error_message_font_size); + d3.select("#violinLoaderDiv").html(message).style("color", error_message_font_color).style("font-size", error_message_font_size); + d3.select("#survivalLoaderDiv").html(message).style("color", error_message_font_color).style("font-size", error_message_font_size); }; \ No newline at end of file From 617d9b8fef10fd6dc23bbf8a9a0e0bd545dd5b29 Mon Sep 17 00:00:00 2001 From: Adit Anand Date: Tue, 21 Apr 2026 13:26:19 -0400 Subject: [PATCH 9/9] Added data fetch graceful failure for cohort names and counts requests --- main/js/dataAcquisition/fetch.js | 7 +- main/js/fillSelectBoxes.js | 145 ++++++++++++++++--------------- 2 files changed, 83 insertions(+), 69 deletions(-) diff --git a/main/js/dataAcquisition/fetch.js b/main/js/dataAcquisition/fetch.js index 75c7c272..17e2f864 100644 --- a/main/js/dataAcquisition/fetch.js +++ b/main/js/dataAcquisition/fetch.js @@ -228,8 +228,13 @@ firebrowse.fetchClinicalFH = async function({cohorts, genes, barcodes, pageNum}) */ firebrowse.fetchCohorts = async function() { const params = { format: "json" }; + //Check that Firebrowse fetch returned properly formatted data const fetchResponse = await firebrowse.fetch("/Metadata/Cohorts", params); - return fetchResponse.Cohorts; + let data_field = "Cohorts"; + if(data_field in fetchResponse) + return fetchResponse[data_field]; + else + throw new Error("Cohort names could not be fetched from Firebrowse"); }; /** Get the number of mRNASeq samples per cohort. diff --git a/main/js/fillSelectBoxes.js b/main/js/fillSelectBoxes.js index 68f14be2..cf682022 100644 --- a/main/js/fillSelectBoxes.js +++ b/main/js/fillSelectBoxes.js @@ -12,31 +12,35 @@ * @returns {undefined} */ const fillCancerTypeSelectBox = async function () { - const cancerTypesQuery = await firebrowse.fetchCohorts(); - const cancerTypesQueryNoFPPP = cancerTypesQuery.reduce((acc, item) => item.cohort !== 'FPPP' ? [...acc, item] : acc, []); - cancerTypesQueryNoFPPP.sort(); - let selectBox = document.getElementById("cancerTypeMultipleSelection"); - for (let i = 0; i < cancerTypesQueryNoFPPP.length; i++) { - let currentOption = document.createElement("option"); - currentOption.value = cancerTypesQueryNoFPPP[i]["cohort"]; - currentOption.text = - "(" + - cancerTypesQueryNoFPPP[i]["cohort"] + - ") " + - cancerTypesQueryNoFPPP[i]["description"]; - currentOption.id = cancerTypesQueryNoFPPP[i]["cohort"]; - selectBox.appendChild(currentOption); - } - let cancerTypeSelectedOptions = localStorage - .getItem("cancerTypeSelectedOptions") || null - if (cancerTypeSelectedOptions) { - cancerTypeSelectedOptions = cancerTypeSelectedOptions.split(","); - $(".cancerTypeMultipleSelection").val(cancerTypeSelectedOptions); - $(".cancerTypeMultipleSelection").trigger('change'); - } - - return true; + try { + const cancerTypesQuery = await firebrowse.fetchCohorts(); + const cancerTypesQueryNoFPPP = cancerTypesQuery.reduce((acc, item) => item.cohort !== 'FPPP' ? [...acc, item] : acc, []); + cancerTypesQueryNoFPPP.sort(); + let selectBox = document.getElementById("cancerTypeMultipleSelection"); + for (let i = 0; i < cancerTypesQueryNoFPPP.length; i++) { + let currentOption = document.createElement("option"); + currentOption.value = cancerTypesQueryNoFPPP[i]["cohort"]; + currentOption.text = + "(" + + cancerTypesQueryNoFPPP[i]["cohort"] + + ") " + + cancerTypesQueryNoFPPP[i]["description"]; + currentOption.id = cancerTypesQueryNoFPPP[i]["cohort"]; + selectBox.appendChild(currentOption); + } + let cancerTypeSelectedOptions = localStorage + .getItem("cancerTypeSelectedOptions") || null + if (cancerTypeSelectedOptions) { + cancerTypeSelectedOptions = cancerTypeSelectedOptions.split(","); + $(".cancerTypeMultipleSelection").val(cancerTypeSelectedOptions); + $(".cancerTypeMultipleSelection").trigger('change'); + } + return true; + } catch (err) { + message = "Could not fetch the cancer types from Firebrowse"; + handleDataFetchError(message); + } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -67,53 +71,58 @@ let displayNumberSamples = async function () { .select2("data") .map((cohortInfo) => cohortInfo.text.match(/\(([^)]+)\)/)[1]); if (myCohort.length != 0) { - // get counts of samples for selected tumor types: - numbersOfSamples = await firebrowse.fetchCounts(myCohort); - let formatted_numbersOfSamples = numbersOfSamples.map(x => { - const container = {}; - container.cohort = x.cohort.substring(0, numbersOfSamples[0].cohort.indexOf('-')); - container.mrnaseq = x.mrnaseq; - return container; - }); - // order counts array based on order in which tumor types were selected: - function orderThings (array, order, key) { - array.sort(function (a, b) { - var A = a[key], B = b[key]; - if (order.indexOf(A) > order.indexOf(B)) { - return 1; + try { + // get counts of samples for selected tumor types: + numbersOfSamples = await firebrowse.fetchCounts(myCohort); + let formatted_numbersOfSamples = numbersOfSamples.map(x => { + const container = {}; + container.cohort = x.cohort.substring(0, numbersOfSamples[0].cohort.indexOf('-')); + container.mrnaseq = x.mrnaseq; + return container; + }); + // order counts array based on order in which tumor types were selected: + function orderThings (array, order, key) { + array.sort(function (a, b) { + var A = a[key], B = b[key]; + if (order.indexOf(A) > order.indexOf(B)) { + return 1; + } else { + return -1; + } + }); + return array; + }; + orderedCountQuery = orderThings(formatted_numbersOfSamples, myCohort, 'cohort') + // build label: + let numSamplesLabel = ""; + let para; + for (let i = 0; i < orderedCountQuery.length; i++) { + if (numSamplesLabel == "") { + numSamplesLabel += orderedCountQuery[i].cohort + ": " + orderedCountQuery[i].mrnaseq; + para = document.createElement("P"); + para.setAttribute( + "style", + 'text-align: center; color: #4db6ac; font-family: Georgia, "Times New Roman", Times, serif' + ); + para.setAttribute("id", "numSamplesText"); + para.innerText = "Number of samples: " + numSamplesLabel; + cancerQuerySelectBox.appendChild(para); } else { - return -1; + document.getElementById("numSamplesText").remove(); + numSamplesLabel += ", " + orderedCountQuery[i].cohort + + ": " + orderedCountQuery[i].mrnaseq; + para.setAttribute( + "style", + 'text-align: center; color: #4db6ac; font-family: Georgia, "Times New Roman", Times, serif' + ); + para.setAttribute("id", "numSamplesText"); + para.innerText = "Number of samples: " + numSamplesLabel; + cancerQuerySelectBox.appendChild(para); } - }); - return array; - }; - orderedCountQuery = orderThings(formatted_numbersOfSamples, myCohort, 'cohort') - // build label: - let numSamplesLabel = ""; - let para; - for (let i = 0; i < orderedCountQuery.length; i++) { - if (numSamplesLabel == "") { - numSamplesLabel += orderedCountQuery[i].cohort + ": " + orderedCountQuery[i].mrnaseq; - para = document.createElement("P"); - para.setAttribute( - "style", - 'text-align: center; color: #4db6ac; font-family: Georgia, "Times New Roman", Times, serif' - ); - para.setAttribute("id", "numSamplesText"); - para.innerText = "Number of samples: " + numSamplesLabel; - cancerQuerySelectBox.appendChild(para); - } else { - document.getElementById("numSamplesText").remove(); - numSamplesLabel += ", " + orderedCountQuery[i].cohort + - ": " + orderedCountQuery[i].mrnaseq; - para.setAttribute( - "style", - 'text-align: center; color: #4db6ac; font-family: Georgia, "Times New Roman", Times, serif' - ); - para.setAttribute("id", "numSamplesText"); - para.innerText = "Number of samples: " + numSamplesLabel; - cancerQuerySelectBox.appendChild(para); } + } catch(err) { + message = "Could not fetch the cancer type counts from Firebrowse"; + handleDataFetchError(message); } } };