From 7dfb38eab9becd5a3658544a6562519f69d805d5 Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Tue, 1 Apr 2008 23:26:12 +0000 Subject: [PATCH] * Removed e-mail templates to the database * Added statistics views, making use of Google Charts * Added initial data fixture, to automatically create e-mail templates at install-time --- api.py | 22 +- fixtures/initial_data.json | 1 + forms.py | 18 +- htdocs/helpdesk.css | 31 ++- lib.py | 121 +++++++++- models.py | 31 ++- scripts/escalate_tickets.py | 8 +- scripts/get_email.py | 12 +- templates/helpdesk/base.html | 5 +- templates/helpdesk/dashboard.html | 6 +- .../base.html => email_html_base.html} | 0 .../text_footer.txt => email_text_footer.txt} | 0 templates/helpdesk/emails/cc_assigned.html | 10 - templates/helpdesk/emails/cc_assigned.txt | 7 - templates/helpdesk/emails/cc_closed.html | 12 - templates/helpdesk/emails/cc_closed.txt | 7 - templates/helpdesk/emails/cc_escalated.html | 10 - templates/helpdesk/emails/cc_escalated.txt | 7 - templates/helpdesk/emails/cc_newticket.html | 10 - templates/helpdesk/emails/cc_newticket.txt | 7 - templates/helpdesk/emails/cc_resolved.html | 18 -- templates/helpdesk/emails/cc_resolved.txt | 13 - templates/helpdesk/emails/cc_updated.html | 18 -- templates/helpdesk/emails/cc_updated.txt | 13 - templates/helpdesk/emails/owner_assigned.html | 15 -- templates/helpdesk/emails/owner_assigned.txt | 9 - templates/helpdesk/emails/owner_closed.html | 12 - templates/helpdesk/emails/owner_closed.txt | 7 - .../helpdesk/emails/owner_escalated.html | 14 -- templates/helpdesk/emails/owner_escalated.txt | 9 - templates/helpdesk/emails/owner_resolved.html | 18 -- templates/helpdesk/emails/owner_resolved.txt | 13 - templates/helpdesk/emails/owner_updated.html | 18 -- templates/helpdesk/emails/owner_updated.txt | 13 - templates/helpdesk/emails/sample.html | 9 - .../helpdesk/emails/submitter_closed.html | 14 -- .../helpdesk/emails/submitter_closed.txt | 9 - .../helpdesk/emails/submitter_escalated.html | 14 -- .../helpdesk/emails/submitter_escalated.txt | 9 - .../helpdesk/emails/submitter_newticket.html | 19 -- .../helpdesk/emails/submitter_newticket.txt | 13 - .../helpdesk/emails/submitter_resolved.html | 18 -- .../helpdesk/emails/submitter_resolved.txt | 13 - .../helpdesk/emails/submitter_updated.html | 18 -- .../helpdesk/emails/submitter_updated.txt | 13 - templates/helpdesk/report_index.html | 27 +++ templates/helpdesk/report_output.html | 15 ++ urls.py | 9 + views.py | 227 ++++++++++++++---- 49 files changed, 443 insertions(+), 499 deletions(-) create mode 100644 fixtures/initial_data.json rename templates/helpdesk/{emails/base.html => email_html_base.html} (100%) rename templates/helpdesk/{emails/text_footer.txt => email_text_footer.txt} (100%) delete mode 100644 templates/helpdesk/emails/cc_assigned.html delete mode 100644 templates/helpdesk/emails/cc_assigned.txt delete mode 100644 templates/helpdesk/emails/cc_closed.html delete mode 100644 templates/helpdesk/emails/cc_closed.txt delete mode 100644 templates/helpdesk/emails/cc_escalated.html delete mode 100644 templates/helpdesk/emails/cc_escalated.txt delete mode 100644 templates/helpdesk/emails/cc_newticket.html delete mode 100644 templates/helpdesk/emails/cc_newticket.txt delete mode 100644 templates/helpdesk/emails/cc_resolved.html delete mode 100644 templates/helpdesk/emails/cc_resolved.txt delete mode 100644 templates/helpdesk/emails/cc_updated.html delete mode 100644 templates/helpdesk/emails/cc_updated.txt delete mode 100644 templates/helpdesk/emails/owner_assigned.html delete mode 100644 templates/helpdesk/emails/owner_assigned.txt delete mode 100644 templates/helpdesk/emails/owner_closed.html delete mode 100644 templates/helpdesk/emails/owner_closed.txt delete mode 100644 templates/helpdesk/emails/owner_escalated.html delete mode 100644 templates/helpdesk/emails/owner_escalated.txt delete mode 100644 templates/helpdesk/emails/owner_resolved.html delete mode 100644 templates/helpdesk/emails/owner_resolved.txt delete mode 100644 templates/helpdesk/emails/owner_updated.html delete mode 100644 templates/helpdesk/emails/owner_updated.txt delete mode 100644 templates/helpdesk/emails/sample.html delete mode 100644 templates/helpdesk/emails/submitter_closed.html delete mode 100644 templates/helpdesk/emails/submitter_closed.txt delete mode 100644 templates/helpdesk/emails/submitter_escalated.html delete mode 100644 templates/helpdesk/emails/submitter_escalated.txt delete mode 100644 templates/helpdesk/emails/submitter_newticket.html delete mode 100644 templates/helpdesk/emails/submitter_newticket.txt delete mode 100644 templates/helpdesk/emails/submitter_resolved.html delete mode 100644 templates/helpdesk/emails/submitter_resolved.txt delete mode 100644 templates/helpdesk/emails/submitter_updated.html delete mode 100644 templates/helpdesk/emails/submitter_updated.txt create mode 100644 templates/helpdesk/report_index.html create mode 100644 templates/helpdesk/report_output.html 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

