django-orchestra-test/orchestra/contrib/issues/models.py

203 lines
7.2 KiB
Python
Raw Normal View History

2014-07-29 20:10:37 +00:00
from django.conf import settings as djsettings
2014-05-08 16:59:35 +00:00
from django.db import models
2016-03-31 16:02:50 +00:00
from django.db.models import query, Q
from django.utils.translation import gettext_lazy as _
2014-05-08 16:59:35 +00:00
2015-04-05 10:46:24 +00:00
from orchestra.contrib.contacts import settings as contacts_settings
from orchestra.contrib.contacts.models import Contact
2014-05-08 16:59:35 +00:00
from orchestra.models.fields import MultiSelectField
from orchestra.utils.mail import send_email_template
2014-05-08 16:59:35 +00:00
from . import settings
class Queue(models.Model):
name = models.CharField(_("name"), max_length=128, unique=True)
2015-03-29 16:10:07 +00:00
verbose_name = models.CharField(_("verbose_name"), max_length=128, blank=True)
2014-05-08 16:59:35 +00:00
default = models.BooleanField(_("default"), default=False)
notify = MultiSelectField(_("notify"), max_length=256, blank=True,
2015-04-05 10:46:24 +00:00
choices=Contact.EMAIL_USAGES,
default=contacts_settings.CONTACTS_DEFAULT_EMAIL_USAGES,
help_text=_("Contacts to notify by email"))
2015-04-02 16:14:55 +00:00
def __str__(self):
2015-03-29 16:10:07 +00:00
return self.verbose_name or self.name
2014-05-08 16:59:35 +00:00
def save(self, *args, **kwargs):
""" mark as default queue if needed """
existing_default = Queue.objects.filter(default=True)
if self.default:
existing_default.update(default=False)
elif not existing_default:
self.default = True
super(Queue, self).save(*args, **kwargs)
2016-03-31 16:02:50 +00:00
class TicketQuerySet(query.QuerySet):
def involved_by(self, user, *args, **kwargs):
qset = Q(creator=user) | Q(owner=user) | Q(messages__author=user)
return self.filter(qset, *args, **kwargs).distinct()
2014-05-08 16:59:35 +00:00
class Ticket(models.Model):
HIGH = 'HIGH'
MEDIUM = 'MEDIUM'
LOW = 'LOW'
PRIORITIES = (
(HIGH, 'High'),
(MEDIUM, 'Medium'),
(LOW, 'Low'),
)
2014-05-08 16:59:35 +00:00
NEW = 'NEW'
IN_PROGRESS = 'IN_PROGRESS'
RESOLVED = 'RESOLVED'
FEEDBACK = 'FEEDBACK'
REJECTED = 'REJECTED'
CLOSED = 'CLOSED'
STATES = (
(NEW, 'New'),
(IN_PROGRESS, 'In Progress'),
(RESOLVED, 'Resolved'),
(FEEDBACK, 'Feedback'),
(REJECTED, 'Rejected'),
(CLOSED, 'Closed'),
)
2014-07-29 20:10:37 +00:00
creator = models.ForeignKey(djsettings.AUTH_USER_MODEL, verbose_name=_("created by"),
2016-04-27 08:35:13 +00:00
related_name='tickets_created', null=True, on_delete=models.SET_NULL)
2014-05-13 13:46:40 +00:00
creator_name = models.CharField(_("creator name"), max_length=256, blank=True)
2014-07-29 20:10:37 +00:00
owner = models.ForeignKey(djsettings.AUTH_USER_MODEL, null=True, blank=True,
2016-04-27 08:35:13 +00:00
on_delete=models.SET_NULL,
2015-04-05 10:46:24 +00:00
related_name='tickets_owned', verbose_name=_("assigned to"))
2016-04-27 08:35:13 +00:00
queue = models.ForeignKey(Queue, related_name='tickets', null=True, blank=True,
on_delete=models.SET_NULL)
2014-05-08 16:59:35 +00:00
subject = models.CharField(_("subject"), max_length=256)
description = models.TextField(_("description"))
2016-03-31 16:02:50 +00:00
priority = models.CharField(_("priority"), max_length=32, choices=PRIORITIES, default=MEDIUM)
2014-05-08 16:59:35 +00:00
state = models.CharField(_("state"), max_length=32, choices=STATES, default=NEW)
created_at = models.DateTimeField(_("created"), auto_now_add=True, db_index=True)
2014-09-26 15:05:20 +00:00
updated_at = models.DateTimeField(_("modified"), auto_now=True)
2016-03-31 16:02:50 +00:00
cc = models.TextField("CC", help_text=_("emails to send a carbon copy to"), blank=True)
2016-03-31 16:02:50 +00:00
objects = TicketQuerySet.as_manager()
2014-05-08 16:59:35 +00:00
class Meta:
2014-09-26 15:05:20 +00:00
ordering = ['-updated_at']
2015-04-02 16:14:55 +00:00
def __str__(self):
return str(self.pk)
2014-05-08 16:59:35 +00:00
def get_notification_emails(self):
""" Get emails of the users related to the ticket """
emails = list(settings.ISSUES_SUPPORT_EMAILS)
emails.append(self.creator.email)
if self.owner:
emails.append(self.owner.email)
for contact in self.creator.contacts.all():
2014-07-23 18:28:40 +00:00
if self.queue and set(contact.email_usage).union(set(self.queue.notify)):
2014-05-08 16:59:35 +00:00
emails.append(contact.email)
for message in self.messages.distinct('author'):
emails.append(message.author.email)
return set(emails + self.get_cc_emails())
2014-05-08 16:59:35 +00:00
def notify(self, message=None, content=None):
""" Send an email to ticket stakeholders notifying an state update """
emails = self.get_notification_emails()
template = 'issues/ticket_notification.mail'
html_template = 'issues/ticket_notification_html.mail'
context = {
'ticket': self,
'ticket_message': message
}
send_email_template(template, context, emails, html=html_template)
2014-05-08 16:59:35 +00:00
def save(self, *args, **kwargs):
""" notify stakeholders of new ticket """
new_issue = not self.pk
2014-05-13 13:46:40 +00:00
if not self.creator_name and self.creator:
self.creator_name = self.creator.get_full_name()
2014-05-08 16:59:35 +00:00
super(Ticket, self).save(*args, **kwargs)
if new_issue:
# PK should be available for rendering the template
self.notify()
2014-05-08 16:59:35 +00:00
def is_involved_by(self, user):
""" returns whether user has participated or is referenced on the ticket
as owner or member of the group
"""
return Ticket.objects.filter(pk=self.pk).involved_by(user).exists()
2014-05-08 16:59:35 +00:00
def get_cc_emails(self):
return self.cc.split(',') if self.cc else []
2014-05-08 16:59:35 +00:00
def mark_as_read_by(self, user):
2014-05-13 13:46:40 +00:00
self.trackers.get_or_create(user=user)
2014-05-08 16:59:35 +00:00
def mark_as_unread_by(self, user):
2014-05-13 13:46:40 +00:00
self.trackers.filter(user=user).delete()
2014-05-08 16:59:35 +00:00
def mark_as_unread(self):
2014-05-13 13:46:40 +00:00
self.trackers.all().delete()
2014-05-08 16:59:35 +00:00
def is_read_by(self, user):
2014-05-13 13:46:40 +00:00
return self.trackers.filter(user=user).exists()
2014-05-08 16:59:35 +00:00
def reject(self):
self.state = Ticket.REJECTED
2016-04-07 11:14:44 +00:00
self.save(update_fields=('state', 'updated_at'))
2014-05-08 16:59:35 +00:00
def resolve(self):
self.state = Ticket.RESOLVED
2016-04-07 11:14:44 +00:00
self.save(update_fields=('state', 'updated_at'))
2014-05-08 16:59:35 +00:00
def close(self):
self.state = Ticket.CLOSED
2016-04-07 11:14:44 +00:00
self.save(update_fields=('state', 'updated_at'))
2014-05-08 16:59:35 +00:00
def take(self, user):
self.owner = user
2016-04-07 11:14:44 +00:00
self.save(update_fields=('state', 'updated_at'))
2014-05-08 16:59:35 +00:00
class Message(models.Model):
ticket = models.ForeignKey('issues.Ticket', on_delete=models.CASCADE,
verbose_name=_("ticket"), related_name='messages')
author = models.ForeignKey(djsettings.AUTH_USER_MODEL, on_delete=models.CASCADE,
verbose_name=_("author"), related_name='ticket_messages')
2014-05-13 13:46:40 +00:00
author_name = models.CharField(_("author name"), max_length=256, blank=True)
2014-05-08 16:59:35 +00:00
content = models.TextField(_("content"))
2016-03-31 16:02:50 +00:00
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
2014-05-13 13:46:40 +00:00
class Meta:
2014-09-24 20:09:41 +00:00
get_latest_by = 'id'
2015-04-02 16:14:55 +00:00
def __str__(self):
return "#%i" % self.id
2014-05-08 16:59:35 +00:00
def save(self, *args, **kwargs):
""" notify stakeholders of ticket update """
if not self.pk:
self.ticket.mark_as_unread()
2014-05-13 13:46:40 +00:00
self.ticket.mark_as_read_by(self.author)
2014-05-08 16:59:35 +00:00
self.ticket.notify(message=self)
2014-05-13 13:46:40 +00:00
self.author_name = self.author.get_full_name()
2014-05-08 16:59:35 +00:00
super(Message, self).save(*args, **kwargs)
2014-05-08 16:59:35 +00:00
@property
def number(self):
2014-05-08 16:59:35 +00:00
return self.ticket.messages.filter(id__lte=self.id).count()
class TicketTracker(models.Model):
""" Keeps track of user read tickets """
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE,
verbose_name=_("ticket"), related_name='trackers')
user = models.ForeignKey(djsettings.AUTH_USER_MODEL, on_delete=models.CASCADE,
verbose_name=_("user"), related_name='ticket_trackers')
2014-05-08 16:59:35 +00:00
class Meta:
2014-09-24 20:09:41 +00:00
unique_together = (
('ticket', 'user'),
)