diff --git a/greenmine/projects/milestones/api.py b/greenmine/projects/milestones/api.py index bbb7a2ec..829b9345 100644 --- a/greenmine/projects/milestones/api.py +++ b/greenmine/projects/milestones/api.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- from django.utils.translation import ugettext_lazy as _ +from django.shortcuts import get_object_or_404 from rest_framework.permissions import IsAuthenticated +from rest_framework.decorators import detail_route +from rest_framework.response import Response from greenmine.base import filters from greenmine.base import exceptions as exc @@ -13,6 +16,8 @@ from . import serializers from . import models from . import permissions +import datetime + class MilestoneViewSet(NotificationSenderMixin, ModelCrudViewSet): model= models.Milestone @@ -37,3 +42,33 @@ class MilestoneViewSet(NotificationSenderMixin, ModelCrudViewSet): super().pre_save(obj) + @detail_route(methods=['get']) + def stats(self, request, pk=None): + milestone = get_object_or_404(models.Milestone, pk=pk) + total_points = milestone.total_points + milestone_stats = { + 'name': milestone.name, + 'estimated_start': milestone.estimated_start, + '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': sum([us.tasks.count() for us in milestone.user_stories.all()]), + 'completed_tasks': sum([us.tasks.filter(status__is_closed=True).count() for us in milestone.user_stories.all()]), + 'days': [] + } + current_date = milestone.estimated_start + optimal_points = sum(total_points.values()) + optimal_points_per_day = sum(total_points.values()) / (milestone.estimated_finish - milestone.estimated_start).days + while current_date <= milestone.estimated_finish: + milestone_stats['days'].append({ + 'day': current_date, + 'name': current_date.day, + 'open_points': sum(total_points.values()) - sum(milestone.closed_points_by_date(current_date).values()), + 'optimal_points': optimal_points, + }) + current_date = current_date + datetime.timedelta(days=1) + optimal_points -= optimal_points_per_day + + return Response(milestone_stats) diff --git a/greenmine/projects/milestones/models.py b/greenmine/projects/milestones/models.py index 31ae76db..54eb1e12 100644 --- a/greenmine/projects/milestones/models.py +++ b/greenmine/projects/milestones/models.py @@ -91,6 +91,10 @@ class Milestone(WatchedMixin): dict_result[key] = value return dict_result + @property + def total_points(self): + return self._get_user_stories_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() @@ -147,6 +151,10 @@ class Milestone(WatchedMixin): "project_owner": (self.project, self.project.owner), } + def closed_points_by_date(self, date): + return self._get_user_stories_points([us for us in self.user_stories.filter(finish_date__lte=date) + if us.is_closed]) + # Reversion registration (usufull for base.notification and for meke a historical) reversion.register(Milestone)