Skip to content

Commit

Permalink
Merge pull request #946 from alligatorbait/custom_datetime
Browse files Browse the repository at this point in the history
CustomField datetime type formats updated to fixed string formats
  • Loading branch information
gwasser committed Feb 11, 2021
2 parents 7559e2a + d06626c commit db6202a
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 31 deletions.
51 changes: 42 additions & 9 deletions helpdesk/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
tickets.
"""
import logging
from datetime import datetime, date, time

from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django import forms
Expand Down Expand Up @@ -35,6 +36,10 @@
'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):
"""
Expand Down Expand Up @@ -71,8 +76,14 @@ def customfield_to_field(self, field, instanceargs):
# Try to use the immediate equivalences dictionary
try:
fieldclass = CUSTOMFIELD_TO_FIELD_DICT[field.data_type]
# Change widget in case it is a boolean
if fieldclass == forms.BooleanField:
# Change widgets for the following classes
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'})

except KeyError:
Expand All @@ -88,6 +99,9 @@ class Meta:
model = Ticket
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):
"""
Add any custom fields that are defined to the form
Expand All @@ -99,14 +113,24 @@ def __init__(self, *args, **kwargs):
self.fields['merged_to'].help_text = _('This ticket is merged into the selected ticket.')

for field in CustomField.objects.all():
initial_value = None
try:
current_value = TicketCustomFieldValue.objects.get(ticket=self.instance, field=field)
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 current_value.field.data_type == 'boolean':
initial_value = initial_value == 'True'
except TicketCustomFieldValue.DoesNotExist:
initial_value = None
elif 'boolean' == current_value.field.data_type:
initial_value = 'True' == initial_value
except (TicketCustomFieldValue.DoesNotExist, ValueError, TypeError):
# ValueError error if parsing fails, using initial_value = current_value.value
# TypeError if parsing None type
pass
instanceargs = {
'label': field.label,
'help_text': field.help_text,
Expand All @@ -126,7 +150,16 @@ def save(self, *args, **kwargs):
cfv = TicketCustomFieldValue.objects.get(ticket=self.instance, field=customfield)
except ObjectDoesNotExist:
cfv = TicketCustomFieldValue(ticket=self.instance, field=customfield)
cfv.value = value

# 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.save()

return super(EditTicketForm, self).save(*args, **kwargs)
Expand Down Expand Up @@ -182,7 +215,7 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form):
due_date = forms.DateTimeField(
widget=forms.TextInput(attrs={'class': 'form-control', 'autocomplete': 'off'}),
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'),
)

Expand All @@ -194,7 +227,7 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form):
)

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):
super().__init__(*args, **kwargs)
Expand Down
10 changes: 10 additions & 0 deletions helpdesk/static/helpdesk/js/init_datetime_classes.js
Original file line number Diff line number Diff line change
@@ -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";
});
2 changes: 1 addition & 1 deletion helpdesk/static/helpdesk/js/init_due_date.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$(() => {
$("#id_due_date").datepicker();
$("#id_due_date").datepicker({dateFormat: 'yy-mm-dd 00:00:00'});
});
8 changes: 2 additions & 6 deletions helpdesk/templates/helpdesk/edit_ticket.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<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 %}
</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'>
{% csrf_token %}
<fieldset>
Expand All @@ -45,9 +45,5 @@
{% endblock %}

{% block helpdesk_js %}
<script>
$(() => {
$("#id_due_date").datepicker({dateFormat: 'yy-mm-dd'});
})
</script>
{{ form.media.js }}
{% endblock %}
20 changes: 13 additions & 7 deletions helpdesk/templates/helpdesk/ticket_desc_table.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{% load i18n humanize ticket_to_link %}
{% load static %}
{% load helpdesk_util %}

<div class="card mb-3">
<!--div class="card-header">
Expand All @@ -9,7 +10,7 @@
<div class="table-responsive">
<table class="table table-sm table-border">
<thead class="thead-light">
<tr class=''><th colspan='4'><h3>{{ ticket.queue.slug }}-{{ ticket.id }}. {{ ticket.title }} [{{ ticket.get_status }}]</h3>
<tr class=''><th colspan='4'><h3>{{ ticket.queue.slug }}-{{ ticket.id }}. {{ ticket.title }} [{{ ticket.get_status }}]</h3>
{% blocktrans with ticket.queue as queue %}Queue: {{ queue }}{% endblocktrans %}
<span class='ticket_toolbar float-right'>
<a href="{% url 'helpdesk:edit' ticket.id %}" class="ticket-edit"><button class="btn btn-warning btn-sm"><i class="fas fa-pencil-alt"></i> {% trans "Edit" %}</button></a>
Expand All @@ -21,20 +22,25 @@
{% for customfield in ticket.ticketcustomfieldvalue_set.all %}
<tr>
<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>
<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>
<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>
<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>
</strong>{% endifequal %}
</strong>{% endif %}
</td>
<th class="table-active">{% trans "Submitter E-Mail" %}</th>
<td> {{ ticket.submitter_email }}
Expand Down Expand Up @@ -97,7 +103,7 @@ <h4>{% trans "Description" %}</h4>
</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>
<td colspan='2'>{{ ticket.get_resolution_markdown|urlizetrunc:50|linebreaksbr }}</td>
Expand Down
29 changes: 27 additions & 2 deletions helpdesk/templatetags/helpdesk_util.py
Original file line number Diff line number Diff line change
@@ -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
def get(value, arg, default=None):
""" Call the dictionary get function """
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
2 changes: 1 addition & 1 deletion helpdesk/tests/test_kb.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_kb_category(self):
self.assertContains(response, 'This is a test category')
self.assertContains(response, 'KBItem 1')
self.assertContains(response, 'KBItem 2')
self.assertContains(response, 'Contact a human')
self.assertContains(response, 'Create New Ticket Queue:')
self.client.login(username=self.user.get_username(), password='password')
response = self.client.get(reverse('helpdesk:kb_category', args=("test_cat", )))
self.assertContains(response, '<i class="fa fa-thumbs-up fa-lg"></i>')
Expand Down
9 changes: 4 additions & 5 deletions helpdesk/views/staff.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from django.utils import timezone
from django.views.generic.edit import FormView, UpdateView

from helpdesk.forms import CUSTOMFIELD_DATE_FORMAT
from helpdesk.query import (
get_query_class,
query_to_base64,
Expand Down Expand Up @@ -75,9 +76,6 @@
lambda u: u.is_authenticated and u.is_active and u.is_staff)


User = get_user_model()


def _get_queue_choices(queues):
"""Return list of `choices` array for html form for given queues
Expand Down Expand Up @@ -382,6 +380,7 @@ def view_ticket(request, ticket_id):
)
else:
submitter_userprofile_url = None

return render(request, 'helpdesk/ticket.html', {
'ticket': ticket,
'submitter_userprofile_url': submitter_userprofile_url,
Expand Down Expand Up @@ -1774,8 +1773,8 @@ def calc_basic_ticket_stats(Tickets):

date_30 = date_rel_to_today(today, 30)
date_60 = date_rel_to_today(today, 60)
date_30_str = date_30.strftime('%Y-%m-%d')
date_60_str = date_60.strftime('%Y-%m-%d')
date_30_str = date_30.strftime(CUSTOMFIELD_DATE_FORMAT)
date_60_str = date_60.strftime(CUSTOMFIELD_DATE_FORMAT)

# > 0 & <= 30
ota_le_30 = all_open_tickets.filter(created__gte=date_30_str)
Expand Down

0 comments on commit db6202a

Please sign in to comment.