Added optional serverside processing on datatables that lists all tickets - True by default

This commit is contained in:
Dilip Dwarak 2018-10-14 20:23:28 -04:00
parent 8c2009a871
commit fc028334d9
8 changed files with 264 additions and 38 deletions

View File

@ -21,6 +21,8 @@ from helpdesk.models import Attachment, EmailTemplate
import six
from model_utils import Choices
if six.PY3:
from base64 import encodebytes as b64encode
from base64 import decodebytes as b64decode
@ -329,3 +331,58 @@ def process_attachments(followup, attached_files):
attachments.append([filename, att.file])
return attachments
ORDER_COLUMN_CHOICES = Choices(
('0', 'id'),
('2', 'priority'),
('3', 'title'),
('4', 'queue'),
('5', 'status'),
('6', 'created'),
('7', 'due_date'),
('8', 'assigned_to')
)
def query_tickets_by_args(objects, order_by, **kwargs):
"""
This function takes in a list of ticket objects from the views and throws it
to the datatables on ticket_list.html. If a search string was entered, this
function filters existing dataset on search string and returns a filtered
filtered list. The `draw`, `length` etc parameters are for datatables to
display meta data on the table contents. The returning queryset is passed
to a Serializer called TicketSerializer in serializers.py.
"""
draw = int(kwargs.get('draw', None)[0])
length = int(kwargs.get('length', None)[0])
start = int(kwargs.get('start', None)[0])
search_value = kwargs.get('search[value]', None)[0]
order_column = kwargs.get('order[0][column]', None)[0]
order = kwargs.get('order[0][dir]', None)[0]
order_column = ORDER_COLUMN_CHOICES[order_column]
# django orm '-' -> desc
if order == 'desc':
order_column = '-' + order_column
queryset = objects.all().order_by(order_by)
total = queryset.count()
if search_value:
queryset = queryset.filter(Q(id__icontains=search_value) |
Q(priority__icontains=search_value) |
Q(title__icontains=search_value) |
Q(queue__title__icontains=search_value) |
Q(status__icontains=search_value) |
Q(created__icontains=search_value) |
Q(due_date__icontains=search_value) |
Q(assigned_to__email__icontains=search_value))
count = queryset.count()
queryset = queryset.order_by(order_column)[start:start + length]
return {
'items': queryset,
'count': count,
'total': total,
'draw': draw
}

48
helpdesk/serializers.py Normal file
View File

@ -0,0 +1,48 @@
from rest_framework import serializers
from .models import Ticket
from django.contrib.humanize.templatetags import humanize
"""
A serializer for the Ticket model, returns data in the format as required by
datatables for ticket_list.html. Called from staff.datatables_ticket_list.
"""
class TicketSerializer(serializers.ModelSerializer):
ticket = serializers.SerializerMethodField()
assigned_to = serializers.SerializerMethodField()
created = serializers.SerializerMethodField()
due_date = serializers.SerializerMethodField()
status = serializers.SerializerMethodField()
row_class = serializers.SerializerMethodField()
class Meta:
model = Ticket
# fields = '__all__'
fields = ('ticket', 'id', 'priority', 'title', 'queue', 'status', 'created', 'due_date', 'assigned_to', 'row_class')
def get_ticket(self, obj):
return (str(obj.id)+" "+obj.ticket)
def get_status(self, obj):
return (obj.get_status)
def get_created(self, obj):
return (humanize.naturaltime(obj.created))
def get_due_date(self, obj):
return (humanize.naturaltime(obj.due_date))
def get_assigned_to(self, obj):
if obj.assigned_to:
if obj.assigned_to.first_name:
return (obj.assigned_to.first_name)
else:
return (obj.assigned_to.email)
else:
return ("None")
def get_row_class(self, obj):
return (obj.get_priority_css_class)

View File

