Merge pull request #438 from alexbarcelo/autocodestyle

More Code Style --PEP8-centric
This commit is contained in:
Jonathan Barratt 2016-10-28 10:44:55 +07:00 committed by GitHub
commit 44fd60970b
18 changed files with 372 additions and 357 deletions

View File

@ -20,11 +20,15 @@ install:
- pip install argparse
- pip install coverage
- pip install codecov
- pip install pep8
- pip install -q Django==$DJANGO
- pip install -q -r requirements.txt
before_script:
- "pep8 --exclude=migrations,south_migrations --ignore=E501 helpdesk"
script:
- coverage run --source='.' quicktest.py helpdesk
after_success:
- codecov
- codecov

View File

@ -15,7 +15,7 @@
"""
A python interface to the `Akismet <http://akismet.com>`_ API.
This is a web service for blocking SPAM comments to blogs - or other online
This is a web service for blocking SPAM comments to blogs - or other online
services.
You will need a Wordpress API key, from `wordpress.com <http://wordpress.com>`_.
@ -24,7 +24,7 @@ You should pass in the keyword argument 'agent' to the name of your program,
when you create an Akismet instance. This sets the ``user-agent`` to a useful
value.
The default is : ::
The default is::
Python Interface by Fuzzyman | akismet.py/0.2.0
@ -32,9 +32,9 @@ Whatever you pass in, will replace the *Python Interface by Fuzzyman* part.
**0.2.0** will change with the version of this interface.
Usage example::
from akismet import Akismet
api = Akismet(agent='Test Script')
# if apikey.txt is in place,
# the key will automatically be set
@ -70,7 +70,7 @@ __all__ = (
'Akismet',
'AkismetError',
'APIKeyError',
)
)
__author__ = 'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>'
@ -92,7 +92,7 @@ if urllib2 is None:
req = urlfetch.fetch(url=url, payload=data, method=urlfetch.POST, headers=headers)
if req.status_code == 200:
return req.content
raise Exception('Could not fetch Akismet URL: %s Response code: %s' %
raise Exception('Could not fetch Akismet URL: %s Response code: %s' %
(url, req.status_code))
else:
def _fetch_url(url, data, headers):
@ -127,7 +127,7 @@ class Akismet(object):
def _getURL(self):
"""
Fetch the url to make requests to.
This comprises of api key plus the baseurl.
"""
return 'http://%s.%s' % (self.key, self.baseurl)
@ -142,11 +142,11 @@ class Akismet(object):
def setAPIKey(self, key=None, blog_url=None):
"""
Set the wordpress API key for all transactions.
If you don't specify an explicit API ``key`` and ``blog_url`` it will
attempt to load them from a file called ``apikey.txt`` in the current
directory.
This method is *usually* called automatically when you create a new
``Akismet`` instance.
"""
@ -165,14 +165,14 @@ class Akismet(object):
def verify_key(self):
"""
This equates to the ``verify-key`` call against the akismet API.
It returns ``True`` if the key is valid.
The docs state that you *ought* to call this at the start of the
transaction.
It raises ``APIKeyError`` if you have not yet set an API key.
If the connection to akismet fails, it allows the normal ``HTTPError``
or ``URLError`` to be raised.
(*akismet.py* uses `urllib2 <http://docs.python.org/lib/module-urllib2.html>`_)
@ -195,21 +195,21 @@ class Akismet(object):
"""
This function builds the data structure required by ``comment_check``,
``submit_spam``, and ``submit_ham``.
It modifies the ``data`` dictionary you give it in place. (and so
doesn't return anything)
It raises an ``AkismetError`` if the user IP or user-agent can't be
worked out.
"""
data['comment_content'] = comment
if not 'user_ip' in data:
if 'user_ip' not in data:
try:
val = os.environ['REMOTE_ADDR']
except KeyError:
raise AkismetError("No 'user_ip' supplied")
data['user_ip'] = val
if not 'user_agent' in data:
if 'user_agent' not in data:
try:
val = os.environ['HTTP_USER_AGENT']
except KeyError:
@ -234,44 +234,44 @@ class Akismet(object):
def comment_check(self, comment, data=None, build_data=True, DEBUG=False):
"""
This is the function that checks comments.
It returns ``True`` for spam and ``False`` for ham.
If you set ``DEBUG=True`` then it will return the text of the response,
instead of the ``True`` or ``False`` object.
It raises ``APIKeyError`` if you have not yet set an API key.
If the connection to Akismet fails then the ``HTTPError`` or
``URLError`` will be propogated.
As a minimum it requires the body of the comment. This is the
``comment`` argument.
Akismet requires some other arguments, and allows some optional ones.
The more information you give it, the more likely it is to be able to
make an accurate diagnosise.
You supply these values using a mapping object (dictionary) as the
``data`` argument.
If ``build_data`` is ``True`` (the default), then *akismet.py* will
attempt to fill in as much information as possible, using default
values where necessary. This is particularly useful for programs
running in a {acro;CGI} environment. A lot of useful information
can be supplied from evironment variables (``os.environ``). See below.
You *only* need supply values for which you don't want defaults filled
in for. All values must be strings.
There are a few required values. If they are not supplied, and
defaults can't be worked out, then an ``AkismetError`` is raised.
If you set ``build_data=False`` and a required value is missing an
``AkismetError`` will also be raised.
The normal values (and defaults) are as follows : ::
'user_ip': os.environ['REMOTE_ADDR'] (*)
'user_agent': os.environ['HTTP_USER_AGENT'] (*)
'referrer': os.environ.get('HTTP_REFERER', 'unknown') [#]_
@ -287,16 +287,16 @@ class Akismet(object):
'SERVER_SIGNATURE': os.environ.get('SERVER_SIGNATURE', '')
'SERVER_SOFTWARE': os.environ.get('SERVER_SOFTWARE', '')
'HTTP_ACCEPT': os.environ.get('HTTP_ACCEPT', '')
(*) Required values
You may supply as many additional 'HTTP_*' type values as you wish.
These should correspond to the http headers sent with the request.
.. [#] Note the spelling "referrer". This is a required value by the
akismet api - however, referrer information is not always
supplied by the browser or server. In fact the HTTP protocol
forbids relying on referrer information for functionality in
forbids relying on referrer information for functionality in
programs.
.. [#] The `API docs <http://akismet.com/development/api/>`_ state that this value
can be " *blank, comment, trackback, pingback, or a made up value*
@ -330,7 +330,7 @@ class Akismet(object):
"""
This function is used to tell akismet that a comment it marked as ham,
is really spam.
It takes all the same arguments as ``comment_check``, except for
*DEBUG*.
"""
@ -350,7 +350,7 @@ class Akismet(object):
"""
This function is used to tell akismet that a comment it marked as spam,
is really ham.
It takes all the same arguments as ``comment_check``, except for
*DEBUG*.
"""

View File

@ -35,6 +35,7 @@ class CustomFieldMixin(object):
"""
Mixin that provides a method to turn CustomFields into an actual field
"""
def customfield_to_field(self, field, instanceargs):
if field.data_type == 'varchar':
fieldclass = forms.CharField
@ -76,6 +77,7 @@ class CustomFieldMixin(object):
class EditTicketForm(CustomFieldMixin, forms.ModelForm):
class Meta:
model = Ticket
exclude = ('created', 'modified', 'status', 'on_hold', 'resolution', 'last_escalation', 'assigned_to')
@ -93,11 +95,11 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
except TicketCustomFieldValue.DoesNotExist:
initial_value = None
instanceargs = {
'label': field.label,
'help_text': field.help_text,
'required': field.required,
'initial': initial_value,
}
'label': field.label,
'help_text': field.help_text,
'required': field.required,
'initial': initial_value,
}
self.customfield_to_field(field, instanceargs)
@ -118,6 +120,7 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
class EditFollowUpForm(forms.ModelForm):
class Meta:
model = FollowUp
exclude = ('date', 'user',)
@ -133,28 +136,28 @@ class TicketForm(CustomFieldMixin, forms.Form):
label=_('Queue'),
required=True,
choices=()
)
)
title = forms.CharField(
max_length=100,
required=True,
widget=forms.TextInput(attrs={'size':'60'}),
widget=forms.TextInput(attrs={'size': '60'}),
label=_('Summary of the problem'),
)
)
submitter_email = forms.EmailField(
required=False,
label=_('Submitter E-Mail Address'),
widget=forms.TextInput(attrs={'size':'60'}),
widget=forms.TextInput(attrs={'size': '60'}),
help_text=_('This e-mail address will receive copies of all public '
'updates to this ticket.'),
)
'updates to this ticket.'),
)
body = forms.CharField(
widget=forms.Textarea(attrs={'cols': 47, 'rows': 15}),
label=_('Description of Issue'),
required=True,
)
)
assigned_to = forms.ChoiceField(
choices=(),
@ -162,7 +165,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
label=_('Case owner'),
help_text=_('If you select an owner other than yourself, they\'ll be '
'e-mailed details of this ticket immediately.'),
)
)
priority = forms.ChoiceField(
choices=Ticket.PRIORITY_CHOICES,
@ -170,13 +173,13 @@ class TicketForm(CustomFieldMixin, forms.Form):
initial='3',
label=_('Priority'),
help_text=_('Please select a priority carefully. If unsure, leave it as \'3\'.'),
)
)
due_date = forms.DateTimeField(
widget=extras.SelectDateWidget,
required=False,
label=_('Due on'),
)
)
def clean_due_date(self):
data = self.cleaned_data['due_date']
@ -189,7 +192,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
required=False,
label=_('Attach File'),
help_text=_('You can attach a file such as a document or screenshot to this ticket.'),
)
)
def __init__(self, *args, **kwargs):
"""
@ -198,10 +201,10 @@ class TicketForm(CustomFieldMixin, forms.Form):
super(TicketForm, self).__init__(*args, **kwargs)
for field in CustomField.objects.all():
instanceargs = {
'label': field.label,
'help_text': field.help_text,
'required': field.required,
}
'label': field.label,
'help_text': field.help_text,
'required': field.required,
}
self.customfield_to_field(field, instanceargs)
@ -263,7 +266,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
filename=filename,
mime_type=mimetypes.guess_type(filename)[0] or 'application/octet-stream',
size=file.size,
)
)
a.file.save(file.name, file, save=False)
a.save()
@ -288,7 +291,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(t.submitter_email)
if t.assigned_to and \
@ -303,7 +306,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(t.assigned_to.email)
if q.new_ticket_cc and q.new_ticket_cc not in messages_sent_to:
@ -314,7 +317,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(q.new_ticket_cc)
if q.updated_ticket_cc and \
@ -327,7 +330,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
return t
@ -337,20 +340,20 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
label=_('Queue'),
required=True,
choices=()
)
)
title = forms.CharField(
max_length=100,
required=True,
widget=forms.TextInput(),
label=_('Summary of your query'),
)
)
submitter_email = forms.EmailField(
required=True,
label=_('Your E-Mail Address'),
help_text=_('We will e-mail you when your ticket is updated.'),
)
)
body = forms.CharField(
widget=forms.Textarea(),
@ -358,7 +361,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
required=True,
help_text=_('Please be as descriptive as possible, including any '
'details we may need to address your query.'),
)
)
priority = forms.ChoiceField(
choices=Ticket.PRIORITY_CHOICES,
@ -366,20 +369,20 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
initial='3',
label=_('Urgency'),
help_text=_('Please select a priority carefully.'),
)
)
due_date = forms.DateTimeField(
widget=extras.SelectDateWidget,
required=False,
label=_('Due on'),
)
)
attachment = forms.FileField(
required=False,
label=_('Attach File'),
help_text=_('You can attach a file such as a document or screenshot to this ticket.'),
max_length=1000,
)
)
def __init__(self, *args, **kwargs):
"""
@ -388,10 +391,10 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
super(PublicTicketForm, self).__init__(*args, **kwargs)
for field in CustomField.objects.filter(staff_only=False):
instanceargs = {
'label': field.label,
'help_text': field.help_text,
'required': field.required,
}
'label': field.label,
'help_text': field.help_text,
'required': field.required,
}
self.customfield_to_field(field, instanceargs)
@ -411,7 +414,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
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:
t.assigned_to = q.default_owner
@ -433,7 +436,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
date=timezone.now(),
public=True,
comment=self.cleaned_data['body'],
)
)
f.save()
@ -447,7 +450,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
filename=filename,
mime_type=mimetypes.guess_type(filename)[0] or 'application/octet-stream',
size=file.size,
)
)
a.file.save(file.name, file, save=False)
a.save()
@ -467,7 +470,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(t.submitter_email)
if t.assigned_to and \
@ -481,7 +484,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(t.assigned_to.email)
if q.new_ticket_cc and q.new_ticket_cc not in messages_sent_to:
@ -492,7 +495,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(q.new_ticket_cc)
if q.updated_ticket_cc and \
@ -505,7 +508,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
return t
@ -515,25 +518,25 @@ class UserSettingsForm(forms.Form):
label=_('Show Ticket List on Login?'),
help_text=_('Display the ticket list upon login? Otherwise, the dashboard is shown.'),
required=False,
)
)
email_on_ticket_change = forms.BooleanField(
label=_('E-mail me on ticket change?'),
help_text=_('If you\'re the ticket owner and the ticket is changed via the web by somebody else, do you want to receive an e-mail?'),
required=False,
)
)
email_on_ticket_assign = forms.BooleanField(
label=_('E-mail me when assigned a ticket?'),
help_text=_('If you are assigned a ticket via the web, do you want to receive an e-mail?'),
required=False,
)
)
email_on_ticket_apichange = forms.BooleanField(
label=_('E-mail me when a ticket is changed via the API?'),
help_text=_('If a ticket is altered by the API, do you want to receive an e-mail?'),
required=False,
)
)
tickets_per_page = forms.IntegerField(
label=_('Number of tickets to show per page'),
@ -541,22 +544,24 @@ class UserSettingsForm(forms.Form):
required=False,
min_value=1,
max_value=1000,
)
)
use_email_as_submitter = forms.BooleanField(
label=_('Use my e-mail address when submitting tickets?'),
help_text=_('When you submit a ticket, do you want to automatically use your e-mail address as the submitter address? You can type a different e-mail address when entering the ticket if needed, this option only changes the default.'),
required=False,
)
)
class EmailIgnoreForm(forms.ModelForm):
class Meta:
model = IgnoreEmail
exclude = []
class TicketCCForm(forms.ModelForm):
class Meta:
model = TicketCC
exclude = ('ticket',)
@ -571,6 +576,7 @@ class TicketCCForm(forms.ModelForm):
class TicketDependencyForm(forms.ModelForm):
class Meta:
model = TicketDependency
exclude = ('ticket',)

View File

@ -109,7 +109,7 @@ def send_templated_mail(template_name,
text_part = template_func(
"%s{%% include '%s' %%}" % (t.plain_text, footer_file)
).render(context)
).render(context)
email_html_base_file = os.path.join('helpdesk', locale, 'email_html_base.html')
@ -233,8 +233,8 @@ def safe_template_context(ticket):
context = {
'queue': {},
'ticket': {},
}
'ticket': {}
}
queue = ticket.queue
for field in ('title', 'slug', 'email_address', 'from_address', 'locale'):

View File

@ -21,6 +21,7 @@ from helpdesk.models import EscalationExclusion, Queue
class Command(BaseCommand):
def __init__(self):
BaseCommand.__init__(self)
@ -42,7 +43,7 @@ class Command(BaseCommand):
default=False,
dest='escalate-verbosely',
help='Display a list of dates excluded'),
)
)
def handle(self, *args, **options):
days = options['days']

View File

@ -25,6 +25,7 @@ from helpdesk.models import Queue
class Command(BaseCommand):
def __init__(self):
BaseCommand.__init__(self)
@ -32,7 +33,7 @@ class Command(BaseCommand):
make_option(
'--queues', '-q',
help='Queues to include (default: all). Use queue slugs'),
)
)
def handle(self, *args, **options):
queue_slugs = options['queues']
@ -71,4 +72,3 @@ class Command(BaseCommand):
)
except IntegrityError:
self.stdout.write(" .. permission already existed, skipping")

View File

@ -4,7 +4,7 @@ django-helpdesk - A Django powered ticket tracker for small enterprise.
See LICENSE for details.
create_usersettings.py - Easy way to create helpdesk-specific settings for
create_usersettings.py - Easy way to create helpdesk-specific settings for
users who don't yet have them.
"""

