new option 'HELPDESK_DASHBOARD_BASIC_TICKET_STATS'

- shows quick ticket stats in dashboard
 - links to detailed 'days until ticket is closed by month' plot
This commit is contained in:
Andreas Kotowicz 2012-03-12 14:39:27 +01:00
parent b4fe8bd91f
commit 0a8f4ce6d6
4 changed files with 128 additions and 1 deletions

View File

@ -118,6 +118,8 @@ HELPDESK_DASHBOARD_SHOW_DELETE_UNASSIGNED = getattr(settings, 'HELPDESK_DASHBOAR
# hide empty queues in dashboard overview? # hide empty queues in dashboard overview?
HELPDESK_DASHBOARD_HIDE_EMPTY_QUEUES = getattr(settings, 'HELPDESK_DASHBOARD_HIDE_EMPTY_QUEUES', True) HELPDESK_DASHBOARD_HIDE_EMPTY_QUEUES = getattr(settings, 'HELPDESK_DASHBOARD_HIDE_EMPTY_QUEUES', True)
# show basic ticket stats on dashboard?
HELPDESK_DASHBOARD_BASIC_TICKET_STATS = getattr(settings, 'HELPDESK_DASHBOARD_BASIC_TICKET_STATS', False)
''' options for footer ''' ''' options for footer '''

View File

@ -29,6 +29,29 @@
</div> </div>
{% if helpdesk_settings.HELPDESK_DASHBOARD_BASIC_TICKET_STATS %}
<br style='clear: both;' />
<br style='clear: both;' />
<table width='100%'>
<tr class='row_tablehead' style="color: #fbff00;"><td colspan='2'><i>{% trans "Current Ticket Stats" %}</i></td></tr>
<tr><td colspan='2'>- {% trans "Current average number of days until ticket is closed: " %}<strong style="color: red;">{{ basic_ticket_stats.average_nbr_days_until_ticket_closed }}</strong>.
{% trans "Click" %} <strong><a href="{% url helpdesk_report_index %}daysuntilticketclosedbymonth">here</a></strong> {% trans "for detailed average by month." %} </td></tr>
<tr><td colspan='2'>- {% trans "Distribution of open tickets, grouped by time period:" %}</td></tr>
<tr class='row_columnheads'><th>{% trans "Days since opened" %}</th><th>{% trans "Number of open tickets" %}</th></tr>
{% for entry in basic_ticket_stats.open_ticket_stats %}
<tr class='row_{% cycle odd,even %} row_hover'>
<th style="padding-left: 20px;">{{ entry.0 }}</th>
<td style="padding-left: 20px;"><span style="color: {{ entry.2 }};">{% if entry.1 > 0 %}<a href="{% url helpdesk_list %}?{{ entry.3 }}">{{ entry.1 }}</a>{% else %}{{ entry.1 }}{% endif %}</span></td>
</tr>
{% endfor %}
</table>
{% endif %}
{% if all_tickets_reported_by_current_user %} {% if all_tickets_reported_by_current_user %}
<br style='clear: both;' /> <br style='clear: both;' />

View File

@ -24,6 +24,7 @@
<li><a href='queuepriority/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Priority" %}</a></li> <li><a href='queuepriority/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Priority" %}</a></li>
<li><a href='queuestatus/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Status" %}</a></li> <li><a href='queuestatus/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Status" %}</a></li>
<li><a href='queuemonth/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Month" %}</a></li> <li><a href='queuemonth/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Month" %}</a></li>
<li><a href='daysuntilticketclosedbymonth/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "Days until ticket closed by Month" %}</a></li>
</ul></li> </ul></li>
</ul> </ul>

View File

@ -8,6 +8,7 @@ views/staff.py - The bulk of the application - provides most business logic and
""" """
from datetime import datetime from datetime import datetime
from datetime import timedelta
import sys import sys
from django.conf import settings from django.conf import settings
@ -78,6 +79,11 @@ def dashboard(request):
).order_by('status') ).order_by('status')
# calculate basic ticket stats if requested
basic_ticket_stats = False
if helpdesk_settings.HELPDESK_DASHBOARD_BASIC_TICKET_STATS:
basic_ticket_stats = calc_basic_ticket_stats(Ticket)
# The following query builds a grid of queues & ticket statuses, # The following query builds a grid of queues & ticket statuses,
# to be displayed to the user. EG: # to be displayed to the user. EG:
# Open Resolved # Open Resolved
@ -122,6 +128,7 @@ def dashboard(request):
'unassigned_tickets': unassigned_tickets, 'unassigned_tickets': unassigned_tickets,
'all_tickets_reported_by_current_user': all_tickets_reported_by_current_user, 'all_tickets_reported_by_current_user': all_tickets_reported_by_current_user,
'dash_tickets': dash_tickets, 'dash_tickets': dash_tickets,
'basic_ticket_stats': basic_ticket_stats,
})) }))
dashboard = staff_member_required(dashboard) dashboard = staff_member_required(dashboard)
@ -934,7 +941,7 @@ report_index = staff_member_required(report_index)
def run_report(request, report): def run_report(request, report):
if Ticket.objects.all().count() == 0 or report not in ('queuemonth', 'usermonth', 'queuestatus', 'queuepriority', 'userstatus', 'userpriority', 'userqueue'): if Ticket.objects.all().count() == 0 or report not in ('queuemonth', 'usermonth', 'queuestatus', 'queuepriority', 'userstatus', 'userpriority', 'userqueue', 'daysuntilticketclosedbymonth'):
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()
@ -958,6 +965,8 @@ def run_report(request, report):
from collections import defaultdict from collections import defaultdict
summarytable = defaultdict(int) summarytable = defaultdict(int)
# a second table for more complex queries
summarytable2 = defaultdict(int)
months = ( months = (
_('Jan'), _('Jan'),
@ -1038,8 +1047,14 @@ def run_report(request, report):
possible_options = periods possible_options = periods
charttype = 'date' charttype = 'date'
elif report == 'daysuntilticketclosedbymonth':
title = _('Days until ticket closed by Month')
col1heading = _('Queue')
possible_options = periods
charttype = 'date'
metric3 = False
for ticket in report_queryset: for ticket in report_queryset:
if report == 'userpriority': if report == 'userpriority':
metric1 = u'%s' % ticket.get_assigned_to metric1 = u'%s' % ticket.get_assigned_to
@ -1069,10 +1084,23 @@ def run_report(request, report):
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' % (months[ticket.created.month - 1], ticket.created.year)
elif report == 'daysuntilticketclosedbymonth':
metric1 = u'%s' % ticket.queue.title
metric2 = u'%s %s' % (months[ticket.created.month - 1], ticket.created.year)
metric3 = ticket.modified - ticket.created
metric3 = metric3.days
summarytable[metric1, metric2] += 1 summarytable[metric1, metric2] += 1
if metric3:
if report == 'daysuntilticketclosedbymonth':
summarytable2[metric1, metric2] += metric3
table = [] table = []
if report == 'daysuntilticketclosedbymonth':
for key in summarytable2.keys():
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
@ -1262,3 +1290,76 @@ def attachment_del(request, ticket_id, attachment_id):
return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket_id])) return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket_id]))
attachment_del = staff_member_required(attachment_del) attachment_del = staff_member_required(attachment_del)
def calc_average_nbr_days_until_ticket_resolved(Tickets):
nbr_closed_tickets = len(Tickets)
days_per_ticket = 0
days_each_ticket = list()
for ticket in Tickets:
time_ticket_open = ticket.modified - ticket.created
days_this_ticket = time_ticket_open.days
days_per_ticket += days_this_ticket
days_each_ticket.append(days_this_ticket)
mean_per_ticket = days_per_ticket / nbr_closed_tickets
return mean_per_ticket
def calc_basic_ticket_stats(Ticket):
# all closed tickets - independent of user
all_closed_tickets = Ticket.objects.filter(status = Ticket.CLOSED_STATUS)
average_nbr_days_until_ticket_closed = calc_average_nbr_days_until_ticket_resolved(all_closed_tickets)
# all not closed tickets (open, reopened, resolved,) - independent of user
all_open_tickets = Ticket.objects.exclude(status = Ticket.CLOSED_STATUS)
today = datetime.today()
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')
# < 30
ota_le_30 = all_open_tickets.filter(created__gt = date_30)
N_ota_le_30 = len(ota_le_30)
# > 30 & < 60
ota_le_60_ge_30 = all_open_tickets.filter(created__gte = date_60, created__lte = date_30)
N_ota_le_60_ge_30 = len(ota_le_60_ge_30)
# > 60
ota_ge_60 = all_open_tickets.filter(created__lt = date_60)
N_ota_ge_60 = len(ota_ge_60)
# (O)pen (T)icket (S)tats
ots = list()
# label, number entries, color, sort_string
ots.append(['< 30 days', N_ota_le_30, get_color_for_nbr_days(N_ota_le_30), sort_string(date_30_str, ''), ])
ots.append(['30 - 60 days', N_ota_le_60_ge_30, get_color_for_nbr_days(N_ota_le_60_ge_30), sort_string(date_60_str, date_30_str), ])
ots.append(['> 60 days', N_ota_ge_60, get_color_for_nbr_days(N_ota_ge_60), sort_string('', date_60_str), ])
# put together basic stats
basic_ticket_stats = { 'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed,
'open_ticket_stats': ots, }
return basic_ticket_stats
def get_color_for_nbr_days(nbr_days):
''' '''
if nbr_days < 5:
color_string = 'green'
elif nbr_days >= 5 and nbr_days < 10:
color_string = 'orange'
else: # more than 10 days
color_string = 'red'
return color_string
def days_since_created(today, ticket):
return (today - ticket.created).days
def date_rel_to_today(today, offset):
return today - timedelta(days = offset)
def sort_string(begin, end):
return 'sort=created&date_from=%s&date_to=%s&status=%s&status=%s&status=%s' %(begin, end, Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS, Ticket.RESOLVED_STATUS)