10587 pagination

This commit is contained in:
Arthur 2024-03-01 10:44:33 -08:00
parent bbdf5c76cb
commit 7d44da0a2a
3 changed files with 173 additions and 135 deletions

View File

@ -5,7 +5,7 @@ from django.conf import settings
from django.utils.translation import gettext_lazy as _
from extras.models import *
from netbox.tables import NetBoxTable, columns
from netbox.tables import BaseTable, NetBoxTable, columns
from .template_code import *
__all__ = (
@ -21,6 +21,8 @@ __all__ = (
'JournalEntryTable',
'ObjectChangeTable',
'SavedFilterTable',
'ReportResultsTable',
'ScriptResultsTable',
'TaggedItemTable',
'TagTable',
'WebhookTable',
@ -507,3 +509,67 @@ class JournalEntryTable(NetBoxTable):
default_columns = (
'pk', 'created', 'created_by', 'assigned_object_type', 'assigned_object', 'kind', 'comments'
)
class ScriptResultsTable(BaseTable):
index = tables.Column(
verbose_name=_('Line')
)
time = tables.Column(
verbose_name=_('Time')
)
status = tables.TemplateColumn(
template_code="""{% load log_levels %}{% log_level record.status %}""",
verbose_name=_('Level')
)
message = tables.Column(
verbose_name=_('Message')
)
class Meta(BaseTable.Meta):
empty_text = _('No results found')
fields = (
'index', 'time', 'status', 'message',
)
default_columns = (
'index', 'time', 'status', 'message',
)
class ReportResultsTable(BaseTable):
index = tables.Column(
verbose_name=_('Line')
)
method = tables.Column(
verbose_name=_('Method')
)
time = tables.Column(
verbose_name=_('Time')
)
status = tables.Column(
empty_values=(),
verbose_name=_('Level')
)
status = tables.TemplateColumn(
template_code="""{% load log_levels %}{% log_level record.status %}""",
verbose_name=_('Level')
)
object = tables.Column(
verbose_name=_('Object')
)
url = tables.Column(
verbose_name=_('Url')
)
message = tables.Column(
verbose_name=_('Message')
)
class Meta(BaseTable.Meta):
empty_text = _('No results found')
fields = (
'index', 'method', 'time', 'status', 'object', 'url', 'message',
)
default_columns = (
'index', 'method', 'time', 'status', 'object', 'url', 'message',
)

View File

@ -17,6 +17,7 @@ from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
from extras.dashboard.utils import get_widget_class
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
from netbox.views import generic
from netbox.views.generic.mixins import TableMixin
from utilities.forms import ConfirmationForm, get_field_value
from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.rqworker import get_workers_for_queue
@ -26,6 +27,7 @@ from utilities.views import ContentTypePermissionRequiredMixin, register_model_v
from . import filtersets, forms, tables
from .models import *
from .scripts import run_script
from .tables import ReportResultsTable, ScriptResultsTable
#
@ -1143,47 +1145,72 @@ class LegacyScriptRedirectView(ContentTypePermissionRequiredMixin, View):
return redirect(f'{url}{path}')
class ScriptResultView(generic.ObjectView):
class ScriptResultView(TableMixin, generic.ObjectView):
queryset = Job.objects.all()
def get_required_permission(self):
return 'extras.view_script'
def job_to_result_array(self, job):
results = []
def get_table(self, job, request, bulk_actions=True):
data = []
tests = None
table_logs = table_tests = None
index = 0
if job.data:
if 'log' in job.data:
if 'tests' in job.data:
tests = job.data['tests']
for log in job.data['log']:
index += 1
result = {
'method': None,
'index': index,
'time': log.get('time', None),
'level': log.get('level', None),
'object': log.get('object', None),
'status': log.get('status', None),
'message': log.get('message', None),
}
results.append(result)
else:
for test in job.data:
if 'log' in test:
for time, level, obj, url, message in test['log']:
result = {
'method': test,
'time': time,
'level': level,
'object': obj,
'url': url,
'message': message,
}
data.append(result)
return data
table_logs = ScriptResultsTable(data, user=request.user)
table_logs.configure(request)
else:
tests = job.data
if tests:
for method, test_data in tests.items():
if 'log' in test_data:
for time, status, obj, url, message in test_data['log']:
index += 1
result = {
'index': index,
'method': method,
'time': time,
'status': status,
'object': obj,
'url': url,
'message': message,
}
data.append(result)
table_tests = ReportResultsTable(data, user=request.user)
table_tests.configure(request)
return table_logs, table_tests
def get(self, request, **kwargs):
job = get_object_or_404(Job.objects.all(), pk=kwargs.get('job_pk'))
table_logs = table_tests = None
if job.completed:
table_logs, table_tests = self.get_table(job, request, bulk_actions=False)
context = {
'script': job.object,
'job': job,
'table_logs': table_logs,
'table_tests': table_tests,
}
if job.data and 'log' in job.data:
# Script
context['tests'] = job.data.get('tests', {})

View File

@ -3,124 +3,69 @@
{% load log_levels %}
{% load i18n %}
<p>
{% if job.started %}
{% trans "Started" %}: <strong>{{ job.started|annotated_date }}</strong>
{% elif job.scheduled %}
{% trans "Scheduled for" %}: <strong>{{ job.scheduled|annotated_date }}</strong> ({{ job.scheduled|naturaltime }})
{% else %}
{% trans "Created" %}: <strong>{{ job.created|annotated_date }}</strong>
{% endif %}
<div class="htmx-container">
<p>
{% if job.started %}
{% trans "Started" %}: <strong>{{ job.started|annotated_date }}</strong>
{% elif job.scheduled %}
{% trans "Scheduled for" %}: <strong>{{ job.scheduled|annotated_date }}</strong> ({{ job.scheduled|naturaltime }})
{% else %}
{% trans "Created" %}: <strong>{{ job.created|annotated_date }}</strong>
{% endif %}
{% if job.completed %}
{% trans "Duration" %}: <strong>{{ job.duration }}</strong>
{% endif %}
<span id="pending-result-label">{% badge job.get_status_display job.get_status_color %}</span>
</p>
{% if job.completed %}
{% trans "Duration" %}: <strong>{{ job.duration }}</strong>
{% endif %}
<span id="pending-result-label">{% badge job.get_status_display job.get_status_color %}</span>
</p>
{% if job.completed %}
{# Script log. Legacy reports will not have this. #}
{% if 'log' in job.data %}
<div class="card mb-3">
<h5 class="card-header">{% trans "Log" %}</h5>
{% if job.data.log %}
<table class="table table-hover panel-body">
<tr>
<th>{% trans "Line" %}</th>
<th>{% trans "Time" %}</th>
<th>{% trans "Level" %}</th>
<th>{% trans "Message" %}</th>
</tr>
{% for log in job.data.log %}
<div class="card">
<div class="table-responsive" id="object_list">
<h5 class="card-header">{% trans "Log" %}</h5>
{% include 'htmx/table.html' with table=table_logs %}
</div>
</div>
{# Script output. Legacy reports will not have this. #}
{% if 'output' in job.data %}
<div class="card mb-3">
<h5 class="card-header">{% trans "Output" %}</h5>
{% if job.data.output %}
<pre class="card-body font-monospace">{{ job.data.output }}</pre>
{% else %}
<div class="card-body text-muted">{% trans "None" %}</div>
{% endif %}
</div>
{% endif %}
{% if table_tests %}
{# Summary of test methods #}
<div class="card">
<h5 class="card-header">{% trans "Test Summary" %}</h5>
<table class="table table-hover">
{% for test, data in tests.items %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ log.time|placeholder }}</td>
<td>{% log_level log.status %}</td>
<td>{{ log.message|markdown }}</td>
<td class="font-monospace"><a href="#{{ test }}">{{ test }}</a></td>
<td class="text-end report-stats">
<span class="badge text-bg-success">{{ data.success }}</span>
<span class="badge text-bg-info">{{ data.info }}</span>
<span class="badge text-bg-warning">{{ data.warning }}</span>
<span class="badge text-bg-danger">{{ data.failure }}</span>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="card-body text-muted">{% trans "None" %}</div>
{% endif %}
</div>
</div>
{# Detailed results for individual tests #}
<div class="card">
<div class="table-responsive" id="object_list">
<h5 class="card-header">{% trans "Test Details" %}</h5>
{% include 'htmx/table.html' with table=table_tests %}
</div>
</div>
{% endif %}
{% elif job.started %}
{% include 'extras/inc/result_pending.html' %}
{% endif %}
{# Script output. Legacy reports will not have this. #}
{% if 'output' in job.data %}
<div class="card mb-3">
<h5 class="card-header">{% trans "Output" %}</h5>
{% if job.data.output %}
<pre class="card-body font-monospace">{{ job.data.output }}</pre>
{% else %}
<div class="card-body text-muted">{% trans "None" %}</div>
{% endif %}
</div>
{% endif %}
{# Test method logs (for legacy Reports) #}
{% if tests %}
{# Summary of test methods #}
<div class="card">
<h5 class="card-header">{% trans "Test Summary" %}</h5>
<table class="table table-hover">
{% for test, data in tests.items %}
<tr>
<td class="font-monospace"><a href="#{{ test }}">{{ test }}</a></td>
<td class="text-end report-stats">
<span class="badge text-bg-success">{{ data.success }}</span>
<span class="badge text-bg-info">{{ data.info }}</span>
<span class="badge text-bg-warning">{{ data.warning }}</span>
<span class="badge text-bg-danger">{{ data.failure }}</span>
</td>
</tr>
{% endfor %}
</table>
</div>
{# Detailed results for individual tests #}
<div class="card">
<h5 class="card-header">{% trans "Test Details" %}</h5>
<table class="table table-hover report">
<thead>
<tr class="table-headings">
<th>{% trans "Time" %}</th>
<th>{% trans "Level" %}</th>
<th>{% trans "Object" %}</th>
<th>{% trans "Message" %}</th>
</tr>
</thead>
<tbody>
{% for test, data in tests.items %}
<tr>
<th colspan="4" style="font-family: monospace">
<a name="{{ test }}"></a>{{ test }}
</th>
</tr>
{% for time, level, obj, url, message in data.log %}
<tr class="{% if level == 'failure' %}danger{% elif level %}{{ level }}{% endif %}">
<td>{{ time }}</td>
<td>
<label class="badge text-bg-{% if level == 'failure' %}danger{% else %}{{ level }}{% endif %}">{{ level|title }}</label>
</td>
<td>
{% if obj and url %}
<a href="{{ url }}">{{ obj }}</a>
{% elif obj %}
{{ obj }}
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
<td class="rendered-markdown">{{ message|markdown }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% elif job.started %}
{% include 'extras/inc/result_pending.html' %}
{% endif %}
</div>