Move tasks to another sprint

stable
Álex Hermida 2018-12-28 12:44:12 +01:00 committed by Alex Hermida
parent fe4cddac30
commit a17ed83755
5 changed files with 142 additions and 7 deletions

View File

@ -31,6 +31,8 @@ from taiga.projects.models import Project
from taiga.projects.notifications.mixins import WatchedResourceMixin from taiga.projects.notifications.mixins import WatchedResourceMixin
from taiga.projects.notifications.mixins import WatchersViewSetMixin from taiga.projects.notifications.mixins import WatchersViewSetMixin
from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.history.mixins import HistoryResourceMixin
from taiga.projects.tasks.validators import UpdateMilestoneBulkValidator as \
TasksUpdateMilestoneValidator
from . import serializers from . import serializers
from . import services from . import services
@ -148,7 +150,7 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin,
def move_userstories_to_sprint(self, request, pk=None, **kwargs): def move_userstories_to_sprint(self, request, pk=None, **kwargs):
milestone = get_object_or_404(models.Milestone, pk=pk) milestone = get_object_or_404(models.Milestone, pk=pk)
self.check_permissions(request, "bulk_update_items", milestone) self.check_permissions(request, "move_related_items", milestone)
validator = validators.UpdateMilestoneBulkValidator(data=request.DATA) validator = validators.UpdateMilestoneBulkValidator(data=request.DATA)
if not validator.is_valid(): if not validator.is_valid():
@ -159,12 +161,33 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin,
milestone_result = get_object_or_404(models.Milestone, pk=data["milestone_id"]) milestone_result = get_object_or_404(models.Milestone, pk=data["milestone_id"])
if data["bulk_stories"]: if data["bulk_stories"]:
permissions = self.check_permissions(request, "move_uss_to_sprint", project) self.check_permissions(request, "move_uss_to_sprint", project)
services.update_userstories_milestone_in_bulk(data["bulk_stories"], milestone_result) services.update_userstories_milestone_in_bulk(data["bulk_stories"], milestone_result)
services.snapshot_userstories_in_bulk(data["bulk_stories"], request.user) services.snapshot_userstories_in_bulk(data["bulk_stories"], request.user)
return response.NoContent() return response.NoContent()
@detail_route(methods=["POST"])
def move_tasks_to_sprint(self, request, pk=None, **kwargs):
milestone = get_object_or_404(models.Milestone, pk=pk)
self.check_permissions(request, "move_related_items", milestone)
validator = TasksUpdateMilestoneValidator(data=request.DATA)
if not validator.is_valid():
return response.BadRequest(validator.errors)
data = validator.data
project = get_object_or_404(Project, pk=data["project_id"])
milestone_result = get_object_or_404(models.Milestone, pk=data["milestone_id"])
if data["bulk_tasks"]:
self.check_permissions(request, "move_tasks_to_sprint", project)
services.update_tasks_milestone_in_bulk(data["bulk_tasks"], milestone_result)
services.snapshot_tasks_in_bulk(data["bulk_tasks"], request.user)
return response.NoContent()
class MilestoneWatchersViewSet(WatchersViewSetMixin, ModelListViewSet): class MilestoneWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
permission_classes = (permissions.MilestoneWatchersPermission,) permission_classes = (permissions.MilestoneWatchersPermission,)

View File

@ -33,8 +33,9 @@ class MilestonePermission(TaigaResourcePermission):
stats_perms = HasProjectPerm('view_milestones') stats_perms = HasProjectPerm('view_milestones')
watch_perms = IsAuthenticated() & HasProjectPerm('view_milestones') watch_perms = IsAuthenticated() & HasProjectPerm('view_milestones')
unwatch_perms = IsAuthenticated() & HasProjectPerm('view_milestones') unwatch_perms = IsAuthenticated() & HasProjectPerm('view_milestones')
bulk_update_items_perms = HasProjectPerm('modify_milestone') move_related_items_perms = HasProjectPerm('modify_milestone')
move_uss_to_sprint_perms = HasProjectPerm('modify_us') move_uss_to_sprint_perms = HasProjectPerm('modify_us')
move_tasks_to_sprint_perms = HasProjectPerm('modify_task')
class MilestoneWatchersPermission(TaigaResourcePermission): class MilestoneWatchersPermission(TaigaResourcePermission):

View File

