django-orchestra/orchestra/apps/issues/models.py

186 lines
6.4 KiB
Python

from django.contrib.auth import get_user_model
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from orchestra.apps.contacts import settings as contacts_settings
from orchestra.models.fields import MultiSelectField
from orchestra.utils import send_email_template
from . import settings
class Queue(models.Model):
name = models.CharField(_("name"), max_length=128, unique=True)
default = models.BooleanField(_("default"), default=False)
notify = MultiSelectField(_("notify"), max_length=256, blank=True,
choices=contacts_settings.CONTACTS_EMAIL_USAGES,
default=contacts_settings.CONTACTS_DEFAULT_EMAIL_USAGES,
help_text=_("Contacts to notify by email"))
def __unicode__(self):
return self.name
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)
class Ticket(models.Model):
HIGH = 'HIGH'
MEDIUM = 'MEDIUM'
LOW = 'LOW'
PRIORITIES = (
(HIGH, 'High'),
(MEDIUM, 'Medium'),
(LOW, 'Low'),
)
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'),
)
creator = models.ForeignKey(get_user_model(), verbose_name=_("created by"),
related_name='tickets_created')
owner = models.ForeignKey(get_user_model(), null=True, blank=True,
related_name='tickets_owned', verbose_name=_("assigned to"))
queue = models.ForeignKey(Queue, related_name='tickets', null=True, blank=True)
subject = models.CharField(_("subject"), max_length=256)
description = models.TextField(_("description"))
priority = models.CharField(_("priority"), max_length=32, choices=PRIORITIES,
default=MEDIUM)
state = models.CharField(_("state"), max_length=32, choices=STATES, default=NEW)
created_on = models.DateTimeField(_("created on"), auto_now_add=True)
last_modified_on = models.DateTimeField(_("last modified on"), auto_now=True)
cc = models.TextField("CC", help_text=_("emails to send a carbon copy to"),
blank=True)
class Meta:
ordering = ["-last_modified_on"]
def __unicode__(self):
return unicode(self.pk)
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.account.contacts.all():
if self.queue and set(contact.email_usage).union(set(self.queue.nofify)):
emails.append(contact.email)
for message in self.messages.distinct('author'):
emails.append(message.author.email)
return set(emails + self.get_cc_emails())
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)
def save(self, *args, **kwargs):
""" notify stakeholders of new ticket """
new_issue = not self.pk
super(Ticket, self).save(*args, **kwargs)
if new_issue:
# PK should be available for rendering the template
self.notify()
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()
def is_visible_by(self, user):
""" returns whether ticket is visible by user """
return Ticket.objects.filter(pk=self.pk).visible_by(user).exists()
def get_cc_emails(self):
return self.cc.split(',') if self.cc else []
def mark_as_read_by(self, user):
TicketTracker.objects.get_or_create(ticket=self, user=user)
def mark_as_unread_by(self, user):
TicketTracker.objects.filter(ticket=self, user=user).delete()
def mark_as_unread(self):
TicketTracker.objects.filter(ticket=self).delete()
def is_read_by(self, user):
return TicketTracker.objects.filter(ticket=self, user=user).exists()
def reject(self):
self.state = Ticket.REJECTED
self.save()
def resolve(self):
self.state = Ticket.RESOLVED
self.save()
def close(self):
self.state = Ticket.CLOSED
self.save()
def take(self, user):
self.owner = user
self.save()
class Message(models.Model):
ticket = models.ForeignKey('issues.Ticket', verbose_name=_("ticket"),
related_name='messages')
author = models.ForeignKey(get_user_model(), verbose_name=_("author"),
related_name='ticket_messages')
content = models.TextField(_("content"))
created_on = models.DateTimeField(_("created on"), auto_now_add=True)
def __unicode__(self):
return u"#%i" % self.id
def save(self, *args, **kwargs):
""" notify stakeholders of ticket update """
if not self.pk:
self.ticket.mark_as_unread()
self.ticket.notify(message=self)
super(Message, self).save(*args, **kwargs)
@property
def num(self):
return self.ticket.messages.filter(id__lte=self.id).count()
class TicketTracker(models.Model):
""" Keeps track of user read tickets """
ticket = models.ForeignKey(Ticket, verbose_name=_("ticket"),
related_name='trackers')
user = models.ForeignKey(get_user_model(), verbose_name=_("user"),
related_name='ticket_trackers')
class Meta:
unique_together = (('ticket', 'user'),)