class CeleryProgressBar { constructor(progressUrl, options) { this.progressUrl = progressUrl; options = options || {}; let progressBarId = options.progressBarId || 'progress-bar'; let progressBarMessage = options.progressBarMessageId || 'progress-bar-message'; this.progressBarElement = options.progressBarElement || document.getElementById(progressBarId); this.progressBarMessageElement = options.progressBarMessageElement || document.getElementById(progressBarMessage); this.onProgress = options.onProgress || this.onProgressDefault; this.onSuccess = options.onSuccess || this.onSuccessDefault; this.onError = options.onError || this.onErrorDefault; this.onTaskError = options.onTaskError || this.onTaskErrorDefault; this.onDataError = options.onDataError || this.onError; this.onRetry = options.onRetry || this.onRetryDefault; this.onIgnored = options.onIgnored || this.onIgnoredDefault; let resultElementId = options.resultElementId || 'celery-result'; this.resultElement = options.resultElement || document.getElementById(resultElementId); this.onResult = options.onResult || this.onResultDefault; // HTTP options this.onNetworkError = options.onNetworkError || this.onError; this.onHttpError = options.onHttpError || this.onError; this.pollInterval = options.pollInterval || 500; // Other options let barColorsDefault = { success: '#76ce60', error: '#dc4f63', progress: '#68a9ef', ignored: '#7a7a7a' } this.barColors = Object.assign({}, barColorsDefault, options.barColors); } onSuccessDefault(progressBarElement, progressBarMessageElement, result) { result = this.getMessageDetails(result); progressBarElement.style.backgroundColor = this.barColors.success; progressBarMessageElement.textContent = "Success! Please refresh page."; } onResultDefault(resultElement, result) { if (resultElement) { resultElement.textContent = result; } } /** * Default handler for all errors. * @param data - A Response object for HTTP errors, undefined for other errors */ onErrorDefault(progressBarElement, progressBarMessageElement, excMessage, data) { progressBarElement.style.backgroundColor = this.barColors.error; excMessage = excMessage || ''; progressBarMessageElement.textContent = "Uh-Oh, something went wrong! " + excMessage; } onTaskErrorDefault(progressBarElement, progressBarMessageElement, excMessage) { let message = this.getMessageDetails(excMessage); this.onError(progressBarElement, progressBarMessageElement, message); } onRetryDefault(progressBarElement, progressBarMessageElement, excMessage, retryWhen) { retryWhen = new Date(retryWhen); let message = 'Retrying in ' + Math.round((retryWhen.getTime() - Date.now())/1000) + 's: ' + excMessage; this.onError(progressBarElement, progressBarMessageElement, message); } onIgnoredDefault(progressBarElement, progressBarMessageElement, result) { progressBarElement.style.backgroundColor = this.barColors.ignored; progressBarMessageElement.textContent = result || 'Task result ignored!' } onProgressDefault(progressBarElement, progressBarMessageElement, progress) { progressBarElement.style.backgroundColor = this.barColors.progress; progressBarElement.style.width = progress.percent + "%"; var description = progress.description || ""; if (progress.current == 0) { if (progress.pending === true) { progressBarMessageElement.textContent = 'Waiting for task to start...'; } else { progressBarMessageElement.textContent = 'Task started...'; } } else { progressBarMessageElement.textContent = progress.current + ' of ' + progress.total + ' processed. ' + description; } } getMessageDetails(result) { if (this.resultElement) { return '' } else { return result || ''; } } /** * Process update message data. * @return true if the task is complete, false if it's not, undefined if `data` is invalid */ onData(data) { let done = false; if (data.progress) { this.onProgress(this.progressBarElement, this.progressBarMessageElement, data.progress); } if (data.complete === true) { done = true; if (data.success === true) { this.onSuccess(this.progressBarElement, this.progressBarMessageElement, data.result); } else if (data.success === false) { if (data.state === 'RETRY') { this.onRetry(this.progressBarElement, this.progressBarMessageElement, data.result.message, data.result.when); done = false; delete data.result; } else { this.onTaskError(this.progressBarElement, this.progressBarMessageElement, data.result); } } else { if (data.state === 'IGNORED') { this.onIgnored(this.progressBarElement, this.progressBarMessageElement, data.result); delete data.result; } else { done = undefined; this.onDataError(this.progressBarElement, this.progressBarMessageElement, "Data Error"); } } if (data.hasOwnProperty('result')) { this.onResult(this.resultElement, data.result); } } else if (data.complete === undefined) { done = undefined; this.onDataError(this.progressBarElement, this.progressBarMessageElement, "Data Error"); } return done; } async connect() { let response; try { response = await fetch(this.progressUrl); } catch (networkError) { this.onNetworkError(this.progressBarElement, this.progressBarMessageElement, "Network Error"); throw networkError; } if (response.status === 200) { let data; try { data = await response.json(); } catch (parsingError) { this.onDataError(this.progressBarElement, this.progressBarMessageElement, "Parsing Error") throw parsingError; } const complete = this.onData(data); if (complete === false) { setTimeout(this.connect.bind(this), this.pollInterval); } } else { this.onHttpError(this.progressBarElement, this.progressBarMessageElement, "HTTP Code " + response.status, response); } } static initProgressBar(progressUrl, options) { const bar = new this(progressUrl, options); bar.connect(); } }