{{ resolution }}<\/blockquote>\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

{{ resolution }}<\/blockquote>\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

{{ resolution }}<\/blockquote>\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

{{ comment }}<\/blockquote>\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

{{ comment }}<\/blockquote>\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

{{ comment }}<\/blockquote>\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 @@

diff --git a/templates/helpdesk/dashboard.html b/templates/helpdesk/dashboard.html index e4041784..b6dfd476 100644 --- a/templates/helpdesk/dashboard.html +++ b/templates/helpdesk/dashboard.html @@ -9,9 +9,9 @@ QueueOpenResolved {% for queue in dash_tickets %} -{{ 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 %} {% 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 %} -

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 %}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/cc_assigned.txt b/templates/helpdesk/emails/cc_assigned.txt deleted file mode 100644 index f4bcb2c3..00000000 --- a/templates/helpdesk/emails/cc_assigned.txt +++ /dev/null @@ -1,7 +0,0 @@ -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 %} - -You can view this online at {{ ticket.staff_url }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/cc_closed.html b/templates/helpdesk/emails/cc_closed.html deleted file mode 100644 index 6a234c56..00000000 --- a/templates/helpdesk/emails/cc_closed.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Closed{% endblock %} - -{% block content %} -

Hello,

- -

Ticket {{ ticket.title }} ("{{ ticket.title }}") has been closed.

- -

If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/cc_closed.txt b/templates/helpdesk/emails/cc_closed.txt deleted file mode 100644 index 2c2a3145..00000000 --- a/templates/helpdesk/emails/cc_closed.txt +++ /dev/null @@ -1,7 +0,0 @@ -Hello, - -Ticket {{ ticket.title }} ("{{ ticket.title }}") has been closed. - -If you wish to view this ticket online, you can visit {{ ticket.get_staff_url }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/cc_escalated.html b/templates/helpdesk/emails/cc_escalated.html deleted file mode 100644 index 53681949..00000000 --- a/templates/helpdesk/emails/cc_escalated.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Escalated{% endblock %} - -{% block content %} -

Hello,

- -

This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} ({{ ticket.title }}) has been escalated automatically.

- -{% endblock %} diff --git a/templates/helpdesk/emails/cc_escalated.txt b/templates/helpdesk/emails/cc_escalated.txt deleted file mode 100644 index e8a374c4..00000000 --- a/templates/helpdesk/emails/cc_escalated.txt +++ /dev/null @@ -1,7 +0,0 @@ -Hello, - -This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} ("{{ ticket.title }}") has been escalated automatically. - -You can view this online at {{ ticket.staff_url }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/cc_newticket.html b/templates/helpdesk/emails/cc_newticket.html deleted file mode 100644 index 56b098d8..00000000 --- a/templates/helpdesk/emails/cc_newticket.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Re-Assigned{% endblock %} - -{% block content %} -

Hello,

- -

This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} ({{ ticket.title }}) has been opened.

