From d6cd12a89a8797e04db4239b1137bb25a1697028 Mon Sep 17 00:00:00 2001 From: moschenkovjas Date: Tue, 29 Apr 2025 19:25:24 +0000 Subject: [PATCH 1/5] update updated --- rating_api/models/base.py | 20 +++++++++- rating_api/routes/comment.py | 76 ++++++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 18 deletions(-) diff --git a/rating_api/models/base.py b/rating_api/models/base.py index 8ffc3b1..823111d 100644 --- a/rating_api/models/base.py +++ b/rating_api/models/base.py @@ -6,7 +6,7 @@ from sqlalchemy.exc import NoResultFound from sqlalchemy.orm import Query, Session, as_declarative, declared_attr -from rating_api.exceptions import ObjectNotFound +from rating_api.exceptions import ObjectNotFound, UpdateError @as_declarative() @@ -59,10 +59,26 @@ def get(cls, id: int | str, *, with_deleted=False, session: Session) -> BaseDbMo raise ObjectNotFound(cls, id) @classmethod - def update(cls, id: int | str, *, session: Session, **kwargs) -> BaseDbModel: + def update(cls, id: int | str, *, session: Session, check_empty: list[str] = None, **kwargs) -> BaseDbModel: obj = cls.get(id, session=session) + + if not kwargs: + raise UpdateError(msg="Provide any parametr.") # 409 + # raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="No fields provided for update.") + + unchanged_fields = [] + for field, new_value in kwargs.items(): + old_value = getattr(obj, field) + if old_value == new_value: + unchanged_fields.append(field) + + if unchanged_fields: + raise UpdateError(msg=f"No changes detected in fields: {', '.join(unchanged_fields)}.") + # raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=f"No changes detected in fields") + for k, v in kwargs.items(): setattr(obj, k, v) + session.flush() return obj diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 96a9de8..a06e0bd 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -256,9 +256,12 @@ async def review_comment( Comment.update(session=db.session, id=uuid, review_status=review_status, approved_by=user.get("id")) ) - @comment.patch("/{uuid}", response_model=CommentGet) -async def update_comment(uuid: UUID, comment_update: CommentUpdate, user=Depends(UnionAuth())) -> CommentGet: +async def update_comment( + uuid: UUID, + comment_update: CommentUpdate, + user=Depends(UnionAuth()) +) -> CommentGet: """Позволяет изменить свой неанонимный комментарий""" comment: Comment = Comment.get(session=db.session, id=uuid) # Ошибка, если не найден @@ -268,23 +271,11 @@ async def update_comment(uuid: UUID, comment_update: CommentUpdate, user=Depends # Получаем только переданные для обновления поля update_data = comment_update.model_dump(exclude_unset=True) - # Если не передано ни одного параметра - if not update_data: - raise UpdateError(msg="Provide any parametr.") - # raise HTTPException(status_code=409, detail="Provide any parametr") # 409 - - # Проверяем, есть ли неизмененные поля - current_data = {key: getattr(comment, key) for key in update_data} # Берем текущие значения из БД - unchanged_fields = {k for k, v in update_data.items() if current_data.get(k) == v} - - if unchanged_fields: - raise UpdateError(msg=f"No changes detected in fields: {', '.join(unchanged_fields)}.") - # raise HTTPException(status_code=409, detail=f"No changes detected in fields: {', '.join(unchanged_fields)}") - - # Обновление комментария + # Обновляем комментарий (проверки уже в базовом update()) updated_comment = Comment.update( session=db.session, id=uuid, + check_empty=["text"], # Запрещаем обновлять `text` на пустую строку **update_data, update_ts=datetime.datetime.utcnow(), review_status=ReviewStatus.PENDING, @@ -293,6 +284,59 @@ async def update_comment(uuid: UUID, comment_update: CommentUpdate, user=Depends return CommentGet.model_validate(updated_comment) +''' +@comment.patch("/{uuid}", response_model=CommentGet) +async def update_comment(uuid: UUID, comment_update: CommentUpdate, user=Depends(UnionAuth())) -> CommentGet: + """Позволяет изменить свой неанонимный комментарий""" + comment: Comment = Comment.get(session=db.session, id=uuid) # Ошибка, если не найден + + if comment.user_id != user.get("id") or comment.user_id is None: + raise ForbiddenAction(Comment) + + # Получаем только переданные для обновления поля + update_data = comment_update.model_dump(exclude_unset=True) + + # Добавляем технические поля + update_data["update_ts"] = datetime.utcnow() + update_data["review_status"] = ReviewStatus.PENDING + + # # Если не передано ни одного параметра + # if not update_data: + # raise UpdateError(msg="Provide any parametr.") + # # raise HTTPException(status_code=409, detail="Provide any parametr") # 409 + + # # Проверяем, есть ли неизмененные поля + # current_data = {key: getattr(comment, key) for key in update_data} # Берем текущие значения из БД + # unchanged_fields = {k for k, v in update_data.items() if current_data.get(k) == v} + + # if unchanged_fields: + # raise UpdateError(msg=f"No changes detected in fields: {', '.join(unchanged_fields)}.") + # # raise HTTPException(status_code=409, detail=f"No changes detected in fields: {', '.join(unchanged_fields)}") + + # # Обновление комментария + # updated_comment = Comment.update( + # session=db.session, + # id=uuid, + # **update_data, + # update_ts=datetime.datetime.utcnow(), + # review_status=ReviewStatus.PENDING, + # ) + + try: + # Вызываем базовый метод update с проверками + updated_comment = Comment.update( + session=db.session, + id=uuid, + check_empty=["text"], # Запрещаем обновлять text на пустую строку + **update_data + ) + except HTTPException as e: + # Перехватываем ошибки из базового метода + raise UpdateError(msg=e.detail) from e + + return CommentGet.model_validate(updated_comment) +''' + @comment.delete("/{uuid}", response_model=StatusResponseModel) async def delete_comment( uuid: UUID, _=Depends(UnionAuth(scopes=["rating.comment.delete"], allow_none=False, auto_error=True)) From 411afa888a6aa541e6008131c39c2d31568451ff Mon Sep 17 00:00:00 2001 From: moschenkovjas Date: Tue, 29 Apr 2025 20:40:57 +0000 Subject: [PATCH 2/5] update method --- rating_api/models/base.py | 25 +++++++++++++++++-------- tests/test_routes/test_comment.py | 12 ++++++------ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/rating_api/models/base.py b/rating_api/models/base.py index 823111d..8a5e985 100644 --- a/rating_api/models/base.py +++ b/rating_api/models/base.py @@ -62,18 +62,27 @@ def get(cls, id: int | str, *, with_deleted=False, session: Session) -> BaseDbMo def update(cls, id: int | str, *, session: Session, check_empty: list[str] = None, **kwargs) -> BaseDbModel: obj = cls.get(id, session=session) - if not kwargs: - raise UpdateError(msg="Provide any parametr.") # 409 - # raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="No fields provided for update.") + # Технические поля не проверяются при update комментария + technical_fields = {'update_ts', 'review_status'} - unchanged_fields = [] + # for field in kwargs.items(): + # if field in technical_fields: + # continue + + # raise UpdateError(msg="Provide any parametr.") # 409 + # # raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="No fields provided for update.") + + # Проверка на изменение полей + changed_fields = False for field, new_value in kwargs.items(): + old_value = getattr(obj, field) - if old_value == new_value: - unchanged_fields.append(field) + if old_value != new_value and not field in technical_fields: + changed_fields = True + break - if unchanged_fields: - raise UpdateError(msg=f"No changes detected in fields: {', '.join(unchanged_fields)}.") + if not changed_fields: + raise UpdateError(msg=f"No changes detected in fields") # raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=f"No changes detected in fields") for k, v in kwargs.items(): diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index eaeaae4..cdd49c7 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -254,13 +254,13 @@ def test_comments_by_user_id(client, lecturers_with_comments, user_id, response_ ], ) def test_review_comment(client, dbsession, unreviewed_comment, comment, review_status, response_status, is_reviewed): - commment_to_reivew = comment if is_reviewed else unreviewed_comment + commment_to_review = comment if is_reviewed else unreviewed_comment query = {"review_status": review_status} - response = client.patch(f"{url}/{commment_to_reivew.uuid}/review", params=query) + response = client.patch(f"{url}/{commment_to_review.uuid}/review", params=query) assert response.status_code == response_status if response.status_code == status.HTTP_200_OK: - dbsession.refresh(commment_to_reivew) - assert commment_to_reivew.review_status == ReviewStatus(review_status) + dbsession.refresh(commment_to_review) + assert commment_to_review.review_status == ReviewStatus(review_status) @pytest.mark.parametrize( @@ -319,7 +319,7 @@ def test_review_comment(client, dbsession, unreviewed_comment, comment, review_s }, status.HTTP_409_CONFLICT, ), - ( # НЕизмененным перелано одно поле + ( # НЕизмененным передано одно поле { "subject": "asf", "text": "asf", @@ -327,7 +327,7 @@ def test_review_comment(client, dbsession, unreviewed_comment, comment, review_s "mark_clarity": 2, "mark_freebie": 1, }, - status.HTTP_409_CONFLICT, + status.HTTP_200_OK, ), ], ) From d6387f2e673f20f37629ded78f9f6b7540760a24 Mon Sep 17 00:00:00 2001 From: moschenkovjas Date: Tue, 29 Apr 2025 20:50:02 +0000 Subject: [PATCH 3/5] =?UTF-8?q?=D0=BF=D0=BE=D1=87=D0=B8=D1=89=D0=B5=D0=BD?= =?UTF-8?q?=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating_api/models/base.py | 7 ----- rating_api/routes/comment.py | 54 ------------------------------------ 2 files changed, 61 deletions(-) diff --git a/rating_api/models/base.py b/rating_api/models/base.py index 8a5e985..90786a1 100644 --- a/rating_api/models/base.py +++ b/rating_api/models/base.py @@ -65,13 +65,6 @@ def update(cls, id: int | str, *, session: Session, check_empty: list[str] = Non # Технические поля не проверяются при update комментария technical_fields = {'update_ts', 'review_status'} - # for field in kwargs.items(): - # if field in technical_fields: - # continue - - # raise UpdateError(msg="Provide any parametr.") # 409 - # # raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="No fields provided for update.") - # Проверка на изменение полей changed_fields = False for field, new_value in kwargs.items(): diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index a06e0bd..d227c3c 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -283,60 +283,6 @@ async def update_comment( return CommentGet.model_validate(updated_comment) - -''' -@comment.patch("/{uuid}", response_model=CommentGet) -async def update_comment(uuid: UUID, comment_update: CommentUpdate, user=Depends(UnionAuth())) -> CommentGet: - """Позволяет изменить свой неанонимный комментарий""" - comment: Comment = Comment.get(session=db.session, id=uuid) # Ошибка, если не найден - - if comment.user_id != user.get("id") or comment.user_id is None: - raise ForbiddenAction(Comment) - - # Получаем только переданные для обновления поля - update_data = comment_update.model_dump(exclude_unset=True) - - # Добавляем технические поля - update_data["update_ts"] = datetime.utcnow() - update_data["review_status"] = ReviewStatus.PENDING - - # # Если не передано ни одного параметра - # if not update_data: - # raise UpdateError(msg="Provide any parametr.") - # # raise HTTPException(status_code=409, detail="Provide any parametr") # 409 - - # # Проверяем, есть ли неизмененные поля - # current_data = {key: getattr(comment, key) for key in update_data} # Берем текущие значения из БД - # unchanged_fields = {k for k, v in update_data.items() if current_data.get(k) == v} - - # if unchanged_fields: - # raise UpdateError(msg=f"No changes detected in fields: {', '.join(unchanged_fields)}.") - # # raise HTTPException(status_code=409, detail=f"No changes detected in fields: {', '.join(unchanged_fields)}") - - # # Обновление комментария - # updated_comment = Comment.update( - # session=db.session, - # id=uuid, - # **update_data, - # update_ts=datetime.datetime.utcnow(), - # review_status=ReviewStatus.PENDING, - # ) - - try: - # Вызываем базовый метод update с проверками - updated_comment = Comment.update( - session=db.session, - id=uuid, - check_empty=["text"], # Запрещаем обновлять text на пустую строку - **update_data - ) - except HTTPException as e: - # Перехватываем ошибки из базового метода - raise UpdateError(msg=e.detail) from e - - return CommentGet.model_validate(updated_comment) -''' - @comment.delete("/{uuid}", response_model=StatusResponseModel) async def delete_comment( uuid: UUID, _=Depends(UnionAuth(scopes=["rating.comment.delete"], allow_none=False, auto_error=True)) From 0afaef57aa231139efd28f1fb6efbc79af9bbc9a Mon Sep 17 00:00:00 2001 From: moschenkovjas Date: Tue, 29 Apr 2025 20:55:18 +0000 Subject: [PATCH 4/5] linting --- rating_api/models/base.py | 4 ++-- rating_api/routes/comment.py | 8 +++----- rating_api/settings.py | 4 +--- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/rating_api/models/base.py b/rating_api/models/base.py index 90786a1..f427405 100644 --- a/rating_api/models/base.py +++ b/rating_api/models/base.py @@ -68,12 +68,12 @@ def update(cls, id: int | str, *, session: Session, check_empty: list[str] = Non # Проверка на изменение полей changed_fields = False for field, new_value in kwargs.items(): - + old_value = getattr(obj, field) if old_value != new_value and not field in technical_fields: changed_fields = True break - + if not changed_fields: raise UpdateError(msg=f"No changes detected in fields") # raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=f"No changes detected in fields") diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index d227c3c..fff7407 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -256,12 +256,9 @@ async def review_comment( Comment.update(session=db.session, id=uuid, review_status=review_status, approved_by=user.get("id")) ) + @comment.patch("/{uuid}", response_model=CommentGet) -async def update_comment( - uuid: UUID, - comment_update: CommentUpdate, - user=Depends(UnionAuth()) -) -> CommentGet: +async def update_comment(uuid: UUID, comment_update: CommentUpdate, user=Depends(UnionAuth())) -> CommentGet: """Позволяет изменить свой неанонимный комментарий""" comment: Comment = Comment.get(session=db.session, id=uuid) # Ошибка, если не найден @@ -283,6 +280,7 @@ async def update_comment( return CommentGet.model_validate(updated_comment) + @comment.delete("/{uuid}", response_model=StatusResponseModel) async def delete_comment( uuid: UUID, _=Depends(UnionAuth(scopes=["rating.comment.delete"], allow_none=False, auto_error=True)) diff --git a/rating_api/settings.py b/rating_api/settings.py index 27b3873..a5baeca 100644 --- a/rating_api/settings.py +++ b/rating_api/settings.py @@ -27,9 +27,7 @@ class Settings(BaseSettings): CORS_ALLOW_METHODS: list[str] = ['*'] CORS_ALLOW_HEADERS: list[str] = ['*'] MAX_COMMENT_LENGTH: int = 3000 - LOGGING_MARKETING_URL: str = LOGGING_MARKETING_URLS.get( - os.getenv("APP_ENV", "dev"), LOGGING_MARKETING_URLS["test"] - ) + LOGGING_MARKETING_URL: str = LOGGING_MARKETING_URLS.get(os.getenv("APP_ENV", "dev"), LOGGING_MARKETING_URLS["test"]) '''Temp settings''' From 6cf419fa88b0f129713584a4f5ca6982c967805e Mon Sep 17 00:00:00 2001 From: moschenkovjas Date: Tue, 29 Apr 2025 21:16:08 +0000 Subject: [PATCH 5/5] comms --- rating_api/models/base.py | 2 +- rating_api/routes/comment.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rating_api/models/base.py b/rating_api/models/base.py index f427405..932ac56 100644 --- a/rating_api/models/base.py +++ b/rating_api/models/base.py @@ -59,7 +59,7 @@ def get(cls, id: int | str, *, with_deleted=False, session: Session) -> BaseDbMo raise ObjectNotFound(cls, id) @classmethod - def update(cls, id: int | str, *, session: Session, check_empty: list[str] = None, **kwargs) -> BaseDbModel: + def update(cls, id: int | str, *, session: Session, **kwargs) -> BaseDbModel: obj = cls.get(id, session=session) # Технические поля не проверяются при update комментария diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 9ab2f38..e6f5dcb 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -268,11 +268,10 @@ async def update_comment(uuid: UUID, comment_update: CommentUpdate, user=Depends # Получаем только переданные для обновления поля update_data = comment_update.model_dump(exclude_unset=True) - # Обновляем комментарий (проверки уже в базовом update()) + # Обновляем комментарий updated_comment = Comment.update( session=db.session, id=uuid, - check_empty=["text"], # Запрещаем обновлять `text` на пустую строку **update_data, update_ts=datetime.datetime.utcnow(), review_status=ReviewStatus.PENDING,