mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-11-25 09:23:39 +01:00
CustomField datetime type formats updated to fixed string formats enabling reformatting and consistency
This commit is contained in:
parent
ae9109bad6
commit
90961897ec
@ -7,6 +7,7 @@ forms.py - Definitions of newforms-based forms for creating and maintaining
|
|||||||
tickets.
|
tickets.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import datetime, date, time
|
||||||
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from django import forms
|
from django import forms
|
||||||
@ -35,6 +36,10 @@ CUSTOMFIELD_TO_FIELD_DICT = {
|
|||||||
'slug': forms.SlugField,
|
'slug': forms.SlugField,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CUSTOMFIELD_DATE_FORMAT = "%Y-%m-%d"
|
||||||
|
CUSTOMFIELD_TIME_FORMAT = "%H:%M:%S"
|
||||||
|
CUSTOMFIELD_DATETIME_FORMAT = f"{CUSTOMFIELD_DATE_FORMAT} {CUSTOMFIELD_TIME_FORMAT}"
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldMixin(object):
|
class CustomFieldMixin(object):
|
||||||
"""
|
"""
|
||||||
@ -71,8 +76,14 @@ class CustomFieldMixin(object):
|
|||||||
# Try to use the immediate equivalences dictionary
|
# Try to use the immediate equivalences dictionary
|
||||||
try:
|
try:
|
||||||
fieldclass = CUSTOMFIELD_TO_FIELD_DICT[field.data_type]
|
fieldclass = CUSTOMFIELD_TO_FIELD_DICT[field.data_type]
|
||||||
# Change widget in case it is a boolean
|
# Change widgets for the following classes
|
||||||
if fieldclass == forms.BooleanField:
|
if fieldclass == forms.DateField:
|
||||||
|
instanceargs['widget'] = forms.DateInput(attrs={'class': 'form-control date-field'})
|
||||||
|
elif fieldclass == forms.DateTimeField:
|
||||||
|
instanceargs['widget'] = forms.DateTimeInput(attrs={'class': 'form-control datetime-field'})
|
||||||
|
elif fieldclass == forms.TimeField:
|
||||||
|
instanceargs['widget'] = forms.TimeInput(attrs={'class': 'form-control time-field'})
|
||||||
|
elif fieldclass == forms.BooleanField:
|
||||||
instanceargs['widget'] = forms.CheckboxInput(attrs={'class': 'form-control'})
|
instanceargs['widget'] = forms.CheckboxInput(attrs={'class': 'form-control'})
|
||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -88,6 +99,9 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
|
|||||||
model = Ticket
|
model = Ticket
|
||||||
exclude = ('created', 'modified', 'status', 'on_hold', 'resolution', 'last_escalation', 'assigned_to')
|
exclude = ('created', 'modified', 'status', 'on_hold', 'resolution', 'last_escalation', 'assigned_to')
|
||||||
|
|
||||||
|
class Media:
|
||||||
|
js = ('helpdesk/js/init_due_date.js', 'helpdesk/js/init_datetime_classes.js')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Add any custom fields that are defined to the form
|
Add any custom fields that are defined to the form
|
||||||
@ -99,14 +113,24 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
|
|||||||
self.fields['merged_to'].help_text = _('This ticket is merged into the selected ticket.')
|
self.fields['merged_to'].help_text = _('This ticket is merged into the selected ticket.')
|
||||||
|
|
||||||
for field in CustomField.objects.all():
|
for field in CustomField.objects.all():
|
||||||
|
initial_value = None
|
||||||
try:
|
try:
|
||||||
current_value = TicketCustomFieldValue.objects.get(ticket=self.instance, field=field)
|
current_value = TicketCustomFieldValue.objects.get(ticket=self.instance, field=field)
|
||||||
initial_value = current_value.value
|
initial_value = current_value.value
|
||||||
|
# Attempt to convert from fixed format string to date/time data type
|
||||||
|
if 'datetime' == current_value.field.data_type:
|
||||||
|
initial_value = datetime.strptime(initial_value, CUSTOMFIELD_DATETIME_FORMAT)
|
||||||
|
elif 'date' == current_value.field.data_type:
|
||||||
|
initial_value = datetime.strptime(initial_value, CUSTOMFIELD_DATE_FORMAT)
|
||||||
|
elif 'time' == current_value.field.data_type:
|
||||||
|
initial_value = datetime.strptime(initial_value, CUSTOMFIELD_TIME_FORMAT)
|
||||||
# If it is boolean field, transform the value to a real boolean instead of a string
|
# If it is boolean field, transform the value to a real boolean instead of a string
|
||||||
if current_value.field.data_type == 'boolean':
|
elif 'boolean' == current_value.field.data_type:
|
||||||
initial_value = initial_value == 'True'
|
initial_value = 'True' == initial_value
|
||||||
except TicketCustomFieldValue.DoesNotExist:
|
except (TicketCustomFieldValue.DoesNotExist, ValueError, TypeError):
|
||||||
initial_value = None
|
# ValueError error if parsing fails, using initial_value = current_value.value
|
||||||
|
# TypeError if parsing None type
|
||||||
|
pass
|
||||||
instanceargs = {
|
instanceargs = {
|
||||||
'label': field.label,
|
'label': field.label,
|
||||||
'help_text': field.help_text,
|
'help_text': field.help_text,
|
||||||
@ -126,6 +150,15 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
|
|||||||
cfv = TicketCustomFieldValue.objects.get(ticket=self.instance, field=customfield)
|
cfv = TicketCustomFieldValue.objects.get(ticket=self.instance, field=customfield)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
cfv = TicketCustomFieldValue(ticket=self.instance, field=customfield)
|
cfv = TicketCustomFieldValue(ticket=self.instance, field=customfield)
|
||||||
|
|
||||||
|
# Convert date/time data type to known fixed format string.
|
||||||
|
if datetime is type(value):
|
||||||
|
cfv.value = value.strftime(CUSTOMFIELD_DATETIME_FORMAT)
|
||||||
|
elif date is type(value):
|
||||||
|
cfv.value = value.strftime(CUSTOMFIELD_DATE_FORMAT)
|
||||||
|
elif time is type(value):
|
||||||
|
cfv.value = value.strftime(CUSTOMFIELD_TIME_FORMAT)
|
||||||
|
else:
|
||||||
cfv.value = value
|
cfv.value = value
|
||||||
cfv.save()
|
cfv.save()
|
||||||
|
|
||||||
@ -182,7 +215,7 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form):
|
|||||||
due_date = forms.DateTimeField(
|
due_date = forms.DateTimeField(
|
||||||
widget=forms.TextInput(attrs={'class': 'form-control', 'autocomplete': 'off'}),
|
widget=forms.TextInput(attrs={'class': 'form-control', 'autocomplete': 'off'}),
|
||||||
required=False,
|
required=False,
|
||||||
input_formats=['%d/%m/%Y', '%m/%d/%Y', "%d.%m.%Y"],
|
input_formats=[CUSTOMFIELD_DATE_FORMAT, CUSTOMFIELD_DATETIME_FORMAT, '%d/%m/%Y', '%m/%d/%Y', "%d.%m.%Y"],
|
||||||
label=_('Due on'),
|
label=_('Due on'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -194,7 +227,7 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form):
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
js = ('helpdesk/js/init_due_date.js',)
|
js = ('helpdesk/js/init_due_date.js', 'helpdesk/js/init_datetime_classes.js')
|
||||||
|
|
||||||
def __init__(self, kbcategory=None, *args, **kwargs):
|
def __init__(self, kbcategory=None, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
10
helpdesk/static/helpdesk/js/init_datetime_classes.js
Normal file
10
helpdesk/static/helpdesk/js/init_datetime_classes.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
$(() => {
|
||||||
|
$(".date-field").datepicker({dateFormat: 'yy-mm-dd'});
|
||||||
|
});
|
||||||
|
$(() => {
|
||||||
|
$(".datetime-field").datepicker({dateFormat: 'yy-mm-dd 00:00:00'});
|
||||||
|
});
|
||||||
|
$(() => {
|
||||||
|
// TODO: This does not work as written, need to make functional
|
||||||
|
$(".time-field").tooltip="Time format 24hr: 00:00:00";
|
||||||
|
});
|
@ -1,3 +1,3 @@
|
|||||||
$(() => {
|
$(() => {
|
||||||
$("#id_due_date").datepicker();
|
$("#id_due_date").datepicker({dateFormat: 'yy-mm-dd 00:00:00'});
|
||||||
});
|
});
|
@ -26,7 +26,7 @@
|
|||||||
<strong>{% trans "Note" %}:</strong>
|
<strong>{% trans "Note" %}:</strong>
|
||||||
{% blocktrans %}Editing a ticket does <em>not</em> send an e-mail to the ticket owner or submitter. No new details should be entered, this form should only be used to fix incorrect details or clean up the submission.{% endblocktrans %}
|
{% blocktrans %}Editing a ticket does <em>not</em> send an e-mail to the ticket owner or submitter. No new details should be entered, this form should only be used to fix incorrect details or clean up the submission.{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
{% if errors %}{% for error in errors %}{% trans "Error: " %}{{ error }}{% endfor %}{% endif %}
|
{% if errors %}<p class="text-danger">{% for error in errors %}{% trans "Error: " %}{{ error }}<br>{% endfor %}</p>{% endif %}
|
||||||
<form method='post'>
|
<form method='post'>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@ -45,9 +45,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block helpdesk_js %}
|
{% block helpdesk_js %}
|
||||||
<script>
|
{{ form.media.js }}
|
||||||
$(() => {
|
|
||||||
$("#id_due_date").datepicker({dateFormat: 'yy-mm-dd'});
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% load i18n humanize ticket_to_link %}
|
{% load i18n humanize ticket_to_link %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load helpdesk_util %}
|
||||||
|
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<!--div class="card-header">
|
<!--div class="card-header">
|
||||||
@ -21,20 +22,25 @@
|
|||||||
{% for customfield in ticket.ticketcustomfieldvalue_set.all %}
|
{% for customfield in ticket.ticketcustomfieldvalue_set.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="table-secondary">{{ customfield.field.label }}</th>
|
<th class="table-secondary">{{ customfield.field.label }}</th>
|
||||||
<td>{% ifequal customfield.field.data_type "url" %}<a href='{{ customfield.value }}'>{{ customfield.value }}</a>{% else %}{{ customfield.value|default:"" }}{% endifequal %}</td>
|
<td>{% spaceless %}{% if "url" == customfield.field.data_type %}<a href='{{ customfield.value }}'>{{ customfield.value }}</a>
|
||||||
|
{% elif "datetime" == customfield.field.data_type %}{{ customfield.value|datetime_string_format }}
|
||||||
|
{% elif "date" == customfield.field.data_type %}{{ customfield.value|datetime_string_format }}
|
||||||
|
{% elif "time" == customfield.field.data_type %}{{ customfield.value|datetime_string_format }}
|
||||||
|
{% else %}{{ customfield.value|default:"" }}
|
||||||
|
{% endif %}{% endspaceless %}</td>
|
||||||
</tr>{% endfor %}
|
</tr>{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="table-active">{% trans "Due Date" %}</th>
|
<th class="table-active">{% trans "Due Date" %}</th>
|
||||||
<td>{{ ticket.due_date|date }} {% if ticket.due_date %}({{ ticket.due_date|naturaltime }}){% endif %}
|
<td>{{ ticket.due_date|date:"DATETIME_FORMAT" }} {% if ticket.due_date %}({{ ticket.due_date|naturaltime }}){% endif %}
|
||||||
</td>
|
</td>
|
||||||
<th class="table-active">{% trans "Submitted On" %}</th>
|
<th class="table-active">{% trans "Submitted On" %}</th>
|
||||||
<td>{{ ticket.created|date }} ({{ ticket.created|naturaltime }})</td>
|
<td>{{ ticket.created|date:"DATETIME_FORMAT" }} ({{ ticket.created|naturaltime }})</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="table-active">{% trans "Assigned To" %}</th>
|
<th class="table-active">{% trans "Assigned To" %}</th>
|
||||||
<td>{{ ticket.get_assigned_to }}{% ifequal ticket.get_assigned_to _('Unassigned') %} <strong>
|
<td>{{ ticket.get_assigned_to }}{% if _('Unassigned') == ticket.get_assigned_to %} <strong>
|
||||||
<a data-toggle="tooltip" href='?take' title='{% trans "Assign this ticket to " %}{{ request.user.email }}'><button type="button" class="btn btn-primary btn-sm float-right"><i class="fas fa-hand-paper"></i></button></a>
|
<a data-toggle="tooltip" href='?take' title='{% trans "Assign this ticket to " %}{{ request.user.email }}'><button type="button" class="btn btn-primary btn-sm float-right"><i class="fas fa-hand-paper"></i></button></a>
|
||||||
</strong>{% endifequal %}
|
</strong>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<th class="table-active">{% trans "Submitter E-Mail" %}</th>
|
<th class="table-active">{% trans "Submitter E-Mail" %}</th>
|
||||||
<td> {{ ticket.submitter_email }}
|
<td> {{ ticket.submitter_email }}
|
||||||
@ -97,7 +103,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% if ticket.resolution %}<tr>
|
{% if ticket.resolution %}<tr>
|
||||||
<th colspan='2'>{% trans "Resolution" %}{% ifequal ticket.get_status_display "Resolved" %} <a href='?close'><button type="button" class="btn btn-warning btn-sm">{% trans "Accept and Close" %}</button></a>{% endifequal %}</th>
|
<th colspan='2'>{% trans "Resolution" %}{% if "Resolved" == ticket.get_status_display %} <a href='?close'><button type="button" class="btn btn-warning btn-sm">{% trans "Accept and Close" %}</button></a>{% endif %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan='2'>{{ ticket.get_resolution_markdown|urlizetrunc:50|linebreaksbr }}</td>
|
<td colspan='2'>{{ ticket.get_resolution_markdown|urlizetrunc:50|linebreaksbr }}</td>
|
||||||
|
@ -1,9 +1,34 @@
|
|||||||
from django import template
|
from django.template import Library
|
||||||
|
from django.template.defaultfilters import date as date_filter
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
register = template.Library()
|
from datetime import datetime
|
||||||
|
|
||||||
|
from helpdesk.forms import CUSTOMFIELD_DATE_FORMAT, CUSTOMFIELD_TIME_FORMAT, CUSTOMFIELD_DATETIME_FORMAT
|
||||||
|
|
||||||
|
register = Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def get(value, arg, default=None):
|
def get(value, arg, default=None):
|
||||||
""" Call the dictionary get function """
|
""" Call the dictionary get function """
|
||||||
return value.get(arg, default)
|
return value.get(arg, default)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(expects_localtime=True)
|
||||||
|
def datetime_string_format(value):
|
||||||
|
"""
|
||||||
|
:param value: String - Expected to be a datetime, date, or time in specific format
|
||||||
|
:return: String - reformatted to default datetime, date, or time string if received in one of the expected formats
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
new_value = date_filter(datetime.strptime(value, CUSTOMFIELD_DATETIME_FORMAT), settings.DATETIME_FORMAT)
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
new_value = date_filter(datetime.strptime(value, CUSTOMFIELD_DATE_FORMAT), settings.DATE_FORMAT)
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
new_value = date_filter(datetime.strptime(value, CUSTOMFIELD_TIME_FORMAT), settings.TIME_FORMAT)
|
||||||
|
except ValueError:
|
||||||
|
new_value = value
|
||||||
|
return new_value
|
||||||
|
@ -24,6 +24,7 @@ from django.utils.html import escape
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.generic.edit import FormView, UpdateView
|
from django.views.generic.edit import FormView, UpdateView
|
||||||
|
|
||||||
|
from helpdesk.forms import CUSTOMFIELD_DATE_FORMAT
|
||||||
from helpdesk.query import (
|
from helpdesk.query import (
|
||||||
get_query_class,
|
get_query_class,
|
||||||
query_to_base64,
|
query_to_base64,
|
||||||
@ -75,9 +76,6 @@ else:
|
|||||||
lambda u: u.is_authenticated and u.is_active and u.is_staff)
|
lambda u: u.is_authenticated and u.is_active and u.is_staff)
|
||||||
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
def _get_queue_choices(queues):
|
def _get_queue_choices(queues):
|
||||||
"""Return list of `choices` array for html form for given queues
|
"""Return list of `choices` array for html form for given queues
|
||||||
|
|
||||||
@ -382,6 +380,7 @@ def view_ticket(request, ticket_id):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
submitter_userprofile_url = None
|
submitter_userprofile_url = None
|
||||||
|
|
||||||
return render(request, 'helpdesk/ticket.html', {
|
return render(request, 'helpdesk/ticket.html', {
|
||||||
'ticket': ticket,
|
'ticket': ticket,
|
||||||
'submitter_userprofile_url': submitter_userprofile_url,
|
'submitter_userprofile_url': submitter_userprofile_url,
|
||||||
@ -1774,8 +1773,8 @@ def calc_basic_ticket_stats(Tickets):
|
|||||||
|
|
||||||
date_30 = date_rel_to_today(today, 30)
|
date_30 = date_rel_to_today(today, 30)
|
||||||
date_60 = date_rel_to_today(today, 60)
|
date_60 = date_rel_to_today(today, 60)
|
||||||
date_30_str = date_30.strftime('%Y-%m-%d')
|
date_30_str = date_30.strftime(CUSTOMFIELD_DATE_FORMAT)
|
||||||
date_60_str = date_60.strftime('%Y-%m-%d')
|
date_60_str = date_60.strftime(CUSTOMFIELD_DATE_FORMAT)
|
||||||
|
|
||||||
# > 0 & <= 30
|
# > 0 & <= 30
|
||||||
ota_le_30 = all_open_tickets.filter(created__gte=date_30_str)
|
ota_le_30 = all_open_tickets.filter(created__gte=date_30_str)
|
||||||
|
Loading…
Reference in New Issue
Block a user