api.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
  2. # Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
  3. # Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as
  6. # published by the Free Software Foundation, either version 3 of the
  7. # License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU Affero General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. from django.utils.translation import ugettext as _
  17. from taiga.base.api.utils import get_object_or_404
  18. from taiga.base import filters, response
  19. from taiga.base import exceptions as exc
  20. from taiga.base.decorators import list_route
  21. from taiga.base.api import ModelCrudViewSet, ModelListViewSet
  22. from taiga.projects.models import Project, TaskStatus
  23. from django.http import HttpResponse
  24. from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
  25. from taiga.projects.history.mixins import HistoryResourceMixin
  26. from taiga.projects.occ import OCCResourceMixin
  27. from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin
  28. from . import models
  29. from . import permissions
  30. from . import serializers
  31. from . import services
  32. class TaskViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, WatchedResourceMixin,
  33. ModelCrudViewSet):
  34. queryset = models.Task.objects.all()
  35. permission_classes = (permissions.TaskPermission,)
  36. filter_backends = (filters.CanViewTasksFilterBackend, filters.WatchersFilter)
  37. retrieve_exclude_filters = (filters.WatchersFilter,)
  38. filter_fields = ["user_story", "milestone", "project", "assigned_to",
  39. "status__is_closed"]
  40. def get_serializer_class(self, *args, **kwargs):
  41. if self.action in ["retrieve", "by_ref"]:
  42. return serializers.TaskNeighborsSerializer
  43. if self.action == "list":
  44. return serializers.TaskListSerializer
  45. return serializers.TaskSerializer
  46. def update(self, request, *args, **kwargs):
  47. self.object = self.get_object_or_none()
  48. project_id = request.DATA.get('project', None)
  49. if project_id and self.object and self.object.project.id != project_id:
  50. try:
  51. new_project = Project.objects.get(pk=project_id)
  52. self.check_permissions(request, "destroy", self.object)
  53. self.check_permissions(request, "create", new_project)
  54. sprint_id = request.DATA.get('milestone', None)
  55. if sprint_id is not None and new_project.milestones.filter(pk=sprint_id).count() == 0:
  56. request.DATA['milestone'] = None
  57. us_id = request.DATA.get('user_story', None)
  58. if us_id is not None and new_project.user_stories.filter(pk=us_id).count() == 0:
  59. request.DATA['user_story'] = None
  60. status_id = request.DATA.get('status', None)
  61. if status_id is not None:
  62. try:
  63. old_status = self.object.project.task_statuses.get(pk=status_id)
  64. new_status = new_project.task_statuses.get(slug=old_status.slug)
  65. request.DATA['status'] = new_status.id
  66. except TaskStatus.DoesNotExist:
  67. request.DATA['status'] = new_project.default_task_status.id
  68. except Project.DoesNotExist:
  69. return response.BadRequest(_("The project doesn't exist"))
  70. return super().update(request, *args, **kwargs)
  71. def get_queryset(self):
  72. qs = super().get_queryset()
  73. qs = self.attach_votes_attrs_to_queryset(qs)
  74. return self.attach_watchers_attrs_to_queryset(qs)
  75. def pre_save(self, obj):
  76. if obj.user_story:
  77. obj.milestone = obj.user_story.milestone
  78. if not obj.id:
  79. obj.owner = self.request.user
  80. super().pre_save(obj)
  81. def pre_conditions_on_save(self, obj):
  82. super().pre_conditions_on_save(obj)
  83. if obj.milestone and obj.milestone.project != obj.project:
  84. raise exc.WrongArguments(_("You don't have permissions to set this sprint to this task."))
  85. if obj.user_story and obj.user_story.project != obj.project:
  86. raise exc.WrongArguments(_("You don't have permissions to set this user story to this task."))
  87. if obj.status and obj.status.project != obj.project:
  88. raise exc.WrongArguments(_("You don't have permissions to set this status to this task."))
  89. if obj.milestone and obj.user_story and obj.milestone != obj.user_story.milestone:
  90. raise exc.WrongArguments(_("You don't have permissions to set this sprint to this task."))
  91. @list_route(methods=["GET"])
  92. def by_ref(self, request):
  93. ref = request.QUERY_PARAMS.get("ref", None)
  94. project_id = request.QUERY_PARAMS.get("project", None)
  95. task = get_object_or_404(models.Task, ref=ref, project_id=project_id)
  96. return self.retrieve(request, pk=task.pk)
  97. @list_route(methods=["GET"])
  98. def csv(self, request):
  99. uuid = request.QUERY_PARAMS.get("uuid", None)
  100. if uuid is None:
  101. return response.NotFound()
  102. project = get_object_or_404(Project, tasks_csv_uuid=uuid)
  103. queryset = project.tasks.all().order_by('ref')
  104. data = services.tasks_to_csv(project, queryset)
  105. csv_response = HttpResponse(data.getvalue(), content_type='application/csv; charset=utf-8')
  106. csv_response['Content-Disposition'] = 'attachment; filename="tasks.csv"'
  107. return csv_response
  108. @list_route(methods=["POST"])
  109. def bulk_create(self, request, **kwargs):
  110. serializer = serializers.TasksBulkSerializer(data=request.DATA)
  111. if serializer.is_valid():
  112. data = serializer.data
  113. project = Project.objects.get(id=data["project_id"])
  114. self.check_permissions(request, 'bulk_create', project)
  115. tasks = services.create_tasks_in_bulk(
  116. data["bulk_tasks"], milestone_id=data["sprint_id"], user_story_id=data["us_id"],
  117. status_id=data.get("status_id") or project.default_task_status_id,
  118. project=project, owner=request.user, callback=self.post_save, precall=self.pre_save)
  119. tasks_serialized = self.get_serializer_class()(tasks, many=True)
  120. return response.Ok(tasks_serialized.data)
  121. return response.BadRequest(serializer.errors)
  122. def _bulk_update_order(self, order_field, request, **kwargs):
  123. serializer = serializers.UpdateTasksOrderBulkSerializer(data=request.DATA)
  124. if not serializer.is_valid():
  125. return response.BadRequest(serializer.errors)
  126. data = serializer.data
  127. project = get_object_or_404(Project, pk=data["project_id"])
  128. self.check_permissions(request, "bulk_update_order", project)
  129. services.update_tasks_order_in_bulk(data["bulk_tasks"],
  130. project=project,
  131. field=order_field)
  132. services.snapshot_tasks_in_bulk(data["bulk_tasks"], request.user)
  133. return response.NoContent()
  134. @list_route(methods=["POST"])
  135. def bulk_update_taskboard_order(self, request, **kwargs):
  136. return self._bulk_update_order("taskboard_order", request, **kwargs)
  137. @list_route(methods=["POST"])
  138. def bulk_update_us_order(self, request, **kwargs):
  139. return self._bulk_update_order("us_order", request, **kwargs)
  140. class TaskVotersViewSet(VotersViewSetMixin, ModelListViewSet):
  141. permission_classes = (permissions.TaskVotersPermission,)
  142. resource_model = models.Task
  143. class TaskWatchersViewSet(WatchersViewSetMixin, ModelListViewSet):
  144. permission_classes = (permissions.TaskWatchersPermission,)
  145. resource_model = models.Task