Closes #5503: ISO 8601 date in UI and alternative format as tooltip

With this commit all dates in the UI are now consistently displayed.

I changed the long date format as suggested by @xkilian and confirmed by my own
research.

* DATETIME_FORMAT
 * Before July 20, 2020 4:52 p.m.
 * Now 20th July, 2020 16:52

"20th July, 2020" would be spoken as "the 20th of July, 2020" but the "the" and
"of" are never written.

The only exception is `object_list.html`. I tried it but there it does not
work so easily because the dates are passed to Jinja as SafeString.
This commit is contained in:
Robin Schneider 2021-02-07 23:54:21 +01:00
parent 1c2800f24f
commit 42179bf97c
21 changed files with 71 additions and 25 deletions

View File

@ -431,9 +431,8 @@ class JournalEntry(ChangeLoggedModel):
verbose_name_plural = 'journal entries' verbose_name_plural = 'journal entries'
def __str__(self): def __str__(self):
created_date = timezone.localdate(self.created) created = timezone.localtime(self.created)
created_time = timezone.localtime(self.created) return f"{date_format(created, format='SHORT_DATETIME_FORMAT')} ({self.get_kind_display()})"
return f"{date_format(created_date)} - {time_format(created_time)} ({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])

View File

@ -67,7 +67,7 @@
<p class="text-muted">{{ settings.HOSTNAME }} (v{{ settings.VERSION }})</p> <p class="text-muted">{{ settings.HOSTNAME }} (v{{ settings.VERSION }})</p>
</div> </div>
<div class="col-xs-4 text-center"> <div class="col-xs-4 text-center">
<p class="text-muted">{% now 'Y-m-d H:i:s T' %}</p> <p class="text-muted">{% annotated_now %} {% now 'T' %}</p>
</div> </div>
<div class="col-xs-4 text-right noprint"> <div class="col-xs-4 text-right noprint">
<p class="text-muted"> <p class="text-muted">

View File

@ -51,7 +51,7 @@
</tr> </tr>
<tr> <tr>
<td>Install Date</td> <td>Install Date</td>
<td>{{ object.install_date|placeholder }}</td> <td>{{ object.install_date|annotated_date|placeholder }}</td>
</tr> </tr>
<tr> <tr>
<td>Commit Rate</td> <td>Commit Rate</td>

View File

@ -268,7 +268,7 @@
</td> </td>
<td> <td>
{{ resv.description }}<br /> {{ resv.description }}<br />
<small>{{ resv.user }} &middot; {{ resv.created }}</small> <small>{{ resv.user }} &middot; {{ resv.created|annotated_date }}</small>
</td> </td>
<td class="text-right noprint"> <td class="text-right noprint">
{% if perms.dcim.change_rackreservation %} {% if perms.dcim.change_rackreservation %}

View File

@ -80,7 +80,11 @@
<td> <td>
{% if object.time_zone %} {% if object.time_zone %}
{{ object.time_zone }} (UTC {{ object.time_zone|tzoffset }})<br /> {{ object.time_zone }} (UTC {{ object.time_zone|tzoffset }})<br />
<small class="text-muted">Site time: {% timezone object.time_zone %}{% now "SHORT_DATETIME_FORMAT" %}{% endtimezone %}</small> <small class="text-muted">Site time:
{% timezone object.time_zone %}
{% annotated_now %}
{% endtimezone %}
</small>
{% else %} {% else %}
<span class="text-muted">&mdash;</span> <span class="text-muted">&mdash;</span>
{% endif %} {% endif %}

View File

@ -25,7 +25,7 @@
<tr> <tr>
<td>Created</td> <td>Created</td>
<td> <td>
{{ object.created }} {{ object.created|annotated_date }}
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -44,7 +44,7 @@
<tr> <tr>
<td>Time</td> <td>Time</td>
<td> <td>
{{ object.time }} {{ object.time|annotated_date }}
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -38,7 +38,7 @@
<div class="col-md-12"> <div class="col-md-12">
{% if report.result %} {% if report.result %}
Last run: <a href="{% url 'extras:report_result' job_result_pk=report.result.pk %}"> Last run: <a href="{% url 'extras:report_result' job_result_pk=report.result.pk %}">
<strong>{{ report.result.created }}</strong> <strong>{{ report.result.created|annotated_date }}</strong>
</a> </a>
{% endif %} {% endif %}
</div> </div>

View File

@ -32,7 +32,7 @@
<td class="rendered-markdown">{{ report.description|render_markdown|placeholder }}</td> <td class="rendered-markdown">{{ report.description|render_markdown|placeholder }}</td>
<td class="text-right"> <td class="text-right">
{% if report.result %} {% if report.result %}
<a href="{% url 'extras:report_result' job_result_pk=report.result.pk %}">{{ report.result.created }}</a> <a href="{% url 'extras:report_result' job_result_pk=report.result.pk %}">{{ report.result.created|annotated_date }}</a>
{% else %} {% else %}
<span class="text-muted">Never</span> <span class="text-muted">Never</span>
{% endif %} {% endif %}

View File