View File

@ -28,6 +28,7 @@ from helpdesk.lib import send_templated_mail, safe_template_context
class Command(BaseCommand):
def __init__(self):
BaseCommand.__init__(self)
@ -40,7 +41,7 @@ class Command(BaseCommand):
action='store_true',
default=False,
help='Display a list of dates excluded'),
)
)
def handle(self, *args, **options):
verbose = False
@ -88,17 +89,17 @@ def escalate_tickets(queues, 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
@ -113,7 +114,7 @@ def escalate_tickets(queues, verbose):
recipients=t.submitter_email,
sender=t.queue.from_address,
fail_silently=True,
)
)
if t.queue.updated_ticket_cc:
send_templated_mail(
@ -122,7 +123,7 @@ def escalate_tickets(queues, verbose):
recipients=t.queue.updated_ticket_cc,
sender=t.queue.from_address,
fail_silently=True,
)
)
if t.assigned_to:
send_templated_mail(
@ -131,14 +132,14 @@ def escalate_tickets(queues, verbose):
recipients=t.assigned_to.email,
sender=t.queue.from_address,
fail_silently=True,
)
)
if verbose:
print(" - Esclating %s from %s>%s" % (
t.ticket,
t.priority+1,
t.priority + 1,
t.priority
)
)
)
f = FollowUp(

View File

@ -48,7 +48,9 @@ STRIPPED_SUBJECT_STRINGS = [
"Automatic reply: ",
]
class Command(BaseCommand):
def __init__(self):
BaseCommand.__init__(self)
@ -58,7 +60,7 @@ class Command(BaseCommand):
default=False,
action='store_true',
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.'
@ -74,7 +76,7 @@ def process_email(quiet=False):
allow_email_submission=True):
if not q.email_box_last_check:
q.email_box_last_check = timezone.now()-timedelta(minutes=30)
q.email_box_last_check = timezone.now() - timedelta(minutes=30)
if not q.email_box_interval:
q.email_box_interval = 0
@ -176,7 +178,7 @@ def process_queue(q, quiet=False):
ticket = ticket_from_message(message=data[0][1], queue=q, quiet=quiet)
if ticket:
server.store(num, '+FLAGS', '\\Deleted')
server.expunge()
server.close()
server.logout()
@ -221,7 +223,7 @@ def ticket_from_message(message, queue, quiet):
return False
return True
matchobj = re.match(r".*\["+queue.slug+"-(?P<id>\d+)\]", subject)
matchobj = re.match(r".*\[" + queue.slug + "-(?P<id>\d+)\]", subject)
if matchobj:
# This is a reply or forward.
ticket = matchobj.group('id')
@ -254,7 +256,7 @@ def ticket_from_message(message, queue, quiet):
'filename': name,
'content': part.get_payload(decode=True),
'type': part.get_content_type()},
)
)
counter += 1
@ -317,7 +319,7 @@ def ticket_from_message(message, queue, quiet):
if t.status == Ticket.REOPENED_STATUS:
f.new_status = Ticket.REOPENED_STATUS
f.title = _('Ticket Re-Opened by E-Mail Received from %(sender_email)s' % {'sender_email': sender_email})
f.save()
if not quiet:
@ -332,7 +334,7 @@ def ticket_from_message(message, queue, quiet):
filename=filename,
mime_type=file['type'],
size=len(file['content']),
)
)
a.file.save(filename, ContentFile(file['content']), save=False)
a.save()
if not quiet:
@ -349,7 +351,7 @@ def ticket_from_message(message, queue, quiet):
recipients=sender_email,
sender=queue.from_address,
fail_silently=True,
)
)
if queue.new_ticket_cc:
send_templated_mail(
@ -358,7 +360,7 @@ def ticket_from_message(message, queue, quiet):
recipients=queue.new_ticket_cc,
sender=queue.from_address,
fail_silently=True,
)
)
if queue.updated_ticket_cc and queue.updated_ticket_cc != queue.new_ticket_cc:
send_templated_mail(
@ -367,7 +369,7 @@ def ticket_from_message(message, queue, quiet):
recipients=queue.updated_ticket_cc,
sender=queue.from_address,
fail_silently=True,
)
)
else:
context.update(comment=f.comment)
@ -384,7 +386,7 @@ def ticket_from_message(message, queue, quiet):
recipients=t.assigned_to.email,
sender=queue.from_address,
fail_silently=True,
)
)
if queue.updated_ticket_cc:
send_templated_mail(
@ -393,11 +395,10 @@ def ticket_from_message(message, queue, quiet):
recipients=queue.updated_ticket_cc,
sender=queue.from_address,
fail_silently=True,
)
)
return t
if __name__ == '__main__':
process_email()

