Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions rating_api/models/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,36 @@ def order_by_like_diff(cls, asc_order: bool = False):
else:
return cls.like_dislike_diff.desc()

@hybrid_method
def has_reaction(self, user_id: int, react: Reaction) -> bool:
return any(reaction.user_id == user_id and reaction.reaction == react for reaction in self.reactions)

@has_reaction.expression
def has_reaction(cls, user_id: int, react: Reaction):
return (
select([true()])
.where(
and_(
CommentReaction.comment_uuid == cls.uuid,
CommentReaction.user_id == user_id,
CommentReaction.reaction == react,
)
)
.exists()
)

@classmethod
def reactions_for_comments(cls, user_id: int, session, comments):
if not user_id or not comments:
return {}
comments_uuid = [c.uuid for c in comments]
reactions = (
session.query(CommentReaction)
.filter(CommentReaction.user_id == user_id, CommentReaction.comment_uuid.in_(comments_uuid))
.all()
)
return {r.comment_uuid: r.reaction for r in reactions}
Comment on lines +268 to +278
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ты можешь сделать это методами алхимии, групнув сджойненые таблички например и посмотрев 1 0 или -1



class LecturerUserComment(BaseDbModel):
id: Mapped[int] = mapped_column(Integer, primary_key=True)
Expand Down
48 changes: 41 additions & 7 deletions rating_api/routes/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
CommentGet,
CommentGetAll,
CommentGetAllWithAllInfo,
CommentGetAllWithLike,
CommentGetAllWithStatus,
CommentGetWithAllInfo,
CommentGetWithLike,
CommentGetWithStatus,
CommentImportAll,
CommentPost,
Expand Down Expand Up @@ -155,18 +157,25 @@ async def import_comments(
return result


@comment.get("/{uuid}", response_model=CommentGet)
async def get_comment(uuid: UUID) -> CommentGet:
@comment.get("/{uuid}", response_model=CommentGetWithLike)
async def get_comment(uuid: UUID, user=Depends(UnionAuth())) -> CommentGetWithLike:
"""
Возвращает комментарий по его UUID в базе данных RatingAPI
"""
comment: Comment = Comment.query(session=db.session).filter(Comment.uuid == uuid).one_or_none()
if comment is None:
raise ObjectNotFound(Comment, uuid)
return CommentGet.model_validate(comment)
base_data = CommentGet.model_validate(comment)
return CommentGetWithLike(
**base_data.model_dump(),
is_liked=comment.has_reaction(user.get("id"), Reaction.LIKE),
is_disliked=comment.has_reaction(user.get("id"), Reaction.DISLIKE),
)


@comment.get("", response_model=Union[CommentGetAll, CommentGetAllWithAllInfo, CommentGetAllWithStatus])
@comment.get(
"", response_model=Union[CommentGetAll, CommentGetAllWithLike, CommentGetAllWithAllInfo, CommentGetAllWithStatus]
)
async def get_comments(
limit: int = 10,
offset: int = 0,
Expand All @@ -180,7 +189,7 @@ async def get_comments(
unreviewed: bool = False,
asc_order: bool = False,
user=Depends(UnionAuth(scopes=["rating.comment.review"], auto_error=False, allow_none=False)),
) -> CommentGetAll:
) -> Union[CommentGetAll, CommentGetAllWithLike, CommentGetAllWithAllInfo, CommentGetAllWithStatus]:
"""
Scopes: `["rating.comment.review"]`

Expand All @@ -203,6 +212,7 @@ async def get_comments(

`asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания
"""

comments_query = (
Comment.query(session=db.session)
.filter(Comment.search_by_lectorer_id(lecturer_id))
Expand All @@ -219,6 +229,7 @@ async def get_comments(
)
)
comments = comments_query.limit(limit).offset(offset).all()
like = False
if not comments:
raise ObjectNotFound(Comment, 'all')
if user and "rating.comment.review" in [scope['name'] for scope in user.get('session_scopes')]:
Expand All @@ -228,8 +239,13 @@ async def get_comments(
result = CommentGetAllWithStatus(limit=limit, offset=offset, total=len(comments))
comment_validator = CommentGetWithStatus
else:
result = CommentGetAll(limit=limit, offset=offset, total=len(comments))
result = (
CommentGetAllWithLike(limit=limit, offset=offset, total=len(comments))
if user
else CommentGetAll(limit=limit, offset=offset, total=len(comments))
)
comment_validator = CommentGet
like = True

result.comments = comments

Expand All @@ -244,8 +260,26 @@ async def get_comments(
result.comments = [comment for comment in result.comments if comment.review_status is ReviewStatus.APPROVED]

result.total = len(result.comments)
result.comments = [comment_validator.model_validate(comment) for comment in result.comments]
comments_with_like = []
current_user_id = user.get("id") if user else None
if current_user_id and result.comments:
user_reactions = Comment.reactions_for_comments(current_user_id, db.session, result.comments)
else:
user_reactions = {}

for comment in result.comments:
base_data = comment_validator.model_validate(comment)

if current_user_id:
reaction = user_reactions.get(comment.uuid)
comment_with_reactions = CommentGetWithLike(
**base_data.model_dump(), is_liked=reaction == Reaction.LIKE, is_disliked=reaction == Reaction.DISLIKE
)
comments_with_like.append(comment_with_reactions)
else:
comments_with_like.append(base_data)

result.comments = comments_with_like
return result


Expand Down
43 changes: 43 additions & 0 deletions rating_api/schemas/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,33 @@ class CommentGet(Base):
dislike_count: int


class CommentGetWithLike(CommentGet):
is_liked: bool
is_disliked: bool


class CommentGetWithStatus(CommentGet):
review_status: ReviewStatus


"""
class CommentGetWithLikeAndStatus(CommentGetWithLike):
review_status: ReviewStatus
"""


class CommentGetWithAllInfo(CommentGet):
review_status: ReviewStatus
approved_by: int | None = None


"""
class CommentGetWithAllInfoAndLike(CommentGetWithLike):
review_status: ReviewStatus
approved_by: int | None = None
"""


class CommentUpdate(Base):
subject: str = None
text: str = None
Expand Down Expand Up @@ -74,20 +92,45 @@ class CommentGetAll(Base):
total: int


class CommentGetAllWithLike(Base):
comments: list[CommentGetWithLike] = []
limit: int
offset: int
total: int


class CommentGetAllWithStatus(Base):
comments: list[CommentGetWithStatus] = []
limit: int
offset: int
total: int


"""
class CommentGetAllWithStatusAndLike(Base):
comments: list[CommentGetWithLikeAndStatus] = []
limit: int
offset: int
total: int
"""


class CommentGetAllWithAllInfo(Base):
comments: list[CommentGetWithAllInfo] = []
limit: int
offset: int
total: int


"""
class CommentGetAllWithAllInfoAndLike(Base):
comments: list[CommentGetWithAllInfoAndLike] = []
limit: int
offset: int
total: int
"""


class LecturerUserCommentPost(Base):
lecturer_id: int
user_id: int
Expand Down
42 changes: 35 additions & 7 deletions tests/test_routes/test_comment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import datetime
import logging
import uuid

import pytest
from starlette import status
Expand Down Expand Up @@ -196,13 +195,42 @@ def test_create_comment(client, dbsession, lecturers, body, lecturer_n, response
assert user_comment is not None


def test_get_comment(client, comment):
@pytest.mark.parametrize(
"reaction_data, expected_reaction, comment_user_id",
[
(None, None, 0),
((0, Reaction.LIKE), "is_liked", 0), # my like on my comment
((0, Reaction.DISLIKE), "is_disliked", 0),
((999, Reaction.LIKE), None, 0), # someone else's like on my comment
((999, Reaction.DISLIKE), None, 0),
((0, Reaction.LIKE), "is_liked", 999), # my like on someone else's comment
((0, Reaction.DISLIKE), "is_disliked", 999),
((333, Reaction.LIKE), None, 999), # someone else's like on another person's comment
((333, Reaction.DISLIKE), None, 999),
(None, None, None), # anonymous
],
)
def test_get_comment_with_reaction(client, dbsession, comment, reaction_data, expected_reaction, comment_user_id):
comment.user_id = comment_user_id

if reaction_data:
user_id, reaction_type = reaction_data
reaction = CommentReaction(user_id=user_id, comment_uuid=comment.uuid, reaction=reaction_type)
dbsession.add(reaction)

dbsession.commit()

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}')
assert response.status_code == status.HTTP_404_NOT_FOUND

if response_comment:
data = response_comment.json()
if expected_reaction:
assert data[expected_reaction]
else:
assert data["is_liked"] == False
assert data["is_disliked"] == False
else:
assert response_comment.status_code == status.HTTP_404_NOT_FOUND


@pytest.fixture
Expand Down