Skip to content

Commit 78afd62

Browse files
committed
[fix] Fixed progress bar behaviour on failed and cancelled
1 parent adf2b65 commit 78afd62

File tree

6 files changed

+202
-155
lines changed

6 files changed

+202
-155
lines changed

openwisp_firmware_upgrader/static/firmware-upgrader/css/batch-upgrade-operation.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,18 @@
200200
background-color: #dc3545;
201201
}
202202

203+
.batch-progress-fill.aborted {
204+
background-color: #6c757d;
205+
}
206+
207+
.batch-progress-fill.cancelled {
208+
background-color: #8b8b8b;
209+
}
210+
211+
.batch-progress-fill.partial-success {
212+
background-color: #fc9240;
213+
}
214+
203215
.status-cell {
204216
width: 200px;
205217
min-width: 200px;
@@ -244,6 +256,10 @@
244256
background-color: #6c757d;
245257
}
246258

259+
.upgrade-status-container .upgrade-progress-fill.cancelled {
260+
background-color: #8b8b8b;
261+
}
262+
247263
.upgrade-status-container span {
248264
font-size: 12px;
249265
font-weight: 500;
@@ -271,6 +287,10 @@
271287
color: #6c757d;
272288
}
273289

290+
.upgrade-status-cancelled {
291+
color: #8b8b8b;
292+
}
293+
274294
.upgrade-status-idle {
275295
color: #6c757d;
276296
}
@@ -314,6 +334,18 @@
314334
background-color: #dc3545;
315335
}
316336

