diff --git a/djangorota/djangorota/templates/rota/rota_run_detail.html b/djangorota/djangorota/templates/rota/rota_run_detail.html
index 4ee29fc..5f4c3a8 100644
--- a/djangorota/djangorota/templates/rota/rota_run_detail.html
+++ b/djangorota/djangorota/templates/rota/rota_run_detail.html
@@ -12,6 +12,11 @@
Rota run for {{ run.rota.name }}
Status: {{ run.get_status_display }} ({{ run.status }})
+
+
+ Status: {{ run.get_status_display }}
+
+
Created: {{ run.created_at|localtime }} started: {{ run.started_at|localtime }} finished: {{ run.finished_at|localtime }}
Back to rota
diff --git a/djangorota/rota/urls.py b/djangorota/rota/urls.py
index dab9593..64d1efd 100644
--- a/djangorota/rota/urls.py
+++ b/djangorota/rota/urls.py
@@ -35,6 +35,7 @@ urlpatterns = [
path("worker//leaves/delete_all/", views.leave_delete_all, name="leave_delete_all"),
path("worker//regenerate_token/", views.regenerate_worker_token, name="regenerate_worker_token"),
path("run//", views.rota_run_detail, name="rota_run_detail"),
+ path("run//status/", views.rota_run_status, name="rota_run_status"),
path("run//export/html/", views.rota_run_export_html, name="rota_run_export_html"),
path("run//export/download/", views.rota_run_export_download, name="rota_run_export_download"),
path("rota//export/builder/", views.rota_export_builder, name="rota_export_builder"),
diff --git a/djangorota/rota/views.py b/djangorota/rota/views.py
index b960c9f..0cffaae 100644
--- a/djangorota/rota/views.py
+++ b/djangorota/rota/views.py
@@ -738,6 +738,40 @@ def rota_run_detail(request, run_id):
return render(request, "rota/rota_run_detail.html", {"run": run, "result_pretty": result_pretty, "builder_html": builder_html})
+def rota_run_status(request, run_id):
+ """Return a small HTML fragment describing the run status.
+
+ This endpoint is intended to be polled by HTMX. While the run is
+ still running we return a fragment that contains the polling HTMX
+ attributes so the client will continue to poll. Once the run is
+ finished we return a final fragment (no polling attributes) and
+ include a small script to reload the page so the UI updates.
+ """
+ from .models import RotaRun
+
+ run = get_object_or_404(RotaRun, pk=run_id)
+
+ # If the run is still pending/running, return a self-updating fragment
+ if run.status in (RotaRun.STATUS_PENDING, RotaRun.STATUS_RUNNING):
+ html = (
+ f''
+ f'Status: {run.get_status_display()}'
+ f' started: {run.started_at}'
+ "
"
+ )
+ return HttpResponse(html)
+
+ # Finished/failed: return final fragment and reload the page so the
+ # user sees final logs/export without further polling.
+ final_html = (
+ f'Status: {run.get_status_display()}'
+ f' finished: {run.finished_at}
'
+ ""
+ )
+ return HttpResponse(final_html)
+
+
def rota_run_export_html(request, run_id):
from .models import RotaRun