diff --git a/netbox/dcim/tables/sites.py b/netbox/dcim/tables/sites.py index f013025f7..a4de18cee 100644 --- a/netbox/dcim/tables/sites.py +++ b/netbox/dcim/tables/sites.py @@ -99,9 +99,9 @@ class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = Site fields = ( - 'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'tenant_group', 'asns', 'asn_count', - 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', - 'contacts', 'tags', 'created', 'last_updated', 'actions', + 'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'tenant_group', 'asns', + 'asn_count', 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', + 'comments', 'contacts', 'tags', 'created', 'last_updated', 'actions', ) default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'description') diff --git a/netbox/extras/choices.py b/netbox/extras/choices.py index ef74ed67b..92d09e2ad 100644 --- a/netbox/extras/choices.py +++ b/netbox/extras/choices.py @@ -148,12 +148,12 @@ class JobResultStatusChoices(ChoiceSet): STATUS_FAILED = 'failed' CHOICES = ( - (STATUS_PENDING, 'Pending'), - (STATUS_SCHEDULED, 'Scheduled'), - (STATUS_RUNNING, 'Running'), - (STATUS_COMPLETED, 'Completed'), - (STATUS_ERRORED, 'Errored'), - (STATUS_FAILED, 'Failed'), + (STATUS_PENDING, 'Pending', 'cyan'), + (STATUS_SCHEDULED, 'Scheduled', 'gray'), + (STATUS_RUNNING, 'Running', 'blue'), + (STATUS_COMPLETED, 'Completed', 'green'), + (STATUS_ERRORED, 'Errored', 'red'), + (STATUS_FAILED, 'Failed', 'red'), ) TERMINAL_STATE_CHOICES = ( diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 4054c4ae4..ce700fedc 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -640,6 +640,9 @@ class JobResult(models.Model): def get_absolute_url(self): return reverse(f'extras:{self.obj_type.name}_result', args=[self.pk]) + def get_status_color(self): + return JobResultStatusChoices.colors.get(self.status) + @property def duration(self): if not self.completed: diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index eb4d6899b..c2b8c9424 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -1,5 +1,6 @@ import django_tables2 as tables from django.conf import settings +from django.utils.translation import gettext as _ from extras.models import * from netbox.tables import NetBoxTable, columns @@ -41,6 +42,15 @@ class JobResultTable(NetBoxTable): name = tables.Column( linkify=True ) + obj_type = columns.ContentTypeColumn( + verbose_name=_('Type') + ) + status = columns.ChoiceFieldColumn() + created = columns.DateTimeColumn() + scheduled = columns.DateTimeColumn() + interval = columns.DurationColumn() + started = columns.DateTimeColumn() + completed = columns.DateTimeColumn() actions = columns.ActionsColumn( actions=('delete',) ) @@ -48,11 +58,11 @@ class JobResultTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = JobResult fields = ( - 'pk', 'id', 'name', 'obj_type', 'status', 'created', 'scheduled', 'interval', 'started', 'completed', + 'pk', 'id', 'obj_type', 'name', 'status', 'created', 'scheduled', 'interval', 'started', 'completed', 'user', 'job_id', ) default_columns = ( - 'pk', 'id', 'name', 'obj_type', 'status', 'created', 'scheduled', 'interval', 'started', 'completed', + 'pk', 'id', 'obj_type', 'name', 'status', 'created', 'scheduled', 'interval', 'started', 'completed', 'user', ) diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index a912c84d5..09a35489d 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -299,7 +299,7 @@ OTHER_MENU = Menu( ), MenuItem( link='extras:jobresult_list', - link_text=_('Job Results'), + link_text=_('Jobs'), permissions=['extras.view_jobresult'], ), ), diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index 492a64abf..358fea3e5 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -28,6 +28,7 @@ __all__ = ( 'ContentTypesColumn', 'CustomFieldColumn', 'CustomLinkColumn', + 'DurationColumn', 'LinkedCountColumn', 'MarkdownColumn', 'ManyToManyColumn', @@ -77,6 +78,24 @@ class DateTimeColumn(tables.DateTimeColumn): return cls(**kwargs) +class DurationColumn(tables.Column): + """ + Express a duration of time (in minutes) in a human-friendly format. Example: 437 minutes becomes "7h 17m" + """ + def render(self, value): + ret = '' + if days := value // 1440: + ret += f'{days}d ' + if hours := value % 1440 // 60: + ret += f'{hours}h ' + if minutes := value % 60: + ret += f'{minutes}m' + return ret.strip() + + def value(self, value): + return value + + class ManyToManyColumn(tables.ManyToManyColumn): """ Overrides django-tables2's stock ManyToManyColumn to ensure that value() returns only plaintext data. diff --git a/netbox/templates/extras/htmx/report_result.html b/netbox/templates/extras/htmx/report_result.html index acc0fe9ab..ddf2c94f4 100644 --- a/netbox/templates/extras/htmx/report_result.html +++ b/netbox/templates/extras/htmx/report_result.html @@ -1,10 +1,11 @@ +{% load humanize %} {% load helpers %}

{% if result.started %} Started: {{ result.started|annotated_date }} {% elif result.scheduled %} - Scheduled for: {{ result.scheduled|annotated_date }} + Scheduled for: {{ result.scheduled|annotated_date }} ({{ result.scheduled|naturaltime }}) {% else %} Created: {{ result.created|annotated_date }} {% endif %} diff --git a/netbox/templates/extras/htmx/script_result.html b/netbox/templates/extras/htmx/script_result.html index 457548d28..ca2d278d3 100644 --- a/netbox/templates/extras/htmx/script_result.html +++ b/netbox/templates/extras/htmx/script_result.html @@ -5,7 +5,7 @@ {% if result.started %} Started: {{ result.started|annotated_date }} {% elif result.scheduled %} - Scheduled for: {{ result.scheduled|annotated_date }} + Scheduled for: {{ result.scheduled|annotated_date }} ({{ result.scheduled|naturaltime }}) {% else %} Created: {{ result.created|annotated_date }} {% endif %}