337+
.batch-main-progress .upgrade-progress-fill.aborted {
338+
background-color: #6c757d;
339+
}
340+
341+
.batch-main-progress .upgrade-progress-fill.cancelled {
342+
background-color: #8b8b8b;
343+
}
344+
345+
.batch-main-progress .upgrade-progress-fill.partial-success {
346+
background-color: #ff9800;
347+
}
348+
317349
.batch-main-progress .upgrade-progress-text {
318350
color: #000;
319351
font-weight: bold;

openwisp_firmware_upgrader/static/firmware-upgrader/js/batch-upgrade-progress.js

Lines changed: 126 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ django.jQuery(function ($) {
77
if (!batchUpgradeId) {
88
return;
99
}
10-
11-
// Initialize existing upgrade operations with progress bars
1210
initializeExistingBatchUpgradeOperations($);
11+
initializeMainProgressBar($);
1312

14-
// Use the controller API host (always defined in change_form.html)
1513
const wsHost = owControllerApiHost.host;
1614
const wsUrl = `${getWebSocketProtocol()}${wsHost}/ws/firmware-upgrader/batch-upgrade-operation/${batchUpgradeId}/`;
1715

@@ -47,35 +45,35 @@ function initializeExistingBatchUpgradeOperations($, isRetry = false) {
4745
if (batchUpgradeOperationsInitialized && isRetry) {
4846
return;
4947
}
50-
5148
let statusCells = $("#result_list tbody td.status-cell");
5249
let processedCount = 0;
53-
5450
statusCells.each(function () {
5551
let statusCell = $(this);
56-
let statusText =
57-
statusCell.find(".status-content").text().trim() ||
58-
statusCell.text().trim();
59-
6052
if (statusCell.find(".upgrade-status-container").length > 0) {
6153
return;
6254
}
63-
55+
let statusText = statusCell.find(".status-content").text().trim();
56+
if (!statusText) {
57+
let cellText = statusCell.text().trim();
58+
statusText = cellText.replace(/\d+%.*$/, "").trim();
59+
}
6460
if (
6561
statusText &&
6662
(statusText.includes("progress") ||
6763
statusText === "success" ||
6864
statusText === "completed successfully" ||
65+
statusText === "completed with some failures" ||
6966
statusText === "failed" ||
70-
statusText === "aborted")
67+
statusText === "aborted" ||
68+
statusText === "cancelled")
7169
) {
7270
let operationId = statusCell.attr("data-operation-id") || "unknown";
71+
7372
let operation = {
7473
status: statusText,
7574
id: operationId,
7675
progress: null,
7776
};
78-
7977
updateBatchStatusWithProgressBar(statusCell, operation);
8078
processedCount++;
8179
}
@@ -92,14 +90,21 @@ function initializeExistingBatchUpgradeOperations($, isRetry = false) {
9290

9391
function initBatchUpgradeProgressWebSockets($, batchUpgradeProgressWebSocket) {
9492
batchUpgradeProgressWebSocket.addEventListener("open", function (e) {
95-
batchUpgradeOperationsInitialized = false;
96-
requestCurrentBatchState(batchUpgradeProgressWebSocket);
97-
initializeExistingBatchUpgradeOperations($, false);
93+
let existingContainers = $(
94+
"#result_list tbody td.status-cell .upgrade-status-container",
95+
);
96+
if (existingContainers.length === 0) {
97+
batchUpgradeOperationsInitialized = false;
98+
requestCurrentBatchState(batchUpgradeProgressWebSocket);
99+
initializeExistingBatchUpgradeOperations($, false);
100+
} else {
101+
// Just request current state without reinitializing
102+
requestCurrentBatchState(batchUpgradeProgressWebSocket);
103+
}
98104
});
99105

100106
batchUpgradeProgressWebSocket.addEventListener("close", function (e) {
101107
batchUpgradeOperationsInitialized = false;
102-
103108
if (e.code === 1006) {
104109
console.error("WebSocket closed");
105110
}
@@ -112,7 +117,6 @@ function initBatchUpgradeProgressWebSockets($, batchUpgradeProgressWebSocket) {
112117
batchUpgradeProgressWebSocket.addEventListener("message", function (e) {
113118
try {
114119
let data = JSON.parse(e.data);
115-
116120
if (data.type === "batch_status") {
117121
updateBatchProgress(data);
118122
} else if (data.type === "operation_progress") {
@@ -134,33 +138,74 @@ function initBatchUpgradeProgressWebSockets($, batchUpgradeProgressWebSocket) {
134138

135139
function updateBatchProgress(data) {
136140
let $ = django.jQuery;
137-
138-
// Update the main progress bar
139141
let mainProgressElement = $(".batch-main-progress");
140142
if (mainProgressElement.length > 0) {
141143
let progressPercentage =
142144
data.total > 0 ? Math.round((data.completed / data.total) * 100) : 0;
143145
let statusClass = (data.status || "").replace(/\s+/g, "-");
146+
let showPercentageText = true;
144147

145148
if (data.status === "success") {
146149
progressPercentage = 100;
147150
statusClass = "completed-successfully";
151+
showPercentageText = true;
152+
} else if (data.status === "failed") {
153+
let successfulOpsCount = $("#result_list tbody tr").filter(function () {
154+
let statusText = $(this).find(".status-cell .status-content").text().trim();
155+
return statusText === "success" || statusText === "completed successfully";
156+
}).length;
157+
158+
// Also check individual operation containers for success
159+
if (successfulOpsCount === 0) {
160+
$("#result_list tbody tr").each(function () {
161+
let statusContainer = $(this).find(".upgrade-status-container");
162+
if (
163+
statusContainer.length &&
164+
statusContainer.find(".upgrade-progress-fill.success").length
165+
) {
166+
successfulOpsCount++;
167+
}
168+
});
169+
}
170+
if (successfulOpsCount > 0) {
171+
// Some operations succeeded - partial success (orange)
172+
progressPercentage = 100;
173+
statusClass = "partial-success";
174+
showPercentageText = false;
175+
} else {
176+
// All operations failed - total failure (red)
177+
progressPercentage = 100;
178+
statusClass = "failed";
179+
showPercentageText = false;
180+
}
148181
}
149182

150183
let progressHtml = `
151184
<div class="upgrade-progress-bar">
152185
<div class="upgrade-progress-fill ${statusClass}" style="width: ${progressPercentage}%"></div>
153186
</div>
154-
<span class="upgrade-progress-text">${progressPercentage}%</span>
155187
`;
188+
if (showPercentageText) {
189+
progressHtml += `<span class="upgrade-progress-text">${progressPercentage}%</span>`;
190+
}
191+
156192
mainProgressElement.html(progressHtml);
157193
}
158194

195+
// Update completion information in the admin form if available
196+
if (data.total && data.completed) {
197+
let completedInfo = $(".field-completed .readonly");
198+
if (completedInfo.length > 0) {
199+
completedInfo.text(`${data.completed} out of ${data.total}`);
200+
}
201+
}
159202
let statusField = $(".field-status .readonly");
160203
if (statusField.length > 0 && data.status) {
161204
let displayStatus = data.status;
162205
if (data.status === "success") {
163206
displayStatus = "completed successfully";
207+
} else if (data.status === "failed") {
208+
displayStatus = "completed with some failures";
164209
} else if (data.status === "in-progress") {
165210
displayStatus = "in progress";
166211
}
@@ -194,15 +239,13 @@ function updateBatchOperationProgress(data) {
194239

195240
if (operationId === data.operation_id) {
196241
found = true;
197-
198242
let operation = {
199243
status: data.status,
200244
id: data.operation_id,
201245
progress: data.progress,
202246
};
203247

204248
updateBatchStatusWithProgressBar(statusCell, operation);
205-
206249
if (data.modified) {
207250
let modifiedCell = row.find("td:nth-child(4)");
208251
modifiedCell.html(getFormattedDateTimeString(data.modified));
@@ -221,13 +264,10 @@ function addNewOperationRow(data) {
221264
if (!data.device_name || !data.device_id) {
222265
return;
223266
}
224-
225267
let tbody = $("#result_list tbody");
226268
let existingRows = tbody.find("tr").length;
227269
let rowClass = existingRows % 2 === 0 ? "row1" : "row2";
228-
229270
tbody.find("tr td[colspan]").parent().remove();
230-
231271
let deviceUrl = `/admin/firmware_upgrader/upgradeoperation/${data.operation_id}/change/`;
232272
let imageDisplay = data.image_name || "None";
233273
let modifiedTime = data.modified
@@ -250,40 +290,22 @@ function addNewOperationRow(data) {
250290
`;
251291

252292
tbody.append(newRowHtml);
253-
254-
// Update the new row with progress bar
255293
let newRow = tbody.find(`tr:last`);
256294
let statusCell = newRow.find(".status-cell");
257295
let operation = {
258296
status: data.status,
259297
id: data.operation_id,
260298
progress: data.progress,
261299
};
262-
263300
updateBatchStatusWithProgressBar(statusCell, operation);
264301
}
265302

266303
function updateBatchStatusWithProgressBar(statusCell, operation) {
267304
let $ = django.jQuery;
268305
let status = operation.status;
269-
let progressPercentage = getBatchProgressPercentage(
270-
status,
271-
operation.progress,
272-
);
273-
let progressClass = status.replace(/\s+/g, "-");
274-
275-
if (!statusCell.find(".upgrade-status-container").length) {
276-
statusCell.find(".status-content").empty();
277-
statusCell
278-
.find(".status-content")
279-
.append('<div class="upgrade-status-container"></div>');
280-
281-
if (!statusCell.find(".status-content").length) {
282-
statusCell.empty();
283-
statusCell.append('<div class="upgrade-status-container"></div>');
284-
}
285-
}
286-
306+
let progressPercentage = getBatchProgressPercentage(status, operation.progress);
307+
statusCell.empty();
308+
statusCell.append('<div class="upgrade-status-container"></div>');
287309
let statusContainer = statusCell.find(".upgrade-status-container");
288310
let statusHtml = "";
289311

@@ -303,30 +325,37 @@ function updateBatchStatusWithProgressBar(statusCell, operation) {
303325
statusHtml = `<div class="upgrade-progress-bar">
304326
<div class="upgrade-progress-fill aborted" style="width: 100%"></div>
305327
</div>`;
328+
} else if (status === "cancelled") {
329+
statusHtml = `<div class="upgrade-progress-bar">
330+
<div class="upgrade-progress-fill cancelled" style="width: 100%"></div>
331+
</div>`;
306332
} else {
307333
statusHtml = `<div class="upgrade-progress-bar">
308334
<div class="upgrade-progress-fill" style="width: ${progressPercentage}%"></div>
309335
</div>`;
310336
}
311-
312337
statusContainer.html(statusHtml);
313338
}
314339

315340
function getBatchProgressPercentage(status, operationProgress = null) {
316341
if (operationProgress !== null && operationProgress !== undefined) {
317342
return Math.min(100, Math.max(5, operationProgress));
318343
}
319-
if (status === "completed successfully") {
344+
if (
345+
status === "completed successfully" ||
346+
status === "success" ||
347+
status === "failed" ||
348+
status === "aborted" ||
349+
status === "cancelled"
350+
) {
320351
return 100;
321352
}
322353
return 5;
323354
}
324355

325356
function getBatchUpgradeIdFromUrl() {
326357
try {
327-
let matches = window.location.pathname.match(
328-
/\/batchupgradeoperation\/([^\/]+)\//,
329-
);
358+
let matches = window.location.pathname.match(/\/batchupgradeoperation\/([^\/]+)\//);
330359
return matches && matches[1] ? matches[1] : null;
331360
} catch (error) {
332361
console.error("Error extracting batch ID from URL:", error);
@@ -342,6 +371,52 @@ function getWebSocketProtocol() {
342371
return protocol;
343372
}
344373

374+
function initializeMainProgressBar($) {
375+
let statusField = $(".field-status .readonly");
376+
if (statusField.length > 0) {
377+
let currentStatusText = statusField
378+
.contents()
379+
.filter(function () {
380+
return this.nodeType === 3 && this.textContent.trim();
381+
})
382+
.first()
383+
.text()
384+
.trim();
385+
386+
let mainProgressElement = $(".batch-main-progress");
387+
if (mainProgressElement.length > 0 && currentStatusText) {
388+
let progressPercentage = 100;
389+
let statusClass = "";
390+
let showPercentageText = true;
391+
392+
if (currentStatusText === "completed successfully") {
393+
statusClass = "completed-successfully";
394+
showPercentageText = true;
395+
} else if (currentStatusText === "completed with some failures") {
396+
statusClass = "partial-success";
397+
showPercentageText = false;
398+
} else if (currentStatusText === "in progress") {
399+
statusClass = "in-progress";
400+
showPercentageText = true;
401+
progressPercentage = 0;
402+
} else {
403+
statusClass = "failed";
404+
showPercentageText = false;
405+
}
406+
407+
let progressHtml = `
408+
<div class="upgrade-progress-bar">
409+
<div class="upgrade-progress-fill ${statusClass}" style="width: ${progressPercentage}%"></div>
410+
</div>
411+
`;
412+
if (showPercentageText) {
413+
progressHtml += `<span class="upgrade-progress-text">${progressPercentage}%</span>`;
414+
}
415+
mainProgressElement.html(progressHtml);
416+
}
417+
}
418+
}
419+
345420
function getFormattedDateTimeString(dateTimeString) {
346421
let dateTime = new Date(dateTimeString);
347422
return dateTime.toLocaleString();

0 commit comments

Comments
 (0)