From ca09cda87cb3af787b44d4b94390025617df40e4 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 22 Dec 2014 10:02:31 +0100 Subject: [PATCH] Some extra api optimizations --- taiga/projects/milestones/api.py | 7 +++- taiga/projects/milestones/models.py | 48 ++++++++++++++++++---------- taiga/projects/models.py | 30 ++++++++++------- taiga/projects/services/stats.py | 34 ++++++++++++++------ taiga/projects/userstories/api.py | 6 ++-- taiga/projects/userstories/models.py | 3 +- 6 files changed, 85 insertions(+), 43 deletions(-) diff --git a/taiga/projects/milestones/api.py b/taiga/projects/milestones/api.py index ea2ef418..d669a5bf 100644 --- a/taiga/projects/milestones/api.py +++ b/taiga/projects/milestones/api.py @@ -47,7 +47,12 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView qs = qs.prefetch_related("user_stories", "user_stories__role_points", "user_stories__role_points__points", - "user_stories__role_points__role") + "user_stories__role_points__role", + "user_stories__generated_from_issue", + "user_stories__project", + "watchers", + "user_stories__watchers") + qs = qs.select_related("project") qs = qs.order_by("-estimated_start") return qs diff --git a/taiga/projects/milestones/models.py b/taiga/projects/milestones/models.py index 2e4e9199..22a9b179 100644 --- a/taiga/projects/milestones/models.py +++ b/taiga/projects/milestones/models.py @@ -91,46 +91,62 @@ class Milestone(WatchedModelMixin, models.Model): @property def total_points(self): return self._get_user_stories_points( - [us for us in self.user_stories.all().prefetch_related('role_points', 'role_points__points')] + [us for us in self.user_stories.all()] ) @property def closed_points(self): return self._get_user_stories_points( - [us for us in self.user_stories.all().prefetch_related('role_points', 'role_points__points') if us.is_closed] + [us for us in self.user_stories.all() if us.is_closed] ) - def _get_points_increment(self, client_requirement, team_requirement): + def _get_increment_points(self): + if hasattr(self, "_increments"): + return self._increments + + self._increments = { + "client_increment": {}, + "team_increment": {}, + "shared_increment": {}, + } user_stories = UserStory.objects.none() if self.estimated_start and self.estimated_finish: - user_stories = UserStory.objects.filter( - created_date__gte=self.estimated_start, - created_date__lt=self.estimated_finish, - project_id=self.project_id, - client_requirement=client_requirement, - team_requirement=team_requirement - ).prefetch_related('role_points', 'role_points__points') - return self._get_user_stories_points(user_stories) + user_stories = filter( + lambda x: x.created_date.date() >= self.estimated_start and x.created_date.date() < self.estimated_finish, + self.project.user_stories.all() + ) + self._increments['client_increment'] = self._get_user_stories_points( + [us for us in user_stories if us.client_requirement is True and us.team_requirement is False] + ) + self._increments['team_increment'] = self._get_user_stories_points( + [us for us in user_stories if us.client_requirement is False and us.team_requirement is True] + ) + self._increments['shared_increment'] = self._get_user_stories_points( + [us for us in user_stories if us.client_requirement is True and us.team_requirement is True] + ) + return self._increments + @property def client_increment_points(self): - client_increment = self._get_points_increment(True, False) + self._get_increment_points() + client_increment = self._get_increment_points()["client_increment"] shared_increment = { - key: value/2 for key, value in self.shared_increment_points.items() + key: value/2 for key, value in self._get_increment_points()["shared_increment"].items() } return dict_sum(client_increment, shared_increment) @property def team_increment_points(self): - team_increment = self._get_points_increment(False, True) + team_increment = self._get_increment_points()["team_increment"] shared_increment = { - key: value/2 for key, value in self.shared_increment_points.items() + key: value/2 for key, value in self._get_increment_points()["shared_increment"].items() } return dict_sum(team_increment, shared_increment) @property def shared_increment_points(self): - return self._get_points_increment(True, True) + return self._get_increment_points()["shared_increment"] def closed_points_by_date(self, date): return self._get_user_stories_points([ diff --git a/taiga/projects/models.py b/taiga/projects/models.py index a90a70fe..7912ceab 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -254,23 +254,20 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): return dict_sum(*flat_role_dicts) def _get_points_increment(self, client_requirement, team_requirement): - userstory_model = apps.get_model("userstories", "UserStory") - user_stories = userstory_model.objects.none() last_milestones = self.milestones.order_by('-estimated_finish') last_milestone = last_milestones[0] if last_milestones else None if last_milestone: - user_stories = userstory_model.objects.filter( + user_stories = self.user_stories.filter( created_date__gte=last_milestone.estimated_finish, - project_id=self.id, client_requirement=client_requirement, team_requirement=team_requirement - ).prefetch_related('role_points', 'role_points__points') + ) else: - user_stories = userstory_model.objects.filter( - project_id=self.id, + user_stories = self.user_stories.filter( client_requirement=client_requirement, team_requirement=team_requirement - ).prefetch_related('role_points', 'role_points__points') + ) + user_stories = user_stories.prefetch_related('role_points', 'role_points__points') return self._get_user_stories_points(user_stories) @property @@ -291,15 +288,26 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): @property def closed_points(self): - return self._get_user_stories_points(self.user_stories.filter(is_closed=True).prefetch_related('role_points', 'role_points__points')) + return self.calculated_points["closed"] @property def defined_points(self): - return self._get_user_stories_points(self.user_stories.all().prefetch_related('role_points', 'role_points__points')) + return self.calculated_points["defined"] @property def assigned_points(self): - return self._get_user_stories_points(self.user_stories.filter(milestone__isnull=False).prefetch_related('role_points', 'role_points__points')) + return self.calculated_points["assigned"] + + @property + def calculated_points(self): + user_stories = self.user_stories.all().prefetch_related('role_points', 'role_points__points') + closed_user_stories = user_stories.filter(is_closed=True) + assigned_user_stories = user_stories.filter(milestone__isnull=False) + return { + "defined": self._get_user_stories_points(user_stories), + "closed": self._get_user_stories_points(closed_user_stories), + "assigned": self._get_user_stories_points(assigned_user_stories), + } class ProjectModulesConfig(models.Model): diff --git a/taiga/projects/services/stats.py b/taiga/projects/services/stats.py index a770f95b..c852459f 100644 --- a/taiga/projects/services/stats.py +++ b/taiga/projects/services/stats.py @@ -15,12 +15,12 @@ # along with this program. If not, see . from django.db.models import Q, Count +from django.apps import apps import datetime import copy from taiga.projects.history.models import HistoryEntry - def _get_milestones_stats_for_backlog(project): """ Get collection of stats for each millestone of project. @@ -37,20 +37,27 @@ def _get_milestones_stats_for_backlog(project): future_team_increment = sum(project.future_team_increment.values()) future_client_increment = sum(project.future_client_increment.values()) - milestones = project.milestones.order_by('estimated_start') + milestones = project.milestones.order_by('estimated_start').\ + prefetch_related("user_stories", + "user_stories__role_points", + "user_stories__role_points__points") + milestones = list(milestones) + milestones_count = len(milestones) optimal_points = 0 team_increment = 0 client_increment = 0 - for current_milestone in range(0, max(milestones.count(), project.total_milestones)): + + for current_milestone in range(0, max(milestones_count, project.total_milestones)): optimal_points = (project.total_story_points - (optimal_points_per_sprint * current_milestone)) evolution = (project.total_story_points - current_evolution if current_evolution is not None else None) - if current_milestone < milestones.count(): + if current_milestone < milestones_count: ml = milestones[current_milestone] + milestone_name = ml.name team_increment = current_team_increment client_increment = current_client_increment @@ -58,6 +65,7 @@ def _get_milestones_stats_for_backlog(project): current_evolution += sum(ml.closed_points.values()) current_team_increment += sum(ml.team_increment_points.values()) current_client_increment += sum(ml.client_increment_points.values()) + else: milestone_name = "Future sprint" team_increment = current_team_increment + future_team_increment, @@ -194,7 +202,13 @@ def get_stats_for_project_issues(project): def get_stats_for_project(project): - closed_points = sum(project.closed_points.values()) + project = apps.get_model("projects", "Project").objects.\ + prefetch_related("milestones", + "user_stories").\ + get(id=project.id) + + points = project.calculated_points + closed_points = sum(points["closed"].values()) closed_milestones = project.milestones.filter(closed=True).count() speed = 0 if closed_milestones != 0: @@ -205,11 +219,11 @@ def get_stats_for_project(project): 'total_milestones': project.total_milestones, 'total_points': project.total_story_points, 'closed_points': closed_points, - 'closed_points_per_role': project.closed_points, - 'defined_points': sum(project.defined_points.values()), - 'defined_points_per_role': project.defined_points, - 'assigned_points': sum(project.assigned_points.values()), - 'assigned_points_per_role': project.assigned_points, + 'closed_points_per_role': points["closed"], + 'defined_points': sum(points["defined"].values()), + 'defined_points_per_role': points["defined"], + 'assigned_points': sum(points["assigned"].values()), + 'assigned_points_per_role': points["assigned"], 'milestones': _get_milestones_stats_for_backlog(project), 'speed': speed, } diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 4ce739ec..e667355d 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -59,10 +59,10 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi def get_queryset(self): qs = self.model.objects.all() - qs = qs.prefetch_related("points", - "role_points", + qs = qs.prefetch_related("role_points", "role_points__points", - "role_points__role") + "role_points__role", + "watchers") qs = qs.select_related("milestone", "project") return qs diff --git a/taiga/projects/userstories/models.py b/taiga/projects/userstories/models.py index a9f64a68..52772ca6 100644 --- a/taiga/projects/userstories/models.py +++ b/taiga/projects/userstories/models.py @@ -124,8 +124,7 @@ class UserStory(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, mod return self.role_points def get_total_points(self): - not_null_role_points = self.role_points.select_related("points").\ - exclude(points__value__isnull=True) + not_null_role_points = [rp for rp in self.role_points.all() if rp.points.value is not None] #If we only have None values the sum should be None if not not_null_role_points: