Skip to content

Commit

Permalink
Merge 31250ef into 54d51ba
Browse files Browse the repository at this point in the history
  • Loading branch information
Kasulejoseph committed Dec 19, 2018
2 parents 54d51ba + 31250ef commit c2d96a4
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 8 deletions.
14 changes: 14 additions & 0 deletions authors/apps/articles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class Article(models.Model):
favourited = models.BooleanField(default=False)
favouriteCount = models.IntegerField(default=0)
likes_count = models.IntegerField(default=0)
read_time = models.IntegerField(default=0)
views_count = models.IntegerField(default=0)

objects = models.Manager()

Expand Down Expand Up @@ -135,10 +137,22 @@ class Likes(models.Model):

like = models.BooleanField()


class Readings(models.Model):
""" model for reading stats """
author = models.ForeignKey(User, on_delete=models.CASCADE)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
viewers = models.IntegerField(default=0)

def __str__(self):
return "article_id: {}, author: {}, views: {}".format(
self.article, self.author, self.viewers)

class ComentLikes(models.Model):

comment = models.ForeignKey(Comments, on_delete=models.CASCADE)

profile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True)

like = models.BooleanField()

22 changes: 20 additions & 2 deletions authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
Article,
ArticleImg,
Tag,
Favourites, Likes
Favourites, Likes,
Readings
)


