Skip to content

Commit

Permalink
Bug(articles): Fix users unable to see likes and dislikes in articles:
Browse files Browse the repository at this point in the history
- add likes and dislikes fields in ArticleSerializer

[Finishes #162163173]
  • Loading branch information
Eric Ebulu authored and Eric Ebulu committed Dec 18, 2018
1 parent 52ce0d0 commit d524cef
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 11 deletions.
35 changes: 35 additions & 0 deletions authors/apps/articles/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.db import models
from authors.apps.profiles.models import UserProfile
from authors.apps.authentication.models import User
from authors.apps.core.models import TimestampedModel
from django.contrib.postgres.fields import ArrayField
from ..profiles.models import UserProfile
Expand All @@ -23,6 +24,14 @@ class Article(TimestampedModel):
related_name='articles')
score = models.FloatField(default=0)
images = ArrayField(models.URLField(max_length=255),default=list)
likes = models.IntegerField(
db_index=True,
default=False
)
dislikes = models.IntegerField(
db_index=True,
default=False
)

def __str__(self):
return self.title
Expand All @@ -35,3 +44,29 @@ class Rating(models.Model):
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
article_id = models.ForeignKey(Article, on_delete=models.CASCADE)
score = models.IntegerField()


class Impressions(TimestampedModel):
"""
Create an `Impression` with a slug, user details, likes and dislikes.
"""

slug = models.ForeignKey(
Article,
on_delete=models.CASCADE,
to_field="slug",
db_column="slug"
)
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
)
likes = models.BooleanField(
db_index=True,
default=None
)
dislikes = models.BooleanField(
db_index=True,
default=None
)

34 changes: 33 additions & 1 deletion authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from rest_framework import serializers
from authors.apps.profiles.serializers import GetUserProfileSerializer
from .models import Article, Rating
from authors.apps.profiles.serializers import (
GetUserProfileSerializer)
from .models import (
Article, Impressions)


class ArticleSerializer(serializers.ModelSerializer):
Expand All @@ -18,7 +22,8 @@ class Meta:
model = Article
fields = (
'author', 'body', 'createdAt', 'description',
'slug', 'title', 'updatedAt', 'score', 'images'
'slug', 'title', 'updatedAt', 'score', 'images',
'likes', 'dislikes'
)

def create(self, validated_data):
Expand All @@ -37,4 +42,31 @@ class Meta:
model = Rating
fields = ('user', 'article_id', 'score')

class ImpressionSerializer(serializers.ModelSerializer):
"""
Serializes impressions requests and adds to an Article.
"""

createdAt = serializers.SerializerMethodField(method_name='get_created_at')
updatedAt = serializers.SerializerMethodField(method_name='get_updated_at')

class Meta:
model = Impressions
fields = (
'slug',
'user',
'likes',
'dislikes',
'updatedAt',
'createdAt',
)

def get_created_at(self, instance):

return instance.created_at.isoformat()

def get_updated_at(self, instance):

return instance.updated_at.isoformat()


78 changes: 78 additions & 0 deletions authors/apps/articles/tests/test_impressions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from rest_framework.test import APIClient
from .base_test import BaseTest
from rest_framework import status


class Test_Impression(BaseTest):

def test_add_like_impression(self):
created_article = self.client.post(
'/api/articles/', data=self.new_article, format='json')
slug = self.get_slug(created_article)

reponse = self.client.post(
'/api/articles/like/{}'.format(slug),
data=self.new_article,
format='json')
self.assertEqual(reponse.status_code, status.HTTP_201_CREATED)

def test_add_dislike_impression(self):
created_article = self.client.post(
'/api/articles/', data=self.new_article, format='json')
slug = self.get_slug(created_article)

reponse = self.client.post(
'/api/articles/dislike/{}'.format(slug),
data=self.article_without_title,
format='json')
self.assertEqual(reponse.status_code, status.HTTP_201_CREATED)

def test_user_is_neutral_after_liking_twice(self):
created_article = self.client.post(
'/api/articles/', data=self.new_article, format='json')
slug = self.get_slug(created_article)

self.client.post(
'/api/articles/like/{}'.format(slug),
data=self.new_article,
format='json')
self.client.post(
'/api/articles/like/{}'.format(slug),
data=self.new_article,
format='json')
reponse = self.client.get('/api/articles/', format='json')
self.assertEqual(reponse.status_code, status.HTTP_200_OK)


def test_user_can_not_dislike_and_like_an_article(self):
created_article = self.client.post(
'/api/articles/', data=self.new_article, format='json')
slug = self.get_slug(created_article)

self.client.post(
'/api/articles/dislike/{}'.format(slug),
data=self.new_article,
format='json')
self.client.post(
'/api/articles/like/{}'.format(slug),
data=self.new_article,
format='json')
reponse = self.client.get('/api/articles/', format='json')
self.assertEqual(reponse.status_code, status.HTTP_200_OK)


def test_user_can_not_like_and_dislike_an_article(self):
created_article = self.client.post(
'/api/articles/', data=self.new_article, format='json')
slug = self.get_slug(created_article)

self.client.post(
'/api/articles/like/{}'.format(slug),
data=self.new_article,
format='json')
self.client.post(
'/api/articles/dislike/{}'.format(slug),
data=self.new_article,
format='json')
reponse = self.client.get('/api/articles/', format='json')
self.assertEqual(reponse.status_code, status.HTTP_200_OK)
6 changes: 5 additions & 1 deletion authors/apps/articles/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet, ArticleRetrieve, RatingsView
from .views import (
ArticleViewSet, ArticleRetrieve,
LikeArticle, DislikeArticle, RatingsView)


