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 += "