Refactoring a little the milestone and project models
parent
8ee3554720
commit
44ff29f511
|
@ -0,0 +1,9 @@
|
||||||
|
import collections
|
||||||
|
|
||||||
|
|
||||||
|
def dict_sum(*args):
|
||||||
|
result = collections.Counter()
|
||||||
|
for arg in args:
|
||||||
|
assert isinstance(arg, dict)
|
||||||
|
result += collections.Counter(arg)
|
||||||
|
return result
|
|
@ -5,13 +5,13 @@ from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from greenmine.base.utils.slug import slugify_uniquely
|
from greenmine.base.utils.slug import slugify_uniquely
|
||||||
|
from greenmine.base.utils.dicts import dict_sum
|
||||||
from greenmine.base.notifications.models import WatchedMixin
|
from greenmine.base.notifications.models import WatchedMixin
|
||||||
|
|
||||||
from greenmine.projects.userstories.models import UserStory
|
from greenmine.projects.userstories.models import UserStory
|
||||||
|
|
||||||
import reversion
|
import reversion
|
||||||
import itertools
|
import itertools
|
||||||
import copy
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,23 +83,15 @@ class Milestone(WatchedMixin):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _dict_sum(self, dict1, dict2):
|
|
||||||
dict_result = copy.copy(dict2)
|
|
||||||
for key, value in dict1.items():
|
|
||||||
if key in dict_result:
|
|
||||||
dict_result[key] += value
|
|
||||||
else:
|
|
||||||
dict_result[key] = value
|
|
||||||
return dict_result
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_points(self):
|
def total_points(self):
|
||||||
return self._get_user_stories_points([us for us in self.user_stories.all()])
|
return self._get_user_stories_points([us for us in self.user_stories.all()])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def closed_points(self):
|
def closed_points(self):
|
||||||
return self._get_user_stories_points([us for us in self.user_stories.all()
|
return self._get_user_stories_points(
|
||||||
if us.is_closed])
|
[us for us in self.user_stories.all() if us.is_closed]
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def client_increment_points(self):
|
def client_increment_points(self):
|
||||||
|
@ -113,9 +105,10 @@ class Milestone(WatchedMixin):
|
||||||
team_requirement=False
|
team_requirement=False
|
||||||
)
|
)
|
||||||
client_increment = self._get_user_stories_points(user_stories)
|
client_increment = self._get_user_stories_points(user_stories)
|
||||||
shared_increment = {key: value/2 for key, value in
|
shared_increment = {
|
||||||
self.shared_increment_points.items()}
|
key: value/2 for key, value in self.shared_increment_points.items()
|
||||||
return self._dict_sum(client_increment, shared_increment)
|
}
|
||||||
|
return dict_sum(client_increment, shared_increment)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def team_increment_points(self):
|
def team_increment_points(self):
|
||||||
|
@ -129,9 +122,10 @@ class Milestone(WatchedMixin):
|
||||||
team_requirement=True
|
team_requirement=True
|
||||||
)
|
)
|
||||||
team_increment = self._get_user_stories_points(user_stories)
|
team_increment = self._get_user_stories_points(user_stories)
|
||||||
shared_increment = {key: value/2 for key, value in
|
shared_increment = {
|
||||||
self.shared_increment_points.items()}
|
key: value/2 for key, value in self.shared_increment_points.items()
|
||||||
return self._dict_sum(team_increment, shared_increment)
|
}
|
||||||
|
return dict_sum(team_increment, shared_increment)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def shared_increment_points(self):
|
def shared_increment_points(self):
|
||||||
|
@ -153,8 +147,11 @@ class Milestone(WatchedMixin):
|
||||||
}
|
}
|
||||||
|
|
||||||
def closed_points_by_date(self, date):
|
def closed_points_by_date(self, date):
|
||||||
return self._get_user_stories_points([us for us in self.user_stories.filter(finish_date__lt=date + datetime.timedelta(days=1))
|
return self._get_user_stories_points([
|
||||||
if us.is_closed])
|
us for us in self.user_stories.filter(
|
||||||
|
finish_date__lt=date + datetime.timedelta(days=1)
|
||||||
|
) if us.is_closed
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
# Reversion registration (usufull for base.notification and for meke a historical)
|
# Reversion registration (usufull for base.notification and for meke a historical)
|
||||||
|
|
|
@ -12,20 +12,20 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from picklefield.fields import PickledObjectField
|
from picklefield.fields import PickledObjectField
|
||||||
|
|
||||||
from greenmine.base.utils.slug import slugify_uniquely
|
from greenmine.base.utils.slug import slugify_uniquely
|
||||||
from greenmine.base.notifications.models import WatchedMixin
|
from greenmine.base.utils.dicts import dict_sum
|
||||||
from greenmine.projects.userstories.models import UserStory
|
from greenmine.projects.userstories.models import UserStory
|
||||||
from . import choices
|
from . import choices
|
||||||
|
|
||||||
import reversion
|
import reversion
|
||||||
import itertools
|
import itertools
|
||||||
import copy
|
|
||||||
|
|
||||||
|
|
||||||
def get_attachment_file_path(instance, filename):
|
def get_attachment_file_path(instance, filename):
|
||||||
return "attachment-files/{project}/{model}/{filename}".format(
|
return "attachment-files/{project}/{model}/{filename}".format(
|
||||||
project=instance.project.slug,
|
project=instance.project.slug,
|
||||||
model=instance.content_type.model,
|
model=instance.content_type.model,
|
||||||
filename=filename)
|
filename=filename
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Attachment(models.Model):
|
class Attachment(models.Model):
|
||||||
|
@ -106,6 +106,7 @@ class Project(models.Model):
|
||||||
verbose_name=_("total story points"))
|
verbose_name=_("total story points"))
|
||||||
tags = PickledObjectField(null=False, blank=True,
|
tags = PickledObjectField(null=False, blank=True,
|
||||||
verbose_name=_("tags"))
|
verbose_name=_("tags"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "project"
|
verbose_name = "project"
|
||||||
verbose_name_plural = "projects"
|
verbose_name_plural = "projects"
|
||||||
|
@ -136,8 +137,10 @@ class Project(models.Model):
|
||||||
|
|
||||||
def get_users(self):
|
def get_users(self):
|
||||||
user_model = get_user_model()
|
user_model = get_user_model()
|
||||||
return user_model.objects.filter(id__in=list(self.memberships.values_list(
|
return user_model.objects.filter(
|
||||||
"user", flat=True)))
|
id__in=list(self.memberships.values_list("user", flat=True))
|
||||||
|
)
|
||||||
|
|
||||||
def update_role_points(self):
|
def update_role_points(self):
|
||||||
rolepoints_model = get_model("userstories", "RolePoints")
|
rolepoints_model = get_model("userstories", "RolePoints")
|
||||||
|
|
||||||
|
@ -152,8 +155,8 @@ class Project(models.Model):
|
||||||
for us in self.user_stories.all():
|
for us in self.user_stories.all():
|
||||||
for role in roles:
|
for role in roles:
|
||||||
if not us.role_points.filter(role=role).exists():
|
if not us.role_points.filter(role=role).exists():
|
||||||
sp = rolepoints_model.objects.create(role=role, user_story=us,
|
rolepoints_model.objects.create(role=role, user_story=us,
|
||||||
points=null_points_value)
|
points=null_points_value)
|
||||||
|
|
||||||
# Now remove rolepoints associated with not existing roles.
|
# Now remove rolepoints associated with not existing roles.
|
||||||
rp_query = rolepoints_model.objects.filter(user_story__in=self.user_stories.all())
|
rp_query = rolepoints_model.objects.filter(user_story__in=self.user_stories.all())
|
||||||
|
@ -174,16 +177,6 @@ class Project(models.Model):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _dict_sum(self, dict1, dict2):
|
|
||||||
dict_result = copy.copy(dict2)
|
|
||||||
for key, value in dict1.items():
|
|
||||||
if key in dict_result:
|
|
||||||
dict_result[key] += value
|
|
||||||
else:
|
|
||||||
dict_result[key] = value
|
|
||||||
return dict_result
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def future_team_increment(self):
|
def future_team_increment(self):
|
||||||
user_stories = UserStory.objects.none()
|
user_stories = UserStory.objects.none()
|
||||||
|
@ -197,7 +190,7 @@ class Project(models.Model):
|
||||||
)
|
)
|
||||||
team_increment = self._get_user_stories_points(user_stories)
|
team_increment = self._get_user_stories_points(user_stories)
|
||||||
shared_increment = {key: value/2 for key, value in self.future_shared_increment.items()}
|
shared_increment = {key: value/2 for key, value in self.future_shared_increment.items()}
|
||||||
return self._dict_sum(team_increment, shared_increment)
|
return dict_sum(team_increment, shared_increment)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def future_client_increment(self):
|
def future_client_increment(self):
|
||||||
|
@ -212,7 +205,7 @@ class Project(models.Model):
|
||||||
)
|
)
|
||||||
client_increment = self._get_user_stories_points(user_stories)
|
client_increment = self._get_user_stories_points(user_stories)
|
||||||
shared_increment = {key: value/2 for key, value in self.future_shared_increment.items()}
|
shared_increment = {key: value/2 for key, value in self.future_shared_increment.items()}
|
||||||
return self._dict_sum(client_increment, shared_increment)
|
return dict_sum(client_increment, shared_increment)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def future_shared_increment(self):
|
def future_shared_increment(self):
|
||||||
|
@ -227,8 +220,8 @@ class Project(models.Model):
|
||||||
)
|
)
|
||||||
return self._get_user_stories_points(user_stories)
|
return self._get_user_stories_points(user_stories)
|
||||||
|
|
||||||
# User Stories common Models
|
|
||||||
|
|
||||||
|
# User Stories common Models
|
||||||
class UserStoryStatus(models.Model):
|
class UserStoryStatus(models.Model):
|
||||||
name = models.CharField(max_length=255, null=False, blank=False,
|
name = models.CharField(max_length=255, null=False, blank=False,
|
||||||
verbose_name=_("name"))
|
verbose_name=_("name"))
|
||||||
|
@ -420,6 +413,7 @@ class QuestionStatus(models.Model):
|
||||||
reversion.register(Project)
|
reversion.register(Project)
|
||||||
reversion.register(Attachment)
|
reversion.register(Attachment)
|
||||||
|
|
||||||
|
|
||||||
# On membership object is created/changed, update
|
# On membership object is created/changed, update
|
||||||
# role-points relation.
|
# role-points relation.
|
||||||
@receiver(models.signals.post_save, sender=Membership,
|
@receiver(models.signals.post_save, sender=Membership,
|
||||||
|
@ -449,12 +443,12 @@ def project_post_save(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
for order, name, is_closed in choices.US_STATUSES:
|
for order, name, is_closed in choices.US_STATUSES:
|
||||||
UserStoryStatus.objects.create(name=name, order=order,
|
UserStoryStatus.objects.create(name=name, order=order,
|
||||||
is_closed=is_closed, project=instance)
|
is_closed=is_closed, project=instance)
|
||||||
|
|
||||||
# Tasks
|
# Tasks
|
||||||
for order, name, is_closed, color in choices.TASK_STATUSES:
|
for order, name, is_closed, color in choices.TASK_STATUSES:
|
||||||
TaskStatus.objects.create(name=name, order=order, color=color,
|
TaskStatus.objects.create(name=name, order=order, color=color,
|
||||||
is_closed=is_closed, project=instance)
|
is_closed=is_closed, project=instance)
|
||||||
|
|
||||||
# Issues
|
# Issues
|
||||||
for order, name in choices.PRIORITY_CHOICES:
|
for order, name in choices.PRIORITY_CHOICES:
|
||||||
|
@ -465,7 +459,7 @@ def project_post_save(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
for order, name, is_closed in choices.ISSUE_STATUSES:
|
for order, name, is_closed in choices.ISSUE_STATUSES:
|
||||||
IssueStatus.objects.create(name=name, order=order,
|
IssueStatus.objects.create(name=name, order=order,
|
||||||
is_closed=is_closed, project=instance)
|
is_closed=is_closed, project=instance)
|
||||||
|
|
||||||
for order, name in choices.ISSUE_TYPES:
|
for order, name in choices.ISSUE_TYPES:
|
||||||
IssueType.objects.create(project=instance, name=name, order=order)
|
IssueType.objects.create(project=instance, name=name, order=order)
|
||||||
|
@ -473,4 +467,4 @@ def project_post_save(sender, instance, created, **kwargs):
|
||||||
# Questions
|
# Questions
|
||||||
for order, name, is_closed in choices.QUESTION_STATUS:
|
for order, name, is_closed in choices.QUESTION_STATUS:
|
||||||
QuestionStatus.objects.create(name=name, order=order,
|
QuestionStatus.objects.create(name=name, order=order,
|
||||||
is_closed=is_closed, project=instance)
|
is_closed=is_closed, project=instance)
|
||||||
|
|
Loading…
Reference in New Issue