- -{% endblock %} diff --git a/templates/helpdesk/emails/cc_newticket.txt b/templates/helpdesk/emails/cc_newticket.txt deleted file mode 100644 index ff692d3f..00000000 --- a/templates/helpdesk/emails/cc_newticket.txt +++ /dev/null @@ -1,7 +0,0 @@ -Hello, - -This is a courtesy e-mail to let you know that ticket {{ ticket.ticket }} ("{{ ticket.title }}") has been opened. - -You can view this online at {{ ticket.staff_url }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/cc_resolved.html b/templates/helpdesk/emails/cc_resolved.html deleted file mode 100644 index 7a29a2a1..00000000 --- a/templates/helpdesk/emails/cc_resolved.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Resolution{% endblock %} - -{% block content %} -

Hello,

- -

Ticket {{ ticket.ticket }} ({{ ticket.title }}) has been resolved.

- -

The following resolution was added:

- -
{{ resolution }}
- -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/cc_resolved.txt b/templates/helpdesk/emails/cc_resolved.txt deleted file mode 100644 index 923f5092..00000000 --- a/templates/helpdesk/emails/cc_resolved.txt +++ /dev/null @@ -1,13 +0,0 @@ -Hello, - -Ticket {{ ticket.ticket }} ({{ ticket.title }}) has been resolved. - -The following resolution was added: - -{{ resolution }} - -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/cc_updated.html b/templates/helpdesk/emails/cc_updated.html deleted file mode 100644 index f1b7bc25..00000000 --- a/templates/helpdesk/emails/cc_updated.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Updated{% endblock %} - -{% block content %} -

Hello,

- -

Ticket {{ ticket.ticket }} ({{ ticket.title }}) has been updated.

- -

The following comment was added:

- -
{{ comment }}
- -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/cc_updated.txt b/templates/helpdesk/emails/cc_updated.txt deleted file mode 100644 index 1b696654..00000000 --- a/templates/helpdesk/emails/cc_updated.txt +++ /dev/null @@ -1,13 +0,0 @@ -Hello, - -Ticket {{ ticket.ticket }} ({{ ticket.title }}) has been updated. - -The following comment was added: - -{{ comment }} - -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/owner_assigned.html b/templates/helpdesk/emails/owner_assigned.html deleted file mode 100644 index 1e3f6836..00000000 --- a/templates/helpdesk/emails/owner_assigned.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Assigned To You{% endblock %} - -{% block content %} -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/owner_assigned.txt b/templates/helpdesk/emails/owner_assigned.txt deleted file mode 100644 index 92630b4e..00000000 --- a/templates/helpdesk/emails/owner_assigned.txt +++ /dev/null @@ -1,9 +0,0 @@ -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 }}, you can view it online at {{ ticket.staff_url }}. It's priority is {{ ticket.get_priority_display }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/owner_closed.html b/templates/helpdesk/emails/owner_closed.html deleted file mode 100644 index f034c882..00000000 --- a/templates/helpdesk/emails/owner_closed.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Closed{% endblock %} - -{% block content %} -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/owner_closed.txt b/templates/helpdesk/emails/owner_closed.txt deleted file mode 100644 index 271b2f79..00000000 --- a/templates/helpdesk/emails/owner_closed.txt +++ /dev/null @@ -1,7 +0,0 @@ -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/owner_escalated.html b/templates/helpdesk/emails/owner_escalated.html deleted file mode 100644 index ce79ea9e..00000000 --- a/templates/helpdesk/emails/owner_escalated.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Escalated{% endblock %} - -{% block content %} -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/owner_escalated.txt b/templates/helpdesk/emails/owner_escalated.txt deleted file mode 100644 index 6190adb3..00000000 --- a/templates/helpdesk/emails/owner_escalated.txt +++ /dev/null @@ -1,9 +0,0 @@ -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/owner_resolved.html b/templates/helpdesk/emails/owner_resolved.html deleted file mode 100644 index 52a3b830..00000000 --- a/templates/helpdesk/emails/owner_resolved.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Resolution{% endblock %} - -{% block content %} -

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 }}:

- -
{{ resolution }}
- -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/owner_resolved.txt b/templates/helpdesk/emails/owner_resolved.txt deleted file mode 100644 index d8530031..00000000 --- a/templates/helpdesk/emails/owner_resolved.txt +++ /dev/null @@ -1,13 +0,0 @@ -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 }}: - -{{ resolution }} - -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/owner_updated.html b/templates/helpdesk/emails/owner_updated.html deleted file mode 100644 index bc350217..00000000 --- a/templates/helpdesk/emails/owner_updated.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Ticket Updated{% endblock %} - -{% block content %} -

