diff --git a/helpdesk/admin.py b/helpdesk/admin.py
index 5529f2b6..3ff8c857 100644
--- a/helpdesk/admin.py
+++ b/helpdesk/admin.py
@@ -5,10 +5,14 @@ from helpdesk.models import EscalationExclusion, EmailTemplate, KBItem
from helpdesk.models import TicketChange, Attachment, IgnoreEmail
from helpdesk.models import CustomField
+
+@admin.register(Queue)
class QueueAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'email_address', 'locale')
prepopulated_fields = {"slug": ("title",)}
+
+@admin.register(Ticket)
class TicketAdmin(admin.ModelAdmin):
list_display = ('title', 'status', 'assigned_to', 'queue', 'hidden_submitter_email',)
date_hierarchy = 'created'
@@ -24,34 +28,38 @@ class TicketAdmin(admin.ModelAdmin):
return ticket.submitter_email
hidden_submitter_email.short_description = _('Submitter E-Mail')
+
class TicketChangeInline(admin.StackedInline):
model = TicketChange
+
class AttachmentInline(admin.StackedInline):
model = Attachment
+
+@admin.register(FollowUp)
class FollowUpAdmin(admin.ModelAdmin):
inlines = [TicketChangeInline, AttachmentInline]
+
+@admin.register(KBItem)
class KBItemAdmin(admin.ModelAdmin):
list_display = ('category', 'title', 'last_updated',)
list_display_links = ('title',)
+
+@admin.register(CustomField)
class CustomFieldAdmin(admin.ModelAdmin):
list_display = ('name', 'label', 'data_type')
+
+@admin.register(EmailTemplate)
class EmailTemplateAdmin(admin.ModelAdmin):
list_display = ('template_name', 'heading', 'locale')
list_filter = ('locale', )
-admin.site.register(Ticket, TicketAdmin)
-admin.site.register(Queue, QueueAdmin)
-admin.site.register(FollowUp, FollowUpAdmin)
admin.site.register(PreSetReply)
admin.site.register(EscalationExclusion)
-admin.site.register(EmailTemplate, EmailTemplateAdmin)
admin.site.register(KBCategory)
-admin.site.register(KBItem, KBItemAdmin)
admin.site.register(IgnoreEmail)
-admin.site.register(CustomField, CustomFieldAdmin)
diff --git a/helpdesk/akismet.py b/helpdesk/akismet.py
index b5783678..64100c1b 100644
--- a/helpdesk/akismet.py
+++ b/helpdesk/akismet.py
@@ -55,7 +55,7 @@ Usage example::
"""
-import os, sys
+import os
from urllib import urlencode
import socket
@@ -104,9 +104,13 @@ else:
class AkismetError(Exception):
"""Base class for all akismet exceptions."""
+ pass
+
class APIKeyError(AkismetError):
"""Invalid API key."""
+ pass
+
class Akismet(object):
"""A class for working with the akismet API"""
@@ -120,7 +124,6 @@ class Akismet(object):
self.user_agent = user_agent % (agent, __version__)
self.setAPIKey(key, blog_url)
-
def _getURL(self):
"""
Fetch the url to make requests to.
@@ -128,8 +131,7 @@ class Akismet(object):
This comprises of api key plus the baseurl.
"""
return 'http://%s.%s' % (self.key, self.baseurl)
-
-
+
def _safeRequest(self, url, data, headers):
try:
resp = _fetch_url(url, data, headers)
@@ -137,7 +139,6 @@ class Akismet(object):
raise AkismetError(str(e))
return resp
-
def setAPIKey(self, key=None, blog_url=None):
"""
Set the wordpress API key for all transactions.
@@ -151,7 +152,7 @@ class Akismet(object):
"""
if key is None and isfile('apikey.txt'):
the_file = [l.strip() for l in open('apikey.txt').readlines()
- if l.strip() and not l.strip().startswith('#')]
+ if l.strip() and not l.strip().startswith('#')]
try:
self.key = the_file[0]
self.blog_url = the_file[1]
@@ -161,7 +162,6 @@ class Akismet(object):
self.key = key
self.blog_url = blog_url
-
def verify_key(self):
"""
This equates to the ``verify-key`` call against the akismet API.
@@ -179,12 +179,12 @@ class Akismet(object):
"""
if self.key is None:
raise APIKeyError("Your have not set an API key.")
- data = { 'key': self.key, 'blog': self.blog_url }
+ data = {'key': self.key, 'blog': self.blog_url}
# this function *doesn't* use the key as part of the URL
url = 'http://%sverify-key' % self.baseurl
# we *don't* trap the error here
# so if akismet is down it will raise an HTTPError or URLError
- headers = {'User-Agent' : self.user_agent}
+ headers = {'User-Agent': self.user_agent}
resp = self._safeRequest(url, urlencode(data), headers)
if resp.lower() == 'valid':
return True
@@ -226,14 +226,11 @@ class Akismet(object):
data.setdefault('SERVER_ADMIN', os.environ.get('SERVER_ADMIN', ''))
data.setdefault('SERVER_NAME', os.environ.get('SERVER_NAME', ''))
data.setdefault('SERVER_PORT', os.environ.get('SERVER_PORT', ''))
- data.setdefault('SERVER_SIGNATURE', os.environ.get('SERVER_SIGNATURE',
- ''))
- data.setdefault('SERVER_SOFTWARE', os.environ.get('SERVER_SOFTWARE',
- ''))
+ data.setdefault('SERVER_SIGNATURE', os.environ.get('SERVER_SIGNATURE', ''))
+ data.setdefault('SERVER_SOFTWARE', os.environ.get('SERVER_SOFTWARE', ''))
data.setdefault('HTTP_ACCEPT', os.environ.get('HTTP_ACCEPT', ''))
data.setdefault('blog', self.blog_url)
-
def comment_check(self, comment, data=None, build_data=True, DEBUG=False):
"""
This is the function that checks comments.
@@ -316,7 +313,7 @@ class Akismet(object):
url = '%scomment-check' % self._getURL()
# we *don't* trap the error here
# so if akismet is down it will raise an HTTPError or URLError
- headers = {'User-Agent' : self.user_agent}
+ headers = {'User-Agent': self.user_agent}
resp = self._safeRequest(url, urlencode(data), headers)
if DEBUG:
return resp
@@ -329,7 +326,6 @@ class Akismet(object):
# NOTE: Happens when you get a 'howdy wilbur' response !
raise AkismetError('missing required argument.')
-
def submit_spam(self, comment, data=None, build_data=True):
"""
This function is used to tell akismet that a comment it marked as ham,
@@ -347,10 +343,9 @@ class Akismet(object):
url = '%ssubmit-spam' % self._getURL()
# we *don't* trap the error here
# so if akismet is down it will raise an HTTPError or URLError
- headers = {'User-Agent' : self.user_agent}
+ headers = {'User-Agent': self.user_agent}
self._safeRequest(url, urlencode(data), headers)
-
def submit_ham(self, comment, data=None, build_data=True):
"""
This function is used to tell akismet that a comment it marked as spam,
@@ -368,5 +363,5 @@ class Akismet(object):
url = '%ssubmit-ham' % self._getURL()
# we *don't* trap the error here
# so if akismet is down it will raise an HTTPError or URLError
- headers = {'User-Agent' : self.user_agent}
+ headers = {'User-Agent': self.user_agent}
self._safeRequest(url, urlencode(data), headers)
diff --git a/helpdesk/apps.py b/helpdesk/apps.py
index 8a9755a4..a573b4ca 100644
--- a/helpdesk/apps.py
+++ b/helpdesk/apps.py
@@ -1,6 +1,6 @@
from django.apps import AppConfig
+
class HelpdeskConfig(AppConfig):
name = 'helpdesk'
verbose_name = "Helpdesk"
-
diff --git a/helpdesk/forms.py b/helpdesk/forms.py
index ed0993c0..44d3be8f 100644
--- a/helpdesk/forms.py
+++ b/helpdesk/forms.py
@@ -6,6 +6,8 @@ django-helpdesk - A Django powered ticket tracker for small enterprise.
forms.py - Definitions of newforms-based forms for creating and maintaining
tickets.
"""
+from django.core.exceptions import ObjectDoesNotExist
+
try:
from StringIO import StringIO
except ImportError:
@@ -13,23 +15,22 @@ except ImportError:
from django import forms
from django.forms import extras
-from django.core.files.storage import default_storage
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
-try:
- from django.contrib.auth import get_user_model
- User = get_user_model()
-except ImportError:
- from django.contrib.auth.models import User
+from django.contrib.auth import get_user_model
try:
from django.utils import timezone
except ImportError:
from datetime import datetime as timezone
from helpdesk.lib import send_templated_mail, safe_template_context
-from helpdesk.models import Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC, CustomField, TicketCustomFieldValue, TicketDependency
+from helpdesk.models import (Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC,
+ CustomField, TicketCustomFieldValue, TicketDependency)
from helpdesk import settings as helpdesk_settings
+User = get_user_model()
+
+
class CustomFieldMixin(object):
"""
Mixin that provides a method to turn CustomFields into an actual field
@@ -52,7 +53,7 @@ class CustomFieldMixin(object):
fieldclass = forms.ChoiceField
choices = field.choices_as_array
if field.empty_selection_list:
- choices.insert(0, ('','---------' ) )
+ choices.insert(0, ('', '---------'))
instanceargs['choices'] = choices
elif field.data_type == 'boolean':
fieldclass = forms.BooleanField
@@ -73,6 +74,7 @@ class CustomFieldMixin(object):
self.fields['custom_%s' % field.name] = fieldclass(**instanceargs)
+
class EditTicketForm(CustomFieldMixin, forms.ModelForm):
class Meta:
model = Ticket
@@ -99,7 +101,6 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
self.customfield_to_field(field, instanceargs)
-
def save(self, *args, **kwargs):
for field, value in self.cleaned_data.items():
@@ -108,7 +109,7 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
customfield = CustomField.objects.get(name=field_name)
try:
cfv = TicketCustomFieldValue.objects.get(ticket=self.instance, field=customfield)
- except:
+ except ObjectDoesNotExist:
cfv = TicketCustomFieldValue(ticket=self.instance, field=customfield)
cfv.value = value
cfv.save()
@@ -117,14 +118,16 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
class EditFollowUpForm(forms.ModelForm):
- def __init__(self, *args, **kwargs):
- "Filter not openned tickets here."
- super(EditFollowUpForm, self).__init__(*args, **kwargs)
- self.fields["ticket"].queryset = Ticket.objects.filter(status__in=(Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS))
class Meta:
model = FollowUp
exclude = ('date', 'user',)
+ def __init__(self, *args, **kwargs):
+ """Filter not openned tickets here."""
+ super(EditFollowUpForm, self).__init__(*args, **kwargs)
+ self.fields["ticket"].queryset = Ticket.objects.filter(status__in=(Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS))
+
+
class TicketForm(CustomFieldMixin, forms.Form):
queue = forms.ChoiceField(
label=_('Queue'),
@@ -158,7 +161,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
required=False,
label=_('Case owner'),
help_text=_('If you select an owner other than yourself, they\'ll be '
- 'e-mailed details of this ticket immediately.'),
+ 'e-mailed details of this ticket immediately.'),
)
priority = forms.ChoiceField(
@@ -166,8 +169,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
required=False,
initial='3',
label=_('Priority'),
- help_text=_('Please select a priority carefully. If unsure, leave it '
- 'as \'3\'.'),
+ help_text=_('Please select a priority carefully. If unsure, leave it as \'3\'.'),
)
due_date = forms.DateTimeField(
@@ -178,9 +180,9 @@ class TicketForm(CustomFieldMixin, forms.Form):
def clean_due_date(self):
data = self.cleaned_data['due_date']
- #TODO: add Google calendar update hook
- #if not hasattr(self, 'instance') or self.instance.due_date != new_data:
- # print "you changed!"
+ # TODO: add Google calendar update hook
+ # if not hasattr(self, 'instance') or self.instance.due_date != new_data:
+ # print "you changed!"
return data
attachment = forms.FileField(
@@ -203,7 +205,6 @@ class TicketForm(CustomFieldMixin, forms.Form):
self.customfield_to_field(field, instanceargs)
-
def save(self, user):
"""
Writes and returns a Ticket() object
@@ -211,15 +212,15 @@ class TicketForm(CustomFieldMixin, forms.Form):
q = Queue.objects.get(id=int(self.cleaned_data['queue']))
- t = Ticket( title = self.cleaned_data['title'],
- submitter_email = self.cleaned_data['submitter_email'],
- created = timezone.now(),
- status = Ticket.OPEN_STATUS,
- queue = q,
- description = self.cleaned_data['body'],
- priority = self.cleaned_data['priority'],
- due_date = self.cleaned_data['due_date'],
- )
+ t = Ticket(title=self.cleaned_data['title'],
+ submitter_email=self.cleaned_data['submitter_email'],
+ created=timezone.now(),
+ status=Ticket.OPEN_STATUS,
+ queue=q,
+ description=self.cleaned_data['body'],
+ priority=self.cleaned_data['priority'],
+ due_date=self.cleaned_data['due_date'],
+ )
if self.cleaned_data['assigned_to']:
try:
@@ -234,16 +235,16 @@ class TicketForm(CustomFieldMixin, forms.Form):
field_name = field.replace('custom_', '', 1)
customfield = CustomField.objects.get(name=field_name)
cfv = TicketCustomFieldValue(ticket=t,
- field=customfield,
- value=value)
+ field=customfield,
+ value=value)
cfv.save()
- f = FollowUp( ticket = t,
- title = _('Ticket Opened'),
- date = timezone.now(),
- public = True,
- comment = self.cleaned_data['body'],
- user = user,
+ f = FollowUp(ticket=t,
+ title=_('Ticket Opened'),
+ date=timezone.now(),
+ public=True,
+ comment=self.cleaned_data['body'],
+ user=user,
)
if self.cleaned_data['assigned_to']:
f.title = _('Ticket Opened & Assigned to %(name)s') % {
@@ -290,7 +291,11 @@ class TicketForm(CustomFieldMixin, forms.Form):
)
messages_sent_to.append(t.submitter_email)
- if t.assigned_to and t.assigned_to != user and t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) and t.assigned_to.email and t.assigned_to.email not in messages_sent_to:
+ if t.assigned_to and \
+ t.assigned_to != user and \
+ t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) and \
+ t.assigned_to.email and \
+ t.assigned_to.email not in messages_sent_to:
send_templated_mail(
'assigned_owner',
context,
@@ -312,7 +317,9 @@ class TicketForm(CustomFieldMixin, forms.Form):
)
messages_sent_to.append(q.new_ticket_cc)
- if q.updated_ticket_cc and q.updated_ticket_cc != q.new_ticket_cc and q.updated_ticket_cc not in messages_sent_to:
+ if q.updated_ticket_cc and \
+ q.updated_ticket_cc != q.new_ticket_cc and \
+ q.updated_ticket_cc not in messages_sent_to:
send_templated_mail(
'newticket_cc',
context,
@@ -350,7 +357,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
label=_('Description of your issue'),
required=True,
help_text=_('Please be as descriptive as possible, including any '
- 'details we may need to address your query.'),
+ 'details we may need to address your query.'),
)
priority = forms.ChoiceField(
@@ -396,14 +403,14 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
q = Queue.objects.get(id=int(self.cleaned_data['queue']))
t = Ticket(
- title = self.cleaned_data['title'],
- submitter_email = self.cleaned_data['submitter_email'],
- created = timezone.now(),
- status = Ticket.OPEN_STATUS,
- queue = q,
- description = self.cleaned_data['body'],
- priority = self.cleaned_data['priority'],
- due_date = self.cleaned_data['due_date'],
+ title=self.cleaned_data['title'],
+ submitter_email=self.cleaned_data['submitter_email'],
+ created=timezone.now(),
+ status=Ticket.OPEN_STATUS,
+ queue=q,
+ description=self.cleaned_data['body'],
+ priority=self.cleaned_data['priority'],
+ due_date=self.cleaned_data['due_date'],
)
if q.default_owner and not t.assigned_to:
@@ -416,16 +423,16 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
field_name = field.replace('custom_', '', 1)
customfield = CustomField.objects.get(name=field_name)
cfv = TicketCustomFieldValue(ticket=t,
- field=customfield,
- value=value)
+ field=customfield,
+ value=value)
cfv.save()
f = FollowUp(
- ticket = t,
- title = _('Ticket Opened Via Web'),
- date = timezone.now(),
- public = True,
- comment = self.cleaned_data['body'],
+ ticket=t,
+ title=_('Ticket Opened Via Web'),
+ date=timezone.now(),
+ public=True,
+ comment=self.cleaned_data['body'],
)
f.save()
@@ -463,7 +470,10 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
)
messages_sent_to.append(t.submitter_email)
- if t.assigned_to and t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) and t.assigned_to.email and t.assigned_to.email not in messages_sent_to:
+ if t.assigned_to and \
+ t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) and \
+ t.assigned_to.email and \
+ t.assigned_to.email not in messages_sent_to:
send_templated_mail(
'assigned_owner',
context,
@@ -485,7 +495,9 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
)
messages_sent_to.append(q.new_ticket_cc)
- if q.updated_ticket_cc and q.updated_ticket_cc != q.new_ticket_cc and q.updated_ticket_cc not in messages_sent_to:
+ if q.updated_ticket_cc and \
+ q.updated_ticket_cc != q.new_ticket_cc and \
+ q.updated_ticket_cc not in messages_sent_to:
send_templated_mail(
'newticket_cc',
context,
@@ -537,12 +549,18 @@ class UserSettingsForm(forms.Form):
required=False,
)
+
class EmailIgnoreForm(forms.ModelForm):
class Meta:
model = IgnoreEmail
exclude = []
+
class TicketCCForm(forms.ModelForm):
+ class Meta:
+ model = TicketCC
+ exclude = ('ticket',)
+
def __init__(self, *args, **kwargs):
super(TicketCCForm, self).__init__(*args, **kwargs)
if helpdesk_settings.HELPDESK_STAFF_ONLY_TICKET_CC:
@@ -550,9 +568,7 @@ class TicketCCForm(forms.ModelForm):
else:
users = User.objects.filter(is_active=True).order_by(User.USERNAME_FIELD)
self.fields['user'].queryset = users
- class Meta:
- model = TicketCC
- exclude = ('ticket',)
+
class TicketDependencyForm(forms.ModelForm):
class Meta:
diff --git a/helpdesk/lib.py b/helpdesk/lib.py
index 95dddbd0..84a60b2f 100644
--- a/helpdesk/lib.py
+++ b/helpdesk/lib.py
@@ -6,7 +6,7 @@ django-helpdesk - A Django powered ticket tracker for small enterprise.
lib.py - Common functions (eg multipart e-mail)
"""
-chart_colours = ('80C65A', '990066', 'FF9900', '3399CC', 'BBCCED', '3399CC', 'FFCC33')
+import logging
try:
from base64 import urlsafe_b64encode as b64encode
@@ -17,13 +17,20 @@ try:
except ImportError:
from base64 import decodestring as b64decode
-import logging
-logger = logging.getLogger('helpdesk')
-
from django.utils.encoding import smart_str
from django.db.models import Q
+from django.utils.safestring import mark_safe
-def send_templated_mail(template_name, email_context, recipients, sender=None, bcc=None, fail_silently=False, files=None):
+logger = logging.getLogger('helpdesk')
+
+
+def send_templated_mail(template_name,
+ email_context,
+ recipients,
+ sender=None,
+ bcc=None,
+ fail_silently=False,
+ files=None):
"""
send_templated_mail() is a warpper around Django's e-mail routines that
allows us to easily send multipart (text/plain & text/html) e-mails using
@@ -83,16 +90,17 @@ def send_templated_mail(template_name, email_context, recipients, sender=None, b
try:
t = EmailTemplate.objects.get(template_name__iexact=template_name, locale__isnull=True)
except EmailTemplate.DoesNotExist:
- logger.warning('template "%s" does not exist, no mail sent' %
- template_name)
- return # just ignore if template doesn't exist
+ logger.warning('template "%s" does not exist, no mail sent',
+ template_name)
+ return # just ignore if template doesn't exist
if not sender:
sender = settings.DEFAULT_FROM_EMAIL
footer_file = os.path.join('helpdesk', locale, 'email_text_footer.txt')
- # get_template_from_string was removed in Django 1.8 http://django.readthedocs.org/en/1.8.x/ref/templates/upgrading.html
+ # get_template_from_string was removed in Django 1.8
+ # http://django.readthedocs.org/en/1.8.x/ref/templates/upgrading.html
try:
from django.template import engines
template_func = engines['django'].from_string
@@ -105,21 +113,22 @@ def send_templated_mail(template_name, email_context, recipients, sender=None, b
email_html_base_file = os.path.join('helpdesk', locale, 'email_html_base.html')
-
- ''' keep new lines in html emails '''
- from django.utils.safestring import mark_safe
-
+ # keep new lines in html emails
if 'comment' in context:
html_txt = context['comment']
html_txt = html_txt.replace('\r\n', '
')
context['comment'] = mark_safe(html_txt)
- # get_template_from_string was removed in Django 1.8 http://django.readthedocs.org/en/1.8.x/ref/templates/upgrading.html
+ # get_template_from_string was removed in Django 1.8
+ # http://django.readthedocs.org/en/1.8.x/ref/templates/upgrading.html
html_part = template_func(
- "{%% extends '%s' %%}{%% block title %%}%s{%% endblock %%}{%% block content %%}%s{%% endblock %%}" % (email_html_base_file, t.heading, t.html)
- ).render(context)
+ "{%% extends '%s' %%}{%% block title %%}"
+ "%s"
+ "{%% endblock %%}{%% block content %%}%s{%% endblock %%}" %
+ (email_html_base_file, t.heading, t.html)).render(context)
- # get_template_from_string was removed in Django 1.8 http://django.readthedocs.org/en/1.8.x/ref/templates/upgrading.html
+ # get_template_from_string was removed in Django 1.8
+ # http://django.readthedocs.org/en/1.8.x/ref/templates/upgrading.html
subject_part = template_func(
HELPDESK_EMAIL_SUBJECT_TEMPLATE % {
"subject": t.subject,
@@ -129,13 +138,11 @@ def send_templated_mail(template_name, email_context, recipients, sender=None, b
if recipients.find(','):
recipients = recipients.split(',')
elif type(recipients) != list:
- recipients = [recipients,]
+ recipients = [recipients, ]
- msg = EmailMultiAlternatives( subject_part.replace('\n', '').replace('\r', ''),
- text_part,
- sender,
- recipients,
- bcc=bcc)
+ msg = EmailMultiAlternatives(
+ subject_part.replace('\n', '').replace('\r', ''),
+ text_part, sender, recipients, bcc=bcc)
msg.attach_alternative(html_part, "text/html")
if files:
@@ -230,19 +237,19 @@ def safe_template_context(ticket):
}
queue = ticket.queue
- for field in ( 'title', 'slug', 'email_address', 'from_address', 'locale'):
+ for field in ('title', 'slug', 'email_address', 'from_address', 'locale'):
attr = getattr(queue, field, None)
if callable(attr):
context['queue'][field] = attr()
else:
context['queue'][field] = attr
- for field in ( 'title', 'created', 'modified', 'submitter_email',
- 'status', 'get_status_display', 'on_hold', 'description',
- 'resolution', 'priority', 'get_priority_display',
- 'last_escalation', 'ticket', 'ticket_for_url',
- 'get_status', 'ticket_url', 'staff_url', '_get_assigned_to'
- ):
+ for field in ('title', 'created', 'modified', 'submitter_email',
+ 'status', 'get_status_display', 'on_hold', 'description',
+ 'resolution', 'priority', 'get_priority_display',
+ 'last_escalation', 'ticket', 'ticket_for_url',
+ 'get_status', 'ticket_url', 'staff_url', '_get_assigned_to'
+ ):
attr = getattr(ticket, field, None)
if callable(attr):
context['ticket'][field] = '%s' % attr()
@@ -278,10 +285,10 @@ def text_is_spam(text, request):
)
if hasattr(settings, 'TYPEPAD_ANTISPAM_API_KEY'):
- ak.setAPIKey(key = settings.TYPEPAD_ANTISPAM_API_KEY)
+ ak.setAPIKey(key=settings.TYPEPAD_ANTISPAM_API_KEY)
ak.baseurl = 'api.antispam.typepad.com/1.1/'
elif hasattr(settings, 'AKISMET_API_KEY'):
- ak.setAPIKey(key = settings.AKISMET_API_KEY)
+ ak.setAPIKey(key=settings.AKISMET_API_KEY)
else:
return False
diff --git a/helpdesk/management/commands/create_escalation_exclusions.py b/helpdesk/management/commands/create_escalation_exclusions.py
index 089e9b26..6c011f1b 100644
--- a/helpdesk/management/commands/create_escalation_exclusions.py
+++ b/helpdesk/management/commands/create_escalation_exclusions.py
@@ -16,7 +16,6 @@ from optparse import make_option
import sys
from django.core.management.base import BaseCommand, CommandError
-from django.db.models import Q
from helpdesk.models import EscalationExclusion, Queue
@@ -47,7 +46,8 @@ class Command(BaseCommand):
def handle(self, *args, **options):
days = options['days']
- occurrences = options['occurrences']
+ # optparse should already handle the `or 1`
+ occurrences = options['occurrences'] or 1
verbose = False
queue_slugs = options['queues']
queues = []
@@ -55,8 +55,6 @@ class Command(BaseCommand):
if options['escalate-verbosely']:
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.')
@@ -116,7 +114,6 @@ def usage():
print(" --verbose, -v: Display a list of dates excluded")
-
if __name__ == '__main__':
# This script can be run from the command-line or via Django's manage.py.
try:
@@ -126,7 +123,7 @@ if __name__ == '__main__':
sys.exit(2)
days = None
- occurrences = None
+ occurrences = 1
verbose = False
queue_slugs = None
queues = []
@@ -139,9 +136,8 @@ if __name__ == '__main__':
if o in ('-q', '--queues'):
queue_slugs = a
if o in ('-o', '--occurrences'):
- occurrences = int(a)
+ occurrences = int(a) or 1
- if not occurrences: occurrences = 1
if not (days and occurrences):
usage()
sys.exit(2)
diff --git a/helpdesk/management/commands/create_usersettings.py b/helpdesk/management/commands/create_usersettings.py
index eafa6278..965d347f 100644
--- a/helpdesk/management/commands/create_usersettings.py
+++ b/helpdesk/management/commands/create_usersettings.py
@@ -10,17 +10,16 @@ users who don't yet have them.
from django.utils.translation import ugettext as _
from django.core.management.base import BaseCommand
-try:
- from django.contrib.auth import get_user_model
- User = get_user_model()
-except ImportError:
- from django.contrib.auth.models import User
+from django.contrib.auth import get_user_model
from helpdesk.models import UserSettings
from helpdesk.settings import DEFAULT_USER_SETTINGS
+User = get_user_model()
+
+
class Command(BaseCommand):
- "create_usersettings command"
+ """create_usersettings command"""
help = _('Check for user without django-helpdesk UserSettings '
'and create settings if required. Uses '
@@ -28,10 +27,7 @@ class Command(BaseCommand):
'suit your situation.')
def handle(self, *args, **options):
- "handle command line"
+ """handle command line"""
for u in User.objects.all():
- try:
- s = UserSettings.objects.get(user=u)
- except UserSettings.DoesNotExist:
- s = UserSettings(user=u, settings=DEFAULT_USER_SETTINGS)
- s.save()
+ UserSettings.objects.get_or_create(user=u,
+ defaults={'settings': DEFAULT_USER_SETTINGS})
diff --git a/helpdesk/management/commands/escalate_tickets.py b/helpdesk/management/commands/escalate_tickets.py
index 9212813b..a3df890a 100644
--- a/helpdesk/management/commands/escalate_tickets.py
+++ b/helpdesk/management/commands/escalate_tickets.py
@@ -56,7 +56,7 @@ class Command(BaseCommand):
queue_set = queue_slugs.split(',')
for queue in queue_set:
try:
- q = Queue.objects.get(slug__exact=queue)
+ Queue.objects.get(slug__exact=queue)
except Queue.DoesNotExist:
raise CommandError("Queue %s does not exist." % queue)
queues.append(queue)
@@ -82,24 +82,23 @@ def escalate_tickets(queues, verbose):
days += 1
workdate = workdate + timedelta(days=1)
-
req_last_escl_date = date.today() - timedelta(days=days)
if verbose:
print("Processing: %s" % q)
for t in q.ticket_set.filter(
- Q(status=Ticket.OPEN_STATUS)
- | Q(status=Ticket.REOPENED_STATUS)
- ).exclude(
- priority=1
- ).filter(
- Q(on_hold__isnull=True)
- | Q(on_hold=False)
- ).filter(
- Q(last_escalation__lte=req_last_escl_date)
- | Q(last_escalation__isnull=True, created__lte=req_last_escl_date)
- ):
+ Q(status=Ticket.OPEN_STATUS)
+ | Q(status=Ticket.REOPENED_STATUS)
+ ).exclude(
+ priority=1
+ ).filter(
+ Q(on_hold__isnull=True)
+ | Q(on_hold=False)
+ ).filter(
+ Q(last_escalation__lte=req_last_escl_date)
+ | Q(last_escalation__isnull=True, created__lte=req_last_escl_date)
+ ):
t.last_escalation = timezone.now()
t.priority -= 1
@@ -143,8 +142,8 @@ def escalate_tickets(queues, verbose):
)
f = FollowUp(
- ticket = t,
- title = 'Ticket Escalated',
+ ticket=t,
+ title='Ticket Escalated',
date=timezone.now(),
public=True,
comment=_('Ticket escalated after %s days' % q.escalate_days),
@@ -152,10 +151,10 @@ def escalate_tickets(queues, verbose):
f.save()
tc = TicketChange(
- followup = f,
- field = _('Priority'),
- old_value = t.priority + 1,
- new_value = t.priority,
+ followup=f,
+ field=_('Priority'),
+ old_value=t.priority + 1,
+ new_value=t.priority,
)
tc.save()
diff --git a/helpdesk/management/commands/get_email.py b/helpdesk/management/commands/get_email.py
index 99f45f73..287cff0f 100644
--- a/helpdesk/management/commands/get_email.py
+++ b/helpdesk/management/commands/get_email.py
@@ -40,6 +40,14 @@ from helpdesk.lib import send_templated_mail, safe_template_context
from helpdesk.models import Queue, Ticket, FollowUp, Attachment, IgnoreEmail
+STRIPPED_SUBJECT_STRINGS = [
+ "Re: ",
+ "Fw: ",
+ "RE: ",
+ "FW: ",
+ "Automatic reply: ",
+]
+
class Command(BaseCommand):
def __init__(self):
BaseCommand.__init__(self)
@@ -52,7 +60,8 @@ class Command(BaseCommand):
help='Hide details about each queue/message as they are processed'),
)
- help = 'Process Jutda Helpdesk queues and process e-mails via POP3/IMAP as required, feeding them into the helpdesk.'
+ help = 'Process Jutda Helpdesk queues and process e-mails via ' \
+ 'POP3/IMAP as required, feeding them into the helpdesk.'
def handle(self, *args, **options):
quiet = options.get('quiet', False)
@@ -70,7 +79,6 @@ def process_email(quiet=False):
if not q.email_box_interval:
q.email_box_interval = 0
-
queue_time_delta = timedelta(minutes=q.email_box_interval)
if (q.email_box_last_check + queue_time_delta) > timezone.now():
@@ -90,39 +98,47 @@ def process_queue(q, quiet=False):
try:
import socks
except ImportError:
- raise ImportError("Queue has been configured with proxy settings, but no socks library was installed. Try to install PySocks via pypi.")
+ raise ImportError("Queue has been configured with proxy settings, "
+ "but no socks library was installed. "
+ "Try to install PySocks via pypi.")
proxy_type = {
'socks4': socks.SOCKS4,
'socks5': socks.SOCKS5,
}.get(q.socks_proxy_type)
- socks.set_default_proxy(proxy_type=proxy_type, addr=q.socks_proxy_host, port=q.socks_proxy_port)
+ socks.set_default_proxy(proxy_type=proxy_type,
+ addr=q.socks_proxy_host,
+ port=q.socks_proxy_port)
socket.socket = socks.socksocket
else:
socket.socket = socket._socketobject
- email_box_type = settings.QUEUE_EMAIL_BOX_TYPE if settings.QUEUE_EMAIL_BOX_TYPE else q.email_box_type
+ email_box_type = settings.QUEUE_EMAIL_BOX_TYPE or q.email_box_type
if email_box_type == 'pop3':
-
if q.email_box_ssl or settings.QUEUE_EMAIL_BOX_SSL:
- if not q.email_box_port: q.email_box_port = 995
- server = poplib.POP3_SSL(q.email_box_host or settings.QUEUE_EMAIL_BOX_HOST, int(q.email_box_port))
+ if not q.email_box_port:
+ q.email_box_port = 995
+ server = poplib.POP3_SSL(q.email_box_host or
+ settings.QUEUE_EMAIL_BOX_HOST,
+ int(q.email_box_port))
else:
- if not q.email_box_port: q.email_box_port = 110
- server = poplib.POP3(q.email_box_host or settings.QUEUE_EMAIL_BOX_HOST, int(q.email_box_port))
+ if not q.email_box_port:
+ q.email_box_port = 110
+ server = poplib.POP3(q.email_box_host or
+ settings.QUEUE_EMAIL_BOX_HOST,
+ int(q.email_box_port))
server.getwelcome()
server.user(q.email_box_user or settings.QUEUE_EMAIL_BOX_USER)
server.pass_(q.email_box_pass or settings.QUEUE_EMAIL_BOX_PASSWORD)
-
messagesInfo = server.list()[1]
for msg in messagesInfo:
msgNum = msg.split(" ")[0]
- msgSize = msg.split(" ")[1]
+ # msgSize = msg.split(" ")[1]
full_message = "\n".join(server.retr(msgNum)[1])
ticket = ticket_from_message(message=full_message, queue=q, quiet=quiet)
@@ -134,13 +150,22 @@ def process_queue(q, quiet=False):
elif email_box_type == 'imap':
if q.email_box_ssl or settings.QUEUE_EMAIL_BOX_SSL:
- if not q.email_box_port: q.email_box_port = 993
- server = imaplib.IMAP4_SSL(q.email_box_host or settings.QUEUE_EMAIL_BOX_HOST, int(q.email_box_port))
+ if not q.email_box_port:
+ q.email_box_port = 993
+ server = imaplib.IMAP4_SSL(q.email_box_host or
+ settings.QUEUE_EMAIL_BOX_HOST,
+ int(q.email_box_port))
else:
- if not q.email_box_port: q.email_box_port = 143
- server = imaplib.IMAP4(q.email_box_host or settings.QUEUE_EMAIL_BOX_HOST, int(q.email_box_port))
+ if not q.email_box_port:
+ q.email_box_port = 143
+ server = imaplib.IMAP4(q.email_box_host or
+ settings.QUEUE_EMAIL_BOX_HOST,
+ int(q.email_box_port))
- server.login(q.email_box_user or settings.QUEUE_EMAIL_BOX_USER, q.email_box_pass or settings.QUEUE_EMAIL_BOX_PASSWORD)
+ server.login(q.email_box_user or
+ settings.QUEUE_EMAIL_BOX_USER,
+ q.email_box_pass or
+ settings.QUEUE_EMAIL_BOX_PASSWORD)
server.select(q.email_box_imap_folder)
status, data = server.search(None, 'NOT', 'DELETED')
@@ -160,22 +185,26 @@ def process_queue(q, quiet=False):
def decodeUnknown(charset, string):
if not charset:
try:
- return string.decode('utf-8','ignore')
+ return string.decode('utf-8', 'ignore')
except:
- return string.decode('iso8859-1','ignore')
+ return string.decode('iso8859-1', 'ignore')
return unicode(string, charset)
+
def decode_mail_headers(string):
decoded = decode_header(string)
return u' '.join([unicode(msg, charset or 'utf-8') for msg, charset in decoded])
+
def ticket_from_message(message, queue, quiet):
# 'message' must be an RFC822 formatted message.
msg = message
message = email.message_from_string(msg)
subject = message.get('subject', _('Created from e-mail'))
subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject))
- subject = subject.replace("Re: ", "").replace("Fw: ", "").replace("RE: ", "").replace("FW: ", "").replace("Automatic reply: ", "").strip()
+ for affix in STRIPPED_SUBJECT_STRINGS:
+ subject = subject.replace(affix, "")
+ subject = subject.strip()
sender = message.get('from', _('Unknown Sender'))
sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender))
@@ -210,9 +239,10 @@ def ticket_from_message(message, queue, quiet):
if name:
name = collapse_rfc2231_value(name)
- if part.get_content_maintype() == 'text' and name == None:
+ if part.get_content_maintype() == 'text' and name is None:
if part.get_content_subtype() == 'plain':
- body_plain = EmailReplyParser.parse_reply(decodeUnknown(part.get_content_charset(), part.get_payload(decode=True)))
+ body_plain = EmailReplyParser.parse_reply(
+ decodeUnknown(part.get_content_charset(), part.get_payload(decode=True)))
else:
body_html = part.get_payload(decode=True)
else:
@@ -259,7 +289,7 @@ def ticket_from_message(message, queue, quiet):
if smtp_priority in high_priority_types or smtp_importance in high_priority_types:
priority = 2
- if ticket == None:
+ if ticket is None:
t = Ticket(
title=subject,
queue=queue,
@@ -270,18 +300,18 @@ def ticket_from_message(message, queue, quiet):
)
t.save()
new = True
- update = ''
+ # update = ''
elif t.status == Ticket.CLOSED_STATUS:
t.status = Ticket.REOPENED_STATUS
t.save()
f = FollowUp(
- ticket = t,
- title = _('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}),
- date = timezone.now(),
- public = True,
- comment = body,
+ ticket=t,
+ title=_('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}),
+ date=timezone.now(),
+ public=True,
+ comment=body,
)
if t.status == Ticket.REOPENED_STATUS:
@@ -308,7 +338,6 @@ def ticket_from_message(message, queue, quiet):
if not quiet:
print(" - %s" % filename)
-
context = safe_template_context(t)
if new:
@@ -343,10 +372,10 @@ def ticket_from_message(message, queue, quiet):
else:
context.update(comment=f.comment)
- if t.status == Ticket.REOPENED_STATUS:
- update = _(' (Reopened)')
- else:
- update = _(' (Updated)')
+ # if t.status == Ticket.REOPENED_STATUS:
+ # update = _(' (Reopened)')
+ # else:
+ # update = _(' (Updated)')
if t.assigned_to:
send_templated_mail(
diff --git a/helpdesk/migrations/0003_initial_data_import.py b/helpdesk/migrations/0003_initial_data_import.py
index 566993e0..cc478377 100644
--- a/helpdesk/migrations/0003_initial_data_import.py
+++ b/helpdesk/migrations/0003_initial_data_import.py
@@ -25,7 +25,7 @@ def load_fixture(apps, schema_editor):
def unload_fixture(apps, schema_editor):
- "Delete all EmailTemplate objects"
+ """Delete all EmailTemplate objects"""
objects = deserialize_fixture()
diff --git a/helpdesk/models.py b/helpdesk/models.py
index a3a9c7e4..4ee9ab08 100644
--- a/helpdesk/models.py
+++ b/helpdesk/models.py
@@ -12,14 +12,10 @@ from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
-from django.contrib.auth import get_user_model
from django.conf import settings
from django.utils.translation import ugettext_lazy as _, ugettext
-from django import VERSION
from django.utils.encoding import python_2_unicode_compatible
-from helpdesk import settings as helpdesk_settings
-
try:
from django.utils import timezone
except ImportError:
@@ -47,7 +43,7 @@ class Queue(models.Model):
max_length=50,
unique=True,
help_text=_('This slug is used when building ticket ID\'s. Once set, '
- 'try not to change it or e-mailing may get messy.'),
+ 'try not to change it or e-mailing may get messy.'),
)
email_address = models.EmailField(
@@ -55,8 +51,8 @@ class Queue(models.Model):
blank=True,
null=True,
help_text=_('All outgoing e-mails for this queue will use this e-mail '
- 'address. If you use IMAP or POP3, this should be the e-mail '
- 'address for that mailbox.'),
+ 'address. If you use IMAP or POP3, this should be the e-mail '
+ 'address for that mailbox.'),
)
locale = models.CharField(
@@ -64,15 +60,15 @@ class Queue(models.Model):
max_length=10,
blank=True,
null=True,
- help_text=_('Locale of this queue. All correspondence in this queue will be in this language.'),
+ help_text=_('Locale of this queue. All correspondence in this '
+ 'queue will be in this language.'),
)
allow_public_submission = models.BooleanField(
_('Allow Public Submission?'),
blank=True,
default=False,
- help_text=_('Should this queue be listed on the public submission '
- 'form?'),
+ help_text=_('Should this queue be listed on the public submission form?'),
)
allow_email_submission = models.BooleanField(
@@ -80,7 +76,7 @@ class Queue(models.Model):
blank=True,
default=False,
help_text=_('Do you want to poll the e-mail box below for new '
- 'tickets?'),
+ 'tickets?'),
)
escalate_days = models.IntegerField(
@@ -88,7 +84,7 @@ class Queue(models.Model):
blank=True,
null=True,
help_text=_('For tickets which are not held, how often do you wish to '
- 'increase their priority? Set to 0 for no escalation.'),
+ 'increase their priority? Set to 0 for no escalation.'),
)
new_ticket_cc = models.CharField(
@@ -97,8 +93,8 @@ class Queue(models.Model):
null=True,
max_length=200,
help_text=_('If an e-mail address is entered here, then it will '
- 'receive notification of all new tickets created for this queue. '
- 'Enter a comma between multiple e-mail addresses.'),
+ 'receive notification of all new tickets created for this queue. '
+ 'Enter a comma between multiple e-mail addresses.'),
)
updated_ticket_cc = models.CharField(
@@ -107,9 +103,9 @@ class Queue(models.Model):
null=True,
max_length=200,
help_text=_('If an e-mail address is entered here, then it will '
- 'receive notification of all activity (new tickets, closed '
- 'tickets, updates, reassignments, etc) for this queue. Separate '
- 'multiple addresses with a comma.'),
+ 'receive notification of all activity (new tickets, closed '
+ 'tickets, updates, reassignments, etc) for this queue. Separate '
+ 'multiple addresses with a comma.'),
)
email_box_type = models.CharField(
@@ -119,7 +115,7 @@ class Queue(models.Model):
blank=True,
null=True,
help_text=_('E-Mail server type for creating tickets automatically '
- 'from a mailbox - both POP3 and IMAP are supported.'),
+ 'from a mailbox - both POP3 and IMAP are supported.'),
)
email_box_host = models.CharField(
@@ -128,7 +124,7 @@ class Queue(models.Model):
blank=True,
null=True,
help_text=_('Your e-mail server address - either the domain name or '
- 'IP address. May be "localhost".'),
+ 'IP address. May be "localhost".'),
)
email_box_port = models.IntegerField(
@@ -136,8 +132,8 @@ class Queue(models.Model):
blank=True,
null=True,
help_text=_('Port number to use for accessing e-mail. Default for '
- 'POP3 is "110", and for IMAP is "143". This may differ on some '
- 'servers. Leave it blank to use the defaults.'),
+ 'POP3 is "110", and for IMAP is "143". This may differ on some '
+ 'servers. Leave it blank to use the defaults.'),
)
email_box_ssl = models.BooleanField(
@@ -145,7 +141,7 @@ class Queue(models.Model):
blank=True,
default=False,
help_text=_('Whether to use SSL for IMAP or POP3 - the default ports '
- 'when using SSL are 993 for IMAP and 995 for POP3.'),
+ 'when using SSL are 993 for IMAP and 995 for POP3.'),
)
email_box_user = models.CharField(
@@ -170,9 +166,9 @@ class Queue(models.Model):
blank=True,
null=True,
help_text=_('If using IMAP, what folder do you wish to fetch messages '
- 'from? This allows you to use one IMAP account for multiple '
- 'queues, by filtering messages on your IMAP server into separate '
- 'folders. Default: INBOX.'),
+ 'from? This allows you to use one IMAP account for multiple '
+ 'queues, by filtering messages on your IMAP server into separate '
+ 'folders. Default: INBOX.'),
)
permission_name = models.CharField(
@@ -375,7 +371,7 @@ class Ticket(models.Model):
blank=True,
null=True,
help_text=_('The submitter will receive an email for all public '
- 'follow-ups left for this task.'),
+ 'follow-ups left for this task.'),
)
assigned_to = models.ForeignKey(
@@ -396,8 +392,7 @@ class Ticket(models.Model):
_('On Hold'),
blank=True,
default=False,
- help_text=_('If a ticket is on hold, it will not automatically be '
- 'escalated.'),
+ help_text=_('If a ticket is on hold, it will not automatically be escalated.'),
)
description = models.TextField(
@@ -433,7 +428,7 @@ class Ticket(models.Model):
null=True,
editable=False,
help_text=_('The date this ticket was last escalated - updated '
- 'automatically by management/commands/escalate_tickets.py.'),
+ 'automatically by management/commands/escalate_tickets.py.'),
)
def _get_assigned_to(self):
@@ -453,7 +448,7 @@ class Ticket(models.Model):
""" A user-friendly ticket ID, which is a combination of ticket ID
and queue slug. This is generally used in e-mail subjects. """
- return u"[%s]" % (self.ticket_for_url)
+ return u"[%s]" % self.ticket_for_url
ticket = property(_get_ticket)
def _get_ticket_for_url(self):
@@ -486,7 +481,8 @@ class Ticket(models.Model):
held_msg = ''
if self.on_hold: held_msg = _(' - On Hold')
dep_msg = ''
- if self.can_be_resolved == False: dep_msg = _(' - Open dependencies')
+ if not self.can_be_resolved:
+ dep_msg = _(' - Open dependencies')
return u'%s%s%s' % (self.get_status_display(), held_msg, dep_msg)
get_status = property(_get_status)
@@ -534,7 +530,8 @@ class Ticket(models.Model):
False = There are non-resolved dependencies
"""
OPEN_STATUSES = (Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS)
- return TicketDependency.objects.filter(ticket=self).filter(depends_on__status__in=OPEN_STATUSES).count() == 0
+ return TicketDependency.objects.filter(ticket=self).filter(
+ depends_on__status__in=OPEN_STATUSES).count() == 0
can_be_resolved = property(_can_be_resolved)
class Meta:
@@ -547,7 +544,7 @@ class Ticket(models.Model):
return '%s %s' % (self.id, self.title)
def get_absolute_url(self):
- return ('helpdesk_view', (self.id,))
+ return 'helpdesk_view', (self.id,)
get_absolute_url = models.permalink(get_absolute_url)
def save(self, *args, **kwargs):
@@ -562,8 +559,8 @@ class Ticket(models.Model):
super(Ticket, self).save(*args, **kwargs)
- @classmethod
- def queue_and_id_from_query(klass, query):
+ @staticmethod
+ def queue_and_id_from_query(query):
# Apply the opposite logic here compared to self._get_ticket_for_url
# Ensure that queues with '-' in them will work
parts = query.split('-')
@@ -621,7 +618,7 @@ class FollowUp(models.Model):
blank=True,
default=False,
help_text=_('Public tickets are viewable by the submitter and all '
- 'staff, but non-public tickets can only be seen by staff.'),
+ 'staff, but non-public tickets can only be seen by staff.'),
)
user = models.ForeignKey(
@@ -785,33 +782,32 @@ class PreSetReply(models.Model):
When replying to a ticket, the user can select any reply set for the current
queue, and the body text is fetched via AJAX.
"""
+ class Meta:
+ ordering = ['name', ]
+ verbose_name = _('Pre-set reply')
+ verbose_name_plural = _('Pre-set replies')
queues = models.ManyToManyField(
Queue,
blank=True,
help_text=_('Leave blank to allow this reply to be used for all '
- 'queues, or select those queues you wish to limit this reply to.'),
+ 'queues, or select those queues you wish to limit this reply to.'),
)
name = models.CharField(
_('Name'),
max_length=100,
help_text=_('Only used to assist users with selecting a reply - not '
- 'shown to the user.'),
+ 'shown to the user.'),
)
body = models.TextField(
_('Body'),
help_text=_('Context available: {{ ticket }} - ticket object (eg '
- '{{ ticket.title }}); {{ queue }} - The queue; and {{ user }} '
- '- the current user.'),
+ '{{ ticket.title }}); {{ queue }} - The queue; and {{ user }} '
+ '- the current user.'),
)
- class Meta:
- ordering = ['name',]
- verbose_name = _('Pre-set reply')
- verbose_name_plural = _('Pre-set replies')
-
def __str__(self):
return '%s' % self.name
@@ -831,9 +827,8 @@ class EscalationExclusion(models.Model):
queues = models.ManyToManyField(
Queue,
blank=True,
- help_text=_('Leave blank for this exclusion to be applied to all '
- 'queues, or select those queues you wish to exclude with this '
- 'entry.'),
+ help_text=_('Leave blank for this exclusion to be applied to all queues, '
+ 'or select those queues you wish to exclude with this entry.'),
)
name = models.CharField(
@@ -873,29 +868,28 @@ class EmailTemplate(models.Model):
_('Subject'),
max_length=100,
help_text=_('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.'),
+ '. We recommend something simple such as "(Updated") or "(Closed)"'
+ ' - the same context is available as in plain_text, below.'),
)
heading = models.CharField(
_('Heading'),
max_length=100,
help_text=_('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.'),
+ 'the email - the same context is available as in plain_text, '
+ 'below.'),
)
plain_text = models.TextField(
_('Plain Text'),
help_text=_('The context available to you includes {{ ticket }}, '
- '{{ queue }}, and depending on the time of the call: '
- '{{ resolution }} or {{ comment }}.'),
+ '{{ queue }}, and depending on the time of the call: '
+ '{{ resolution }} or {{ comment }}.'),
)
html = models.TextField(
_('HTML'),
- help_text=_('The same context is available here as in plain_text, '
- 'above.'),
+ help_text=_('The same context is available here as in plain_text, above.'),
)
locale = models.CharField(
@@ -944,7 +938,7 @@ class KBCategory(models.Model):
verbose_name_plural = _('Knowledge base categories')
def get_absolute_url(self):
- return ('helpdesk_kb_category', (), {'slug': self.slug})
+ return 'helpdesk_kb_category', (), {'slug': self.slug}
get_absolute_url = models.permalink(get_absolute_url)
@@ -986,8 +980,7 @@ class KBItem(models.Model):
last_updated = models.DateTimeField(
_('Last Updated'),
- help_text=_('The date on which this question was most recently '
- 'changed.'),
+ help_text=_('The date on which this question was most recently changed.'),
blank=True,
)
@@ -1012,7 +1005,7 @@ class KBItem(models.Model):
verbose_name_plural = _('Knowledge base items')
def get_absolute_url(self):
- return ('helpdesk_kb_item', (self.id,))
+ return 'helpdesk_kb_item', (self.id,)
get_absolute_url = models.permalink(get_absolute_url)
@@ -1076,7 +1069,8 @@ class UserSettings(models.Model):
settings_pickled = models.TextField(
_('Settings Dictionary'),
- help_text=_('This is a base64-encoded representation of a pickled Python dictionary. Do not change this field via the admin.'),
+ help_text=_('This is a base64-encoded representation of a pickled Python dictionary. '
+ 'Do not change this field via the admin.'),
blank=True,
null=True,
)
@@ -1125,15 +1119,7 @@ def create_usersettings(sender, instance, created, **kwargs):
if created:
UserSettings.objects.create(user=instance, settings=DEFAULT_USER_SETTINGS)
-try:
- # Connecting via settings.AUTH_USER_MODEL (string) fails in Django < 1.7. We need the actual model there.
- # https://docs.djangoproject.com/en/1.7/topics/auth/customizing/#referencing-the-user-model
- if VERSION < (1, 7):
- raise ValueError
- models.signals.post_save.connect(create_usersettings, sender=settings.AUTH_USER_MODEL)
-except:
- signal_user = get_user_model()
- models.signals.post_save.connect(create_usersettings, sender=signal_user)
+models.signals.post_save.connect(create_usersettings, sender=settings.AUTH_USER_MODEL)
@python_2_unicode_compatible
@@ -1143,12 +1129,15 @@ class IgnoreEmail(models.Model):
processing IMAP and POP3 mailboxes, eg mails from postmaster or from
known trouble-makers.
"""
+ class Meta:
+ verbose_name = _('Ignored e-mail address')
+ verbose_name_plural = _('Ignored e-mail addresses')
+
queues = models.ManyToManyField(
Queue,
blank=True,
- help_text=_('Leave blank for this e-mail to be ignored on all '
- 'queues, or select those queues you wish to ignore this e-mail '
- 'for.'),
+ help_text=_('Leave blank for this e-mail to be ignored on all queues, '
+ 'or select those queues you wish to ignore this e-mail for.'),
)
name = models.CharField(
@@ -1167,16 +1156,15 @@ class IgnoreEmail(models.Model):
_('E-Mail Address'),
max_length=150,
help_text=_('Enter a full e-mail address, or portions with '
- 'wildcards, eg *@domain.com or postmaster@*.'),
+ 'wildcards, eg *@domain.com or postmaster@*.'),
)
keep_in_mailbox = models.BooleanField(
_('Save Emails in Mailbox?'),
blank=True,
default=False,
- help_text=_('Do you want to save emails from this address in the '
- 'mailbox? If this is unticked, emails from this address will '
- 'be deleted.'),
+ help_text=_('Do you want to save emails from this address in the mailbox? '
+ 'If this is unticked, emails from this address will be deleted.'),
)
def __str__(self):
@@ -1202,18 +1190,14 @@ class IgnoreEmail(models.Model):
own_parts = self.email_address.split("@")
email_parts = email.split("@")
- if self.email_address == email \
- or own_parts[0] == "*" and own_parts[1] == email_parts[1] \
- or own_parts[1] == "*" and own_parts[0] == email_parts[0] \
- or own_parts[0] == "*" and own_parts[1] == "*":
+ if self.email_address == email or \
+ own_parts[0] == "*" and own_parts[1] == email_parts[1] or \
+ own_parts[1] == "*" and own_parts[0] == email_parts[0] or \
+ own_parts[0] == "*" and own_parts[1] == "*":
return True
else:
return False
- class Meta:
- verbose_name = _('Ignored e-mail address')
- verbose_name_plural = _('Ignored e-mail addresses')
-
@python_2_unicode_compatible
class TicketCC(models.Model):
@@ -1277,6 +1261,7 @@ class TicketCC(models.Model):
def __str__(self):
return '%s for %s' % (self.display, self.ticket.title)
+
class CustomFieldManager(models.Manager):
def get_queryset(self):
return super(CustomFieldManager, self).get_queryset().order_by('ordering')
@@ -1290,7 +1275,8 @@ class CustomField(models.Model):
name = models.SlugField(
_('Field Name'),
- help_text=_('As used in the database and behind the scenes. Must be unique and consist of only lowercase letters with no punctuation.'),
+ help_text=_('As used in the database and behind the scenes. '
+ 'Must be unique and consist of only lowercase letters with no punctuation.'),
unique=True,
)
@@ -1346,7 +1332,8 @@ class CustomField(models.Model):
empty_selection_list = models.BooleanField(
_('Add empty first choice to List?'),
default=False,
- help_text=_('Only for List: adds an empty first entry to the choices list, which enforces that the user makes an active choice.'),
+ help_text=_('Only for List: adds an empty first entry to the choices list, '
+ 'which enforces that the user makes an active choice.'),
)
list_values = models.TextField(
@@ -1379,14 +1366,15 @@ class CustomField(models.Model):
staff_only = models.BooleanField(
_('Staff Only?'),
- help_text=_('If this is ticked, then the public submission form will NOT show this field'),
+ help_text=_('If this is ticked, then the public submission form '
+ 'will NOT show this field'),
default=False,
)
objects = CustomFieldManager()
def __str__(self):
- return '%s' % (self.name)
+ return '%s' % self.name
class Meta:
verbose_name = _('Custom field')
@@ -1423,6 +1411,11 @@ class TicketDependency(models.Model):
To help enforce this, a helper function `can_be_resolved` on each Ticket instance checks that
these have all been resolved.
"""
+ class Meta:
+ unique_together = (('ticket', 'depends_on'),)
+ verbose_name = _('Ticket dependency')
+ verbose_name_plural = _('Ticket dependencies')
+
ticket = models.ForeignKey(
Ticket,
verbose_name=_('Ticket'),
@@ -1437,8 +1430,3 @@ class TicketDependency(models.Model):
def __str__(self):
return '%s / %s' % (self.ticket, self.depends_on)
-
- class Meta:
- unique_together = (('ticket', 'depends_on'),)
- verbose_name = _('Ticket dependency')
- verbose_name_plural = _('Ticket dependencies')
diff --git a/helpdesk/settings.py b/helpdesk/settings.py
index 0ead588d..606af04f 100644
--- a/helpdesk/settings.py
+++ b/helpdesk/settings.py
@@ -11,22 +11,27 @@ try:
except:
DEFAULT_USER_SETTINGS = None
-if type(DEFAULT_USER_SETTINGS) != type(dict()):
+if not isinstance(DEFAULT_USER_SETTINGS, dict):
DEFAULT_USER_SETTINGS = {
- 'use_email_as_submitter': True,
- 'email_on_ticket_assign': True,
- 'email_on_ticket_change': True,
- 'login_view_ticketlist': True,
- 'email_on_ticket_apichange': True,
- 'tickets_per_page': 25
- }
+ 'use_email_as_submitter': True,
+ 'email_on_ticket_assign': True,
+ 'email_on_ticket_change': True,
+ 'login_view_ticketlist': True,
+ 'email_on_ticket_apichange': True,
+ 'tickets_per_page': 25
+ }
HAS_TAG_SUPPORT = False
-''' generic options - visible on all pages '''
+##########################################
+# generic options - visible on all pages #
+##########################################
+
# redirect to login page instead of the default homepage when users visits "/"?
-HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT = getattr(settings, 'HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT', False)
+HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT = getattr(settings,
+ 'HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT',
+ False)
# show knowledgebase links?
HELPDESK_KB_ENABLED = getattr(settings, 'HELPDESK_KB_ENABLED', True)
@@ -34,14 +39,20 @@ HELPDESK_KB_ENABLED = getattr(settings, 'HELPDESK_KB_ENABLED', True)
# show extended navigation by default, to all users, irrespective of staff status?
HELPDESK_NAVIGATION_ENABLED = getattr(settings, 'HELPDESK_NAVIGATION_ENABLED', False)
-# use public CDNs to serve jquery and other javascript by default? otherwise, use built-in static copy
+# use public CDNs to serve jquery and other javascript by default?
+# otherwise, use built-in static copy
HELPDESK_USE_CDN = getattr(settings, 'HELPDESK_USE_CDN', False)
# show dropdown list of languages that ticket comments can be translated into?
-HELPDESK_TRANSLATE_TICKET_COMMENTS = getattr(settings, 'HELPDESK_TRANSLATE_TICKET_COMMENTS', False)
+HELPDESK_TRANSLATE_TICKET_COMMENTS = getattr(settings,
+ 'HELPDESK_TRANSLATE_TICKET_COMMENTS',
+ False)
-# list of languages to offer. if set to false, all default google translate languages will be shown.
-HELPDESK_TRANSLATE_TICKET_COMMENTS_LANG = getattr(settings, 'HELPDESK_TRANSLATE_TICKET_COMMENTS_LANG', ["en", "de", "fr", "it", "ru"])
+# list of languages to offer. if set to false,
+# all default google translate languages will be shown.
+HELPDESK_TRANSLATE_TICKET_COMMENTS_LANG = getattr(settings,
+ 'HELPDESK_TRANSLATE_TICKET_COMMENTS_LANG',
+ ["en", "de", "fr", "it", "ru"])
# show link to 'change password' on 'User Settings' page?
HELPDESK_SHOW_CHANGE_PASSWORD = getattr(settings, 'HELPDESK_SHOW_CHANGE_PASSWORD', False)
@@ -50,10 +61,15 @@ HELPDESK_SHOW_CHANGE_PASSWORD = getattr(settings, 'HELPDESK_SHOW_CHANGE_PASSWORD
HELPDESK_FOLLOWUP_MOD = getattr(settings, 'HELPDESK_FOLLOWUP_MOD', False)
# auto-subscribe user to ticket if (s)he responds to a ticket?
-HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE = getattr(settings, 'HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE', False)
+HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE = getattr(settings,
+ 'HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE',
+ False)
-''' options for public pages '''
+############################
+# options for public pages #
+############################
+
# show 'view a ticket' section on public page?
HELPDESK_VIEW_A_TICKET_PUBLIC = getattr(settings, 'HELPDESK_VIEW_A_TICKET_PUBLIC', True)
@@ -61,17 +77,25 @@ HELPDESK_VIEW_A_TICKET_PUBLIC = getattr(settings, 'HELPDESK_VIEW_A_TICKET_PUBLIC
HELPDESK_SUBMIT_A_TICKET_PUBLIC = getattr(settings, 'HELPDESK_SUBMIT_A_TICKET_PUBLIC', True)
+###################################
+# options for update_ticket views #
+###################################
-''' options for update_ticket views '''
-# allow non-staff users to interact with tickets? this will also change how 'staff_member_required'
+# allow non-staff users to interact with tickets?
+# this will also change how 'staff_member_required'
# in staff.py will be defined.
-HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = getattr(settings, 'HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE', False)
+HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = getattr(settings,
+ 'HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE',
+ False)
# show edit buttons in ticket follow ups.
-HELPDESK_SHOW_EDIT_BUTTON_FOLLOW_UP = getattr(settings, 'HELPDESK_SHOW_EDIT_BUTTON_FOLLOW_UP', True)
+HELPDESK_SHOW_EDIT_BUTTON_FOLLOW_UP = getattr(settings,
+ 'HELPDESK_SHOW_EDIT_BUTTON_FOLLOW_UP',
+ True)
# show delete buttons in ticket follow ups if user is 'superuser'
-HELPDESK_SHOW_DELETE_BUTTON_SUPERUSER_FOLLOW_UP = getattr(settings, 'HELPDESK_SHOW_DELETE_BUTTON_SUPERUSER_FOLLOW_UP', False)
+HELPDESK_SHOW_DELETE_BUTTON_SUPERUSER_FOLLOW_UP = getattr(
+ settings, 'HELPDESK_SHOW_DELETE_BUTTON_SUPERUSER_FOLLOW_UP', False)
# make all updates public by default? this will hide the 'is this update public' checkbox
HELPDESK_UPDATE_PUBLIC_DEFAULT = getattr(settings, 'HELPDESK_UPDATE_PUBLIC_DEFAULT', False)
@@ -82,21 +106,28 @@ HELPDESK_STAFF_ONLY_TICKET_OWNERS = getattr(settings, 'HELPDESK_STAFF_ONLY_TICKE
# only show staff users in ticket cc drop-down
HELPDESK_STAFF_ONLY_TICKET_CC = getattr(settings, 'HELPDESK_STAFF_ONLY_TICKET_CC', False)
-
# allow the subject to have a configurable template.
-HELPDESK_EMAIL_SUBJECT_TEMPLATE = getattr(settings, 'HELPDESK_EMAIL_SUBJECT_TEMPLATE', "{{ ticket.ticket }} {{ ticket.title|safe }} %(subject)s")
+HELPDESK_EMAIL_SUBJECT_TEMPLATE = getattr(
+ settings, 'HELPDESK_EMAIL_SUBJECT_TEMPLATE',
+ "{{ ticket.ticket }} {{ ticket.title|safe }} %(subject)s")
# default fallback locale when queue locale not found
HELPDESK_EMAIL_FALLBACK_LOCALE = getattr(settings, 'HELPDESK_EMAIL_FALLBACK_LOCALE', 'en')
-''' options for staff.create_ticket view '''
+########################################
+# options for staff.create_ticket view #
+########################################
+
# hide the 'assigned to' / 'Case owner' field from the 'create_ticket' view?
-HELPDESK_CREATE_TICKET_HIDE_ASSIGNED_TO = getattr(settings, 'HELPDESK_CREATE_TICKET_HIDE_ASSIGNED_TO', False)
+HELPDESK_CREATE_TICKET_HIDE_ASSIGNED_TO = getattr(
+ settings, 'HELPDESK_CREATE_TICKET_HIDE_ASSIGNED_TO', False)
+#################
+# email options #
+#################
-''' email options '''
# default Queue email submission settings
QUEUE_EMAIL_BOX_TYPE = getattr(settings, 'QUEUE_EMAIL_BOX_TYPE', None)
QUEUE_EMAIL_BOX_SSL = getattr(settings, 'QUEUE_EMAIL_BOX_SSL', None)
@@ -104,6 +135,6 @@ QUEUE_EMAIL_BOX_HOST = getattr(settings, 'QUEUE_EMAIL_BOX_HOST', None)
QUEUE_EMAIL_BOX_USER = getattr(settings, 'QUEUE_EMAIL_BOX_USER', None)
QUEUE_EMAIL_BOX_PASSWORD = getattr(settings, 'QUEUE_EMAIL_BOX_PASSWORD', None)
-
# only allow users to access queues that they are members of?
-HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION = getattr(settings, 'HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION', False)
+HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION = getattr(
+ settings, 'HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION', False)
diff --git a/helpdesk/templatetags/in_list.py b/helpdesk/templatetags/in_list.py
index 16c3d678..fcc58898 100644
--- a/helpdesk/templatetags/in_list.py
+++ b/helpdesk/templatetags/in_list.py
@@ -17,8 +17,9 @@ Assuming 'food' = 'pizza' and 'best_foods' = ['pizza', 'pie', 'cake]:
from django import template
+
def in_list(value, arg):
- return value in ( arg or [] )
+ return value in (arg or [])
register = template.Library()
register.filter(in_list)
diff --git a/helpdesk/templatetags/load_helpdesk_settings.py b/helpdesk/templatetags/load_helpdesk_settings.py
index f14e4b30..835efe81 100644
--- a/helpdesk/templatetags/load_helpdesk_settings.py
+++ b/helpdesk/templatetags/load_helpdesk_settings.py
@@ -4,17 +4,19 @@ django-helpdesk - A Django powered ticket tracker for small enterprise.
templatetags/load_helpdesk_settings.py - returns the settings as defined in
django-helpdesk/helpdesk/settings.py
"""
-
+from __future__ import print_function
from django.template import Library
from helpdesk import settings as helpdesk_settings_config
+
def load_helpdesk_settings(request):
try:
return helpdesk_settings_config
except Exception as e:
import sys
- print >> sys.stderr, "'load_helpdesk_settings' template tag (django-helpdesk) crashed with following error:"
- print >> sys.stderr, e
+ print("'load_helpdesk_settings' template tag (django-helpdesk) crashed with following error:",
+ file=sys.stderr)
+ print(e, file=sys.stderr)
return ''
register = Library()
diff --git a/helpdesk/templatetags/ticket_to_link.py b/helpdesk/templatetags/ticket_to_link.py
index 2e72729a..4fc65dd9 100644
--- a/helpdesk/templatetags/ticket_to_link.py
+++ b/helpdesk/templatetags/ticket_to_link.py
@@ -20,18 +20,6 @@ from django.utils.safestring import mark_safe
from helpdesk.models import Ticket
-class ReverseProxy:
- def __init__(self, sequence):
- self.sequence = sequence
-
- def __iter__(self):
- length = len(self.sequence)
- i = length
- while i > 0:
- i = i - 1
- yield self.sequence[i]
-
-
def num_to_link(text):
if text == '':
return text
@@ -40,9 +28,7 @@ def num_to_link(text):
for match in re.finditer(r"(?:[^&]|\b|^)#(\d+)\b", text):
matches.append(match)
- for match in ReverseProxy(matches):
- start = match.start()
- end = match.end()
+ for match in reversed(matches):
number = match.groups()[0]
url = reverse('helpdesk_view', args=[number])
try:
@@ -52,7 +38,8 @@ def num_to_link(text):
if ticket:
style = ticket.get_status_display()
- text = "%s #%s%s" % (text[:match.start()], url, style, match.groups()[0], text[match.end():])
+ text = "%s #%s%s" % (
+ text[:match.start()], url, style, match.groups()[0], text[match.end():])
return mark_safe(text)
register = template.Library()
diff --git a/helpdesk/templatetags/user_admin_url.py b/helpdesk/templatetags/user_admin_url.py
index 13517609..e779ee9f 100644
--- a/helpdesk/templatetags/user_admin_url.py
+++ b/helpdesk/templatetags/user_admin_url.py
@@ -12,6 +12,7 @@ templatetags/admin_url.py - Very simple template tag allow linking to the
from django import template
from django.contrib.auth import get_user_model
+
def user_admin_url(action):
user = get_user_model()
try:
diff --git a/helpdesk/tests/helpers.py b/helpdesk/tests/helpers.py
index b64970be..09055adc 100644
--- a/helpdesk/tests/helpers.py
+++ b/helpdesk/tests/helpers.py
@@ -1,11 +1,8 @@
# -*- coding: utf-8 -*-
import sys
-try:
- from django.contrib.auth import get_user_model
-except ImportError:
- from django.contrib.auth.models import User
-else:
- User = get_user_model()
+from django.contrib.auth import get_user_model
+
+User = get_user_model()
def get_staff_user(username='helpdesk.staff', password='password'):
diff --git a/helpdesk/tests/test_public_actions.py b/helpdesk/tests/test_public_actions.py
index 95ccfb70..4133de70 100644
--- a/helpdesk/tests/test_public_actions.py
+++ b/helpdesk/tests/test_public_actions.py
@@ -1,9 +1,9 @@
-from helpdesk.models import Queue, CustomField, Ticket
+from helpdesk.models import Queue, Ticket
from django.test import TestCase
-from django.core import mail
from django.test.client import Client
from django.core.urlresolvers import reverse
+
class PublicActionsTestCase(TestCase):
"""
Tests for public actions:
@@ -15,13 +15,23 @@ class PublicActionsTestCase(TestCase):
"""
Create a queue & ticket we can use for later tests.
"""
- self.queue = Queue.objects.create(title='Queue 1', slug='q', allow_public_submission=True, new_ticket_cc='new.public@example.com', updated_ticket_cc='update.public@example.com')
- self.ticket = Ticket.objects.create(title='Test Ticket', queue=self.queue, submitter_email='test.submitter@example.com', description='This is a test ticket.')
+ self.queue = Queue.objects.create(title='Queue 1',
+ slug='q',
+ allow_public_submission=True,
+ new_ticket_cc='new.public@example.com',
+ updated_ticket_cc='update.public@example.com')
+ self.ticket = Ticket.objects.create(title='Test Ticket',
+ queue=self.queue,
+ submitter_email='test.submitter@example.com',
+ description='This is a test ticket.')
self.client = Client()
def test_public_view_ticket(self):
- response = self.client.get('%s?ticket=%s&email=%s' % (reverse('helpdesk_public_view'), self.ticket.ticket_for_url, 'test.submitter@example.com'))
+ response = self.client.get('%s?ticket=%s&email=%s' % (
+ reverse('helpdesk_public_view'),
+ self.ticket.ticket_for_url,
+ 'test.submitter@example.com'))
self.assertEqual(response.status_code, 200)
self.assertTemplateNotUsed(response, 'helpdesk/public_view_form.html')
@@ -38,7 +48,10 @@ class PublicActionsTestCase(TestCase):
current_followups = ticket.followup_set.all().count()
- response = self.client.get('%s?ticket=%s&email=%s&close' % (reverse('helpdesk_public_view'), ticket.ticket_for_url, 'test.submitter@example.com'))
+ response = self.client.get('%s?ticket=%s&email=%s&close' % (
+ reverse('helpdesk_public_view'),
+ ticket.ticket_for_url,
+ 'test.submitter@example.com'))
ticket = Ticket.objects.get(id=self.ticket.id)
diff --git a/helpdesk/tests/test_savequery.py b/helpdesk/tests/test_savequery.py
index df95b9e7..f9d142b2 100644
--- a/helpdesk/tests/test_savequery.py
+++ b/helpdesk/tests/test_savequery.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse
from django.test import TestCase
-from helpdesk.models import Ticket, Queue
-from helpdesk.tests.helpers import get_staff_user, reload_urlconf
+from helpdesk.models import Queue
+from helpdesk.tests.helpers import get_staff_user
+
class TestSavingSharedQuery(TestCase):
def setUp(self):
@@ -15,12 +16,15 @@ class TestSavingSharedQuery(TestCase):
url = reverse('helpdesk_savequery')
self.client.login(username=get_staff_user().get_username(),
password='password')
- response = self.client.post(url,
- data={'title': 'ticket on my queue',
- 'queue':self.q,
- 'shared':'on',
- 'query_encoded':'KGRwMApWZmlsdGVyaW5nCnAxCihkcDIKVnN0YXR1c19faW4KcDMKKGxwNApJMQphSTIKYUkzCmFzc1Zzb3J0aW5nCnA1ClZjcmVhdGVkCnA2CnMu'})
+ response = self.client.post(
+ url,
+ data={
+ 'title': 'ticket on my queue',
+ 'queue': self.q,
+ 'shared': 'on',
+ 'query_encoded':
+ 'KGRwMApWZmlsdGVyaW5nCnAxCihkcDIKVnN0YXR1c19faW4KcDMKKG'
+ 'xwNApJMQphSTIKYUkzCmFzc1Zzb3J0aW5nCnA1ClZjcmVhdGVkCnA2CnMu'
+ })
self.assertEqual(response.status_code, 302)
self.assertTrue('tickets/?saved_query=1' in response.url)
-
-
diff --git a/helpdesk/tests/test_ticket_lookup.py b/helpdesk/tests/test_ticket_lookup.py
index 39999421..6e55ce6f 100644
--- a/helpdesk/tests/test_ticket_lookup.py
+++ b/helpdesk/tests/test_ticket_lookup.py
@@ -3,6 +3,7 @@ from django.core.urlresolvers import reverse
from django.test import TestCase
from helpdesk.models import Ticket, Queue
+
class TestKBDisabled(TestCase):
def setUp(self):
q = Queue(title='Q1', slug='q1')
@@ -14,22 +15,19 @@ class TestKBDisabled(TestCase):
def test_ticket_by_id(self):
"""Can a ticket be looked up by its ID"""
- from django.core.urlresolvers import NoReverseMatch
-
# get the ticket from models
t = Ticket.objects.get(id=self.ticket.id)
self.assertEqual(t.title, self.ticket.title)
def test_ticket_by_link(self):
"""Can a ticket be looked up by its link from (eg) an email"""
- # Work out the link which would have been inserted into the email
- link = self.ticket.ticket_url
- # however instead of using that link, we will exercise 'reverse'
- # to lookup/build the URL from the ticket info we have
- # http://example.com/helpdesk/view/?ticket=q1-1&email=None
+ # Instead of using the ticket_for_url link,
+ # we will exercise 'reverse' to lookup/build the URL
+ # from the ticket info we have
+ # http://example.com/helpdesk/view/?ticket=q1-1&email=None
response = self.client.get(reverse('helpdesk_public_view'),
{'ticket': self.ticket.ticket_for_url,
- 'email':self.ticket.submitter_email})
+ 'email': self.ticket.submitter_email})
self.assertEqual(response.status_code, 200)
def test_ticket_with_changed_queue(self):
@@ -40,7 +38,7 @@ class TestKBDisabled(TestCase):
# grab the URL / params which would have been emailed out to submitter.
url = reverse('helpdesk_public_view')
params = {'ticket': self.ticket.ticket_for_url,
- 'email':self.ticket.submitter_email}
+ 'email': self.ticket.submitter_email}
# Pickup the ticket created in setup() and change its queue
self.ticket.queue = q2
self.ticket.save()
diff --git a/helpdesk/tests/test_ticket_submission.py b/helpdesk/tests/test_ticket_submission.py
index e5f9933b..3776dc01 100644
--- a/helpdesk/tests/test_ticket_submission.py
+++ b/helpdesk/tests/test_ticket_submission.py
@@ -14,13 +14,23 @@ class TicketBasicsTestCase(TestCase):
fixtures = ['emailtemplate.json']
def setUp(self):
- self.queue_public = Queue.objects.create(title='Queue 1', slug='q1', allow_public_submission=True, new_ticket_cc='new.public@example.com', updated_ticket_cc='update.public@example.com')
- self.queue_private = Queue.objects.create(title='Queue 2', slug='q2', allow_public_submission=False, new_ticket_cc='new.private@example.com', updated_ticket_cc='update.private@example.com')
+ self.queue_public = Queue.objects.create(
+ title='Queue 1',
+ slug='q1',
+ allow_public_submission=True,
+ new_ticket_cc='new.public@example.com',
+ updated_ticket_cc='update.public@example.com')
+ self.queue_private = Queue.objects.create(
+ title='Queue 2',
+ slug='q2',
+ allow_public_submission=False,
+ new_ticket_cc='new.private@example.com',
+ updated_ticket_cc='update.private@example.com')
self.ticket_data = {
- 'title': 'Test Ticket',
- 'description': 'Some Test Ticket',
- }
+ 'title': 'Test Ticket',
+ 'description': 'Some Test Ticket',
+ }
self.client = Client()
@@ -30,7 +40,6 @@ class TicketBasicsTestCase(TestCase):
ticket = Ticket.objects.create(**ticket_data)
self.assertEqual(ticket.ticket_for_url, "q1-%s" % ticket.id)
self.assertEqual(email_count, len(mail.outbox))
-
def test_create_ticket_public(self):
email_count = len(mail.outbox)
@@ -49,7 +58,7 @@ class TicketBasicsTestCase(TestCase):
response = self.client.post(reverse('helpdesk_home'), post_data, follow=True)
last_redirect = response.redirect_chain[-1]
last_redirect_url = last_redirect[0]
- last_redirect_status = last_redirect[1]
+ # last_redirect_status = last_redirect[1]
# Ensure we landed on the "View" page.
# Django 1.9 compatible way of testing this
@@ -63,12 +72,12 @@ class TicketBasicsTestCase(TestCase):
def test_create_ticket_private(self):
email_count = len(mail.outbox)
post_data = {
- 'title': 'Private ticket test',
- 'queue': self.queue_private.id,
- 'submitter_email': 'ticket2.submitter@example.com',
- 'body': 'Test ticket body',
- 'priority': 3,
- }
+ 'title': 'Private ticket test',
+ 'queue': self.queue_private.id,
+ 'submitter_email': 'ticket2.submitter@example.com',
+ 'body': 'Test ticket body',
+ 'priority': 3,
+ }
response = self.client.post(reverse('helpdesk_home'), post_data)
self.assertEqual(response.status_code, 200)
@@ -77,23 +86,34 @@ class TicketBasicsTestCase(TestCase):
def test_create_ticket_customfields(self):
email_count = len(mail.outbox)
- queue_custom = Queue.objects.create(title='Queue 3', slug='q3', allow_public_submission=True, updated_ticket_cc='update.custom@example.com')
- custom_field_1 = CustomField.objects.create(name='textfield', label='Text Field', data_type='varchar', max_length=100, ordering=10, required=False, staff_only=False)
+ queue_custom = Queue.objects.create(
+ title='Queue 3',
+ slug='q3',
+ allow_public_submission=True,
+ updated_ticket_cc='update.custom@example.com')
+ custom_field_1 = CustomField.objects.create(
+ name='textfield',
+ label='Text Field',
+ data_type='varchar',
+ max_length=100,
+ ordering=10,
+ required=False,
+ staff_only=False)
post_data = {
- 'queue': queue_custom.id,
- 'title': 'Ticket with custom text field',
- 'submitter_email': 'ticket3.submitter@example.com',
- 'body': 'Test ticket body',
- 'priority': 3,
- 'custom_textfield': 'This is my custom text.',
- }
+ 'queue': queue_custom.id,
+ 'title': 'Ticket with custom text field',
+ 'submitter_email': 'ticket3.submitter@example.com',
+ 'body': 'Test ticket body',
+ 'priority': 3,
+ 'custom_textfield': 'This is my custom text.',
+ }
response = self.client.post(reverse('helpdesk_home'), post_data, follow=True)
custom_field_1.delete()
last_redirect = response.redirect_chain[-1]
last_redirect_url = last_redirect[0]
- last_redirect_status = last_redirect[1]
+ # last_redirect_status = last_redirect[1]
# Ensure we landed on the "View" page.
# Django 1.9 compatible way of testing this
diff --git a/helpdesk/urls.py b/helpdesk/urls.py
index be64ab81..8a629e63 100644
--- a/helpdesk/urls.py
+++ b/helpdesk/urls.py
@@ -7,21 +7,18 @@ urls.py - Mapping of URL's to our various views. Note we always used NAMED
views for simplicity in linking later on.
"""
-from django.conf import settings
-import django
-if django.get_version().startswith("1.3"):
- from django.conf.urls.defaults import *
-else:
- from django.conf.urls import *
+from django.conf.urls import url
from django.contrib.auth.decorators import login_required
+from django.contrib.auth import views as auth_views
+from django.views.generic import TemplateView
from helpdesk import settings as helpdesk_settings
from helpdesk.views import feeds, staff, public, api, kb
-from django.contrib.auth import views as auth_views
-from django.views.generic import TemplateView
+
class DirectTemplateView(TemplateView):
extra_context = None
+
def get_context_data(self, **kwargs):
context = super(self.__class__, self).get_context_data(**kwargs)
if self.extra_context is not None:
diff --git a/helpdesk/views/api.py b/helpdesk/views/api.py
index 072caee2..dc286202 100644
--- a/helpdesk/views/api.py
+++ b/helpdesk/views/api.py
@@ -11,16 +11,10 @@ The API documentation can be accessed by visiting http://helpdesk/api/help/
through templates/helpdesk/help_api.html.
"""
-from django import forms
from django.contrib.auth import authenticate
-try:
- from django.contrib.auth import get_user_model
- User = get_user_model()
-except ImportError:
- from django.contrib.auth.models import User
+from django.contrib.auth import get_user_model
from django.http import HttpResponse
from django.shortcuts import render
-from django.template import loader, Context
import simplejson
from django.views.decorators.csrf import csrf_exempt
@@ -35,6 +29,8 @@ from helpdesk.models import Ticket, Queue, FollowUp
import warnings
+User = get_user_model()
+
STATUS_OK = 200
STATUS_ERROR = 400
@@ -61,7 +57,9 @@ def api(request, method):
"""
- warnings.warn("django-helpdesk API will be removed in January 2016. See https://github.com/django-helpdesk/django-helpdesk/issues/198 for details.", category=DeprecationWarning)
+ warnings.warn("django-helpdesk API will be removed in January 2016. "
+ "See https://github.com/django-helpdesk/django-helpdesk/issues/198 for details.",
+ category=DeprecationWarning)
if method == 'help':
return render(request, template_name='helpdesk/help_api.html')
@@ -114,7 +112,6 @@ class API:
def __init__(self, request):
self.request = request
-
def api_public_create_ticket(self):
form = TicketForm(self.request.POST)
form.fields['queue'].choices = [[q.id, q.title] for q in Queue.objects.all()]
@@ -126,10 +123,11 @@ class API:
else:
return api_return(STATUS_ERROR, text=form.errors.as_text())
-
def api_public_list_queues(self):
- return api_return(STATUS_OK, simplejson.dumps([{"id": "%s" % q.id, "title": "%s" % q.title} for q in Queue.objects.all()]), json=True)
-
+ return api_return(STATUS_OK, simplejson.dumps([
+ {"id": "%s" % q.id, "title": "%s" % q.title}
+ for q in Queue.objects.all()
+ ]), json=True)
def api_public_find_user(self):
username = self.request.POST.get('username', False)
@@ -141,7 +139,6 @@ class API:
except User.DoesNotExist:
return api_return(STATUS_ERROR, "Invalid username provided")
-
def api_public_delete_ticket(self):
if not self.request.POST.get('confirm', False):
return api_return(STATUS_ERROR, "No confirmation provided")
@@ -155,7 +152,6 @@ class API:
return api_return(STATUS_OK)
-
def api_public_hold_ticket(self):
try:
ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False))
@@ -167,7 +163,6 @@ class API:
return api_return(STATUS_OK)
-
def api_public_unhold_ticket(self):
try:
ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False))
@@ -179,7 +174,6 @@ class API:
return api_return(STATUS_OK)
-
def api_public_add_followup(self):
try:
ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False))
@@ -264,7 +258,6 @@ class API:
return api_return(STATUS_OK)
-
def api_public_resolve(self):
try:
ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False))
@@ -289,7 +282,7 @@ class API:
context = safe_template_context(ticket)
context['resolution'] = f.comment
- subject = '%s %s (Resolved)' % (ticket.ticket, ticket.title)
+ # subject = '%s %s (Resolved)' % (ticket.ticket, ticket.title)
messages_sent_to = []
@@ -324,7 +317,12 @@ class API:
)
messages_sent_to.append(ticket.queue.updated_ticket_cc)
- if ticket.assigned_to and self.request.user != ticket.assigned_to and getattr(ticket.assigned_to.usersettings.settings, 'email_on_ticket_apichange', False) and ticket.assigned_to.email and ticket.assigned_to.email not in messages_sent_to:
+ if ticket.assigned_to and \
+ self.request.user != ticket.assigned_to and \
+ getattr(ticket.assigned_to.usersettings.settings,
+ 'email_on_ticket_apichange', False) and \
+ ticket.assigned_to.email and \
+ ticket.assigned_to.email not in messages_sent_to:
send_templated_mail(
'resolved_resolved',
context,
diff --git a/helpdesk/views/feeds.py b/helpdesk/views/feeds.py
index 4efd75b4..c476ca9c 100644
--- a/helpdesk/views/feeds.py
+++ b/helpdesk/views/feeds.py
@@ -7,11 +7,7 @@ views/feeds.py - A handful of staff-only RSS feeds to provide ticket details
to feed readers or similar software.
"""
-try:
- from django.contrib.auth import get_user_model
- User = get_user_model()
-except ImportError:
- from django.contrib.auth.models import User
+from django.contrib.auth import get_user_model
from django.contrib.syndication.views import Feed
from django.core.urlresolvers import reverse
from django.db.models import Q
@@ -20,6 +16,8 @@ from django.shortcuts import get_object_or_404
from helpdesk.models import Ticket, FollowUp, Queue
+User = get_user_model()
+
class OpenTicketsByUser(Feed):
title_template = 'helpdesk/rss/ticket_title.html'
@@ -101,7 +99,7 @@ class UnassignedTickets(Feed):
title = _('Helpdesk: Unassigned Tickets')
description = _('Unassigned Open and Reopened tickets')
- link = ''#%s?assigned_to=' % reverse('helpdesk_list')
+ link = '' # '%s?assigned_to=' % reverse('helpdesk_list')
def items(self, obj):
return Ticket.objects.filter(
@@ -113,7 +111,6 @@ class UnassignedTickets(Feed):
def item_pubdate(self, item):
return item.created
-
def item_author_name(self, item):
if item.assigned_to:
return item.assigned_to.get_username()
@@ -127,7 +124,7 @@ class RecentFollowUps(Feed):
title = _('Helpdesk: Recent Followups')
description = _('Recent FollowUps, such as e-mail replies, comments, attachments and resolutions')
- link = '/tickets/' # reverse('helpdesk_list')
+ link = '/tickets/' # reverse('helpdesk_list')
def items(self):
return FollowUp.objects.order_by('-date')[:20]
diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py
index 384efd84..a7e7a1e8 100644
--- a/helpdesk/views/kb.py
+++ b/helpdesk/views/kb.py
@@ -8,12 +8,8 @@ views/kb.py - Public-facing knowledgebase views. The knowledgebase is a
resolutions to common problems.
"""
-from datetime import datetime
-
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
-from django.template import RequestContext
-from django.utils.translation import ugettext as _
from helpdesk import settings as helpdesk_settings
from helpdesk.models import KBCategory, KBItem
@@ -22,31 +18,28 @@ from helpdesk.models import KBCategory, KBItem
def index(request):
category_list = KBCategory.objects.all()
# TODO: It'd be great to have a list of most popular items here.
- return render(request, template_name='helpdesk/kb_index.html',
- context = {
- 'kb_categories': category_list,
- 'helpdesk_settings': helpdesk_settings,
- })
+ return render(request, 'helpdesk/kb_index.html', {
+ 'kb_categories': category_list,
+ 'helpdesk_settings': helpdesk_settings,
+ })
def category(request, slug):
category = get_object_or_404(KBCategory, slug__iexact=slug)
items = category.kbitem_set.all()
- return render(request, template_name='helpdesk/kb_category.html',
- context = {
- 'category': category,
- 'items': items,
- 'helpdesk_settings': helpdesk_settings,
- })
+ return render(request, 'helpdesk/kb_category.html', {
+ 'category': category,
+ 'items': items,
+ 'helpdesk_settings': helpdesk_settings,
+ })
def item(request, item):
item = get_object_or_404(KBItem, pk=item)
- return render(request, template_name='helpdesk/kb_item.html',
- context = {
- 'item': item,
- 'helpdesk_settings': helpdesk_settings,
- })
+ return render(request, 'helpdesk/kb_item.html', {
+ 'item': item,
+ 'helpdesk_settings': helpdesk_settings,
+ })
def vote(request, item):
@@ -59,4 +52,3 @@ def vote(request, item):
item.save()
return HttpResponseRedirect(item.get_absolute_url())
-
diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py
index c0f36c80..4ff174c6 100644
--- a/helpdesk/views/public.py
+++ b/helpdesk/views/public.py
@@ -6,16 +6,15 @@ django-helpdesk - A Django powered ticket tracker for small enterprise.
views/public.py - All public facing views, eg non-staff (no authentication
required) views.
"""
-
+from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect, Http404, HttpResponse
-from django.shortcuts import render, get_object_or_404
-from django.template import loader, Context, RequestContext
+from django.http import HttpResponseRedirect
+from django.shortcuts import render
from django.utils.translation import ugettext as _
from helpdesk import settings as helpdesk_settings
from helpdesk.forms import PublicTicketForm
-from helpdesk.lib import send_templated_mail, text_is_spam
+from helpdesk.lib import text_is_spam
from helpdesk.models import Ticket, Queue, UserSettings, KBCategory
@@ -23,7 +22,9 @@ def homepage(request):
if not request.user.is_authenticated() and helpdesk_settings.HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT:
return HttpResponseRedirect(reverse('login'))
- if (request.user.is_staff or (request.user.is_authenticated() and helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE)):
+ if request.user.is_staff or \
+ (request.user.is_authenticated() and
+ helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE):
try:
if request.user.usersettings.settings.get('login_view_ticketlist', False):
return HttpResponseRedirect(reverse('helpdesk_list'))
@@ -34,14 +35,15 @@ def homepage(request):
if request.method == 'POST':
form = PublicTicketForm(request.POST, request.FILES)
- form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.filter(allow_public_submission=True)]
+ form.fields['queue'].choices = [('', '--------')] + [
+ (q.id, q.title) for q in Queue.objects.filter(allow_public_submission=True)]
if form.is_valid():
if text_is_spam(form.cleaned_data['body'], request):
# This submission is spam. Let's not save it.
return render(request, template_name='helpdesk/public_spam.html')
else:
ticket = form.save()
- return HttpResponseRedirect('%s?ticket=%s&email=%s'% (
+ return HttpResponseRedirect('%s?ticket=%s&email=%s' % (
reverse('helpdesk_public_view'),
ticket.ticket_for_url,
ticket.submitter_email)
@@ -59,34 +61,36 @@ def homepage(request):
initial_data['submitter_email'] = request.user.email
form = PublicTicketForm(initial=initial_data)
- form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.filter(allow_public_submission=True)]
+ form.fields['queue'].choices = [('', '--------')] + [
+ (q.id, q.title) for q in Queue.objects.filter(allow_public_submission=True)]
knowledgebase_categories = KBCategory.objects.all()
- return render(request, 'helpdesk/public_homepage.html',
- {
+ return render(request, 'helpdesk/public_homepage.html', {
'form': form,
'helpdesk_settings': helpdesk_settings,
'kb_categories': knowledgebase_categories
- })
+ })
def view_ticket(request):
ticket_req = request.GET.get('ticket', '')
- ticket = False
email = request.GET.get('email', '')
- error_message = ''
if ticket_req and email:
queue, ticket_id = Ticket.queue_and_id_from_query(ticket_req)
try:
ticket = Ticket.objects.get(id=ticket_id, submitter_email__iexact=email)
- except:
- ticket = False
+ except ObjectDoesNotExist:
error_message = _('Invalid ticket ID or e-mail address. Please try again.')
- if ticket:
-
+ return render(request, 'helpdesk/public_view_form.html', {
+ 'ticket': False,
+ 'email': email,
+ 'error_message': error_message,
+ 'helpdesk_settings': helpdesk_settings,
+ })
+ else:
if request.user.is_staff:
redirect_url = reverse('helpdesk_view', args=[ticket_id])
if 'close' in request.GET:
@@ -114,25 +118,16 @@ def view_ticket(request):
if helpdesk_settings.HELPDESK_NAVIGATION_ENABLED:
redirect_url = reverse('helpdesk_view', args=[ticket_id])
- return render(request, 'helpdesk/public_view_ticket.html',
- {
- 'ticket': ticket,
- 'helpdesk_settings': helpdesk_settings,
- 'next': redirect_url,
- })
+ return render(request, 'helpdesk/public_view_ticket.html', {
+ 'ticket': ticket,
+ 'helpdesk_settings': helpdesk_settings,
+ 'next': redirect_url,
+ })
- return render(request, template_name='helpdesk/public_view_form.html',
- context = {
- 'ticket': ticket,
- 'email': email,
- 'error_message': error_message,
- 'helpdesk_settings': helpdesk_settings,
- })
def change_language(request):
return_to = ''
if 'return_to' in request.GET:
return_to = request.GET['return_to']
- return render(request, template_name='helpdesk/public_change_language.html',
- context = {'next': return_to})
+ return render(request, 'helpdesk/public_change_language.html', {'next': return_to})
diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py
index 7faed93e..c14ebf40 100644
--- a/helpdesk/views/staff.py
+++ b/helpdesk/views/staff.py
@@ -7,19 +7,12 @@ views/staff.py - The bulk of the application - provides most business logic and
renders all staff-facing views.
"""
from __future__ import unicode_literals
-from django.utils.encoding import python_2_unicode_compatible
from datetime import datetime, timedelta
-import sys
from django import VERSION
from django.conf import settings
-try:
- from django.contrib.auth import get_user_model
- User = get_user_model()
-except ImportError:
- from django.contrib.auth.models import User
-from django.contrib.auth.decorators import login_required, user_passes_test
-from django.core.files.base import ContentFile
+from django.contrib.auth import get_user_model
+from django.contrib.auth.decorators import user_passes_test
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError, PermissionDenied
from django.core import paginator
@@ -27,7 +20,6 @@ from django.db import connection
from django.db.models import Q
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.shortcuts import render, get_object_or_404
-from django.template import loader, Context, RequestContext
from django.utils.dates import MONTHS_3
from django.utils.translation import ugettext as _
from django.utils.html import escape
@@ -38,19 +30,33 @@ try:
except ImportError:
from datetime import datetime as timezone
-from helpdesk.forms import TicketForm, UserSettingsForm, EmailIgnoreForm, EditTicketForm, TicketCCForm, EditFollowUpForm, TicketDependencyForm
-from helpdesk.lib import send_templated_mail, query_to_dict, apply_query, safe_template_context
-from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC, TicketDependency
+from helpdesk.forms import (
+ TicketForm, UserSettingsForm, EmailIgnoreForm, EditTicketForm, TicketCCForm,
+ EditFollowUpForm, TicketDependencyForm
+)
+from helpdesk.lib import (
+ send_templated_mail, query_to_dict, apply_query, safe_template_context,
+)
+from helpdesk.models import (
+ Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch,
+ IgnoreEmail, TicketCC, TicketDependency,
+)
from helpdesk import settings as helpdesk_settings
+User = get_user_model()
+
+
if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE:
# treat 'normal' users like 'staff'
- staff_member_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active)
+ staff_member_required = user_passes_test(
+ lambda u: u.is_authenticated() and u.is_active)
else:
- staff_member_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active and u.is_staff)
+ staff_member_required = user_passes_test(
+ lambda u: u.is_authenticated() and u.is_active and u.is_staff)
-superuser_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active and u.is_superuser)
+superuser_required = user_passes_test(
+ lambda u: u.is_authenticated() and u.is_active and u.is_superuser)
def _get_user_queues(user):
@@ -60,7 +66,9 @@ def _get_user_queues(user):
:return: A Python list of Queues
"""
all_queues = Queue.objects.all()
- limit_queues_by_user = helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION and not user.is_superuser
+ limit_queues_by_user = \
+ helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION \
+ and not user.is_superuser
if limit_queues_by_user:
id_list = [q.pk for q in all_queues if user.has_perm(q.permission_name)]
return all_queues.filter(pk__in=id_list)
@@ -92,13 +100,13 @@ def dashboard(request):
tickets = Ticket.objects.select_related('queue').filter(
assigned_to=request.user,
).exclude(
- status__in = [Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS],
+ status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS],
)
# closed & resolved tickets, assigned to current user
- tickets_closed_resolved = Ticket.objects.select_related('queue').filter(
+ tickets_closed_resolved = Ticket.objects.select_related('queue').filter(
assigned_to=request.user,
- status__in = [Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS])
+ status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS])
user_queues = _get_user_queues(request.user)
@@ -117,10 +125,10 @@ def dashboard(request):
submitter_email=email_current_user,
).order_by('status')
- Tickets = Ticket.objects.filter(
+ tickets_in_queues = Ticket.objects.filter(
queue__in=user_queues,
)
- basic_ticket_stats = calc_basic_ticket_stats(Tickets)
+ basic_ticket_stats = calc_basic_ticket_stats(tickets_in_queues)
# The following query builds a grid of queues & ticket statuses,
# to be displayed to the user. EG:
@@ -153,15 +161,14 @@ def dashboard(request):
dash_tickets = query_to_dict(cursor.fetchall(), cursor.description)
- return render(request, 'helpdesk/dashboard.html',
- {
- 'user_tickets': tickets,
- 'user_tickets_closed_resolved': tickets_closed_resolved,
- 'unassigned_tickets': unassigned_tickets,
- 'all_tickets_reported_by_current_user': all_tickets_reported_by_current_user,
- 'dash_tickets': dash_tickets,
- 'basic_ticket_stats': basic_ticket_stats,
- })
+ return render(request, 'helpdesk/dashboard.html', {
+ 'user_tickets': tickets,
+ 'user_tickets_closed_resolved': tickets_closed_resolved,
+ 'unassigned_tickets': unassigned_tickets,
+ 'all_tickets_reported_by_current_user': all_tickets_reported_by_current_user,
+ 'dash_tickets': dash_tickets,
+ 'basic_ticket_stats': basic_ticket_stats,
+ })
dashboard = staff_member_required(dashboard)
@@ -171,38 +178,38 @@ def delete_ticket(request, ticket_id):
raise PermissionDenied()
if request.method == 'GET':
- return render(request, template_name='helpdesk/delete_ticket.html',
- context = {
- 'ticket': ticket,
- })
+ return render(request, 'helpdesk/delete_ticket.html', {
+ 'ticket': ticket,
+ })
else:
ticket.delete()
return HttpResponseRedirect(reverse('helpdesk_home'))
delete_ticket = staff_member_required(delete_ticket)
+
def followup_edit(request, ticket_id, followup_id):
- "Edit followup options with an ability to change the ticket."
+ """Edit followup options with an ability to change the ticket."""
followup = get_object_or_404(FollowUp, id=followup_id)
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if request.method == 'GET':
- form = EditFollowUpForm(initial=
- {'title': escape(followup.title),
- 'ticket': followup.ticket,
- 'comment': escape(followup.comment),
- 'public': followup.public,
- 'new_status': followup.new_status,
- })
+ form = EditFollowUpForm(initial={
+ 'title': escape(followup.title),
+ 'ticket': followup.ticket,
+ 'comment': escape(followup.comment),
+ 'public': followup.public,
+ 'new_status': followup.new_status,
+ })
- ticketcc_string, SHOW_SUBSCRIBE = return_ticketccstring_and_show_subscribe(request.user, ticket)
+ ticketcc_string, show_subscribe = \
+ return_ticketccstring_and_show_subscribe(request.user, ticket)
- return render(request, template_name='helpdesk/followup_edit.html',
- context = {
- 'followup': followup,
- 'ticket': ticket,
- 'form': form,
- 'ticketcc_string': ticketcc_string,
+ return render(request, 'helpdesk/followup_edit.html', {
+ 'followup': followup,
+ 'ticket': ticket,
+ 'form': form,
+ 'ticketcc_string': ticketcc_string,
})
elif request.method == 'POST':
form = EditFollowUpForm(request.POST)
@@ -212,7 +219,7 @@ def followup_edit(request, ticket_id, followup_id):
comment = form.cleaned_data['comment']
public = form.cleaned_data['public']
new_status = form.cleaned_data['new_status']
- #will save previous date
+ # will save previous date
old_date = followup.date
new_followup = FollowUp(title=title, date=old_date, ticket=_ticket, comment=comment, public=public, new_status=new_status, )
# keep old user if one did exist before.
@@ -229,8 +236,9 @@ def followup_edit(request, ticket_id, followup_id):
return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket.id]))
followup_edit = staff_member_required(followup_edit)
+
def followup_delete(request, ticket_id, followup_id):
- ''' followup delete for superuser'''
+ """followup delete for superuser"""
ticket = get_object_or_404(Ticket, id=ticket_id)
if not request.user.is_superuser:
@@ -262,8 +270,9 @@ def view_ticket(request, ticket_id):
if 'subscribe' in request.GET:
# Allow the user to subscribe him/herself to the ticket whilst viewing it.
- ticketcc_string, SHOW_SUBSCRIBE = return_ticketccstring_and_show_subscribe(request.user, ticket)
- if SHOW_SUBSCRIBE:
+ ticket_cc, show_subscribe = \
+ return_ticketccstring_and_show_subscribe(request.user, ticket)
+ if show_subscribe:
subscribe_staff_member_to_ticket(ticket, request.user)
return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket.id]))
@@ -292,24 +301,26 @@ def view_ticket(request, ticket_id):
# TODO: shouldn't this template get a form to begin with?
- form = TicketForm(initial={'due_date':ticket.due_date})
+ form = TicketForm(initial={'due_date': ticket.due_date})
- ticketcc_string, SHOW_SUBSCRIBE = return_ticketccstring_and_show_subscribe(request.user, ticket)
+ ticketcc_string, show_subscribe = \
+ return_ticketccstring_and_show_subscribe(request.user, ticket)
- return render(request, template_name='helpdesk/ticket.html',
- context = {
- 'ticket': ticket,
- 'form': form,
- 'active_users': users,
- 'priorities': Ticket.PRIORITY_CHOICES,
- 'preset_replies': PreSetReply.objects.filter(Q(queues=ticket.queue) | Q(queues__isnull=True)),
- 'ticketcc_string': ticketcc_string,
- 'SHOW_SUBSCRIBE': SHOW_SUBSCRIBE,
- })
+ return render(request, 'helpdesk/ticket.html', {
+ 'ticket': ticket,
+ 'form': form,
+ 'active_users': users,
+ 'priorities': Ticket.PRIORITY_CHOICES,
+ 'preset_replies': PreSetReply.objects.filter(
+ Q(queues=ticket.queue) | Q(queues__isnull=True)),
+ 'ticketcc_string': ticketcc_string,
+ 'SHOW_SUBSCRIBE': show_subscribe,
+ })
view_ticket = staff_member_required(view_ticket)
+
def return_ticketccstring_and_show_subscribe(user, ticket):
- ''' used in view_ticket() and followup_edit()'''
+ """used in view_ticket() and followup_edit()"""
# create the ticketcc_string and check whether current user is already
# subscribed
username = user.get_username().upper()
@@ -321,14 +332,14 @@ def return_ticketccstring_and_show_subscribe(user, ticket):
ticketcc_string = ''
all_ticketcc = ticket.ticketcc_set.all()
counter_all_ticketcc = len(all_ticketcc) - 1
- SHOW_SUBSCRIBE = True
+ show_subscribe = True
for i, ticketcc in enumerate(all_ticketcc):
ticketcc_this_entry = str(ticketcc.display)
- ticketcc_string = ticketcc_string + ticketcc_this_entry
+ ticketcc_string += ticketcc_this_entry
if i < counter_all_ticketcc:
- ticketcc_string = ticketcc_string + ', '
+ ticketcc_string += ', '
if strings_to_check.__contains__(ticketcc_this_entry.upper()):
- SHOW_SUBSCRIBE = False
+ show_subscribe = False
# check whether current user is a submitter or assigned to ticket
assignedto_username = str(ticket.assigned_to).upper()
@@ -337,24 +348,30 @@ def return_ticketccstring_and_show_subscribe(user, ticket):
strings_to_check.append(assignedto_username)
strings_to_check.append(submitter_email)
if strings_to_check.__contains__(username) or strings_to_check.__contains__(useremail):
- SHOW_SUBSCRIBE = False
+ show_subscribe = False
- return ticketcc_string, SHOW_SUBSCRIBE
+ return ticketcc_string, show_subscribe
def subscribe_staff_member_to_ticket(ticket, user):
- ''' used in view_ticket() and update_ticket() '''
- ticketcc = TicketCC()
- ticketcc.ticket = ticket
- ticketcc.user = user
- ticketcc.can_view = True
- ticketcc.can_update = True
+ """used in view_ticket() and update_ticket()"""
+ ticketcc = TicketCC(
+ ticket=ticket,
+ user=user,
+ can_view=True,
+ can_update=True,
+ )
ticketcc.save()
def update_ticket(request, ticket_id, public=False):
- if not (public or (request.user.is_authenticated() and request.user.is_active and (request.user.is_staff or helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE))):
- return HttpResponseRedirect('%s?next=%s' % (reverse('login'), request.path))
+ if not (public or (
+ request.user.is_authenticated() and
+ request.user.is_active and (
+ request.user.is_staff or
+ helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE))):
+ return HttpResponseRedirect('%s?next=%s' %
+ (reverse('login'), request.path))
ticket = get_object_or_404(Ticket, id=ticket_id)
@@ -384,7 +401,8 @@ def update_ticket(request, ticket_id, public=False):
title == ticket.title,
priority == int(ticket.priority),
due_date == ticket.due_date,
- (owner == -1) or (not owner and not ticket.assigned_to) or (owner and User.objects.get(id=owner) == ticket.assigned_to),
+ (owner == -1) or (not owner and not ticket.assigned_to) or
+ (owner and User.objects.get(id=owner) == ticket.assigned_to),
])
if no_changes:
return return_to_ticket(request.user, helpdesk_settings, ticket)
@@ -398,7 +416,8 @@ def update_ticket(request, ticket_id, public=False):
# then the following line will give us a crash, since django expects {% if %}
# to be closed with an {% endif %} tag.
- # get_template_from_string was removed in Django 1.8 http://django.readthedocs.org/en/1.8.x/ref/templates/upgrading.html
+ # get_template_from_string was removed in Django 1.8
+ # http://django.readthedocs.org/en/1.8.x/ref/templates/upgrading.html
try:
from django.template import engines
template_func = engines['django'].from_string
@@ -455,7 +474,7 @@ def update_ticket(request, ticket_id, public=False):
files = []
if request.FILES:
- import mimetypes, os
+ import mimetypes
for file in request.FILES.getlist('attachment'):
filename = file.name.encode('ascii', 'ignore')
a = Attachment(
@@ -472,7 +491,6 @@ def update_ticket(request, ticket_id, public=False):
# settings.MAX_EMAIL_ATTACHMENT_SIZE) are sent via email.
files.append([a.filename, a.file])
-
if title != ticket.title:
c = TicketChange(
followup=f,
@@ -517,9 +535,9 @@ def update_ticket(request, ticket_id, public=False):
comment=f.comment,
)
- if public and (f.comment or (f.new_status in (Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS))):
-
-
+ if public and (f.comment or (
+ f.new_status in (Ticket.RESOLVED_STATUS,
+ Ticket.CLOSED_STATUS))):
if f.new_status == Ticket.RESOLVED_STATUS:
template = 'resolved_'
elif f.new_status == Ticket.CLOSED_STATUS:
@@ -554,7 +572,10 @@ def update_ticket(request, ticket_id, public=False):
)
messages_sent_to.append(cc.email_address)
- if ticket.assigned_to and request.user != ticket.assigned_to and ticket.assigned_to.email and ticket.assigned_to.email not in messages_sent_to:
+ if ticket.assigned_to and \
+ request.user != ticket.assigned_to and \
+ ticket.assigned_to.email and \
+ ticket.assigned_to.email not in messages_sent_to:
# We only send e-mails to staff members if the ticket is updated by
# another user. The actual template varies, depending on what has been
# changed.
@@ -567,7 +588,13 @@ def update_ticket(request, ticket_id, public=False):
else:
template_staff = 'updated_owner'
- if (not reassigned or ( reassigned and ticket.assigned_to.usersettings.settings.get('email_on_ticket_assign', False))) or (not reassigned and ticket.assigned_to.usersettings.settings.get('email_on_ticket_change', False)):
+ if (not reassigned or
+ (reassigned and
+ ticket.assigned_to.usersettings.settings.get(
+ 'email_on_ticket_assign', False))) or \
+ (not reassigned and
+ ticket.assigned_to.usersettings.settings.get(
+ 'email_on_ticket_change', False)):
send_templated_mail(
template_staff,
context,
@@ -609,7 +636,7 @@ def update_ticket(request, ticket_id, public=False):
def return_to_ticket(user, helpdesk_settings, ticket):
- ''' Helpder function for update_ticket '''
+ """Helper function for update_ticket"""
if user.is_staff or helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE:
return HttpResponseRedirect(ticket.get_absolute_url())
@@ -638,29 +665,47 @@ def mass_update(request):
if action == 'assign' and t.assigned_to != user:
t.assigned_to = user
t.save()
- f = FollowUp(ticket=t, date=timezone.now(), title=_('Assigned to %(username)s in bulk update' % {'username': user.get_username()}), public=True, user=request.user)
+ f = FollowUp(ticket=t,
+ date=timezone.now(),
+ title=_('Assigned to %(username)s in bulk update' % {
+ 'username': user.get_username()
+ }),
+ public=True,
+ user=request.user)
f.save()
elif action == 'unassign' and t.assigned_to is not None:
t.assigned_to = None
t.save()
- f = FollowUp(ticket=t, date=timezone.now(), title=_('Unassigned in bulk update'), public=True, user=request.user)
+ f = FollowUp(ticket=t,
+ date=timezone.now(),
+ title=_('Unassigned in bulk update'),
+ public=True,
+ user=request.user)
f.save()
elif action == 'close' and t.status != Ticket.CLOSED_STATUS:
t.status = Ticket.CLOSED_STATUS
t.save()
- f = FollowUp(ticket=t, date=timezone.now(), title=_('Closed in bulk update'), public=False, user=request.user, new_status=Ticket.CLOSED_STATUS)
+ f = FollowUp(ticket=t,
+ date=timezone.now(),
+ title=_('Closed in bulk update'),
+ public=False,
+ user=request.user,
+ new_status=Ticket.CLOSED_STATUS)
f.save()
elif action == 'close_public' and t.status != Ticket.CLOSED_STATUS:
t.status = Ticket.CLOSED_STATUS
t.save()
- f = FollowUp(ticket=t, date=timezone.now(), title=_('Closed in bulk update'), public=True, user=request.user, new_status=Ticket.CLOSED_STATUS)
+ f = FollowUp(ticket=t,
+ date=timezone.now(),
+ title=_('Closed in bulk update'),
+ public=True,
+ user=request.user,
+ new_status=Ticket.CLOSED_STATUS)
f.save()
# Send email to Submitter, Owner, Queue CC
context = safe_template_context(t)
- context.update(
- resolution = t.resolution,
- queue = t.queue,
- )
+ context.update(resolution=t.resolution,
+ queue=t.queue)
messages_sent_to = []
@@ -685,7 +730,10 @@ def mass_update(request):
)
messages_sent_to.append(cc.email_address)
- if t.assigned_to and request.user != t.assigned_to and t.assigned_to.email and t.assigned_to.email not in messages_sent_to:
+ if t.assigned_to and \
+ request.user != t.assigned_to and \
+ t.assigned_to.email and \
+ t.assigned_to.email not in messages_sent_to:
send_templated_mail(
'closed_owner',
context,
@@ -695,7 +743,8 @@ def mass_update(request):
)
messages_sent_to.append(t.assigned_to.email)
- if t.queue.updated_ticket_cc and t.queue.updated_ticket_cc not in messages_sent_to:
+ if t.queue.updated_ticket_cc and \
+ t.queue.updated_ticket_cc not in messages_sent_to:
send_templated_mail(
'closed_cc',
context,
@@ -710,6 +759,7 @@ def mass_update(request):
return HttpResponseRedirect(reverse('helpdesk_list'))
mass_update = staff_member_required(mass_update)
+
def ticket_list(request):
context = {}
@@ -745,7 +795,7 @@ def ticket_list(request):
id = None
if id:
- filter = {'queue__slug': queue, 'id': id }
+ filter = {'queue__slug': queue, 'id': id}
else:
try:
query = int(query)
@@ -753,7 +803,7 @@ def ticket_list(request):
query = None
if query:
- filter = {'id': int(query) }
+ filter = {'id': int(query)}
if filter:
try:
@@ -781,13 +831,13 @@ def ticket_list(request):
# Query deserialization failed. (E.g. was a pickled query)
return HttpResponseRedirect(reverse('helpdesk_list'))
- elif not ( 'queue' in request.GET
- or 'assigned_to' in request.GET
- or 'status' in request.GET
- or 'q' in request.GET
- or 'sort' in request.GET
- or 'sortreverse' in request.GET
- ):
+ elif not ('queue' in request.GET
+ or 'assigned_to' in request.GET
+ or 'status' in request.GET
+ or 'q' in request.GET
+ or 'sort' in request.GET
+ or 'sortreverse' in request.GET
+ ):
# Fall-back if no querying is being done, force the list to only
# show open/reopened/resolved (not closed) cases sorted by creation
@@ -830,14 +880,14 @@ def ticket_list(request):
if date_to:
query_params['filtering']['created__lte'] = date_to
- ### KEYWORD SEARCHING
+ # KEYWORD SEARCHING
q = request.GET.get('q', None)
if q:
context = dict(context, query=q)
query_params['search_string'] = q
- ### SORTING
+ # SORTING
sort = request.GET.get('sort', None)
if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority'):
sort = 'created'
@@ -858,7 +908,9 @@ def ticket_list(request):
}
ticket_qs = apply_query(tickets, query_params)
- ticket_paginator = paginator.Paginator(ticket_qs, request.user.usersettings.settings.get('tickets_per_page') or 20)
+ ticket_paginator = paginator.Paginator(
+ ticket_qs,
+ request.user.usersettings.settings.get('tickets_per_page') or 20)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
@@ -871,8 +923,13 @@ def ticket_list(request):
search_message = ''
if 'query' in context and settings.DATABASES['default']['ENGINE'].endswith('sqlite'):
- search_message = _('
Note: Your keyword search is case sensitive because of your database. This means the search will not be accurate. By switching to a different database system you will gain better searching! For more information, read the Django Documentation on string matching in SQLite.') - + search_message = _( + '
Note: Your keyword search is case sensitive ' + 'because of your database. This means the search will not ' + 'be accurate. By switching to a different database system you will gain ' + 'better searching! For more information, read the ' + '' + 'Django Documentation on string matching in SQLite.') import json from helpdesk.lib import b64encode @@ -883,22 +940,20 @@ def ticket_list(request): querydict = request.GET.copy() querydict.pop('page', 1) - - return render(request, 'helpdesk/ticket_list.html', - dict( - context, - query_string=querydict.urlencode(), - tickets=tickets, - user_choices=User.objects.filter(is_active=True,is_staff=True), - queue_choices=user_queues, - status_choices=Ticket.STATUS_CHOICES, - urlsafe_query=urlsafe_query, - user_saved_queries=user_saved_queries, - query_params=query_params, - from_saved_query=from_saved_query, - saved_query=saved_query, - search_message=search_message, - )) + return render(request, 'helpdesk/ticket_list.html', dict( + context, + query_string=querydict.urlencode(), + tickets=tickets, + user_choices=User.objects.filter(is_active=True, is_staff=True), + queue_choices=user_queues, + status_choices=Ticket.STATUS_CHOICES, + urlsafe_query=urlsafe_query, + user_saved_queries=user_saved_queries, + query_params=query_params, + from_saved_query=from_saved_query, + saved_query=saved_query, + search_message=search_message, + )) ticket_list = staff_member_required(ticket_list) @@ -915,12 +970,10 @@ def edit_ticket(request, ticket_id): else: form = EditTicketForm(instance=ticket) - return render(request, template_name='helpdesk/edit_ticket.html', - context = { - 'form': form, - }) + return render(request, 'helpdesk/edit_ticket.html', {'form': form}) edit_ticket = staff_member_required(edit_ticket) + def create_ticket(request): if helpdesk_settings.HELPDESK_STAFF_ONLY_TICKET_OWNERS: assignable_users = User.objects.filter(is_active=True, is_staff=True).order_by(User.USERNAME_FIELD) @@ -929,8 +982,10 @@ def create_ticket(request): if request.method == 'POST': form = TicketForm(request.POST, request.FILES) - form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()] - form.fields['assigned_to'].choices = [('', '--------')] + [[u.id, u.get_username()] for u in assignable_users] + form.fields['queue'].choices = [('', '--------')] + [ + (q.id, q.title) for q in Queue.objects.all()] + form.fields['assigned_to'].choices = [('', '--------')] + [ + (u.id, u.get_username()) for u in assignable_users] if form.is_valid(): ticket = form.save(user=request.user) if _has_access_to_queue(request.user, ticket.queue): @@ -945,13 +1000,14 @@ def create_ticket(request): initial_data['queue'] = request.GET['queue'] form = TicketForm(initial=initial_data) - form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()] - form.fields['assigned_to'].choices = [('', '--------')] + [[u.id, u.get_username()] for u in assignable_users] + form.fields['queue'].choices = [('', '--------')] + [ + (q.id, q.title) for q in Queue.objects.all()] + form.fields['assigned_to'].choices = [('', '--------')] + [ + (u.id, u.get_username()) for u in assignable_users] if helpdesk_settings.HELPDESK_CREATE_TICKET_HIDE_ASSIGNED_TO: form.fields['assigned_to'].widget = forms.HiddenInput() - return render(request, template_name='helpdesk/create_ticket.html', - context = {'form': form}) + return render(request, 'helpdesk/create_ticket.html', {'form': form}) create_ticket = staff_member_required(create_ticket) @@ -960,7 +1016,7 @@ def raw_details(request, type): # in the future it needs to be expanded to include other items. All it # does is return a plain-text representation of an object. - if not type in ('preset',): + if type not in ('preset',): raise Http404 if type == 'preset' and request.GET.get('id', False): @@ -987,11 +1043,11 @@ def hold_ticket(request, ticket_id, unhold=False): title = _('Ticket placed on hold') f = FollowUp( - ticket = ticket, - user = request.user, - title = title, - date = timezone.now(), - public = True, + ticket=ticket, + user=request.user, + title=title, + date=timezone.now(), + public=True, ) f.save() @@ -1007,26 +1063,24 @@ unhold_ticket = staff_member_required(unhold_ticket) def rss_list(request): - return render(request, template_name='helpdesk/rss_list.html', - context = { - 'queues': Queue.objects.all(), - }) + return render(request, 'helpdesk/rss_list.html', {'queues': Queue.objects.all()}) rss_list = staff_member_required(rss_list) def report_index(request): number_tickets = Ticket.objects.all().count() saved_query = request.GET.get('saved_query', None) - return render(request, template_name='helpdesk/report_index.html', - context = { - 'number_tickets': number_tickets, - 'saved_query': saved_query, - }) + return render(request, 'helpdesk/report_index.html', { + 'number_tickets': number_tickets, + 'saved_query': saved_query, + }) report_index = staff_member_required(report_index) def run_report(request, report): - if Ticket.objects.all().count() == 0 or report not in ('queuemonth', 'usermonth', 'queuestatus', 'queuepriority', 'userstatus', 'userpriority', 'userqueue', 'daysuntilticketclosedbymonth'): + if Ticket.objects.all().count() == 0 or report not in ( + 'queuemonth', 'usermonth', 'queuestatus', 'queuepriority', 'userstatus', + 'userpriority', 'userqueue', 'daysuntilticketclosedbymonth'): return HttpResponseRedirect(reverse("helpdesk_report_index")) report_queryset = Ticket.objects.all().select_related().filter( @@ -1191,15 +1245,14 @@ def run_report(request, report): data.append(summarytable[item, hdr]) table.append([item] + data) - return render(request, 'helpdesk/report_output.html', - { - 'title': title, - 'charttype': charttype, - 'data': table, - 'headings': column_headings, - 'from_saved_query': from_saved_query, - 'saved_query': saved_query, - }) + return render(request, 'helpdesk/report_output.html', { + 'title': title, + 'charttype': charttype, + 'data': table, + 'headings': column_headings, + 'from_saved_query': from_saved_query, + 'saved_query': saved_query, + }) run_report = staff_member_required(run_report) @@ -1227,10 +1280,7 @@ def delete_saved_query(request, id): query.delete() return HttpResponseRedirect(reverse('helpdesk_list')) else: - return render(request, template_name='helpdesk/confirm_delete_saved_query.html', - context = { - 'query': query, - }) + return render(request, 'helpdesk/confirm_delete_saved_query.html', {'query': query}) delete_saved_query = staff_member_required(delete_saved_query) @@ -1244,18 +1294,14 @@ def user_settings(request): else: form = UserSettingsForm(s.settings) - return render(request, template_name='helpdesk/user_settings.html', - context = { - 'form': form, - }) + return render(request, 'helpdesk/user_settings.html', {'form': form}) user_settings = staff_member_required(user_settings) def email_ignore(request): - return render(request, template_name='helpdesk/email_ignore_list.html', - context = { - 'ignore_list': IgnoreEmail.objects.all(), - }) + return render(request, 'helpdesk/email_ignore_list.html', { + 'ignore_list': IgnoreEmail.objects.all(), + }) email_ignore = superuser_required(email_ignore) @@ -1263,15 +1309,12 @@ def email_ignore_add(request): if request.method == 'POST': form = EmailIgnoreForm(request.POST) if form.is_valid(): - ignore = form.save() + form.save() return HttpResponseRedirect(reverse('helpdesk_email_ignore')) else: form = EmailIgnoreForm(request.GET) - return render(request, template_name='helpdesk/email_ignore_add.html', - context = { - 'form': form, - }) + return render(request, 'helpdesk/email_ignore_add.html', {'form': form}) email_ignore_add = superuser_required(email_ignore_add) @@ -1281,25 +1324,23 @@ def email_ignore_del(request, id): ignore.delete() return HttpResponseRedirect(reverse('helpdesk_email_ignore')) else: - return render(request, template_name='helpdesk/email_ignore_del.html', - context = { - 'ignore': ignore, - }) + return render(request, 'helpdesk/email_ignore_del.html', {'ignore': ignore}) email_ignore_del = superuser_required(email_ignore_del) + def ticket_cc(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): raise PermissionDenied() copies_to = ticket.ticketcc_set.all() - return render(request, template_name='helpdesk/ticket_cc_list.html', - context = { - 'copies_to': copies_to, - 'ticket': ticket, - }) + return render(request, 'helpdesk/ticket_cc_list.html', { + 'copies_to': copies_to, + 'ticket': ticket, + }) ticket_cc = staff_member_required(ticket_cc) + def ticket_cc_add(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -1311,28 +1352,28 @@ def ticket_cc_add(request, ticket_id): ticketcc = form.save(commit=False) ticketcc.ticket = ticket ticketcc.save() - return HttpResponseRedirect(reverse('helpdesk_ticket_cc', kwargs={'ticket_id': ticket.id})) + return HttpResponseRedirect(reverse('helpdesk_ticket_cc', + kwargs={'ticket_id': ticket.id})) else: form = TicketCCForm() - return render(request, template_name='helpdesk/ticket_cc_add.html', - context = { - 'ticket': ticket, - 'form': form, - }) + return render(request, 'helpdesk/ticket_cc_add.html', { + 'ticket': ticket, + 'form': form, + }) ticket_cc_add = staff_member_required(ticket_cc_add) + def ticket_cc_del(request, ticket_id, cc_id): cc = get_object_or_404(TicketCC, ticket__id=ticket_id, id=cc_id) if request.method == 'POST': cc.delete() - return HttpResponseRedirect(reverse('helpdesk_ticket_cc', kwargs={'ticket_id': cc.ticket.id})) - return render(request, template_name='helpdesk/ticket_cc_del.html', - context = { - 'cc': cc, - }) + return HttpResponseRedirect(reverse('helpdesk_ticket_cc', + kwargs={'ticket_id': cc.ticket.id})) + return render(request, 'helpdesk/ticket_cc_del.html', {'cc': cc}) ticket_cc_del = staff_member_required(ticket_cc_del) + def ticket_dependency_add(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -1347,24 +1388,22 @@ def ticket_dependency_add(request, ticket_id): return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket.id])) else: form = TicketDependencyForm() - return render(request, template_name='helpdesk/ticket_dependency_add.html', - context = { - 'ticket': ticket, - 'form': form, - }) + return render(request, 'helpdesk/ticket_dependency_add.html', { + 'ticket': ticket, + 'form': form, + }) ticket_dependency_add = staff_member_required(ticket_dependency_add) + def ticket_dependency_del(request, ticket_id, dependency_id): dependency = get_object_or_404(TicketDependency, ticket__id=ticket_id, id=dependency_id) if request.method == 'POST': dependency.delete() return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket_id])) - return render(request, template_name='helpdesk/ticket_dependency_del.html', - context = { - 'dependency': dependency, - }) + return render(request, 'helpdesk/ticket_dependency_del.html', {'dependency': dependency}) ticket_dependency_del = staff_member_required(ticket_dependency_del) + def attachment_del(request, ticket_id, attachment_id): ticket = get_object_or_404(Ticket, id=ticket_id) if not _has_access_to_queue(request.user, ticket.queue): @@ -1374,6 +1413,7 @@ def attachment_del(request, ticket_id, attachment_id): return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket_id])) attachment_del = staff_member_required(attachment_del) + def calc_average_nbr_days_until_ticket_resolved(Tickets): nbr_closed_tickets = len(Tickets) days_per_ticket = 0 @@ -1392,9 +1432,10 @@ def calc_average_nbr_days_until_ticket_resolved(Tickets): return mean_per_ticket + def calc_basic_ticket_stats(Tickets): # all not closed tickets (open, reopened, resolved,) - independent of user - all_open_tickets = Tickets.exclude(status = Ticket.CLOSED_STATUS) + all_open_tickets = Tickets.exclude(status=Ticket.CLOSED_STATUS) today = datetime.today() date_30 = date_rel_to_today(today, 30) @@ -1403,57 +1444,66 @@ def calc_basic_ticket_stats(Tickets): date_60_str = date_60.strftime('%Y-%m-%d') # > 0 & <= 30 - ota_le_30 = all_open_tickets.filter(created__gte = date_30_str) + ota_le_30 = all_open_tickets.filter(created__gte=date_30_str) N_ota_le_30 = len(ota_le_30) # >= 30 & <= 60 - ota_le_60_ge_30 = all_open_tickets.filter(created__gte = date_60_str, created__lte = date_30_str) + ota_le_60_ge_30 = all_open_tickets.filter(created__gte=date_60_str, created__lte=date_30_str) N_ota_le_60_ge_30 = len(ota_le_60_ge_30) # >= 60 - ota_ge_60 = all_open_tickets.filter(created__lte = date_60_str) + ota_ge_60 = all_open_tickets.filter(created__lte=date_60_str) N_ota_ge_60 = len(ota_ge_60) # (O)pen (T)icket (S)tats ots = list() # label, number entries, color, sort_string - ots.append(['< 30 days', N_ota_le_30, get_color_for_nbr_days(N_ota_le_30), sort_string(date_30_str, ''), ]) - ots.append(['30 - 60 days', N_ota_le_60_ge_30, get_color_for_nbr_days(N_ota_le_60_ge_30), sort_string(date_60_str, date_30_str), ]) - ots.append(['> 60 days', N_ota_ge_60, get_color_for_nbr_days(N_ota_ge_60), sort_string('', date_60_str), ]) + ots.append(['< 30 days', N_ota_le_30, get_color_for_nbr_days(N_ota_le_30), + sort_string(date_30_str, ''), ]) + ots.append(['30 - 60 days', N_ota_le_60_ge_30, get_color_for_nbr_days(N_ota_le_60_ge_30), + sort_string(date_60_str, date_30_str), ]) + ots.append(['> 60 days', N_ota_ge_60, get_color_for_nbr_days(N_ota_ge_60), + sort_string('', date_60_str), ]) # all closed tickets - independent of user. - all_closed_tickets = Tickets.filter(status = Ticket.CLOSED_STATUS) - average_nbr_days_until_ticket_closed = calc_average_nbr_days_until_ticket_resolved(all_closed_tickets) + all_closed_tickets = Tickets.filter(status=Ticket.CLOSED_STATUS) + average_nbr_days_until_ticket_closed = \ + calc_average_nbr_days_until_ticket_resolved(all_closed_tickets) # all closed tickets that were opened in the last 60 days. - all_closed_last_60_days = all_closed_tickets.filter(created__gte = date_60_str) - average_nbr_days_until_ticket_closed_last_60_days = calc_average_nbr_days_until_ticket_resolved(all_closed_last_60_days) + all_closed_last_60_days = all_closed_tickets.filter(created__gte=date_60_str) + average_nbr_days_until_ticket_closed_last_60_days = \ + calc_average_nbr_days_until_ticket_resolved(all_closed_last_60_days) # put together basic stats - basic_ticket_stats = { 'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed, - 'average_nbr_days_until_ticket_closed_last_60_days': average_nbr_days_until_ticket_closed_last_60_days, - 'open_ticket_stats': ots, } + basic_ticket_stats = { + 'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed, + 'average_nbr_days_until_ticket_closed_last_60_days': + average_nbr_days_until_ticket_closed_last_60_days, + 'open_ticket_stats': ots, + } return basic_ticket_stats + def get_color_for_nbr_days(nbr_days): - ''' ''' if nbr_days < 5: color_string = 'green' - elif nbr_days >= 5 and nbr_days < 10: + elif nbr_days < 10: color_string = 'orange' - else: # more than 10 days + else: # more than 10 days color_string = 'red' return color_string + def days_since_created(today, ticket): return (today - ticket.created).days + def date_rel_to_today(today, offset): return today - timedelta(days = offset) + def sort_string(begin, end): - return 'sort=created&date_from=%s&date_to=%s&status=%s&status=%s&status=%s' %(begin, end, Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS, Ticket.RESOLVED_STATUS) - - - + return 'sort=created&date_from=%s&date_to=%s&status=%s&status=%s&status=%s' % ( + begin, end, Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS, Ticket.RESOLVED_STATUS)