Skip to content

165 feat: add comments and quiz ratings#180

Open
WiktorGruszczynski wants to merge 4 commits intodevfrom
feat/165-add-comments-and-quiz-rating
Open

165 feat: add comments and quiz ratings#180
WiktorGruszczynski wants to merge 4 commits intodevfrom
feat/165-add-comments-and-quiz-rating

Conversation

@WiktorGruszczynski
Copy link
Copy Markdown
Contributor

Opis

PR dodaje funkcjonalność komentarzy oraz oceniania quizów.

Zmiany

Modele

  • Dodano model Comment z obsługą zagnieżdżonych odpowiedzi, soft delete oraz przypisaniem do SharedQuiz lub Question (wymuszone przez CheckConstraint)
  • Dodano model QuizRating (1–5 gwiazdek) z unikalną oceną per użytkownik/quiz
  • Dodano metodę get_average_rating() do modelu Quiz

Serializery

  • CommentSerializer z walidacją treści, obsługą soft delete oraz właściwością is_reply
  • QuizRatingSerializer z HiddenField dla użytkownika

Widoki

  • CommentViewSet — pełny CRUD, soft delete przy DELETE, modyfikacja tylko przez autora via IsCommentAuthorOrReadOnly
  • QuizRatingViewSet — pełny CRUD ograniczony do zalogowanego użytkownika

Testy

  • Testy jednostkowe dla obu viewsetów pokrywające tworzenie, edycję, usuwanie, uprawnienia oraz edge case'y

Uwagi

  • Usunięcie komentarza wykonuje soft delete — treść i autor są czyszczone, ale rekord zostaje zachowany dla spójności wątku dyskusji
  • Użytkownik może zmieniać swoją ocenę quizu

Copy link
Copy Markdown
Member

@Antoni-Czaplicki Antoni-Czaplicki left a comment

Choose a reason for hiding this comment

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

some suggestions and also pls fix merge conflicts

content = models.TextField()

# comment can be reply to other comment
parent = models.ForeignKey("self", on_delete=models.CASCADE, null=True, blank=True, related_name="replies")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

EDIT: i see it's already done, however lets only mark it as deleted and don't remove content. also we could add last eddited_at field

prev. why not set ie is_deleted and don't return content for deleted in serializer? this would help us with potential moderation and prevent from one comment deletion removing the whole chain

related_name="comments", # quiz.comments.all()
)

# additionally, comment can be linked to question inside SharedQuiz
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

a little to much comments, i think we can remove them in places where the code makes everything clear

"id",
"author",
"content",
"parent",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

parent also neeeds to be validated if it belongs to a quiz

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

and also check if user has access to quiz

Comment on lines +810 to +861
class QuizRatingViewSet(viewsets.ModelViewSet):
"""
Manages quiz ratings for the authenticated user.

All operations are scoped to the authenticated user.
A user can only have one rating per quiz (enforced by unique constraint).
"""

permission_classes = [permissions.IsAuthenticated]
serializer_class = QuizRatingSerializer
queryset = QuizRating.objects.all()

def get_queryset(self):
return QuizRating.objects.filter(user=self.request.user)


class CommentViewSet(viewsets.ModelViewSet):
"""
Manages comments for the authenticated user.

Access Control (get_queryset):
Users can only access comments if:
1. They are the quiz maintainer (owner).
2. The quiz has been shared with them directly or via their study groups.
3. The quiz visibility is set to Public (visibility=3).

DELETE performs a soft delete — comment content is cleared but record is kept.
Only the author can modify or delete their own comments.
"""

permission_classes = [permissions.IsAuthenticated, IsCommentAuthorOrReadOnly]
serializer_class = CommentSerializer
queryset = Comment.objects.all()

def get_queryset(self):
user = self.request.user

shared_quiz_ids = SharedQuiz.objects.filter(
Q(user=user) | Q(study_group__in=user.study_groups.all()), quiz__visibility__gte=1
).values_list("quiz_id", flat=True)

return Comment.objects.filter(
Q(quiz__maintainer=user) # user is maintainer
| Q(quiz_id__in=shared_quiz_ids) # quiz was shared to user
| Q(quiz__visibility=3) # quiz is public
)

def perform_destroy(self, instance: Comment):
if instance.is_deleted:
raise ValidationError("Comment is already deleted.")

instance.mark_as_deleted()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Okay, soooo

in general this probably should be a little more advanced - now it gives us an object without filters and anything.

we should:

  • require quiz id to be provided to avoid returning a list of all users and public comments or reviews at once
  • return quiz rating in quiz metadata serializer directly with average and review count
  • only allow access to objects linked to users that have access to specific quiz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Komentarze w udostępnionych quizach

4 participants