diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index fc01f0c..8cea2ad 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -39,8 +39,19 @@ @comment.post("", response_model=CommentGet) async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depends(UnionAuth())) -> CommentGet: """ + Scopes: `["rating.comment.create"]` + Создает комментарий к преподавателю в базе данных RatingAPI - Для создания комментария нужно быть авторизованным + + Комментарий создается со статусом PENDING (на модерации) + + Исключение **TooManyCommentRequests**, если число комментариев превысило общий лимит + + Исключение **TooManyCommentsToLecturer**, если число комментариев превысило лимит для лектора + + Исключение **CommentTooLong**, если комментарий слишком длинный + + Исключение **ForbiddenSymbol**, если в комментарии использованы запрещенные символы """ # Проверяем, что лектор с заданным id существует Lecturer.get(session=db.session, id=lecturer_id) @@ -148,7 +159,8 @@ async def import_comments( ) -> CommentGetAll: """ Scopes: `["rating.comment.import"]` - Создает комментарии в базе данных RatingAPI + + Создает комментарии в базе данных """ number_of_comments = len(comments_info.comments) result = CommentGetAll(limit=number_of_comments, offset=number_of_comments, total=number_of_comments) @@ -165,7 +177,13 @@ async def import_comments( @comment.get("/{uuid}", response_model=CommentGet) async def get_comment(uuid: UUID, user=Depends(UnionAuth(auto_error=False, allow_none=False))) -> CommentGet: """ + Scopes: `["rating.comment.read"]` + Возвращает комментарий по его UUID в базе данных RatingAPI + + Если пользователь авторизован, добавляются флаги is_liked/is_disliked (реакция пользователя на комментарий) + + Исключение **ObjectNotFound**, если `uuid` не найден """ comment: Comment = Comment.query(session=db.session).filter(Comment.uuid == uuid).one_or_none() if comment is None: @@ -213,6 +231,15 @@ async def get_comments( `unreviewed` - вернет все непроверенные комментарии, если True. По дефолту False. `asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания + + Разные модели ответа в зависимости от прав пользователя: + CommentGetAllWithAllInfo: для модераторов (со статусом комментария); + CommentGetAllWithStatus: для авторов комментариев (со статусом); + CommentGetAll: для всех остальных (только одобренные комментарии) + + Исключение **ObjectNotFound**, если комментарий с введенными параметрами не найден + + Исключение **ForbiddenAction**, если пользователь пытается получить непроверенный комментарий """ comments_query = ( Comment.query(session=db.session) @@ -288,6 +315,12 @@ async def review_comment( `review_status` - возможные значения `approved` - комментарий одобрен и возвращается при запросе лектора `dismissed` - комментарий отклонен, не отображается в запросе лектора + + Комментарий может быть либо одобрен, либо отклонен + + Отклоненные комментарии не отображаются в обычных GET-запросах(можно посмотреть только через `uuid`) + + Исключение **ObjectNotFound**, если `uuid` не найден """ check_comment: Comment = Comment.query(session=db.session).filter(Comment.uuid == uuid).one_or_none() @@ -301,7 +334,17 @@ async def review_comment( @comment.patch("/{uuid}", response_model=CommentGet) async def update_comment(uuid: UUID, comment_update: CommentUpdate, user=Depends(UnionAuth())) -> CommentGet: - """Позволяет изменить свой неанонимный комментарий""" + """ + Scopes: `["rating.comment.update"]` + + Позволяет изменить свой неанонимный комментарий + + После редактирования комментарий снова отправляется на модерацию + + Исключение **ForbiddenAction** при попытке отредактировать чужой комментарий + + Исключение **ForbiddenAction** при попытке отредактировать анонимный комментарий + """ comment: Comment = Comment.get(session=db.session, id=uuid) # Ошибка, если не найден if comment.user_id != user.get("id") or comment.user_id is None: @@ -334,6 +377,16 @@ async def delete_comment( Scopes: `["rating.comment.delete"]` Удаляет комментарий по его UUID в базе данных RatingAPI + + Модератор может удалить любой комментарий + + Обычный пользователь может удалить только свой неанонимный комментарий + + Анонимные комментарии может удалить только модератор + + Исключение **ObjectNotFound**, если `uuid` не найден + + Исключение **ForbiddenAction** при попытке удалить комментарий пользователем без прав """ comment = Comment.get(uuid, session=db.session) if comment is None: @@ -358,22 +411,18 @@ async def like_comment( user=Depends(UnionAuth()), ) -> CommentGet: """ - Handles like/dislike reactions for a comment. + Scopes: `["rating.comment.write"]` - This endpoint allows authenticated users to react to a comment (like/dislike) or change their existing reaction. - If the user has no existing reaction, a new one is created. If the user changes their reaction, it gets updated. - If the user clicks the same reaction again, the reaction is removed. + Ставит лайк или дизлайк на комментарий по его uuid - Args: - uuid (UUID): The UUID of the comment to react to. - reaction (Reaction): The reaction type (like/dislike). - user (dict): Authenticated user data from UnionAuth dependency. + Дизлайка и лайка не может быть одновременно - Returns: - CommentGet: The updated comment with reactions in CommentGet format. + Если реакции от пользователя на этот комментарий не было — создается новая; + Если была противоположная реакция — она заменяется на новую; + Если была такая же реакция — она удаляется; + После операции поля is_liked/is_disliked в ответе отражают итоговое состояние - Raises: - ObjectNotFound: If the comment with given UUID doesn't exist. + Исключение **ObjectNotFound**, если `uuid` не найден """ comment = Comment.get(session=db.session, id=uuid) if not comment: diff --git a/rating_api/routes/lecturer.py b/rating_api/routes/lecturer.py index 1a51028..8200b6d 100644 --- a/rating_api/routes/lecturer.py +++ b/rating_api/routes/lecturer.py @@ -34,7 +34,9 @@ async def create_lecturer( """ Scopes: `["rating.lecturer.create"]` - Создает преподавателя в базе данных RatingAPI + Создает преподавателя в базе данных + + Исключение **AlreadyExists**, если преподаватель с введеным `timetable_id` уже существует """ get_lecturer: Lecturer = ( Lecturer.query(session=db.session).filter(Lecturer.timetable_id == lecturer_info.timetable_id).one_or_none() @@ -54,7 +56,7 @@ async def update_lecturer_rating( """ Scopes: `["rating.lecturer.update_rating"]` - Обновляет рейтинг преподавателя в базе данных RatingAPI + Обновляет рейтинг преподавателя в базе данных """ updated_lecturers = [] response = { @@ -95,6 +97,8 @@ async def update_lecturer_rating( async def get_lecturer_by_timetable_id(timetable_id: int) -> LecturerGet: """ Возвращает преподавателя по его timetable_id + + Исключение **ObjectNotFound**, если `timetable_id` не найден """ lecturer: Lecturer = Lecturer.query(session=db.session).filter(Lecturer.timetable_id == timetable_id).one_or_none() if lecturer is None: @@ -107,11 +111,13 @@ async def get_lecturer(id: int, info: list[Literal["comments"]] = Query(default= """ Scopes: `["rating.lecturer.read"]` - Возвращает преподавателя по его ID в базе данных RatingAPI + Возвращает преподавателя по его ID в базе данных *QUERY* `info: string` - возможные значения `'comments'`. Если передано `'comments'`, то возвращаются одобренные комментарии к преподавателю. - Subject лектора возвращшается либо из базы данных, либо из любого аппрувнутого комментария + Subject лектора возвращается либо из базы данных, либо из любого аппрувнутого комментария + + Исключение **ObjectNotFound**, если `id` не найден """ lecturer: Lecturer = Lecturer.query(session=db.session).filter(Lecturer.id == id).one_or_none() if lecturer is None: @@ -168,6 +174,8 @@ async def get_lecturers( `mark` Поле для оценки. Если передано, то возвращает только тех преподавателей, для которых средняя общая оценка ('general_mark') больше, чем переданный 'mark'. + + Исключение **ObjectNotFound**, если преподаватель с введенными параметрами не найден """ lecturers_query = lecturer_filter.filter( Lecturer.query(session=db.session).outerjoin(Lecturer.comments).group_by(Lecturer.id) @@ -212,6 +220,10 @@ async def update_lecturer( ) -> LecturerGet: """ Scopes: `["rating.lecturer.update"]` + + Обновляет данные о преподавателе по его id + + Исключение **ObjectNotFound**, если `id` не найден """ lecturer = Lecturer.get(id, session=db.session) if lecturer is None: @@ -238,6 +250,10 @@ async def delete_lecturer( ): """ Scopes: `["rating.lecturer.delete"]` + + Удаляет из базы данных преподавателя по его id + + Исключение **ObjectNotFound**, если `id` не найден """ check_lecturer = Lecturer.get(session=db.session, id=id) if check_lecturer is None: