From 69fa312201d6f0e12502d5b936f1d69f6ffbedce Mon Sep 17 00:00:00 2001 From: ITQ Date: Sat, 25 Jan 2025 22:01:36 +0300 Subject: [PATCH] feat: added comments cruds --- solution/api/v1/user/schemas.py | 34 +++++++ solution/api/v1/user/utils.py | 15 ++- solution/api/v1/user/views.py | 161 +++++++++++++++++++++++++++++++- 3 files changed, 208 insertions(+), 2 deletions(-) diff --git a/solution/api/v1/user/schemas.py b/solution/api/v1/user/schemas.py index 8c98d5b..9314b37 100644 --- a/solution/api/v1/user/schemas.py +++ b/solution/api/v1/user/schemas.py @@ -1,9 +1,11 @@ +import datetime import uuid from typing import ClassVar from ninja import ModelSchema, Schema from pydantic import Field, StrictInt +from apps.promo.models import PromocodeComment from apps.user.models import User @@ -96,3 +98,35 @@ class PromocodeLikeOut(Schema): class PromocodeRemoveLikeOut(Schema): status: str = "ok" + + +class PromocodeCommentsFilters(Schema): + limit: int = Field(10, gt=0, description="Limit must be greater than 0") + offset: int = Field( + 0, ge=0, description="Offset must be greater than or equal to 0" + ) + + +class CommentIn(ModelSchema): + class Meta: + model = PromocodeComment + fields: ClassVar[list[str]] = [ + PromocodeComment.text.field.name + ] + + +class CommentAuthor(Schema): + name: str + surname: str + avatar_url: str | None + + +class CommentOut(Schema): + id: uuid.UUID + text: str + date: datetime.datetime + author: CommentAuthor + + +class CommentDeletedOut(Schema): + status: str = "ok" diff --git a/solution/api/v1/user/utils.py b/solution/api/v1/user/utils.py index 603b548..22d230b 100644 --- a/solution/api/v1/user/utils.py +++ b/solution/api/v1/user/utils.py @@ -1,5 +1,5 @@ from api.v1.user import schemas -from apps.promo.models import Promocode +from apps.promo.models import Promocode, PromocodeComment from apps.user.models import User @@ -29,3 +29,16 @@ def map_promocode_to_schema(promocode: Promocode) -> schemas.PromocodeViewOut: comment_count=promocode.comment_count, active=promocode.active, ) + + +def map_comment_to_schema(comment: PromocodeComment) -> schemas.CommentOut: + return schemas.CommentOut( + id=comment.id, + text=comment.text, + date=comment.date, + author=schemas.CommentAuthor( + name=comment.author.name, + surname=comment.author.surname, + avatar_url=comment.author.avatar_url + ) + ) diff --git a/solution/api/v1/user/views.py b/solution/api/v1/user/views.py index 3b6cf6e..cd29fdb 100644 --- a/solution/api/v1/user/views.py +++ b/solution/api/v1/user/views.py @@ -9,7 +9,12 @@ from ninja.errors import AuthenticationError, HttpError from api.v1 import schemas as global_schemas from api.v1.auth import UserAuth from api.v1.user import schemas, utils -from apps.promo.models import Promocode, PromocodeActivation, PromocodeLike +from apps.promo.models import ( + Promocode, + PromocodeActivation, + PromocodeComment, + PromocodeLike, +) from apps.user.models import User from config.errors import UniqueConstraintError @@ -268,3 +273,157 @@ def delete_like( ).delete() return status.OK, schemas.PromocodeLikeOut() + + +@router.post( + "/promo/{promocode_id}/comments", + auth=UserAuth(), + response={ + status.CREATED: schemas.CommentOut, + status.BAD_REQUEST: global_schemas.BadRequestError, + }, +) +def add_comment( + request: HttpRequest, promocode_id: str, comment: schemas.CommentIn +) -> tuple[int, schemas.CommentOut]: + user: User = request.auth + + promocodes = Promocode.objects.filter(id=promocode_id) + + if not promocodes.exists(): + raise HttpError(status.NOT_FOUND, status.NOT_FOUND.phrase) + + comment_obj = PromocodeComment( + promocode=promocodes.first(), author=user, **comment.dict() + ) + comment_obj.save() + + return status.CREATED, utils.map_comment_to_schema(comment_obj) + + +@router.get( + "/promo/{promocode_id}/comments", + auth=UserAuth(), + response={ + status.OK: list[schemas.CommentOut], + status.NOT_FOUND: global_schemas.NotFoundError, + }, + exclude_none=True, +) +def list_comments( + request: HttpRequest, + filters: Query[schemas.PromocodeCommentsFilters], + promocode_id: str, +) -> tuple[int, schemas.CommentOut]: + promocodes = Promocode.objects.filter(id=promocode_id) + + if not promocodes.exists(): + raise HttpError(status.NOT_FOUND, status.NOT_FOUND.phrase) + + promocodes = promocodes.prefetch_related("comments", "comments__author") + + comments = promocodes.first().comments.all() + + comments = comments[filters.offset : filters.offset + filters.limit] + + return status.OK, [ + utils.map_comment_to_schema(comment) for comment in comments + ] + + +@router.get( + "/promo/{promocode_id}/comments/{comment_id}", + auth=UserAuth(), + response={ + status.OK: schemas.CommentOut, + status.NOT_FOUND: global_schemas.NotFoundError, + }, + exclude_none=True, +) +def get_comment( + request: HttpRequest, promocode_id: str, comment_id: str +) -> tuple[int, schemas.CommentOut]: + commnets = PromocodeComment.objects.filter( + id=comment_id, promocode_id=promocode_id + ) + + if not commnets.exists(): + raise HttpError(status.NOT_FOUND, status.NOT_FOUND.phrase) + + comment = commnets.select_related("author").first() + + return status.OK, utils.map_comment_to_schema(comment) + + +@router.put( + "/promo/{promocode_id}/comments/{comment_id}", + auth=UserAuth(), + response={ + status.OK: schemas.CommentOut, + status.BAD_REQUEST: global_schemas.BadRequestError, + }, +) +def update_comment( + request: HttpRequest, + promocode_id: str, + comment_id: str, + comment: schemas.CommentIn, +) -> tuple[int, schemas.CommentOut]: + user: User = request.auth + + commnets = PromocodeComment.objects.filter( + id=comment_id, promocode_id=promocode_id + ) + + if not commnets.exists(): + raise HttpError(status.NOT_FOUND, status.NOT_FOUND.phrase) + + comments = commnets.select_related("author").filter(author=user) + + if not comments.exists(): + raise HttpError(status.FORBIDDEN, status.FORBIDDEN.phrase) + + comment_obj = comments.first() + + put_data = comment.dict() + for field, value in put_data.items(): + setattr(comment_obj, field, value) + + comment_obj.save() + + return status.OK, utils.map_comment_to_schema(comment_obj) + + +@router.delete( + "/promo/{promocode_id}/comments/{comment_id}", + auth=UserAuth(), + response={ + status.OK: schemas.CommentDeletedOut, + status.BAD_REQUEST: global_schemas.BadRequestError, + status.NOT_FOUND: global_schemas.NotFoundError, + }, +) +def delete_comment( + request: HttpRequest, + promocode_id: str, + comment_id: str, +) -> tuple[int, schemas.CommentOut]: + user: User = request.auth + + commnets = PromocodeComment.objects.filter( + id=comment_id, promocode_id=promocode_id + ) + + if not commnets.exists(): + raise HttpError(status.NOT_FOUND, status.NOT_FOUND.phrase) + + comments = commnets.select_related("author").filter(author=user) + + if not comments.exists(): + raise HttpError(status.FORBIDDEN, status.FORBIDDEN.phrase) + + comment_obj = comments.first() + + comment_obj.delete() + + return status.OK, schemas.CommentDeletedOut()