Improving milestone stats API
parent
b142cafc14
commit
a4172012ba
|
@ -113,10 +113,10 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView
|
|||
'estimated_finish': milestone.estimated_finish,
|
||||
'total_points': total_points,
|
||||
'completed_points': milestone.closed_points.values(),
|
||||
'total_userstories': milestone.user_stories.count(),
|
||||
'completed_userstories': len([us for us in milestone.user_stories.all() if us.is_closed]),
|
||||
'total_tasks': milestone.tasks.all().count(),
|
||||
'completed_tasks': milestone.tasks.all().filter(status__is_closed=True).count(),
|
||||
'total_userstories': milestone.get_cached_user_stories().count(),
|
||||
'completed_userstories': milestone.get_cached_user_stories().filter(is_closed=True).count(),
|
||||
'total_tasks': milestone.tasks.count(),
|
||||
'completed_tasks': milestone.tasks.filter(status__is_closed=True).count(),
|
||||
'iocaine_doses': milestone.tasks.filter(is_iocaine=True).count(),
|
||||
'days': []
|
||||
}
|
||||
|
@ -125,11 +125,12 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView
|
|||
optimal_points = sumTotalPoints
|
||||
milestone_days = (milestone.estimated_finish - milestone.estimated_start).days
|
||||
optimal_points_per_day = sumTotalPoints / milestone_days if milestone_days else 0
|
||||
|
||||
while current_date <= milestone.estimated_finish:
|
||||
milestone_stats['days'].append({
|
||||
'day': current_date,
|
||||
'name': current_date.day,
|
||||
'open_points': sumTotalPoints - sum(milestone.closed_points_by_date(current_date).values()),
|
||||
'open_points': sumTotalPoints - milestone.total_closed_points_by_date(current_date),
|
||||
'optimal_points': optimal_points,
|
||||
})
|
||||
current_date = current_date + datetime.timedelta(days=1)
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django.db.models import Prefetch, Count
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
|
@ -53,6 +55,8 @@ class Milestone(WatchedModelMixin, models.Model):
|
|||
order = models.PositiveSmallIntegerField(default=1, null=False, blank=False,
|
||||
verbose_name=_("order"))
|
||||
_importing = None
|
||||
_total_closed_points_by_date = None
|
||||
_cached_user_stories = None
|
||||
|
||||
class Meta:
|
||||
verbose_name = "milestone"
|
||||
|
@ -82,6 +86,14 @@ class Milestone(WatchedModelMixin, models.Model):
|
|||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def get_cached_user_stories(self):
|
||||
if self._cached_user_stories is None:
|
||||
self._cached_user_stories = self.user_stories.\
|
||||
prefetch_related("role_points", "role_points__points").\
|
||||
annotate(num_tasks=Count("tasks"))
|
||||
|
||||
return self._cached_user_stories
|
||||
|
||||
def _get_user_stories_points(self, user_stories):
|
||||
role_points = [us.role_points.all() for us in user_stories]
|
||||
flat_role_points = itertools.chain(*role_points)
|
||||
|
@ -91,13 +103,13 @@ class Milestone(WatchedModelMixin, models.Model):
|
|||
@property
|
||||
def total_points(self):
|
||||
return self._get_user_stories_points(
|
||||
[us for us in self.user_stories.all()]
|
||||
[us for us in self.get_cached_user_stories()]
|
||||
)
|
||||
|
||||
@property
|
||||
def closed_points(self):
|
||||
return self._get_user_stories_points(
|
||||
[us for us in self.user_stories.all() if us.is_closed]
|
||||
[us for us in self.get_cached_user_stories() if us.is_closed]
|
||||
)
|
||||
|
||||
def _get_increment_points(self):
|
||||
|
@ -148,9 +160,51 @@ class Milestone(WatchedModelMixin, models.Model):
|
|||
def shared_increment_points(self):
|
||||
return self._get_increment_points()["shared_increment"]
|
||||
|
||||
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)
|
||||
).prefetch_related('role_points', 'role_points__points') if us.is_closed
|
||||
])
|
||||
def total_closed_points_by_date(self, date):
|
||||
# Milestone instance will keep a cache of the total closed points by date
|
||||
if self._total_closed_points_by_date is None:
|
||||
self._total_closed_points_by_date = {}
|
||||
|
||||
# We need to keep the milestone user stories indexed by id in a dict
|
||||
user_stories = {}
|
||||
for us in self.get_cached_user_stories():
|
||||
us._total_us_points = sum(self._get_user_stories_points([us]).values())
|
||||
user_stories[us.id] = us
|
||||
|
||||
tasks = self.tasks.\
|
||||
select_related("user_story").\
|
||||
exclude(finished_date__isnull=True).\
|
||||
exclude(user_story__isnull=True)
|
||||
|
||||
# For each finished task we try to know the proporional part of points
|
||||
# it represetnts from the user story and add it to the closed points
|
||||
# for that date
|
||||
# This calulation is the total user story points divided by its number of tasks
|
||||
for task in tasks:
|
||||
user_story = user_stories[task.user_story.id]
|
||||
total_us_points = user_story._total_us_points
|
||||
us_tasks_counter = user_story.num_tasks
|
||||
|
||||
# If the task was finished before starting the sprint it needs
|
||||
# to be included
|
||||
finished_date = task.finished_date.date()
|
||||
if finished_date < self.estimated_start:
|
||||
finished_date = self.estimated_start
|
||||
|
||||
points_by_date = self._total_closed_points_by_date.get(finished_date, 0)
|
||||
points_by_date += total_us_points / us_tasks_counter
|
||||
self._total_closed_points_by_date[finished_date] = points_by_date
|
||||
|
||||
# At this point self._total_closed_points_by_date keeps a dict where the
|
||||
# finished date of the task is the key and the value is the increment of points
|
||||
# We are transforming this dict of increments in an acumulation one including
|
||||
# all the dates from the sprint
|
||||
|
||||
acumulated_date_points = 0
|
||||
current_date = self.estimated_start
|
||||
while current_date <= self.estimated_finish:
|
||||
acumulated_date_points += self._total_closed_points_by_date.get(current_date, 0)
|
||||
self._total_closed_points_by_date[current_date] = acumulated_date_points
|
||||
current_date = current_date + datetime.timedelta(days=1)
|
||||
|
||||
return self._total_closed_points_by_date.get(date, 0)
|
||||
|
|
Loading…
Reference in New Issue