9af188ea6147f9edb20bd531d3ec988501cf997c chmalee Fri Feb 6 12:18:25 2026 -0800 Fix jsonOutputArrays columnTypes output when more than one track is requested with /getData/track. Leaves /list/schema alone. Add option to hgTracks Downloads -> Download track data in view menu to include column headers in output, refs #36858 diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index 25c93b14a81..b16c28aaf2c 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -5664,71 +5664,85 @@ //////// var downloadCurrentTrackData = { downloadData: {}, // container for holding data while it comes in from the api currentRequests: {}, // pending requests intervalId: null, // the id of the timer that waits on the api failedTrackDataRequest: function(msg) { msgJson = JSON.parse(msg); alert("Download failed. Error message: '" + msgJson.error); }, receiveTrackData: function(track, data) { downloadCurrentTrackData.downloadData[track] = data; }, - convertJson: function(data, outType) { + convertJson: function(data, outType, withHeaders) { if (outType !== "tsv" && outType !== "csv") { alert("ERROR: incorrect output format option"); return null; } outSep = outType === "tsv" ? '\t' : ','; // TODO: someday we will probably want to include some of these fields // for each track downloaded, perhaps as an option ignoredKeys = new Set(["chrom", "dataTime", "dataTimeStamp", "downloadTime", "downloadTimeStamp", "start", "end", "track", "trackType", "genome", "itemsReturned", "columnTypes", "bigDataUrl", "chromSize", "hubUrl"]); // first get rid of top level non track object keys _.each(data, function(val, key) { - if (ignoredKeys.has(key)) {delete data[key];} + if (ignoredKeys.has(key)) { + // squirrel away the columnTypes if requested + if (key === "columnTypes") { + columnTypes = data[key]; + } + delete data[key]; + } }); // now go through each track and format it correctly str = ""; _.each(data, function(val, track) { str += "track name=\"" + track + "\"\n"; + if (withHeaders) { + headers = []; + for (let i of columnTypes[track]) { + headers.push(i.name); + } + str += headers.join(outSep) + "\n"; + } for (var row in val) { for (var i = 0; i < val[row].length; i++) { str += JSON.stringify(val[row][i]); if (i < val[row].length) { str += outSep; } } str += "\n"; } str += "\n"; // extra new line after each track oh well }); return new Blob([str], {type: "text/plain"}); }, makeDownloadFile: function(key) { if (_.keys(downloadCurrentTrackData.currentRequests).length === 0) { // first stop the timer so we don't execute again clearInterval(downloadCurrentTrackData.intervalId); - outType = $("#outputFormat")[0].selectedOptions[0].value; + let outType = $("#outputFormat")[0].selectedOptions[0].value; + let withHeaders = document.getElementById("downloadTrackHeaders").checked; var blob = null; if (outType === 'json') { blob = new Blob([JSON.stringify(downloadCurrentTrackData.downloadData[key])], {type: "text/plain"}); } else { - blob = downloadCurrentTrackData.convertJson(downloadCurrentTrackData.downloadData[key], outType); + blob = downloadCurrentTrackData.convertJson(downloadCurrentTrackData.downloadData[key], outType, withHeaders); } if (blob) { anchor = document.createElement("a"); anchor.href = URL.createObjectURL(blob); fname = $("#downloadFileName")[0].value; if (fname.length === 0) { fname = "trackDownload.txt"; } switch (outType) { case "tsv": if (!fname.endsWith(".tsv")) {fname += ".tsv";} break; case "csv": if (!fname.endsWith(".csv")) {fname += ".csv";} break; @@ -5842,38 +5856,41 @@ htmlStr += " disabled "; } else { htmlStr += " checked "; } htmlStr += ">"; htmlStr += ""; htmlStr += ""; if (showDisabledMsg) { htmlStr += " (?)"; } htmlStr += "
"; } }); htmlStr += "
"; htmlStr += ""; + " value='" + getDb() + ".tracks'>"; htmlStr += "
"; htmlStr += ""; htmlStr += ""; + htmlStr += "
"; + htmlStr += ""; + htmlStr += ""; htmlStr += "
"; downloadDialog.innerHTML = htmlStr; $("#checkAllDownloadTracks").on("click", function() { $(".downloadTrackName").each(function(i, elem) { elem.checked = true; }); }); $("#uncheckAllDownloadTracks").on("click", function() { $(".downloadTrackName").each(function(i, elem) { elem.checked = false; }); }); $(downloadDialog).dialog('open'); $("[id$='Tooltip'").each(function(i, elem) { addMouseover(elem, "This track must be downloaded with the Table Browser");