@ -94,3 +94,51 @@ def snapshot_userstories_in_bulk(bulk_data, user):
take_snapshot(us, user=user) take_snapshot(us, user=user)
except UserStory.DoesNotExist: except UserStory.DoesNotExist:
pass pass
def update_tasks_milestone_in_bulk(bulk_data: list, milestone: object):
"""
Update the milestone and the milestone order of some tasks adding
the extra orders needed to keep consistency.
`bulk_data` should be a list of dicts with the following format:
[{'task_id': <value>, 'order': <value>}, ...]
"""
tasks = milestone.tasks.all()
task_orders = {task.id: getattr(task, "taskboard_order") for task in tasks}
new_task_orders = {}
for e in bulk_data:
new_task_orders[e["task_id"]] = e["order"]
# The base orders where we apply the new orders must containg all
# the values
task_orders[e["task_id"]] = e["order"]
apply_order_updates(task_orders, new_task_orders)
task_milestones = {e["task_id"]: milestone.id for e in bulk_data}
task_ids = task_milestones.keys()
events.emit_event_for_ids(ids=task_ids,
content_type="tasks.task",
projectid=milestone.project.pk)
task_instance_list = []
task_values = []
for task_id in task_ids:
task = Task.objects.get(pk=task_id)
task_instance_list.append(task)
task_values.append({'milestone_id': milestone.id})
db.update_in_bulk(task_instance_list, task_values)
db.update_attr_in_bulk_for_ids(task_orders, "taskboard_order", Task)
return task_milestones
def snapshot_tasks_in_bulk(bulk_data, user):
for task_data in bulk_data:
try:
task = Task.objects.get(pk=task_data['task_id'])
take_snapshot(task, user=user)
except Task.DoesNotExist:
pass

View File

@ -31,9 +31,9 @@ def cached_prev_task(sender, instance, **kwargs):
instance.prev = sender.objects.get(id=instance.id) instance.prev = sender.objects.get(id=instance.id)
#################################### ######################################
# Signals for close US and Milestone # Signals for close Task and Milestone
#################################### ######################################
def try_to_close_or_open_us_and_milestone_when_create_or_edit_task(sender, instance, created, **kwargs): def try_to_close_or_open_us_and_milestone_when_create_or_edit_task(sender, instance, created, **kwargs):
_try_to_close_or_open_us_when_create_or_edit_task(instance) _try_to_close_or_open_us_when_create_or_edit_task(instance)

View File

@ -26,7 +26,6 @@ from urllib.parse import quote
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.userstories.serializers import UserStorySerializer
from .. import factories as f from .. import factories as f
@ -270,3 +269,67 @@ def test_api_move_userstories_to_another_sprint_close_previous(client):
assert project.milestones.get(id=milestone1.id).user_stories.count() == 1 assert project.milestones.get(id=milestone1.id).user_stories.count() == 1
assert project.milestones.get(id=milestone2.id).user_stories.count() == 1 assert project.milestones.get(id=milestone2.id).user_stories.count() == 1
assert project.milestones.get(id=milestone1.id).closed assert project.milestones.get(id=milestone1.id).closed
def test_api_move_tasks_to_another_sprint(client):
project = f.create_project()
f.MembershipFactory.create(project=project, user=project.owner,
is_admin=True)
milestone1 = f.MilestoneFactory.create(project=project)
milestone2 = f.MilestoneFactory.create(project=project)
task1 = f.create_task(project=project, milestone=milestone1, taskboard_order=1)
task2 = f.create_task(project=project, milestone=milestone1, taskboard_order=2)
assert project.milestones.get(id=milestone1.id).tasks.count() == 2
url = reverse("milestones-move-tasks-to-sprint", kwargs={"pk": milestone1.pk})
data = {
"project_id": project.id,
"milestone_id": milestone2.id,
"bulk_tasks": [{"task_id": task2.id, "order": 2}]
}
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 204, response.data
assert project.milestones.get(id=milestone1.id).tasks.count() == 1
assert project.milestones.get(id=milestone2.id).tasks.count() == 1
def test_api_move_tasks_to_another_sprint_close_previous(client):
project = f.create_project()
f.MembershipFactory.create(project=project, user=project.owner,
is_admin=True)
milestone1 = f.MilestoneFactory.create(project=project)
milestone2 = f.MilestoneFactory.create(project=project)
closed_status = f.TaskStatusFactory.create(project=project, is_closed=True)
task1 = f.create_task(project=project, milestone=milestone1, taskboard_order=1,
status=closed_status)
task2 = f.create_task(project=project, milestone=milestone1, taskboard_order=2)
milestone = project.milestones.get(id=milestone1.id)
manager_tasks = milestone.tasks
# all_tasks = list(manager_tasks.all())
count_result = manager_tasks.count()
assert count_result == 2
assert not milestone1.closed
url = reverse("milestones-move-tasks-to-sprint", kwargs={"pk": milestone1.pk})
data = {
"project_id": project.id,
"milestone_id": milestone2.id,
"bulk_tasks": [{"task_id": task2.id, "order": 2}]
}
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 204, response.data
assert project.milestones.get(id=milestone1.id).tasks.count() == 1
assert project.milestones.get(id=milestone2.id).tasks.count() == 1
assert project.milestones.get(id=milestone1.id).closed