From 38306a176efbcf9205cc1640a4c3d859f285b317 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 26 Mar 2013 13:42:09 +0100 Subject: [PATCH] Refactor choices. --- greenmine/base/utils/slug.py | 16 +- greenmine/scrum/admin.py | 63 ++++-- greenmine/scrum/choices.py | 60 +++--- greenmine/scrum/models.py | 352 +++++++++++++++++++-------------- greenmine/scrum/sigdispatch.py | 9 - greenmine/scrum/utils.py | 65 ------ 6 files changed, 294 insertions(+), 271 deletions(-) delete mode 100644 greenmine/scrum/utils.py diff --git a/greenmine/base/utils/slug.py b/greenmine/base/utils/slug.py index a3528231..96a55d17 100644 --- a/greenmine/base/utils/slug.py +++ b/greenmine/base/utils/slug.py @@ -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 diff --git a/greenmine/scrum/admin.py b/greenmine/scrum/admin.py index c84802cf..c7207f51 100644 --- a/greenmine/scrum/admin.py +++ b/greenmine/scrum/admin.py @@ -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) diff --git a/greenmine/scrum/choices.py b/greenmine/scrum/choices.py index afc78a42..d8f95690 100644 --- a/greenmine/scrum/choices.py +++ b/greenmine/scrum/choices.py @@ -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,22 +16,42 @@ 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'?'), - (0, u'0'), + (0, u'0'), (-2, u'1/2'), - (1, u'1'), - (2, u'2'), - (3, u'3'), - (5, u'5'), - (8, u'8'), + (1, u'1'), + (2, u'2'), + (3, u'3'), + (5, u'5'), + (8, u'8'), (10, u'10'), (15, u'15'), (20, u'20'), @@ -53,6 +59,8 @@ POINTS_CHOICES = ( ) +# TODO: pending to refactor + TASK_COMMENT = 1 TASK_STATUS_CHANGE = 2 TASK_PRIORITY_CHANGE = 3 diff --git a/greenmine/scrum/models.py b/greenmine/scrum/models.py index c4538121..8747677d 100644 --- a/greenmine/scrum/models.py +++ b/greenmine/scrum/models.py @@ -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 diff --git a/greenmine/scrum/sigdispatch.py b/greenmine/scrum/sigdispatch.py index 5bf7789b..111aa57d 100644 --- a/greenmine/scrum/sigdispatch.py +++ b/greenmine/scrum/sigdispatch.py @@ -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) diff --git a/greenmine/scrum/utils.py b/greenmine/scrum/utils.py deleted file mode 100644 index 3a1b7bb4..00000000 --- a/greenmine/scrum/utils.py +++ /dev/null @@ -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()