1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00

fix(operations): the Jobs pages refresh while a job is active

The list and the detail page were rendered once, so a job that finished
kept showing running until the page was reloaded by hand. While a shown
job is queued, running or cancelling, the page now reloads every five
seconds (not while hidden or while a form field has focus).
This commit is contained in:
catborise 2026-10-15 08:18:40 +03:00
parent 9d3d218fbb
commit aabce6b037
5 changed files with 26 additions and 1 deletions

View file

@ -0,0 +1,8 @@
{# Reloads the page while it shows an active job, so the page follows the job to its end. #}
<script id="follow-active-jobs">
setInterval(function () {
if (!document.hidden && !document.querySelector("form :focus")) {
window.location.reload();
}
}, 5000);
</script>

View file

@ -97,3 +97,6 @@
</div>
</div>
{% endblock %}
{% block script %}
{% if follow %}{% include "operations/follow_active.html" %}{% endif %}
{% endblock script %}

View file

@ -92,3 +92,6 @@
</div>
</div>
{% endblock %}
{% block script %}
{% if follow %}{% include "operations/follow_active.html" %}{% endif %}
{% endblock script %}

View file

@ -117,6 +117,15 @@ class JobsPageTestCase(TransactionTestCase):
self.assertContains(response, "Oct 13 23:04:00")
self.assertNotContains(response, "2026-10-13T20:04")
def test_list_and_detail_refresh_while_a_job_is_active(self):
self.client.force_login(self.user)
pages = (reverse("operations:jobs"), reverse("operations:job", args=[self.op.pk]))
for url in pages: # the job is queued
self.assertContains(self.client.get(url), 'id="follow-active-jobs"')
Operation.objects.filter(pk=self.op.pk).update(state=Operation.SUCCEEDED)
for url in pages:
self.assertNotContains(self.client.get(url), 'id="follow-active-jobs"')
def test_list_and_detail_show_a_state_badge(self):
Operation.objects.filter(pk=self.op.pk).update(state=Operation.FAILED, error="disk busy")
self.client.force_login(self.admin)

View file

@ -45,6 +45,7 @@ def jobs(request):
return render(request, "operations/jobs.html",
{"page": page, "rows": rows, "worker_last_seen": seen, "worker_unavailable": unavailable,
"filters": request.GET, "active_states": Operation.ACTIVE,
"follow": any(op.state in Operation.ACTIVE for op in page.object_list),
"states": Operation.ACTIVE + Operation.TERMINAL,
"kinds": sorted((name, _(kind.label)) for name, kind in jobs_module.KINDS.items()),
"page_query": urlencode({k: v for k, v in request.GET.items() if k != "page"})})
@ -55,7 +56,8 @@ def job(request, pk):
kind = jobs_module.KINDS.get(op.kind)
return render(request, "operations/job.html",
{"job": OperationSerializer(op, context={"request": request}).data, "op": op,
"progress_bytes": kind is not None and kind.progress_bytes})
"progress_bytes": kind is not None and kind.progress_bytes,
"follow": op.state in Operation.ACTIVE})
@require_POST