Add bulk update order in user story and taskboard for tasks
parent
ad34a06481
commit
1101c5a719
|
@ -15,6 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from taiga.base import filters, response
|
||||
from taiga.base import exceptions as exc
|
||||
|
@ -79,3 +80,27 @@ class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin,
|
|||
return response.Ok(tasks_serialized.data)
|
||||
|
||||
return response.BadRequest(serializer.errors)
|
||||
|
||||
def _bulk_update_order(self, order_field, request, **kwargs):
|
||||
serializer = serializers.UpdateTasksOrderBulkSerializer(data=request.DATA)
|
||||
if not serializer.is_valid():
|
||||
return response.BadRequest(serializer.errors)
|
||||
|
||||
data = serializer.data
|
||||
project = get_object_or_404(Project, pk=data["project_id"])
|
||||
|
||||
self.check_permissions(request, "bulk_update_order", project)
|
||||
services.update_tasks_order_in_bulk(data["bulk_tasks"],
|
||||
project=project,
|
||||
field=order_field)
|
||||
services.snapshot_tasks_in_bulk(data["bulk_tasks"], request.user)
|
||||
|
||||
return response.NoContent()
|
||||
|
||||
@list_route(methods=["POST"])
|
||||
def bulk_update_taskboard_order(self, request, **kwargs):
|
||||
return self._bulk_update_order("taskboard_order", request, **kwargs)
|
||||
|
||||
@list_route(methods=["POST"])
|
||||
def bulk_update_us_order(self, request, **kwargs):
|
||||
return self._bulk_update_order("us_order", request, **kwargs)
|
||||
|
|
|
@ -27,3 +27,4 @@ class TaskPermission(TaigaResourcePermission):
|
|||
destroy_perms = HasProjectPerm('delete_task')
|
||||
list_perms = AllowAny()
|
||||
bulk_create_perms = HasProjectPerm('add_task')
|
||||
bulk_update_order_perms = HasProjectPerm('modify_task')
|
||||
|
|
|
@ -18,9 +18,9 @@ from rest_framework import serializers
|
|||
|
||||
from taiga.base.serializers import Serializer, PickleField, NeighborsSerializerMixin, PgArrayField
|
||||
from taiga.mdrender.service import render as mdrender
|
||||
from taiga.projects.validators import ProjectExistsValidator, TaskStatusExistsValidator
|
||||
from taiga.projects.validators import ProjectExistsValidator
|
||||
from taiga.projects.milestones.validators import SprintExistsValidator
|
||||
from taiga.projects.userstories.validators import UserStoryExistsValidator
|
||||
from taiga.projects.tasks.validators import TaskExistsValidator
|
||||
from taiga.projects.notifications.validators import WatchersValidator
|
||||
|
||||
from . import models
|
||||
|
@ -70,10 +70,21 @@ class NeighborTaskSerializer(serializers.ModelSerializer):
|
|||
depth = 0
|
||||
|
||||
|
||||
class TasksBulkSerializer(ProjectExistsValidator, SprintExistsValidator, TaskStatusExistsValidator,
|
||||
UserStoryExistsValidator, Serializer):
|
||||
class TasksBulkSerializer(ProjectExistsValidator, SprintExistsValidator,
|
||||
TaskExistsValidator, Serializer):
|
||||
project_id = serializers.IntegerField()
|
||||
sprint_id = serializers.IntegerField()
|
||||
status_id = serializers.IntegerField(required=False)
|
||||
us_id = serializers.IntegerField(required=False)
|
||||
bulk_tasks = serializers.CharField()
|
||||
|
||||
## Order bulk serializers
|
||||
|
||||
class _TaskOrderBulkSerializer(TaskExistsValidator, Serializer):
|
||||
task_id = serializers.IntegerField()
|
||||
order = serializers.IntegerField()
|
||||
|
||||
|
||||
class UpdateTasksOrderBulkSerializer(ProjectExistsValidator, Serializer):
|
||||
project_id = serializers.IntegerField()
|
||||
bulk_tasks = _TaskOrderBulkSerializer(many=True)
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from taiga.base.utils import db, text
|
||||
from taiga.projects.history.services import take_snapshot
|
||||
from taiga.events import events
|
||||
|
||||
from . import models
|
||||
|
||||
|
@ -43,3 +45,33 @@ def create_tasks_in_bulk(bulk_data, callback=None, precall=None, **additional_fi
|
|||
tasks = get_tasks_from_bulk(bulk_data, **additional_fields)
|
||||
db.save_in_bulk(tasks, callback, precall)
|
||||
return tasks
|
||||
|
||||
|
||||
def update_tasks_order_in_bulk(bulk_data:list, field:str, project:object):
|
||||
"""
|
||||
Update the order of some tasks.
|
||||
`bulk_data` should be a list of tuples with the following format:
|
||||
|
||||
[(<task id>, {<field>: <value>, ...}), ...]
|
||||
"""
|
||||
task_ids = []
|
||||
new_order_values = []
|
||||
for task_data in bulk_data:
|
||||
task_ids.append(task_data["task_id"])
|
||||
new_order_values.append({field: task_data["order"]})
|
||||
|
||||
events.emit_event_for_ids(ids=task_ids,
|
||||
content_type="tasks.task",
|
||||
projectid=project.pk)
|
||||
|
||||
db.update_in_bulk_with_ids(task_ids, new_order_values, model=models.Task)
|
||||
|
||||
|
||||
def snapshot_tasks_in_bulk(bulk_data, user):
|
||||
task_ids = []
|
||||
for task_data in bulk_data:
|
||||
try:
|
||||
task = models.Task.objects.get(pk=task_data['task_id'])
|
||||
take_snapshot(task, user=user)
|
||||
except models.UserStory.DoesNotExist:
|
||||
pass
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
class TaskExistsValidator:
|
||||
def validate_task_id(self, attrs, source):
|
||||
value = attrs[source]
|
||||
if not models.Task.objects.filter(pk=value).exists():
|
||||
msg = _("There's no task with that id")
|
||||
raise serializers.ValidationError(msg)
|
||||
return attrs
|
|
@ -110,8 +110,7 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi
|
|||
return response.Ok(user_stories_serialized.data)
|
||||
return response.BadRequest(serializer.errors)
|
||||
|
||||
@list_route(methods=["POST"])
|
||||
def bulk_update_backlog_order(self, request, **kwargs):
|
||||
def _bulk_update_order(self, order_field, request, **kwargs):
|
||||
serializer = serializers.UpdateUserStoriesOrderBulkSerializer(data=request.DATA)
|
||||
if not serializer.is_valid():
|
||||
return response.BadRequest(serializer.errors)
|
||||
|
@ -122,42 +121,22 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi
|
|||
self.check_permissions(request, "bulk_update_order", project)
|
||||
services.update_userstories_order_in_bulk(data["bulk_stories"],
|
||||
project=project,
|
||||
field="backlog_order")
|
||||
field=order_field)
|
||||
services.snapshot_userstories_in_bulk(data["bulk_stories"], request.user)
|
||||
|
||||
return response.NoContent()
|
||||
|
||||
@list_route(methods=["POST"])
|
||||
def bulk_update_backlog_order(self, request, **kwargs):
|
||||
return self._bulk_update_order("backlog_order", request, **kwargs)
|
||||
|
||||
@list_route(methods=["POST"])
|
||||
def bulk_update_sprint_order(self, request, **kwargs):
|
||||
serializer = serializers.UpdateUserStoriesOrderBulkSerializer(data=request.DATA)
|
||||
if not serializer.is_valid():
|
||||
return response.BadRequest(serializer.errors)
|
||||
|
||||
data = serializer.data
|
||||
project = get_object_or_404(Project, pk=data["project_id"])
|
||||
|
||||
self.check_permissions(request, "bulk_update_order", project)
|
||||
services.update_userstories_order_in_bulk(data["bulk_stories"],
|
||||
project=project,
|
||||
field="sprint_order")
|
||||
services.snapshot_userstories_in_bulk(data["bulk_stories"], request.user)
|
||||
return response.NoContent()
|
||||
return self._bulk_update_order("sprint_order", request, **kwargs)
|
||||
|
||||
@list_route(methods=["POST"])
|
||||
def bulk_update_kanban_order(self, request, **kwargs):
|
||||
serializer = serializers.UpdateUserStoriesOrderBulkSerializer(data=request.DATA)
|
||||
if not serializer.is_valid():
|
||||
return response.BadRequest(serializer.errors)
|
||||
|
||||
data = serializer.data
|
||||
project = get_object_or_404(Project, pk=data["project_id"])
|
||||
|
||||
self.check_permissions(request, "bulk_update_order", project)
|
||||
services.update_userstories_order_in_bulk(data["bulk_stories"],
|
||||
project=project,
|
||||
field="kanban_order")
|
||||
services.snapshot_userstories_in_bulk(data["bulk_stories"], request.user)
|
||||
return response.NoContent()
|
||||
return self._bulk_update_order("kanban_order", request, **kwargs)
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, *args, **kwargs):
|
||||
|
|
|
@ -87,3 +87,27 @@ def test_api_create_invalid_task(client):
|
|||
client.login(us.owner)
|
||||
response = client.json.post(url, json.dumps(data))
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
def test_api_update_order_in_bulk(client):
|
||||
project = f.create_project()
|
||||
f.MembershipFactory.create(project=project, user=project.owner, is_owner=True)
|
||||
task1 = f.create_task(project=project)
|
||||
task2 = f.create_task(project=project)
|
||||
|
||||
url1 = reverse("tasks-bulk-update-taskboard-order")
|
||||
url2 = reverse("tasks-bulk-update-us-order")
|
||||
|
||||
data = {
|
||||
"project_id": project.id,
|
||||
"bulk_tasks": [{"task_id": task1.id, "order": 1},
|
||||
{"task_id": task2.id, "order": 2}]
|
||||
}
|
||||
|
||||
client.login(project.owner)
|
||||
|
||||
response1 = client.json.post(url1, json.dumps(data))
|
||||
response2 = client.json.post(url2, json.dumps(data))
|
||||
|
||||
assert response1.status_code == 204, response1.data
|
||||
assert response2.status_code == 204, response2.data
|
||||
|
|
Loading…
Reference in New Issue