Expand All @@ -29,7 +30,8 @@ class Meta:
or response, this includes fields specified explicitly above.
"""
fields = ['title', 'body', 'description', 'tagList',
'author', 'slug', 'published', 'created_at', 'updated_at', ]
'author', 'slug', 'published', 'created_at',
'updated_at', 'read_time', 'views_count', 'likes_count' ]

def create(self, validated_data):
tags = validated_data.pop('tags', [])
Expand Down Expand Up @@ -108,3 +110,19 @@ class LikeArticleViewSerializer(serializers.ModelSerializer):
class Meta:
model = Likes
fields = ['id', 'article', 'profile', 'like']

class ReadingSerializer(serializers.ModelSerializer):
read_time = serializers.IntegerField(read_only=True)
likes_count = serializers.IntegerField(read_only=True)

def to_representation(self, instance):
response = super().to_representation(instance)
article = Article.objects.all().filter().values()[0]
response['viewers'] = article['views_count']
response['read_time'] = article['read_time']
response['likes_count '] = article['likes_count']
response['article'] = article['title']
return response
class Meta:
model = Readings
fields = ['read_time' ,'viewers', 'article', 'likes_count']
59 changes: 59 additions & 0 deletions authors/apps/articles/test/test_stats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from django.test import TestCase

import json
from ..test.base import BaseTestArticles

class TestStats(BaseTestArticles):
def test_read_statistics_updated_successfully(self):
self.client.credentials(
HTTP_AUTHORIZATION='Bearer ' + self.login_user())
response = self.client.post(
'/api/articles/', data=self.article, format='json')
res = self.client.post(
'/api/articles/{}/3'.format(self.create_article()),
format='json')
self.assertTrue(res.status_code, 200)
self.assertEqual(1, res.data['viewers'])

def test_user_count_for_a_read_is_only_one(self):
self.client.credentials(
HTTP_AUTHORIZATION='Bearer ' + self.login_user())
res = self.client.post(
'/api/articles/', data=self.article, format='json')
slug = res.data['slug']
self.client.post(
'/api/articles/{}/3'.format(slug),
format='json')
resp = self.client.post(
'/api/articles/{}/3'.format(slug),
format='json')
self.assertTrue(resp.status_code, 200)
self.assertEqual(1, resp.data['viewers'])

def test_a_read_cannot_be_recorded_when_user_hasnot_read_the_article(self):
data = {

"article": {
"title": "How to train your dragon added on the titlt",
"description": "Ever wonder how?",
"body": "You have to believe this body has beeb updated " * 100,
"tagList": ["Rails", "Golang", "magic!"],
"images": [
{
"image_url": "https://imgur.comhenry/",
"description": "image is cool"
}
]
}
}
self.client.credentials(
HTTP_AUTHORIZATION='Bearer ' + self.login_user())
resp = self.client.post(
'/api/articles/', data=data, format='json')
slug = resp.data['slug']
res = self.client.post(
'/api/articles/{}/2'.format(slug),
format='json')
self.assertTrue(res.status_code, 301)
self.assertEqual('read not recorded', res.data['message'])

4 changes: 3 additions & 1 deletion authors/apps/articles/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@
ArticleTagsAPIView,
ArticleDeleteAPIView,
FavouritesView,
LikeArticleView
LikeArticleView,
ReadingView
)

urlpatterns = [
path('articles', RetrieveArticlesAPIView.as_view(),),
path('articles/<str:slug>/like/',
LikeArticleView.as_view(), name='article-like'),
path('articles/<str:slug>/', CreateArticleView.as_view()),
path('articles/<slug>/<count>', ReadingView.as_view(), name='reading'),
path('articles/', CreateArticleView.as_view(), name='article-create'),
path('<slug>/tags/', ArticleTagsAPIView.as_view(), name='article-tags'),
path('<slug>/tags/<tag>/', ArticleDeleteAPIView.as_view(), name='delete-tag'),
Expand Down
61 changes: 57 additions & 4 deletions authors/apps/articles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
from rest_framework.response import Response
from rest_framework import exceptions
from django.template.defaultfilters import slugify

from authors.settings import RPD
from ..authentication.backends import JWTAuthentication
from ..authentication.models import User
from .renderers import ArticleJSONRenderer, ListArticlesJSONRenderer
from .models import ArticleImg, Article, Tag, Favourites, Likes
from .models import ArticleImg, Article, Tag, Favourites, Likes, Readings

from ..profiles.models import Profile

Expand All @@ -34,7 +34,8 @@
ArticleImgSerializer,
TagsSerializer,
FavouriteSerializer,
LikeArticleViewSerializer
LikeArticleViewSerializer,
ReadingSerializer

)

Expand All @@ -57,7 +58,10 @@ def post(self, request):
slug = slugify(article["title"]).replace("_", "-")
slug = slug + "-" + str(uuid.uuid4()).split("-")[-1]
article["slug"] = slug

full_article = "{} {}".format(article['title'], article['body'])
words = full_article.split()
minutes = (len(words)//RPD)
article['read_time'] = int(minutes)
current_user = User.objects.all().filter(
email=request.user).values()[0]
user_id = current_user['id']
Expand Down Expand Up @@ -412,3 +416,52 @@ def delete(self, request, slug):
serializer.save(article=current_article, profile=profile)

return Response(serializer.data, status=status.HTTP_201_CREATED)


class ReadingView(GenericAPIView):
""" class view to enable viewing readers statistics """
serializer_class = ReadingSerializer

def do_math(self, article, count):
"""
If the amount of time a user spends on an article is equal,
greater than article read time, or greater 1/2 the read time
the user is counted as to read the article. otherwise,
the use is not counted.
method returns True if the user is eligible to have
read the article and False otherwise.
"""
read_time = article.read_time
average = 0
if int(read_time) < int(count) or int(read_time) == int(count):
average = int(count)
elif int(count) != 0 and int(read_time) > int(count):
average = int(read_time) // int(count)

if average >= int(read_time)/2:
print(int(read_time)/2)
return True
return False

def post(self, request, slug, count):
"""
This class method updates the view counts on an article
"""
article = Article.objects.filter(slug=slug).first()
reader = Readings.objects.filter(author=request.user.id).filter(article=article)
if self.do_math(article, count):
if len(reader) < 1:
article.views_count += 1
article.save()
author = User.objects.get(id=request.user.id)
read_obj = Readings(author=author,article=article)
read_obj.save()
serializer = self.serializer_class(read_obj)
return Response(serializer.data, status=status.HTTP_200_OK)
else:
serializer = self.serializer_class(reader.first())
return Response(serializer.data, status=status.HTTP_200_OK)
return Response({"message":"read not recorded"}, status=status.HTTP_301_MOVED_PERMANENTLY)



17 changes: 17 additions & 0 deletions authors/apps/authentication/test/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from rest_framework.views import status
from rest_framework.test import APITestCase, APIClient
from ..models import UserManager, User
from authors.apps.articles.models import Readings, Article, Comments


class TestUsers(APITestCase):
Expand All @@ -13,6 +14,19 @@ def setUp(self):
username='', email='', password='')
self.supper = User.objects.create_superuser(
username='henry', email='antena@andela.com', password='longpass')
self.create_article = Article.objects.create(
title='hello', )
self.comment = Comments.objects.create(
comment_body='hello', author=self.user, article=self.create_article)
self.reading = Readings.objects.create(
author=self.user, article=self.create_article, viewers=1)

def test_readings(self):
self.assertTrue(self.reading)
self.assertEqual(self.reading.viewers, 1)

def test_comment_model(self):
self.assertEqual(str(self.comment), "hello")

def test_users_is_instance_of_User(self):
self.assertIsInstance(self.user, User)
Expand Down Expand Up @@ -46,3 +60,6 @@ def test_get_short_name_and_full_name(self):

def test_token_created_successfully(self):
self.assertGreater(len(User.token(self.supper)), 12)

def test_print_readings_returns_correct_format(self):
self.assertEqual(str(self.reading), "article_id: hello, author: , views: 1")
2 changes: 1 addition & 1 deletion authors/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
]

ROOT_URLCONF = 'authors.urls'

RPD = 120 #read per word
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
Expand Down

0 comments on commit c2d96a4

Please sign in to comment.