diff --git a/api.py b/api.py index 743428fe..6a28662b 100644 --- a/api.py +++ b/api.py @@ -20,7 +20,7 @@ from django.shortcuts import render_to_response from django.template import loader, Context from django import newforms as forms -from helpdesk.lib import send_multipart_mail +from helpdesk.lib import send_templated_mail from helpdesk.models import Ticket, Queue, FollowUp from helpdesk.forms import TicketForm @@ -174,19 +174,14 @@ class API: 'comment': f.comment, } - subject = '%s %s (Updated)' % (ticket.ticket, ticket.title) - if public and ticket.submitter_email: - template = 'helpdesk/emails/submitter_updated' - send_multipart_mail(template, context, subject, ticket.submitter_email, ticket.queue.from_address) + send_templated_mail('updated_submitter', context, recipients=ticket.submitter_email, sender=ticket.queue.from_address, fail_silently=True) if ticket.queue.updated_ticket_cc: - template_cc = 'helpdesk/emails/cc_updated' - send_multipart_mail(template_cc, context, subject, q.updated_ticket_cc, ticket.queue.from_address) + send_templated_mail('updated_cc', context, recipients=ticket.queue.updated_ticket_cc, sender=ticket.queue.from_address, fail_silently=True) if ticket.assigned_to and self.request.user != ticket.assigned_to: - template_owner = 'helpdesk/emails/owner_updated' - send_multipart_mail(template_owner, context, subject, t.assigned_to.email, ticket.queue.from_address) + send_templated_mail('updated_owner', context, recipients=ticket.assigned_to.email, sender=ticket.queue.from_address, fail_silently=True) ticket.save() @@ -216,16 +211,13 @@ class API: subject = '%s %s (Resolved)' % (ticket.ticket, ticket.title) if ticket.submitter_email: - template = 'helpdesk/emails/submitter_resolved' - send_multipart_mail(template, context, subject, ticket.submitter_email, ticket.queue.from_address) + send_templated_mail('resolved_submitter', context, recipients=ticket.submitter_email, sender=ticket.queue.from_address, fail_silently=True) if ticket.queue.updated_ticket_cc: - template_cc = 'helpdesk/emails/cc_resolved' - send_multipart_mail(template_cc, context, subject, q.updated_ticket_cc, ticket.queue.from_address) + send_templated_mail('resolved_cc', context, recipients=ticket.queue.updated_ticket_cc, sender=ticket.queue.from_address, fail_silently=True) if ticket.assigned_to and self.request.user != ticket.assigned_to: - template_owner = 'helpdesk/emails/owner_resolved' - send_multipart_mail(template_owner, context, subject, t.assigned_to.email, ticket.queue.from_address) + send_templated_mail('resolved_resolved', context, recipients=ticket.assigned_to.email, sender=ticket.queue.from_address, fail_silently=True) ticket.resoltuion = f.comment ticket.status = Ticket.RESOLVED_STATUS diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json new file mode 100644 index 00000000..37b528f1 --- /dev/null +++ b/fixtures/initial_data.json @@ -0,0 +1 @@ +[{"pk": "1", "model": "helpdesk.emailtemplate", "fields": {"template_name": "assigned_cc", "html": "
Hello,<\/p>\n\n
This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }}<\/b><\/a> ({{ ticket.title }}<\/em>) has been {% if ticket.assigned_to %}assigned to {{ ticket.assigned_to %}{% else %}unassigned{% endif %}.<\/p>", "plain_text": "Hello,\n\nThis is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} (\"{{ ticket.title }}\") has been {% if ticket.assigned_to %}assigned to {{ ticket.assigned_to }}{% else %}unassigned{% endif %}\n\nYou can view this online at {{ ticket.staff_url }}.", "heading": "Ticket Assigned", "subject": "(Assigned)"}}, {"pk": "2", "model": "helpdesk.emailtemplate", "fields": {"template_name": "assigned_owner", "html": " Hello,<\/p>\n\n\n This is a courtesy e-mail to let you know that a ticket has been assigned to you.<\/p>\n\n {% if ticket.submitter_email %}The ticket was submitted by {{ ticket.submitter_email }}, and the subject{% else %}The ticket subject{% endif%} is {{ ticket.title }}<\/i>.<\/p>\n\n The ticket ID is {{ ticket.ticket }}<\/a><\/b>. It\"s priority is {{ ticket.get_priority_display }}.<\/p>", "plain_text": "Hello,\n\nThis is a courtesy e-mail to let you know that a ticket has been assigned to you.\n\n{% if ticket.submitter_email %}The ticket was submitted by {{ ticket.submitter_email }}, and the subject{% else %}The ticket subject{% endif%} is {{ ticket.title }}.\n\nThe ticket ID is {{ ticket.ticket }}, you can view it online at {{ ticket.staff_url }}. It\"s priority is {{ ticket.get_priority_display }}.", "heading": "Ticket Assigned To You", "subject": "(Assigned)"}}, {"pk": "3", "model": "helpdesk.emailtemplate", "fields": {"template_name": "closed_cc", "html": " Hello,<\/p>\n\n Ticket {{ ticket.title }}<\/i> ('{{ ticket.title }}') has been closed.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nTicket {{ ticket.title }} (\"{{ ticket.title }}\") has been closed.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}.", "heading": "Ticket Closed", "subject": "(Closed)"}}, {"pk": "4", "model": "helpdesk.emailtemplate", "fields": {"template_name": "closed_owner", "html": " Hello,<\/p>\n\n A ticket currently assigned to you with a subject of {{ ticket.title }}<\/i> has been closed.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nA ticket currently assigned to you with a subject of \"{{ ticket.title }}\" has been closed.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}.", "heading": "Ticket Closed", "subject": "(Closed)"}}, {"pk": "5", "model": "helpdesk.emailtemplate", "fields": {"template_name": "closed_submitter", "html": " Hello,<\/p>\n\n You recently logged a ticket with a subject of {{ ticket.title }}<\/i> with us. This e-mail is to confirm that this ticket has been closed.<\/p>\n\n If you believe that further work is required on this ticket, please let us know by replying to this e-mail and keeping the subject intact.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nYou recently logged a ticket with a subject of \"{{ ticket.title }}\" with us. This e-mail is to confirm that this ticket has been closed.\n\nIf you believe that further work is required on this ticket, please let us know by replying to this e-mail and keeping the subject intact.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}.", "heading": "Ticket Closed", "subject": "(Closed)"}}, {"pk": "6", "model": "helpdesk.emailtemplate", "fields": {"template_name": "escalated_cc", "html": " Hello,<\/p>\n\n This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }}<\/b><\/a> ({{ ticket.title }}<\/em>) has been escalated automatically.<\/p>", "plain_text": "Hello,\n\nThis is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} (\"{{ ticket.title }}\") has been escalated automatically.\n\nYou can view this online at {{ ticket.staff_url }}.", "heading": "Ticket Escalated", "subject": "(Escalated)"}}, {"pk": "8", "model": "helpdesk.emailtemplate", "fields": {"template_name": "escalated_owner", "html": " Hello,<\/p>\n\n A ticket currently assigned to you with a subject of {{ ticket.title }}<\/i> has been automatically escalated.<\/p>\n\n Please review this ticket and attempt to provide a resolution as soon as possible.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nA ticket currently assigned to you with a subject of \"{{ ticket.title }}\" has been automatically escalated.\n\nPlease review this ticket and attempt to provide a resolution as soon as possible.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}.", "heading": "Ticket Assigned to You Has Been Escalated", "subject": "(Escalated)"}}, {"pk": "7", "model": "helpdesk.emailtemplate", "fields": {"template_name": "escalated_submitter", "html": " Hello,<\/p>\n\n You recently logged a ticket with a subject of {{ ticket.title }}<\/i> with us. This e-mail is to advise you of an automated escalation of that ticket.<\/p>\n\n We will review your ticket shortly and attempt to provide a resolution as soon as possible.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nYou recently logged a ticket with a subject of \"{{ ticket.title }}\" with us. This e-mail is to advise you of an automated escalation of that ticket.\n\nWe will review your ticket shortly and attempt to provide a resolution as soon as possible.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}.", "heading": "Your Ticket Has Been Escalated", "subject": "(Escalated)"}}, {"pk": "9", "model": "helpdesk.emailtemplate", "fields": {"template_name": "newticket_cc", "html": " Hello,<\/p>\n\n This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }}<\/b><\/a> ({{ ticket.title }}<\/em>) has been opened.<\/p>", "plain_text": "Hello,\n\nThis is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} (\"{{ ticket.title }}\") has been opened.\n\nYou can view this online at {{ ticket.staff_url }}.", "heading": "New Ticket Opened", "subject": "(Opened)"}}, {"pk": "10", "model": "helpdesk.emailtemplate", "fields": {"template_name": "newticket_submitter", "html": " Hello,<\/p>\n\n\n This is a courtesy e-mail to let you know that we have received your helpdesk query with a subject of {{ ticket.title }}<\/i>. <\/p>\n\n You do not have to do anything further at this stage. Your ticket has been assigned a number of {{ ticket.ticket }}<\/b>.<\/p>\n\n If you wish to send us further details, or if you have any queries about this ticket, please include the ticket id of {{ ticket.ticket }}<\/b> in the subject. The easiest way to do this is just press \"reply\" to this message.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}<\/a>.<\/p>\n\n We will investigate your query and attempt to resolve it as soon as possible. You will receive further updates and a resolution via this e-mail address.<\/p>", "plain_text": "Hello,\n\nThis is a courtesy e-mail to let you know that we have received your helpdesk query with a subject of {{ ticket.title }}. \n\nYou do not have to do anything further at this stage. Your ticket has been assigned a number of {{ ticket.ticket }}.\n\nIf you wish to send us further details, or if you have any queries about this ticket, please include the ticket id of {{ ticket.ticket }} in the subject. The easiest way to do this is just press \"reply\" to this message.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}.\n\nWe will investigate your query and attempt to resolve it as soon as possible. You will receive further updates and a resolution via this e-mail address.", "heading": "Your Ticket Has Been Opened", "subject": "(Opened)"}}, {"pk": "11", "model": "helpdesk.emailtemplate", "fields": {"template_name": "resolved_cc", "html": " Hello,<\/p>\n\n Ticket {{ ticket.ticket }} ({{ ticket.title }}<\/i>) has been resolved.<\/p>\n\n The following resolution was added:<\/p>\n\n This resolution has been e-mailed to the submitter, who will verify it before you can close this ticket.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nTicket {{ ticket.ticket }} ({{ ticket.title }}) has been resolved.\n\nThe following resolution was added:\n\n{{ resolution }}\n\nThis resolution has been e-mailed to the submitter, who will verify it before you can close this ticket.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}.", "heading": "Ticket Resolved", "subject": "(Resolved)"}}, {"pk": "12", "model": "helpdesk.emailtemplate", "fields": {"template_name": "resolved_owner", "html": " Hello,<\/p>\n\n A ticket currently assigned to you with a subject of {{ ticket.title }}<\/i> has had a resolution added.<\/p>\n\n The following resolution was added to ticket {{ ticket.ticket }}<\/b>:<\/p>\n\n This resolution has been e-mailed to the submitter, who will verify it before you can close this ticket.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nA ticket currently assigned to you with a subject of {{ ticket.title }} has had a resolution added.\n\nThe following resolution was added to ticket {{ ticket.ticket }}:\n\n{{ resolution }}\n\nThis resolution has been e-mailed to the submitter, who will verify it before you can close this ticket.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}.", "heading": "Ticket Resolved", "subject": "(Resolved)"}}, {"pk": "13", "model": "helpdesk.emailtemplate", "fields": {"template_name": "resolved_submitter", "html": " Hello,<\/p>\n\n You recently logged a ticket with a subject of {{ ticket.title }}<\/i> with us. This e-mail is to advise you of a resolution to that ticket.<\/p>\n\n The following resolution was added to ticket {{ ticket.ticket }}<\/b>:<\/p>\n\n Can you please confirm that this resolution addresses your needs so we may close this ticket? If you have any further queries, or if you do not believe this resolution is adequate, please reply to this e-mail and keep the subject intact.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nYou recently logged a ticket with a subject of \"{{ ticket.title }}\" with us. This e-mail is to advise you of a resolution to that ticket.\n\nThe following resolution was added to ticket {{ ticket.ticket }}:\n\n{{ resolution }}\n\nCan you please confirm that this resolution addresses your needs so we may close this ticket? If you have any further queries, or if you do not believe this resolution is adequate, please reply to this e-mail and keep the subject intact.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}.", "heading": "Your Ticket Has Been Resolved", "subject": "(Resolved)"}}, {"pk": "14", "model": "helpdesk.emailtemplate", "fields": {"template_name": "updated_cc", "html": " Hello,<\/p>\n\n Ticket {{ ticket.ticket }}<\/a> ({{ ticket.title }}<\/em>) has been updated.<\/p>\n\n The following comment was added:<\/p>\n\n This information has {% if private %}not {% endif %} been e-mailed to the submitter.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.staff_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nTicket {{ ticket.ticket }} ({{ ticket.title }}) has been updated.\n\nThe following comment was added:\n\n{{ comment }}\n\nThis information has {% if private %}not {% endif %} been e-mailed to the submitter.\n\nIf you wish to view this ticket online, you can visit {{ ticket.staff_url }}.", "heading": "Ticket Updated", "subject": "(Updated)"}}, {"pk": "15", "model": "helpdesk.emailtemplate", "fields": {"template_name": "updated_owner", "html": " Hello,<\/p>\n\n Ticket {{ ticket.ticket }}<\/a> ({{ ticket.title }}<\/em>), which is assigned to you, has been updated.<\/p>\n\n The following comment was added to ticket {{ ticket.ticket }}<\/b>:<\/p>\n\n This information has {% if private %}not {% endif %} been e-mailed to the submitter.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.staff_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nTicket {{ ticket.ticket }} ({{ ticket.title }}), which is assigned to you, has been updated.\n\nThe following comment was added to ticket {{ ticket.ticket }}:\n\n{{ comment }}\n\nThis information has {% if private %}not {% endif %} been e-mailed to the submitter.\n\nIf you wish to view this ticket online, you can visit {{ ticket.staff_url }}.", "heading": "Ticket Updated", "subject": "(Updated)"}}, {"pk": "16", "model": "helpdesk.emailtemplate", "fields": {"template_name": "updated_submitter", "html": " Hello,<\/p>\n\n You recently logged a ticket with a subject of {{ ticket.title }}<\/i> with us. This e-mail is to advise you of an update to that ticket.<\/p>\n\n The following comment was added to ticket {{ ticket.ticket }}<\/b>:<\/p>\n\n To provide us with further information, please reply to this e-mail.<\/p>\n\n If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}<\/a>.<\/p>", "plain_text": "Hello,\n\nYou recently logged a ticket with a subject of \"{{ ticket.title }}\" with us. This e-mail is to advise you of an update to that ticket.\n\nThe following comment was added to ticket {{ ticket.ticket }}:\n\n{{ comment }}\n\nTo provide us with further information, please reply to this e-mail.\n\nIf you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}.", "heading": "Your Ticket Has Been Updated", "subject": "(Updated)"}}]
diff --git a/forms.py b/forms.py
index 3344830d..27eff547 100644
--- a/forms.py
+++ b/forms.py
@@ -77,19 +77,19 @@ class TicketForm(forms.Form):
'queue': q,
}
- from helpdesk.lib import send_multipart_mail
+ from helpdesk.lib import send_templated_mail
if t.submitter_email:
- send_multipart_mail('helpdesk/emails/submitter_newticket', context, '%s %s' % (t.ticket, t.title), t.submitter_email, q.from_address)
+ send_templated_mail('newticket_submitter', context, recipients=t.submitter_email, sender=q.from_address, fail_silently=True)
if t.assigned_to and t.assigned_to != user:
- send_multipart_mail('helpdesk/emails/owner_assigned', context, '%s %s (Opened)' % (t.ticket, t.title), t.assigned_to.email, q.from_address)
+ send_templated_mail('assigned_owner', context, recipients=t.assigned_to.email, sender=q.from_address, fail_silently=True)
if q.new_ticket_cc:
- send_multipart_mail('helpdesk/emails/cc_newticket', context, '%s %s (Opened)' % (t.ticket, t.title), q.updated_ticket_cc, q.from_address)
+ send_templated_mail('newticket_cc', context, recipients=q.new_ticket_cc, sender=q.from_address, fail_silently=True)
if q.updated_ticket_cc and q.updated_ticket_cc != q.new_ticket_cc:
- send_multipart_mail('helpdesk/emails/cc_newticket', context, '%s %s (Opened)' % (t.ticket, t.title), q.updated_ticket_cc, q.from_address)
+ send_templated_mail('newticket_cc', context, recipients=q.updated_ticket_cc, sender=q.from_address, fail_silently=True)
return t
@@ -146,13 +146,13 @@ class PublicTicketForm(forms.Form):
'queue': q,
}
- from helpdesk.lib import send_multipart_mail
- send_multipart_mail('helpdesk/emails/submitter_newticket', context, '%s %s' % (t.ticket, t.title), t.submitter_email, q.from_address)
+ from helpdesk.lib import send_templated_mail
+ send_templated_mail('newticket_submitter', context, recipients=t.submitter_email, sender=q.from_address, fail_silently=True)
if q.new_ticket_cc:
- send_multipart_mail('helpdesk/emails/cc_newticket', context, '%s %s (Opened)' % (t.ticket, t.title), q.updated_ticket_cc, q.from_address)
+ send_templated_mail('newticket_cc', context, recipients=q.new_ticket_cc, sender=q.from_address, fail_silently=True)
if q.updated_ticket_cc and q.updated_ticket_cc != q.new_ticket_cc:
- send_multipart_mail('helpdesk/emails/cc_newticket', context, '%s %s (Opened)' % (t.ticket, t.title), q.updated_ticket_cc, q.from_address)
+ send_templated_mail('newticket_cc', context, recipients=q.updated_ticket_cc, sender=q.from_address, fail_silently=True)
return t
diff --git a/htdocs/helpdesk.css b/htdocs/helpdesk.css
index 0ac348df..bb85a24f 100644
--- a/htdocs/helpdesk.css
+++ b/htdocs/helpdesk.css
@@ -11,7 +11,7 @@ table {
}
#container {
- width: 600px;
+ width: 700px;
margin: 0 auto;
text-align: left;
}
@@ -26,7 +26,34 @@ table {
#header li {
display: inline;
float: left;
- padding-left: 5px;
+}
+
+#header li a {
+ padding: 2px 3px;
+ margin: 2px;
+ border: solid #444 1px;
+ color: #000;
+ text-decoration: none;
+ font-size: 10pt;
+ line-height: 12pt;
+}
+
+#searchform .input {
+ border: solid #444 1px;
+ background-color: #fff;
+ color: #ccc;
+ padding: 2px 3px;
+ margin: 0px 2px;
+ font-size: 10pt;
+ line-height: 12pt;
+}
+
+#searchform .input:focus {
+ color: #000;
+}
+
+#header li a:hover {
+ background-color: #ccc;
}
#header, #body, #footer {
diff --git a/lib.py b/lib.py
index 38805561..196f0ebb 100644
--- a/lib.py
+++ b/lib.py
@@ -5,6 +5,39 @@ Jutda Helpdesk - A Django powered ticket tracker for small enterprise.
lib.py - Common functions (eg multipart e-mail)
"""
+def send_templated_mail(template_name, email_context, recipients, sender=None, bcc=None, fail_silently=False, files=None):
+ from helpdesk.models import EmailTemplate
+ from django.core.mail import EmailMultiAlternatives
+ from django.template import loader, Context
+ from django.conf import settings
+
+ t = EmailTemplate.objects.get(template_name__iexact=template_name)
+
+ if not sender:
+ sender = settings.DEFAULT_FROM_EMAIL
+
+ context = Context(email_context)
+
+ text_part = loader.get_template_from_string("%s{%% include 'helpdesk/email_text_footer.txt' %%}" % t.plain_text).render(context)
+ html_part = loader.get_template_from_string("{%% extends 'helpdesk/email_html_base.html' %%}{%% block title %%}%s{%% endblock %%}{%% block content %%}%s{%% endblock %%}" % (t.heading, t.html)).render(context)
+ subject_part = loader.get_template_from_string("{{ ticket.ticket }} {{ ticket.title }} %s" % t.subject).render(context)
+
+ if type(recipients) != list:
+ recipients = [recipients,]
+
+ msg = EmailMultiAlternatives(subject_part, text_part, sender, recipients, bcc=bcc)
+ msg.attach_alternative(html_part, "text/html")
+
+ if files:
+ if type(files) != list:
+ files = [files,]
+
+ for file in files:
+ msg.attach_file(file)
+
+ return msg.send(fail_silently)
+
+
def send_multipart_mail(template_name, email_context, subject, recipients, sender=None, bcc=None, fail_silently=False, files=None):
"""
@@ -58,14 +91,96 @@ def send_multipart_mail(template_name, email_context, subject, recipients, sende
return msg.send(fail_silently)
-def normalise_to_100(data):
+def normalise_data(data, to=100):
"""
Used for normalising data prior to graphing with Google charting API
"""
max_value = max(data)
- if max_value > 100:
+ if max_value > to:
new_data = []
for d in data:
- new_data.append(int(d/float(max_value)*100))
+ new_data.append(int(d/float(max_value)*to))
data = new_data
return data
+
+chart_colours = ('80C65A', '990066', 'FF9900', '3399CC', 'BBCCED', '3399CC', 'FFCC33')
+
+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'.
+ chart_url = 'http://chart.apis.google.com/chart?cht=lc&chs=%sx90&chd=t:' % (len(column_headings)*65)
+ 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 '150px * number of months'.
+ chart_url = 'http://chart.apis.google.com/chart?cht=bvg&chs=%sx90&chd=t:' % (len(column_headings) * 150)
+ 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
diff --git a/models.py b/models.py
index e986cf03..b8141abe 100644
--- a/models.py
+++ b/models.py
@@ -94,11 +94,11 @@ class Ticket(models.Model):
)
PRIORITY_CHOICES = (
- (1, '1 (Critical)'),
- (2, '2 (High)'),
- (3, '3 (Normal)'),
- (4, '4 (Low)'),
- (5, '5 (Very Low)'),
+ (1, '1. Critical'),
+ (2, '2. High'),
+ (3, '3. Normal'),
+ (4, '4. Low'),
+ (5, '5. Very Low'),
)
title = models.CharField(maxlength=200)
@@ -342,3 +342,24 @@ class EscalationExclusion(models.Model):
def __unicode__(self):
return u'%s' % self.name
+class EmailTemplate(models.Model):
+ """
+ Since these are more likely to be changed than other templates, we store
+ them in the database.
+ """
+
+ template_name = models.CharField(maxlength=100, unique=True)
+
+ subject = models.CharField(maxlength=100, help_text=u'This will be prefixed with "[ticket.ticket] ticket.title". We recommend something simple such as "(Updated") or "(Closed)" - the same context is available as in plain_text, below.')
+ heading = models.CharField(maxlength=100, help_text=u'In HTML e-mails, this will be the heading at the top of the email - the same context is available as in plain_text, below.')
+ plain_text = models.TextField(help_text=u'The context available to you includes {{ ticket }}, {{ queue }}, and depending on the time of the call: {{ resolution }} or {{ comment }}.')
+ html = models.TextField(help_text=u'The same context is available here as in plain_text, above.')
+
+ class Admin:
+ pass
+
+ def __unicode__(self):
+ return u'%s' % self.template_name
+
+ class Meta:
+ ordering = ['template_name',]
diff --git a/scripts/escalate_tickets.py b/scripts/escalate_tickets.py
index 8f9cf625..dc96206e 100644
--- a/scripts/escalate_tickets.py
+++ b/scripts/escalate_tickets.py
@@ -10,7 +10,7 @@ scripts/escalate_tickets.py - Easy way to escalate tickets based on their age,
from datetime import datetime, timedelta, date
from django.db.models import Q
from helpdesk.models import Queue, Ticket, FollowUp, EscalationExclusion, TicketChange
-from helpdesk.lib import send_multipart_mail
+from helpdesk.lib import send_templated_mail
import sys, getopt
def escalate_tickets(queues, verbose):
@@ -48,13 +48,13 @@ def escalate_tickets(queues, verbose):
}
if t.submitter_email:
- send_multipart_mail('helpdesk/emails/submitter_escalated', context, '%s %s' % (t.ticket, t.title), t.submitter_email, t.queue.from_address)
+ send_templated_mail('escalated_submitter', context, recipients=t.submitter_email, sender=t.queue.from_address, fail_silently=True)
if t.queue.updated_ticket_cc:
- send_multipart_mail('helpdesk/emails/cc_escalated', context, '%s %s' % (t.ticket, t.title), t.queue.updated_ticket_cc, t.queue.from_address)
+ send_templated_mail('escalated_cc', context, recipients=t.queue.updated_ticket_cc, sender=t.queue.from_address, fail_silently=True)
if t.assigned_to:
- send_multipart_mail('helpdesk/emails/owner_escalated', context, '%s %s' % (t.ticket, t.title), t.assigned_to, t.queue.from_address)
+ send_templated_mail('escalated_owner', context, recipients=t.assigned_to.email, sender=t.queue.from_address, fail_silently=True)
if verbose:
print " - Esclating %s from %s>%s" % (t.ticket, t.priority+1, t.priority)
diff --git a/scripts/get_email.py b/scripts/get_email.py
index bbb57008..f8b35af4 100644
--- a/scripts/get_email.py
+++ b/scripts/get_email.py
@@ -15,7 +15,7 @@ from datetime import datetime, timedelta
import email, mimetypes, re
from email.Utils import parseaddr
from helpdesk.models import Queue, Ticket, FollowUp, Attachment
-from helpdesk.lib import send_multipart_mail
+from helpdesk.lib import send_templated_mail
def process_email():
for q in Queue.objects.filter(email_box_type__isnull=False):
@@ -138,22 +138,22 @@ def ticket_from_message(message, queue):
if new:
if sender_email:
- send_multipart_mail('helpdesk/emails/submitter_newticket', context, '%s %s' % (t.ticket, t.title), sender_email, queue.from_address, fail_silently=True)
+ send_templated_mail('newticket_submitter', context, recipients=sender_email, sender=queue.from_address, fail_silently=True)
if queue.new_ticket_cc:
- send_multipart_mail('helpdesk/emails/cc_newticket', context, '%s %s (Opened)' % (t.ticket, t.title), queue.updated_ticket_cc, queue.from_address, fail_silently=True)
+ send_templated_mail('newticket_cc', context, recipients=queue.new_ticket_cc, sender=queue.from_address, fail_silently=True)
if queue.updated_ticket_cc and queue.updated_ticket_cc != queue.new_ticket_cc:
- send_multipart_mail('helpdesk/emails/cc_newticket', context, '%s %s (Opened)' % (t.ticket, t.title), queue.updated_ticket_cc, queue.from_address, fail_silently=True)
+ send_templated_mail('newticket_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True)
else:
update = " (Updated)"
if t.assigned_to:
- send_multipart_mail('helpdesk/emails/owner_updated', context, '%s %s (Updated)' % (t.ticket, t.title), t.assigned_to.email, queue.from_address, file_silently=True)
+ send_templated_mail('updated_owner', context, recipients=t.assigned_to.email, sender=queue.from_address, fail_silently=True)
if queue.updated_ticket_cc:
- send_multipart_mail('helpdesk/emails/cc_updated', context, '%s %s (Updated)' % (t.ticket, t.title), queue.updated_ticket_cc, queue.from_address, fail_silently=True)
+ send_templated_mail('updated_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True)
f = FollowUp(
ticket = t,
diff --git a/templates/helpdesk/base.html b/templates/helpdesk/base.html
index bfb6623d..fb2866bf 100644
--- a/templates/helpdesk/base.html
+++ b/templates/helpdesk/base.html
@@ -12,9 +12,10 @@
Hello, This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} ({{ ticket.title }}) has been {% if ticket.assigned_to %}assigned to {{ ticket.assigned_to %}{% else %}unassigned{% endif %}. Hello, Ticket {{ ticket.title }} ("{{ ticket.title }}") has been closed. If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}. Hello, This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} ({{ ticket.title }}) has been escalated automatically. Hello, This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} ({{ ticket.title }}) has been opened. Hello, Ticket {{ ticket.ticket }} ({{ ticket.title }}) has been resolved. The following resolution was added: This resolution has been e-mailed to the submitter, who will verify it before you can close this ticket. If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}. Hello, Ticket {{ ticket.ticket }} ({{ ticket.title }}) has been updated. The following comment was added: This information has {% if private %}not {% endif %} been e-mailed to the submitter. If you wish to view this ticket online, you can visit {{ ticket.staff_url }}. Hello, This is a courtesy e-mail to let you know that a ticket has been assigned to you. {% if ticket.submitter_email %}The ticket was submitted by {{ ticket.submitter_email }}, and the subject{% else %}The ticket subject{% endif%} is {{ ticket.title }}. The ticket ID is {{ ticket.ticket }}. It's priority is {{ ticket.get_priority_display }}. Hello, A ticket currently assigned to you with a subject of {{ ticket.title }} has been closed. If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}. Hello, A ticket currently assigned to you with a subject of {{ ticket.title }} has been automatically escalated. Please review this ticket and attempt to provide a resolution as soon as possible. If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}. Hello, A ticket currently assigned to you with a subject of {{ ticket.title }} has had a resolution added. The following resolution was added to ticket {{ ticket.ticket }}: This resolution has been e-mailed to the submitter, who will verify it before you can close this ticket. If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}. Hello, Ticket {{ ticket.ticket }} ({{ ticket.title }}), which is assigned to you, has been updated. The following comment was added to ticket {{ ticket.ticket }}: This information has {% if private %}not {% endif %} been e-mailed to the submitter. If you wish to view this ticket online, you can visit {{ ticket.staff_url }}. Dear User, This is some content. Hello, You recently logged a ticket with a subject of {{ ticket.title }} with us. This e-mail is to confirm that this ticket has been closed. If you believe that further work is required on this ticket, please let us know by replying to this e-mail and keeping the subject intact. If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}. Hello, You recently logged a ticket with a subject of {{ ticket.title }} with us. This e-mail is to advise you of an automated escalation of that ticket. We will review your ticket shortly and attempt to provide a resolution as soon as possible. If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}. Hello, This is a courtesy e-mail to let you know that we have received your helpdesk query with a subject of {{ ticket.title }}. You do not have to do anything further at this stage. Your ticket has been assigned a number of {{ ticket.ticket }}. If you wish to send us further details, or if you have any queries about this ticket, please include the ticket id of {{ ticket.ticket }} in the subject. The easiest way to do this is just press 'reply' to this message. If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}. We will investigate your query and attempt to resolve it as soon as possible. You will receive further updates and a resolution via this e-mail address. Hello, You recently logged a ticket with a subject of {{ ticket.title }} with us. This e-mail is to advise you of a resolution to that ticket. The following resolution was added to ticket {{ ticket.ticket }}: Can you please confirm that this resolution addresses your needs so we may close this ticket? If you have any further queries, or if you do not believe this resolution is adequate, please reply to this e-mail and keep the subject intact. If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}. Hello, You recently logged a ticket with a subject of {{ ticket.title }} with us. This e-mail is to advise you of an update to that ticket. The following comment was added to ticket {{ ticket.ticket }}: To provide us with further information, please reply to this e-mail. If you wish to view this ticket online, you can visit {{ ticket.get_ticket_url }}.{{ resolution }}<\/blockquote>\n\n
{{ resolution }}<\/blockquote>\n\n
{{ resolution }}<\/blockquote>\n\n
{{ comment }}<\/blockquote>\n\n
{{ comment }}<\/blockquote>\n\n
{{ comment }}<\/blockquote>\n\n
{% for queue in dash_tickets %}
Queue Open Resolved
-
{% endfor %}
diff --git a/templates/helpdesk/emails/base.html b/templates/helpdesk/email_html_base.html
similarity index 100%
rename from templates/helpdesk/emails/base.html
rename to templates/helpdesk/email_html_base.html
diff --git a/templates/helpdesk/emails/text_footer.txt b/templates/helpdesk/email_text_footer.txt
similarity index 100%
rename from templates/helpdesk/emails/text_footer.txt
rename to templates/helpdesk/email_text_footer.txt
diff --git a/templates/helpdesk/emails/cc_assigned.html b/templates/helpdesk/emails/cc_assigned.html
deleted file mode 100644
index c0875b1b..00000000
--- a/templates/helpdesk/emails/cc_assigned.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% extends "helpdesk/emails/base.html" %}
-
-{% block header %}Ticket Re-Assigned{% endblock %}
-
-{% block content %}
-{{ queue.queue }}
-{% if queue.open %}{% endif %}{{ queue.open }}{% if queue.open %}{% endif %}
-{% if queue.resolved %}{% endif %}{{ queue.resolved }}{% if queue.resolved %}{% endif %}
+{{ queue.name }}
+{% if queue.open %}{% endif %}{{ queue.open }}{% if queue.open %}{% endif %}
+{% if queue.resolved %}{% endif %}{{ queue.resolved }}{% if queue.resolved %}{% endif %}
{{ resolution }}
-
-{{ comment }}
-
-{{ resolution }}
-
-{{ comment }}
-
-{{ resolution }}
-
-{{ comment }}
-
-Reports & Statistics
+
+
+
+
+{% endblock %}
diff --git a/templates/helpdesk/report_output.html b/templates/helpdesk/report_output.html
new file mode 100644
index 00000000..65424282
--- /dev/null
+++ b/templates/helpdesk/report_output.html
@@ -0,0 +1,15 @@
+{% extends "helpdesk/base.html" %}
+
+{% block helpdesk_title %}Reports & Statistics{% endblock %}
+
+{% block helpdesk_body %}
+
+
+
+
+
Reports & Statistics
+
+
+
+
+{% if chart %}{% endif %}
+
+{% endblock %}
diff --git a/urls.py b/urls.py
index 8905c016..099e48a2 100644
--- a/urls.py
+++ b/urls.py
@@ -59,6 +59,14 @@ urlpatterns = patterns('helpdesk.views',
url(r'^rss/$',
'rss_list',
name='helpdesk_rss_index'),
+
+ url(r'^reports/$',
+ 'report_index',
+ name='helpdesk_report_index'),
+
+ url(r'^reports/(?P{% for h in headings %}
+{% for d in data %}{{ h }} {% endfor %}{% for f in d %} {% endfor %}
+{{ f }} {% endfor %}