@ -8,7 +8,7 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<p> <p>
Run: <strong>{{ result.created }}</strong> Run: <strong>{{ result.created|annotated_date }}</strong>
{% if result.completed %} {% if result.completed %}
Duration: <strong>{{ result.duration }}</strong> Duration: <strong>{{ result.duration }}</strong>
{% else %} {% else %}

View File

@ -29,7 +29,7 @@
<td>{{ script.Meta.description|render_markdown }}</td> <td>{{ script.Meta.description|render_markdown }}</td>
{% if script.result %} {% if script.result %}
<td class="text-right"> <td class="text-right">
<a href="{% url 'extras:script_result' job_result_pk=script.result.pk %}">{{ script.result.created }}</a> <a href="{% url 'extras:script_result' job_result_pk=script.result.pk %}">{{ script.result.created|annotated_date }}</a>
</td> </td>
{% else %} {% else %}
<td class="text-right text-muted">Never</td> <td class="text-right text-muted">Never</td>

View File

@ -13,7 +13,7 @@
<li><a href="{% url 'extras:script_list' %}">Scripts</a></li> <li><a href="{% url 'extras:script_list' %}">Scripts</a></li>
<li><a href="{% url 'extras:script_list' %}#module.{{ script.module }}">{{ script.module|bettertitle }}</a></li> <li><a href="{% url 'extras:script_list' %}#module.{{ script.module }}">{{ script.module|bettertitle }}</a></li>
<li><a href="{% url 'extras:script' module=script.module name=class_name %}">{{ script }}</a></li> <li><a href="{% url 'extras:script' module=script.module name=class_name %}">{{ script }}</a></li>
<li>{{ result.created }}</li> <li>{{ result.created|annotated_date }}</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -32,7 +32,7 @@
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<p> <p>
Run: <strong>{{ result.created }}</strong> Run: <strong>{{ result.created|annotated_date }}</strong>
{% if result.completed %} {% if result.completed %}
Duration: <strong>{{ result.duration }}</strong> Duration: <strong>{{ result.duration }}</strong>
{% else %} {% else %}

View File

@ -42,8 +42,8 @@
<h1 class="title">{% block title %}{{ object }}{% endblock %}</h1> <h1 class="title">{% block title %}{{ object }}{% endblock %}</h1>
<p> <p>
<small class="text-muted"> <small class="text-muted">
Created {{ object.created }} &middot; Created {{ object.created|annotated_date }} &middot;
Updated <span title="{{ object.last_updated }}">{{ object.last_updated|timesince }}</span> ago Updated <span title="{{ object.last_updated|date:'SHORT_DATETIME_FORMAT' }} ({{ object.last_updated|date:'DATETIME_FORMAT' }})">{{ object.last_updated|timesince }}</span> ago
</small> </small>
<span class="label label-default">{{ object|meta:"app_label" }}.{{ object|meta:"model_name" }}:{{ object.pk }}</span> <span class="label label-default">{{ object|meta:"app_label" }}.{{ object|meta:"model_name" }}:{{ object.pk }}</span>
</p> </p>

View File

@ -291,7 +291,7 @@
{% for result in report_results %} {% for result in report_results %}
<tr> <tr>
<td><a href="{% url 'extras:report_result' job_result_pk=result.pk %}">{{ result.name }}</a></td> <td><a href="{% url 'extras:report_result' job_result_pk=result.pk %}">{{ result.name }}</a></td>
<td class="text-right"><span title="{{ result.created }}">{% include 'extras/inc/job_label.html' %}</span></td> <td class="text-right"><span title="{{ result.created|date:'SHORT_DATETIME_FORMAT' }}">{% include 'extras/inc/job_label.html' %}</span></td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View File

@ -1,3 +1,4 @@
{% load helpers %}
{% with custom_fields=object.get_custom_fields %} {% with custom_fields=object.get_custom_fields %}
{% if custom_fields %} {% if custom_fields %}
<div class="panel panel-default"> <div class="panel panel-default">
@ -17,6 +18,8 @@
<a href="{{ value }}">{{ value|truncatechars:70 }}</a> <a href="{{ value }}">{{ value|truncatechars:70 }}</a>
{% elif field.type == 'multiselect' and value %} {% elif field.type == 'multiselect' and value %}
{{ value|join:", " }} {{ value|join:", " }}
{% elif field.type == 'date' and value %}
{{ value|annotated_date }}
{% elif value is not None %} {% elif value is not None %}
{{ value }} {{ value }}
{% elif field.required %} {% elif field.required %}

View File

@ -1,3 +1,4 @@
{% load helpers %}
{% if images %} {% if images %}
<table class="table table-hover panel-body"> <table class="table table-hover panel-body">
<tr> <tr>
@ -13,7 +14,7 @@
<a class="image-preview" href="{{ attachment.image.url }}" target="_blank">{{ attachment }}</a> <a class="image-preview" href="{{ attachment.image.url }}" target="_blank">{{ attachment }}</a>
</td> </td>
<td>{{ attachment.size|filesizeformat }}</td> <td>{{ attachment.size|filesizeformat }}</td>
<td>{{ attachment.created }}</td> <td>{{ attachment.created|annotated_date }}</td>
<td class="text-right noprint"> <td class="text-right noprint">
{% if perms.extras.change_imageattachment %} {% if perms.extras.change_imageattachment %}
<a href="{% url 'extras:imageattachment_edit' pk=attachment.pk %}" class="btn btn-warning btn-xs" title="Edit image"> <a href="{% url 'extras:imageattachment_edit' pk=attachment.pk %}" class="btn btn-warning btn-xs" title="Edit image">

