# -*- coding: utf-8 -*- import re from django.conf import settings 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.fields import DictField from greenmine.base.utils import iter_points from greenmine.scrum.choices import * class Severity(models.Model): name = models.CharField(max_length=255) order = models.IntegerField(default=10) project = models.ForeignKey("Project", related_name="severities") class Meta: unique_together = ('project', 'name') def __unicode__(self): return u"project({0})/severity({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: unique_together = ('project', 'name') def __unicode__(self): return u"project({0})/issue-status({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: unique_together = ('project', 'name') def __unicode__(self): return u"project({0})/task-status({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: unique_together = ('project', 'name') def __unicode__(self): return u"project({0})/us-status({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: unique_together = ('project', 'name') def __unicode__(self): return u"project({0})/priority({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: unique_together = ('project', 'name') def __unicode__(self): return u"project({0})/type({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: unique_together = ('project', 'name') def __unicode__(self): return u"project({0})/point({1})".format(self.project.id, self.name) 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="projects") 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: permissions = ( # global permissions ('list_projects', 'Can list projects'), ('list_my_projects', 'Can list my projects'), # per project permissions ('view_projects', 'Can view projects'), ('create_tasks', 'Can create tasks'), ('comment_tasks', 'Can comment tasks'), ('modify_tasks', 'Can modify tasks'), ('delete_task', 'Can delete tasks'), ('modify_owned_tasks', 'Can modify owned tasks'), ('modify_assigned_tasks', 'Can modify assigned tasks'), ('assign_tasks_to_others', 'Can assign tasks to others'), ('assign_tasks_to_myself', 'Can assign tasks to myself'), ('change_tasks_state', 'Can change the task state'), ('add_tasks_to_us', 'Can add tasks to a user story'), ('create_us', 'Can create user stories'), ('comment_us', 'Can comment user stories'), ('modify_us', 'Can modify user stories'), ('delete_us', 'Can delete user stories'), ('modify_owned_us', 'Can modify owned user stories'), ('add_us_to_milestones', 'Can add user stories to milestones'), ('create_questions', 'Can create questions'), ('reply_questions', 'Can reply questions'), ('modify_questions', 'Can modify questions'), ('delete_questions', 'Can delete questions'), ('modify_owned_questions', 'Can modify owned questions'), ('create_wiki_page', 'Can create wiki pages'), ('modify_wiki_page', 'Can modify wiki pages'), ('delete_wiki_page', 'Can delete wiki pages'), ('modify_owned_wiki_page', 'Can modify owned wiki pages'), ('create_documents', 'Can create documents'), ('modify_documents', 'Can modify documents'), ('delete_documents', 'Can delete documents'), ('modify_owned_documents', 'Can modify owned documents'), ('create_milestone', 'Can create milestones'), ('modify_milestone', 'Can modify milestones'), ('manage_users', 'Can manage users'), ) def __unicode__(self): return self.name def __repr__(self): return u"" % (self.slug) 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") 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) tags = DictField(blank=True, null=True) def save(self, *args, **kwargs): if not self.slug: self.slug = slugify_uniquely(self.name, self.__class__) super(Milestone, self).save(*args, **kwargs) class Meta: ordering = ['-created_date'] unique_together = ('name', 'project') @property def total_points(self): """ Get total story points for this milestone. """ total = sum(iter_points(self.user_stories.all())) return "{0:.1f}".format(total) def __unicode__(self): return self.name def __repr__(self): return u"" % (self.id) class UserStory(models.Model): uuid = models.CharField(max_length=40, unique=True, blank=True) ref = models.BigIntegerField(db_index=True, null=True, default=None) 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", null=True, default=None, 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: ordering = ['order'] unique_together = ('ref', 'project') def __repr__(self): return u"" % (self.id) def __unicode__(self): return u"{0} ({1})".format(self.subject, self.ref) def save(self, *args, **kwargs): if not self.ref: self.ref = ref_uniquely(self.project, "last_us_ref", self.__class__) super(UserStory, self).save(*args, **kwargs) class Change(models.Model): change_type = models.IntegerField(choices=TASK_CHANGE_CHOICES) owner = models.ForeignKey('base.User', related_name='changes') created_date = models.DateTimeField(auto_now_add=True) project = models.ForeignKey("Project", related_name="changes") content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') data = DictField(blank=True, null=True) tags = DictField(blank=True, null=True) class ChangeAttachment(models.Model): change = models.ForeignKey("Change", related_name="attachments") owner = models.ForeignKey("base.User", related_name="change_attachments") created_date = models.DateTimeField(auto_now_add=True) attached_file = models.FileField(upload_to="files/msg", max_length=500, null=True, blank=True) tags = DictField(blank=True, null=True) 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") severity = models.ForeignKey("Severity", related_name="tasks") priority = models.ForeignKey("Priority", 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) changes = generic.GenericRelation(Change) tags = PickledObjectField() class Meta: unique_together = ('ref', 'project') def __unicode__(self): return 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") 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) changes = generic.GenericRelation(Change) tags = PickledObjectField() class Meta: unique_together = ('ref', 'project') def __unicode__(self): return 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(Task, 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): from greenmine.base.services import RoleGroupsService if not created: return RoleGroupsService().replicate_all_roles_on_one_project(instance) # 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 POINTS_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) # Email alerts signals handlers # TODO: temporary commented (Pending refactor) # from . import sigdispatch