Merge branch 'master' into template-cleanup

This commit is contained in:
Stefano Brentegani 2014-07-22 06:43:05 +02:00
commit d95aa46b03
7 changed files with 113 additions and 53 deletions

View File

@ -17,7 +17,9 @@
<li><a href='{% url 'helpdesk_list' %}'><span class="glyphicon glyphicon-tags"></span> <span class="nav-text">{% trans "Tickets" %}</span></a></li> <li><a href='{% url 'helpdesk_list' %}'><span class="glyphicon glyphicon-tags"></span> <span class="nav-text">{% trans "Tickets" %}</span></a></li>
<li><a href='{% url 'helpdesk_submit' %}'><span class="glyphicon glyphicon-plus"></span> <span class="nav-text">{% trans "New Ticket" %}</span></a></li> <li><a href='{% url 'helpdesk_submit' %}'><span class="glyphicon glyphicon-plus"></span> <span class="nav-text">{% trans "New Ticket" %}</span></a></li>
<li><a href='{% url 'helpdesk_report_index' %}'><span class="glyphicon glyphicon-stats"></span><span class="nav-text"> {% trans "Stats" %}</span></a></li> <li><a href='{% url 'helpdesk_report_index' %}'><span class="glyphicon glyphicon-stats"></span><span class="nav-text"> {% trans "Stats" %}</span></a></li>
{% if helpdesk_settings.HELPDESK_KB_ENABLED %}
<li><a href='{% url 'helpdesk_kb_index' %}'><span class="glyphicon glyphicon-tree-deciduous"></span><span class="nav-text">{% trans "Knowledgebase" %}</span></a></li> <li><a href='{% url 'helpdesk_kb_index' %}'><span class="glyphicon glyphicon-tree-deciduous"></span><span class="nav-text">{% trans "Knowledgebase" %}</span></a></li>
{% endif %}
{% if user_saved_queries_ %} {% if user_saved_queries_ %}
<li class="headerlink dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#"><span class="glyphicon glyphicon-filter"></span><span class="nav-text"> {% trans "Saved Query" %} <b class="caret"></b></span></a> <li class="headerlink dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#"><span class="glyphicon glyphicon-filter"></span><span class="nav-text"> {% trans "Saved Query" %} <b class="caret"></b></span></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">

View File

@ -8,12 +8,12 @@
<p>The following items can be maintained by you or other superusers:</p>{% endblocktrans %} <p>The following items can be maintained by you or other superusers:</p>{% endblocktrans %}
<ul> <ul>
<li><a href='../ignore/'>{% trans "E-Mail Ignore list" %}</a></li> <li><a href='{% url 'helpdesk_email_ignore' %}'>{% trans "E-Mail Ignore list" %}</a></li>
<li><a href='{{ ADMIN_URL }}helpdesk/queue/'>{% trans "Maintain Queues" %}</a></li> <li><a href='{% url 'admin:helpdesk_queue_changelist' %}'>{% trans "Maintain Queues" %}</a></li>
<li><a href='{{ ADMIN_URL }}helpdesk/presetreply/'>{% trans "Maintain Pre-Set Replies" %}</a></li> <li><a href='{% url 'admin:helpdesk_presetreply_changelist' %}'>{% trans "Maintain Pre-Set Replies" %}</a></li>
<li><a href='{{ ADMIN_URL }}helpdesk/kbcategory/'>{% trans "Maintain Knowledgebase Categories" %}</a></li> <li><a href='{% url 'admin:helpdesk_kbcategory_changelist' %}'>{% trans "Maintain Knowledgebase Categories" %}</a></li>
<li><a href='{{ ADMIN_URL }}helpdesk/kbitem/'>{% trans "Maintain Knowledgebase Items" %}</a></li> <li><a href='{% url 'admin:helpdesk_kbitem_changelist' %}'>{% trans "Maintain Knowledgebase Items" %}</a></li>
<li><a href='{{ ADMIN_URL }}helpdesk/emailtemplate/'>{% trans "Maintain E-Mail Templates" %}</a></li> <li><a href='{% url 'admin:helpdesk_emailtemplate_changelist' %}'>{% trans "Maintain E-Mail Templates" %}</a></li>
<li><a href='{{ ADMIN_URL }}auth/user/'>{% trans "Maintain Users" %}</a></li> <li><a href='{% url 'admin:auth_user_changelist' %}'>{% trans "Maintain Users" %}</a></li>
</ul> </ul>
{% endblock %} {% endblock %}