View File

@ -54,7 +54,7 @@
</tr> </tr>
<tr> <tr>
<td>Date Added</td> <td>Date Added</td>
<td>{{ object.date_added|placeholder }}</td> <td>{{ object.date_added|annotated_date|placeholder }}</td>
</tr> </tr>
<tr> <tr>
<td>Description</td> <td>Description</td>

View File

@ -24,12 +24,12 @@
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<small class="text-muted">Created</small><br /> <small class="text-muted">Created</small><br />
<span title="{{ token.created }}">{{ token.created|date }}</span> {{ token.created|annotated_date }}
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<small class="text-muted">Expires</small><br /> <small class="text-muted">Expires</small><br />
{% if token.expires %} {% if token.expires %}
<span title="{{ token.expires }}">{{ token.expires|date }}</span> {{ token.expires|annotated_date }}
{% else %} {% else %}
<span>Never</span> <span>Never</span>
{% endif %} {% endif %}

View File

@ -11,7 +11,7 @@
<small class="text-muted">Email</small> <small class="text-muted">Email</small>
<h5>{{ request.user.email }}</h5> <h5>{{ request.user.email }}</h5>
<small class="text-muted">Registered</small> <small class="text-muted">Registered</small>
<h5>{{ request.user.date_joined }}</h5> <h5>{{ request.user.date_joined|annotated_date }}</h5>
<small class="text-muted">Groups</small> <small class="text-muted">Groups</small>
<h5>{{ request.user.groups.all|join:', ' }}</h5> <h5>{{ request.user.groups.all|join:', ' }}</h5>
<small class="text-muted">Admin access</small> <small class="text-muted">Admin access</small>

View File

@ -1,4 +1,5 @@
{% extends 'users/base.html' %} {% extends 'users/base.html' %}
{% load helpers %}
{% block title %}User Key{% endblock %} {% block title %}User Key{% endblock %}
@ -19,7 +20,9 @@
{% endif %} {% endif %}
</h4> </h4>
<p> <p>
<small class="text-muted">Created {{ object.created }} &middot; Updated <span title="{{ object.last_updated }}">{{ object.last_updated|timesince }}</span> ago</small> <small class="text-muted">
Created {{ object.created|annotated_date }} &middot;
Updated <span title="{{ object.last_updated|date:'SHORT_DATETIME_FORMAT' }} ({{ object.last_updated|date:'DATETIME_FORMAT' }})">{{ object.last_updated|timesince }}</span> ago
</p> </p>
{% if not object.is_active %} {% if not object.is_active %}
<div class="alert alert-warning" role="alert"> <div class="alert alert-warning" role="alert">
@ -37,7 +40,7 @@
</a> </a>
</div> </div>
<h4>Session key: <span class="label label-success">Active</span></h4> <h4>Session key: <span class="label label-success">Active</span></h4>
<small class="text-muted">Created {{ object.session_key.created }}</small> <small class="text-muted">Created {{ object.session_key.created|annotated_date }}</small>
{% else %} {% else %}
<h4>No active session key</h4> <h4>No active session key</h4>
{% endif %} {% endif %}

View File

@ -4,8 +4,10 @@ import re
import yaml import yaml
from django import template from django import template
from django.template.defaultfilters import date
from django.conf import settings from django.conf import settings
from django.urls import NoReverseMatch, reverse from django.urls import NoReverseMatch, reverse
from django.utils import timezone
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from markdown import markdown from markdown import markdown
@ -151,6 +153,40 @@ def tzoffset(value):
return datetime.datetime.now(value).strftime('%z') return datetime.datetime.now(value).strftime('%z')
@register.filter(expects_localtime=True)
def annotated_date(date_value):
"""
Returns date as HTML span with short date format as the content and the
(long) date format as the title.
"""
if not date_value:
return ''
# ../../templates/inc/custom_fields_panel.html passes a string.
if type(date_value) == str:
date_value = datetime.datetime.strptime(date_value, "%Y-%m-%d").date()
if type(date_value) == datetime.date:
long_ts = date(date_value, 'DATE_FORMAT')
short_ts = date(date_value, 'SHORT_DATE_FORMAT')
else:
long_ts = date(date_value, 'DATETIME_FORMAT')
short_ts = date(date_value, 'SHORT_DATETIME_FORMAT')
span = f'<span title="{long_ts}">{short_ts}</span>'
return mark_safe(span)
@register.simple_tag
def annotated_now():
"""
Returns the current date piped through the annotated_date filter.
"""
tzinfo = timezone.get_current_timezone() if settings.USE_TZ else None
return annotated_date(datetime.datetime.now(tz=tzinfo))
@register.filter() @register.filter()
def fgcolor(value): def fgcolor(value):
""" """