Merge pull request '(ui) Add review actions to filtered run lists' (#32) from issue-22-operational-review-actions into master
Reviewed-on: #32
This commit was merged in pull request #32.
This commit is contained in:
@@ -273,6 +273,11 @@
|
||||
}
|
||||
button.secondary:hover,
|
||||
.button-link.secondary:hover { background: #eef3f8; }
|
||||
button.compact,
|
||||
.button-link.compact {
|
||||
font-size: 12px;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
button:disabled {
|
||||
background: #d8dee6;
|
||||
border-color: #d8dee6;
|
||||
|
||||
@@ -95,7 +95,22 @@
|
||||
<span class="muted">none</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{% if run.reviewed_at %}reviewed{% elif run.status == "failed" or run.status == "warning" %}<span class="status warning">needed</span>{% else %}<span class="muted">none</span>{% endif %}</td>
|
||||
<td>
|
||||
{% if run.reviewed_at %}
|
||||
reviewed
|
||||
{% elif run.status == "failed" or run.status == "warning" %}
|
||||
<div class="stack">
|
||||
<span class="status warning">needed</span>
|
||||
<form class="inline-form" method="post" action="{% url 'resolve_run_review' run.id %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{{ request.get_full_path }}">
|
||||
<button type="submit" class="secondary compact">Mark reviewed</button>
|
||||
</form>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="muted">none</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="9" class="muted">No runs matched the current filter.</td></tr>
|
||||
|
||||
@@ -234,6 +234,31 @@ class ViewTests(TestCase):
|
||||
self.assertContains(response, "needed")
|
||||
self.assertNotContains(response, f"Run {success.id}")
|
||||
|
||||
def test_runs_list_can_mark_problem_run_reviewed(self) -> None:
|
||||
self.client.force_login(self.staff_user)
|
||||
host = HostConfig.objects.create(host="web-01", address="web-01.example.test")
|
||||
run = BackupRun.objects.create(host=host, status=BackupRun.Status.FAILED, run_type=BackupRun.RunType.MANUAL)
|
||||
list_url = f'{reverse("runs_list")}?status=failed&review=needed'
|
||||
|
||||
response = self.client.get(list_url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Mark reviewed")
|
||||
self.assertContains(response, 'value="/runs/?status=failed&review=needed"', html=False)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("resolve_run_review", args=[run.id]),
|
||||
{"next": list_url},
|
||||
follow=True,
|
||||
)
|
||||
|
||||
run.refresh_from_db()
|
||||
self.assertIsNotNone(run.reviewed_at)
|
||||
self.assertEqual(run.reviewed_by, self.staff_user.username)
|
||||
self.assertRedirects(response, list_url)
|
||||
self.assertContains(response, f"Run {run.id} marked reviewed.")
|
||||
self.assertNotContains(response, f"Run {run.id}</a>", html=False)
|
||||
|
||||
def test_snapshots_list_filters_by_host_and_kind(self) -> None:
|
||||
self.client.force_login(self.staff_user)
|
||||
web = HostConfig.objects.create(host="web-01", address="web-01.example.test")
|
||||
|
||||
@@ -661,13 +661,13 @@ def resolve_run_review(request, run_id: int):
|
||||
return redirect("run_detail", run_id=run.id)
|
||||
if run.reviewed_at:
|
||||
messages.info(request, f"Run {run.id} was already marked reviewed.")
|
||||
return redirect("run_detail", run_id=run.id)
|
||||
return _redirect_after_run_review(request, run)
|
||||
|
||||
run.reviewed_at = timezone.now()
|
||||
run.reviewed_by = request.user.get_username()
|
||||
run.save(update_fields=["reviewed_at", "reviewed_by"])
|
||||
messages.success(request, f"Run {run.id} marked reviewed.")
|
||||
return redirect("run_detail", run_id=run.id)
|
||||
return _redirect_after_run_review(request, run)
|
||||
|
||||
|
||||
@staff_member_required
|
||||
@@ -937,6 +937,13 @@ def _next_run_for_schedule(schedule: ScheduleConfig | None, host_config: HostCon
|
||||
return None
|
||||
|
||||
|
||||
def _redirect_after_run_review(request, run: BackupRun):
|
||||
next_url = request.POST.get("next", "").strip()
|
||||
if next_url.startswith("/"):
|
||||
return redirect(next_url)
|
||||
return redirect("run_detail", run_id=run.id)
|
||||
|
||||
|
||||
def _retention_warning_for_host(host_config: HostConfig, schedule: ScheduleConfig | None) -> dict[str, object]:
|
||||
incomplete_count = host_config.snapshots.filter(
|
||||
kind=SnapshotRecord.Kind.INCOMPLETE,
|
||||
|
||||
Reference in New Issue
Block a user