Corp/backend/api/v1/tasks/viewsets/automation.py

115 lines
4.3 KiB
Python

"""HTTP adapter for shared task automation rules."""
from django.db.models import Count, Q
from django.http import Http404
from rest_framework import filters, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from tasks.automation.application import (
AccessibleTaskNotFound,
ManualAutomationUnavailable,
eligible_button_automations,
get_accessible_automation_task,
run_manual_automation,
)
from tasks.automation.schema import automation_catalog
from tasks.models import TaskAutomation, TaskAutomationRun
from ..permissions import CanAccessTaskAutomationAPI
from ..serializers import TaskAutomationRunSerializer, TaskAutomationSerializer
class TaskAutomationViewSet(viewsets.ModelViewSet):
"""Manage shared rules and execute task-scoped automation buttons."""
permission_classes = [CanAccessTaskAutomationAPI]
serializer_class = TaskAutomationSerializer
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ["name", "description"]
ordering_fields = ["position", "name", "created_at", "last_run_at"]
ordering = ["kind", "position", "name", "id"]
def get_queryset(self):
queryset = (
TaskAutomation.objects.select_related("created_by")
.prefetch_related("boards")
.annotate(runs_count=Count("runs", distinct=True))
)
kind = self.request.query_params.get("kind")
if kind:
queryset = queryset.filter(kind=kind)
if self.request.query_params.get("active") in {"true", "false"}:
queryset = queryset.filter(
is_active=self.request.query_params["active"] == "true"
)
if board_id := self.request.query_params.get("board"):
queryset = queryset.filter(
Q(applies_to_all_boards=True) | Q(boards=board_id)
).distinct()
return queryset
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
@action(detail=False, methods=["get"])
def catalog(self, request):
return Response(automation_catalog())
def _accessible_task(self, request, task_id):
try:
return get_accessible_automation_task(
actor=request.user,
task_id=task_id,
)
except AccessibleTaskNotFound as exc:
raise Http404 from exc
@action(detail=False, methods=["get"])
def buttons(self, request):
task_id = request.query_params.get("task")
if not task_id:
return Response(
{"task": ["Укажите задачу."]},
status=status.HTTP_400_BAD_REQUEST,
)
task = self._accessible_task(request, task_id)
eligible = eligible_button_automations(self.get_queryset(), task=task)
return Response(self.get_serializer(eligible, many=True).data)
@action(detail=True, methods=["post"])
def run(self, request, pk=None):
automation = self.get_object()
task_id = request.data.get("task")
if not task_id:
return Response(
{"task": ["Укажите задачу."]},
status=status.HTTP_400_BAD_REQUEST,
)
task = self._accessible_task(request, task_id)
try:
outcome = run_manual_automation(
automation,
task=task,
actor=request.user,
)
except ManualAutomationUnavailable:
return Response(
{"detail": "Эту автоматизацию нельзя запустить вручную."},
status=status.HTTP_400_BAD_REQUEST,
)
if outcome.conflict_message:
return Response(
{"detail": outcome.conflict_message},
status=status.HTTP_409_CONFLICT,
)
return Response(TaskAutomationRunSerializer(outcome.run).data)
@action(detail=True, methods=["get"])
def runs(self, request, pk=None):
automation = self.get_object()
queryset = TaskAutomationRun.objects.filter(
automation=automation,
).select_related("automation", "task", "actor")[:100]
return Response(TaskAutomationRunSerializer(queryset, many=True).data)