Refactor choices.

remotes/origin/enhancement/email-actions
Andrey Antukh 2013-03-26 13:42:09 +01:00
parent 28650f46c4
commit 38306a176e
6 changed files with 294 additions and 271 deletions

View File

@ -23,19 +23,21 @@ def slugify_uniquely(value, model, slugfield="slug"):
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.
"""
project = project.__class__.objects.select_for_update().get(pk=p.pk)
# this prevents concurrent and inconsistent references.
time.sleep(0.001)
ref = getattr(project, seq_field) + 1
new_timestamp = lambda: int("".join(str(time.time()).split(".")))
while True:
potential = baseconv.base62.encode(new_timestamp())
params = {field: potential, 'project': project}
params = {field: ref, 'project': _project}
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)
ref += 1

View File

@ -1,21 +1,20 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
from guardian.admin import GuardedModelAdmin
from greenmine.scrum import models
import reversion
from greenmine.scrum.models import Project, Milestone, UserStory, Change, \
ChangeAttachment, Task
class MilestoneInline(admin.TabularInline):
model = Milestone
model = models.Milestone
fields = ('name', 'owner', 'estimated_start', 'estimated_finish', 'closed', 'disponibility', 'order')
sortable_field_name = 'order'
extra = 0
class UserStoryInline(admin.TabularInline):
model = UserStory
model = models.UserStory
fields = ('subject', 'order')
sortable_field_name = 'order'
extra = 0
@ -24,40 +23,76 @@ class UserStoryInline(admin.TabularInline):
if obj:
return obj.user_stories.filter(mileston__isnone=True)
else:
return UserStory.objects.none()
return models.UserStory.objects.none()
class ProjectAdmin(reversion.VersionAdmin):
list_display = ["name", "owner"]
inlines = [MilestoneInline, UserStoryInline]
admin.site.register(Project, ProjectAdmin)
admin.site.register(models.Project, ProjectAdmin)
class MilestoneAdmin(reversion.VersionAdmin):
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):
list_display = ["ref", "milestone", "project", "owner"]
admin.site.register(UserStory, UserStoryAdmin)
admin.site.register(models.UserStory, UserStoryAdmin)
class ChangeAdmin(reversion.VersionAdmin):
list_display = ["id", "change_type", "project", "owner"]
admin.site.register(Change, ChangeAdmin)
admin.site.register(models.Change, ChangeAdmin)
class ChangeAttachmentAdmin(reversion.VersionAdmin):
list_display = ["id", "change", "owner"]
admin.site.register(ChangeAttachment, ChangeAttachmentAdmin)
admin.site.register(models.ChangeAttachment, ChangeAttachmentAdmin)
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)

View File

@ -2,27 +2,13 @@
from django.utils.translation import ugettext_lazy as _
from .utils import SCRUM_STATES
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 = (
PRIORITY_CHOICES = (
(1, _(u'Low')),
(3, _(u'Normal')),
(5, _(u'High')),
)
TASK_SEVERITY_CHOICES = (
SEVERITY_CHOICES = (
(1, _(u'Wishlist')),
(2, _(u'Minor')),
(3, _(u'Normal')),
@ -30,12 +16,32 @@ TASK_SEVERITY_CHOICES = (
(5, _(u'Critical')),
)
TASK_TYPE_CHOICES = (
('bug', _(u'Bug')),
('task', _(u'Task')),
TASKSTATUSES = (
(1, _(u"New"), False),
(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 = (
(-1, u'?'),
@ -53,6 +59,8 @@ POINTS_CHOICES = (
)
# TODO: pending to refactor
TASK_COMMENT = 1
TASK_STATUS_CHANGE = 2
TASK_PRIORITY_CHANGE = 3

View File

@ -6,15 +6,106 @@ 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 django.contrib.auth.models import User
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 *
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):
@ -28,19 +119,14 @@ class Project(models.Model):
owner = models.ForeignKey("auth.User", related_name="projects")
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_task_ref = models.BigIntegerField(null=True, default=0)
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)
task_parser_re = models.CharField(max_length=1000, blank=True, null=True, default=None)
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)
tags = DictField(blank=True, null=True)
tags = PickledObjectField()
class Meta:
permissions = (
@ -107,6 +193,7 @@ class Project(models.Model):
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('auth.User', related_name="milestones")
project = models.ForeignKey('Project', related_name="milestones")
@ -122,6 +209,12 @@ class Milestone(models.Model):
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')
@ -144,35 +237,30 @@ class Milestone(models.Model):
class UserStory(models.Model):
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,
related_name="user_stories", null=True,
default=None)
project = models.ForeignKey("Project", related_name="user_stories")
owner = models.ForeignKey("auth.User", null=True, default=None,
related_name="user_stories")
priority = models.IntegerField(default=1)
points = models.IntegerField(choices=POINTS_CHOICES, default=-1)
status = models.CharField(max_length=50,
choices=SCRUM_STATES.get_us_choices(),
db_index=True, default="open")
status = models.ForeignKey("UserStoryStatus", related_name="userstories", default=1)
points = models.ForeignKey("Points", related_name="userstories", default= -1)
order = models.PositiveSmallIntegerField(default=100)
created_date = models.DateTimeField(auto_now_add=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)
description = models.TextField()
finish_date = models.DateTimeField(null=True, blank=True)
watchers = models.ManyToManyField('auth.User', related_name='us_watch',
null=True)
watchers = models.ManyToManyField('auth.User', related_name='us_watch', null=True)
client_requirement = models.BooleanField(default=False)
team_requirement = models.BooleanField(default=False)
order = models.PositiveSmallIntegerField("Order")
tags = DictField(blank=True, null=True)
tags = PickledObjectField()
class Meta:
ordering = ['order']
@ -186,7 +274,7 @@ class UserStory(models.Model):
def save(self, *args, **kwargs):
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)
@ -216,118 +304,24 @@ class ChangeAttachment(models.Model):
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):
uuid = models.CharField(max_length=40, unique=True, blank=True)
user_story = models.ForeignKey('UserStory', related_name='tasks', null=True, blank=True)
last_user_story = models.ForeignKey('UserStory', null=True, blank=True)
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')
user_story = models.ForeignKey('UserStory', related_name='tasks')
ref = models.BigIntegerField(db_index=True, null=True, default=None)
owner = models.ForeignKey("auth.User", null=True, default=None,
related_name="tasks")
severity = models.IntegerField(choices=TASK_SEVERITY_CHOICES, default=3)
priority = models.IntegerField(choices=TASK_PRIORITY_CHOICES, default=3)
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')
type = models.CharField(max_length=10, choices=TASK_TYPE_CHOICES,
default='task')
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now_add=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)
description = models.TextField(blank=True)
@ -339,9 +333,7 @@ class Task(models.Model):
null=True)
changes = generic.GenericRelation(Change)
tags = DictField(blank=True, null=True)
objects = TaskManager()
tags = PickledObjectField()
class Meta:
unique_together = ('ref', 'project')
@ -349,38 +341,98 @@ class Task(models.Model):
def __unicode__(self):
return self.subject
@property
def fake_status(self):
return SCRUM_STATES.get_us_state_for_task_state(self.status)
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:
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:
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)
if last_user_story:
last_user_story.update_status()
if self.user_story:
self.user_story.update_status()
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("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

View File

@ -106,12 +106,3 @@ def mail_task_assigned(sender, task, user, **kwargs):
subject = ugettext("Greenmine: task assigned")
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)

View File

@ -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()