From 9b6be87f99bd1801b075c567e621fd234eacca6b Mon Sep 17 00:00:00 2001 From: pivovarovma Date: Tue, 15 Apr 2025 19:07:49 +0000 Subject: [PATCH 1/9] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B0=20=D1=81=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B0=D1=82=D1=8C=20=D1=80=D0=B0=D1=81=D1=88=D0=B8=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=83=D1=8E=20=D1=81=D0=BE=D1=80=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating_api/routes/comment.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 137953d..e2eff2a 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -173,8 +173,9 @@ async def get_comments( offset: int = 0, lecturer_id: int | None = None, user_id: int | None = None, - order_by: list[Literal["create_ts"]] = Query(default=[]), + order_by: list[Literal["create_ts", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"]] = Query(default=[]), unreviewed: bool = False, + asc_order: bool = False, user=Depends(UnionAuth(scopes=["rating.comment.review"], auto_error=False, allow_none=True)), ) -> CommentGetAll: """ @@ -186,13 +187,17 @@ async def get_comments( Если без смещения возвращается комментарий с условным номером N, то при значении offset = X будет возвращаться комментарий с номером N + X - `order_by` - возможное значение `'create_ts'` - возвращается список комментариев отсортированных по времени создания + `order_by` - возможные значения `"create_ts", "mark_weighted", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"`. + Если передано `'create_ts'` - возвращается список преподавателей отсортированных по времени + Если передано `'mark_...'` - возвращается список преподавателей отсортированных по конкретной оценке `lecturer_id` - вернет все комментарии для преподавателя с конкретным id, по дефолту возвращает вообще все аппрувнутые комментарии. `user_id` - вернет все комментарии пользователя с конкретным id `unreviewed` - вернет все непроверенные комментарии, если True. По дефолту False. + + `asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания """ comments = Comment.query(session=db.session).all() if not comments: @@ -206,6 +211,7 @@ async def get_comments( else: result = CommentGetAll(limit=limit, offset=offset, total=len(comments)) comment_validator = CommentGet + result.comments = comments if user_id is not None: result.comments = [comment for comment in result.comments if comment.user_id == user_id] @@ -230,10 +236,23 @@ async def get_comments( result.comments = result.comments[offset : limit + offset] if "create_ts" in order_by: - result.comments.sort(key=lambda comment: comment.create_ts, reverse=True) + result.comments.sort(key=lambda comment: comment.create_ts, reverse=asc_order) + + if "mark_kindness" in order_by: + result.comments.sort(key=lambda comment: comment.mark_kindness, reverse=asc_order) + + if "mark_freebie" in order_by: + result.comments.sort(key=lambda comment: comment.mark_freebie, reverse=asc_order) + + if "mark_clarity" in order_by: + result.comments.sort(key=lambda comment: comment.mark_clarity, reverse=asc_order) + + if "mark_general" in order_by: + result.comments.sort(key=lambda comment: comment.mark_general, reverse=asc_order) + result.total = len(result.comments) result.comments = [comment_validator.model_validate(comment) for comment in result.comments] - result.comments.sort(key=lambda comment: comment.create_ts, reverse=True) + return result From c72d70b339a8a1b8e9e852c31903db931e4dae9d Mon Sep 17 00:00:00 2001 From: pivovarovma Date: Tue, 15 Apr 2025 19:26:03 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B1=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20=D0=BF=D1=83=D1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating_api/routes/comment.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index e2eff2a..c52980a 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -173,31 +173,33 @@ async def get_comments( offset: int = 0, lecturer_id: int | None = None, user_id: int | None = None, - order_by: list[Literal["create_ts", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"]] = Query(default=[]), + order_by: list[Literal["create_ts", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"]] = Query( + default=[] + ), unreviewed: bool = False, asc_order: bool = False, user=Depends(UnionAuth(scopes=["rating.comment.review"], auto_error=False, allow_none=True)), ) -> CommentGetAll: """ - Scopes: `["rating.comment.review"]` + Scopes: `["rating.comment.review"]` - `limit` - максимальное количество возвращаемых комментариев + `limit` - максимальное количество возвращаемых комментариев - `offset` - смещение, определяющее, с какого по порядку комментария начинать выборку. - Если без смещения возвращается комментарий с условным номером N, - то при значении offset = X будет возвращаться комментарий с номером N + X + `offset` - смещение, определяющее, с какого по порядку комментария начинать выборку. + Если без смещения возвращается комментарий с условным номером N, + то при значении offset = X будет возвращаться комментарий с номером N + X - `order_by` - возможные значения `"create_ts", "mark_weighted", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"`. - Если передано `'create_ts'` - возвращается список преподавателей отсортированных по времени - Если передано `'mark_...'` - возвращается список преподавателей отсортированных по конкретной оценке + `order_by` - возможные значения `"create_ts", "mark_weighted", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"`. + Если передано `'create_ts'` - возвращается список преподавателей отсортированных по времени + Если передано `'mark_...'` - возвращается список преподавателей отсортированных по конкретной оценке - `lecturer_id` - вернет все комментарии для преподавателя с конкретным id, по дефолту возвращает вообще все аппрувнутые комментарии. + `lecturer_id` - вернет все комментарии для преподавателя с конкретным id, по дефолту возвращает вообще все аппрувнутые комментарии. - `user_id` - вернет все комментарии пользователя с конкретным id + `user_id` - вернет все комментарии пользователя с конкретным id - `unreviewed` - вернет все непроверенные комментарии, если True. По дефолту False. + `unreviewed` - вернет все непроверенные комментарии, если True. По дефолту False. - `asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания + `asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания """ comments = Comment.query(session=db.session).all() if not comments: @@ -243,10 +245,10 @@ async def get_comments( if "mark_freebie" in order_by: result.comments.sort(key=lambda comment: comment.mark_freebie, reverse=asc_order) - + if "mark_clarity" in order_by: result.comments.sort(key=lambda comment: comment.mark_clarity, reverse=asc_order) - + if "mark_general" in order_by: result.comments.sort(key=lambda comment: comment.mark_general, reverse=asc_order) From 5dab7bd346b82b0d044a3b3dc6edc0b2f416839f Mon Sep 17 00:00:00 2001 From: pivovarovma Date: Sat, 19 Apr 2025 18:54:02 +0000 Subject: [PATCH 3/9] =?UTF-8?q?=D0=92=D1=82=D0=BE=D1=80=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating_api/models/db.py | 28 ++++++++++++++++++++++ rating_api/routes/comment.py | 46 ++++++++++++++---------------------- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/rating_api/models/db.py b/rating_api/models/db.py index 2f01068..821a322 100644 --- a/rating_api/models/db.py +++ b/rating_api/models/db.py @@ -123,6 +123,34 @@ class Comment(BaseDbModel): def mark_general(self): return (self.mark_kindness + self.mark_freebie + self.mark_clarity) / 3 + @hybrid_method + def order_by_create_ts( + self, query: str, asc_order: bool + ) -> tuple[UnaryExpression[datetime.datetime] | InstrumentedAttribute]: + return (getattr(Comment, query) if asc_order else getattr(Comment, query).desc), Comment.user_id + + @hybrid_method + def order_by_mark( + self, query: str, asc_order: bool + ) -> tuple[UnaryExpression[float], InstrumentedAttribute, InstrumentedAttribute]: + expression = func(getattr(Comment, query)).filter(Comment.review_status == ReviewStatus.APPROVED) + if not asc_order: + expression = expression.desc() + return nulls_last(expression), Comment.user_id + + @hybrid_method + def search_by_lectorer_id(self, query: int) -> bool: + response = true + if query: + response = and_(Comment.review_status == ReviewStatus.APPROVED, func(Comment.lecturer_id).contains(query)) + return response + + @hybrid_method + def search_by_user_id(self, query: int) -> bool: + response = true + if query: + response = func(Comment.user_id).contains(query) + return response class LecturerUserComment(BaseDbModel): id: Mapped[int] = mapped_column(Integer, primary_key=True) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index c52980a..b1dda32 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -173,8 +173,9 @@ async def get_comments( offset: int = 0, lecturer_id: int | None = None, user_id: int | None = None, - order_by: list[Literal["create_ts", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"]] = Query( - default=[] + order_by: str = Query( + enum=["create_ts", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"], + default=None, ), unreviewed: bool = False, asc_order: bool = False, @@ -189,9 +190,9 @@ async def get_comments( Если без смещения возвращается комментарий с условным номером N, то при значении offset = X будет возвращаться комментарий с номером N + X - `order_by` - возможные значения `"create_ts", "mark_weighted", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"`. - Если передано `'create_ts'` - возвращается список преподавателей отсортированных по времени - Если передано `'mark_...'` - возвращается список преподавателей отсортированных по конкретной оценке + `order_by` - возможные значения `"create_ts", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"`. + Если передано `'create_ts'` - возвращается список комментариев отсортированных по времени + Если передано `'mark_...'` - возвращается список комментариев отсортированных по конкретной оценке `lecturer_id` - вернет все комментарии для преподавателя с конкретным id, по дефолту возвращает вообще все аппрувнутые комментарии. @@ -201,7 +202,18 @@ async def get_comments( `asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания """ - comments = Comment.query(session=db.session).all() + comment_query = ( + Comment.query(session=db.session) + .filter(Comment.search_by_lectorer_id(lecturer_id)) + .filter(Comment.search_by_user_id(user_id)) + .order_by(*(Comment.order_by_mark(order_by, asc_order) + if "mark" in order_by + else Comment.order_by_create_ts(order_by, asc_order) + ) + ) + ) + comments = comment_query.offset(offset).limit(limit).all() + if not comments: raise ObjectNotFound(Comment, 'all') if "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: @@ -214,13 +226,6 @@ async def get_comments( result = CommentGetAll(limit=limit, offset=offset, total=len(comments)) comment_validator = CommentGet - result.comments = comments - if user_id is not None: - result.comments = [comment for comment in result.comments if comment.user_id == user_id] - - if lecturer_id is not None: - result.comments = [comment for comment in result.comments if comment.lecturer_id == lecturer_id] - if unreviewed: if not user: raise ForbiddenAction(Comment) @@ -237,21 +242,6 @@ async def get_comments( result.comments = result.comments[offset : limit + offset] - if "create_ts" in order_by: - result.comments.sort(key=lambda comment: comment.create_ts, reverse=asc_order) - - if "mark_kindness" in order_by: - result.comments.sort(key=lambda comment: comment.mark_kindness, reverse=asc_order) - - if "mark_freebie" in order_by: - result.comments.sort(key=lambda comment: comment.mark_freebie, reverse=asc_order) - - if "mark_clarity" in order_by: - result.comments.sort(key=lambda comment: comment.mark_clarity, reverse=asc_order) - - if "mark_general" in order_by: - result.comments.sort(key=lambda comment: comment.mark_general, reverse=asc_order) - result.total = len(result.comments) result.comments = [comment_validator.model_validate(comment) for comment in result.comments] From 52bcca694631808ab77e07a5bbf217aa17b59151 Mon Sep 17 00:00:00 2001 From: pivovarovma Date: Mon, 21 Apr 2025 20:37:19 +0000 Subject: [PATCH 4/9] =?UTF-8?q?=D0=95=D1=89=D0=B5=20=D0=BE=D0=B4=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B0...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating_api/models/db.py | 20 +++++++++----------- rating_api/routes/comment.py | 11 +++++++---- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/rating_api/models/db.py b/rating_api/models/db.py index 821a322..dbd2813 100644 --- a/rating_api/models/db.py +++ b/rating_api/models/db.py @@ -8,7 +8,7 @@ from fastapi_sqlalchemy import db from sqlalchemy import UUID, Boolean, DateTime from sqlalchemy import Enum as DbEnum -from sqlalchemy import ForeignKey, Integer, String, UnaryExpression, and_, func, nulls_last, or_, true +from sqlalchemy import ForeignKey, Integer, String, UnaryExpression, and_, desc, func, nulls_last, or_, true from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm.attributes import InstrumentedAttribute @@ -126,25 +126,22 @@ def mark_general(self): @hybrid_method def order_by_create_ts( self, query: str, asc_order: bool - ) -> tuple[UnaryExpression[datetime.datetime] | InstrumentedAttribute]: - return (getattr(Comment, query) if asc_order else getattr(Comment, query).desc), Comment.user_id - + ) -> tuple[UnaryExpression[datetime.datetime] | InstrumentedAttribute, InstrumentedAttribute]: + return getattr(Comment, query) if asc_order else desc(getattr(Comment, query)), Comment.user_id + @hybrid_method def order_by_mark( self, query: str, asc_order: bool - ) -> tuple[UnaryExpression[float], InstrumentedAttribute, InstrumentedAttribute]: - expression = func(getattr(Comment, query)).filter(Comment.review_status == ReviewStatus.APPROVED) - if not asc_order: - expression = expression.desc() - return nulls_last(expression), Comment.user_id - + ) -> tuple[UnaryExpression[float] | InstrumentedAttribute, InstrumentedAttribute]: + return getattr(Comment, query) if asc_order else desc(getattr(Comment, query)), Comment.user_id + @hybrid_method def search_by_lectorer_id(self, query: int) -> bool: response = true if query: response = and_(Comment.review_status == ReviewStatus.APPROVED, func(Comment.lecturer_id).contains(query)) return response - + @hybrid_method def search_by_user_id(self, query: int) -> bool: response = true @@ -152,6 +149,7 @@ def search_by_user_id(self, query: int) -> bool: response = func(Comment.user_id).contains(query) return response + class LecturerUserComment(BaseDbModel): id: Mapped[int] = mapped_column(Integer, primary_key=True) user_id: Mapped[int] = mapped_column(Integer, nullable=False) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index b1dda32..5c6b42e 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -175,7 +175,7 @@ async def get_comments( user_id: int | None = None, order_by: str = Query( enum=["create_ts", "mark_kindness", "mark_freebie", "mark_clarity", "mark_general"], - default=None, + default="create_ts", ), unreviewed: bool = False, asc_order: bool = False, @@ -206,12 +206,15 @@ async def get_comments( Comment.query(session=db.session) .filter(Comment.search_by_lectorer_id(lecturer_id)) .filter(Comment.search_by_user_id(user_id)) - .order_by(*(Comment.order_by_mark(order_by, asc_order) - if "mark" in order_by - else Comment.order_by_create_ts(order_by, asc_order) + .order_by( + *( + Comment.order_by_create_ts(order_by, asc_order) + if "mark" in order_by + else Comment.order_by_mark(order_by, asc_order) ) ) ) + comments = comment_query.offset(offset).limit(limit).all() if not comments: From 2956011618acc410fcff01dc73def019e9cb6663 Mon Sep 17 00:00:00 2001 From: pivovarovma Date: Mon, 28 Apr 2025 18:42:08 +0000 Subject: [PATCH 5/9] =?UTF-8?q?3=20=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/1c001709fc55_advanced_sort.py | 32 +++++++++++++++++++ rating_api/models/db.py | 2 +- rating_api/routes/comment.py | 10 +++--- tests/conftest.py | 3 ++ tests/test_routes/test_comment.py | 1 + 5 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 migrations/versions/1c001709fc55_advanced_sort.py diff --git a/migrations/versions/1c001709fc55_advanced_sort.py b/migrations/versions/1c001709fc55_advanced_sort.py new file mode 100644 index 0000000..0f6eb04 --- /dev/null +++ b/migrations/versions/1c001709fc55_advanced_sort.py @@ -0,0 +1,32 @@ +"""advanced_sort + +Revision ID: 1c001709fc55 +Revises: dd44854aa12a +Create Date: 2025-04-26 17:01:57.140143 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '1c001709fc55' +down_revision = 'dd44854aa12a' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('comment', 'approved_by', + existing_type=sa.INTEGER(), + nullable=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('comment', 'approved_by', + existing_type=sa.INTEGER(), + nullable=False) + # ### end Alembic commands ### diff --git a/rating_api/models/db.py b/rating_api/models/db.py index dbd2813..ce7ffe9 100644 --- a/rating_api/models/db.py +++ b/rating_api/models/db.py @@ -139,7 +139,7 @@ def order_by_mark( def search_by_lectorer_id(self, query: int) -> bool: response = true if query: - response = and_(Comment.review_status == ReviewStatus.APPROVED, func(Comment.lecturer_id).contains(query)) + response = and_(Comment.review_status == ReviewStatus.APPROVED, Comment.lecturer_id == query) return response @hybrid_method diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 5c6b42e..235e403 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -179,7 +179,7 @@ async def get_comments( ), unreviewed: bool = False, asc_order: bool = False, - user=Depends(UnionAuth(scopes=["rating.comment.review"], auto_error=False, allow_none=True)), + user=Depends(UnionAuth(scopes=["rating.comment.review"], auto_error=False, allow_none=False)), ) -> CommentGetAll: """ Scopes: `["rating.comment.review"]` @@ -202,7 +202,7 @@ async def get_comments( `asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания """ - comment_query = ( + comments = ( Comment.query(session=db.session) .filter(Comment.search_by_lectorer_id(lecturer_id)) .filter(Comment.search_by_user_id(user_id)) @@ -213,10 +213,11 @@ async def get_comments( else Comment.order_by_mark(order_by, asc_order) ) ) + .limit(limit) + .offset(offset) + .all() ) - comments = comment_query.offset(offset).limit(limit).all() - if not comments: raise ObjectNotFound(Comment, 'all') if "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: @@ -242,7 +243,6 @@ async def get_comments( raise ForbiddenAction(Comment) else: result.comments = [comment for comment in result.comments if comment.review_status is ReviewStatus.APPROVED] - result.comments = result.comments[offset : limit + offset] result.total = len(result.comments) diff --git a/tests/conftest.py b/tests/conftest.py index f5f6378..5791f5b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -46,11 +46,14 @@ def lecturer(dbsession): @pytest.fixture def comment(dbsession, lecturer): _comment = Comment( + user_id=0, + create_ts="2025-04-25T19:38:56.408Z", subject="test_subject", text="test_comment", mark_kindness=1, mark_clarity=1, mark_freebie=1, + mark_general=1, lecturer_id=lecturer.id, review_status=ReviewStatus.APPROVED, ) diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index eaeaae4..c2a3e27 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -198,6 +198,7 @@ def test_create_comment(client, dbsession, lecturers, body, lecturer_n, response def test_get_comment(client, comment): response_comment = client.get(f'{url}/{comment.uuid}') + print("1") assert response_comment.status_code == status.HTTP_200_OK random_uuid = uuid.uuid4() response = client.get(f'{url}/{random_uuid}') From 6bcb83d72121bcbe73298f9c99424dbbf466fb81 Mon Sep 17 00:00:00 2001 From: pivovarovma Date: Mon, 28 Apr 2025 19:26:31 +0000 Subject: [PATCH 6/9] =?UTF-8?q?=D0=9D=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating_api/models/db.py | 2 +- rating_api/routes/comment.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rating_api/models/db.py b/rating_api/models/db.py index ce7ffe9..290075b 100644 --- a/rating_api/models/db.py +++ b/rating_api/models/db.py @@ -146,7 +146,7 @@ def search_by_lectorer_id(self, query: int) -> bool: def search_by_user_id(self, query: int) -> bool: response = true if query: - response = func(Comment.user_id).contains(query) + response = and_(Comment.user_id == query) return response diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 235e403..2a64aa6 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -202,7 +202,7 @@ async def get_comments( `asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания """ - comments = ( + comments_query = ( Comment.query(session=db.session) .filter(Comment.search_by_lectorer_id(lecturer_id)) .filter(Comment.search_by_user_id(user_id)) @@ -213,14 +213,13 @@ async def get_comments( else Comment.order_by_mark(order_by, asc_order) ) ) - .limit(limit) - .offset(offset) - .all() ) - + + comments = comments_query.limit(limit).offset(offset).all() + if not comments: raise ObjectNotFound(Comment, 'all') - if "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: + if user and "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: result = CommentGetAllWithAllInfo(limit=limit, offset=offset, total=len(comments)) comment_validator = CommentGetWithAllInfo elif user.get('id') == user_id: @@ -230,6 +229,8 @@ async def get_comments( result = CommentGetAll(limit=limit, offset=offset, total=len(comments)) comment_validator = CommentGet + result.comments = comments + if unreviewed: if not user: raise ForbiddenAction(Comment) From ddc3bcb2e1688cd268f9b084a968b2ee50afe4c9 Mon Sep 17 00:00:00 2001 From: MikhailPI1 Date: Fri, 2 May 2025 18:24:59 +0300 Subject: [PATCH 7/9] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/1c001709fc55_advanced_sort.py | 16 ++++--------- rating_api/models/db.py | 24 ++++++++----------- rating_api/routes/comment.py | 18 +++++++------- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/migrations/versions/1c001709fc55_advanced_sort.py b/migrations/versions/1c001709fc55_advanced_sort.py index 0f6eb04..8238980 100644 --- a/migrations/versions/1c001709fc55_advanced_sort.py +++ b/migrations/versions/1c001709fc55_advanced_sort.py @@ -5,11 +5,11 @@ Create Date: 2025-04-26 17:01:57.140143 """ -from alembic import op + import sqlalchemy as sa +from alembic import op -# revision identifiers, used by Alembic. revision = '1c001709fc55' down_revision = 'dd44854aa12a' branch_labels = None @@ -17,16 +17,8 @@ def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('comment', 'approved_by', - existing_type=sa.INTEGER(), - nullable=True) - # ### end Alembic commands ### + op.alter_column('comment', 'approved_by', existing_type=sa.INTEGER(), nullable=True) def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('comment', 'approved_by', - existing_type=sa.INTEGER(), - nullable=False) - # ### end Alembic commands ### + op.alter_column('comment', 'approved_by', existing_type=sa.INTEGER(), nullable=False) diff --git a/rating_api/models/db.py b/rating_api/models/db.py index 290075b..28b75fa 100644 --- a/rating_api/models/db.py +++ b/rating_api/models/db.py @@ -126,28 +126,24 @@ def mark_general(self): @hybrid_method def order_by_create_ts( self, query: str, asc_order: bool - ) -> tuple[UnaryExpression[datetime.datetime] | InstrumentedAttribute, InstrumentedAttribute]: - return getattr(Comment, query) if asc_order else desc(getattr(Comment, query)), Comment.user_id + ) -> UnaryExpression[datetime.datetime] | InstrumentedAttribute: + return getattr(Comment, query) if asc_order else desc(getattr(Comment, query)) @hybrid_method - def order_by_mark( - self, query: str, asc_order: bool - ) -> tuple[UnaryExpression[float] | InstrumentedAttribute, InstrumentedAttribute]: - return getattr(Comment, query) if asc_order else desc(getattr(Comment, query)), Comment.user_id + def order_by_mark(self, query: str, asc_order: bool) -> UnaryExpression[float] | InstrumentedAttribute: + return getattr(Comment, query) if asc_order else desc(getattr(Comment, query)) @hybrid_method def search_by_lectorer_id(self, query: int) -> bool: - response = true - if query: - response = and_(Comment.review_status == ReviewStatus.APPROVED, Comment.lecturer_id == query) - return response + if not query: + return true() + return and_(Comment.review_status == ReviewStatus.APPROVED, Comment.lecturer_id == query) @hybrid_method def search_by_user_id(self, query: int) -> bool: - response = true - if query: - response = and_(Comment.user_id == query) - return response + if not query: + return true() + return Comment.user_id == query class LecturerUserComment(BaseDbModel): diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 2a64aa6..775d39f 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -207,19 +207,18 @@ async def get_comments( .filter(Comment.search_by_lectorer_id(lecturer_id)) .filter(Comment.search_by_user_id(user_id)) .order_by( - *( - Comment.order_by_create_ts(order_by, asc_order) - if "mark" in order_by - else Comment.order_by_mark(order_by, asc_order) - ) + Comment.order_by_create_ts(order_by, asc_order) + if "mark" in order_by + else Comment.order_by_mark(order_by, asc_order) ) ) - + + print(comments_query.statement.compile()) comments = comments_query.limit(limit).offset(offset).all() - + if not comments: raise ObjectNotFound(Comment, 'all') - if user and "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: + if "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: result = CommentGetAllWithAllInfo(limit=limit, offset=offset, total=len(comments)) comment_validator = CommentGetWithAllInfo elif user.get('id') == user_id: @@ -230,7 +229,7 @@ async def get_comments( comment_validator = CommentGet result.comments = comments - + if unreviewed: if not user: raise ForbiddenAction(Comment) @@ -244,7 +243,6 @@ async def get_comments( raise ForbiddenAction(Comment) else: result.comments = [comment for comment in result.comments if comment.review_status is ReviewStatus.APPROVED] - result.comments = result.comments[offset : limit + offset] result.total = len(result.comments) result.comments = [comment_validator.model_validate(comment) for comment in result.comments] From 6b5d91ad7a939c040b31d9aaa468c6d69cb62285 Mon Sep 17 00:00:00 2001 From: MikhailPI1 Date: Fri, 2 May 2025 22:32:41 +0300 Subject: [PATCH 8/9] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20order=5Fby?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rating_api/routes/comment.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index 775d39f..b6b4440 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -207,13 +207,12 @@ async def get_comments( .filter(Comment.search_by_lectorer_id(lecturer_id)) .filter(Comment.search_by_user_id(user_id)) .order_by( - Comment.order_by_create_ts(order_by, asc_order) + Comment.order_by_mark(order_by, asc_order) if "mark" in order_by - else Comment.order_by_mark(order_by, asc_order) + else Comment.order_by_create_ts(order_by, asc_order) ) ) - print(comments_query.statement.compile()) comments = comments_query.limit(limit).offset(offset).all() if not comments: From 6f44efb378d598bb0c19553a863660d6f4a41ac8 Mon Sep 17 00:00:00 2001 From: Zimovchik <63729114+Zimovchik@users.noreply.github.com> Date: Sat, 3 May 2025 10:13:51 +0300 Subject: [PATCH 9/9] fix tests --- rating_api/routes/comment.py | 5 ++--- tests/conftest.py | 14 +++++++++----- tests/test_routes/test_comment.py | 13 ++++++++----- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/rating_api/routes/comment.py b/rating_api/routes/comment.py index b6b4440..b17ffa0 100644 --- a/rating_api/routes/comment.py +++ b/rating_api/routes/comment.py @@ -214,13 +214,12 @@ async def get_comments( ) comments = comments_query.limit(limit).offset(offset).all() - if not comments: raise ObjectNotFound(Comment, 'all') - if "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: + if user and "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]: result = CommentGetAllWithAllInfo(limit=limit, offset=offset, total=len(comments)) comment_validator = CommentGetWithAllInfo - elif user.get('id') == user_id: + elif user and user.get('id') == user_id: result = CommentGetAllWithStatus(limit=limit, offset=offset, total=len(comments)) comment_validator = CommentGetWithStatus else: diff --git a/tests/conftest.py b/tests/conftest.py index 5791f5b..cfc960f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -53,7 +53,6 @@ def comment(dbsession, lecturer): mark_kindness=1, mark_clarity=1, mark_freebie=1, - mark_general=1, lecturer_id=lecturer.id, review_status=ReviewStatus.APPROVED, ) @@ -169,8 +168,9 @@ def lecturers_with_comments(dbsession, lecturers): (lecturers[2].id, 9992, 'test_subject12', ReviewStatus.APPROVED, 0, 0, 0), ] - comments = [ - Comment( + comments = [] + for lecturer_id, user_id, subject, review_status, mark_kindness, mark_clarity, mark_freebie in comments_data: + comment = Comment( subject=subject, text="test_comment", mark_kindness=mark_kindness, @@ -180,8 +180,12 @@ def lecturers_with_comments(dbsession, lecturers): user_id=user_id, review_status=review_status, ) - for lecturer_id, user_id, subject, review_status, mark_kindness, mark_clarity, mark_freebie in comments_data - ] + + # Set approved_by to -1 for approved or dismissed comments + if review_status in [ReviewStatus.APPROVED, ReviewStatus.DISMISSED]: + comment.approved_by = -1 + + comments.append(comment) dbsession.add_all(comments) dbsession.commit() diff --git a/tests/test_routes/test_comment.py b/tests/test_routes/test_comment.py index c2a3e27..79803ef 100644 --- a/tests/test_routes/test_comment.py +++ b/tests/test_routes/test_comment.py @@ -206,7 +206,8 @@ def test_get_comment(client, comment): @pytest.mark.parametrize( - 'lecturer_n,response_status', [(0, status.HTTP_200_OK), (1, status.HTTP_200_OK), (3, status.HTTP_200_OK)] + 'lecturer_n,response_status', + [(0, status.HTTP_200_OK), (1, status.HTTP_200_OK), (2, status.HTTP_200_OK), (3, status.HTTP_404_NOT_FOUND)], ) def test_comments_by_lecturer_id(client, lecturers_with_comments, lecturer_n, response_status): lecturers, comments = lecturers_with_comments @@ -217,8 +218,10 @@ def test_comments_by_lecturer_id(client, lecturers_with_comments, lecturer_n, re assert len(json_response["comments"]) == len( [ comment - for comment in lecturers[lecturer_n].comments - if comment.review_status == ReviewStatus.APPROVED and not comment.is_deleted + for comment in comments + if comment.lecturer_id == lecturers[lecturer_n].id + and comment.review_status == ReviewStatus.APPROVED + and not comment.is_deleted ] ) @@ -228,7 +231,7 @@ def test_comments_by_lecturer_id(client, lecturers_with_comments, lecturer_n, re ) def test_comments_by_user_id(client, lecturers_with_comments, user_id, response_status): _, comments = lecturers_with_comments - response = response = client.get(f'{url}', params={"user_id": user_id}) + response = client.get(f'{url}', params={"user_id": 9990 + user_id}) assert response.status_code == response_status if response.status_code == status.HTTP_200_OK: json_response = response.json() @@ -236,7 +239,7 @@ def test_comments_by_user_id(client, lecturers_with_comments, user_id, response_ [ comment for comment in comments - if comment.user_id == user_id + if comment.user_id == 9990 + user_id and comment.review_status == ReviewStatus.APPROVED and not comment.is_deleted ]