Hello,

- -

Ticket {{ ticket.ticket }} ({{ ticket.title }}), which is assigned to you, has been updated.

- -

The following comment was added to ticket {{ ticket.ticket }}:

- -
{{ comment }}
- -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/owner_updated.txt b/templates/helpdesk/emails/owner_updated.txt deleted file mode 100644 index ce5f4657..00000000 --- a/templates/helpdesk/emails/owner_updated.txt +++ /dev/null @@ -1,13 +0,0 @@ -Hello, - -Ticket {{ ticket.ticket }} ({{ ticket.title }}), which is assigned to you, has been updated. - -The following comment was added to ticket {{ ticket.ticket }}: - -{{ comment }} - -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/sample.html b/templates/helpdesk/emails/sample.html deleted file mode 100644 index 25d86adc..00000000 --- a/templates/helpdesk/emails/sample.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}E-Mail Heading Here{% endblock %} - -{% block content %} -

Dear User,

- -

This is some content.

-{% endblock %} diff --git a/templates/helpdesk/emails/submitter_closed.html b/templates/helpdesk/emails/submitter_closed.html deleted file mode 100644 index 234d5e17..00000000 --- a/templates/helpdesk/emails/submitter_closed.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Your Ticket Has Been Closed{% endblock %} - -{% block 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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/submitter_closed.txt b/templates/helpdesk/emails/submitter_closed.txt deleted file mode 100644 index 2c44779c..00000000 --- a/templates/helpdesk/emails/submitter_closed.txt +++ /dev/null @@ -1,9 +0,0 @@ -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/submitter_escalated.html b/templates/helpdesk/emails/submitter_escalated.html deleted file mode 100644 index 7762a1e0..00000000 --- a/templates/helpdesk/emails/submitter_escalated.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Your Ticket Has Been Escalated{% endblock %} - -{% block content %} -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/submitter_escalated.txt b/templates/helpdesk/emails/submitter_escalated.txt deleted file mode 100644 index b408678f..00000000 --- a/templates/helpdesk/emails/submitter_escalated.txt +++ /dev/null @@ -1,9 +0,0 @@ -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/submitter_newticket.html b/templates/helpdesk/emails/submitter_newticket.html deleted file mode 100644 index 16738675..00000000 --- a/templates/helpdesk/emails/submitter_newticket.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Thank You For Your Submission{% endblock %} - -{% block content %} -

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.

- -{% endblock %} diff --git a/templates/helpdesk/emails/submitter_newticket.txt b/templates/helpdesk/emails/submitter_newticket.txt deleted file mode 100644 index 7045a513..00000000 --- a/templates/helpdesk/emails/submitter_newticket.txt +++ /dev/null @@ -1,13 +0,0 @@ -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. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/submitter_resolved.html b/templates/helpdesk/emails/submitter_resolved.html deleted file mode 100644 index 6d108d0a..00000000 --- a/templates/helpdesk/emails/submitter_resolved.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Your Ticket Has Been Resolved{% endblock %} - -{% block content %} -

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 }}:

- -
{{ resolution }}
- -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/submitter_resolved.txt b/templates/helpdesk/emails/submitter_resolved.txt deleted file mode 100644 index f0adf404..00000000 --- a/templates/helpdesk/emails/submitter_resolved.txt +++ /dev/null @@ -1,13 +0,0 @@ -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 }}: - -{{ resolution }} - -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/emails/submitter_updated.html b/templates/helpdesk/emails/submitter_updated.html deleted file mode 100644 index 44a20c04..00000000 --- a/templates/helpdesk/emails/submitter_updated.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "helpdesk/emails/base.html" %} - -{% block header %}Your Ticket Has Been Updated{% endblock %} - -{% block content %} -

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 }}:

- -
{{ comment }}
- -

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 }}.

- -{% endblock %} diff --git a/templates/helpdesk/emails/submitter_updated.txt b/templates/helpdesk/emails/submitter_updated.txt deleted file mode 100644 index 04a021c1..00000000 --- a/templates/helpdesk/emails/submitter_updated.txt +++ /dev/null @@ -1,13 +0,0 @@ -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 }}: - -{{ comment }} - -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 }}. - -{% include "helpdesk/emails/text_footer.txt" %} diff --git a/templates/helpdesk/report_index.html b/templates/helpdesk/report_index.html new file mode 100644 index 00000000..82c2f033 --- /dev/null +++ b/templates/helpdesk/report_index.html @@ -0,0 +1,27 @@ +{% extends "helpdesk/base.html" %} + +{% block helpdesk_title %}Reports & Statistics{% endblock %} + +{% block helpdesk_body %} +

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

