# -*- coding: utf-8 -*- from django.db import models from django.utils import timezone from django.dispatch import receiver from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from picklefield.fields import PickledObjectField from greenmine.base.utils.slug import slugify_uniquely, ref_uniquely from greenmine.base.utils import iter_points from greenmine.scrum.choices import (ISSUESTATUSES, TASKSTATUSES, USSTATUSES, POINTS_CHOICES, SEVERITY_CHOICES, ISSUETYPES, TASK_CHANGE_CHOICES, PRIORITY_CHOICES) class Severity(models.Model): name = models.CharField(max_length=255) order = models.IntegerField(default=10) project = models.ForeignKey("Project", related_name="severities") class Meta: verbose_name = u'severity' verbose_name_plural = u'severities' ordering = ['project', 'name'] unique_together = ('project', 'name') def __unicode__(self): return u"project {0} - {1}".format(self.project_id, self.name) class IssueStatus(models.Model): name = models.CharField(max_length=255) order = models.IntegerField(default=10) is_closed = models.BooleanField(default=False) project = models.ForeignKey("Project", related_name="issuestatuses") class Meta: verbose_name = u'issue status' verbose_name_plural = u'issue statuses' ordering = ['project', 'name'] unique_together = ('project', 'name') def __unicode__(self): return u"project {0} - {1}".format(self.project_id, self.name) class TaskStatus(models.Model): name = models.CharField(max_length=255) order = models.IntegerField(default=10) is_closed = models.BooleanField(default=False) color = models.CharField(max_length=20, default="#999999") project = models.ForeignKey("Project", related_name="taskstatuses") class Meta: verbose_name = u'task status' verbose_name_plural = u'task statuses' ordering = ['project', 'name'] unique_together = ('project', 'name') def __unicode__(self): return u"project {0} - {1}".format(self.project_id, self.name) class UserStoryStatus(models.Model): name = models.CharField(max_length=255) order = models.IntegerField(default=10) is_closed = models.BooleanField(default=False) project = models.ForeignKey("Project", related_name="usstatuses") class Meta: verbose_name = u'user story status' verbose_name_plural = u'user story statuses' ordering = ['project', 'name'] unique_together = ('project', 'name') def __unicode__(self): return u"project {0} - {1}".format(self.project_id, self.name) class Priority(models.Model): name = models.CharField(max_length=255) order = models.IntegerField(default=10) project = models.ForeignKey("Project", related_name="priorities") class Meta: verbose_name = u'priority' verbose_name_plural = u'priorities' ordering = ['project', 'name'] unique_together = ('project', 'name') def __unicode__(self): return u"project {0} - {1}".format(self.project_id, self.name) class IssueType(models.Model): name = models.CharField(max_length=255) order = models.IntegerField(default=10) project = models.ForeignKey("Project", related_name="issuetypes") class Meta: verbose_name = u'issue type' verbose_name_plural = u'issue types' ordering = ['project', 'name'] unique_together = ('project', 'name') def __unicode__(self): return u"project {0} - {1}".format(self.project_id, self.name) class Points(models.Model): name = models.CharField(max_length=255) order = models.IntegerField(default=10) project = models.ForeignKey("Project", related_name="points") class Meta: verbose_name = u'point' verbose_name_plural = u'points' ordering = ['project', 'name'] unique_together = ('project', 'name') def __unicode__(self): return u"project {0} - {1}".format(self.project_id, self.name) class Membership(models.Model): user = models.ForeignKey("base.User") project = models.ForeignKey("Project") role = models.ForeignKey("base.Role") class Meta: unique_together = ('user', 'project') class Project(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) name = models.CharField(max_length=250, unique=True) slug = models.SlugField(max_length=250, unique=True, blank=True) description = models.TextField(blank=False) created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now_add=True, auto_now=True) owner = models.ForeignKey("base.User", related_name="owned_projects", blank=True) members = models.ManyToManyField("base.User", related_name="projects", through='Membership') public = models.BooleanField(default=True) last_us_ref = models.BigIntegerField(null=True, default=1) last_task_ref = models.BigIntegerField(null=True, default=1) last_issue_ref = models.BigIntegerField(null=True, default=1) sprints = models.IntegerField(default=1, blank=True, null=True) total_story_points = models.FloatField(default=None, null=True) tags = PickledObjectField() class Meta: verbose_name = u'project' verbose_name_plural = u'projects' ordering = ['name'] permissions = ( ('can_list_projects', 'Can list projects'), ('can_view_project', 'Can view project'), ('can_manage_users', 'Can manage users'), ) def __unicode__(self): return self.name def __repr__(self): return u"".format(self.id) def save(self, *args, **kwargs): if not self.slug: self.slug = slugify_uniquely(self.name, self.__class__) super(Project, self).save(*args, **kwargs) class Milestone(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) name = models.CharField(max_length=200, db_index=True) slug = models.SlugField(max_length=250, unique=True, blank=True) owner = models.ForeignKey('base.User', related_name="milestones", null=True, blank=True) project = models.ForeignKey('Project', related_name="milestones") estimated_start = models.DateField(null=True, default=None) estimated_finish = models.DateField(null=True, default=None) created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now_add=True, auto_now=True) closed = models.BooleanField(default=False) disponibility = models.FloatField(null=True, default=0.0) order = models.PositiveSmallIntegerField("Order", default=1) class Meta: verbose_name = u'milestone' verbose_name_plural = u'milestones' ordering = ['project', '-created_date'] unique_together = ('name', 'project') permissions = ( ('can_view_milestone', 'Can view milestones'), ) def __unicode__(self): return self.name def __repr__(self): return u"".format(self.id) def save(self, *args, **kwargs): if not self.slug: self.slug = slugify_uniquely(self.name, self.__class__) super(Milestone, self).save(*args, **kwargs) class UserStory(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) ref = models.BigIntegerField(db_index=True, null=True, default=None, blank=True) milestone = models.ForeignKey("Milestone", blank=True, related_name="user_stories", null=True, default=None) project = models.ForeignKey("Project", related_name="user_stories") owner = models.ForeignKey("base.User", blank=True, null=True, related_name="user_stories") status = models.ForeignKey("UserStoryStatus", related_name="userstories") points = models.ForeignKey("Points", related_name="userstories") order = models.PositiveSmallIntegerField(default=100) created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now_add=True, auto_now=True) finish_date = models.DateTimeField(null=True, blank=True) subject = models.CharField(max_length=500) description = models.TextField() watchers = models.ManyToManyField('base.User', related_name='us_watch', null=True) client_requirement = models.BooleanField(default=False) team_requirement = models.BooleanField(default=False) tags = PickledObjectField() class Meta: verbose_name = u'user story' verbose_name_plural = u'user stories' ordering = ['project', 'order'] unique_together = ('ref', 'project') permissions = ( ('can_comment_userstory', 'Can comment user stories'), ('can_view_userstory', 'Can view user stories'), ('can_change_owned_userstory', 'Can modify owned user stories'), ('can_delete_userstory', 'Can delete user stories'), ('can_add_userstory_to_milestones', 'Can add user stories to milestones'), ) def __unicode__(self): return u"({1}) {0}".format(self.ref, self.subject) def __repr__(self): return u"" % (self.id) @property def is_closed(self): return self.status.is_closed class Attachment(models.Model): owner = models.ForeignKey("base.User", related_name="change_attachments") project = models.ForeignKey("Project", related_name="attachments") content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') created_date = models.DateTimeField(auto_now_add=True) attached_file = models.FileField(upload_to="files/msg", max_length=500, null=True, blank=True) class Meta: verbose_name = u'attachment' verbose_name_plural = u'attachments' ordering = ['project', 'created_date'] def __unicode__(self): return u'content_type {0} - object_id {1} - attachment {2}'.format( self.content_type, self.object_id, self.id ) class Task(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) user_story = models.ForeignKey('UserStory', related_name='tasks') ref = models.BigIntegerField(db_index=True, null=True, default=None) owner = models.ForeignKey("base.User", null=True, default=None, related_name="tasks") status = models.ForeignKey("TaskStatus", related_name="tasks") severity = models.ForeignKey("Severity", related_name="tasks") priority = models.ForeignKey("Priority", related_name="tasks") status = models.ForeignKey("TaskStatus", related_name="tasks") milestone = models.ForeignKey('Milestone', related_name='tasks', null=True, default=None, blank=True) project = models.ForeignKey('Project', related_name='tasks') created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now_add=True) finished_date = models.DateTimeField(null=True, blank=True) subject = models.CharField(max_length=500) description = models.TextField(blank=True) assigned_to = models.ForeignKey('base.User', related_name='user_storys_assigned_to_me', blank=True, null=True, default=None) watchers = models.ManyToManyField('base.User', related_name='task_watch', null=True) tags = PickledObjectField() class Meta: verbose_name = u'task' verbose_name_plural = u'tasks' ordering = ['project', 'created_date'] unique_together = ('ref', 'project') permissions = ( ('can_comment_task', 'Can comment tasks'), ('can_change_owned_task', 'Can modify owned tasks'), ('can_change_assigned_task', 'Can modify assigned tasks'), ('can_assign_task_to_other', 'Can assign tasks to others'), ('can_assign_task_to_myself', 'Can assign tasks to myself'), ('can_change_task_state', 'Can change the task state'), ('can_view_task', 'Can view the task'), ('can_add_task_to_us', 'Can add tasks to a user story'), ) def __unicode__(self): return u"({1}) {0}".format(self.ref, self.subject) def save(self, *args, **kwargs): if self.id: self.modified_date = timezone.now() if not self.ref: self.ref = ref_uniquely(self.project, "last_task_ref", self.__class__) super(Task, self).save(*args, **kwargs) class Issue(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) ref = models.BigIntegerField(db_index=True, null=True, default=None) owner = models.ForeignKey("base.User", null=True, default=None, related_name="issues") status = models.ForeignKey("IssueStatus", related_name="issues") severity = models.ForeignKey("Severity", related_name="issues") priority = models.ForeignKey("Priority", related_name="issues") type = models.ForeignKey("IssueType", related_name="issues") milestone = models.ForeignKey('Milestone', related_name='issues', null=True, default=None, blank=True) project = models.ForeignKey('Project', related_name='issues') created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now_add=True) finished_date = models.DateTimeField(null=True, blank=True) subject = models.CharField(max_length=500) description = models.TextField(blank=True) assigned_to = models.ForeignKey('base.User', related_name='issues_assigned_to_me', blank=True, null=True, default=None) watchers = models.ManyToManyField('base.User', related_name='issue_watch', null=True) tags = PickledObjectField() class Meta: verbose_name = u'issue' verbose_name_plural = u'issues' ordering = ['project', 'created_date'] unique_together = ('ref', 'project') permissions = ( ('can_comment_issue', 'Can comment issues'), ('can_change_owned_issue', 'Can modify owned issues'), ('can_change_assigned_issue', 'Can modify assigned issues'), ('can_assign_issue_to_other', 'Can assign issues to others'), ('can_assign_issue_to_myself', 'Can assign issues to myself'), ('can_change_issue_state', 'Can change the issue state'), ('can_view_issue', 'Can view the issue'), ) def __unicode__(self): return u"({1}) {0}".format(self.ref, self.subject) def save(self, *args, **kwargs): if self.id: self.modified_date = timezone.now() if not self.ref: self.ref = ref_uniquely(self.project, "last_issue_ref", self.__class__) super(Issue, self).save(*args, **kwargs) # Model related signals handlers @receiver(models.signals.post_save, sender=Project, dispatch_uid="project_post_save") def project_post_save(sender, instance, created, **kwargs): """ Create all project model depences on project is created. """ if not created: return # Populate new project dependen default data for order, name, is_closed in ISSUESTATUSES: IssueStatus.objects.create(name=name, order=order, is_closed=is_closed, project=instance) for order, name, is_closed, color in TASKSTATUSES: TaskStatus.objects.create(name=name, order=order, color=color, is_closed=is_closed, project=instance) for order, name, is_closed in USSTATUSES: UserStoryStatus.objects.create(name=name, order=order, is_closed=is_closed, project=instance) for order, name in PRIORITY_CHOICES: Priority.objects.create(project=instance, name=name, order=order) for order, name in SEVERITY_CHOICES: Severity.objects.create(project=instance, name=name, order=order) for order, name in POINTS_CHOICES: Points.objects.create(project=instance, name=name, order=order) for order, name in ISSUETYPES: IssueType.objects.create(project=instance, name=name, order=order) @receiver(models.signals.pre_save, sender=UserStory, dispatch_uid="user_story_ref_handler") def user_story_ref_handler(sender, instance, **kwargs): """ Automatically assignes a seguent reference code to a user story if that is not created. """ if not instance.id and instance.project: instance.ref = ref_uniquely(instance.project, "last_us_ref", instance.__class__) # Email alerts signals handlers # TODO: temporary commented (Pending refactor) # from . import sigdispatch