Adding member stats to the API
parent
fa1e0c6bf6
commit
e7b963a674
|
@ -80,6 +80,12 @@ class ProjectViewSet(ModelCrudViewSet):
|
||||||
self.check_permissions(request, 'stats', project)
|
self.check_permissions(request, 'stats', project)
|
||||||
return Response(services.get_stats_for_project(project))
|
return Response(services.get_stats_for_project(project))
|
||||||
|
|
||||||
|
@detail_route(methods=['get'])
|
||||||
|
def member_stats(self, request, pk=None):
|
||||||
|
project = self.get_object()
|
||||||
|
self.check_permissions(request, 'member_stats', project)
|
||||||
|
return Response(services.get_member_stats_for_project(project))
|
||||||
|
|
||||||
@detail_route(methods=['get'])
|
@detail_route(methods=['get'])
|
||||||
def issues_stats(self, request, pk=None):
|
def issues_stats(self, request, pk=None):
|
||||||
project = self.get_object()
|
project = self.get_object()
|
||||||
|
|
|
@ -28,6 +28,7 @@ class ProjectPermission(TaigaResourcePermission):
|
||||||
modules_perms = IsProjectOwner()
|
modules_perms = IsProjectOwner()
|
||||||
list_perms = AllowAny()
|
list_perms = AllowAny()
|
||||||
stats_perms = AllowAny()
|
stats_perms = AllowAny()
|
||||||
|
member_stats_perms = HasProjectPerm('view_project')
|
||||||
star_perms = IsAuthenticated()
|
star_perms = IsAuthenticated()
|
||||||
unstar_perms = IsAuthenticated()
|
unstar_perms = IsAuthenticated()
|
||||||
issues_stats_perms = AllowAny()
|
issues_stats_perms = AllowAny()
|
||||||
|
|
|
@ -30,6 +30,7 @@ from .filters import get_issues_filters_data
|
||||||
|
|
||||||
from .stats import get_stats_for_project_issues
|
from .stats import get_stats_for_project_issues
|
||||||
from .stats import get_stats_for_project
|
from .stats import get_stats_for_project
|
||||||
|
from .stats import get_member_stats_for_project
|
||||||
|
|
||||||
from .members import create_members_in_bulk
|
from .members import create_members_in_bulk
|
||||||
from .members import get_members_from_bulk
|
from .members import get_members_from_bulk
|
||||||
|
|
|
@ -18,6 +18,8 @@ from django.db.models import Q, Count
|
||||||
import datetime
|
import datetime
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
|
from taiga.projects.history.models import HistoryEntry
|
||||||
|
|
||||||
|
|
||||||
def _get_milestones_stats_for_backlog(project):
|
def _get_milestones_stats_for_backlog(project):
|
||||||
"""
|
"""
|
||||||
|
@ -212,3 +214,77 @@ def get_stats_for_project(project):
|
||||||
'speed': speed,
|
'speed': speed,
|
||||||
}
|
}
|
||||||
return project_stats
|
return project_stats
|
||||||
|
|
||||||
|
|
||||||
|
def _get_closed_bugs_per_member_stats(project):
|
||||||
|
# Closed bugs per user
|
||||||
|
closed_bugs = project.issues.filter(status__is_closed=True)\
|
||||||
|
.values('assigned_to')\
|
||||||
|
.annotate(count=Count('assigned_to'))\
|
||||||
|
.order_by()
|
||||||
|
closed_bugs = { p["assigned_to"]: p["count"] for p in closed_bugs}
|
||||||
|
return closed_bugs
|
||||||
|
|
||||||
|
|
||||||
|
def _get_iocaine_tasks_per_member_stats(project):
|
||||||
|
# Iocaine tasks assigned per user
|
||||||
|
iocaine_tasks = project.tasks.filter(is_iocaine=True)\
|
||||||
|
.values('assigned_to')\
|
||||||
|
.annotate(count=Count('assigned_to'))\
|
||||||
|
.order_by()
|
||||||
|
iocaine_tasks = { t["assigned_to"]: t["count"] for t in iocaine_tasks}
|
||||||
|
return iocaine_tasks
|
||||||
|
|
||||||
|
|
||||||
|
def _get_wiki_changes_per_member_stats(project):
|
||||||
|
# Wiki changes
|
||||||
|
wiki_changes = {}
|
||||||
|
wiki_page_keys = ["wiki.wikipage:%s"%id for id in project.wiki_pages.values_list("id", flat=True)]
|
||||||
|
history_entries = HistoryEntry.objects.filter(key__in=wiki_page_keys).values('user')
|
||||||
|
for entry in history_entries:
|
||||||
|
editions = wiki_changes.get(entry["user"]["pk"], 0)
|
||||||
|
wiki_changes[entry["user"]["pk"]] = editions + 1
|
||||||
|
|
||||||
|
return wiki_changes
|
||||||
|
|
||||||
|
|
||||||
|
def _get_created_bugs_per_member_stats(project):
|
||||||
|
# Created_bugs
|
||||||
|
created_bugs = project.issues\
|
||||||
|
.values('owner')\
|
||||||
|
.annotate(count=Count('owner'))\
|
||||||
|
.order_by()
|
||||||
|
created_bugs = { p["owner"]: p["count"] for p in created_bugs }
|
||||||
|
return created_bugs
|
||||||
|
|
||||||
|
|
||||||
|
def _get_closed_tasks_per_member_stats(project):
|
||||||
|
# Closed tasks
|
||||||
|
closed_tasks = project.tasks.filter(status__is_closed=True)\
|
||||||
|
.values('assigned_to')\
|
||||||
|
.annotate(count=Count('assigned_to'))\
|
||||||
|
.order_by()
|
||||||
|
closed_tasks = {p["assigned_to"]: p["count"] for p in closed_tasks}
|
||||||
|
return closed_tasks
|
||||||
|
|
||||||
|
def get_member_stats_for_project(project):
|
||||||
|
base_counters = {id: 0 for id in project.members.values_list("id", flat=True)}
|
||||||
|
closed_bugs = base_counters.copy()
|
||||||
|
closed_bugs.update(_get_closed_bugs_per_member_stats(project))
|
||||||
|
iocaine_tasks = base_counters.copy()
|
||||||
|
iocaine_tasks.update(_get_iocaine_tasks_per_member_stats(project))
|
||||||
|
wiki_changes = base_counters.copy()
|
||||||
|
wiki_changes.update(_get_wiki_changes_per_member_stats(project))
|
||||||
|
created_bugs = base_counters.copy()
|
||||||
|
created_bugs.update(_get_created_bugs_per_member_stats(project))
|
||||||
|
closed_tasks = base_counters.copy()
|
||||||
|
closed_tasks.update(_get_closed_tasks_per_member_stats(project))
|
||||||
|
|
||||||
|
member_stats = {
|
||||||
|
"closed_bugs": closed_bugs,
|
||||||
|
"iocaine_tasks": iocaine_tasks,
|
||||||
|
"wiki_changes": wiki_changes,
|
||||||
|
"created_bugs": created_bugs,
|
||||||
|
"closed_tasks": closed_tasks,
|
||||||
|
}
|
||||||
|
return member_stats
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from taiga.base.utils import json
|
from taiga.base.utils import json
|
||||||
|
from taiga.projects.services import stats as stats_services
|
||||||
|
from taiga.projects.history.services import take_snapshot
|
||||||
|
|
||||||
from .. import factories as f
|
from .. import factories as f
|
||||||
|
|
||||||
|
@ -84,6 +86,7 @@ def test_issue_status_slug_generation(client):
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.data["slug"] == "new-status"
|
assert response.data["slug"] == "new-status"
|
||||||
|
|
||||||
|
|
||||||
def test_points_name_duplicated(client):
|
def test_points_name_duplicated(client):
|
||||||
point_1 = f.PointsFactory()
|
point_1 = f.PointsFactory()
|
||||||
point_2 = f.PointsFactory(project=point_1.project)
|
point_2 = f.PointsFactory(project=point_1.project)
|
||||||
|
@ -96,9 +99,64 @@ def test_points_name_duplicated(client):
|
||||||
assert response.status_code == 400
|
assert response.status_code == 400
|
||||||
assert response.data["name"][0] == "Name duplicated for the project"
|
assert response.data["name"][0] == "Name duplicated for the project"
|
||||||
|
|
||||||
|
|
||||||
def test_update_points_when_not_null_values_for_points(client):
|
def test_update_points_when_not_null_values_for_points(client):
|
||||||
points = f.PointsFactory(name="?", value="6")
|
points = f.PointsFactory(name="?", value="6")
|
||||||
role = f.RoleFactory(project=points.project, computable=True)
|
role = f.RoleFactory(project=points.project, computable=True)
|
||||||
assert points.project.points.filter(value__isnull=True).count() == 0
|
assert points.project.points.filter(value__isnull=True).count() == 0
|
||||||
points.project.update_role_points()
|
points.project.update_role_points()
|
||||||
assert points.project.points.filter(value__isnull=True).count() == 1
|
assert points.project.points.filter(value__isnull=True).count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_closed_bugs_per_member_stats():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
membership_1 = f.MembershipFactory(project=project)
|
||||||
|
membership_2 = f.MembershipFactory(project=project)
|
||||||
|
issue_closed_status = f.IssueStatusFactory(is_closed=True, project=project)
|
||||||
|
issue_open_status = f.IssueStatusFactory(is_closed=False, project=project)
|
||||||
|
issue_closed = f.IssueFactory(project=project,
|
||||||
|
status=issue_closed_status,
|
||||||
|
owner=membership_1.user,
|
||||||
|
assigned_to=membership_1.user)
|
||||||
|
issue_open = f.IssueFactory(project=project,
|
||||||
|
status=issue_open_status,
|
||||||
|
owner=membership_2.user,
|
||||||
|
assigned_to=membership_2.user)
|
||||||
|
task_closed_status = f.TaskStatusFactory(is_closed=True, project=project)
|
||||||
|
task_open_status = f.TaskStatusFactory(is_closed=False, project=project)
|
||||||
|
task_closed = f.TaskFactory(project=project,
|
||||||
|
status=task_closed_status,
|
||||||
|
owner=membership_1.user,
|
||||||
|
assigned_to=membership_1.user)
|
||||||
|
task_open = f.TaskFactory(project=project,
|
||||||
|
status=task_open_status,
|
||||||
|
owner=membership_2.user,
|
||||||
|
assigned_to=membership_2.user)
|
||||||
|
task_iocaine = f.TaskFactory(project=project,
|
||||||
|
status=task_open_status,
|
||||||
|
owner=membership_2.user,
|
||||||
|
assigned_to=membership_2.user,
|
||||||
|
is_iocaine=True)
|
||||||
|
|
||||||
|
wiki_page = f.WikiPageFactory.create(project=project, owner=membership_1.user)
|
||||||
|
take_snapshot(wiki_page, user=membership_1.user)
|
||||||
|
wiki_page.content="Frontend, future"
|
||||||
|
wiki_page.save()
|
||||||
|
take_snapshot(wiki_page, user=membership_1.user)
|
||||||
|
|
||||||
|
stats = stats_services.get_member_stats_for_project(project)
|
||||||
|
|
||||||
|
assert stats["closed_bugs"][membership_1.user.id] == 1
|
||||||
|
assert stats["closed_bugs"][membership_2.user.id] == 0
|
||||||
|
|
||||||
|
assert stats["iocaine_tasks"][membership_1.user.id] == 0
|
||||||
|
assert stats["iocaine_tasks"][membership_2.user.id] == 1
|
||||||
|
|
||||||
|
assert stats["wiki_changes"][membership_1.user.id] == 2
|
||||||
|
assert stats["wiki_changes"][membership_2.user.id] == 0
|
||||||
|
|
||||||
|
assert stats["created_bugs"][membership_1.user.id] == 1
|
||||||
|
assert stats["created_bugs"][membership_2.user.id] == 1
|
||||||
|
|
||||||
|
assert stats["closed_tasks"][membership_1.user.id] == 1
|
||||||
|
assert stats["closed_tasks"][membership_2.user.id] == 0
|
||||||
|
|
Loading…
Reference in New Issue