diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py
index 8482c5e24..048f5cdd3 100644
--- a/netbox/extras/tables/tables.py
+++ b/netbox/extras/tables/tables.py
@@ -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',
+ )
diff --git a/netbox/extras/views.py b/netbox/extras/views.py
index 0c7a6a48b..ead25e576 100644
--- a/netbox/extras/views.py
+++ b/netbox/extras/views.py
@@ -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', {})
diff --git a/netbox/templates/extras/htmx/script_result.html b/netbox/templates/extras/htmx/script_result.html
index ed5dd9cbd..438adc460 100644
--- a/netbox/templates/extras/htmx/script_result.html
+++ b/netbox/templates/extras/htmx/script_result.html
@@ -3,124 +3,69 @@
{% load log_levels %}
{% load i18n %}
-
- {% if job.started %}
- {% trans "Started" %}: {{ job.started|annotated_date }}
- {% elif job.scheduled %}
- {% trans "Scheduled for" %}: {{ job.scheduled|annotated_date }} ({{ job.scheduled|naturaltime }})
- {% else %}
- {% trans "Created" %}: {{ job.created|annotated_date }}
- {% endif %}
+
+
+ {% if job.started %}
+ {% trans "Started" %}: {{ job.started|annotated_date }}
+ {% elif job.scheduled %}
+ {% trans "Scheduled for" %}: {{ job.scheduled|annotated_date }} ({{ job.scheduled|naturaltime }})
+ {% else %}
+ {% trans "Created" %}: {{ job.created|annotated_date }}
+ {% endif %}
+ {% if job.completed %}
+ {% trans "Duration" %}: {{ job.duration }}
+ {% endif %}
+ {% badge job.get_status_display job.get_status_color %}
+
{% if job.completed %}
- {% trans "Duration" %}:
{{ job.duration }}
- {% endif %}
-
{% badge job.get_status_display job.get_status_color %}
-
-{% if job.completed %}
- {# Script log. Legacy reports will not have this. #}
- {% if 'log' in job.data %}
-
-
- {% if job.data.log %}
-
-
- {% trans "Line" %} |
- {% trans "Time" %} |
- {% trans "Level" %} |
- {% trans "Message" %} |
-
- {% for log in job.data.log %}
+
+
+
+ {% include 'htmx/table.html' with table=table_logs %}
+
+
+
+ {# Script output. Legacy reports will not have this. #}
+ {% if 'output' in job.data %}
+
+
+ {% if job.data.output %}
+
{{ job.data.output }}
+ {% else %}
+
{% trans "None" %}
+ {% endif %}
+
+ {% endif %}
+
+ {% if table_tests %}
+ {# Summary of test methods #}
+
+
+
+ {% for test, data in tests.items %}
- {{ forloop.counter }} |
- {{ log.time|placeholder }} |
- {% log_level log.status %} |
- {{ log.message|markdown }} |
+ {{ test }} |
+
+ {{ data.success }}
+ {{ data.info }}
+ {{ data.warning }}
+ {{ data.failure }}
+ |
{% endfor %}
- {% else %}
-
{% trans "None" %}
- {% endif %}
-
+
+
+ {# Detailed results for individual tests #}
+
+
+
+ {% include 'htmx/table.html' with table=table_tests %}
+
+
+ {% endif %}
+ {% elif job.started %}
+ {% include 'extras/inc/result_pending.html' %}
{% endif %}
-
- {# Script output. Legacy reports will not have this. #}
- {% if 'output' in job.data %}
-
-
- {% if job.data.output %}
-
{{ job.data.output }}
- {% else %}
-
{% trans "None" %}
- {% endif %}
-
- {% endif %}
-
- {# Test method logs (for legacy Reports) #}
- {% if tests %}
-
- {# Summary of test methods #}
-
-
-
- {% for test, data in tests.items %}
-
- {{ test }} |
-
- {{ data.success }}
- {{ data.info }}
- {{ data.warning }}
- {{ data.failure }}
- |
-
- {% endfor %}
-
-
-
- {# Detailed results for individual tests #}
-
-
-
-
-
- {% trans "Time" %} |
- {% trans "Level" %} |
- {% trans "Object" %} |
- {% trans "Message" %} |
-
-
-
- {% for test, data in tests.items %}
-
-
- {{ test }}
- |
-
- {% for time, level, obj, url, message in data.log %}
-
- {{ time }} |
-
-
- |
-
- {% if obj and url %}
- {{ obj }}
- {% elif obj %}
- {{ obj }}
- {% else %}
- {{ ''|placeholder }}
- {% endif %}
- |
- {{ message|markdown }} |
-
- {% endfor %}
- {% endfor %}
-
-
-
-
- {% endif %}
-{% elif job.started %}
- {% include 'extras/inc/result_pending.html' %}
-{% endif %}
+