From d014f5a2fbb0dfbc24d7cb24d053dd4975d15825 Mon Sep 17 00:00:00 2001 From: xoliqberdiyev Date: Thu, 30 Apr 2026 16:02:04 +0500 Subject: [PATCH] fix --- config/urls.py | 1 + core/apps/accounts/serializers/user.py | 3 +- core/apps/tasks/serializers/board.py | 22 +++++++++++ core/apps/tasks/serializers/comment.py | 3 +- core/apps/tasks/serializers/task.py | 26 ++++++++++++- core/apps/tasks/urls.py | 31 +++++++++++++++- core/apps/tasks/views/board.py | 13 +++++++ core/apps/tasks/views/column.py | 47 ++++++++++++++++++++++++ core/apps/tasks/views/comment.py | 51 ++++++++++++++++++++++++++ core/apps/tasks/views/label.py | 22 +++++++++++ core/apps/tasks/views/task.py | 40 ++++++++++++++++++++ 11 files changed, 253 insertions(+), 6 deletions(-) create mode 100644 core/apps/tasks/serializers/board.py create mode 100644 core/apps/tasks/views/board.py create mode 100644 core/apps/tasks/views/label.py diff --git a/config/urls.py b/config/urls.py index c43854b..e64d95b 100644 --- a/config/urls.py +++ b/config/urls.py @@ -23,6 +23,7 @@ urlpatterns = [ path("api/v1/", include("core.apps.evaluation.urls")), path("api/v1/", include("core.apps.payment.urls")), path("api/v1/", include("core.apps.chat.urls")), + path("api/v1/tasks/", include("core.apps.tasks.urls")), ] urlpatterns += [ path("admin/", admin.site.urls), diff --git a/core/apps/accounts/serializers/user.py b/core/apps/accounts/serializers/user.py index b93910c..23eee5e 100644 --- a/core/apps/accounts/serializers/user.py +++ b/core/apps/accounts/serializers/user.py @@ -63,7 +63,8 @@ class ShortUserSerializer(serializers.ModelSerializer): model = get_user_model() fields = [ 'id', - 'full_name', + 'first_name', + 'last_name', 'avatar', ] diff --git a/core/apps/tasks/serializers/board.py b/core/apps/tasks/serializers/board.py new file mode 100644 index 0000000..ad04081 --- /dev/null +++ b/core/apps/tasks/serializers/board.py @@ -0,0 +1,22 @@ +from rest_framework import serializers + +from core.apps.tasks.serializers.comment import CommentSerializer +from core.apps.tasks.serializers.task import TaskSerializer +from core.apps.tasks.models import Column, Task + + +class BoardTaskSerializer(TaskSerializer): + comments = CommentSerializer(many=True, read_only=True) + + class Meta(TaskSerializer.Meta): + TaskSerializer.Meta.fields += ['comments'] + + +class BoardSerializer(serializers.ModelSerializer): + tasks = BoardTaskSerializer(many=True, read_only=True) + + class Meta: + model = Column + fields = [ + 'id', 'name', 'tasks', + ] diff --git a/core/apps/tasks/serializers/comment.py b/core/apps/tasks/serializers/comment.py index 5500fa0..3c053af 100644 --- a/core/apps/tasks/serializers/comment.py +++ b/core/apps/tasks/serializers/comment.py @@ -17,7 +17,8 @@ class CommentSerializer(serializers.ModelSerializer): request = self.context.get('request') return { "id": obj.created_by.id, - "full_name": obj.created_by.full_name, + "first_name": obj.created_by.first_name, + "last_name": obj.created_by.last_name, "avatar": request.build_absolute_uri(obj.created_by.avatar.url) if obj.created_by.avatar else None } diff --git a/core/apps/tasks/serializers/task.py b/core/apps/tasks/serializers/task.py index cc7b5ae..bef96e7 100644 --- a/core/apps/tasks/serializers/task.py +++ b/core/apps/tasks/serializers/task.py @@ -7,6 +7,8 @@ from core.apps.tasks.serializers.label import LabelSerializer class TaskSerializer(serializers.ModelSerializer): labels = LabelSerializer(many=True) + assignees = serializers.SerializerMethodField(method_name='get_assignees') + created_by = serializers.SerializerMethodField(method_name='get_created_by') class Meta: model = Task @@ -24,7 +26,27 @@ class TaskSerializer(serializers.ModelSerializer): ] def get_assignees(self, obj): - return ShortUserSerializer(obj.assignees.all(), many=True, context={"request": self.context['request']}) + return ShortUserSerializer(obj.assignees.all(), many=True, context={"request": self.context['request']}).data def get_created_by(self, obj): - return ShortUserSerializer(obj.created_by, context={"request": self.context['request']}) + return ShortUserSerializer(obj.created_by, context={"request": self.context['request']}).data + + +class TaskCreateSerializer(serializers.ModelSerializer): + class Meta: + model = Task + fields = [ + 'id', + 'column', + 'name', + 'description', + 'priority', + 'from_date', + 'to_date', + 'labels', + 'assignees', + ] + + def create(self, validated_data): + validated_data['created_by'] = self.context['request'].user + return super().create(validated_data) diff --git a/core/apps/tasks/urls.py b/core/apps/tasks/urls.py index 072a89f..29d59af 100644 --- a/core/apps/tasks/urls.py +++ b/core/apps/tasks/urls.py @@ -1,7 +1,34 @@ from django.urls import path, include -from core.apps.tasks.views import task, column, comment +from core.apps.tasks.views import task, column, comment, label, board urlpatterns = [ - + path('column/', include( + [ + path('list/', column.ColumnListApiView.as_view()), + path('create/', column.ColumnCreateApiView.as_view()), + path('/update/', column.ColumnUpdateApiView.as_view()), + path('/delete/', column.ColumnDeleteApiView.as_view()) + ] + )), + path('label/', include( + [ + path('', label.LabelListCreateApiView.as_view()), + path('/', label.LabelRetrieveUpdateDestroyApiView.as_view()), + ] + )), + path('task/', include( + [ + path('list/', task.TaskListView.as_view()), + path('/', task.TaskDetailView.as_view()), + path('create/', task.TaskCreateView.as_view()), + ] + )), + path('comment/', include( + [ + path('', comment.CommentListCreateAPIView.as_view()), + path('/', comment.CommentDetailAPIView.as_view()), + ] + )), + path('board/', board.BoardListView.as_view()), ] diff --git a/core/apps/tasks/views/board.py b/core/apps/tasks/views/board.py new file mode 100644 index 0000000..755089e --- /dev/null +++ b/core/apps/tasks/views/board.py @@ -0,0 +1,13 @@ +from rest_framework import generics, permissions +from rest_framework.response import Response + +from core.apps.tasks.serializers.board import BoardSerializer +from core.apps.tasks.models import Column + + +class BoardListView(generics.ListAPIView): + queryset = Column.objects.order_by('id') + serializer_class = BoardSerializer + permission_classes = [permissions.IsAuthenticated] + + \ No newline at end of file diff --git a/core/apps/tasks/views/column.py b/core/apps/tasks/views/column.py index e69de29..015d269 100644 --- a/core/apps/tasks/views/column.py +++ b/core/apps/tasks/views/column.py @@ -0,0 +1,47 @@ +from django.db import transaction + +from rest_framework import generics, permissions +from rest_framework.response import Response + +from drf_spectacular.utils import extend_schema + +from core.apps.tasks.serializers.column import ColumnSerializer +from core.apps.tasks.models.column import Column + + +@extend_schema(tags=['Tasks']) +class ColumnCreateApiView(generics.GenericAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = ColumnSerializer + + @transaction.atomic + def post(self, request): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + column = serializer.save() + return Response(serializer.data) + + +@extend_schema(tags=['Tasks']) +class ColumnListApiView(generics.ListAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = ColumnSerializer + + def get_queryset(self): + return Column.objects.order_by('id') + + +@extend_schema(tags=['Tasks']) +class ColumnUpdateApiView(generics.UpdateAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = ColumnSerializer + lookup_field = 'id' + queryset = Column.objects.all() + + +@extend_schema(tags=['Tasks']) +class ColumnDeleteApiView(generics.DestroyAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = ColumnSerializer + lookup_field = 'id' + queryset = Column.objects.all() diff --git a/core/apps/tasks/views/comment.py b/core/apps/tasks/views/comment.py index e69de29..31f4d68 100644 --- a/core/apps/tasks/views/comment.py +++ b/core/apps/tasks/views/comment.py @@ -0,0 +1,51 @@ +from rest_framework import generics, permissions +from rest_framework.exceptions import PermissionDenied + +from core.apps.tasks.models.comment import Comment +from core.apps.tasks.serializers.comment import CommentSerializer, CommentCreateSerializer + + +class CommentListCreateAPIView(generics.ListCreateAPIView): + queryset = Comment.objects.all() + permission_classes = [permissions.IsAuthenticated] + + def get_serializer_class(self): + if self.request.method == 'POST': + return CommentCreateSerializer + return CommentSerializer + + def get_queryset(self): + task_id = self.request.query_params.get('task_id') + queryset = self.queryset + + if task_id: + queryset = queryset.filter(task_id=task_id) + + return queryset.order_by('-id') + + def get_serializer_context(self): + return {"request": self.request} + + +class CommentDetailAPIView(generics.RetrieveUpdateDestroyAPIView): + queryset = Comment.objects.all() + permission_classes = [permissions.IsAuthenticated] + + def get_serializer_class(self): + if self.request.method in ['PUT', 'PATCH']: + return CommentCreateSerializer + return CommentSerializer + + def get_serializer_context(self): + return {"request": self.request} + + def perform_update(self, serializer): + comment = self.get_object() + if comment.created_by != self.request.user: + raise PermissionDenied("You cannot edit this comment") + serializer.save() + + def perform_destroy(self, instance): + if instance.created_by != self.request.user: + raise PermissionDenied("You cannot delete this comment") + instance.delete() diff --git a/core/apps/tasks/views/label.py b/core/apps/tasks/views/label.py new file mode 100644 index 0000000..3cd6105 --- /dev/null +++ b/core/apps/tasks/views/label.py @@ -0,0 +1,22 @@ +from rest_framework import generics, permissions +from rest_framework.response import Response + +from drf_spectacular.utils import extend_schema + +from core.apps.tasks.serializers.label import LabelSerializer +from core.apps.tasks.models.label import Label + + +@extend_schema(tags=['Tasks']) +class LabelListCreateApiView(generics.ListCreateAPIView): + queryset = Label.objects.order_by('id') + serializer_class = LabelSerializer + permission_classes = [permissions.IsAuthenticated] + + +@extend_schema(tags=['Tasks']) +class LabelRetrieveUpdateDestroyApiView(generics.RetrieveUpdateDestroyAPIView): + queryset = Label.objects.order_by('id') + serializer_class = LabelSerializer + permission_classes = [permissions.IsAuthenticated] + lookup_field = 'id' diff --git a/core/apps/tasks/views/task.py b/core/apps/tasks/views/task.py index e69de29..22dc730 100644 --- a/core/apps/tasks/views/task.py +++ b/core/apps/tasks/views/task.py @@ -0,0 +1,40 @@ +from django.db import transaction + +from rest_framework import permissions, generics +from rest_framework.response import Response + +from drf_spectacular.utils import extend_schema + +from core.apps.tasks.models.task import Task +from core.apps.tasks.serializers.task import TaskSerializer, TaskCreateSerializer + + +@extend_schema(tags=['Tasks']) +class TaskCreateView(generics.GenericAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = TaskCreateSerializer + queryset = Task.objects.order_by('id') + + @transaction.atomic + def post(self, request): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data) + + +@extend_schema(tags=['Tasks']) +class TaskListView(generics.ListAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = TaskSerializer + queryset = Task.objects.order_by('id') + + def serializer_context(self): + return self.serializer_class(context={"request": self.request}) + + +@extend_schema(tags=['Tasks']) +class TaskDetailView(generics.RetrieveUpdateDestroyAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = TaskSerializer + queryset = Task.objects.order_by('id')