Skip to content

Commit

Permalink
Merge 6c09495 into 811de0f
Browse files Browse the repository at this point in the history
  • Loading branch information
Peace-Apple committed Mar 21, 2019
2 parents 811de0f + 6c09495 commit a3b8b62
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 13 deletions.
14 changes: 12 additions & 2 deletions authors/apps/articles/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 2.1.5 on 2019-03-21 11:56
# Generated by Django 2.1.5 on 2019-03-21 21:05

from django.conf import settings
import django.contrib.postgres.fields
Expand All @@ -11,8 +11,8 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
('profiles', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('profiles', '0001_initial'),
]

operations = [
Expand All @@ -33,6 +33,7 @@ class Migration(migrations.Migration):
('tagList', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, default=list, size=None)),
('favorited', models.BooleanField(default=False)),
('favoritesCount', models.IntegerField(default=0)),
('read_stats', models.IntegerField(default=0)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='author_articles', to='profiles.Profile')),
('favorites', models.ManyToManyField(blank=True, related_name='favorited_articles', to='profiles.Profile')),
],
Expand Down Expand Up @@ -73,6 +74,15 @@ class Migration(migrations.Migration):
'ordering': ['-score'],
},
),
migrations.CreateModel(
name='ReadingStats',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('read_stats', models.IntegerField(default=0)),
('article', models.ForeignKey(blank=True, 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)),
],
),
migrations.CreateModel(
name='ReportedArticle',
fields=[
Expand Down
17 changes: 16 additions & 1 deletion authors/apps/articles/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from rest_framework.response import Response
from rest_framework import status

Expand Down Expand Up @@ -41,6 +41,7 @@ def unfavorite_an_article(self, request_user, slug):
return Response({"message": "Article has been removed from favorites"},
status=status.HTTP_200_OK)


class Article(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(
Expand All @@ -65,7 +66,9 @@ class Article(models.Model):
favorites = models.ManyToManyField(Profile, related_name='favorited_articles', blank=True)
favorited = models.BooleanField(default=False)
favoritesCount = models.IntegerField(default=0)
read_stats = models.IntegerField(default=0)
objects = ArticleManager()

class Meta:
ordering = ['-created_at']
get_latest_by = ['id']
Expand Down Expand Up @@ -137,8 +140,20 @@ class Meta:

def __str__(self):
return self.reason


class Bookmark(models.Model):
"""Model for creating bookmarks of an article by a user."""
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=False)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
bookmarked_at = models.DateTimeField(auto_now_add=True)


class ReadingStats(models.Model):
"""Model for viewing reading stats of a user"""
article = models.ForeignKey(Article, on_delete=models.CASCADE, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
read_stats = models.IntegerField(default=0)

def __str__(self):
return "article: {}, user: {}, read_stats: {}".format(self.article, self.user, self.read_stats)
20 changes: 17 additions & 3 deletions authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from rest_framework import serializers
from authors.apps.comments.models import Comment
from .models import Article, ArticleLikesDislikes, Rating, ReportedArticle

from .models import Article, ArticleLikesDislikes, Rating, Bookmark
from .models import Article, ArticleLikesDislikes, Rating, ReportedArticle, Bookmark, ReadingStats

from ..profiles.serializers import ProfileSerializer

Expand All @@ -15,6 +13,7 @@ class ArticleSerializer (serializers.ModelSerializer):
read_time = serializers.CharField(max_length=100, read_only=True)
favorites = serializers.SerializerMethodField()
comments = serializers.SerializerMethodField()
read_stats = serializers.SerializerMethodField()

class Meta:
model = Article
Expand All @@ -37,6 +36,7 @@ class Meta:
"favorites",
"favoritesCount",
"comments",
"read_stats",
)
read_only_fields = (
'author',
Expand Down Expand Up @@ -68,6 +68,10 @@ def get_comments(self,obj):
return comment_data


def get_read_stats(self, obj):
return ReadingStats.objects.filter(article=obj).count()


class ArticleLikeDislikeSerializer(serializers.ModelSerializer):

class Meta:
Expand Down Expand Up @@ -116,9 +120,19 @@ class ReportedArticleSerializer(serializers.ModelSerializer):
class Meta:
model = ReportedArticle
fields = ('id', 'reporter', 'article', 'reason',)


class BookmarkSerializer(serializers.ModelSerializer):
article = ArticleSerializer(read_only=True)

class Meta:
model = Bookmark
fields = ('id', 'article', 'bookmarked_at',)


class ReadingStatsSerializer(serializers.ModelSerializer):

class Meta:
model = ReadingStats
fields = ('article', 'read_stats', 'user')
read_only = ('article', 'user', )
27 changes: 27 additions & 0 deletions authors/apps/articles/tests/test_read_stats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.urls import reverse
from authors.apps.articles.tests.base_class import ArticlesBaseTest
from ..models import Article


class TestReadStats(ArticlesBaseTest):
"""Read stats tests"""
def setUp(self):
super().setUp()

def test_an_author_reading_their_own_article(self):
"""Test for an author reading their own article"""
self.add_article()
article = Article.objects.all().first()
url = reverse("articles:article-detail", kwargs={'slug': article.slug})
response = self.client.get(url)
print(response.data)
self.assertEqual(response.status_code, 200)

def test_an_author_reading_another_authors_article(self):
"""Test for an author reading another authors article"""
self.add_article()
self.register_and_login_new_user()
article = Article.objects.all().first()
url = reverse("articles:article-detail", kwargs={'slug': article.slug})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
5 changes: 3 additions & 2 deletions authors/apps/articles/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
ArticlesApiView, ArticleDetailApiView,
ArticleLikeApiView, RateArticleView,
FavoriteHandlerView, ArticleTagsApiView, ReportArticleView,
BookmarksApiView,BookmarksListView)
BookmarksApiView, BookmarksListView, ReadingStatsApiView)

urlpatterns = [
path('articles/', ArticlesApiView.as_view(), name='articles'),
Expand All @@ -29,7 +29,8 @@
name='article-tags'
),
path('articles/<slug>/favorite', FavoriteHandlerView.as_view(),
name='article-favorite'),
name='article-favorite'),
path('articles/<slug>/bookmark/', BookmarksApiView.as_view(), name="bookmark_article"),
path('bookmarks/', BookmarksListView.as_view(), name="articles_bookmarked"),
path('<username>/readstats/', ReadingStatsApiView.as_view(), name='reading-stats'),
]
30 changes: 28 additions & 2 deletions authors/apps/articles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
from rest_framework import status, filters
from rest_framework import generics, permissions

