Add functionality to reset stale RUNNING tasks to READY in task overview
This commit is contained in:
@@ -32,6 +32,8 @@ from reversion.admin import VersionAdmin
|
|||||||
from django.forms.widgets import RadioSelect
|
from django.forms.widgets import RadioSelect
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from tinymce.widgets import TinyMCE
|
from tinymce.widgets import TinyMCE
|
||||||
|
|
||||||
@@ -226,6 +228,24 @@ if DBTaskResult is not None:
|
|||||||
msg += f" Skipped {blocked} running task(s)."
|
msg += f" Skipped {blocked} running task(s)."
|
||||||
self.message_user(request, msg, level=messages.SUCCESS)
|
self.message_user(request, msg, level=messages.SUCCESS)
|
||||||
|
|
||||||
|
@admin.action(description="Reset stale RUNNING tasks to READY (>30 min)")
|
||||||
|
def reset_stale_running_to_ready(self, request, queryset):
|
||||||
|
cutoff = timezone.now() - timedelta(minutes=30)
|
||||||
|
updated = queryset.filter(status="RUNNING", started_at__lt=cutoff).update(
|
||||||
|
status="READY",
|
||||||
|
started_at=None,
|
||||||
|
finished_at=None,
|
||||||
|
worker_ids="",
|
||||||
|
exception_class_path="",
|
||||||
|
traceback="",
|
||||||
|
return_value=None,
|
||||||
|
)
|
||||||
|
self.message_user(
|
||||||
|
request,
|
||||||
|
f"Reset {updated} stale RUNNING task(s) to READY (older than 30 minutes).",
|
||||||
|
level=messages.SUCCESS,
|
||||||
|
)
|
||||||
|
|
||||||
@admin.action(description="Delete selected COMPLETED tasks")
|
@admin.action(description="Delete selected COMPLETED tasks")
|
||||||
def delete_selected_completed(self, request, queryset):
|
def delete_selected_completed(self, request, queryset):
|
||||||
completed_qs = queryset.filter(status="SUCCESSFUL")
|
completed_qs = queryset.filter(status="SUCCESSFUL")
|
||||||
@@ -278,6 +298,7 @@ if DBTaskResult is not None:
|
|||||||
admin_cls.actions = (
|
admin_cls.actions = (
|
||||||
"reset_failed_to_ready",
|
"reset_failed_to_ready",
|
||||||
"reset_selected_to_ready",
|
"reset_selected_to_ready",
|
||||||
|
"reset_stale_running_to_ready",
|
||||||
"delete_selected_completed",
|
"delete_selected_completed",
|
||||||
"delete_selected_failed",
|
"delete_selected_failed",
|
||||||
)
|
)
|
||||||
@@ -339,6 +360,7 @@ if DBTaskResult is not None:
|
|||||||
admin_cls.traceback_pretty = traceback_pretty
|
admin_cls.traceback_pretty = traceback_pretty
|
||||||
admin_cls.reset_failed_to_ready = reset_failed_to_ready
|
admin_cls.reset_failed_to_ready = reset_failed_to_ready
|
||||||
admin_cls.reset_selected_to_ready = reset_selected_to_ready
|
admin_cls.reset_selected_to_ready = reset_selected_to_ready
|
||||||
|
admin_cls.reset_stale_running_to_ready = reset_stale_running_to_ready
|
||||||
admin_cls.delete_selected_completed = delete_selected_completed
|
admin_cls.delete_selected_completed = delete_selected_completed
|
||||||
admin_cls.delete_selected_failed = delete_selected_failed
|
admin_cls.delete_selected_failed = delete_selected_failed
|
||||||
admin_cls.has_add_permission = has_add_permission
|
admin_cls.has_add_permission = has_add_permission
|
||||||
|
|||||||
@@ -82,10 +82,22 @@
|
|||||||
<option value="">Choose task action...</option>
|
<option value="">Choose task action...</option>
|
||||||
<option value="reset_failed">Reset FAILED to READY</option>
|
<option value="reset_failed">Reset FAILED to READY</option>
|
||||||
<option value="reset_selected">Reset selected to READY (skip RUNNING)</option>
|
<option value="reset_selected">Reset selected to READY (skip RUNNING)</option>
|
||||||
|
<option value="reset_stale_running">Reset stale RUNNING to READY</option>
|
||||||
<option value="delete_completed">Delete selected COMPLETED</option>
|
<option value="delete_completed">Delete selected COMPLETED</option>
|
||||||
<option value="delete_failed">Delete selected FAILED</option>
|
<option value="delete_failed">Delete selected FAILED</option>
|
||||||
<option value="delete_selected_all">Delete ALL selected (including RUNNING)</option>
|
<option value="delete_selected_all">Delete ALL selected (including RUNNING)</option>
|
||||||
</select>
|
</select>
|
||||||
|
<input
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
type="number"
|
||||||
|
name="stale_minutes"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
value="30"
|
||||||
|
style="max-width: 150px;"
|
||||||
|
title="Stale cutoff in minutes for reset stale RUNNING action"
|
||||||
|
aria-label="Stale minutes"
|
||||||
|
>
|
||||||
<button type="submit" class="btn btn-sm btn-primary">Apply to selected</button>
|
<button type="submit" class="btn btn-sm btn-primary">Apply to selected</button>
|
||||||
<div class="form-check ms-2">
|
<div class="form-check ms-2">
|
||||||
<input class="form-check-input" type="checkbox" id="select-all-tasks">
|
<input class="form-check-input" type="checkbox" id="select-all-tasks">
|
||||||
|
|||||||
+24
-1
@@ -4,6 +4,7 @@ import csv
|
|||||||
import random
|
import random
|
||||||
import copy
|
import copy
|
||||||
import io
|
import io
|
||||||
|
from datetime import timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from django.db.models.base import Model as Model
|
from django.db.models.base import Model as Model
|
||||||
@@ -25,6 +26,7 @@ from django.template.defaultfilters import filesizeformat
|
|||||||
# from django.contrib.auth.models import User
|
# from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib import messages
|
||||||
from django.core.exceptions import (
|
from django.core.exceptions import (
|
||||||
FieldError,
|
FieldError,
|
||||||
ObjectDoesNotExist,
|
ObjectDoesNotExist,
|
||||||
@@ -882,7 +884,7 @@ def series_reconstruct_status_htmx(request, series_id, task_id):
|
|||||||
@login_required
|
@login_required
|
||||||
@user_is_atlas_editor
|
@user_is_atlas_editor
|
||||||
def task_overview(request):
|
def task_overview(request):
|
||||||
"""Atlas admin-style task overview page with active/completed/failed task summaries."""
|
"""Atlas editor task control page for inspecting and recovering queued/background tasks."""
|
||||||
if DBTaskResult is None:
|
if DBTaskResult is None:
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
@@ -930,6 +932,27 @@ def task_overview(request):
|
|||||||
if blocked:
|
if blocked:
|
||||||
msg += f" Skipped {blocked} running task(s)."
|
msg += f" Skipped {blocked} running task(s)."
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
|
elif action == "reset_stale_running":
|
||||||
|
stale_minutes_raw = (request.POST.get("stale_minutes") or "30").strip()
|
||||||
|
try:
|
||||||
|
stale_minutes = max(int(stale_minutes_raw), 1)
|
||||||
|
except ValueError:
|
||||||
|
stale_minutes = 30
|
||||||
|
|
||||||
|
cutoff = timezone.now() - timedelta(minutes=stale_minutes)
|
||||||
|
updated = selected_qs.filter(status="RUNNING", started_at__lt=cutoff).update(
|
||||||
|
status="READY",
|
||||||
|
started_at=None,
|
||||||
|
finished_at=None,
|
||||||
|
worker_ids="",
|
||||||
|
exception_class_path="",
|
||||||
|
traceback="",
|
||||||
|
return_value=None,
|
||||||
|
)
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
f"Reset {updated} stale RUNNING task(s) to READY (older than {stale_minutes} minute(s)).",
|
||||||
|
)
|
||||||
elif action == "delete_completed":
|
elif action == "delete_completed":
|
||||||
deleted, _ = selected_qs.filter(status="SUCCESSFUL").delete()
|
deleted, _ = selected_qs.filter(status="SUCCESSFUL").delete()
|
||||||
messages.success(request, f"Deleted {deleted} completed task record(s).")
|
messages.success(request, f"Deleted {deleted} completed task record(s).")
|
||||||
|
|||||||
Reference in New Issue
Block a user