View File

@ -36,7 +36,7 @@ class Queue(models.Model):
title = models.CharField(
_('Title'),
max_length=100,
)
)
slug = models.SlugField(
_('Slug'),
@ -44,7 +44,7 @@ class Queue(models.Model):
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.'),
)
)
email_address = models.EmailField(
_('E-Mail Address'),
@ -53,7 +53,7 @@ class Queue(models.Model):
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.'),
)
)
locale = models.CharField(
_('Locale'),
@ -62,14 +62,14 @@ class Queue(models.Model):
null=True,
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?'),
)
)
allow_email_submission = models.BooleanField(
_('Allow E-Mail Submission?'),
@ -77,7 +77,7 @@ class Queue(models.Model):
default=False,
help_text=_('Do you want to poll the e-mail box below for new '
'tickets?'),
)
)
escalate_days = models.IntegerField(
_('Escalation Days'),
@ -85,7 +85,7 @@ class Queue(models.Model):
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.'),
)
)
new_ticket_cc = models.CharField(
_('New Ticket CC Address'),
@ -95,7 +95,7 @@ class Queue(models.Model):
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.'),
)
)
updated_ticket_cc = models.CharField(
_('Updated Ticket CC Address'),
@ -106,7 +106,7 @@ class Queue(models.Model):
'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(
_('E-Mail Box Type'),
@ -116,7 +116,7 @@ class Queue(models.Model):
null=True,
help_text=_('E-Mail server type for creating tickets automatically '
'from a mailbox - both POP3 and IMAP are supported.'),
)
)
email_box_host = models.CharField(
_('E-Mail Hostname'),
@ -125,7 +125,7 @@ class Queue(models.Model):
null=True,
help_text=_('Your e-mail server address - either the domain name or '
'IP address. May be "localhost".'),
)
)
email_box_port = models.IntegerField(
_('E-Mail Port'),
@ -134,7 +134,7 @@ class Queue(models.Model):
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.'),
)
)
email_box_ssl = models.BooleanField(
_('Use SSL for E-Mail?'),
@ -142,7 +142,7 @@ class Queue(models.Model):
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.'),
)
)
email_box_user = models.CharField(
_('E-Mail Username'),
@ -150,7 +150,7 @@ class Queue(models.Model):
blank=True,
null=True,
help_text=_('Username for accessing this mailbox.'),
)
)
email_box_pass = models.CharField(
_('E-Mail Password'),
@ -158,7 +158,7 @@ class Queue(models.Model):
blank=True,
null=True,
help_text=_('Password for the above username'),
)
)
email_box_imap_folder = models.CharField(
_('IMAP Folder'),
@ -169,7 +169,7 @@ class Queue(models.Model):
'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(
_('Django auth permission name'),
@ -178,8 +178,7 @@ class Queue(models.Model):
null=True,
editable=False,
help_text=_('Name used in the django.contrib.auth permission system'),
)
)
email_box_interval = models.IntegerField(
_('E-Mail Check Interval'),
@ -187,14 +186,14 @@ class Queue(models.Model):
blank=True,
null=True,
default='5',
)
)
email_box_last_check = models.DateTimeField(
blank=True,
null=True,
editable=False,
# This is updated by management/commands/get_mail.py.
)
)
socks_proxy_type = models.CharField(
_('Socks Proxy Type'),
@ -347,24 +346,24 @@ class Ticket(models.Model):
title = models.CharField(
_('Title'),
max_length=200,
)
)
queue = models.ForeignKey(
Queue,
verbose_name=_('Queue'),
)
)
created = models.DateTimeField(
_('Created'),
blank=True,
help_text=_('Date this ticket was first created'),
)
)
modified = models.DateTimeField(
_('Modified'),
blank=True,
help_text=_('Date this ticket was most recently changed.'),
)
)
submitter_email = models.EmailField(
_('Submitter E-Mail'),
@ -372,7 +371,7 @@ class Ticket(models.Model):
null=True,
help_text=_('The submitter will receive an email for all public '
'follow-ups left for this task.'),
)
)
assigned_to = models.ForeignKey(
settings.AUTH_USER_MODEL,
@ -380,34 +379,34 @@ class Ticket(models.Model):
blank=True,
null=True,
verbose_name=_('Assigned to'),
)
)
status = models.IntegerField(
_('Status'),
choices=STATUS_CHOICES,
default=OPEN_STATUS,
)
)
on_hold = models.BooleanField(
_('On Hold'),
blank=True,
default=False,
help_text=_('If a ticket is on hold, it will not automatically be escalated.'),
)
)
description = models.TextField(
_('Description'),
blank=True,
null=True,
help_text=_('The content of the customers query.'),
)
)
resolution = models.TextField(
_('Resolution'),
blank=True,
null=True,
help_text=_('The resolution provided to the customer by our staff.'),
)
)
priority = models.IntegerField(
_('Priority'),
@ -415,13 +414,13 @@ class Ticket(models.Model):
default=3,
blank=3,
help_text=_('1 = Highest Priority, 5 = Low Priority'),
)
)
due_date = models.DateTimeField(
_('Due on'),
blank=True,
null=True,
)
)
last_escalation = models.DateTimeField(
blank=True,
@ -429,7 +428,7 @@ class Ticket(models.Model):
editable=False,
help_text=_('The date this ticket was last escalated - updated '
'automatically by management/commands/escalate_tickets.py.'),
)
)
def _get_assigned_to(self):
""" Custom property to allow us to easily print 'Unassigned' if a
@ -479,7 +478,8 @@ class Ticket(models.Model):
Displays the ticket status, with an "On Hold" message if needed.
"""
held_msg = ''
if self.on_hold: held_msg = _(' - On Hold')
if self.on_hold:
held_msg = _(' - On Hold')
dep_msg = ''
if not self.can_be_resolved:
dep_msg = _(' - Open dependencies')
@ -502,7 +502,7 @@ class Ticket(models.Model):
reverse('helpdesk_public_view'),
self.ticket_for_url,
self.submitter_email
)
)
ticket_url = property(_get_ticket_url)
def _get_staff_url(self):
@ -519,8 +519,8 @@ class Ticket(models.Model):
return u"http://%s%s" % (
site.domain,
reverse('helpdesk_view',
args=[self.id])
)
args=[self.id])
)
staff_url = property(_get_staff_url)
def _can_be_resolved(self):
@ -569,6 +569,7 @@ class Ticket(models.Model):
class FollowUpManager(models.Manager):
def private_followups(self):
return self.filter(public=False)
@ -593,25 +594,25 @@ class FollowUp(models.Model):
ticket = models.ForeignKey(
Ticket,
verbose_name=_('Ticket'),
)
)
date = models.DateTimeField(
_('Date'),
default = timezone.now
)
default=timezone.now
)
title = models.CharField(
_('Title'),
max_length=200,
blank=True,
null=True,
)
)
comment = models.TextField(
_('Comment'),
blank=True,
null=True,
)
)
public = models.BooleanField(
_('Public'),
@ -619,14 +620,14 @@ class FollowUp(models.Model):
default=False,
help_text=_('Public tickets are viewable by the submitter and all '
'staff, but non-public tickets can only be seen by staff.'),
)
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True,
null=True,
verbose_name=_('User'),
)
)
new_status = models.IntegerField(
_('New Status'),
@ -634,12 +635,12 @@ class FollowUp(models.Model):
blank=True,
null=True,
help_text=_('If the status was changed, what was it changed to?'),
)
)
objects = FollowUpManager()
class Meta:
ordering = ['date']
ordering = ('date',)
verbose_name = _('Follow-up')
verbose_name_plural = _('Follow-ups')
@ -666,24 +667,24 @@ class TicketChange(models.Model):
followup = models.ForeignKey(
FollowUp,
verbose_name=_('Follow-up'),
)
)
field = models.CharField(
_('Field'),
max_length=100,
)
)
old_value = models.TextField(
_('Old Value'),
blank=True,
null=True,
)
)
new_value = models.TextField(
_('New Value'),
blank=True,
null=True,
)
)
def __str__(self):
out = '%s ' % self.field
@ -695,7 +696,7 @@ class TicketChange(models.Model):
out += ugettext('changed from "%(old_value)s" to "%(new_value)s"') % {
'old_value': self.old_value,
'new_value': self.new_value
}
}
return out
class Meta:
@ -711,7 +712,7 @@ def attachment_path(instance, filename):
import os
from django.conf import settings
os.umask(0)
path = 'helpdesk/attachments/%s/%s' % (instance.followup.ticket.ticket_for_url, instance.followup.id )
path = 'helpdesk/attachments/%s/%s' % (instance.followup.ticket.ticket_for_url, instance.followup.id)
att_path = os.path.join(settings.MEDIA_ROOT, path)
if settings.DEFAULT_FILE_STORAGE == "django.core.files.storage.FileSystemStorage":
if not os.path.exists(att_path):
@ -729,28 +730,28 @@ class Attachment(models.Model):
followup = models.ForeignKey(
FollowUp,
verbose_name=_('Follow-up'),
)
)
file = models.FileField(
_('File'),
upload_to=attachment_path,
max_length=1000,
)
)
filename = models.CharField(
_('Filename'),
max_length=1000,
)
)
mime_type = models.CharField(
_('MIME Type'),
max_length=255,
)
)
size = models.IntegerField(
_('Size'),
help_text=_('Size of this file in bytes'),
)
)
def get_upload_to(self, field_attname):
""" Get upload_to path specific to this item """
@ -759,13 +760,13 @@ class Attachment(models.Model):
return u'helpdesk/attachments/%s/%s' % (
self.followup.ticket.ticket_for_url,
self.followup.id
)
)
def __str__(self):
return '%s' % self.filename
class Meta:
ordering = ['filename',]
ordering = ('filename',)
verbose_name = _('Attachment')
verbose_name_plural = _('Attachments')
@ -783,7 +784,7 @@ class PreSetReply(models.Model):
queue, and the body text is fetched via AJAX.
"""
class Meta:
ordering = ['name', ]
ordering = ('name',)
verbose_name = _('Pre-set reply')
verbose_name_plural = _('Pre-set replies')
@ -792,21 +793,21 @@ class PreSetReply(models.Model):
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.'),
)
)
name = models.CharField(
_('Name'),
max_length=100,
help_text=_('Only used to assist users with selecting a reply - not '
'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.'),
)
)
def __str__(self):
return '%s' % self.name
@ -829,17 +830,17 @@ class EscalationExclusion(models.Model):
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.'),
)
)
name = models.CharField(
_('Name'),
max_length=100,
)
)
date = models.DateField(
_('Date'),
help_text=_('Date on which escalation should not happen'),
)
)
def __str__(self):
return '%s' % self.name
@ -862,7 +863,7 @@ class EmailTemplate(models.Model):
template_name = models.CharField(
_('Template Name'),
max_length=100,
)
)
subject = models.CharField(
_('Subject'),
@ -870,7 +871,7 @@ class EmailTemplate(models.Model):
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.'),
)
)
heading = models.CharField(
_('Heading'),
@ -878,19 +879,19 @@ class EmailTemplate(models.Model):
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.'),
)
)
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 }}.'),
)
)
html = models.TextField(
_('HTML'),
help_text=_('The same context is available here as in plain_text, above.'),
)
)
locale = models.CharField(
_('Locale'),
@ -898,13 +899,13 @@ class EmailTemplate(models.Model):
blank=True,
null=True,
help_text=_('Locale of this template.'),
)
)
def __str__(self):
return '%s' % self.template_name
class Meta:
ordering = ['template_name', 'locale']
ordering = ('template_name', 'locale')
verbose_name = _('e-mail template')
verbose_name_plural = _('e-mail templates')
@ -919,21 +920,21 @@ class KBCategory(models.Model):
title = models.CharField(
_('Title'),
max_length=100,
)
)
slug = models.SlugField(
_('Slug'),
)
)
description = models.TextField(
_('Description'),
)
)
def __str__(self):
return '%s' % self.title
class Meta:
ordering = ['title',]
ordering = ('title',)
verbose_name = _('Knowledge base category')
verbose_name_plural = _('Knowledge base categories')
@ -951,38 +952,38 @@ class KBItem(models.Model):
category = models.ForeignKey(
KBCategory,
verbose_name=_('Category'),
)
)
title = models.CharField(
_('Title'),
max_length=100,
)
)
question = models.TextField(
_('Question'),
)
)
answer = models.TextField(
_('Answer'),
)
)
votes = models.IntegerField(
_('Votes'),
help_text=_('Total number of votes cast for this item'),
default=0,
)
)
recommendations = models.IntegerField(
_('Positive Votes'),
help_text=_('Number of votes for this item which were POSITIVE.'),
default=0,
)
)
last_updated = models.DateTimeField(
_('Last Updated'),
help_text=_('The date on which this question was most recently changed.'),
blank=True,
)
)
def save(self, *args, **kwargs):
if not self.last_updated:
@ -1000,7 +1001,7 @@ class KBItem(models.Model):
return '%s' % self.title
class Meta:
ordering = ['title',]
ordering = ('title',)
verbose_name = _('Knowledge base item')
verbose_name_plural = _('Knowledge base items')
@ -1024,25 +1025,25 @@ class SavedSearch(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=_('User'),
)
)
title = models.CharField(
_('Query Name'),
max_length=100,
help_text=_('User-provided name for this query'),
)
)
shared = models.BooleanField(
_('Shared With Other Users?'),
blank=True,
default=False,
help_text=_('Should other users see this query?'),
)
)
query = models.TextField(
_('Search Query'),
help_text=_('Pickled query object. Be wary changing this.'),
)
)
def __str__(self):
if self.shared:
@ -1073,7 +1074,7 @@ class UserSettings(models.Model):
'Do not change this field via the admin.'),
blank=True,
null=True,
)
)
def _set_settings(self, data):
# data should always be a Python dictionary.
@ -1138,26 +1139,26 @@ class IgnoreEmail(models.Model):
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.'),
)
)
name = models.CharField(
_('Name'),
max_length=100,
)
)
date = models.DateField(
_('Date'),
help_text=_('Date on which this e-mail address was added'),
blank=True,
editable=False
)
)
email_address = models.CharField(
_('E-Mail Address'),
max_length=150,
help_text=_('Enter a full e-mail address, or portions with '
'wildcards, eg *@domain.com or postmaster@*.'),
)
)
keep_in_mailbox = models.BooleanField(
_('Save Emails in Mailbox?'),
@ -1165,7 +1166,7 @@ class IgnoreEmail(models.Model):
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.'),
)
)
def __str__(self):
return '%s' % self.name
@ -1213,7 +1214,7 @@ class TicketCC(models.Model):
ticket = models.ForeignKey(
Ticket,
verbose_name=_('Ticket'),
)
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
@ -1221,28 +1222,28 @@ class TicketCC(models.Model):
null=True,
help_text=_('User who wishes to receive updates for this ticket.'),
verbose_name=_('User'),
)
)
email = models.EmailField(
_('E-Mail Address'),
blank=True,
null=True,
help_text=_('For non-user followers, enter their e-mail address'),
)
)
can_view = models.BooleanField(
_('Can View Ticket?'),
blank=True,
default=False,
help_text=_('Can this CC login to view the ticket details?'),
)
)
can_update = models.BooleanField(
_('Can Update Ticket?'),
blank=True,
default=False,
help_text=_('Can this CC login and update the ticket?'),
)
)
def _email_address(self):
if self.user and self.user.email is not None:
@ -1263,6 +1264,7 @@ class TicketCC(models.Model):
class CustomFieldManager(models.Manager):
def get_queryset(self):
return super(CustomFieldManager, self).get_queryset().order_by('ordering')
@ -1278,77 +1280,77 @@ class CustomField(models.Model):
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,
)
)
label = models.CharField(
_('Label'),
max_length=30,
help_text=_('The display label for this field'),
)
)
help_text = models.TextField(
_('Help Text'),
help_text=_('Shown to the user when editing the ticket'),
blank=True,
null=True
)
)
DATA_TYPE_CHOICES = (
('varchar', _('Character (single line)')),
('text', _('Text (multi-line)')),
('integer', _('Integer')),
('decimal', _('Decimal')),
('list', _('List')),
('boolean', _('Boolean (checkbox yes/no)')),
('date', _('Date')),
('time', _('Time')),
('datetime', _('Date & Time')),
('email', _('E-Mail Address')),
('url', _('URL')),
('ipaddress', _('IP Address')),
('slug', _('Slug')),
)
('varchar', _('Character (single line)')),
('text', _('Text (multi-line)')),
('integer', _('Integer')),
('decimal', _('Decimal')),
('list', _('List')),
('boolean', _('Boolean (checkbox yes/no)')),
('date', _('Date')),
('time', _('Time')),
('datetime', _('Date & Time')),
('email', _('E-Mail Address')),
('url', _('URL')),
('ipaddress', _('IP Address')),
('slug', _('Slug')),
)
data_type = models.CharField(
_('Data Type'),
max_length=100,
help_text=_('Allows you to restrict the data entered into this field'),
choices=DATA_TYPE_CHOICES,
)
)
max_length = models.IntegerField(
_('Maximum Length (characters)'),
blank=True,
null=True,
)
)
decimal_places = models.IntegerField(
_('Decimal Places'),
help_text=_('Only used for decimal fields'),
blank=True,
null=True,
)
)
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.'),
)
)
list_values = models.TextField(
_('List Values'),
help_text=_('For list fields only. Enter one option per line.'),
blank=True,
null=True,
)
)
ordering = models.IntegerField(
_('Ordering'),
help_text=_('Lower numbers are displayed first; higher numbers are listed later'),
blank=True,
null=True,
)
)
def _choices_as_array(self):
from StringIO import StringIO
@ -1362,14 +1364,14 @@ class CustomField(models.Model):
_('Required?'),
help_text=_('Does the user have to enter a value for this field?'),
default=False,
)
)
staff_only = models.BooleanField(
_('Staff Only?'),
help_text=_('If this is ticked, then the public submission form '
'will NOT show this field'),
default=False,
)
)
objects = CustomFieldManager()
@ -1386,12 +1388,12 @@ class TicketCustomFieldValue(models.Model):
ticket = models.ForeignKey(
Ticket,
verbose_name=_('Ticket'),
)
)
field = models.ForeignKey(
CustomField,
verbose_name=_('Field'),
)
)
value = models.TextField(blank=True, null=True)
@ -1420,13 +1422,13 @@ class TicketDependency(models.Model):
Ticket,
verbose_name=_('Ticket'),
related_name='ticketdependency',
)
)
depends_on = models.ForeignKey(
Ticket,
verbose_name=_('Depends On Ticket'),
related_name='depends_on',
)
)
def __str__(self):
return '%s / %s' % (self.ticket, self.depends_on)

View File

@ -1,7 +1,7 @@
"""
django-helpdesk - A Django powered ticket tracker for small enterprise.
templatetags/load_helpdesk_settings.py - returns the settings as defined in
templatetags/load_helpdesk_settings.py - returns the settings as defined in
django-helpdesk/helpdesk/settings.py
"""
from __future__ import print_function

View File

@ -1,7 +1,7 @@
"""
django-helpdesk - A Django powered ticket tracker for small enterprise.
templatetags/saved_queries.py - This template tag returns previously saved
templatetags/saved_queries.py - This template tag returns previously saved
queries. Therefore you don't need to modify
any views.
"""
@ -17,8 +17,8 @@ def saved_queries(user):
return user_saved_queries
except Exception as e:
import sys
print >> sys.stderr, "'saved_queries' template tag (django-helpdesk) crashed with following error:"
print >> sys.stderr, e
print >> sys.stderr, "'saved_queries' template tag (django-helpdesk) crashed with following error:"
print >> sys.stderr, e
return ''
register = Library()

View File

@ -11,6 +11,7 @@ class PublicActionsTestCase(TestCase):
- Add a followup
- Close resolved case
"""
def setUp(self):
"""
Create a queue & ticket we can use for later tests.
@ -41,26 +42,26 @@ class PublicActionsTestCase(TestCase):
resolution_text = 'Resolved by test script'
ticket = Ticket.objects.get(id=self.ticket.id)
ticket.status = Ticket.RESOLVED_STATUS
ticket.resolution = resolution_text
ticket.save()
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'))
ticket = Ticket.objects.get(id=self.ticket.id)
self.assertEqual(response.status_code, 302)
self.assertTemplateNotUsed(response, 'helpdesk/public_view_form.html')
self.assertEqual(ticket.status, Ticket.CLOSED_STATUS)
self.assertEqual(ticket.resolution, resolution_text)
self.assertEqual(current_followups+1, ticket.followup_set.all().count())
self.assertEqual(current_followups + 1, ticket.followup_set.all().count())
ticket.resolution = old_resolution
ticket.status = old_status
ticket.save()

View File

@ -48,12 +48,12 @@ class TicketBasicsTestCase(TestCase):
self.assertEqual(response.status_code, 200)
post_data = {
'title': 'Test ticket title',
'queue': self.queue_public.id,
'submitter_email': 'ticket1.submitter@example.com',
'body': 'Test ticket body',
'priority': 3,
}
'title': 'Test ticket title',
'queue': self.queue_public.id,
'submitter_email': 'ticket1.submitter@example.com',
'body': 'Test ticket body',
'priority': 3,
}
response = self.client.post(reverse('helpdesk_home'), post_data, follow=True)
last_redirect = response.redirect_chain[-1]
@ -67,7 +67,7 @@ class TicketBasicsTestCase(TestCase):
self.assertEqual(urlparts.path, reverse('helpdesk_public_view'))
# Ensure submitter, new-queue + update-queue were all emailed.
self.assertEqual(email_count+3, len(mail.outbox))
self.assertEqual(email_count + 3, len(mail.outbox))
def test_create_ticket_private(self):
email_count = len(mail.outbox)
@ -114,7 +114,7 @@ class TicketBasicsTestCase(TestCase):
last_redirect = response.redirect_chain[-1]
last_redirect_url = last_redirect[0]
# last_redirect_status = last_redirect[1]
# Ensure we landed on the "View" page.
# Django 1.9 compatible way of testing this
# https://docs.djangoproject.com/en/1.9/releases/1.9/#http-redirects-no-longer-forced-to-absolute-uris
@ -122,4 +122,4 @@ class TicketBasicsTestCase(TestCase):
self.assertEqual(urlparts.path, reverse('helpdesk_public_view'))
# Ensure only two e-mails were sent - submitter & updated.
self.assertEqual(email_count+2, len(mail.outbox))
self.assertEqual(email_count + 2, len(mail.outbox))

View File

@ -71,7 +71,7 @@ def api(request, method):
request.user = authenticate(
username=request.POST.get('user', False),
password=request.POST.get('password'),
)
)
if request.user is None:
return api_return(STATUS_ERROR_PERMISSIONS)
@ -109,6 +109,7 @@ def api_return(status, text='', json=False):
class API:
def __init__(self, request):
self.request = request
@ -195,7 +196,7 @@ class API:
comment=message,
user=self.request.user,
title='Comment Added',
)
)
if public:
f.public = True
@ -214,7 +215,7 @@ class API:
recipients=ticket.submitter_email,
sender=ticket.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(ticket.submitter_email)
if public:
@ -226,7 +227,7 @@ class API:
recipients=cc.email_address,
sender=ticket.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(cc.email_address)
if ticket.queue.updated_ticket_cc and ticket.queue.updated_ticket_cc not in messages_sent_to:
@ -236,7 +237,7 @@ class API:
recipients=ticket.queue.updated_ticket_cc,
sender=ticket.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(ticket.queue.updated_ticket_cc)
if (
@ -276,7 +277,7 @@ class API:
user=self.request.user,
title='Resolved',
public=True,
)
)
f.save()
context = safe_template_context(ticket)
@ -293,7 +294,7 @@ class API:
recipients=ticket.submitter_email,
sender=ticket.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(ticket.submitter_email)
for cc in ticket.ticketcc_set.all():
@ -304,7 +305,7 @@ class API:
recipients=cc.email_address,
sender=ticket.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(cc.email_address)
if ticket.queue.updated_ticket_cc and ticket.queue.updated_ticket_cc not in messages_sent_to:
@ -314,7 +315,7 @@ class API:
recipients=ticket.queue.updated_ticket_cc,
sender=ticket.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(ticket.queue.updated_ticket_cc)
if ticket.assigned_to and \
@ -329,7 +330,7 @@ class API:
recipients=ticket.assigned_to.email,
sender=ticket.queue.from_address,
fail_silently=True,
)
)
ticket.resoltuion = f.comment
ticket.status = Ticket.RESOLVED_STATUS

View File

@ -37,22 +37,22 @@ class OpenTicketsByUser(Feed):
return _("Helpdesk: Open Tickets in queue %(queue)s for %(username)s") % {
'queue': obj['queue'].title,
'username': obj['user'].get_username(),
}
}
else:
return _("Helpdesk: Open Tickets for %(username)s") % {
'username': obj['user'].get_username(),
}
}
def description(self, obj):
if obj['queue']:
return _("Open and Reopened Tickets in queue %(queue)s for %(username)s") % {
'queue': obj['queue'].title,
'username': obj['user'].get_username(),
}
}
else:
return _("Open and Reopened Tickets for %(username)s") % {
'username': obj['user'].get_username(),
}
}
def link(self, obj):
if obj['queue']:
@ -60,28 +60,28 @@ class OpenTicketsByUser(Feed):
reverse('helpdesk_list'),
obj['user'].id,
obj['queue'].id,
)
)
else:
return u'%s?assigned_to=%s' % (
reverse('helpdesk_list'),
obj['user'].id,
)
)
def items(self, obj):
if obj['queue']:
return Ticket.objects.filter(
assigned_to=obj['user']
).filter(
queue=obj['queue']
).filter(
Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)
)
assigned_to=obj['user']
).filter(
queue=obj['queue']
).filter(
Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)
)
else:
return Ticket.objects.filter(
assigned_to=obj['user']
).filter(
Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)
)
assigned_to=obj['user']
).filter(
Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)
)
def item_pubdate(self, item):
return item.created
@ -103,10 +103,10 @@ class UnassignedTickets(Feed):
def items(self, obj):
return Ticket.objects.filter(
assigned_to__isnull=True
).filter(
Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)
)
assigned_to__isnull=True
).filter(
Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)
)
def item_pubdate(self, item):
return item.created
@ -140,25 +140,25 @@ class OpenTicketsByQueue(Feed):
def title(self, obj):
return _('Helpdesk: Open Tickets in queue %(queue)s') % {
'queue': obj.title,
}
}
def description(self, obj):
return _('Open and Reopened Tickets in queue %(queue)s') % {
'queue': obj.title,
}
}
def link(self, obj):
return '%s?queue=%s' % (
reverse('helpdesk_list'),
obj.id,
)
)
def items(self, obj):
return Ticket.objects.filter(
queue=obj
).filter(
Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)
)
queue=obj
).filter(
Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)
)
def item_pubdate(self, item):
return item.created
@ -168,4 +168,3 @@ class OpenTicketsByQueue(Feed):
return item.assigned_to.get_username()
else:
return _('Unassigned')

View File

@ -47,7 +47,7 @@ def homepage(request):
reverse('helpdesk_public_view'),
ticket.ticket_for_url,
ticket.submitter_email)
)
)
else:
try:
queue = Queue.objects.get(slug=request.GET.get('queue', None))
@ -67,9 +67,9 @@ def homepage(request):
knowledgebase_categories = KBCategory.objects.all()
return render(request, 'helpdesk/public_homepage.html', {
'form': form,
'helpdesk_settings': helpdesk_settings,
'kb_categories': knowledgebase_categories
'form': form,
'helpdesk_settings': helpdesk_settings,
'kb_categories': knowledgebase_categories
})
@ -106,7 +106,7 @@ def view_ticket(request):
'public': 1,
'title': ticket.title,
'comment': _('Submitter accepted resolution and closed ticket'),
}
}
if ticket.assigned_to:
request.POST['owner'] = ticket.assigned_to.id
request.GET = {}

View File

@ -98,24 +98,24 @@ def dashboard(request):
# open & reopened tickets, assigned to current user
tickets = Ticket.objects.select_related('queue').filter(
assigned_to=request.user,
).exclude(
status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS],
)
assigned_to=request.user,
).exclude(
status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS],
)
# closed & resolved tickets, assigned to current user
tickets_closed_resolved = Ticket.objects.select_related('queue').filter(
assigned_to=request.user,
status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS])
assigned_to=request.user,
status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS])
user_queues = _get_user_queues(request.user)
unassigned_tickets = Ticket.objects.select_related('queue').filter(
assigned_to__isnull=True,
queue__in=user_queues
).exclude(
status=Ticket.CLOSED_STATUS,
)
assigned_to__isnull=True,
queue__in=user_queues
).exclude(
status=Ticket.CLOSED_STATUS,
)
# all tickets, reported by current user
all_tickets_reported_by_current_user = ''
@ -126,8 +126,8 @@ def dashboard(request):
).order_by('status')
tickets_in_queues = Ticket.objects.filter(
queue__in=user_queues,
)
queue__in=user_queues,
)
basic_ticket_stats = calc_basic_ticket_stats(tickets_in_queues)
# The following query builds a grid of queues & ticket statuses,
@ -227,7 +227,7 @@ def followup_edit(request, ticket_id, followup_id):
new_followup.user = followup.user
new_followup.save()
# get list of old attachments & link them to new_followup
attachments = Attachment.objects.filter(followup = followup)
attachments = Attachment.objects.filter(followup=followup)
for attachment in attachments:
attachment.followup = new_followup
attachment.save()
@ -290,7 +290,7 @@ def view_ticket(request, ticket_id):
'owner': owner,
'title': ticket.title,
'comment': _('Accepted resolution and closed ticket'),
}
}
return update_ticket(request, ticket_id)
@ -299,7 +299,6 @@ def view_ticket(request, ticket_id):
else:
users = User.objects.filter(is_active=True).order_by(User.USERNAME_FIELD)
# TODO: shouldn't this template get a form to begin with?
form = TicketForm(initial={'due_date': ticket.due_date})
@ -447,7 +446,7 @@ def update_ticket(request, ticket_id, public=False):
new_user = User.objects.get(id=owner)
f.title = _('Assigned to %(username)s') % {
'username': new_user.get_username(),
}
}
ticket.assigned_to = new_user
reassigned = True
# user changed owner to 'unassign'
@ -482,7 +481,7 @@ def update_ticket(request, ticket_id, public=False):
filename=filename,
mime_type=mimetypes.guess_type(filename)[0] or 'application/octet-stream',
size=file.size,
)
)
a.file.save(filename, file, save=False)
a.save()
@ -497,7 +496,7 @@ def update_ticket(request, ticket_id, public=False):
field=_('Title'),
old_value=ticket.title,
new_value=title,
)
)
c.save()
ticket.title = title
@ -507,7 +506,7 @@ def update_ticket(request, ticket_id, public=False):
field=_('Priority'),
old_value=ticket.priority,
new_value=priority,
)
)
c.save()
ticket.priority = priority
@ -517,11 +516,11 @@ def update_ticket(request, ticket_id, public=False):
field=_('Due on'),
old_value=ticket.due_date,
new_value=due_date,
)
)
c.save()
ticket.due_date = due_date
if new_status in [ Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS ]:
if new_status in (Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS):
if new_status == Ticket.RESOLVED_STATUS or ticket.resolution is None:
ticket.resolution = comment
@ -533,11 +532,11 @@ def update_ticket(request, ticket_id, public=False):
context.update(
resolution=ticket.resolution,
comment=f.comment,
)
)
if public and (f.comment or (
f.new_status in (Ticket.RESOLVED_STATUS,
Ticket.CLOSED_STATUS))):
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:
@ -555,7 +554,7 @@ def update_ticket(request, ticket_id, public=False):
sender=ticket.queue.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(ticket.submitter_email)
template_suffix = 'cc'
@ -569,7 +568,7 @@ def update_ticket(request, ticket_id, public=False):
sender=ticket.queue.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(cc.email_address)
if ticket.assigned_to and \
@ -602,7 +601,7 @@ def update_ticket(request, ticket_id, public=False):
sender=ticket.queue.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(ticket.assigned_to.email)
if ticket.queue.updated_ticket_cc and ticket.queue.updated_ticket_cc not in messages_sent_to:
@ -622,7 +621,7 @@ def update_ticket(request, ticket_id, public=False):
sender=ticket.queue.from_address,
fail_silently=True,
files=files,
)
)
ticket.save()
@ -716,7 +715,7 @@ def mass_update(request):
recipients=t.submitter_email,
sender=t.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(t.submitter_email)
for cc in t.ticketcc_set.all():
@ -727,7 +726,7 @@ def mass_update(request):
recipients=cc.email_address,
sender=t.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(cc.email_address)
if t.assigned_to and \
@ -740,7 +739,7 @@ def mass_update(request):
recipients=t.assigned_to.email,
sender=t.queue.from_address,
fail_silently=True,
)
)
messages_sent_to.append(t.assigned_to.email)
if t.queue.updated_ticket_cc and \
@ -751,7 +750,7 @@ def mass_update(request):
recipients=t.queue.updated_ticket_cc,
sender=t.queue.from_address,
fail_silently=True,
)
)
elif action == 'delete':
t.delete()
@ -775,7 +774,7 @@ def ticket_list(request):
'sortreverse': False,
'keyword': None,
'search_string': None,
}
}
from_saved_query = False
@ -831,13 +830,12 @@ 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
@ -1113,7 +1111,8 @@ def run_report(request, report):
# a second table for more complex queries
summarytable2 = defaultdict(int)
month_name = lambda m: MONTHS_3[m].title()
def month_name(m):
MONTHS_3[m].title()
first_ticket = Ticket.objects.all().order_by('created')[0]
first_month = first_ticket.created.month
@ -1259,7 +1258,7 @@ run_report = staff_member_required(run_report)
def save_query(request):
title = request.POST.get('title', None)
shared = request.POST.get('shared', False)
if shared == 'on': # django only translates '1', 'true', 't' into True
if shared == 'on': # django only translates '1', 'true', 't' into True
shared = True
query_encoded = request.POST.get('query_encoded', None)
@ -1501,7 +1500,7 @@ def days_since_created(today, ticket):
def date_rel_to_today(today, offset):
return today - timedelta(days = offset)
return today - timedelta(days=offset)
def sort_string(begin, end):