forked from openemr/openemr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ehi-exporter.js
254 lines (233 loc) · 12 KB
/
ehi-exporter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
(function(window, oeExporter) {
class ExporterState {
taskIds = [];
currentTaskIndex = 0;
ajaxUrl = "";
csrfToken = "";
currentTaskPollingInterval = 0;
currentTaskPollingTimeout = 5000;
startExport() {
this.currentTaskIndex = -1;
this.runNextExport();
}
runNextExport() {
this.currentTaskIndex++;
if (this.currentTaskIndex < this.taskIds.length) {
let callBack = function() {
this.startExportRequestForTask(this.taskIds[this.currentTaskIndex]);
};
// just give a way to break promise callback chain
setTimeout(callBack.bind(this), 100);
} else {
// if we've finished everything... then we should clear the polling interval
this.clearPollingForExportStatus();
}
}
showErrorCardForTaskId(taskId, errorMessage='') {
// hide the processing div template node
let processingTask = document.querySelector(".template-task-processing[data-task-id='" + taskId + "']");
if (!processingTask) {
console.error("Could not find processing task for task id: " + taskId);
return;
}
processingTask.classList.add("d-none");
// grab the error div template node
let errorTaskTemplate = document.querySelector(".template-task-failed");
let errorTask = errorTaskTemplate.cloneNode(true);
// populate the error div template node with the task id, the patient pids
errorTask.querySelector(".taskId").innerText = taskId;
errorTask.dataset['taskId'] = taskId;
// show the error div template node
if (errorMessage) {
errorTask.querySelector(".errorMessage").innerText = errorMessage;
}
// TODO: @adunsulag need to handle what happens when they retry the export and we need to move on to the next
// possible export. Should we disable all of the buttons until the export has processed everything...
errorTask.querySelector(".btn-retry-export-task").addEventListener("click", () => {
errorTask.remove();
this.startExportRequestForTask(taskId);
});
errorTask.classList.remove("d-none");
processingTask.insertAdjacentElement("afterend", errorTask);
processingTask.remove(); // remove the processing node at the end since we don't need it.
}
startExportRequestForTask(taskId) {
// hide the queued div template node
this.showProcessingCardForTaskId(taskId, {taskId: taskId});
// send off the ajax request to start the export
let formParams = new FormData();
formParams.set("taskId", taskId);
formParams.set("submit", "Start Export");
formParams.set("action", "startExport");
formParams.set("_token", this.csrfToken);
window.top.restoreSession(); // make sure the session is populated before we send off an ajax request
let resultPromise = window.fetch(this.ajaxUrl, {
method: 'POST',
body: new URLSearchParams(formParams)
});
let exporterState = this;
resultPromise.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Failed to receive response from server');
}
})
.then(data => {
if (data.status == 'failed') {
this.showErrorCardForTaskId(taskId, data.error_message);
// move onto the next task in the queue
return exporterState.runNextExport();
} else {
this.showSuccessCardForTaskId(taskId, data);
return exporterState.runNextExport();
}
})
.catch(error => {
console.log(error);
this.showErrorCardForTaskId(taskId, error.message);
return exporterState.runNextExport();
});
// TODO: @adunsulag start the polling for the export status
this.startPollingForExportStatus(taskId);
}
startPollingForExportStatus(taskId) {
if (this.currentTaskPollingInterval > 0) {
this.clearPollingForExportStatus();
}
this.currentTaskPollingInterval = setInterval(this.pollForExportStatus.bind(this), this.currentTaskPollingTimeout, taskId);
}
clearPollingForExportStatus() {
clearInterval(this.currentTaskPollingInterval);
}
pollForExportStatus(taskId) {
let formParams = new FormData();
formParams.set("taskId", taskId);
formParams.set("submit", "Get Status");
formParams.set("action", "statusUpdate");
formParams.set("_token", this.csrfToken);
window.top.restoreSession(); // make sure the session is populated before we send off an ajax request
let resultPromise = window.fetch(this.ajaxUrl, {
method: 'POST',
body: new URLSearchParams(formParams)
});
let exporterState = this;
resultPromise.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Failed to receive response from server');
}
})
.then(data => {
if (data.status == 'failed') {
this.showErrorCardForTaskId(taskId, data.error_message);
} else if (data.status == 'completed') {
this.showSuccessCardForTaskId(taskId, data);
} else {
this.showProcessingCardForTaskId(taskId, data);
}
})
.catch(error => {
this.showErrorCardForTaskId(taskId, error.message);
console.log(error);
});
}
showProcessingCardForTaskId(taskId, data) {
let queuedTask = document.querySelector(".template-task-queued[data-task-id='" + taskId + "']");
queuedTask.classList.add("d-none");
// if there are any existing processing tasks we need to remove them
let processingTasks = document.querySelectorAll(".template-task-processing[data-task-id='" + taskId + "']");
processingTasks.forEach(function (task) {
task.remove();
});
// grab the processing div template node
let processingTaskTemplate = document.querySelector(".template-task-processing");
let processingTask = processingTaskTemplate.cloneNode(true);
// populate the processing div template node with the task id, the patient pids
processingTask.querySelector(".taskId").innerText = taskId;
processingTask.dataset['taskId'] = taskId;
processingTask.querySelector(".patientPids").innerText = queuedTask.querySelector(".patientPids").innerText;
// show the processing div template node
if (data.exportedResult) {
this.populateCardWithResultData(processingTask, taskId, data);
}
processingTask.classList.remove("d-none");
queuedTask.insertAdjacentElement("afterend", processingTask);
}
populateCardWithResultData(cardNode, taskId, data) {
// .exportedTablesList needs to be looped on the data.exportedResult table
let totalTablesExported = 0;
let totalRecordsExported = 0;
if (data.exportedResult) {
if (data.exportedResult.exportedTables) {
let tableNames = Object.keys(data.exportedResult.exportedTables);
totalTablesExported = tableNames.length;
let itemTemplate = cardNode.querySelector(".exportedTableListItem");
let templateParent = itemTemplate.parentNode;
for (let i = 0; i < totalTablesExported; i++) {
let tableItem = data.exportedResult.exportedTables[tableNames[i]];
let exportedTableListItem = itemTemplate.cloneNode(true);
exportedTableListItem.classList.remove("d-none");
exportedTableListItem.querySelector(".exportedTableName").innerText = tableItem.tableName + ".csv";
exportedTableListItem.querySelector(".exportedTableCount").innerText = tableItem.count;
totalRecordsExported += tableItem.count;
templateParent.appendChild(exportedTableListItem);
}
}
if (data.exportedResult.exportedDocumentCount >= 0) {
cardNode.querySelector(".documentsExportedCount").innerText = data.exportedResult.exportedDocumentCount;
}
}
cardNode.querySelector(".total-tables-exported").innerText = totalTablesExported;
cardNode.querySelector(".total-records-exported").innerText = totalRecordsExported;
if (data.includePatientDocuments) {
cardNode.querySelector(".documentsExportedSection").classList.remove("d-none");
}
}
showSuccessCardForTaskId(taskId, data) {
let processingTask = document.querySelector(".template-task-processing[data-task-id='" + taskId + "']");
processingTask.classList.add("d-none");
// grab the error div template node
let successTemplate = document.querySelector(".template-result-success");
let successTask = successTemplate.cloneNode(true);
successTask.querySelector(".taskId").innerText = taskId;
successTask.dataset['taskId'] = taskId;
// .download-link .download-link-name need to be populated
successTask.querySelector(".download-link-name").innerText = data.downloadName;
successTask.querySelector(".download-link").href = data.downloadLink;
successTask.querySelector(".download-link").addEventListener('click', function() {
window.top.restoreSession(); // make sure the session is populated before the download starts
});
// .hash-algo-title, .hash-text need to be populated
successTask.querySelector(".hash-algo-title").innerText = data.hashAlgoTitle;
successTask.querySelector(".hash-text").innerText = data.hash;
this.populateCardWithResultData(successTask, taskId, data);
successTask.classList.remove("d-none");
processingTask.insertAdjacentElement("afterend", successTask);
processingTask.remove(); // remove the processing node at the end since we don't need it.
}
}
let exporterState;
function displayExportStartDialog(dialogId) {
let container = document.getElementById(dialogId);
let modal = new bootstrap.Modal(container, {keyboard: false, focus: true, backdrop: 'static'});
modal.show();
}
oeExporter.displayExportStartDialog = displayExportStartDialog;
oeExporter.startTaskExports = function (ajaxUrl, csrfToken) {
let queuedTasks = document.querySelectorAll(".template-task-queued[data-task-id]");
let queuedTaskIds = [];
queuedTasks.forEach(function (task) {
queuedTaskIds.push(+task.dataset.taskId);
});
if (queuedTaskIds.length > 0) {
exporterState = new ExporterState();
exporterState.ajaxUrl = ajaxUrl;
exporterState.csrfToken = csrfToken;
exporterState.taskIds = queuedTaskIds;
exporterState.startExport();
}
};
window.oeExporter = oeExporter;
})(window, window.oeExporter || window.top.oeExporter || {});