Working sprint graph
parent
53ae1d3b1b
commit
e1454106af
|
@ -1,8 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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.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 filters
|
||||||
from greenmine.base import exceptions as exc
|
from greenmine.base import exceptions as exc
|
||||||
|
@ -13,6 +16,8 @@ from . import serializers
|
||||||
from . import models
|
from . import models
|
||||||
from . import permissions
|
from . import permissions
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class MilestoneViewSet(NotificationSenderMixin, ModelCrudViewSet):
|
class MilestoneViewSet(NotificationSenderMixin, ModelCrudViewSet):
|
||||||
model= models.Milestone
|
model= models.Milestone
|
||||||
|
@ -37,3 +42,33 @@ class MilestoneViewSet(NotificationSenderMixin, ModelCrudViewSet):
|
||||||
|
|
||||||
super().pre_save(obj)
|
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)
|
||||||
|
|
|
@ -91,6 +91,10 @@ class Milestone(WatchedMixin):
|
||||||
dict_result[key] = value
|
dict_result[key] = value
|
||||||
return dict_result
|
return dict_result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total_points(self):
|
||||||
|
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([us for us in self.user_stories.all()
|
||||||
|
@ -147,6 +151,10 @@ class Milestone(WatchedMixin):
|
||||||
"project_owner": (self.project, self.project.owner),
|
"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 registration (usufull for base.notification and for meke a historical)
|
||||||
reversion.register(Milestone)
|
reversion.register(Milestone)
|
||||||
|
|
Loading…
Reference in New Issue