mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-11-22 07:53:19 +01:00
Overhaul reporting: remove custom SQL commands for compatibility reasons. Code is MUCh simpler now. Also allows user to specify a saved search query via GET paramater - need to add links to this, as it lets them filter charts - very useful\!
This commit is contained in:
parent
eea960f9b9
commit
ac95f9d893
@ -119,104 +119,6 @@ def send_templated_mail(template_name, email_context, recipients, sender=None, b
|
||||
return msg.send(fail_silently)
|
||||
|
||||
|
||||
def normalise_data(data, to=100):
|
||||
"""
|
||||
Used for normalising data prior to graphing with Google charting API. EG:
|
||||
|
||||
[1, 4, 10] becomes [10, 40, 100]
|
||||
[36, 54, 240] becomes [15, 23, 100]
|
||||
"""
|
||||
max_value = max(data)
|
||||
if max_value > to:
|
||||
new_data = []
|
||||
for d in data:
|
||||
new_data.append(int(d/float(max_value)*to))
|
||||
data = new_data
|
||||
return data
|
||||
|
||||
|
||||
def line_chart(data):
|
||||
"""
|
||||
'data' is a list of lists making a table.
|
||||
Row 1, columns 2-n are data headings (the time periods)
|
||||
Rows 2-n are data, with column 1 being the line labels
|
||||
"""
|
||||
|
||||
column_headings = data[0][1:]
|
||||
max = 0
|
||||
for row in data[1:]:
|
||||
for field in row[1:]:
|
||||
if field > max:
|
||||
max = field
|
||||
|
||||
|
||||
# Set width to '65px * number of months + 100 for headings.'.
|
||||
chart_url = 'http://chart.apis.google.com/chart?cht=lc&chs=%sx150&chd=t:' % (min(len(column_headings)*65+100, 1000))
|
||||
first_row = True
|
||||
row_headings = []
|
||||
for row in data[1:]:
|
||||
# Add data to URL, normalised to the maximum for all lines on this chart
|
||||
norm = normalise_data(row[1:], max)
|
||||
if not first_row:
|
||||
chart_url += '|'
|
||||
chart_url += ','.join([str(num) for num in norm])
|
||||
row_headings.append(row[0])
|
||||
first_row = False
|
||||
|
||||
chart_url += '&chds='
|
||||
rows = len(data)-1
|
||||
first = True
|
||||
for row in range(rows):
|
||||
# Set maximum data ranges to '0:x' where 'x' is the maximum number in use.
|
||||
if not first:
|
||||
chart_url += ','
|
||||
chart_url += '0,%s' % max
|
||||
first = False
|
||||
chart_url += '&chdl=%s' % '|'.join(row_headings) # Display legend/labels
|
||||
chart_url += '&chco=%s' % ','.join(chart_colours) # Default colour set
|
||||
chart_url += '&chxt=x,y' # Turn on axis labels
|
||||
chart_url += '&chxl=0:|%s|1:|0|%s' % ('|'.join(column_headings), max) # Axis Label Text
|
||||
|
||||
return chart_url
|
||||
|
||||
|
||||
def bar_chart(data):
|
||||
"""
|
||||
'data' is a list of lists making a table.
|
||||
Row 1, columns 2-n are data headings
|
||||
Rows 2-n are data, with column 1 being the line labels
|
||||
"""
|
||||
|
||||
column_headings = data[0][1:]
|
||||
max = 0
|
||||
for row in data[1:]:
|
||||
for field in row[1:]:
|
||||
if field > max:
|
||||
max = field
|
||||
|
||||
|
||||
# Set width to '220px * number of months'.
|
||||
chart_url = 'http://chart.apis.google.com/chart?cht=bvg&chs=%sx150&chd=t:' % (min(len(column_headings) * 220, 1000))
|
||||
first_row = True
|
||||
row_headings = []
|
||||
for row in data[1:]:
|
||||
# Add data to URL, normalised to the maximum for all lines on this chart
|
||||
norm = normalise_data(row[1:], max)
|
||||
if not first_row:
|
||||
chart_url += '|'
|
||||
chart_url += ','.join([str(num) for num in norm])
|
||||
row_headings.append(row[0])
|
||||
first_row = False
|
||||
|
||||
chart_url += '&chds=0,%s' % max
|
||||
chart_url += '&chdl=%s' % '|'.join(row_headings) # Display legend/labels
|
||||
chart_url += '&chco=%s' % ','.join(chart_colours) # Default colour set
|
||||
chart_url += '&chxt=x,y' # Turn on axis labels
|
||||
chart_url += '&chxl=0:|%s|1:|0|%s' % ('|'.join(column_headings), max) # Axis Label Text
|
||||
|
||||
return chart_url
|
||||
|
||||
|
||||
def query_to_dict(results, descriptions):
|
||||
"""
|
||||
Replacement method for cursor.dictfetchall() as that method no longer
|
||||
|
@ -3,33 +3,29 @@
|
||||
{% block helpdesk_title %}{% trans "Reports & Statistics" %}{% endblock %}
|
||||
|
||||
{% block helpdesk_body %}
|
||||
{% ifequal number_tickets 0 %}{% blocktrans %}
|
||||
<h2>Reports & Statistics</h2>
|
||||
<h2>{% trans "Reports & Statistics" %}</h2>
|
||||
|
||||
<p>You haven't created any tickets yet, so you cannot run any reports.</p>
|
||||
{% endblocktrans %}
|
||||
{% ifequal number_tickets 0 %}
|
||||
<p>{% trans "You haven't created any tickets yet, so you cannot run any reports." %}</p>
|
||||
{% else %}
|
||||
{% blocktrans %}
|
||||
<h2>Reports & Statistics</h2>
|
||||
|
||||
|
||||
<ul>
|
||||
<li>User<ul>
|
||||
<li>{% trans "Reports By User" %}<ul>
|
||||
|
||||
<li><a href='userpriority/'>by Priority</a></li>
|
||||
<li><a href='userqueue/'>by Queue</a></li>
|
||||
<li><a href='userstatus/'>by Status</a></li>
|
||||
<li><a href='usermonth/'>by Month</a></li>
|
||||
<li><a href='userpriority/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Priority" %}</a></li>
|
||||
<li><a href='userqueue/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Queue" %}</a></li>
|
||||
<li><a href='userstatus/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Status" %}</a></li>
|
||||
<li><a href='usermonth/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Month" %}</a></li>
|
||||
|
||||
</ul></li>
|
||||
|
||||
<li>Queue<ul>
|
||||
<li>{% trans "Reports By Queue" %}<ul>
|
||||
|
||||
<li><a href='queuepriority/'>by Priority</a></li>
|
||||
<li><a href='queuestatus/'>by Status</a></li>
|
||||
<li><a href='queuemonth/'>by Month</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='queuemonth/{% if saved_query %}?saved_query={{ saved_query }}{% endif %}'>{% trans "by Month" %}</a></li>
|
||||
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
{% endblocktrans %}{% endifequal %}{% endblock %}
|
||||
{% endifequal %}{% endblock %}
|
||||
|
@ -24,7 +24,7 @@ from django.utils.translation import ugettext as _
|
||||
from django.utils.html import escape
|
||||
|
||||
from helpdesk.forms import TicketForm, UserSettingsForm, EmailIgnoreForm, EditTicketForm, TicketCCForm, EditFollowUpForm, TicketDependencyForm
|
||||
from helpdesk.lib import send_templated_mail, line_chart, bar_chart, 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.settings import HAS_TAG_SUPPORT
|
||||
|
||||
@ -745,54 +745,56 @@ rss_list = staff_member_required(rss_list)
|
||||
|
||||
def report_index(request):
|
||||
number_tickets = Ticket.objects.all().count()
|
||||
saved_query = request.GET.get('saved_query', None):
|
||||
return render_to_response('helpdesk/report_index.html',
|
||||
RequestContext(request, {
|
||||
'number_tickets': number_tickets,
|
||||
'saved_query': saved_query,
|
||||
}))
|
||||
report_index = staff_member_required(report_index)
|
||||
|
||||
|
||||
def run_report(request, report):
|
||||
if Ticket.objects.all().count() == 0:
|
||||
if Ticket.objects.all().count() == 0 or report not in ('queuemonth', 'usermonth', 'queuestatus', 'queuepriority', 'userstatus', 'userpriority', 'userqueue'):
|
||||
return HttpResponseRedirect(reverse("helpdesk_report_index"))
|
||||
priority_sql = []
|
||||
priority_columns = []
|
||||
for p in Ticket.PRIORITY_CHOICES:
|
||||
priority_sql.append("COUNT(CASE t.priority WHEN '%s' THEN t.id END) AS \"%s\"" % (p[0], p[1]._proxy____unicode_cast()))
|
||||
priority_columns.append("%s" % p[1]._proxy____unicode_cast())
|
||||
priority_sql = ", ".join(priority_sql)
|
||||
|
||||
status_sql = []
|
||||
status_columns = []
|
||||
for s in Ticket.STATUS_CHOICES:
|
||||
status_sql.append("COUNT(CASE t.status WHEN '%s' THEN t.id END) AS \"%s\"" % (s[0], s[1]._proxy____unicode_cast()))
|
||||
status_columns.append("%s" % s[1]._proxy____unicode_cast())
|
||||
status_sql = ", ".join(status_sql)
|
||||
report_queryset = Ticket.objects.all().select_related()
|
||||
|
||||
queue_sql = []
|
||||
queue_columns = []
|
||||
for q in Queue.objects.all():
|
||||
queue_sql.append("COUNT(CASE t.queue_id WHEN '%s' THEN t.id END) AS \"%s\"" % (q.id, q.title))
|
||||
queue_columns.append(q.title)
|
||||
queue_sql = ", ".join(queue_sql)
|
||||
from_saved_query = False
|
||||
saved_query = None
|
||||
|
||||
if request.GET.get('saved_query', None):
|
||||
from_saved_query = True
|
||||
try:
|
||||
saved_query = SavedSearch.objects.get(pk=request.GET.get('saved_query'))
|
||||
except SavedSearch.DoesNotExist:
|
||||
return HttpResponseRedirect(reverse('helpdesk_report_index'))
|
||||
if not (saved_query.shared or saved_query.user == request.user):
|
||||
return HttpResponseRedirect(reverse('helpdesk_report_index'))
|
||||
|
||||
import cPickle
|
||||
from helpdesk.lib import b64decode
|
||||
query_params = cPickle.loads(b64decode(str(saved_query.query)))
|
||||
report_queryset = apply_query(report_queryset, query_params)
|
||||
|
||||
from collections import defaultdict
|
||||
summarytable = defaultdict(int)
|
||||
|
||||
month_sql = []
|
||||
months = (
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
_('Jan'),
|
||||
_('Feb'),
|
||||
_('Mar'),
|
||||
_('Apr'),
|
||||
_('May'),
|
||||
_('Jun'),
|
||||
_('Jul'),
|
||||
_('Aug'),
|
||||
_('Sep'),
|
||||
_('Oct'),
|
||||
_('Nov'),
|
||||
_('Dec'),
|
||||
)
|
||||
month_columns = []
|
||||
# Throw an error if there are no tickets
|
||||
|
||||
first_ticket = Ticket.objects.all().order_by('created')[0]
|
||||
first_month = first_ticket.created.month
|
||||
first_year = first_ticket.created.year
|
||||
@ -804,115 +806,114 @@ def run_report(request, report):
|
||||
periods = []
|
||||
year, month = first_year, first_month
|
||||
working = True
|
||||
periods.append("%s %s" % (months[month], year))
|
||||
|
||||
while working:
|
||||
temp = (year, month)
|
||||
month += 1
|
||||
if month > 12:
|
||||
year += 1
|
||||
month = 1
|
||||
if (year > last_year) or (month > last_month and year >= last_year):
|
||||
working = False
|
||||
periods.append((temp, (year, month)))
|
||||
|
||||
for (low_bound, upper_bound) in periods:
|
||||
low_sqlmonth = '%s-%02i-01' % (low_bound[0], low_bound[1])
|
||||
upper_sqlmonth = '%s-%02i-01' % (upper_bound[0], upper_bound[1])
|
||||
desc = '%s %s' % (months[low_bound[1]-1], low_bound[0])
|
||||
month_sql.append("""
|
||||
COUNT(
|
||||
CASE 1 = 1
|
||||
WHEN (date(t.created) >= date('%s')
|
||||
AND date(t.created) < date('%s')) THEN t.id END) AS "%s"
|
||||
""" % (low_sqlmonth, upper_sqlmonth, desc))
|
||||
month_columns.append(desc)
|
||||
|
||||
month_sql = ", ".join(month_sql)
|
||||
|
||||
queue_base_sql = """
|
||||
SELECT q.title as queue, %s
|
||||
FROM helpdesk_ticket t,
|
||||
helpdesk_queue q
|
||||
WHERE q.id = t.queue_id
|
||||
GROUP BY queue
|
||||
ORDER BY queue;
|
||||
"""
|
||||
|
||||
user_base_sql = """
|
||||
SELECT u.username as username, %s
|
||||
FROM helpdesk_ticket t,
|
||||
auth_user u
|
||||
WHERE u.id = t.assigned_to_id
|
||||
GROUP BY u.username
|
||||
ORDER BY u.username;
|
||||
"""
|
||||
periods.append("%s %s" % (months[month], year))
|
||||
|
||||
if report == 'userpriority':
|
||||
sql = user_base_sql % priority_sql
|
||||
columns = ['username'] + priority_columns
|
||||
title = 'User by Priority'
|
||||
title = _('User by Priority')
|
||||
col1heading = _('User')
|
||||
possible_options = [t[1].__unicode__() for t in Ticket.PRIORITY_CHOICES]
|
||||
charttype = 'bar'
|
||||
|
||||
elif report == 'userqueue':
|
||||
sql = user_base_sql % queue_sql
|
||||
columns = ['username'] + queue_columns
|
||||
title = 'User by Queue'
|
||||
title = _('User by Queue')
|
||||
col1heading = _('User')
|
||||
possible_options = [q.title.encode('utf-8') for q in Queue.objects.all()]
|
||||
charttype = 'bar'
|
||||
|
||||
elif report == 'userstatus':
|
||||
sql = user_base_sql % status_sql
|
||||
columns = ['username'] + status_columns
|
||||
title = 'User by Status'
|
||||
title = _('User by Status')
|
||||
col1heading = _('User')
|
||||
possible_options = [s[1].__unicode__() for s in Ticket.STATUS_CHOICES]
|
||||
charttype = 'bar'
|
||||
|
||||
elif report == 'usermonth':
|
||||
sql = user_base_sql % month_sql
|
||||
columns = ['username'] + month_columns
|
||||
title = 'User by Month'
|
||||
title = _('User by Month')
|
||||
col1heading = _('User')
|
||||
possible_options = periods
|
||||
charttype = 'date'
|
||||
|
||||
elif report == 'queuepriority':
|
||||
sql = queue_base_sql % priority_sql
|
||||
columns = ['queue'] + priority_columns
|
||||
title = 'Queue by Priority'
|
||||
title = _('Queue by Priority')
|
||||
col1heading = _('Queue')
|
||||
possible_options = [t[1].__unicode__() for t in Ticket.PRIORITY_CHOICES]
|
||||
charttype = 'bar'
|
||||
|
||||
elif report == 'queuestatus':
|
||||
sql = queue_base_sql % status_sql
|
||||
columns = ['queue'] + status_columns
|
||||
title = 'Queue by Status'
|
||||
title = _('Queue by Status')
|
||||
col1heading = _('Queue')
|
||||
possible_options = [s[1].__unicode__() for s in Ticket.STATUS_CHOICES]
|
||||
charttype = 'bar'
|
||||
|
||||
elif report == 'queuemonth':
|
||||
sql = queue_base_sql % month_sql
|
||||
columns = ['queue'] + month_columns
|
||||
title = 'Queue by Month'
|
||||
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(sql)
|
||||
report_output = query_to_dict(cursor.fetchall(), cursor.description)
|
||||
|
||||
data = []
|
||||
|
||||
for record in report_output:
|
||||
line = []
|
||||
for c in columns:
|
||||
c = c.encode('utf-8')
|
||||
line.append(record[c])
|
||||
data.append(line)
|
||||
|
||||
if report in ('queuemonth', 'usermonth'):
|
||||
chart_url = line_chart([columns] + data)
|
||||
title = _('Queue by Month')
|
||||
col1heading = _('Queue')
|
||||
possible_options = periods
|
||||
charttype = 'date'
|
||||
elif report in ('queuestatus', 'queuepriority', 'userstatus', 'userpriority'):
|
||||
chart_url = bar_chart([columns] + data)
|
||||
charttype = 'bar'
|
||||
else:
|
||||
chart_url = ''
|
||||
charttype = ''
|
||||
|
||||
|
||||
|
||||
for ticket in report_queryset:
|
||||
if report == 'userpriority':
|
||||
metric1 = u'%s' % ticket.get_assigned_to
|
||||
metric2 = u'%s' % ticket.get_priority_display()
|
||||
|
||||
elif report == 'userqueue':
|
||||
metric1 = u'%s' % ticket.get_assigned_to
|
||||
metric2 = u'%s' % ticket.queue.title
|
||||
|
||||
elif report == 'userstatus':
|
||||
metric1 = u'%s' % ticket.get_assigned_to
|
||||
metric2 = u'%s' % ticket.get_status_display()
|
||||
|
||||
elif report == 'usermonth':
|
||||
metric1 = u'%s' % ticket.get_assigned_to
|
||||
metric2 = u'%s %s' % (months[ticket.created.month], ticket.created.year)
|
||||
|
||||
elif report == 'queuepriority':
|
||||
metric1 = u'%s' % ticket.queue.title
|
||||
metric2 = u'%s' % ticket.get_priority_display()
|
||||
|
||||
elif report == 'queuestatus':
|
||||
metric1 = u'%s' % ticket.queue.title
|
||||
metric2 = u'%s' % ticket.get_status_display()
|
||||
|
||||
elif report == 'queuemonth':
|
||||
metric1 = u'%s' % ticket.queue.title
|
||||
metric2 = u'%s %s' % (months[ticket.created.month], ticket.created.year)
|
||||
|
||||
summarytable[metric1, metric2] += 1
|
||||
|
||||
table = []
|
||||
|
||||
header1 = sorted(set(list( i.encode('utf-8') for i,_ in summarytable.keys() )))
|
||||
|
||||
column_headings = [col1heading] + possible_options
|
||||
|
||||
# Pivot the data so that 'header1' fields are always first column
|
||||
# in the row, and 'possible_options' are always the 2nd - nth columns.
|
||||
for item in header1:
|
||||
data = []
|
||||
for hdr in possible_options:
|
||||
data.append(summarytable[item, hdr])
|
||||
table.append([item] + data)
|
||||
|
||||
return render_to_response('helpdesk/report_output.html',
|
||||
RequestContext(request, {
|
||||
'headings': columns,
|
||||
'data': data,
|
||||
'chart': chart_url,
|
||||
'title': title,
|
||||
'charttype': charttype,
|
||||
'data': table,
|
||||
'headings': column_headings,
|
||||
'from_saved_query': from_saved_query,
|
||||
'saved_query': saved_query,
|
||||
}))
|
||||
run_report = staff_member_required(run_report)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user