forked from extern/django-helpdesk
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.
This commit is contained in:
parent
4583d1c7c8
commit
cea6394b70
22
README
22
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 <http://djangopeople.net/vezult/>
|
||||
Chris Etcp
|
||||
Nikolay Panov
|
||||
|
2
forms.py
2
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()
|
||||
|
||||
|
0
management/commands/__init__.py
Normal file
0
management/commands/__init__.py
Normal file
@ -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)
|
@ -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)
|
@ -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
|
@ -13,7 +13,7 @@
|
||||
|
||||
<table width='100%'>
|
||||
<tr class='row_tablehead'><td colspan='2'>{{ ticket.id }}. {{ ticket.title }} [{{ ticket.get_status }}]</td></tr>
|
||||
<tr class='row_columnheads'><th colspan='2'>{% blocktrans %}Queue: {{ ticket.queue }}{% endblocktrans %}</th></tr>
|
||||
<tr class='row_columnheads'><th colspan='2'>{% blocktrans with ticket.queue as queue_name %}Queue: {{ queue_name }}{% endblocktrans %}</th></tr>
|
||||
|
||||
<tr class='row_odd'>
|
||||
<th>{% trans "Submitted On" %}</th>
|
||||
|
@ -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:
|
||||
|
12
views/api.py
12
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)
|
||||
|
@ -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.')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user