Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TN-3259 research data export - UI not showing the most current download link #4339

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 141 additions & 18 deletions portal/static/js/src/components/ExportInstruments.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,15 @@
:initElementId="getInitElementId()"
:exportUrl="getExportUrl()"
:exportIdentifier="currentStudy"
v-on:initExport="handleInitExport"
v-on:doneExport="handleAfterExport"
v-on:initExportCustomEvent="initExportEvent"></ExportDataLoader>
<!-- display link to the last export -->
<div class="export__history" v-if="hasExportHistory()">
<div class="text-muted prompt" v-text="exportHistoryTitle"></div>
<div v-if="exportHistory">
<a :href="exportHistory.url" target="_blank">
<span v-text="exportHistory.instruments.join(', ')"></span>
<span v-text="(exportHistory.instruments || []).join(', ')"></span>
<span v-text="exportHistory.date"></span>
</a>
</div>
Expand Down Expand Up @@ -98,24 +99,25 @@
subStudyIdentifier: "substudy",
mainStudyInstrumentsList:[],
subStudyInstrumentsList:[],
exportHistory: null
exportHistory: null,
currentTaskUrl: null
}};
},
mixins: [CurrentUser],
mounted: function() {
this.setCurrentMainStudy();
this.initCurrentUser(function() {
this.getInstrumentList();
this.setExportHistory(this.getCacheExportedDataInfo());
this.handleSetExportHistory();
}.bind(this));
},
watch: {
currentStudy: function(newVal, oldVal) {
//watch for when study changes
//reset last exported item link as it is specific to each study
this.setExportHistory(this.getCacheExportedDataInfo());
//reset export error
this.resetExportError();
this.handleSetExportHistory();
//reset export display info
this.resetExportInfoUI();
//reset instrument(s) selected
this.resetInstrumentSelections();
},
Expand Down Expand Up @@ -206,8 +208,11 @@
self.instruments.selected = arrSelected;
});
$("#patientsInstrumentList [name='instrument'], #patientsDownloadTypeList [name='downloadType']").on("click", function() {
//clear pre-existing error
self.resetExportError();
//clear pre-existing export info display
self.resetExportInfoUI();
if (self.hasInstrumentsSelection()) {
$("#patientsDownloadButton").removeAttr("disabled");
}
});
//patientsDownloadTypeList downloadType
$("#patientsDownloadTypeList [name='downloadType']").on("click", function() {
Expand All @@ -218,23 +223,34 @@
}
});
$("#dataDownloadModal").on("show.bs.modal", function () {
self.resetExportInfoUI();
self.setInstrumentsListReady();
self.instruments.selected = [];
self.instruments.dataType = "csv";
self.resetExportError();
self.setInstrumentsListReady();
$(this).find("#patientsInstrumentList label").removeClass("active");
$(this).find("[name='instrument']").prop("checked", false);
});
},
resetExportError: function() {
this.$refs.exportDataLoader.clearExportDataUI();
resetExportInfoUI: function() {
this.$refs.exportDataLoader.clearInProgress();
},
setInProgress: function(inProgress) {
if (!inProgress) {
this.resetExportInfoUI();
return;
}
this.$refs.exportDataLoader.setInProgress(true);
},
initExportEvent: function() {
/*
* custom UI events associated with exporting data
*/
let self = this;
$("#dataDownloadModal").on("hide.bs.modal", function () {
$("#"+self.getInitElementId()).removeAttr("data-export-in-progress");
self.setInProgress(false);
});
$(window).on("focus", function() {
self.handleSetExportHistory();
});
},
setDataType: function (event) {
Expand All @@ -259,21 +275,100 @@
var queryStringInstruments = (this.instruments.selected).map(item => `instrument_id=${item}`).join("&");
return `/api/patient/assessment?${queryStringInstruments}&format=${this.instruments.dataType}`;
},
getDefaultExportObj: function() {
return {
study: this.currentStudy,
date: new Date().toLocaleString(),
instruments: this.instruments.selected || []
}
},
handleInitExport: function(statusUrl) {
if (!statusUrl) return;
// whenever the user initiates an export, we cache the associated celery task URL
this.setCacheTask({
...this.getDefaultExportObj(),
url: statusUrl
});
this.currentTaskUrl = statusUrl;
},
handleAfterExport: function(resultUrl) {
//export is done, save the last export to local storage
this.setCacheExportedDataInfo(resultUrl);
},
getCacheExportTaskKey: function() {
return `export_data_task_${this.getUserId()}_${this.currentStudy}`;
},
removeCacheTaskURL: function() {
localStorage.removeItem(this.getCacheExportTaskKey());
},
setCacheTask: function(taskObj) {
if (!taskObj) return;
localStorage.setItem(this.getCacheExportTaskKey(), JSON.stringify(taskObj));
},
getCacheTask: function() {
const task = localStorage.getItem(this.getCacheExportTaskKey());
if (!task) return null;
let resultJSON = null;
try {
resultJSON = JSON.parse(task);
} catch(e) {
console.log("Unable to parse task JSON ", e);
resultJSON = null;
}
return resultJSON;
},
getFinishedStatusURL: function(url) {
if (!url) return "";
return url.replace("/status", "");
},
getExportDataInfoFromTask: function(callback) {
callback = callback || function() {};
const task = this.getCacheTask();
const self = this;
if (!task) {
callback({data: null});
return;
}
const taskURL = task.url;
if (!taskURL) {
callback({data: null});
return;
}
$.getJSON(taskURL, function(data) {
if (!data) {
callback({data: null});
return;
}
// check the status of the celery task and returns the data if it had been successful
const exportStatus = String(data["state"]).toUpperCase();
callback({
data :
exportStatus === "SUCCESS"?
{
...task,
url: self.getFinishedStatusURL(taskURL)
}:
null
});
// callback({
// data: {
// ...task,
// url: self.getFinishedStatusURL(taskURL)
// }
// })
}).fail(function() {
callback({data: null});
});
},
getCacheExportedDataInfoKey: function() {
//uniquely identified by each user and the study
return `exporDataInfo_${this.getUserId()}_${this.currentStudy}}`;
return `exporDataInfo_${this.getUserId()}_${this.currentStudy}`;
},
setCacheExportedDataInfo: function(resultUrl) {
if (!resultUrl) return false;
if (!this.hasInstrumentsSelection()) return;
var o = {
study: this.currentStudy,
date: new Date().toLocaleString(),
instruments: this.instruments.selected,
...this.getDefaultExportObj(),
url: resultUrl
};
localStorage.setItem(this.getCacheExportedDataInfoKey(), JSON.stringify(o));
Expand All @@ -284,14 +379,42 @@
if (!cachedItem) {
return null;
}
return JSON.parse(cachedItem);
let resultJSON = null;
try {
resultJSON = JSON.parse(cachedItem);
} catch(e) {
console.log("Unable to parse cached data export info ", e);
resultJSON = null;
}
return resultJSON;
},
hasExportHistory: function() {
return this.exportHistory || this.getCacheExportedDataInfo();
},
setExportHistory: function(o) {
this.exportHistory = o;
},
handleSetExportHistory: function() {
const self = this;
this.getExportDataInfoFromTask(function(data) {
if (data && data.data) {
this.setExportHistory(data.data);
const task = this.getCacheTask();
// console.log("current task URL ", self.getFinishedStatusURL(self.currentTaskUrl));
// console.log("cached task URL ", self.getFinishedStatusURL(task.url));
if (task &&
task.url &&
self.getFinishedStatusURL(task.url) === self.getFinishedStatusURL(self.currentTaskUrl)) {
this.setInProgress(false);
}
return;
}
const cachedDataInfo = this.getCacheExportedDataInfo();
if (cachedDataInfo) {
this.setExportHistory(cachedDataInfo);
}
}.bind(this));
}
}
};
</script>
28 changes: 18 additions & 10 deletions portal/static/js/src/components/asyncExportDataLoader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,15 @@
clearTimeElapsed: function() {
this.exportTimeElapsed = 0;
},
onBeforeExportData: function() {
clearInProgress: function() {
this.updateExportProgress("", "");
this.clearExportDataTimeoutID();
this.clearTimeElapsed();
this.clearExportDataUI();
this.setInProgress(false);
},
onBeforeExportData: function() {
this.clearInProgress();
this.setInProgress(true);
$("#" + this.initElementId).attr("disabled", true);
$(".export__display-container").addClass("active");
Expand All @@ -103,7 +107,6 @@
},
onAfterExportData: function(options) {
options = options || {};
let delay = options.delay||5000;
$("#" + this.initElementId).attr("disabled", false);
$(".export__status").removeClass("active");
this.setInProgress();
Expand All @@ -129,6 +132,8 @@
url: exportDataUrl,
success: function(data, status, request) {
let statusUrl= request.getResponseHeader("Location");
// console.log("status URL ", statusUrl)
self.$emit("initExport", statusUrl);
self.updateExportProgress(statusUrl, function(data) {
self.onAfterExportData(data);
});
Expand Down Expand Up @@ -165,7 +170,7 @@
let self = this;
let waitTime = 3000;
// send GET request to status URL
let rqId = $.getJSON(statusUrl, function(data) {
$.getJSON(statusUrl, function(data) {
if (!data) {
callback({error: true});
return;
Expand All @@ -183,12 +188,12 @@
percent = parseInt(data['current'] * 100 / data['total']) + "%";
} else {
percent = " -- %";
//allow maximum allowed elapsed time of pending status and no progress percentage returned,
//if still no progress returned, then return error and display message
if (exportStatus === "PENDING" && self.exportTimeElapsed > self.maximumPendingTime) {
callback({error: true, message: "Processing job not responding. Please try again.", delay: 10000});
return;
}
}
//allow maximum allowed elapsed time of pending status and no progress percentage returned,
//if still no progress returned, then return error and display message
if (exportStatus === "PENDING" && self.exportTimeElapsed > self.maximumPendingTime) {
callback({error: true, message: "Processing job not responding. Please try again.", delay: 30000});
return;
}
//update status and percentage displays
self.updateProgressDisplay(exportStatus, percent, true);
Expand All @@ -199,6 +204,9 @@
setTimeout(function() {
window.location.assign(resultUrl);
}, 50); //wait a bit before retrieving results
} else {
callback({error: true, message: "Unknown status return from the processing job. Status: ", exportStatus});
return;
}
self.updateProgressDisplay(data["state"], "");
setTimeout(function() {
Expand All @@ -214,7 +222,7 @@
(self.arrExportDataTimeoutID).push(self.exportDataTimeoutID);
}
}).fail(function() {
callback({error: true});
callback({error: true, message: "The processing job has failed."});
});
}
}
Expand Down
Loading