diff --git a/authors/apps/articles/migrations/0007_article_likes.py b/authors/apps/articles/migrations/0007_article_likes.py new file mode 100644 index 0000000..364bc01 --- /dev/null +++ b/authors/apps/articles/migrations/0007_article_likes.py @@ -0,0 +1,20 @@ +# Generated by Django 2.0.7 on 2018-08-08 10:05 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('articles', '0006_auto_20180801_0945'), + ] + + operations = [ + migrations.AddField( + model_name='article', + name='likes', + field=models.ManyToManyField(blank=True, related_name='likes', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/authors/apps/articles/migrations/0008_article_dislikes.py b/authors/apps/articles/migrations/0008_article_dislikes.py new file mode 100644 index 0000000..de0619a --- /dev/null +++ b/authors/apps/articles/migrations/0008_article_dislikes.py @@ -0,0 +1,20 @@ +# Generated by Django 2.0.7 on 2018-08-08 14:11 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('articles', '0007_article_likes'), + ] + + operations = [ + migrations.AddField( + model_name='article', + name='dislikes', + field=models.ManyToManyField(blank=True, related_name='dislikes', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/authors/apps/articles/migrations/0009_article_dislikescount.py b/authors/apps/articles/migrations/0009_article_dislikescount.py new file mode 100644 index 0000000..44692d4 --- /dev/null +++ b/authors/apps/articles/migrations/0009_article_dislikescount.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.7 on 2018-08-08 14:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('articles', '0008_article_dislikes'), + ] + + operations = [ + migrations.AddField( + model_name='article', + name='dislikesCount', + field=models.IntegerField(default=0), + ), + ] diff --git a/authors/apps/articles/migrations/0024_merge_20180809_0647.py b/authors/apps/articles/migrations/0024_merge_20180809_0647.py new file mode 100644 index 0000000..8f98ede --- /dev/null +++ b/authors/apps/articles/migrations/0024_merge_20180809_0647.py @@ -0,0 +1,14 @@ +# Generated by Django 2.0.7 on 2018-08-09 06:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('articles', '0023_auto_20180808_1545'), + ('articles', '0009_article_dislikescount'), + ] + + operations = [ + ] diff --git a/authors/apps/articles/models.py b/authors/apps/articles/models.py index 063191e..a768da8 100644 --- a/authors/apps/articles/models.py +++ b/authors/apps/articles/models.py @@ -17,9 +17,11 @@ class Article(models.Model): author = models.ForeignKey(User, related_name = "user_articles", on_delete = models.CASCADE) favorited = models.BooleanField(default = False) favoritesCount = models.IntegerField(default = 0) + dislikesCount = models.IntegerField(default = 0) tags = TaggableManager(blank = True) rating = models.PositiveIntegerField(blank = True, editable = False, null = True) - + likes = models.ManyToManyField(User,related_name="likes",blank=True) + dislikes = models.ManyToManyField(User,related_name="dislikes",blank=True) def save(self, *args, **kwargs): if not self.id: diff --git a/authors/apps/articles/renderers.py b/authors/apps/articles/renderers.py index 1ba0a22..efa0036 100644 --- a/authors/apps/articles/renderers.py +++ b/authors/apps/articles/renderers.py @@ -32,3 +32,4 @@ def render(self, data, media_type=None, renderer_context=None): return json.dumps({ 'comment': data, }) + diff --git a/authors/apps/articles/serializers.py b/authors/apps/articles/serializers.py index bb0632c..1466b4a 100644 --- a/authors/apps/articles/serializers.py +++ b/authors/apps/articles/serializers.py @@ -61,4 +61,5 @@ def get_article(self, obj): class Meta: model = Comment fields = '__all__' + diff --git a/authors/apps/articles/tests/test_like_dislike.py b/authors/apps/articles/tests/test_like_dislike.py new file mode 100644 index 0000000..3c92429 --- /dev/null +++ b/authors/apps/articles/tests/test_like_dislike.py @@ -0,0 +1,110 @@ +from django.test import TestCase, RequestFactory +from authors.apps.articles.views import ArticleCreateAPIView,LikeArticleAPIView,DislikeArticleAPIView +from authors.apps.authentication.views import RegistrationAPIView +from rest_framework.test import force_authenticate +import json + +class LikeDislikeTestCase(TestCase): + + def setUp(self): + self.factory = RequestFactory() + + self.user = { + "user" : { + "email":"test@gmail.com", + "username":"tester", + "password":"testpass@word" + } + } + + self.request = self.factory.post('/api/users/', data = json.dumps(self.user), content_type='application/json') + self.response = RegistrationAPIView.as_view()(self.request) + self.headers = { + 'HTTP_AUTHORIZATION': 'Token ' + self.response.data["token"] + } + + self.article_data = { + "title":"How to Survive", + "description":"How?", + "body":"This is how to survive" + } + + + request = self.factory.post('api/articles', data = json.dumps(self.article_data), **self.headers, content_type = 'application/json') + response = ArticleCreateAPIView.as_view()(request) + + def test_like_normal(self): + kwargs = {"slug":"how-to-survive"} + request = self.factory.post('api/articles/how-to-survive/favorite', **self.headers) + response = LikeArticleAPIView.as_view()(request,**kwargs) + self.assertEqual(response.status_code, 201) + + def test_like_already_liked(self): + kwargs = {"slug":"how-to-survive"} + request = self.factory.post('api/articles/how-to-survive/favorite', **self.headers) + response = LikeArticleAPIView.as_view()(request,**kwargs) + response = LikeArticleAPIView.as_view()(request,**kwargs) + self.assertEqual(response.status_code, 409) + + def test_like_missing_slug(self): + request = self.factory.post('api/articles/favorite', **self.headers) + response = LikeArticleAPIView.as_view()(request) + self.assertEqual(response.status_code, 404) + + def test_delete_like_normal(self): + kwargs = {"slug":"how-to-survive"} + LikeArticleAPIView.as_view()(self.factory.post('api/articles/how-to-survive/favorite', **self.headers),**kwargs) + request = self.factory.delete('api/articles/how-to-survive/favorite', **self.headers) + response = LikeArticleAPIView.as_view()(request,**kwargs) + self.assertEqual(response.status_code, 200) + + def test_delete_like_before_like(self): + kwargs = {"slug":"how-to-survive"} + request = self.factory.delete('api/articles/how-to-survive/favorite', **self.headers) + response = LikeArticleAPIView.as_view()(request,**kwargs) + self.assertEqual(response.status_code, 409) + + def test_delete_like_missing_slug(self): + request = self.factory.delete('api/articles/favorite', **self.headers) + response = LikeArticleAPIView.as_view()(request) + self.assertEqual(response.status_code, 404) + + def test_dislike_normal(self): + kwargs = {"slug":"how-to-survive"} + request = self.factory.post('api/articles/how-to-survive/dislike', **self.headers) + response = DislikeArticleAPIView.as_view()(request,**kwargs) + self.assertEqual(response.status_code, 201) + + def test_dislike_already_disliked(self): + kwargs = {"slug":"how-to-survive"} + request = self.factory.post('api/articles/how-to-survive/dislike', **self.headers) + response = DislikeArticleAPIView.as_view()(request,**kwargs) + response = DislikeArticleAPIView.as_view()(request,**kwargs) + self.assertEqual(response.status_code, 409) + + def test_dislike_missing_slug(self): + request = self.factory.post('api/articles/dislike', **self.headers) + response = DislikeArticleAPIView.as_view()(request) + self.assertEqual(response.status_code, 404) + + def test_delete_dislike_normal(self): + kwargs = {"slug":"how-to-survive"} + DislikeArticleAPIView.as_view()(self.factory.post('api/articles/how-to-survive/dislike', **self.headers),**kwargs) + request = self.factory.delete('api/articles/how-to-survive/dislike', **self.headers) + response = DislikeArticleAPIView.as_view()(request,**kwargs) + self.assertEqual(response.status_code, 200) + + def test_delete_dislike_before_dislike(self): + kwargs = {"slug":"how-to-survive"} + request = self.factory.delete('api/articles/how-to-survive/dislike', **self.headers) + response = DislikeArticleAPIView.as_view()(request,**kwargs) + self.assertEqual(response.status_code, 409) + + def test_delete_dislike_missing_slug(self): + request = self.factory.delete('api/articles//dislike', **self.headers) + response = DislikeArticleAPIView.as_view()(request,) + self.assertEqual(response.status_code, 404) + + + + diff --git a/authors/apps/articles/urls.py b/authors/apps/articles/urls.py index 55bb52d..040e4a1 100644 --- a/authors/apps/articles/urls.py +++ b/authors/apps/articles/urls.py @@ -1,6 +1,7 @@ from django.urls import path -from .views import ArticleCreateAPIView, RateArticleAPIView,CommentCreateAPIView +from .views import ArticleCreateAPIView, RateArticleAPIView,CommentCreateAPIView,LikeArticleAPIView,DislikeArticleAPIView + app_name = "articles" @@ -8,5 +9,7 @@ path('articles', ArticleCreateAPIView.as_view()), path('articles//rate/', RateArticleAPIView.as_view()), path('articles//comment', CommentCreateAPIView.as_view()), + path('articles//like',LikeArticleAPIView.as_view()), + path('articles//dislike',DislikeArticleAPIView.as_view()), ] diff --git a/authors/apps/articles/views.py b/authors/apps/articles/views.py index fd2e4cb..a567cdb 100644 --- a/authors/apps/articles/views.py +++ b/authors/apps/articles/views.py @@ -1,7 +1,9 @@ from django.shortcuts import render -from rest_framework.generics import CreateAPIView, RetrieveAPIView +from rest_framework.generics import CreateAPIView, RetrieveAPIView, UpdateAPIView from authors.apps.articles.serializers import CreateArticleSerializer, RateArticleSerializer,CreateCommentSerializer from authors.apps.articles.renderers import ArticleJSONRenderer, RateArticleJSONRenderer,CommentJSONRenderer +from rest_framework.views import APIView +from .serializers import CreateArticleSerializer from authors.apps.authentication.models import User from authors.apps.articles.models import Article,Comment from rest_framework.permissions import IsAuthenticated @@ -10,6 +12,12 @@ from rest_framework import status from rest_framework.response import Response from django.shortcuts import get_object_or_404 + + +from authors.apps.authentication.backends import JWTAuthentication + +from rest_framework.response import Response +from rest_framework import status import json class ArticleCreateAPIView(CreateAPIView): @@ -49,3 +57,69 @@ def perform_create(self, serializer): user_data = jwt.authenticate(self.request) slug = self.kwargs.get(self.look_url_kwarg) serializer.save(author = user_data[0], article = get_object_or_404(Article,slug=slug)) +class LikeArticleAPIView(APIView): + permission_classes = (IsAuthenticated, ) + look_url_kwarg = "slug" + + + def post(self,*args,**kwargs): + jwt = JWTAuthentication() + user_data = jwt.authenticate(self.request) + slug = self.kwargs.get(self.look_url_kwarg) + article = get_object_or_404(Article,slug = slug) + if article.likes.filter(id=User.objects.filter(email=user_data[0])[0].id).exists(): + return Response(data = {"errors":{"error":"Already liked"}},status=status.HTTP_409_CONFLICT) + article.likes.add(user_data[0]) + article.save() + + return Response(data = CreateArticleSerializer(article).data,status=status.HTTP_201_CREATED) + + def delete(self,*args,**kwargs): + jwt = JWTAuthentication() + user_data = jwt.authenticate(self.request) + slug = self.kwargs.get(self.look_url_kwarg) + article = get_object_or_404(Article,slug = slug) + if article.likes.filter(id=User.objects.filter(email=user_data[0])[0].id).exists(): + article.likes.remove(user_data[0]) + article.save() + return Response(data = CreateArticleSerializer(article).data,status=status.HTTP_200_OK) + else: + return Response(data = {"errors":{"error":"Not yet liked"}},status=status.HTTP_409_CONFLICT) + + +class DislikeArticleAPIView(APIView): + permission_classes = (IsAuthenticated, ) + look_url_kwarg = "slug" + + + def post(self,*args,**kwargs): + jwt = JWTAuthentication() + user_data = jwt.authenticate(self.request) + slug = self.kwargs.get(self.look_url_kwarg) + article = get_object_or_404(Article,slug = slug) + if article.dislikes.filter(id=User.objects.filter(email=user_data[0])[0].id).exists(): + return Response(data = {"errors":{"error":"Already disliked"}},status=status.HTTP_409_CONFLICT) + + if article.likes.filter(id=User.objects.filter(email=user_data[0])[0].id).exists(): + article.likes.remove(user_data[0]) + + article.dislikes.add(user_data[0]) + article.dislikesCount+=1 + article.save() + + return Response(data = CreateArticleSerializer(article).data,status=status.HTTP_201_CREATED) + + def delete(self,*args,**kwargs): + jwt = JWTAuthentication() + user_data = jwt.authenticate(self.request) + slug = self.kwargs.get(self.look_url_kwarg) + article = get_object_or_404(Article,slug = slug) + if article.dislikes.filter(id=User.objects.filter(email=user_data[0])[0].id).exists(): + article.dislikes.remove(user_data[0]) + article.dislikesCount-=1 + article.save() + return Response(data = CreateArticleSerializer(article).data,status=status.HTTP_200_OK) + else: + return Response(data = {"errors":{"error":"Not yet disliked"}},status=status.HTTP_409_CONFLICT) + +