Skip to content

Commit

Permalink
feature(like and dislike a comment): implement feature to allow likin…
Browse files Browse the repository at this point in the history
…g and disliking articles

 - add feature to allow liking article comments
 - add feature to allow disliking article comments.

 [Delivers #161966629]
  • Loading branch information
vincent Onyango committed Dec 19, 2018
1 parent 67e5f0a commit 15e220a
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 9 deletions.
9 changes: 8 additions & 1 deletion authors/apps/articles/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
from rest_framework.exceptions import NotFound

class ArticleNotFound(NotFound):
default_detail = 'Article with this slug not found'
default_detail = 'Article with this slug not found'

class CommentNotFound(NotFound):
"""
comment id not found exception
"""

default_detail = 'Comment ID not found.'
65 changes: 60 additions & 5 deletions authors/apps/articles/extra_views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from rest_framework.generics import CreateAPIView, ListAPIView
from authors.apps.articles.models import BookMarkArticle, Article
from rest_framework.generics import (
CreateAPIView, ListAPIView,
UpdateAPIView
)
from authors.apps.articles.models import (
BookMarkArticle, Article,
Comment
)
from rest_framework.response import Response
from rest_framework import status
from authors.apps.articles.exceptions import ArticleNotFound
from authors.apps.articles.exceptions import ArticleNotFound, CommentNotFound
from authors.apps.articles.serializers import BookMarkArticleSerializer


Expand All @@ -21,7 +27,7 @@ def post(self, request, article_slug):
try:
article = Article.objects.get(article_slug=article_slug)
except Article.DoesNotExist:
raise ArticleNotFound()
raise ArticleNotFound
bookmark, created = BookMarkArticle.objects.get_or_create(
user=user,
article=article
Expand Down Expand Up @@ -69,4 +75,53 @@ def list(self, request):
'message': 'No articles bookmarked yet.'
},
status=status.HTTP_200_OK
)
)

class LikeComment(UpdateAPIView):
"""Class for liking and un -liking an article"""

def update(self, request, comment_pk):
"""This method updates the liking of an article"""
try:
comment = Comment.objects.get(id=comment_pk)
except Comment.DoesNotExist:
raise CommentNotFound

# gets the user of that specific session
user = request.user
# checks for the boolean value of liking an article
liked = bool(user in comment.like.all())
if liked is True:
comment.like.remove(user.id)
message = {"message": "You have unliked this comment"}
return Response(message, status.HTTP_200_OK)

# if like is false, the article is liked
comment.like.add(user.id)
message = {"message": "You have liked this comment"}
return Response(message, status.HTTP_200_OK)


class DislikeComment(UpdateAPIView):
"""Class for disliking and un-disliking an comment"""

def update(self, request, comment_pk):
"""This method updates the liking of an comment"""
LikeComment.update(self, request, comment_pk)
comment = Comment.objects.filter(id=comment_pk).first()
if comment is None:
raise CommentNotFound

# gets the user of that specific session
user = request.user
# checks for the boolean value of disliking an article
disliked = bool(user in comment.dislike.all())
if disliked is True:
comment.dislike.remove(user.id)
message = {"message": "This comment has been un-disliked"}
return Response(message, status.HTTP_200_OK)

# if disike is false, the comment is disliked
comment.dislike.add(user.id)
message = {"message": "You have disliked this comment"}
return Response(message, status.HTTP_200_OK)
25 changes: 25 additions & 0 deletions authors/apps/articles/migrations/0004_auto_20181219_0847.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 2.1 on 2018-12-19 08:47

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('articles', '0003_auto_20181218_2105'),
]

operations = [
migrations.AddField(
model_name='comment',
name='dislike',
field=models.ManyToManyField(blank=True, related_name='dislikecomment', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='comment',
name='like',
field=models.ManyToManyField(blank=True, related_name='likecomment', to=settings.AUTH_USER_MODEL),
),
]
3 changes: 3 additions & 0 deletions authors/apps/articles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class Comment(models.Model):
blank=False,
null=False,
)
# add like and dislike field in the comment
article = models.ForeignKey(Article, related_name='comments', on_delete=models.CASCADE)
author = models.ForeignKey(Profile, related_name='comments', on_delete=models.CASCADE)
# The beginning of the highlight
Expand All @@ -94,6 +95,8 @@ class Comment(models.Model):
highlight_end = models.IntegerField(default=-1)
created_at = models.DateTimeField(auto_now_add=True)
last_update = models.DateTimeField(auto_now=True)
like = models.ManyToManyField(User, blank=True, related_name='likecomment')
dislike = models.ManyToManyField(User, blank=True, related_name='dislikecomment')