@ -150,3 +150,7 @@ QUEUE_EMAIL_BOX_UPDATE_ONLY = getattr(settings, 'QUEUE_EMAIL_BOX_UPDATE_ONLY', F
# only allow users to access queues that they are members of?
HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION = getattr(
settings, 'HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION', False)
# Asynchronous Datatables - Optional
USE_SERVERSIDE_PROCESSING = True

View File

@ -6,8 +6,71 @@
<script src='{% static "helpdesk/filter.js" %}'></script>
<script>
$(document).ready(function() {
{% if server_side %}
$(document).ready(function()
{
let url = ""
//DataTables Initialization
let tasks_table = $('#ticketTable').DataTable({
"language": {
"emptyTable": "{% trans 'No Tickets Match Your Selection' %}"
},
"processing": true,
"serverSide": true,
"ajax": {
"url": "/datatables_ticket_list/",
"type": "GET",
},
createdRow: function( row, data, dataIndex )
{
$( row ).addClass(data.row_class);
},
"columns": [
{"data": "ticket",
"render": function (data, type, row, meta)
{
var id = data.split(" ")[0];
var name = data.split(" ")[1];
let url = "{% url 'helpdesk:view' 1234 %}".replace(/1234/, id.toString());
if (type === 'display')
{
data = '<b><a href="' + url + '" >' + name + '</a></b>';
}
return data
}
},
{"data": "id",
"orderable": false,
"render": function(data, type, row, meta)
{
var pk = data;
if(type === 'display'){
data = "<input type='checkbox' name='ticket_id' value='"+pk+"'"+ "class='ticket_multi_select' />"
}
return data
}
},
{"data": "priority"},
{"data": "title",
"render": function (data, type, row, meta)
{
if (type === 'display')
{
data = '<b><a href="' + url + '" >' + data + '</a></b>';
}
return data
}
},
{"data": "queue"},
{"data": "status"},
{"data": "created"},
{"data": "due_date"},
{"data": "assigned_to"},
]
});
})
{% else %}
$('#ticketTable').DataTable({
"oLanguage": {
"sEmptyTable": "{% trans 'No Tickets Match Your Selection' %}"
@ -15,7 +78,9 @@ $(document).ready(function() {
"order": [],
responsive: true
});
{% endif %}
$(document).ready(function()
{
$("#select_all").click(function() {
$(".ticket_multi_select").attr('checked', true);
return false;
@ -30,8 +95,11 @@ $(document).ready(function() {
});
return false;
});
});
})
</script>
{% endblock %}
{% block h1_title %}Tickets
{% if from_saved_query %} [{{ saved_query.title }}]{% endif %}{% endblock %}
@ -227,21 +295,9 @@ $(document).ready(function() {
<th>{% trans "Owner" %}</th>
</tr>
</thead>
<tbody>
{% for ticket in tickets %}
<tr class="{{ ticket.get_priority_css_class }}">
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.ticket }}</a></th>
<td><input type='checkbox' name='ticket_id' value='{{ ticket.id }}' class='ticket_multi_select' /></td>
<td>{{ ticket.priority }}</td>
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.title }}</a></th>
<td>{{ ticket.queue }}</td>
<td>{{ ticket.get_status }}</td>
<td data-order='{{ ticket.created|date:"U" }}'><span title='{{ ticket.created|date:"r" }}'>{{ ticket.created|naturaltime }}</span></td>
<td data-order='{{ ticket.due_date|date:"U" }}'><span title='{{ ticket.due_date|date:"r" }}'>{{ ticket.due_date|naturaltime }}</span></td>
<td>{{ ticket.get_assigned_to }}</td>
</tr>
{% endfor %}
</tbody>
{% if not server_side %}
{% include 'helpdesk/ticket_list_table.html' %}
{% endif %}
</table>
{% csrf_token %}

View File

@ -0,0 +1,17 @@
{% load i18n humanize %}
<tbody>
{% for ticket in tickets %}
<tr class="{{ ticket.get_priority_css_class }}">
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.ticket }}</a></th>
<td><input type='checkbox' name='ticket_id' value='{{ ticket.id }}' class='ticket_multi_select' /></td>
<td>{{ ticket.priority }}</td>
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.title }}</a></th>
<td>{{ ticket.queue }}</td>
<td>{{ ticket.get_status }}</td>
<td data-order='{{ ticket.created|date:"U" }}'><span title='{{ ticket.created|date:"r" }}'>{{ ticket.created|naturaltime }}</span></td>
<td data-order='{{ ticket.due_date|date:"U" }}'><span title='{{ ticket.due_date|date:"r" }}'>{{ ticket.due_date|naturaltime }}</span></td>
<td>{{ ticket.get_assigned_to }}</td>
</tr>
{% endfor %}
</tbody>

View File

@ -141,6 +141,10 @@ urlpatterns = [
url(r'^ignore/delete/(?P<id>[0-9]+)/$',
staff.email_ignore_del,
name='email_ignore_del'),
url(r'^datatables_ticket_list/$',
staff.datatables_ticket_list,
name="datatables_ticket_list"),
]
urlpatterns += [

View File

@ -30,6 +30,14 @@ from django.views.generic.edit import FormView
from django.utils import six
# For datatables serverside
from django.core.cache import cache
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from helpdesk.lib import query_tickets_by_args
from helpdesk.serializers import TicketSerializer
from helpdesk.decorators import (
helpdesk_staff_member_required, helpdesk_superuser_required,
is_helpdesk_staff
@ -980,6 +988,14 @@ def ticket_list(request):
user_saved_queries = SavedSearch.objects.filter(Q(user=request.user) | Q(shared__exact=True))
# Serverside processing on datatables is optional. Set
# USE_SERVERSIDE_PROCESSING to False in settings.py to disable
if helpdesk_settings.USE_SERVERSIDE_PROCESSING:
cache.set('ticket_qs', ticket_qs)
context['server_side'] = True
else:
context['server_side'] = False
return render(request, 'helpdesk/ticket_list.html', dict(
context,
tickets=ticket_qs,
@ -999,6 +1015,28 @@ def ticket_list(request):
ticket_list = staff_member_required(ticket_list)
@helpdesk_staff_member_required
@api_view(['GET', 'POST'])
def datatables_ticket_list(request):
"""
Datatable on ticket_list.html uses this view from to get objects to display
on the table. query_tickets_by_args is at lib.py, TicketSerializer is in
serializers.py. The serializers and this view use django-rest_framework methods
"""
try:
model_object = query_tickets_by_args(cache.get('ticket_qs'), '-date_created', **request.query_params)
serializer = TicketSerializer(model_object['items'], many=True)
result = dict()
result['data'] = serializer.data
result['draw'] = model_object['draw']
result['recordsTotal'] = model_object['total']
result['recordsFiltered'] = model_object['count']
return (Response(result, status=status.HTTP_200_OK, template_name=None, content_type=None))
except TypeError as e:
return (Response(e, status=status.HTTP_404_NOT_FOUND, template_name=None, content_type=None))
@helpdesk_staff_member_required
def edit_ticket(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)

View File

@ -7,3 +7,5 @@ lxml
simplejson
pytz
six
djangorestframework
django-model-utils