Refactor choices.
parent
28650f46c4
commit
38306a176e
|
@ -23,19 +23,21 @@ def slugify_uniquely(value, model, slugfield="slug"):
|
||||||
suffix += 1
|
suffix += 1
|
||||||
|
|
||||||
|
|
||||||
def ref_uniquely(project, model, field='ref'):
|
|
||||||
|
def ref_uniquely(p, seq_field, model, field='ref'):
|
||||||
"""
|
"""
|
||||||
Returns a unique reference code based on base64 and time.
|
Returns a unique reference code based on base64 and time.
|
||||||
"""
|
"""
|
||||||
|
project = project.__class__.objects.select_for_update().get(pk=p.pk)
|
||||||
|
|
||||||
# this prevents concurrent and inconsistent references.
|
ref = getattr(project, seq_field) + 1
|
||||||
time.sleep(0.001)
|
|
||||||
|
|
||||||
new_timestamp = lambda: int("".join(str(time.time()).split(".")))
|
|
||||||
while True:
|
while True:
|
||||||
potential = baseconv.base62.encode(new_timestamp())
|
params = {field: ref, 'project': _project}
|
||||||
params = {field: potential, 'project': project}
|
|
||||||
if not model.objects.filter(**params).exists():
|
if not model.objects.filter(**params).exists():
|
||||||
return potential
|
setattr(_project, seq_field, ref)
|
||||||
|
_project.save(update_fields=[seq_field])
|
||||||
|
return ref
|
||||||
|
|
||||||
time.sleep(0.0002)
|
time.sleep(0.0002)
|
||||||
|
ref += 1
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from guardian.admin import GuardedModelAdmin
|
from guardian.admin import GuardedModelAdmin
|
||||||
|
from greenmine.scrum import models
|
||||||
import reversion
|
import reversion
|
||||||
|
|
||||||
from greenmine.scrum.models import Project, Milestone, UserStory, Change, \
|
|
||||||
ChangeAttachment, Task
|
|
||||||
|
|
||||||
|
|
||||||
class MilestoneInline(admin.TabularInline):
|
class MilestoneInline(admin.TabularInline):
|
||||||
model = Milestone
|
model = models.Milestone
|
||||||
fields = ('name', 'owner', 'estimated_start', 'estimated_finish', 'closed', 'disponibility', 'order')
|
fields = ('name', 'owner', 'estimated_start', 'estimated_finish', 'closed', 'disponibility', 'order')
|
||||||
sortable_field_name = 'order'
|
sortable_field_name = 'order'
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class UserStoryInline(admin.TabularInline):
|
class UserStoryInline(admin.TabularInline):
|
||||||
model = UserStory
|
model = models.UserStory
|
||||||
fields = ('subject', 'order')
|
fields = ('subject', 'order')
|
||||||
sortable_field_name = 'order'
|
sortable_field_name = 'order'
|
||||||
extra = 0
|
extra = 0
|
||||||
|
@ -24,40 +23,76 @@ class UserStoryInline(admin.TabularInline):
|
||||||
if obj:
|
if obj:
|
||||||
return obj.user_stories.filter(mileston__isnone=True)
|
return obj.user_stories.filter(mileston__isnone=True)
|
||||||
else:
|
else:
|
||||||
return UserStory.objects.none()
|
return models.UserStory.objects.none()
|
||||||
|
|
||||||
class ProjectAdmin(reversion.VersionAdmin):
|
class ProjectAdmin(reversion.VersionAdmin):
|
||||||
list_display = ["name", "owner"]
|
list_display = ["name", "owner"]
|
||||||
inlines = [MilestoneInline, UserStoryInline]
|
inlines = [MilestoneInline, UserStoryInline]
|
||||||
|
|
||||||
admin.site.register(Project, ProjectAdmin)
|
admin.site.register(models.Project, ProjectAdmin)
|
||||||
|
|
||||||
|
|
||||||
class MilestoneAdmin(reversion.VersionAdmin):
|
class MilestoneAdmin(reversion.VersionAdmin):
|
||||||
list_display = ["name", "project", "owner", "closed", "estimated_start", "estimated_finish"]
|
list_display = ["name", "project", "owner", "closed", "estimated_start", "estimated_finish"]
|
||||||
|
|
||||||
admin.site.register(Milestone, MilestoneAdmin)
|
admin.site.register(models.Milestone, MilestoneAdmin)
|
||||||
|
|
||||||
|
|
||||||
class UserStoryAdmin(reversion.VersionAdmin):
|
class UserStoryAdmin(reversion.VersionAdmin):
|
||||||
list_display = ["ref", "milestone", "project", "owner"]
|
list_display = ["ref", "milestone", "project", "owner"]
|
||||||
|
|
||||||
admin.site.register(UserStory, UserStoryAdmin)
|
admin.site.register(models.UserStory, UserStoryAdmin)
|
||||||
|
|
||||||
|
|
||||||
class ChangeAdmin(reversion.VersionAdmin):
|
class ChangeAdmin(reversion.VersionAdmin):
|
||||||
list_display = ["id", "change_type", "project", "owner"]
|
list_display = ["id", "change_type", "project", "owner"]
|
||||||
|
|
||||||
admin.site.register(Change, ChangeAdmin)
|
admin.site.register(models.Change, ChangeAdmin)
|
||||||
|
|
||||||
|
|
||||||
class ChangeAttachmentAdmin(reversion.VersionAdmin):
|
class ChangeAttachmentAdmin(reversion.VersionAdmin):
|
||||||
list_display = ["id", "change", "owner"]
|
list_display = ["id", "change", "owner"]
|
||||||
|
|
||||||
admin.site.register(ChangeAttachment, ChangeAttachmentAdmin)
|
admin.site.register(models.ChangeAttachment, ChangeAttachmentAdmin)
|
||||||
|
|
||||||
|
|
||||||
class TaskAdmin(reversion.VersionAdmin):
|
class TaskAdmin(reversion.VersionAdmin):
|
||||||
list_display = ["subject", "type", "user_story"]
|
list_display = ["subject", "user_story"]
|
||||||
|
|
||||||
|
|
||||||
|
class IssueAdmin(reversion.VersionAdmin):
|
||||||
|
list_display = ["subject", "type"]
|
||||||
|
|
||||||
|
|
||||||
|
class SeverityAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["name", "order", "project"]
|
||||||
|
|
||||||
|
class PriorityAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["name", "order", "project"]
|
||||||
|
|
||||||
|
class PointsAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["name", "order", "project"]
|
||||||
|
|
||||||
|
class IssueTypeAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["name", "order", "project"]
|
||||||
|
|
||||||
|
class IssueStatusAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["name", "order", "is_closed", "project"]
|
||||||
|
|
||||||
|
class TaskStatusAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["name", "order", "is_closed", "project"]
|
||||||
|
|
||||||
|
class UserStoryStatusAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["name", "order", "is_closed", "project"]
|
||||||
|
|
||||||
|
admin.site.register(models.Task, TaskAdmin)
|
||||||
|
admin.site.register(models.Issue, IssueAdmin)
|
||||||
|
|
||||||
|
admin.site.register(models.Severity, SeverityAdmin)
|
||||||
|
admin.site.register(models.IssueStatus, IssueStatusAdmin)
|
||||||
|
admin.site.register(models.TaskStatus, TaskStatusAdmin)
|
||||||
|
admin.site.register(models.UserStoryStatus, UserStoryStatusAdmin)
|
||||||
|
admin.site.register(models.Priority, PriorityAdmin)
|
||||||
|
admin.site.register(models.IssueType, IssueTypeAdmin)
|
||||||
|
admin.site.register(models.Points, PointsAdmin)
|
||||||
|
|
||||||
admin.site.register(Task, TaskAdmin)
|
|
||||||
|
|
|
@ -2,27 +2,13 @@
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .utils import SCRUM_STATES
|
PRIORITY_CHOICES = (
|
||||||
|
|
||||||
ORG_ROLE_CHOICES = (
|
|
||||||
('owner', _(u'Owner')),
|
|
||||||
('developer', _(u'Developer')),
|
|
||||||
)
|
|
||||||
|
|
||||||
MARKUP_TYPE = (
|
|
||||||
('md', _(u'Markdown')),
|
|
||||||
('rst', _('Restructured Text')),
|
|
||||||
)
|
|
||||||
|
|
||||||
US_STATUS_CHOICES = SCRUM_STATES.get_us_choices()
|
|
||||||
|
|
||||||
TASK_PRIORITY_CHOICES = (
|
|
||||||
(1, _(u'Low')),
|
(1, _(u'Low')),
|
||||||
(3, _(u'Normal')),
|
(3, _(u'Normal')),
|
||||||
(5, _(u'High')),
|
(5, _(u'High')),
|
||||||
)
|
)
|
||||||
|
|
||||||
TASK_SEVERITY_CHOICES = (
|
SEVERITY_CHOICES = (
|
||||||
(1, _(u'Wishlist')),
|
(1, _(u'Wishlist')),
|
||||||
(2, _(u'Minor')),
|
(2, _(u'Minor')),
|
||||||
(3, _(u'Normal')),
|
(3, _(u'Normal')),
|
||||||
|
@ -30,12 +16,32 @@ TASK_SEVERITY_CHOICES = (
|
||||||
(5, _(u'Critical')),
|
(5, _(u'Critical')),
|
||||||
)
|
)
|
||||||
|
|
||||||
TASK_TYPE_CHOICES = (
|
TASKSTATUSES = (
|
||||||
('bug', _(u'Bug')),
|
(1, _(u"New"), False),
|
||||||
('task', _(u'Task')),
|
(2, _(u"In progress"), False),
|
||||||
|
(3, _(u"Ready for test"), True),
|
||||||
|
(4, _(u"Closed"), True),
|
||||||
|
(5, _(u"Needs Info"), False),
|
||||||
)
|
)
|
||||||
|
|
||||||
TASK_STATUS_CHOICES = SCRUM_STATES.get_task_choices()
|
ISSUESTATUSES = (
|
||||||
|
(1, _(u"New"), False),
|
||||||
|
(2, _(u"In progress"), False),
|
||||||
|
(3, _(u"Ready for test"), True),
|
||||||
|
(4, _(u"Closed"), True),
|
||||||
|
(5, _(u"Needs Info"), False),
|
||||||
|
(6, _(u"Rejected"), True),
|
||||||
|
(7, _(u"Postponed"), False),
|
||||||
|
)
|
||||||
|
|
||||||
|
USSTATUSES = (
|
||||||
|
(1, _("Open"), False),
|
||||||
|
(2, _("Closed"), True),
|
||||||
|
)
|
||||||
|
|
||||||
|
ISSUETYPES = (
|
||||||
|
(1, _(u'Bug')),
|
||||||
|
)
|
||||||
|
|
||||||
POINTS_CHOICES = (
|
POINTS_CHOICES = (
|
||||||
(-1, u'?'),
|
(-1, u'?'),
|
||||||
|
@ -53,6 +59,8 @@ POINTS_CHOICES = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: pending to refactor
|
||||||
|
|
||||||
TASK_COMMENT = 1
|
TASK_COMMENT = 1
|
||||||
TASK_STATUS_CHANGE = 2
|
TASK_STATUS_CHANGE = 2
|
||||||
TASK_PRIORITY_CHANGE = 3
|
TASK_PRIORITY_CHANGE = 3
|
||||||
|
|
|
@ -6,15 +6,106 @@ from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.dispatch import receiver
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes import generic
|
from django.contrib.contenttypes import generic
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from picklefield.fields import PickledObjectField
|
||||||
|
|
||||||
from greenmine.base.utils.slug import slugify_uniquely, ref_uniquely
|
from greenmine.base.utils.slug import slugify_uniquely, ref_uniquely
|
||||||
from greenmine.base.fields import DictField
|
from greenmine.base.fields import DictField
|
||||||
from greenmine.base.utils import iter_points
|
from greenmine.base.utils import iter_points
|
||||||
from greenmine.scrum.choices import *
|
from greenmine.scrum.choices import *
|
||||||
from greenmine.scrum.utils import SCRUM_STATES
|
|
||||||
|
|
||||||
|
class Severity(models.Model):
|
||||||
|
name = models.CharField(max_length=255, unique=True)
|
||||||
|
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, unique=True)
|
||||||
|
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, unique=True)
|
||||||
|
order = models.IntegerField(default=10)
|
||||||
|
is_closed = models.BooleanField(default=False)
|
||||||
|
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, unique=True)
|
||||||
|
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):
|
class Project(models.Model):
|
||||||
|
@ -28,19 +119,14 @@ class Project(models.Model):
|
||||||
|
|
||||||
owner = models.ForeignKey("auth.User", related_name="projects")
|
owner = models.ForeignKey("auth.User", related_name="projects")
|
||||||
public = models.BooleanField(default=True)
|
public = models.BooleanField(default=True)
|
||||||
markup = models.CharField(max_length=10, choices=MARKUP_TYPE, default='md')
|
|
||||||
|
|
||||||
last_us_ref = models.BigIntegerField(null=True, default=0)
|
last_us_ref = models.BigIntegerField(null=True, default=1)
|
||||||
last_task_ref = models.BigIntegerField(null=True, default=0)
|
last_task_ref = models.BigIntegerField(null=True, default=1)
|
||||||
|
last_issue_ref = models.BigIntegerField(null=True, default=1)
|
||||||
|
|
||||||
task_parser_re = models.CharField(max_length=1000, blank=True, null=True, default=None)
|
|
||||||
sprints = models.IntegerField(default=1, blank=True, null=True)
|
sprints = models.IntegerField(default=1, blank=True, null=True)
|
||||||
show_burndown = models.BooleanField(default=False, blank=True)
|
|
||||||
show_burnup = models.BooleanField(default=False, blank=True)
|
|
||||||
show_sprint_burndown = models.BooleanField(default=False, blank=True)
|
|
||||||
total_story_points = models.FloatField(default=None, null=True)
|
total_story_points = models.FloatField(default=None, null=True)
|
||||||
|
tags = PickledObjectField()
|
||||||
tags = DictField(blank=True, null=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
|
@ -107,6 +193,7 @@ class Project(models.Model):
|
||||||
class Milestone(models.Model):
|
class Milestone(models.Model):
|
||||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||||
name = models.CharField(max_length=200, db_index=True)
|
name = models.CharField(max_length=200, db_index=True)
|
||||||
|
slug = models.SlugField(max_length=250, unique=True, blank=True)
|
||||||
owner = models.ForeignKey('auth.User', related_name="milestones")
|
owner = models.ForeignKey('auth.User', related_name="milestones")
|
||||||
project = models.ForeignKey('Project', related_name="milestones")
|
project = models.ForeignKey('Project', related_name="milestones")
|
||||||
|
|
||||||
|
@ -122,6 +209,12 @@ class Milestone(models.Model):
|
||||||
|
|
||||||
tags = DictField(blank=True, null=True)
|
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:
|
class Meta:
|
||||||
ordering = ['-created_date']
|
ordering = ['-created_date']
|
||||||
unique_together = ('name', 'project')
|
unique_together = ('name', 'project')
|
||||||
|
@ -144,35 +237,30 @@ class Milestone(models.Model):
|
||||||
|
|
||||||
class UserStory(models.Model):
|
class UserStory(models.Model):
|
||||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||||
ref = models.CharField(max_length=200, db_index=True, null=True, default=None)
|
ref = models.BigIntegerField(db_index=True, null=True, default=None)
|
||||||
milestone = models.ForeignKey("Milestone", blank=True,
|
milestone = models.ForeignKey("Milestone", blank=True,
|
||||||
related_name="user_stories", null=True,
|
related_name="user_stories", null=True,
|
||||||
default=None)
|
default=None)
|
||||||
project = models.ForeignKey("Project", related_name="user_stories")
|
project = models.ForeignKey("Project", related_name="user_stories")
|
||||||
owner = models.ForeignKey("auth.User", null=True, default=None,
|
owner = models.ForeignKey("auth.User", null=True, default=None,
|
||||||
related_name="user_stories")
|
related_name="user_stories")
|
||||||
priority = models.IntegerField(default=1)
|
|
||||||
points = models.IntegerField(choices=POINTS_CHOICES, default=-1)
|
status = models.ForeignKey("UserStoryStatus", related_name="userstories", default=1)
|
||||||
status = models.CharField(max_length=50,
|
points = models.ForeignKey("Points", related_name="userstories", default= -1)
|
||||||
choices=SCRUM_STATES.get_us_choices(),
|
order = models.PositiveSmallIntegerField(default=100)
|
||||||
db_index=True, default="open")
|
|
||||||
|
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
modified_date = models.DateTimeField(auto_now_add=True, auto_now=True)
|
modified_date = models.DateTimeField(auto_now_add=True, auto_now=True)
|
||||||
tested = models.BooleanField(default=False)
|
finish_date = models.DateTimeField(null=True, blank=True)
|
||||||
|
|
||||||
subject = models.CharField(max_length=500)
|
subject = models.CharField(max_length=500)
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
finish_date = models.DateTimeField(null=True, blank=True)
|
|
||||||
|
|
||||||
watchers = models.ManyToManyField('auth.User', related_name='us_watch',
|
watchers = models.ManyToManyField('auth.User', related_name='us_watch', null=True)
|
||||||
null=True)
|
|
||||||
|
|
||||||
client_requirement = models.BooleanField(default=False)
|
client_requirement = models.BooleanField(default=False)
|
||||||
team_requirement = models.BooleanField(default=False)
|
team_requirement = models.BooleanField(default=False)
|
||||||
order = models.PositiveSmallIntegerField("Order")
|
tags = PickledObjectField()
|
||||||
|
|
||||||
tags = DictField(blank=True, null=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['order']
|
ordering = ['order']
|
||||||
|
@ -186,7 +274,7 @@ class UserStory(models.Model):
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.ref:
|
if not self.ref:
|
||||||
self.ref = ref_uniquely(self.project, self.__class__)
|
self.ref = ref_uniquely(self.project, "last_us_ref", self.__class__)
|
||||||
|
|
||||||
super(UserStory, self).save(*args, **kwargs)
|
super(UserStory, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -216,118 +304,24 @@ class ChangeAttachment(models.Model):
|
||||||
tags = DictField(blank=True, null=True)
|
tags = DictField(blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
class TaskQuerySet(models.query.QuerySet):
|
|
||||||
def _add_categories(self, section_dict, category_id, category_element, selected):
|
|
||||||
section_dict[category_id] = section_dict.get(category_id, {
|
|
||||||
'element': unicode(category_element),
|
|
||||||
'count': 0,
|
|
||||||
'id': category_id,
|
|
||||||
'selected': selected,
|
|
||||||
})
|
|
||||||
section_dict[category_id]['count'] += 1
|
|
||||||
|
|
||||||
def _get_category(self, section_dict, order_by='element', reverse=False):
|
|
||||||
values = section_dict.values()
|
|
||||||
values = sorted(values, key=lambda entry: unicode(entry[order_by]))
|
|
||||||
if reverse:
|
|
||||||
values.reverse()
|
|
||||||
return values
|
|
||||||
|
|
||||||
def _get_filter_and_build_filter_dict(self, queryset, milestone_id, status_id, tags_ids, assigned_to_id, severity_id):
|
|
||||||
task_list = list(queryset)
|
|
||||||
milestones = {}
|
|
||||||
status = {}
|
|
||||||
tags = {}
|
|
||||||
assigned_to = {}
|
|
||||||
severity = {}
|
|
||||||
|
|
||||||
for task in task_list:
|
|
||||||
if task.milestone:
|
|
||||||
selected = milestone_id and task.milestone.id == milestone_id
|
|
||||||
self._add_categories(milestones, task.milestone.id, task.milestone.name, selected)
|
|
||||||
|
|
||||||
selected = status_id and task.status == status_id
|
|
||||||
self._add_categories(status, task.status, task.get_status_display(), selected)
|
|
||||||
|
|
||||||
for tag in task.tags.all():
|
|
||||||
selected = tags_ids and tag.id in tags_ids
|
|
||||||
self._add_categories(tags, tag.id, tag.name, selected)
|
|
||||||
|
|
||||||
if task.assigned_to:
|
|
||||||
selected = assigned_to_id and task.assigned_to.id == assigned_to_id
|
|
||||||
self._add_categories(assigned_to, task.assigned_to.id, task.assigned_to.first_name, selected)
|
|
||||||
|
|
||||||
selected = severity_id and task.severity == int(severity_id)
|
|
||||||
self._add_categories(severity, task.severity, task.get_severity_display(), selected)
|
|
||||||
|
|
||||||
return{
|
|
||||||
'list': task_list,
|
|
||||||
'filters': {
|
|
||||||
'milestones': self._get_category(milestones),
|
|
||||||
'status': self._get_category(status),
|
|
||||||
'tags': self._get_category(tags),
|
|
||||||
'assigned_to': self._get_category(assigned_to),
|
|
||||||
'severity': self._get_category(severity),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def filter_and_build_filter_dict(self, milestone=None, status=None, tags=None, assigned_to=None, severity=None):
|
|
||||||
|
|
||||||
queryset = self
|
|
||||||
if milestone:
|
|
||||||
queryset = queryset.filter(milestone=milestone)
|
|
||||||
|
|
||||||
if status:
|
|
||||||
queryset = queryset.filter(status=status)
|
|
||||||
|
|
||||||
if tags:
|
|
||||||
for tag in tags:
|
|
||||||
queryset = queryset.filter(tags__in=[tag])
|
|
||||||
|
|
||||||
if assigned_to:
|
|
||||||
queryset = queryset.filter(assigned_to=assigned_to)
|
|
||||||
|
|
||||||
if severity:
|
|
||||||
queryset = queryset.filter(severity=severity)
|
|
||||||
|
|
||||||
milestone_id = milestone and milestone.id
|
|
||||||
status_id = status
|
|
||||||
tags_ids = tags and tags.values_list('id', flat=True)
|
|
||||||
assigned_to_id = assigned_to and assigned_to.id
|
|
||||||
severity_id = severity
|
|
||||||
|
|
||||||
return self._get_filter_and_build_filter_dict(queryset, milestone_id, status_id, tags_ids, assigned_to_id, severity_id)
|
|
||||||
|
|
||||||
|
|
||||||
class TaskManager(models.Manager):
|
|
||||||
def get_query_set(self):
|
|
||||||
return TaskQuerySet(self.model)
|
|
||||||
|
|
||||||
|
|
||||||
class Task(models.Model):
|
class Task(models.Model):
|
||||||
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||||
user_story = models.ForeignKey('UserStory', related_name='tasks', null=True, blank=True)
|
user_story = models.ForeignKey('UserStory', related_name='tasks')
|
||||||
last_user_story = models.ForeignKey('UserStory', null=True, blank=True)
|
ref = models.BigIntegerField(db_index=True, null=True, default=None)
|
||||||
ref = models.CharField(max_length=200, db_index=True, null=True, default=None)
|
|
||||||
status = models.CharField(max_length=50, choices=TASK_STATUS_CHOICES,
|
|
||||||
default='open')
|
|
||||||
owner = models.ForeignKey("auth.User", null=True, default=None,
|
owner = models.ForeignKey("auth.User", null=True, default=None,
|
||||||
related_name="tasks")
|
related_name="tasks")
|
||||||
|
|
||||||
severity = models.IntegerField(choices=TASK_SEVERITY_CHOICES, default=3)
|
severity = models.ForeignKey("Severity", related_name="tasks")
|
||||||
priority = models.IntegerField(choices=TASK_PRIORITY_CHOICES, default=3)
|
priority = models.ForeignKey("Priority", related_name="tasks")
|
||||||
|
|
||||||
milestone = models.ForeignKey('Milestone', related_name='tasks', null=True,
|
milestone = models.ForeignKey('Milestone', related_name='tasks', null=True,
|
||||||
default=None, blank=True)
|
default=None, blank=True)
|
||||||
|
|
||||||
project = models.ForeignKey('Project', related_name='tasks')
|
project = models.ForeignKey('Project', related_name='tasks')
|
||||||
type = models.CharField(max_length=10, choices=TASK_TYPE_CHOICES,
|
|
||||||
default='task')
|
|
||||||
|
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
modified_date = models.DateTimeField(auto_now_add=True)
|
modified_date = models.DateTimeField(auto_now_add=True)
|
||||||
finished_date = models.DateTimeField(null=True, blank=True)
|
finished_date = models.DateTimeField(null=True, blank=True)
|
||||||
last_status = models.CharField(max_length=50, choices=TASK_STATUS_CHOICES,
|
|
||||||
null=True, blank=True)
|
|
||||||
|
|
||||||
subject = models.CharField(max_length=500)
|
subject = models.CharField(max_length=500)
|
||||||
description = models.TextField(blank=True)
|
description = models.TextField(blank=True)
|
||||||
|
@ -339,9 +333,7 @@ class Task(models.Model):
|
||||||
null=True)
|
null=True)
|
||||||
|
|
||||||
changes = generic.GenericRelation(Change)
|
changes = generic.GenericRelation(Change)
|
||||||
tags = DictField(blank=True, null=True)
|
tags = PickledObjectField()
|
||||||
|
|
||||||
objects = TaskManager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('ref', 'project')
|
unique_together = ('ref', 'project')
|
||||||
|
@ -349,38 +341,98 @@ class Task(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.subject
|
return self.subject
|
||||||
|
|
||||||
@property
|
|
||||||
def fake_status(self):
|
|
||||||
return SCRUM_STATES.get_us_state_for_task_state(self.status)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
last_user_story = None
|
|
||||||
if self.last_user_story != self.user_story:
|
|
||||||
last_user_story = self.last_user_story
|
|
||||||
self.last_user_story = self.user_story
|
|
||||||
|
|
||||||
if self.id:
|
if self.id:
|
||||||
self.modified_date = timezone.now()
|
self.modified_date = timezone.now()
|
||||||
# Store information about close date of a task
|
|
||||||
if self.last_status != self.status:
|
|
||||||
if self.last_status in SCRUM_STATES.get_finished_task_states():
|
|
||||||
if self.status in SCRUM_STATES.get_unfinished_task_states():
|
|
||||||
self.finished_date = None
|
|
||||||
elif self.last_status in SCRUM_STATES.get_unfinished_task_states():
|
|
||||||
if self.status in SCRUM_STATES.get_finished_task_states():
|
|
||||||
self.finished_date = timezone.now()
|
|
||||||
self.last_status = self.status
|
|
||||||
|
|
||||||
if not self.ref:
|
if not self.ref:
|
||||||
self.ref = ref_uniquely(self.project, self.__class__)
|
self.ref = ref_uniquely(self.project, "last_task_ref", self.__class__)
|
||||||
|
|
||||||
super(Task, self).save(*args, **kwargs)
|
super(Task, self).save(*args, **kwargs)
|
||||||
|
|
||||||
if last_user_story:
|
|
||||||
last_user_story.update_status()
|
|
||||||
|
|
||||||
if self.user_story:
|
class Issue(models.Model):
|
||||||
self.user_story.update_status()
|
uuid = models.CharField(max_length=40, unique=True, blank=True)
|
||||||
|
ref = models.BigIntegerField(db_index=True, null=True, default=None)
|
||||||
|
owner = models.ForeignKey("auth.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('auth.User',
|
||||||
|
related_name='issues_assigned_to_me',
|
||||||
|
blank=True, null=True, default=None)
|
||||||
|
|
||||||
|
watchers = models.ManyToManyField('auth.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.profile.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 in TASKSTATUSES:
|
||||||
|
TaskStatus.objects.create(name=name, order=order,
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
from . import sigdispatch
|
|
||||||
|
# Email alerts signals handlers
|
||||||
|
# TODO: temporary commented (Pending refactor)
|
||||||
|
# from . import sigdispatch
|
||||||
|
|
|
@ -106,12 +106,3 @@ def mail_task_assigned(sender, task, user, **kwargs):
|
||||||
|
|
||||||
subject = ugettext("Greenmine: task assigned")
|
subject = ugettext("Greenmine: task assigned")
|
||||||
send_mail.delay(subject, template, [task.assigned_to.email])
|
send_mail.delay(subject, template, [task.assigned_to.email])
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Project)
|
|
||||||
def project_post_save(sender, instance, created, **kwargs):
|
|
||||||
"""
|
|
||||||
Recalculate project groups
|
|
||||||
"""
|
|
||||||
if created:
|
|
||||||
RoleGroupsService().replicate_all_roles_on_one_project(instance)
|
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
__all__ = ('SCRUM_STATES',)
|
|
||||||
|
|
||||||
|
|
||||||
class GmScrumStates(object):
|
|
||||||
def __init__(self):
|
|
||||||
self._states = settings.GM_SCRUM_STATES
|
|
||||||
|
|
||||||
def get_task_choices(self):
|
|
||||||
task_choices = []
|
|
||||||
for us_state in self._states.values():
|
|
||||||
task_choices += us_state['task_states']
|
|
||||||
return task_choices
|
|
||||||
|
|
||||||
def get_us_choices(self):
|
|
||||||
us_choices = []
|
|
||||||
for key, value in self._states.iteritems():
|
|
||||||
us_choices.append((key, value['name']))
|
|
||||||
return us_choices
|
|
||||||
|
|
||||||
def get_finished_task_states(self):
|
|
||||||
finished_task_states = []
|
|
||||||
for us_state in self._states.values():
|
|
||||||
if us_state['is_finished']:
|
|
||||||
finished_task_states += us_state['task_states']
|
|
||||||
return [x[0] for x in finished_task_states]
|
|
||||||
|
|
||||||
def get_unfinished_task_states(self):
|
|
||||||
unfinished_task_states = []
|
|
||||||
for us_state in self._states.values():
|
|
||||||
if not us_state['is_finished']:
|
|
||||||
unfinished_task_states += us_state['task_states']
|
|
||||||
return [x[0] for x in unfinished_task_states]
|
|
||||||
|
|
||||||
def get_finished_us_states(self):
|
|
||||||
finished_us_states = []
|
|
||||||
for key, value in self._states.iteritems():
|
|
||||||
if value['is_finished']:
|
|
||||||
finished_us_states.append(key)
|
|
||||||
return finished_us_states
|
|
||||||
|
|
||||||
def get_unfinished_us_states(self):
|
|
||||||
finished_us_states = []
|
|
||||||
for key, value in self._states.iteritems():
|
|
||||||
if not value['is_finished']:
|
|
||||||
finished_us_states.append(key)
|
|
||||||
return finished_us_states
|
|
||||||
|
|
||||||
def get_us_state_for_task_state(self, state):
|
|
||||||
for key, value in self._states.iteritems():
|
|
||||||
if state in [x[0] for x in value['task_states']]:
|
|
||||||
return key
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_task_states_for_us_state(self, state):
|
|
||||||
if state in self._states.keys():
|
|
||||||
return [x[0] for x in self._states[state]['task_states']]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def ordered_us_states(self):
|
|
||||||
ordered = sorted([(value['order'], key) for key, value in self._states.iteritems()])
|
|
||||||
return [x[1] for x in ordered]
|
|
||||||
|
|
||||||
SCRUM_STATES = GmScrumStates()
|
|
Loading…
Reference in New Issue