class Meta:
""""
Expand Down
22 changes: 21 additions & 1 deletion authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ class to serialize comments data
last_update = serializers.SerializerMethodField(method_name='get_formated_last_update')
replies = ReplySerializer(many=True, read_only=True)

def likes(self, instance):
"""method to return a user who has liked an article"""
request = self.context.get('request')
liked = False
if request is not None and request.user.is_authenticated:
user_id = request.user.id
liked = instance.likes.all().filter(id=user_id).count() == 1
return {'likes': instance.likes.count(), 'User': liked}

def dislikes(self, instance):
"""method to return a user who has disliked an article"""
request = self.context.get('request')
disliked = False
if request is not None and request.user.is_authenticated:
user_id = request.user.id
disliked = instance.dislikes.all().filter(id=user_id).count() == 1
return {'dislikes': instance.dislikes.count(), 'User': disliked}

class Meta:
"""
define serializer attributes
Expand All @@ -147,7 +165,9 @@ class Meta:
'body',
'created_at',
'last_update',
'replies'
'replies',
'like',
'dislike'
)

def create(self, validated_data):
Expand Down
28 changes: 27 additions & 1 deletion authors/apps/articles/tests/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,33 @@ def create_comment(self, token, article_slug):
HTTP_AUTHORIZATION=token
)
return response


def like_comment_url(self, comment_pk):
"""
return the url for liking a specific comment ID
"""
return reverse('articles:like-comment', kwargs={
'comment_pk': comment_pk
})

def dislike_comment_url(self, comment_pk):
"""
return the url for disking a specific comment ID
"""
return reverse('articles:dislike-comment', kwargs={
'comment_pk': comment_pk
})

def get_comment_id(self):
"""
return comment ID for liking and disliking a comment
"""
self.user_signup()
token = self.user_login()
article_url, saved_article, token = self.create_article()
article_slug = saved_article.data['slug']
comment_response = self.create_comment(token, article_slug)
return comment_response.data['comment']['id'], token

def create_article(self):
"""
Expand Down
57 changes: 57 additions & 0 deletions authors/apps/articles/tests/test_liking_and_disliking_comments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from rest_framework import status
from .base_test import BaseTestCase
from django.urls import reverse


class TestLikingComments(BaseTestCase):
"class to test liking and disliking of articles"

def test_like_comment(self):
"""test for liking a comment"""
comment_id, token = self.get_comment_id()
response = self.client.put(self.like_comment_url(comment_id), format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {'message': 'You have liked this comment'})

def test_dislike_comment(self):
"""test for liking a comment"""
comment_id, token = self.get_comment_id()
response = self.client.put(self.dislike_comment_url(comment_id) , format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {'message': "You have disliked this comment"})

def test_like_dislike_comment(self):
"""test for liking then disliking a comment"""
comment_id, token = self.get_comment_id()
response = self.client.put(self.like_comment_url(comment_id), format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.data, {'message': 'You have liked this comment'})
response = self.client.put(self.dislike_comment_url(comment_id), format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {'message': "You have disliked this comment"})

def test_dislike_like_comment(self):
"""test for liking then unliking a comment"""
comment_id, token = self.get_comment_id()
response = self.client.put(self.dislike_comment_url(comment_id), format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {'message': "You have disliked this comment"})
response = self.client.put(self.like_comment_url(comment_id), format='json', HTTP_AUTHORIZATION=token)
self.assertEqual(response.data, {'message': 'You have unliked this comment'})

def test_like_nonexisting_comment(self):
"""test for liking an comment"""
self.user_signup()
token = self.user_login()
non_existing_id = 5 # not existing comment ID
response = self.client.put(self.like_comment_url(non_existing_id), format='json', HTTP_AUTHORIZATION='Token ' + token)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(response.data, {'detail': "Comment ID not found."})

def test_dislike_nonexisting_comment(self):
"""test for liking an comment"""
self.user_signup()
token = self.user_login()
non_existing_id = 5 # not existing comment ID
response = self.client.put(self.like_comment_url(non_existing_id), format='json', HTTP_AUTHORIZATION='Token ' + token)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(response.data, {'detail': "Comment ID not found."})
6 changes: 5 additions & 1 deletion authors/apps/articles/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
)
from authors.apps.articles.extra_views import (
BookMarkCreateAPIView,
BookMarkListAPIView
BookMarkListAPIView,
LikeComment,
DislikeComment,
)


Expand All @@ -29,4 +31,6 @@
path('articles/reports/', ReportListAPIView.as_view(), name='report-list'),
path('articles/<str:article_slug>/bookmark/', BookMarkCreateAPIView.as_view(), name='bookmark-article'),
path('articles/bookmarks/', BookMarkListAPIView.as_view(), name='get-bookmarks'),
path('comments/<int:comment_pk>/like/', LikeComment.as_view(), name='like-comment'),
path('comments/<int:comment_pk>/dislike/', DislikeComment.as_view(), name='dislike-comment'),
]

0 comments on commit 15e220a

Please sign in to comment.