* Introduce the isodate(), isotime(), and isodatetime() template filters * Display the relative time on mouse hover * Render journal entry times in ISO 8601 format * Use ISO 8601 format when displaying dates & times in a table * Standardize the use of DateTimeColumn across all tables
This commit is contained in:
parent
f0aca5bac1
commit
77a4300888
|
@ -30,10 +30,12 @@ class UserTokenTable(NetBoxTable):
|
||||||
write_enabled = columns.BooleanColumn(
|
write_enabled = columns.BooleanColumn(
|
||||||
verbose_name=_('Write Enabled')
|
verbose_name=_('Write Enabled')
|
||||||
)
|
)
|
||||||
created = columns.DateColumn(
|
created = columns.DateTimeColumn(
|
||||||
|
timespec='minutes',
|
||||||
verbose_name=_('Created'),
|
verbose_name=_('Created'),
|
||||||
)
|
)
|
||||||
expires = columns.DateColumn(
|
expires = columns.DateTimeColumn(
|
||||||
|
timespec='minutes',
|
||||||
verbose_name=_('Expires'),
|
verbose_name=_('Expires'),
|
||||||
)
|
)
|
||||||
last_used = columns.DateTimeColumn(
|
last_used = columns.DateTimeColumn(
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django_tables2.utils import A
|
from django_tables2.utils import A
|
||||||
|
|
||||||
from core.tables.columns import RQJobStatusColumn
|
from core.tables.columns import RQJobStatusColumn
|
||||||
from netbox.tables import BaseTable
|
from netbox.tables import BaseTable, columns
|
||||||
|
|
||||||
|
|
||||||
class BackgroundQueueTable(BaseTable):
|
class BackgroundQueueTable(BaseTable):
|
||||||
|
@ -75,13 +75,13 @@ class BackgroundTaskTable(BaseTable):
|
||||||
linkify=("core:background_task", [A("id")]),
|
linkify=("core:background_task", [A("id")]),
|
||||||
verbose_name=_("ID")
|
verbose_name=_("ID")
|
||||||
)
|
)
|
||||||
created_at = tables.DateTimeColumn(
|
created_at = columns.DateTimeColumn(
|
||||||
verbose_name=_("Created")
|
verbose_name=_("Created")
|
||||||
)
|
)
|
||||||
enqueued_at = tables.DateTimeColumn(
|
enqueued_at = columns.DateTimeColumn(
|
||||||
verbose_name=_("Enqueued")
|
verbose_name=_("Enqueued")
|
||||||
)
|
)
|
||||||
ended_at = tables.DateTimeColumn(
|
ended_at = columns.DateTimeColumn(
|
||||||
verbose_name=_("Ended")
|
verbose_name=_("Ended")
|
||||||
)
|
)
|
||||||
status = RQJobStatusColumn(
|
status = RQJobStatusColumn(
|
||||||
|
@ -117,7 +117,7 @@ class WorkerTable(BaseTable):
|
||||||
state = tables.Column(
|
state = tables.Column(
|
||||||
verbose_name=_("State")
|
verbose_name=_("State")
|
||||||
)
|
)
|
||||||
birth_date = tables.DateTimeColumn(
|
birth_date = columns.DateTimeColumn(
|
||||||
verbose_name=_("Birth")
|
verbose_name=_("Birth")
|
||||||
)
|
)
|
||||||
pid = tables.Column(
|
pid = tables.Column(
|
||||||
|
|
|
@ -732,7 +732,7 @@ class JournalEntry(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ExportTemplat
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
created = timezone.localtime(self.created)
|
created = timezone.localtime(self.created)
|
||||||
return f"{date_format(created, format='SHORT_DATETIME_FORMAT')} ({self.get_kind_display()})"
|
return f"{created.date().isoformat()} {created.time().isoformat(timespec='minutes')} ({self.get_kind_display()})"
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('extras:journalentry', args=[self.pk])
|
return reverse('extras:journalentry', args=[self.pk])
|
||||||
|
|
|
@ -432,10 +432,10 @@ class ConfigTemplateTable(NetBoxTable):
|
||||||
|
|
||||||
|
|
||||||
class ObjectChangeTable(NetBoxTable):
|
class ObjectChangeTable(NetBoxTable):
|
||||||
time = tables.DateTimeColumn(
|
time = columns.DateTimeColumn(
|
||||||
verbose_name=_('Time'),
|
verbose_name=_('Time'),
|
||||||
linkify=True,
|
timespec='minutes',
|
||||||
format=settings.SHORT_DATETIME_FORMAT
|
linkify=True
|
||||||
)
|
)
|
||||||
user_name = tables.Column(
|
user_name = tables.Column(
|
||||||
verbose_name=_('Username')
|
verbose_name=_('Username')
|
||||||
|
@ -475,10 +475,10 @@ class ObjectChangeTable(NetBoxTable):
|
||||||
|
|
||||||
|
|
||||||
class JournalEntryTable(NetBoxTable):
|
class JournalEntryTable(NetBoxTable):
|
||||||
created = tables.DateTimeColumn(
|
created = columns.DateTimeColumn(
|
||||||
verbose_name=_('Created'),
|
verbose_name=_('Created'),
|
||||||
linkify=True,
|
timespec='minutes',
|
||||||
format=settings.SHORT_DATETIME_FORMAT
|
linkify=True
|
||||||
)
|
)
|
||||||
assigned_object_type = columns.ContentTypeColumn(
|
assigned_object_type = columns.ContentTypeColumn(
|
||||||
verbose_name=_('Object Type')
|
verbose_name=_('Object Type')
|
||||||
|
|
|
@ -10,7 +10,6 @@ from django.db.models import DateField, DateTimeField
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.dateparse import parse_date
|
from django.utils.dateparse import parse_date
|
||||||
from django.utils.formats import date_format
|
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
@ -52,18 +51,17 @@ __all__ = (
|
||||||
#
|
#
|
||||||
|
|
||||||
@library.register
|
@library.register
|
||||||
class DateColumn(tables.DateColumn):
|
class DateColumn(tables.Column):
|
||||||
"""
|
"""
|
||||||
Overrides the default implementation of DateColumn to better handle null values, returning a default value for
|
Render a datetime.date in ISO 8601 format.
|
||||||
tables and null when exporting data. It is registered in the tables library to use this class instead of the
|
|
||||||
default, making this behavior consistent in all fields of type DateField.
|
|
||||||
"""
|
"""
|
||||||
def render(self, value):
|
def render(self, value):
|
||||||
if value:
|
if value:
|
||||||
return date_format(value, format="SHORT_DATE_FORMAT")
|
return value.isoformat()
|
||||||
|
|
||||||
def value(self, value):
|
def value(self, value):
|
||||||
return value
|
if value:
|
||||||
|
return value.isoformat()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_field(cls, field, **kwargs):
|
def from_field(cls, field, **kwargs):
|
||||||
|
@ -72,16 +70,24 @@ class DateColumn(tables.DateColumn):
|
||||||
|
|
||||||
|
|
||||||
@library.register
|
@library.register
|
||||||
class DateTimeColumn(tables.DateTimeColumn):
|
class DateTimeColumn(tables.Column):
|
||||||
"""
|
"""
|
||||||
Overrides the default implementation of DateTimeColumn to better handle null values, returning a default value for
|
Render a datetime.datetime in ISO 8601 format.
|
||||||
tables and null when exporting data. It is registered in the tables library to use this class instead of the
|
|
||||||
default, making this behavior consistent in all fields of type DateTimeField.
|
Args:
|
||||||
|
timespec: Granularity specification; passed through to datetime.isoformat()
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, *args, timespec='seconds', **kwargs):
|
||||||
|
self.timespec = timespec
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def render(self, value):
|
||||||
|
if value:
|
||||||
|
return f"{value.date().isoformat()} {value.time().isoformat(timespec=self.timespec)}"
|
||||||
|
|
||||||
def value(self, value):
|
def value(self, value):
|
||||||
if value:
|
if value:
|
||||||
return date_format(value, format="SHORT_DATETIME_FORMAT")
|
return value.isoformat()
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_field(cls, field, **kwargs):
|
def from_field(cls, field, **kwargs):
|
||||||
|
@ -498,7 +504,7 @@ class CustomFieldColumn(tables.Column):
|
||||||
if self.customfield.type == CustomFieldTypeChoices.TYPE_LONGTEXT and value:
|
if self.customfield.type == CustomFieldTypeChoices.TYPE_LONGTEXT and value:
|
||||||
return render_markdown(value)
|
return render_markdown(value)
|
||||||
if self.customfield.type == CustomFieldTypeChoices.TYPE_DATE and value:
|
if self.customfield.type == CustomFieldTypeChoices.TYPE_DATE and value:
|
||||||
return date_format(parse_date(value), format="SHORT_DATE_FORMAT")
|
return parse_date(value).isoformat()
|
||||||
if value is not None:
|
if value is not None:
|
||||||
obj = self.customfield.deserialize(value)
|
obj = self.customfield.deserialize(value)
|
||||||
return mark_safe(self._linkify_item(obj))
|
return mark_safe(self._linkify_item(obj))
|
||||||
|
|
|
@ -31,11 +31,11 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Account Created" %}</th>
|
<th scope="row">{% trans "Account Created" %}</th>
|
||||||
<td>{{ request.user.date_joined|annotated_date }}</td>
|
<td>{{ request.user.date_joined|isodate }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Last Login" %}</th>
|
<th scope="row">{% trans "Last Login" %}</th>
|
||||||
<td>{{ request.user.last_login|annotated_date }}</td>
|
<td>{{ request.user.last_login|isodatetime:"minutes"|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Superuser" %}</th>
|
<th scope="row">{% trans "Superuser" %}</th>
|
||||||
|
|
|
@ -41,15 +41,15 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Created" %}</th>
|
<th scope="row">{% trans "Created" %}</th>
|
||||||
<td>{{ object.created|annotated_date }}</td>
|
<td>{{ object.created|isodatetime }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Expires" %}</th>
|
<th scope="row">{% trans "Expires" %}</th>
|
||||||
<td>{{ object.expires|placeholder }}</td>
|
<td>{{ object.expires|isodatetime|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Last used" %}</th>
|
<th scope="row">{% trans "Last used" %}</th>
|
||||||
<td>{{ object.last_used|placeholder }}</td>
|
<td>{{ object.last_used|isodatetime|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Allowed IPs" %}</th>
|
<th scope="row">{% trans "Allowed IPs" %}</th>
|
||||||
|
|
|
@ -45,11 +45,11 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Install Date" %}</th>
|
<th scope="row">{% trans "Install Date" %}</th>
|
||||||
<td>{{ object.install_date|annotated_date|placeholder }}</td>
|
<td>{{ object.install_date|isodate|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Termination Date" %}</th>
|
<th scope="row">{% trans "Termination Date" %}</th>
|
||||||
<td>{{ object.termination_date|annotated_date|placeholder }}</td>
|
<td>{{ object.termination_date|isodate|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Commit Rate" %}</th>
|
<th scope="row">{% trans "Commit Rate" %}</th>
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
{% block subtitle %}
|
{% block subtitle %}
|
||||||
{% if object.created %}
|
{% if object.created %}
|
||||||
<div class="text-secondary fs-5">
|
<div class="text-secondary fs-5">
|
||||||
{% trans "Created" %} {{ object.created|annotated_date }}
|
{% trans "Created" %} {{ object.created|isodatetime }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock subtitle %}
|
{% endblock subtitle %}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block subtitle %}
|
||||||
<div class="text-secondary fs-5">
|
<div class="text-secondary fs-5">
|
||||||
{% trans "Created" %} {{ object.created|annotated_date }}
|
{% trans "Created" %} {{ object.created|isodatetime }}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -49,12 +49,12 @@
|
||||||
<table class="table table-hover attr-table">
|
<table class="table table-hover attr-table">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Created" %}</th>
|
<th scope="row">{% trans "Created" %}</th>
|
||||||
<td>{{ object.created|annotated_date }}</td>
|
<td>{{ object.created|isodatetime }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Scheduled" %}</th>
|
<th scope="row">{% trans "Scheduled" %}</th>
|
||||||
<td>
|
<td>
|
||||||
{{ object.scheduled|annotated_date|placeholder }}
|
{{ object.scheduled|isodatetime|placeholder }}
|
||||||
{% if object.interval %}
|
{% if object.interval %}
|
||||||
({% blocktrans with interval=object.interval %}every {{ interval }} minutes{% endblocktrans %})
|
({% blocktrans with interval=object.interval %}every {{ interval }} minutes{% endblocktrans %})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -62,11 +62,11 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Started" %}</th>
|
<th scope="row">{% trans "Started" %}</th>
|
||||||
<td>{{ object.started|annotated_date|placeholder }}</td>
|
<td>{{ object.started|isodatetime|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Completed" %}</th>
|
<th scope="row">{% trans "Completed" %}</th>
|
||||||
<td>{{ object.completed|annotated_date|placeholder }}</td>
|
<td>{{ object.completed|isodatetime|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block subtitle %}
|
||||||
<div class="text-secondary fs-5">
|
<div class="text-secondary fs-5">
|
||||||
<span>{% trans "Created" %} {{ job.created_at|annotated_date }}</span>
|
<span>{% trans "Created" %} {{ job.created_at|isodatetime }}</span>
|
||||||
</div>
|
</div>
|
||||||
{% endblock subtitle %}
|
{% endblock subtitle %}
|
||||||
|
|
||||||
|
@ -71,11 +71,11 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Created" %}</th>
|
<th scope="row">{% trans "Created" %}</th>
|
||||||
<td>{{ job.created_at|annotated_date }}</td>
|
<td>{{ job.created_at|isodatetime }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Queued" %}</th>
|
<th scope="row">{% trans "Queued" %}</th>
|
||||||
<td>{{ job.enqueued_at|annotated_date }}</td>
|
<td>{{ job.enqueued_at|isodatetime }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Status" %}</th>
|
<th scope="row">{% trans "Status" %}</th>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block subtitle %}
|
||||||
<div class="text-secondary fs-5">
|
<div class="text-secondary fs-5">
|
||||||
<span>{% trans "Created" %} {{ worker.birth_date|annotated_date }}</span>
|
<span>{% trans "Created" %} {{ worker.birth_date|isodatetime }}</span>
|
||||||
</div>
|
</div>
|
||||||
{% endblock subtitle %}
|
{% endblock subtitle %}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Birth" %}</th>
|
<th scope="row">{% trans "Birth" %}</th>
|
||||||
<td>{{ worker.birth_date|annotated_date }}</td>
|
<td>{{ worker.birth_date|isodatetime }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Queues" %}</th>
|
<th scope="row">{% trans "Queues" %}</th>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
{% load humanize %}
|
|
||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
{% load log_levels %}
|
{% load log_levels %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
@ -6,11 +5,11 @@
|
||||||
<div class="htmx-container">
|
<div class="htmx-container">
|
||||||
<p>
|
<p>
|
||||||
{% if job.started %}
|
{% if job.started %}
|
||||||
{% trans "Started" %}: <strong>{{ job.started|annotated_date }}</strong>
|
{% trans "Started" %}: <strong>{{ job.started|isodatetime }}</strong>
|
||||||
{% elif job.scheduled %}
|
{% elif job.scheduled %}
|
||||||
{% trans "Scheduled for" %}: <strong>{{ job.scheduled|annotated_date }}</strong> ({{ job.scheduled|naturaltime }})
|
{% trans "Scheduled for" %}: <strong>{{ job.scheduled|isodatetime }}</strong>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans "Created" %}: <strong>{{ job.created|annotated_date }}</strong>
|
{% trans "Created" %}: <strong>{{ job.created|isodatetime }}</strong>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if job.completed %}
|
{% if job.completed %}
|
||||||
{% trans "Duration" %}: <strong>{{ job.duration }}</strong>
|
{% trans "Duration" %}: <strong>{{ job.duration }}</strong>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Created" %}</th>
|
<th scope="row">{% trans "Created" %}</th>
|
||||||
<td>{{ object.created|annotated_date }}</td>
|
<td>{{ object.created|isodatetime:"minutes" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Created By" %}</th>
|
<th scope="row">{% trans "Created By" %}</th>
|
||||||
|
|
|
@ -29,9 +29,7 @@
|
||||||
<table class="table table-hover attr-table">
|
<table class="table table-hover attr-table">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Time" %}</th>
|
<th scope="row">{% trans "Time" %}</th>
|
||||||
<td>
|
<td>{{ object.time|isodatetime }}</td>
|
||||||
{{ object.time|annotated_date }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "User" %}</th>
|
<th scope="row">{% trans "User" %}</th>
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
<td>{{ script.description|markdown|placeholder }}</td>
|
<td>{{ script.description|markdown|placeholder }}</td>
|
||||||
{% if last_job %}
|
{% if last_job %}
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url 'extras:script_result' job_pk=last_job.pk %}">{{ last_job.created|annotated_date }}</a>
|
<a href="{% url 'extras:script_result' job_pk=last_job.pk %}">{{ last_job.created|isodatetime }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% badge last_job.get_status_display last_job.get_status_color %}
|
{% badge last_job.get_status_display last_job.get_status_color %}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<li class="breadcrumb-item"><a href="{% url 'extras:script_list' %}">{% trans "Scripts" %}</a></li>
|
<li class="breadcrumb-item"><a href="{% url 'extras:script_list' %}">{% trans "Scripts" %}</a></li>
|
||||||
<li class="breadcrumb-item"><a href="{% url 'extras:script_list' %}#module.{{ script.module }}">{{ script.module|bettertitle }}</a></li>
|
<li class="breadcrumb-item"><a href="{% url 'extras:script_list' %}#module.{{ script.module }}">{{ script.module|bettertitle }}</a></li>
|
||||||
<li class="breadcrumb-item"><a href="{{ script.get_absolute_url }}">{{ script }}</a></li>
|
<li class="breadcrumb-item"><a href="{{ script.get_absolute_url }}">{{ script }}</a></li>
|
||||||
<li class="breadcrumb-item">{{ job.created|annotated_date }}</li>
|
<li class="breadcrumb-item">{{ job.created|isodatetime }}</li>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -48,10 +48,10 @@ Context:
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block subtitle %}
|
||||||
<div class="text-secondary fs-5">
|
<div class="text-secondary fs-5">
|
||||||
<span>{% trans "Created" %} {{ object.created|annotated_date }}</span>
|
{% trans "Created" %} {{ object.created|isodatetime:"minutes" }}
|
||||||
{% if object.last_updated %}
|
{% if object.last_updated %}
|
||||||
<span class="separator">·</span>
|
<span class="separator">·</span>
|
||||||
<span>{% trans "Updated" %} <span title="{{ object.last_updated }}">{{ object.last_updated|timesince }}</span> {% trans "ago" %}</span>
|
{% trans "Updated" %} {{ object.last_updated|isodatetime:"minutes" }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock subtitle %}
|
{% endblock subtitle %}
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Date Added" %}</th>
|
<th scope="row">{% trans "Date Added" %}</th>
|
||||||
<td>{{ object.date_added|annotated_date|placeholder }}</td>
|
<td>{{ object.date_added|isodate|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Description" %}</th>
|
<th scope="row">{% trans "Description" %}</th>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends 'generic/object.html' %}
|
{% extends 'generic/object.html' %}
|
||||||
{% load i18n %}
|
|
||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
{% load i18n %}
|
||||||
{% load render_table from django_tables2 %}
|
{% load render_table from django_tables2 %}
|
||||||
|
|
||||||
{% block title %}{% trans "Token" %} {{ object }}{% endblock %}
|
{% block title %}{% trans "Token" %} {{ object }}{% endblock %}
|
||||||
|
@ -33,15 +33,15 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Created" %}</th>
|
<th scope="row">{% trans "Created" %}</th>
|
||||||
<td>{{ object.created|annotated_date }}</td>
|
<td>{{ object.created|isodatetime }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Expires" %}</th>
|
<th scope="row">{% trans "Expires" %}</th>
|
||||||
<td>{{ object.expires|placeholder }}</td>
|
<td>{{ object.expires|isodatetime|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Last used" %}</th>
|
<th scope="row">{% trans "Last used" %}</th>
|
||||||
<td>{{ object.last_used|placeholder }}</td>
|
<td>{{ object.last_used|isodatetime|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Allowed IPs" %}</th>
|
<th scope="row">{% trans "Allowed IPs" %}</th>
|
||||||
|
|
|
@ -27,11 +27,11 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Account Created" %}</th>
|
<th scope="row">{% trans "Account Created" %}</th>
|
||||||
<td>{{ object.date_joined|annotated_date }}</td>
|
<td>{{ object.date_joined|isodate }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Last Login" %}</th>
|
<th scope="row">{% trans "Last Login" %}</th>
|
||||||
<td>{{ object.last_login|annotated_date }}</td>
|
<td>{{ object.last_login|isodatetime:"minutes"|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Active" %}</th>
|
<th scope="row">{% trans "Active" %}</th>
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
{% elif customfield.type == 'boolean' and value == False %}
|
{% elif customfield.type == 'boolean' and value == False %}
|
||||||
{% checkmark value false="False" %}
|
{% checkmark value false="False" %}
|
||||||
{% elif customfield.type == 'date' and value %}
|
{% elif customfield.type == 'date' and value %}
|
||||||
{{ value|annotated_date }}
|
{{ value|isodate }}
|
||||||
{% elif customfield.type == 'datetime' and value %}
|
{% elif customfield.type == 'datetime' and value %}
|
||||||
{{ value|annotated_date }}
|
{{ value|isodate }} {{ value|isodatetime }}
|
||||||
{% elif customfield.type == 'url' and value %}
|
{% elif customfield.type == 'url' and value %}
|
||||||
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
||||||
{% elif customfield.type == 'json' and value %}
|
{% elif customfield.type == 'json' and value %}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import re
|
||||||
import yaml
|
import yaml
|
||||||
from django import template
|
from django import template
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
|
@ -20,6 +21,9 @@ __all__ = (
|
||||||
'content_type',
|
'content_type',
|
||||||
'content_type_id',
|
'content_type_id',
|
||||||
'fgcolor',
|
'fgcolor',
|
||||||
|
'isodate',
|
||||||
|
'isodatetime',
|
||||||
|
'isotime',
|
||||||
'linkify',
|
'linkify',
|
||||||
'meta',
|
'meta',
|
||||||
'placeholder',
|
'placeholder',
|
||||||
|
@ -202,3 +206,36 @@ def render_yaml(value):
|
||||||
{{ data_dict|yaml }}
|
{{ data_dict|yaml }}
|
||||||
"""
|
"""
|
||||||
return yaml.dump(json.loads(json.dumps(value)))
|
return yaml.dump(json.loads(json.dumps(value)))
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Time & date
|
||||||
|
#
|
||||||
|
|
||||||
|
@register.filter()
|
||||||
|
def isodate(value):
|
||||||
|
if type(value) is datetime.date:
|
||||||
|
text = value.isoformat()
|
||||||
|
elif type(value) is datetime.datetime:
|
||||||
|
text = value.date().isoformat()
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
return mark_safe(f'<span title="{naturaltime(value)}">{text}</span>')
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter()
|
||||||
|
def isotime(value, spec='seconds'):
|
||||||
|
if type(value) is datetime.time:
|
||||||
|
return value.isoformat(timespec=spec)
|
||||||
|
if type(value) is datetime.datetime:
|
||||||
|
return value.time().isoformat(timespec=spec)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter()
|
||||||
|
def isodatetime(value, spec='seconds'):
|
||||||
|
if type(value) is datetime.datetime:
|
||||||
|
text = f'{isodate(value)} {isotime(value, spec=spec)}'
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
return mark_safe(f'<span title="{naturaltime(value)}">{text}</span>')
|
||||||
|
|
Loading…
Reference in New Issue