urlpatterns = [
path('articles/', ArticleViewSet.as_view()),
path('articles/<slug>', ArticleRetrieve.as_view()),
path('articles/<slug>/ratings/', RatingsView.as_view()),
path('articles/like/<slug>', LikeArticle.as_view()),
path('articles/dislike/<slug>', DislikeArticle.as_view())
]
119 changes: 117 additions & 2 deletions authors/apps/articles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,22 @@
from .serializers import ArticleSerializer, RatingSerializer
from .pagination import PageNumbering
from django.db.models import Avg
from authors.apps.authentication.models import User
from authors.apps.profiles.models import UserProfile
from rest_framework.permissions import (
IsAuthenticatedOrReadOnly, IsAuthenticated)
from rest_framework.response import Response
from rest_framework.exceptions import NotFound
from rest_framework.generics import (
RetrieveUpdateDestroyAPIView,
ListCreateAPIView)
from .models import (
Article, Impressions)
from .renderers import ArticleJSONRenderer
from .serializers import (
ArticleSerializer, ImpressionSerializer,
RatingSerializer)
from ..authentication.models import User
from django.db.models import Count


class ArticleViewSet(ListCreateAPIView):
Expand Down Expand Up @@ -79,8 +93,9 @@ def destroy(self, request, slug):
except Article.DoesNotExist:
raise NotFound('An article with this slug does not exist.')

self.perform_destroy(serializer_instance)
return Response(
"The Article has been successfully deleted",
"Article successfully deleted!",
status=status.HTTP_204_NO_CONTENT)


Expand Down Expand Up @@ -131,3 +146,103 @@ def update_article_rating(self, article_id):
article = Article.objects.filter(id=article_id)[0]
article.score = round(average['score__avg'], 1)
article.save()


class LikeArticle(ListCreateAPIView):
"""
article like view
"""

permission_classes = (IsAuthenticated,)

def post(self, request, slug):
user = User.objects.get(username=request.user.username)

impression = {
'user': user.id,
'likes': True,
'dislikes': False,
'slug': slug
}
self.updateimpression(impression)
try:
impression = Impressions.objects.all().filter(slug=slug, likes=True)
total_likes = impression.aggregate(Count('likes'))
article = Article.objects.get(slug=slug)
article.likes = total_likes['likes__count']
article.save()
except Article.DoesNotExist:
raise NotFound('An article with this slug does not exist.')
return Response(
{'message': 'i like this article.'},
status=status.HTTP_201_CREATED)

def updateimpression(self, impression):
try:
item = Impressions.objects.filter(
user = impression['user'],
slug = impression['slug']
)[0]
if item.likes == True:
item.likes = False
elif item.likes == False and item.dislikes == True:
item.likes = True
item.dislikes = False
item.save()
except:
serializer = ImpressionSerializer(
data=impression
)
serializer.is_valid(raise_exception=True)
serializer.save()


class DislikeArticle(ListCreateAPIView):
"""
article dislike view
"""

permission_classes = (IsAuthenticated,)

def post(self, request, slug):
user = User.objects.get(username=request.user.username)

impression = {
'user': user.id,
'likes': False,
'dislikes': True,
'slug': slug
}

self.updateimpression(impression)
try:
impression = Impressions.objects.all().filter(slug=slug, dislikes=True)
total_dislikes = impression.aggregate(Count('dislikes'))
article = Article.objects.get(slug=slug)
article.dislikes = total_dislikes['dislikes__count']
article.save()
except Article.DoesNotExist:
raise NotFound('An article with this slug does not exist.')
return Response(
{'message': 'i dislike this article'},
status=status.HTTP_201_CREATED)

def updateimpression(self, impression):
try:
item = Impressions.objects.filter(
user = impression['user'],
slug = impression['slug']
)[0]
if item.dislikes == True:
item.dislikes = False
elif item.dislikes == False and item.likes == True:
item.dislikes = True
item.likes = False
item.save()
except:
serializer = ImpressionSerializer(
data=impression
)
serializer.is_valid(raise_exception=True)
serializer.save()

19 changes: 19 additions & 0 deletions authors/apps/authentication/tests/test_user_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ def test_for_new_user(self):
response = self.client.post('/api/users/' ,data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIn ("{'message': 'User successfully Registered, check your email and click the link to verify'}", str(response.data) )

def test_for_new_user_without_email(self):
"""
Method for testing registration of a new user without an email.
"""
data = {"user": { "username":"lindsey1", "email": "", "password":"Lindseypatra1/"}}
response = self.client.post('/api/users/' ,data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
assert 'errors' in response.data

def test_for_new_user_without_username(self):
"""
Method for testing registration of a new user without a username.
"""
data = {"user": { "username":"", "email": "lindsey1@gmail.com", "password":"Lindseypatra1/"}}
response = self.client.post('/api/users/' ,data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
assert 'errors' in response.data

def test_login_unverified_user(self):
"""
Expand All @@ -62,3 +80,4 @@ def test_token_received_after_successful_login(self):
response = self.client.post('/api/users/login/',self.user_data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
assert 'token' in response.data

Loading

0 comments on commit d524cef

Please sign in to comment.