mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-11-21 23:43:11 +01:00
Added optional serverside processing on datatables that lists all tickets - True by default
This commit is contained in:
parent
8c2009a871
commit
fc028334d9
@ -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
48
helpdesk/serializers.py
Normal 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)
|
@ -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
|
||||
|
@ -6,32 +6,100 @@
|
||||
|
||||
<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);
|
||||
},
|
||||
|
||||
$('#ticketTable').DataTable({
|
||||
"oLanguage": {
|
||||
"sEmptyTable": "{% trans 'No Tickets Match Your Selection' %}"
|
||||
},
|
||||
"order": [],
|
||||
responsive: true
|
||||
});
|
||||
|
||||
$("#select_all").click(function() {
|
||||
$(".ticket_multi_select").attr('checked', true);
|
||||
return false;
|
||||
});
|
||||
$("#select_none").click(function() {
|
||||
$(".ticket_multi_select").attr('checked', false);
|
||||
return false;
|
||||
});
|
||||
$("#select_inverse").click(function() {
|
||||
$(".ticket_multi_select").each(function() {
|
||||
$(this).attr('checked', !$(this).attr('checked'));
|
||||
"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' %}"
|
||||
},
|
||||
"order": [],
|
||||
responsive: true
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
{% endif %}
|
||||
$(document).ready(function()
|
||||
{
|
||||
$("#select_all").click(function() {
|
||||
$(".ticket_multi_select").attr('checked', true);
|
||||
return false;
|
||||
});
|
||||
$("#select_none").click(function() {
|
||||
$(".ticket_multi_select").attr('checked', false);
|
||||
return false;
|
||||
});
|
||||
$("#select_inverse").click(function() {
|
||||
$(".ticket_multi_select").each(function() {
|
||||
$(this).attr('checked', !$(this).attr('checked'));
|
||||
});
|
||||
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 %}
|
||||
|
||||
|
17
helpdesk/templates/helpdesk/ticket_list_table.html
Normal file
17
helpdesk/templates/helpdesk/ticket_list_table.html
Normal 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>
|
@ -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 += [
|
||||
|
@ -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)
|
||||
|
@ -7,3 +7,5 @@ lxml
|
||||
simplejson
|
||||
pytz
|
||||
six
|
||||
djangorestframework
|
||||
django-model-utils
|
Loading…
Reference in New Issue
Block a user