+ + +{% for h in headings %}{% endfor %} +{% for d in data %}{% for f in d %}{% endfor %}{% endfor %} +
{{ h }}
{{ f }}
+ +{% 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\w+)/$', + 'run_report', + name='helpdesk_run_report'), ) urlpatterns += patterns('', @@ -67,6 +75,7 @@ urlpatterns += patterns('', {'feed_dict': feed_setup}, name='helpdesk_rss'), ) + urlpatterns += patterns('', url(r'^api/(?P[a-z_-]+)/$', 'helpdesk.api.api', diff --git a/views.py b/views.py index f4abe051..3a412015 100644 --- a/views.py +++ b/views.py @@ -17,7 +17,7 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.template import loader, Context, RequestContext from helpdesk.forms import TicketForm, PublicTicketForm -from helpdesk.lib import send_multipart_mail +from helpdesk.lib import send_templated_mail, line_chart, bar_chart from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment def dashboard(request): @@ -29,15 +29,22 @@ def dashboard(request): if request.user.is_authenticated(): tickets = Ticket.objects.filter(assigned_to=request.user).exclude(status=Ticket.CLOSED_STATUS) unassigned_tickets = Ticket.objects.filter(assigned_to__isnull=True).exclude(status=Ticket.CLOSED_STATUS) - - dash_tickets = [] - for q in Queue.objects.all(): - dash_tickets.append({ - 'queue': q, - 'open': q.ticket_set.filter(Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)).count(), - 'resolved': q.ticket_set.filter(status=Ticket.RESOLVED_STATUS).count(), - }) + from django.db import connection + cursor = connection.cursor() + cursor.execute(""" + SELECT q.id as queue, + q.title AS name, + COUNT(CASE t.status WHEN '1' THEN t.id WHEN '2' THEN t.id END) AS open, + COUNT(CASE t.status WHEN '3' THEN t.id END) AS resolved + FROM helpdesk_ticket t, + helpdesk_queue q + WHERE q.id = t.queue_id + GROUP BY queue, name + ORDER BY q.id; + """) + dash_tickets = cursor.dictfetchall() + return render_to_response('helpdesk/dashboard.html', RequestContext(request, { 'user_tickets': tickets, @@ -164,50 +171,38 @@ def update_ticket(request, ticket_id): 'comment': f.comment, } if f.new_status == Ticket.RESOLVED_STATUS: - template = 'helpdesk/emails/submitter_resolved' - subject = '%s %s (Resolved)' % (ticket.ticket, ticket.title) + template = 'resolved_submitter' elif f.new_status == Ticket.CLOSED_STATUS: - template = 'helpdesk/emails/submitter_closed' - subject = '%s %s (Closed)' % (ticket.ticket, ticket.title) + template = 'closed_submitter' else: - template = 'helpdesk/emails/submitter_updated' - subject = '%s %s (Updated)' % (ticket.ticket, ticket.title) - send_multipart_mail(template, context, subject, ticket.submitter_email, ticket.queue.from_address, fail_silently=True) + template = 'updated_submitter' + send_templated_mail(template, context, recipients=ticket.submitter_email, sender=ticket.queue.from_address, fail_silently=True) if ticket.assigned_to and request.user != ticket.assigned_to and ticket.assigned_to.email: # We only send e-mails to staff members if the ticket is updated by # another user. if reassigned: - template_staff = 'helpdesk/emails/owner_assigned' - subject = '%s %s (Assigned)' % (ticket.ticket, ticket.title) + template_staff = 'assigned_owner' elif f.new_status == Ticket.RESOLVED_STATUS: - template_staff = 'helpdesk/emails/owner_resolved' - subject = '%s %s (Resolved)' % (ticket.ticket, ticket.title) + template_staff = 'resolved_owner' elif f.new_status == Ticket.CLOSED_STATUS: - template_staff = 'helpdesk/emails/owner_closed' - subject = '%s %s (Closed)' % (ticket.ticket, ticket.title) + template_staff = 'closed_owner' else: - template_staff = 'helpdesk/emails/owner_updated' - subject = '%s %s (Updated)' % (ticket.ticket, ticket.title) + template_staff = 'updated_owner' - send_multipart_mail(template_staff, context, subject, ticket.assigned_to.email, ticket.queue.from_address, fail_silently=True) - + send_templated_mail(template_staff, context, recipients=ticket.assigned_to.email, sender=ticket.queue.from_address, fail_silently=True) - if ticket.queue.updated_ticket_cc: - if reassigned: - template_cc = 'helpdesk/emails/cc_assigned' - subject = '%s %s (Assigned)' % (ticket.ticket, ticket.title) - elif f.new_status == Ticket.RESOLVED_STATUS: - template_cc = 'helpdesk/emails/cc_resolved' - subject = '%s %s (Resolved)' % (ticket.ticket, ticket.title) - elif f.new_status == Ticket.CLOSED_STATUS: - template_cc = 'helpdesk/emails/cc_closed' - subject = '%s %s (Closed)' % (ticket.ticket, ticket.title) - else: - template_cc = 'helpdesk/emails/cc_updated' - subject = '%s %s (Updated)' % (ticket.ticket, ticket.title) - - send_multipart_mail(template_cc, context, subject, ticket.queue.updated_ticket_cc, ticket.queue.from_address, fail_silently=True) + if ticket.queue.updated_ticket_cc: + if reassigned: + template_cc = 'assigned_cc' + elif f.new_status == Ticket.RESOLVED_STATUS: + template_cc = 'resolved_cc' + elif f.new_status == Ticket.CLOSED_STATUS: + template_cc = 'closed_cc' + else: + template_cc = 'updated_cc' + + send_templated_mail(template_cc, context, recipients=ticket.queue.updated_ticket_cc, sender=ticket.queue.from_address, fail_silently=True) if request.FILES: for file in request.FILES.getlist('attachment'): @@ -363,3 +358,153 @@ def rss_list(request): 'queues': Queue.objects.all(), })) rss_list = login_required(rss_list) + +def report_index(request): + return render_to_response('helpdesk/report_index.html', + RequestContext(request, {})) +report_index = login_required(report_index) + +def run_report(request, report): + 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])) + priority_columns.append("%s" % p[1]) + 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])) + status_columns.append("%s" % s[1]) + status_sql = ", ".join(status_sql) + + 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) + + month_sql = [] + months = ( + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ) + month_columns = [] + + first_ticket = Ticket.objects.all().order_by('created')[0] + first_month = first_ticket.created.month + first_year = first_ticket.created.year + + last_ticket = Ticket.objects.all().order_by('-created')[0] + last_month = last_ticket.created.month + last_year = last_ticket.created.year + + periods = [] + year, month = first_year, first_month + working = True + + while working: + periods.append((year, month)) + month += 1 + if month > 12: + year += 1 + month = 1 + if month > last_month and year >= last_year: + working = False + + + + for (year, month) in periods: + sqlmonth = '%s-%s' % (year, months[month-1]) + desc = '%s %s' % (months[month-1], year) + month_sql.append("COUNT(CASE to_char(t.created, 'YYYY-Mon') WHEN '%s' THEN t.id END) AS \"%s\"" % (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; + """ + + if report == 'userpriority': + sql = user_base_sql % priority_sql + columns = ['username'] + priority_columns + + elif report == 'userqueue': + sql = user_base_sql % queue_sql + columns = ['username'] + queue_columns + + elif report == 'userstatus': + sql = user_base_sql % status_sql + columns = ['username'] + status_columns + + elif report == 'usermonth': + sql = user_base_sql % month_sql + columns = ['username'] + month_columns + + elif report == 'queuepriority': + sql = queue_base_sql % priority_sql + columns = ['queue'] + priority_columns + + elif report == 'queuestatus': + sql = queue_base_sql % status_sql + columns = ['queue'] + status_columns + + elif report == 'queuemonth': + sql = queue_base_sql % month_sql + columns = ['queue'] + month_columns + + + from django.db import connection + cursor = connection.cursor() + cursor.execute(sql) + report_output = cursor.dictfetchall() + + data = [] + + for record in report_output: + line = [] + for c in columns: + line.append(record[c]) + data.append(line) + + if report in ('queuemonth', 'usermonth'): + chart_url = line_chart([columns] + data) + elif report in ('queuestatus', 'queuepriority', 'userstatus', 'userpriority'): + chart_url = bar_chart([columns] + data) + else: + chart_url = '' + + return render_to_response('helpdesk/report_output.html', + RequestContext(request, { + 'headings': columns, + 'data': data, + 'sql': sql, + 'chart': chart_url, + })) +run_report = login_required(run_report)