View File

@ -1,2 +1,3 @@
from helpdesk.tests.ticket_submission import * from helpdesk.tests.ticket_submission import *
from helpdesk.tests.public_actions import * from helpdesk.tests.public_actions import *
from helpdesk.tests.navigation import *

33
helpdesk/tests/helpers.py Normal file
View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
import sys
try:
from django.contrib.auth import get_user_model
except ImportError:
from django.contrib.auth.models import User
else:
User = get_user_model()
def get_staff_user(username='helpdesk.staff', password='password'):
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
user = User.objects.create_user(username=username, password=password, email='staff@example.com')
user.is_staff = True
user.save()
else:
user.set_password(password)
user.save()
return user
def reload_urlconf(urlconf=None):
if urlconf is None:
from django.conf import settings
urlconf = settings.ROOT_URLCONF
if urlconf in sys.modules:
from django.core.urlresolvers import clear_url_caches
reload(sys.modules[urlconf])
clear_url_caches()

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse
from django.test import TestCase
from helpdesk.tests.helpers import get_staff_user, reload_urlconf
class TestKBDisabled(TestCase):
def setUp(self):
from helpdesk import settings
self.HELPDESK_KB_ENABLED = settings.HELPDESK_KB_ENABLED
if self.HELPDESK_KB_ENABLED:
settings.HELPDESK_KB_ENABLED = False
reload_urlconf()
def tearDown(self):
from helpdesk import settings
if self.HELPDESK_KB_ENABLED:
settings.HELPDESK_KB_ENABLED = True
reload_urlconf()
def test_navigation(self):
"""Test proper rendering of navigation.html by accessing the dashboard"""
from django.core.urlresolvers import NoReverseMatch
self.client.login(username=get_staff_user().username, password='password')
self.assertRaises(NoReverseMatch, reverse, 'helpdesk_kb_index')
try:
response = self.client.get(reverse('helpdesk_dashboard'))
except NoReverseMatch, e:
if 'helpdesk_kb_index' in e.message:
self.fail("Please verify any unchecked references to helpdesk_kb_index (start with navigation.html)")
else:
raise
else:
self.assertEqual(response.status_code, 200)

View File