from authors.apps.articles.serializers import ReadingStatsSerializer, ArticleSerializer
from . import (
serializers,
permissions as app_permissions
)
from .pagination import ArticlesLimitOffsetPagination
from .renderers import ArticleJSONRenderer
from .utils import Utils
from authors.apps.articles.models import Article, ArticleLikesDislikes, Rating, Bookmark
from authors.apps.articles.models import Article, ArticleLikesDislikes, Rating, Bookmark, ReadingStats
from ..profiles.models import Profile
from authors.apps.core.utils import Utilities

Expand Down Expand Up @@ -87,9 +88,17 @@ def get(self, request, slug):
article = self.get_object(slug)
context = {"request": request}
if not article:
return Response({'errors': 'that article was not found'}, status=status.HTTP_404_NOT_FOUND)
return Response({
'errors': 'that article was not found'
}, status=status.HTTP_404_NOT_FOUND)
user = request.user

serialized_data = self.serializer_class(article, context=context)

if request.auth and (user.id != article.author.pk):

ReadingStats.objects.create(article=article, user=user)

return Response(serialized_data.data, status=status.HTTP_200_OK)

def patch(self, request, slug):
Expand Down Expand Up @@ -311,3 +320,20 @@ def get(self, request):
return Response({'message': 'You have not bookmarked any articles yet'})
serializer = self.serializer_class(bookmarks, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)


class ReadingStatsApiView(generics.ListAPIView):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = ReadingStatsSerializer

def get(self, request, username):
user = request.user.id
read_stats = ReadingStats.objects.filter(user=user).all()
if username == request.user.username:

count = read_stats.count()
serializer = self.serializer_class(read_stats, many=True)
return Response({'Number of articles read': count, 'Articles read': serializer.data},
status=status.HTTP_200_OK)
return Response({'error': 'Access denied, please ensure that you are authenticated and username is correct'},
status=status.HTTP_403_FORBIDDEN)
4 changes: 2 additions & 2 deletions authors/apps/comments/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 2.1.5 on 2019-03-21 12:48
# Generated by Django 2.1.5 on 2019-03-21 21:05

from django.conf import settings
from django.db import migrations, models
Expand All @@ -11,8 +11,8 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
('articles', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('articles', '0001_initial'),
('profiles', '0001_initial'),
]

Expand Down
2 changes: 1 addition & 1 deletion authors/apps/profiles/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 2.1.5 on 2019-03-21 09:50
# Generated by Django 2.1.5 on 2019-03-21 21:05

import datetime
from django.conf import settings
Expand Down

0 comments on commit a3b8b62

Please sign in to comment.