From 699a544b4ba4eb856cc5b66b60549d6b51093c36 Mon Sep 17 00:00:00 2001 From: Andrew Hinga Date: Tue, 22 Jan 2019 20:09:15 +0300 Subject: [PATCH 1/3] feat(article rating): add unit tests for article rating - write unit tests for article rating [Delivers #162948843] --- .../articles/tests/test_article_ratings.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 authors/apps/articles/tests/test_article_ratings.py diff --git a/authors/apps/articles/tests/test_article_ratings.py b/authors/apps/articles/tests/test_article_ratings.py new file mode 100644 index 00000000..4d8ed650 --- /dev/null +++ b/authors/apps/articles/tests/test_article_ratings.py @@ -0,0 +1,86 @@ +from django.urls import reverse +from rest_framework.test import APIClient, APITestCase +from rest_framework import status + + +class TestArticleRatings(APITestCase): + def setUp(self): + self.client = APIClient() + self.register_url = reverse('authentication:auth-register') + self.login_url = reverse("authentication:auth-login") + self.rating_url = reverse( + "articles: articles-listcreate", + kwargs={'slug': 'your-first-blog'}) + + self.rating_non_existing_url = reverse( + "articles:rate-article", kwargs={'slug': 'your-second-blog'}) + + self.username = "testuser" + self.email = "testuser@gmail.com" + self.password = "this_user123@A" + + self.test_user = User.objects.create_user( + username=self.username, + email=self.email, + password=self.password) + + self.data_for_test = {"user": { + "email": self.email, + "password": self.password + }} + + login_res = self.client.post( + self.login_url, self.data_for_test, format='json') + self.assertEqual(login_res.status_code, status.HTTP_200_OK) + self.token = login_res.data["token"] + + def create_article(self): + article_details = dict(article=dict( + title="your first blog", + description='this is your first blog', + body='this is your first blog' + )) + self.client.post( + reverse("articles:articles-listcreate"), + data=json.dumps(article_details), + content_type="application/json", + HTTP_AUTHORIZATION='Token ' + self.token) + + def test_rate_article(self): + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) + response = self.client.post(self.rating_url, data={"rating": 4}) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + def test_rate_with_number_outside_range(self): + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) + response = self.client.post(self.rating_url, data={"rating": 6},) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.data['message'], 'Give a rating between 1 to 5 inclusive') + + def test_rate_with_non_numeric(self): + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) + response = self.client.post(self.rating_url, data={"rating": "a"}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.data['message'], 'Only integers, 1 to 5 are allowed') + + def test_rate_non_existing_article(self): + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) + response = self.client.post( + self.rating_non_existing_url, data={"rating": 4}) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(response.data["message"], 'Article not found') + + def test_rating_by_unauthenticated_user(self): + response = self.client.post( + self.rating_url, data={"rating": 4}) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.data["message"], 'Please sign in') + + def test_rate_with_empty_rating(self): + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) + response = self.client.post(self.rating_url, data={"rating": {}}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.data["message"], 'Please provide a rating between 1 to 5') From 1d7bf24696ff58c1de93c04b6c205bae01057c3f Mon Sep 17 00:00:00 2001 From: Andrew Hinga Date: Fri, 25 Jan 2019 07:27:59 +0300 Subject: [PATCH 2/3] feat(article rating): implement rating of articles - create article rating model - create an article rating serializer class - add view for viewing an article's average ratings - add view for posting a rating to an article - refactor tests [Delivers #162948843] --- .../articles/migrations/0010_articlerating.py | 25 +++ .../migrations/0013_merge_20190125_1538.py | 14 ++ authors/apps/articles/models.py | 12 ++ authors/apps/articles/serializers.py | 15 +- .../articles/tests/test_article_ratings.py | 170 ++++++++++++------ authors/apps/articles/urls.py | 18 +- authors/apps/articles/views.py | 92 +++++++++- 7 files changed, 287 insertions(+), 59 deletions(-) create mode 100644 authors/apps/articles/migrations/0010_articlerating.py create mode 100644 authors/apps/articles/migrations/0013_merge_20190125_1538.py diff --git a/authors/apps/articles/migrations/0010_articlerating.py b/authors/apps/articles/migrations/0010_articlerating.py new file mode 100644 index 00000000..7e09ad9f --- /dev/null +++ b/authors/apps/articles/migrations/0010_articlerating.py @@ -0,0 +1,25 @@ +# Generated by Django 2.1.4 on 2019-01-23 13:52 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('articles', '0009_auto_20190121_0754'), + ] + + operations = [ + migrations.CreateModel( + name='ArticleRating', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rate', models.IntegerField(default=0)), + ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/authors/apps/articles/migrations/0013_merge_20190125_1538.py b/authors/apps/articles/migrations/0013_merge_20190125_1538.py new file mode 100644 index 00000000..e071a769 --- /dev/null +++ b/authors/apps/articles/migrations/0013_merge_20190125_1538.py @@ -0,0 +1,14 @@ +# Generated by Django 2.1.5 on 2019-01-25 15:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('articles', '0012_merge_20190125_1317'), + ('articles', '0010_articlerating'), + ] + + operations = [ + ] diff --git a/authors/apps/articles/models.py b/authors/apps/articles/models.py index 07b0e0ad..82725df2 100644 --- a/authors/apps/articles/models.py +++ b/authors/apps/articles/models.py @@ -23,3 +23,15 @@ class Article(models.Model): User, related_name='likes', blank=True) user_id_dislikes = models.ManyToManyField( User, related_name='dislikes', blank=True) + + +class ArticleRating(models.Model): + """ + This is a model to handle article ratings + """ + user = models.ForeignKey(User, on_delete=models.CASCADE) + article = models.ForeignKey(Article, on_delete=models.CASCADE) + rate = models.IntegerField(default=0) + + def __str__(self): + return self.rate diff --git a/authors/apps/articles/serializers.py b/authors/apps/articles/serializers.py index be7ab808..9af7e778 100644 --- a/authors/apps/articles/serializers.py +++ b/authors/apps/articles/serializers.py @@ -1,7 +1,7 @@ import re from rest_framework import serializers -from .models import Article +from .models import Article, ArticleRating from taggit_serializer.serializers import (TagListSerializerField, TaggitSerializer) @@ -85,3 +85,16 @@ class DeleteArticleSerializer(serializers.ModelSerializer): class Meta: model = Article fields = ['is_deleted'] + + +class RatingSerializer(serializers.Serializer): + """validate rating article""" + rate = serializers.IntegerField(required=True) + + def validate(self, data): + rate = data['rate'] + if not int(rate) > 0 or not int(rate) <= 5: + raise serializers.ValidationError( + 'Give a rating between 1 to 5 inclusive' + ) + return data diff --git a/authors/apps/articles/tests/test_article_ratings.py b/authors/apps/articles/tests/test_article_ratings.py index 4d8ed650..4e6e2369 100644 --- a/authors/apps/articles/tests/test_article_ratings.py +++ b/authors/apps/articles/tests/test_article_ratings.py @@ -1,86 +1,154 @@ +import json from django.urls import reverse from rest_framework.test import APIClient, APITestCase from rest_framework import status +from authors.apps.articles.models import Article +from authors.apps.authentication.models import User class TestArticleRatings(APITestCase): def setUp(self): self.client = APIClient() - self.register_url = reverse('authentication:auth-register') self.login_url = reverse("authentication:auth-login") - self.rating_url = reverse( - "articles: articles-listcreate", - kwargs={'slug': 'your-first-blog'}) - self.rating_non_existing_url = reverse( - "articles:rate-article", kwargs={'slug': 'your-second-blog'}) + self.article_author = User.objects.create_user( + username="testuser", + email="testuser@gmail.com", + password="this_user123@A") - self.username = "testuser" - self.email = "testuser@gmail.com" - self.password = "this_user123@A" - - self.test_user = User.objects.create_user( - username=self.username, - email=self.email, - password=self.password) - - self.data_for_test = {"user": { - "email": self.email, - "password": self.password + self.author_data = {"user": { + "email": "testuser@gmail.com", + "password": "this_user123@A" }} login_res = self.client.post( - self.login_url, self.data_for_test, format='json') - self.assertEqual(login_res.status_code, status.HTTP_200_OK) + self.login_url, self.author_data, format='json') self.token = login_res.data["token"] - def create_article(self): - article_details = dict(article=dict( - title="your first blog", - description='this is your first blog', - body='this is your first blog' - )) - self.client.post( - reverse("articles:articles-listcreate"), - data=json.dumps(article_details), - content_type="application/json", - HTTP_AUTHORIZATION='Token ' + self.token) + article_details = { + "title": "your first blog", + "description": "this is your first blog", + "body": "this is your first blog", + "tagList": ["dragons", "training"], + } - def test_rate_article(self): - self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) - response = self.client.post(self.rating_url, data={"rating": 4}) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.response = self.client.post( + reverse("articles:articles-listcreate"), + data=json.dumps(article_details), + content_type="application/json", + HTTP_AUTHORIZATION='Token ' + self.token) - def test_rate_with_number_outside_range(self): - self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) - response = self.client.post(self.rating_url, data={"rating": 6},) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.slug = self.response.data['slug'] + + self.rating_user = User.objects.create_user( + username="andrew", + email="andrew@gmail.com", + password="this_user123@A") + + self.rating_user_data = {"user": { + "email": "andrew@gmail.com", + "password": "this_user123@A" + }} + + login_res1 = self.client.post( + self.login_url, self.rating_user_data, format='json') + self.assertEqual(login_res1.status_code, status.HTTP_200_OK) + self.token1 = login_res1.data["token"] + + def test_rate_article(self): + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token1) + slug = self.slug + data = {"rate": 4} + response = self.client.post( + "/api/articles/{}/rate/".format(slug), data=data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( - response.data['message'], 'Give a rating between 1 to 5 inclusive') + response.data['response'], 'article successfully rated' + ) - def test_rate_with_non_numeric(self): + def test_rate_with_number_outside_range(self): self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) - response = self.client.post(self.rating_url, data={"rating": "a"}) + slug = self.slug + data = {"rate": 6} + response = self.client.post( + "/api/articles/{}/rate/".format(slug), data=data,) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual( - response.data['message'], 'Only integers, 1 to 5 are allowed') + self.assertIn( + 'Give a rating between 1 to 5 inclusive', response.data['errors']) def test_rate_non_existing_article(self): self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) + slug = "home-away" + data = {"rate": 3} response = self.client.post( - self.rating_non_existing_url, data={"rating": 4}) + "/api/articles/{}/rate/".format(slug), data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response.data["message"], 'Article not found') + self.assertIn('article not found', response.data['detail']) def test_rating_by_unauthenticated_user(self): + slug = self.slug + data = {"rate": 4} response = self.client.post( - self.rating_url, data={"rating": 4}) + "/api/articles/{}/rate/".format(slug), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(response.data["message"], 'Please sign in') - def test_rate_with_empty_rating(self): + def test_rate_own_article(self): self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) - response = self.client.post(self.rating_url, data={"rating": {}}) + slug = self.slug + data = {"rate": 4} + response = self.client.post( + "/api/articles/{}/rate/".format(slug), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.data["message"], 'Please provide a rating between 1 to 5') + response.data["response"], + "You are not allowed to rate your own article" + ) + + def test_update_ratings(self): + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token1) + + # assert that the first rating is made successfully + slug = self.slug + data1 = {"rate": 4} + res1 = self.client.post( + "/api/articles/{}/rate/".format(slug), data=data1, format='json') + self.assertEqual(res1.status_code, status.HTTP_200_OK) + + # assert that the rating is 4 + res2 = self.client.get( + "/api/articles/rate/{}/".format(slug)) + self.assertEqual(res2.status_code, status.HTTP_200_OK) + self.assertEqual(res2.data['ratings'], 4) + + # assert that rating can be modified + data2 = {"rate": 3} + res3 = self.client.post( + "/api/articles/{}/rate/".format(slug), data=data2, format='json') + self.assertEqual(res3.status_code, status.HTTP_200_OK) + + # assert that the new rating is 3 + res4 = self.client.get( + "/api/articles/rate/{}/".format(slug)) + self.assertEqual(res4.status_code, status.HTTP_200_OK) + self.assertEqual(res4.data['ratings'], 3) + + def test_view_article_rating(self): + # assert that ratings of an article that's not rated is 0 + slug = self.slug + res1 = self.client.get( + "/api/articles/rate/{}/".format(slug)) + self.assertEqual(res1.status_code, status.HTTP_200_OK) + self.assertEqual(res1.data['ratings'], 0) + + # assert that the article is rated + data = {"rate": 4} + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token1) + res2 = self.client.post( + "/api/articles/{}/rate/".format(slug), data=data, format='json') + self.assertEqual(res2.status_code, status.HTTP_200_OK) + + # assert that the new rating is 4 + res3 = self.client.get( + "/api/articles/rate/{}/".format(slug)) + self.assertEqual(res3.status_code, status.HTTP_200_OK) + self.assertEqual(res3.data['ratings'], 4) diff --git a/authors/apps/articles/urls.py b/authors/apps/articles/urls.py index 1d59977f..1acfcf35 100644 --- a/authors/apps/articles/urls.py +++ b/authors/apps/articles/urls.py @@ -1,10 +1,14 @@ from django.urls import path from .views import (ArticleAPIView, ArticleDetailsView, - DeleteArticle, LikeArticleApiView, DislikeArticleApiView) + DeleteArticle, LikeArticleApiView, + DislikeArticleApiView, PostRatingsAPIView, + AverageRatingsAPIView + ) + + app_name = "articles" -urlpatterns = [ - path('articles/', ArticleAPIView.as_view(), name="articles-listcreate"), +urlpatterns = [path('articles/', ArticleAPIView.as_view(), name="articles-listcreate"), path('articles//', ArticleDetailsView.as_view(), name="articles-retrieveupdate"), @@ -14,5 +18,11 @@ path('articles//like/', LikeArticleApiView.as_view(), name='likes'), path('articles//dislike/', - DislikeArticleApiView.as_view(), name='dislikes') + DislikeArticleApiView.as_view(), name='dislikes'), + path('articles//rate/', + PostRatingsAPIView.as_view(), + name="rate_article"), + path('articles/rate//', + AverageRatingsAPIView.as_view(), + name="articles-view_ratings") ] diff --git a/authors/apps/articles/views.py b/authors/apps/articles/views.py index 1d138e72..66bb6912 100644 --- a/authors/apps/articles/views.py +++ b/authors/apps/articles/views.py @@ -2,20 +2,24 @@ from django.template.defaultfilters import slugify from django.core.exceptions import ObjectDoesNotExist +from rest_framework import status from django.core import serializers from rest_framework import status from rest_framework.response import Response -from rest_framework.generics import (ListCreateAPIView, +from rest_framework.generics import (CreateAPIView, ListCreateAPIView, RetrieveUpdateAPIView, UpdateAPIView) +from rest_framework.views import APIView from rest_framework.permissions import (IsAuthenticated, IsAuthenticatedOrReadOnly) +from rest_framework.renderers import JSONRenderer from rest_framework.exceptions import NotFound from authors.apps.core.permissions import IsAuthorOrReadOnly from . import serializers from .serializers import (ArticleSerializer, - GetArticleSerializer, DeleteArticleSerializer) -from .models import Article + GetArticleSerializer, DeleteArticleSerializer, + RatingSerializer) +from .models import Article, ArticleRating class ArticleAPIView(ListCreateAPIView): @@ -131,3 +135,85 @@ def put(self, request, slug): context={'request': request}, partial=True) return Response(serializer.data, status.HTTP_200_OK) + + +class AverageRatingsAPIView(APIView): + """ + Class for viewing article ratings + """ + + serializer_class = RatingSerializer + + def get(self, request, slug): + """method for viewing average ratings""" + rate_article = None + rate = 0 + + try: + article = Article.objects.get(slug=slug) + except ObjectDoesNotExist: + raise NotFound('article not found') + + art_id = article.id + + try: + rate_articles = ArticleRating.objects.filter(article_id=art_id) + except Exception as e: + raise serializers.ValidationError(str(e)) + + for rate_article in rate_articles: + rate += rate_article.rate + rate_value = 0 + if rate: + rate_value = rate / len(rate_articles) + return Response( + data={"ratings": round(rate_value, 1)}, status=status.HTTP_200_OK) + + +class PostRatingsAPIView(CreateAPIView): + """ + Class for rating an article + """ + permission_classes = (IsAuthenticated,) + serializer_class = RatingSerializer + + def post(self, request, slug): + """method for posting a rating""" + data = request.data + serializer = self.serializer_class(data=data) + serializer.validate(data=data) + serializer.is_valid(raise_exception=True) + + try: + article = Article.objects.get(slug=slug) + except ObjectDoesNotExist: + raise NotFound('article not found') + + author = article.author.id + if author == request.user.id: + return Response( + {"response": "You are not allowed to rate your own article"}, + status=status.HTTP_400_BAD_REQUEST) + + user = request.user + article_ratings = None + response = Response( + {"response": "article successfully rated"}, + status=status.HTTP_200_OK + ) + + # update the rating if there's one + try: + article_ratings = ArticleRating.objects.get( + user=user, article=article + ) + article_ratings.rate = data['rate'] + article_ratings.save() + return response + # create new rating if none exists + except Exception: + article_ratings = ArticleRating( + user=user, article=article, rate=data['rate'] + ) + article_ratings.save() + return response From cc366a92da62601be9f0937d10d70fca80a4a193 Mon Sep 17 00:00:00 2001 From: Andrew Hinga Date: Sun, 27 Jan 2019 12:12:16 +0300 Subject: [PATCH 3/3] feat(article rating): remove unused imports, use count, refactor error messages - remove unused imports - use count instead of len to get the length of queryset - refactor error messages - get rid of unnecessary exception [Fixes #162948843] --- authors/apps/articles/serializers.py | 10 +------ .../articles/tests/test_article_ratings.py | 12 ++++---- authors/apps/articles/urls.py | 2 +- authors/apps/articles/views.py | 29 +++++++++---------- 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/authors/apps/articles/serializers.py b/authors/apps/articles/serializers.py index 9af7e778..96c218fb 100644 --- a/authors/apps/articles/serializers.py +++ b/authors/apps/articles/serializers.py @@ -1,7 +1,7 @@ import re from rest_framework import serializers -from .models import Article, ArticleRating +from .models import Article from taggit_serializer.serializers import (TagListSerializerField, TaggitSerializer) @@ -90,11 +90,3 @@ class Meta: class RatingSerializer(serializers.Serializer): """validate rating article""" rate = serializers.IntegerField(required=True) - - def validate(self, data): - rate = data['rate'] - if not int(rate) > 0 or not int(rate) <= 5: - raise serializers.ValidationError( - 'Give a rating between 1 to 5 inclusive' - ) - return data diff --git a/authors/apps/articles/tests/test_article_ratings.py b/authors/apps/articles/tests/test_article_ratings.py index 4e6e2369..c3cb612d 100644 --- a/authors/apps/articles/tests/test_article_ratings.py +++ b/authors/apps/articles/tests/test_article_ratings.py @@ -2,7 +2,6 @@ from django.urls import reverse from rest_framework.test import APIClient, APITestCase from rest_framework import status -from authors.apps.articles.models import Article from authors.apps.authentication.models import User @@ -63,8 +62,9 @@ def test_rate_article(self): "/api/articles/{}/rate/".format(slug), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( - response.data['response'], 'article successfully rated' + response.data['message'], 'Article successfully rated' ) + self.assertEqual(response.data["rating"]["rate"], 4) def test_rate_with_number_outside_range(self): self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) @@ -73,8 +73,8 @@ def test_rate_with_number_outside_range(self): response = self.client.post( "/api/articles/{}/rate/".format(slug), data=data,) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertIn( - 'Give a rating between 1 to 5 inclusive', response.data['errors']) + self.assertEqual( + 'Give a rating between 1 to 5 inclusive', response.data['message']) def test_rate_non_existing_article(self): self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token) @@ -83,7 +83,7 @@ def test_rate_non_existing_article(self): response = self.client.post( "/api/articles/{}/rate/".format(slug), data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertIn('article not found', response.data['detail']) + self.assertIn('Article not found', response.data['detail']) def test_rating_by_unauthenticated_user(self): slug = self.slug @@ -100,7 +100,7 @@ def test_rate_own_article(self): "/api/articles/{}/rate/".format(slug), data=data, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.data["response"], + response.data["error"], "You are not allowed to rate your own article" ) diff --git a/authors/apps/articles/urls.py b/authors/apps/articles/urls.py index 1acfcf35..d971ca69 100644 --- a/authors/apps/articles/urls.py +++ b/authors/apps/articles/urls.py @@ -24,5 +24,5 @@ name="rate_article"), path('articles/rate//', AverageRatingsAPIView.as_view(), - name="articles-view_ratings") + name="articles_view_ratings") ] diff --git a/authors/apps/articles/views.py b/authors/apps/articles/views.py index 66bb6912..cef13572 100644 --- a/authors/apps/articles/views.py +++ b/authors/apps/articles/views.py @@ -2,7 +2,6 @@ from django.template.defaultfilters import slugify from django.core.exceptions import ObjectDoesNotExist -from rest_framework import status from django.core import serializers from rest_framework import status from rest_framework.response import Response @@ -15,7 +14,6 @@ from rest_framework.exceptions import NotFound from authors.apps.core.permissions import IsAuthorOrReadOnly -from . import serializers from .serializers import (ArticleSerializer, GetArticleSerializer, DeleteArticleSerializer, RatingSerializer) @@ -146,7 +144,6 @@ class AverageRatingsAPIView(APIView): def get(self, request, slug): """method for viewing average ratings""" - rate_article = None rate = 0 try: @@ -154,18 +151,13 @@ def get(self, request, slug): except ObjectDoesNotExist: raise NotFound('article not found') - art_id = article.id - - try: - rate_articles = ArticleRating.objects.filter(article_id=art_id) - except Exception as e: - raise serializers.ValidationError(str(e)) + rate_articles = ArticleRating.objects.filter(article=article) for rate_article in rate_articles: rate += rate_article.rate rate_value = 0 if rate: - rate_value = rate / len(rate_articles) + rate_value = rate / rate_articles.count() return Response( data={"ratings": round(rate_value, 1)}, status=status.HTTP_200_OK) @@ -181,24 +173,31 @@ def post(self, request, slug): """method for posting a rating""" data = request.data serializer = self.serializer_class(data=data) - serializer.validate(data=data) serializer.is_valid(raise_exception=True) + if not int(data['rate']) > 0 or not int(data['rate']) <= 5: + return Response( + {"message": "Give a rating between 1 to 5 inclusive"}, + status=status.HTTP_400_BAD_REQUEST + ) + try: article = Article.objects.get(slug=slug) except ObjectDoesNotExist: - raise NotFound('article not found') + raise NotFound('Article not found') author = article.author.id if author == request.user.id: return Response( - {"response": "You are not allowed to rate your own article"}, + {"error": "You are not allowed to rate your own article"}, status=status.HTTP_400_BAD_REQUEST) user = request.user - article_ratings = None response = Response( - {"response": "article successfully rated"}, + { + "message": "Article successfully rated", + "rating": serializer.data + }, status=status.HTTP_200_OK )