@ -99,7 +99,7 @@ urlpatterns = patterns('helpdesk.views.staff',
url(r'^tickets/(?P<ticket_id>[0-9]+)/dependency/delete/(?P<dependency_id>[0-9]+)/$', url(r'^tickets/(?P<ticket_id>[0-9]+)/dependency/delete/(?P<dependency_id>[0-9]+)/$',
'ticket_dependency_del', 'ticket_dependency_del',
name='helpdesk_ticket_dependency_del'), name='helpdesk_ticket_dependency_del'),
url(r'^tickets/(?P<ticket_id>[0-9]+)/attachment_delete/(?P<attachment_id>[0-9]+)/$', url(r'^tickets/(?P<ticket_id>[0-9]+)/attachment_delete/(?P<attachment_id>[0-9]+)/$',
'attachment_del', 'attachment_del',
name='helpdesk_attachment_del'), name='helpdesk_attachment_del'),
@ -156,7 +156,7 @@ urlpatterns += patterns('helpdesk.views.public',
url(r'^change_language/$', url(r'^change_language/$',
'change_language', 'change_language',
name='helpdesk_public_change_language'), name='helpdesk_public_change_language'),
) )
urlpatterns += patterns('', urlpatterns += patterns('',
@ -203,7 +203,7 @@ if helpdesk_settings.HELPDESK_KB_ENABLED:
urlpatterns += patterns('helpdesk.views.kb', urlpatterns += patterns('helpdesk.views.kb',
url(r'^kb/$', url(r'^kb/$',
'index', name='helpdesk_kb_index'), 'index', name='helpdesk_kb_index'),
url(r'^kb/(?P<item>[0-9]+)/$', url(r'^kb/(?P<item>[0-9]+)/$',
'item', name='helpdesk_kb_item'), 'item', name='helpdesk_kb_item'),
@ -215,13 +215,12 @@ if helpdesk_settings.HELPDESK_KB_ENABLED:
) )
urlpatterns += patterns('', urlpatterns += patterns('',
url(r'^api/$',TemplateView.as_view(template_name='helpdesk/help_api.html'), url(r'^api/$', TemplateView.as_view(template_name='helpdesk/help_api.html'),
name='helpdesk_api_help'), name='helpdesk_api_help'),
url(r'^help/context/$',TemplateView.as_view(template_name='helpdesk/help_context.html'), url(r'^help/context/$', TemplateView.as_view(template_name='helpdesk/help_context.html'),
name='helpdesk_help_context'), name='helpdesk_help_context'),
url(r'^system_settings/$',DirectTemplateView.as_view(template_name='helpdesk/system_settings.html', url(r'^system_settings/$', DirectTemplateView.as_view(template_name='helpdesk/system_settings.html'),
extra_context={'ADMIN_URL': getattr(settings, 'ADMIN_URL', '/admin/')}), name='helpdesk_system_settings'),
name='helpdesk_system_settings'),
) )

View File

@ -26,6 +26,7 @@ from django.db.models import Q
from django.http import HttpResponseRedirect, Http404, HttpResponse, HttpResponseForbidden from django.http import HttpResponseRedirect, Http404, HttpResponse, HttpResponseForbidden
from django.shortcuts import render_to_response, get_object_or_404 from django.shortcuts import render_to_response, get_object_or_404
from django.template import loader, Context, RequestContext from django.template import loader, Context, RequestContext
from django.utils.dates import MONTHS_3
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.html import escape from django.utils.html import escape
from django import forms from django import forms
@ -39,7 +40,7 @@ from helpdesk.forms import TicketForm, UserSettingsForm, EmailIgnoreForm, EditTi
from helpdesk.lib import send_templated_mail, query_to_dict, apply_query, safe_template_context from helpdesk.lib import send_templated_mail, query_to_dict, apply_query, safe_template_context
from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC, TicketDependency from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC, TicketDependency
from helpdesk import settings as helpdesk_settings from helpdesk import settings as helpdesk_settings
if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE:
# treat 'normal' users like 'staff' # treat 'normal' users like 'staff'
staff_member_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active) staff_member_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active)
@ -69,7 +70,7 @@ def dashboard(request):
# closed & resolved tickets, assigned to current user # closed & resolved tickets, assigned to current user
tickets_closed_resolved = Ticket.objects.filter( tickets_closed_resolved = Ticket.objects.filter(
assigned_to=request.user, assigned_to=request.user,
status__in = [Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS]) status__in = [Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS])
unassigned_tickets = Ticket.objects.filter( unassigned_tickets = Ticket.objects.filter(
@ -107,7 +108,7 @@ def dashboard(request):
GROUP BY queue, name GROUP BY queue, name
ORDER BY q.id; ORDER BY q.id;
""") """)
dash_tickets = query_to_dict(cursor.fetchall(), cursor.description) dash_tickets = query_to_dict(cursor.fetchall(), cursor.description)
return render_to_response('helpdesk/dashboard.html', return render_to_response('helpdesk/dashboard.html',
@ -200,7 +201,7 @@ def view_ticket(request, ticket_id):
if request.GET.has_key('take'): if request.GET.has_key('take'):
# Allow the user to assign the ticket to themselves whilst viewing it. # Allow the user to assign the ticket to themselves whilst viewing it.
# Trick the update_ticket() view into thinking it's being called with # Trick the update_ticket() view into thinking it's being called with
# a valid POST. # a valid POST.
request.POST = { request.POST = {
@ -261,7 +262,7 @@ view_ticket = staff_member_required(view_ticket)
def return_ticketccstring_and_show_subscribe(user, ticket): def return_ticketccstring_and_show_subscribe(user, ticket):
''' used in view_ticket() and followup_edit()''' ''' used in view_ticket() and followup_edit()'''
# create the ticketcc_string and check whether current user is already # create the ticketcc_string and check whether current user is already
# subscribed # subscribed
username = user.username.upper() username = user.username.upper()
useremail = user.email.upper() useremail = user.email.upper()
@ -448,7 +449,7 @@ def update_ticket(request, ticket_id, public=False):
messages_sent_to = [] messages_sent_to = []
# ticket might have changed above, so we re-instantiate context with the # ticket might have changed above, so we re-instantiate context with the
# (possibly) updated ticket. # (possibly) updated ticket.
context = safe_template_context(ticket) context = safe_template_context(ticket)
context.update( context.update(
@ -457,7 +458,7 @@ def update_ticket(request, ticket_id, public=False):
) )
if public and (f.comment or (f.new_status in (Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS))): if public and (f.comment or (f.new_status in (Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS))):
if f.new_status == Ticket.RESOLVED_STATUS: if f.new_status == Ticket.RESOLVED_STATUS:
template = 'resolved_' template = 'resolved_'
@ -712,7 +713,7 @@ def ticket_list(request):
or request.GET.has_key('status') or request.GET.has_key('status')
or request.GET.has_key('q') or request.GET.has_key('q')
or request.GET.has_key('sort') or request.GET.has_key('sort')
or request.GET.has_key('sortreverse') or request.GET.has_key('sortreverse')
): ):
# Fall-back if no querying is being done, force the list to only # Fall-back if no querying is being done, force the list to only
@ -751,7 +752,7 @@ def ticket_list(request):
date_from = request.GET.get('date_from') date_from = request.GET.get('date_from')
if date_from: if date_from:
query_params['filtering']['created__gte'] = date_from query_params['filtering']['created__gte'] = date_from
date_to = request.GET.get('date_to') date_to = request.GET.get('date_to')
if date_to: if date_to:
query_params['filtering']['created__lte'] = date_to query_params['filtering']['created__lte'] = date_to
@ -842,7 +843,7 @@ def edit_ticket(request, ticket_id):
return HttpResponseRedirect(ticket.get_absolute_url()) return HttpResponseRedirect(ticket.get_absolute_url())
else: else:
form = EditTicketForm(instance=ticket) form = EditTicketForm(instance=ticket)
return render_to_response('helpdesk/edit_ticket.html', return render_to_response('helpdesk/edit_ticket.html',
RequestContext(request, { RequestContext(request, {
'form': form, 'form': form,
@ -854,7 +855,7 @@ def create_ticket(request):
assignable_users = User.objects.filter(is_active=True, is_staff=True).order_by('username') assignable_users = User.objects.filter(is_active=True, is_staff=True).order_by('username')
else: else:
assignable_users = User.objects.filter(is_active=True).order_by('username') assignable_users = User.objects.filter(is_active=True).order_by('username')
if request.method == 'POST': if request.method == 'POST':
form = TicketForm(request.POST, request.FILES) form = TicketForm(request.POST, request.FILES)
form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()] form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()]
@ -955,7 +956,7 @@ def run_report(request, report):
return HttpResponseRedirect(reverse("helpdesk_report_index")) return HttpResponseRedirect(reverse("helpdesk_report_index"))
report_queryset = Ticket.objects.all().select_related() report_queryset = Ticket.objects.all().select_related()
from_saved_query = False from_saved_query = False
saved_query = None saved_query = None
@ -978,22 +979,8 @@ def run_report(request, report):
# a second table for more complex queries # a second table for more complex queries
summarytable2 = defaultdict(int) summarytable2 = defaultdict(int)
month_name = lambda m: MONTHS_3[m].title()
months = (
_('Jan'),
_('Feb'),
_('Mar'),
_('Apr'),
_('May'),
_('Jun'),
_('Jul'),
_('Aug'),
_('Sep'),
_('Oct'),
_('Nov'),
_('Dec'),
)
first_ticket = Ticket.objects.all().order_by('created')[0] first_ticket = Ticket.objects.all().order_by('created')[0]
first_month = first_ticket.created.month first_month = first_ticket.created.month
first_year = first_ticket.created.year first_year = first_ticket.created.year
@ -1005,7 +992,7 @@ def run_report(request, report):
periods = [] periods = []
year, month = first_year, first_month year, month = first_year, first_month
working = True working = True
periods.append("%s %s" % (months[month - 1], year)) periods.append("%s %s" % (month_name(month), year))
while working: while working:
month += 1 month += 1
@ -1014,7 +1001,7 @@ def run_report(request, report):
month = 1 month = 1
if (year > last_year) or (month > last_month and year >= last_year): if (year > last_year) or (month > last_month and year >= last_year):
working = False working = False
periods.append("%s %s" % (months[month - 1], year)) periods.append("%s %s" % (month_name(month), year))
if report == 'userpriority': if report == 'userpriority':
title = _('User by Priority') title = _('User by Priority')
@ -1080,7 +1067,7 @@ def run_report(request, report):
elif report == 'usermonth': elif report == 'usermonth':
metric1 = u'%s' % ticket.get_assigned_to metric1 = u'%s' % ticket.get_assigned_to
metric2 = u'%s %s' % (months[ticket.created.month - 1], ticket.created.year) metric2 = u'%s %s' % (month_name(ticket.created.month), ticket.created.year)
elif report == 'queuepriority': elif report == 'queuepriority':
metric1 = u'%s' % ticket.queue.title metric1 = u'%s' % ticket.queue.title
@ -1092,11 +1079,11 @@ def run_report(request, report):
elif report == 'queuemonth': elif report == 'queuemonth':
metric1 = u'%s' % ticket.queue.title metric1 = u'%s' % ticket.queue.title
metric2 = u'%s %s' % (months[ticket.created.month - 1], ticket.created.year) metric2 = u'%s %s' % (month_name(ticket.created.month), ticket.created.year)
elif report == 'daysuntilticketclosedbymonth': elif report == 'daysuntilticketclosedbymonth':
metric1 = u'%s' % ticket.queue.title metric1 = u'%s' % ticket.queue.title
metric2 = u'%s %s' % (months[ticket.created.month - 1], ticket.created.year) metric2 = u'%s %s' % (month_name(ticket.created.month), ticket.created.year)
metric3 = ticket.modified - ticket.created metric3 = ticket.modified - ticket.created
metric3 = metric3.days metric3 = metric3.days
@ -1106,15 +1093,15 @@ def run_report(request, report):
if report == 'daysuntilticketclosedbymonth': if report == 'daysuntilticketclosedbymonth':
summarytable2[metric1, metric2] += metric3 summarytable2[metric1, metric2] += metric3
table = [] table = []
if report == 'daysuntilticketclosedbymonth': if report == 'daysuntilticketclosedbymonth':
for key in summarytable2.keys(): for key in summarytable2.keys():
summarytable[key] = summarytable2[key] / summarytable[key] summarytable[key] = summarytable2[key] / summarytable[key]
header1 = sorted(set(list( i.encode('utf-8') for i,_ in summarytable.keys() ))) header1 = sorted(set(list( i.encode('utf-8') for i,_ in summarytable.keys() )))
column_headings = [col1heading] + possible_options column_headings = [col1heading] + possible_options
# Pivot the data so that 'header1' fields are always first column # Pivot the data so that 'header1' fields are always first column
@ -1330,11 +1317,11 @@ def calc_basic_ticket_stats(Ticket):
date_30_str = date_30.strftime('%Y-%m-%d') date_30_str = date_30.strftime('%Y-%m-%d')
date_60_str = date_60.strftime('%Y-%m-%d') date_60_str = date_60.strftime('%Y-%m-%d')
# > 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)
N_ota_le_30 = len(ota_le_30) N_ota_le_30 = len(ota_le_30)
# >= 30 & <= 60 # >= 30 & <= 60
ota_le_60_ge_30 = all_open_tickets.filter(created__gte = date_60_str, created__lte = date_30_str) ota_le_60_ge_30 = all_open_tickets.filter(created__gte = date_60_str, created__lte = date_30_str)
N_ota_le_60_ge_30 = len(ota_le_60_ge_30) N_ota_le_60_ge_30 = len(ota_le_60_ge_30)
@ -1357,7 +1344,7 @@ def calc_basic_ticket_stats(Ticket):
average_nbr_days_until_ticket_closed_last_60_days = calc_average_nbr_days_until_ticket_resolved(all_closed_last_60_days) average_nbr_days_until_ticket_closed_last_60_days = calc_average_nbr_days_until_ticket_resolved(all_closed_last_60_days)
# put together basic stats # put together basic stats
basic_ticket_stats = { 'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed, basic_ticket_stats = { 'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed,
'average_nbr_days_until_ticket_closed_last_60_days': average_nbr_days_until_ticket_closed_last_60_days, 'average_nbr_days_until_ticket_closed_last_60_days': average_nbr_days_until_ticket_closed_last_60_days,
'open_ticket_stats': ots, } 'open_ticket_stats': ots, }