14729 exec detail and common view

This commit is contained in:
Arthur 2024-01-22 15:38:05 -08:00
parent cf56066944
commit ddb7ec3f46
5 changed files with 192 additions and 19 deletions

View File

@ -59,10 +59,6 @@ class BackgroundTaskTable(BaseTable):
except Exception as e: except Exception as e:
return repr(e) return repr(e)
def __init__(self, queue_index, *args, **kwargs):
self.queue_index = queue_index
super().__init__(*args, **kwargs)
class WorkerTable(BaseTable): class WorkerTable(BaseTable):
name = tables.Column(verbose_name=_("Name")) name = tables.Column(verbose_name=_("Name"))

View File

@ -14,6 +14,8 @@ from django.views.generic import View
from netbox.config import get_config, PARAMS from netbox.config import get_config, PARAMS
from netbox.views import generic from netbox.views import generic
from netbox.views.generic.base import BaseObjectView from netbox.views.generic.base import BaseObjectView
from netbox.views.generic.mixins import ActionsMixin, TableMixin
from rq.exceptions import NoSuchJobError from rq.exceptions import NoSuchJobError
from rq.job import Job as RQ_Job from rq.job import Job as RQ_Job
from rq.registry import ( from rq.registry import (
@ -25,6 +27,7 @@ from rq.registry import (
) )
from rq.worker import Worker from rq.worker import Worker
from rq.worker_registration import clean_worker_registry from rq.worker_registration import clean_worker_registry
from utilities.htmx import is_embedded, is_htmx
from utilities.utils import count_related from utilities.utils import count_related
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
from . import filtersets, forms, tables from . import filtersets, forms, tables
@ -256,6 +259,12 @@ class ConfigRevisionRestoreView(ContentTypePermissionRequiredMixin, View):
# #
class BaseTaskListView(UserPassesTestMixin, TableMixin, View):
def test_func(self):
return self.request.user.is_staff
class BackgroundQueueListView(UserPassesTestMixin, View): class BackgroundQueueListView(UserPassesTestMixin, View):
def test_func(self): def test_func(self):
@ -269,15 +278,12 @@ class BackgroundQueueListView(UserPassesTestMixin, View):
}) })
class BackgroundTaskListView(UserPassesTestMixin, View): class BackgroundTaskListView(BaseTaskListView):
table = tables.BackgroundTaskTable
def test_func(self): def get_table_data(self, request, queue, status):
return self.request.user.is_staff
def get(self, request, queue_index, status):
registry = None registry = None
jobs = [] jobs = []
queue = get_queue_by_index(queue_index)
if status == 'queued': if status == 'queued':
if queue.count > 0: if queue.count > 0:
@ -311,8 +317,27 @@ class BackgroundTaskListView(UserPassesTestMixin, View):
for job in jobs: for job in jobs:
job.scheduled_at = registry.get_scheduled_time(job) job.scheduled_at = registry.get_scheduled_time(job)
table = tables.BackgroundTaskTable(data=jobs, user=request.user, queue_index=queue_index) return jobs
table.configure(request)
def get(self, request, queue_index, status):
queue = get_queue_by_index(queue_index)
data = self.get_table_data(request, queue, status)
table = self.get_table(data, request, False)
# If this is an HTMX request, return only the rendered table HTML
if is_htmx(request):
if is_embedded(request):
table.embedded = True
# Hide selection checkboxes
if 'pk' in table.base_columns:
table.columns.hide('pk')
return render(request, 'htmx/table.html', {
'table': table,
'queue': queue,
'status': status,
})
return render(request, 'core/background_task_list.html', { return render(request, 'core/background_task_list.html', {
'table': table, 'table': table,
'queue': queue, 'queue': queue,
@ -320,19 +345,36 @@ class BackgroundTaskListView(UserPassesTestMixin, View):
}) })
class WorkerListView(UserPassesTestMixin, View): class WorkerListView(BaseTaskListView):
table = tables.WorkerTable
def test_func(self): def test_func(self):
return self.request.user.is_staff return self.request.user.is_staff
def get(self, request, queue_index): def get_table_data(self, request, queue):
queue = get_queue_by_index(queue_index)
clean_worker_registry(queue) clean_worker_registry(queue)
all_workers = Worker.all(queue.connection) all_workers = Worker.all(queue.connection)
workers = [worker for worker in all_workers if queue.name in worker.queue_names()] workers = [worker for worker in all_workers if queue.name in worker.queue_names()]
return workers
def get(self, request, queue_index):
queue = get_queue_by_index(queue_index)
data = self.get_table_data(request, queue)
table = self.get_table(data, request, False)
# If this is an HTMX request, return only the rendered table HTML
if is_htmx(request):
if is_embedded(request):
table.embedded = True
# Hide selection checkboxes
if 'pk' in table.base_columns:
table.columns.hide('pk')
return render(request, 'htmx/table.html', {
'table': table,
'queue': queue,
})
table = tables.WorkerTable(data=workers, user=request.user)
table.configure(request)
return render(request, 'core/worker_list.html', { return render(request, 'core/worker_list.html', {
'table': table, 'table': table,
'queue': queue, 'queue': queue,
@ -361,11 +403,18 @@ class BackgroundTaskDetailView(UserPassesTestMixin, View):
except Exception: except Exception:
data_is_valid = False data_is_valid = False
try:
exc_info = job._exc_info
except AttributeError:
exc_info = None
return render(request, 'core/background_task.html', { return render(request, 'core/background_task.html', {
'queue': queue, 'queue': queue,
'job': job, 'job': job,
'queue_index': queue_index, 'queue_index': queue_index,
'data_is_valid': data_is_valid, 'data_is_valid': data_is_valid,
'dependency_id': job._dependency_id,
'exc_info': exc_info,
}) })

View File

@ -5,7 +5,7 @@
{% block breadcrumbs %} {% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:background_queue_list' %}">{% trans 'Background Tasks' %}</a></li> <li class="breadcrumb-item"><a href="{% url 'core:background_queue_list' %}">{% trans 'Background Tasks' %}</a></li>
<li class="breadcrumb-item"><a href="{% url 'core:background_task_list' queue_index=queue_index %}">{{ queue.name }}</a></li> <li class="breadcrumb-item"><a href="{% url 'core:background_task_list' queue_index=queue_index status=job.get_status %}">{{ queue.name }}</a></li>
{% endblock breadcrumbs %} {% endblock breadcrumbs %}
{% block title %}{% trans "Job" %} {{ job.id }}{% endblock %} {% block title %}{% trans "Job" %} {{ job.id }}{% endblock %}
@ -75,6 +75,13 @@
<th scope="row">{% trans "Kwargs" %}</th> <th scope="row">{% trans "Kwargs" %}</th>
<td>{{ job.kwargs|placeholder }}</td> <td>{{ job.kwargs|placeholder }}</td>
</tr> </tr>
{% if exc_info %}
<tr>
<th scope="row">{% trans "Exception:" %}</th>
<td><pre>{% if job.exc_info %}{{ job.exc_info|linebreaks }}{% endif %}</pre></td>
</tr>
{% endif %}
</table> </table>
</div> </div>
</div> </div>

View File

@ -1,4 +1,5 @@
{% extends 'generic/_base.html' %} {% extends 'generic/_base.html' %}
{% load buttons %}
{% load helpers %} {% load helpers %}
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %} {% load render_table from django_tables2 %}
@ -26,6 +27,7 @@
{% endblock tabs %} {% endblock tabs %}
{% block content %} {% block content %}
{% comment %}
<div class="row mb-3"> <div class="row mb-3">
<div class="col-auto ms-auto d-print-none"> <div class="col-auto ms-auto d-print-none">
{# Table configuration button #} {# Table configuration button #}
@ -37,9 +39,81 @@
</div> </div>
</div> </div>
{# Objects table #}
<div class="card"> <div class="card">
{% render_table table %} <div class="htmx-container table-responsive" id="object_list">
{% include 'htmx/table.html' %}
</div> </div>
</div>
{# /Objects table #}
{% endcomment %}
{# Object list tab #}
<div class="tab-pane show active" id="object-list" role="tabpanel" aria-labelledby="object-list-tab">
{# Object table controls #}
{% include 'inc/table_controls_htmx.html' with table_modal="ObjectTable_config" %}
<form method="post" class="form form-horizontal">
{% csrf_token %}
{# "Select all" form #}
{% if table.paginator.num_pages > 1 %}
<div id="select-all-box" class="d-none card d-print-none">
<div class="form col-md-12">
<div class="card-body">
<div class="float-end">
{% if 'bulk_edit' in actions %}
{% bulk_edit_button model query_params=request.GET %}
{% endif %}
{% if 'bulk_delete' in actions %}
{% bulk_delete_button model query_params=request.GET %}
{% endif %}
</div>
<div class="form-check">
<input type="checkbox" id="select-all" name="_all" class="form-check-input" />
<label for="select-all" class="form-check-label">
{% blocktrans trimmed with count=table.rows|length object_type_plural=table.data.verbose_name_plural %}
Select <strong>all {{ count }} {{ object_type_plural }}</strong> matching query
{% endblocktrans %}
</label>
</div>
</div>
</div>
</div>
{% endif %}
<div class="form form-horizontal">
{% csrf_token %}
<input type="hidden" name="return_url" value="{% if return_url %}{{ return_url }}{% else %}{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}{% endif %}" />
{# Objects table #}
<div class="card">
<div class="htmx-container table-responsive" id="object_list">
{% include 'htmx/table.html' %}
</div>
</div>
{# /Objects table #}
{# Form buttons #}
<div class="btn-list d-print-none mt-2">
{% block bulk_buttons %}
{% if 'bulk_edit' in actions %}
{% bulk_edit_button model query_params=request.GET %}
{% endif %}
{% if 'bulk_delete' in actions %}
{% bulk_delete_button model query_params=request.GET %}
{% endif %}
{% endblock %}
</div>
{# /Form buttons #}
</div>
</form>
</div>
{# /Object list tab #}
{% endblock content %} {% endblock content %}
{% block modals %} {% block modals %}

View File

@ -0,0 +1,47 @@
{% extends 'generic/_base.html' %}
{% load helpers %}
{% load i18n %}
{% load render_table from django_tables2 %}
{% block page-header %}
<div class="d-flex justify-content-between align-items-center">
{# Breadcrumbs #}
<nav class="breadcrumb-container px-3" aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'core:background_queue_list' %}">{% trans 'Background Tasks' %}</a></li>
</ol>
</nav>
</div>
{{ block.super }}
{% endblock page-header %}
{% block title %}{{ status|capfirst }} {% trans "Workers in " %}{{ queue.name }}{% endblock %}
{% block tabs %}
<ul class="nav nav-tabs px-3">
<li class="nav-item" role="presentation">
<a class="nav-link active" role="tab">{% trans "Workers" %}</a>
</li>
</ul>
{% endblock tabs %}
{% block content %}
<div class="row mb-3">
<div class="col-auto ms-auto d-print-none">
{# Table configuration button #}
<div class="table-configure input-group">
<button type="button" data-bs-toggle="modal" title="{% trans "Configure Table" %}" data-bs-target="#ObjectTable_config" class="btn">
<i class="mdi mdi-cog"></i> {% trans "Configure Table" %}
</button>
</div>
</div>
</div>
<div class="card">
{% render_table table %}
</div>
{% endblock content %}
{% block modals %}
{% table_config_form table table_name="ObjectTable" %}
{% endblock modals %}