From cea6394b7087452e9bf33e11aa1c1abe480e711e Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Mon, 18 Aug 2008 21:29:31 +0000 Subject: [PATCH] Big bugfix release - addresses a number of issues introduced in recent Django updates, and other bugs in the codebase. Many thanks to David Clymer and Chris Etcp for reporting these bugs and then providing fixes. Tickets closed: #3: BUG E-Mail Script Incompatible with Python 2.5 #4: BUG Failure on empty attachments #5: ENHANCEMENT Run scripts as command extensions [Backwards Compatible] #7: BUG Cannot view tickets when not logged in #8: BUG Overly broad error handling Note that #5 is backwards-incompatible, as you need to change any CRON or scheduler entries for the 'get_email.py', 'escalate_tickets.py' or 'create_escalation_exclusions.py' scripts. See the README file for the new commands. --- README | 22 ++++++-- forms.py | 2 +- {scripts => management}/__init__.py | 0 management/commands/__init__.py | 0 .../commands}/create_escalation_exclusions.py | 54 ++++++++++++++++++- .../commands}/escalate_tickets.py | 43 ++++++++++++++- {scripts => management/commands}/get_email.py | 12 +++-- templates/helpdesk/public_view_ticket.html | 2 +- templatetags/ticket_to_link.py | 2 +- views/api.py | 12 ++--- views/public.py | 2 +- 11 files changed, 132 insertions(+), 19 deletions(-) rename {scripts => management}/__init__.py (100%) create mode 100644 management/commands/__init__.py rename {scripts => management/commands}/create_escalation_exclusions.py (61%) rename {scripts => management/commands}/escalate_tickets.py (75%) rename {scripts => management/commands}/get_email.py (95%) diff --git a/README b/README index 17e2bf1f..7b98cfe8 100644 --- a/README +++ b/README @@ -11,6 +11,7 @@ Jutda Helpdesk - A Django powered ticket tracker for small enterprise. 3. Installation 4. Initial Configuration 5. API Usage +6. Thank You ######################### 1. Licensing @@ -104,7 +105,7 @@ LICENSE.JQUERY and LICENSE.NICEDIT for their respective license terms. Don't forget to set the relevant Django environment variables in your crontab: - */5 * * * * DJANGO_SETTINGS_MODULE='myproject.settings' python /path/to/helpdesk/scripts/get_email.py + */5 * * * * /path/to/helpdesksite/manage.py get_email This will run the e-mail import every 5 minutes @@ -114,7 +115,7 @@ LICENSE.JQUERY and LICENSE.NICEDIT for their respective license terms. 4. If you wish to automatically escalate tickets based on their age, set up a cronjob to run scripts/escalate_tickets.py on a regular basis: - 0 * * * * DJANGO_SETTINGS_MODULE='myproject.settings' python /path/to/helpdesk/scripts/escalate_tickets.py + 0 * * * * /path/to/helpdesksite/manage.py escalate_tickets.py This will run the escalation process hourly, using the 'Escalation Hours' setting for each queue to determine which tickets to escalate. @@ -123,7 +124,7 @@ LICENSE.JQUERY and LICENSE.NICEDIT for their respective license terms. the dates manually via the Admin, or setup a cronjob to run scripts/create_escalation_exclusions.py on a regular basis: - 0 0 * * 0 DJANGO_SETTINGS_MODULE='myproject.settings' python /path/to/helpdesk/scripts/create_escalation_exclusions.py --days saturday,sunday --verbose + 0 0 * * 0 /path/to/helpdesksite/manage.py create_escalation_exclusions.py --days saturday,sunday --verbose This will, on a weekly basis, create exclusions for the coming weekend. @@ -138,3 +139,18 @@ you to create and alter tickets from 3rd party software and systems. For usage instructions and command syntax, see the file templates/helpdesk/api_help.html, or visit http://helpdesk/api/help/. + +######################### +6. Thank You +######################### + +While this started as a project to suit my own needs, since publishing the +code a number of people have made some fantastic improvements and provided +bug fixes and updates as the Django codebase has moved on and caused small +portions of this application to break. + +To these people, my sincere thanks: + +David Clymer +Chris Etcp +Nikolay Panov diff --git a/forms.py b/forms.py index d87e5539..73f40a4f 100644 --- a/forms.py +++ b/forms.py @@ -57,7 +57,7 @@ class TicketForm(forms.Form): try: u = User.objects.get(id=self.cleaned_data['assigned_to']) t.assigned_to = u - except: + except User.DoesNotExist: t.assigned_to = None t.save() diff --git a/scripts/__init__.py b/management/__init__.py similarity index 100% rename from scripts/__init__.py rename to management/__init__.py diff --git a/management/commands/__init__.py b/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scripts/create_escalation_exclusions.py b/management/commands/create_escalation_exclusions.py similarity index 61% rename from scripts/create_escalation_exclusions.py rename to management/commands/create_escalation_exclusions.py index 27e16bda..e497e15a 100644 --- a/scripts/create_escalation_exclusions.py +++ b/management/commands/create_escalation_exclusions.py @@ -13,6 +13,58 @@ from django.db.models import Q from helpdesk.models import EscalationExclusion, Queue import sys, getopt +from django.core.management.base import BaseCommand, CommandError +from optparse import make_option + +class Command(BaseCommand): + def __init__(self): + BaseCommand.__init__(self) + + self.option_list += ( + make_option( + '--days', '-d', + help='Days of week (monday, tuesday, etc)'), + make_option( + '--occurrences', '-o', + type='int', + default=1, + help='Occurrences: How many weeks ahead to exclude this day'), + make_option( + '--queues', '-q', + help='Queues to include (default: all). Use queue slugs'), + make_option( + '--verbose', '-v', + action='store_true', + default=False, + help='Display a list of dates excluded'), + ) + + def handle(self, *args, **options): + days = options['days'] + occurrences = options['occurrences'] + verbose = False + queue_slugs = options['queues'] + queues = [] + + if options['verbose']: + verbose = True + + # this should already be handled by optparse + if not occurrences: occurrences = 1 + if not (days and occurrences): + raise CommandError('One or more occurrences must be specified.') + + if queue_slugs is not None: + queue_set = queue_slugs.split(',') + for queue in queue_set: + try: + q = Queue.objects.get(slug__exact=queue) + except Queue.DoesNotExist: + raise CommandError("Queue %s does not exist." % queue) + queues.append(q) + + create_exclusions(days=days, occurrences=occurrences, verbose=verbose, queues=queues) + day_names = { 'monday': 0, 'tuesday': 1, @@ -88,7 +140,7 @@ if __name__ == '__main__': for queue in queue_set: try: q = Queue.objects.get(slug__exact=queue) - except: + except Queue.DoesNotExist: print "Queue %s does not exist." % queue sys.exit(2) queues.append(q) diff --git a/scripts/escalate_tickets.py b/management/commands/escalate_tickets.py similarity index 75% rename from scripts/escalate_tickets.py rename to management/commands/escalate_tickets.py index e4970f7f..885926c6 100644 --- a/scripts/escalate_tickets.py +++ b/management/commands/escalate_tickets.py @@ -15,6 +15,45 @@ from django.utils.translation import ugettext as _ from helpdesk.models import Queue, Ticket, FollowUp, EscalationExclusion, TicketChange from helpdesk.lib import send_templated_mail + +from django.core.management.base import BaseCommand +from optparse import make_option + +class Command(BaseCommand): + def __init__(self): + BaseCommand.__init__(self) + + self.option_list += ( + make_option( + '--queues', '-q', + help='Queues to include (default: all). Use queue slugs'), + make_option( + '--verbose', '-v', + action='store_true', + default=False, + help='Display a list of dates excluded'), + ) + + def handle(self, *args, **options): + verbose = False + queue_slugs = None + queues = [] + + if options['verbose']: + verbose = True + if options['queues']: + queue_slugs = options['queues'] + + if queue_slugs is not None: + queue_set = queue_slugs.split(',') + for queue in queue_set: + try: + q = Queue.objects.get(slug__exact=queue) + except Queue.DoesNotExist: + raise CommandError("Queue %s does not exist." % queue) + queues.append(queue) + + escalate_tickets(queues=queues, verbose=verbose) def escalate_tickets(queues, verbose): """ Only include queues with escalation configured """ @@ -47,7 +86,7 @@ def escalate_tickets(queues, verbose): context = { 'ticket': t, - 'queue': queue, + 'queue': q, } if t.submitter_email: @@ -106,7 +145,7 @@ if __name__ == '__main__': for queue in queue_set: try: q = Queue.objects.get(slug__exact=queue) - except: + except Queue.DoesNotExist: print "Queue %s does not exist." % queue sys.exit(2) queues.append(queue) diff --git a/scripts/get_email.py b/management/commands/get_email.py similarity index 95% rename from scripts/get_email.py rename to management/commands/get_email.py index 94147dc0..a457d3e8 100644 --- a/scripts/get_email.py +++ b/management/commands/get_email.py @@ -19,6 +19,12 @@ from helpdesk.lib import send_templated_mail from django.core.files.base import ContentFile +from django.core.management.base import BaseCommand + +class Command(BaseCommand): + def handle(self, *args, **options): + process_email() + def process_email(): for q in Queue.objects.filter(email_box_type__isnull=False, allow_email_submission=True): if not q.email_box_last_check: q.email_box_last_check = datetime.now()-timedelta(minutes=30) @@ -89,7 +95,7 @@ def ticket_from_message(message, queue): counter = 0 files = [] for part in message.walk(): - if part.get_main_type() == 'multipart': + if part.get_content_maintype() == 'multipart': continue name = part.get_param("name") @@ -97,7 +103,7 @@ def ticket_from_message(message, queue): if part.get_content_maintype() == 'text' and name == None: body = part.get_payload() else: - if name == None: + if not name: ext = mimetypes.guess_extension(part.get_content_type()) name = "part-%i%s" % (counter, ext) files.append({'filename': name, 'content': part.get_payload(decode=True), 'type': part.get_content_type()}) @@ -110,7 +116,7 @@ def ticket_from_message(message, queue): try: t = Ticket.objects.get(id=ticket) new = False - except: + except Ticket.DoesNotExist: ticket = None priority = 3 diff --git a/templates/helpdesk/public_view_ticket.html b/templates/helpdesk/public_view_ticket.html index b5f91457..b9fea7c6 100644 --- a/templates/helpdesk/public_view_ticket.html +++ b/templates/helpdesk/public_view_ticket.html @@ -13,7 +13,7 @@ - + diff --git a/templatetags/ticket_to_link.py b/templatetags/ticket_to_link.py index 0211e74d..403118a2 100644 --- a/templatetags/ticket_to_link.py +++ b/templatetags/ticket_to_link.py @@ -41,7 +41,7 @@ def num_to_link(text): url = reverse('helpdesk_view', args=[number]) try: ticket = Ticket.objects.get(id=number) - except: + except Ticket.DoesNotExist: ticket = None if ticket: diff --git a/views/api.py b/views/api.py index 243fcc85..34e7d602 100644 --- a/views/api.py +++ b/views/api.py @@ -107,7 +107,7 @@ class API: try: u = User.objects.get(username=username) return api_return(STATUS_OK, "%s" % u.id) - except: + except User.DoesNotExist: return api_return(STATUS_ERROR, "Invalid username provided") @@ -117,7 +117,7 @@ class API: try: ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except: + except Ticket.DoesNotExist: return api_return(STATUS_ERROR, "Invalid ticket ID") ticket.delete() @@ -127,7 +127,7 @@ class API: def api_public_hold_ticket(self): try: ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except: + except Ticket.DoesNotExist: return api_return(STATUS_ERROR, "Invalid ticket ID") ticket.on_hold = True @@ -139,7 +139,7 @@ class API: def api_public_unhold_ticket(self): try: ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except: + except Ticket.DoesNotExist: return api_return(STATUS_ERROR, "Invalid ticket ID") ticket.on_hold = False @@ -151,7 +151,7 @@ class API: def api_public_add_followup(self): try: ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except: + except Ticket.DoesNotExist: return api_return(STATUS_ERROR, "Invalid ticket ID") message = self.request.POST.get('message', None) @@ -191,7 +191,7 @@ class API: def api_public_resolve(self): try: ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except: + except Ticket.DoesNotExist: return api_return(STATUS_ERROR, "Invalid ticket ID") resolution = self.request.POST.get('resolution', None) diff --git a/views/public.py b/views/public.py index 66312da9..6f6462ba 100644 --- a/views/public.py +++ b/views/public.py @@ -48,7 +48,7 @@ def view_ticket(request): t = Ticket.objects.get(id=ticket_id, queue__slug__iexact=queue, submitter_email__iexact=email) return render_to_response('helpdesk/public_view_ticket.html', RequestContext(request, {'ticket': t,})) - except: + except Ticket.DoesNotExist: t = False; error_message = _('Invalid ticket ID or e-mail address. Please try again.')
{{ ticket.id }}. {{ ticket.title }} [{{ ticket.get_status }}]
{% blocktrans %}Queue: {{ ticket.queue }}{% endblocktrans %}
{% blocktrans with ticket.queue as queue_name %}Queue: {{ queue_name }}{% endblocktrans %}
{% trans "Submitted On" %}