Skip to content

Commit

Permalink
Merge pull request #39 from andela/ft-fetch-all-articles-160233594
Browse files Browse the repository at this point in the history
#160233594 Fetch all articles
  • Loading branch information
AGMETEOR committed Sep 4, 2018
2 parents 99a25c5 + 1d2d27a commit ad3ff6c
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 49 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ omit =
*email_reg_util*
*reset_password_util*
*app_util*
*/media/*

6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,9 @@ db.sqlite3
profile_image/
#sendgrid file
sendgrid.env

#media files
media/

#migrations
migrations/
93 changes: 53 additions & 40 deletions authors/apps/articles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,34 @@


class Article(models.Model):
title = models.CharField(max_length = 500)
title = models.CharField(max_length=500)
description = models.TextField()
slug = models.SlugField(max_length = 255, blank = True)
slug = models.SlugField(max_length=255, blank=True)
body = models.TextField()
created_at = models.DateTimeField(editable = False)
updated_at = models.DateTimeField(blank = True, null = True)
author = models.ForeignKey(User, related_name = "user_articles", on_delete = models.CASCADE)
favorited = models.BooleanField(default = False)
favoritesCount = 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)
likesCount = models.IntegerField(default = 0)
dislikes = models.ManyToManyField(User,related_name="dislikes",blank=True)
dislikesCount = models.IntegerField(default = 0)
favorites = models.ManyToManyField(User,related_name="favorites",blank=True)
created_at = models.DateTimeField(editable=False)
updated_at = models.DateTimeField(blank=True, null=True)
author = models.ForeignKey(
User, related_name="user_articles", on_delete=models.CASCADE)
favorited = models.BooleanField(default=False)
favoritesCount = 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)
likesCount = models.IntegerField(default=0)
dislikes = models.ManyToManyField(
User, related_name="dislikes", blank=True)
dislikesCount = models.IntegerField(default=0)
favorites = models.ManyToManyField(
User, related_name="favorites", blank=True)

def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title).replace("_","-")
self.slug = slugify(self.title).replace("_", "-")
article_class = self.__class__
qs_exists = article_class.objects.filter(slug=self.slug)
if qs_exists:
self.slug = self.slug + "-" + str(uuid.uuid4()).replace("_","-")
self.slug = self.slug + "-" + \
str(uuid.uuid4()).replace("_", "-")
self.created_at = timezone.now()
return super(Article, self).save(*args, **kwargs)
self.updated_at = timezone.now()
Expand All @@ -43,27 +47,31 @@ class Meta:
def __str__(self):
return self.title


class ArticleImage(models.Model):
article = models.ForeignKey(Article, related_name = "article_images", on_delete = models.CASCADE)
image = models.ImageField()
article = models.ForeignKey(
Article, related_name="article_images", on_delete=models.CASCADE)
image = models.ImageField(upload_to='article_images/%Y/%m/%d', blank=True)



class Rating(models.Model):
RATING_CHOICES = (
(1,1),
(2,2),
(3,3),
(4,4),
(5,5),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
)
user = models.ForeignKey(User, related_name = "user_article_rating", on_delete = models.CASCADE, blank = True)
article = models.ForeignKey(Article, related_name = "article_ratings",on_delete = models.CASCADE, blank = True)
amount = models.PositiveIntegerField(choices = RATING_CHOICES)
user = models.ForeignKey(
User, related_name="user_article_rating", on_delete=models.CASCADE, blank=True)
article = models.ForeignKey(
Article, related_name="article_ratings", on_delete=models.CASCADE, blank=True)
amount = models.PositiveIntegerField(choices=RATING_CHOICES)

class Meta:
ordering = ('-amount', )

def av_rating(self,qs_set, new_rating = None):
def av_rating(self, qs_set, new_rating=None):
if new_rating:
new_qs_set_ratings = [rating.amount for rating in qs_set]
new_qs_set_ratings.append(new_rating)
Expand All @@ -73,30 +81,35 @@ def av_rating(self,qs_set, new_rating = None):

def save(self, *args, **kwargs):
rating_class = self.__class__
qs_exists = rating_class.objects.filter(article = self.article).filter(user = self.user)
qs_exists = rating_class.objects.filter(
article=self.article).filter(user=self.user)
if not qs_exists:
existing_ratings = self.article.article_ratings.all()
existing_ratings = self.article.article_ratings.all()
if existing_ratings:
Article.objects.filter(pk = self.article.id).update(rating = self.av_rating(existing_ratings, self.amount))
Article.objects.filter(pk=self.article.id).update(
rating=self.av_rating(existing_ratings, self.amount))
return super(Rating, self).save(*args, **kwargs)
qs_exists.update(amount = self.amount)
qs_exists.update(amount=self.amount)
ratings = self.article.article_ratings.all()
Article.objects.filter(pk = self.article.id).update(rating = self.av_rating(ratings))
return
Article.objects.filter(pk=self.article.id).update(
rating=self.av_rating(ratings))
return

def __str__(self):
return 'Rating of {} on {} by user {}'.format(self.amount, self.article, self.user)


class Comment(models.Model):
author = models.ForeignKey(User, related_name = "comment_author", on_delete = models.CASCADE)
article = models.ForeignKey(Article,related_name = "user_comments", on_delete = models.CASCADE, blank = True)
author = models.ForeignKey(
User, related_name="comment_author", on_delete=models.CASCADE)
article = models.ForeignKey(
Article, related_name="user_comments", on_delete=models.CASCADE, blank=True)
body = models.TextField()
created_at = models.DateTimeField(editable = False,auto_now=True)
updated_at = models.DateTimeField(blank = True, null = True,auto_now_add=True)
created_at = models.DateTimeField(editable=False, auto_now=True)
updated_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)

class Meta:
ordering = ('-created_at',)

def __str__(self):
return self.body


13 changes: 12 additions & 1 deletion authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
from taggit_serializer.serializers import (
TagListSerializerField, TaggitSerializer)
from django.utils import timezone
from .models import ArticleImage


class CreateArticleSerializer(TaggitSerializer, serializers.ModelSerializer):

tags = TagListSerializerField(required=False)
author = serializers.SerializerMethodField()
article_images = serializers.SerializerMethodField()

def get_author(self, obj):
user = {
Expand All @@ -20,10 +22,19 @@ def get_author(self, obj):
}
return user

def get_article_images(self, obj):
image_array = []
images = obj.article_images.all()

for image in images:
image_array.append(image.image.url)

return image_array

class Meta:
model = Article
fields = ['slug', 'title', 'description', 'body', 'created_at', 'updated_at',
'author', 'favorited', 'favoritesCount', 'likesCount', 'dislikesCount', 'tags']
'author', 'favorited', 'favoritesCount', 'likesCount', 'dislikesCount', 'tags','article_images']


class RateArticleSerializer(serializers.ModelSerializer):
Expand Down
50 changes: 50 additions & 0 deletions authors/apps/articles/tests/test_fetch_all_articles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from django.test import TestCase, RequestFactory
from authors.apps.articles.views import ListAllArticlesAPIView, ArticleCreateAPIView
from authors.apps.authentication.views import RegistrationAPIView, VerificationAPIView


class FetchAllArticlesTestCase(TestCase):
def setUp(self):

self.user = {
"user": {
"email": "test@gmail.com",
"username": "tester",
"password": "testpass@word"
}
}

self.obj = UtilClass()
registered_user = self.obj.get_reg_data(self.user)
self.obj.verify_user({"token": registered_user.data["token"]})
logged_in_user = self.obj.get_login_data(self.user)

self.headers = {
'HTTP_AUTHORIZATION': 'Token ' + logged_in_user.data["token"]
}

def test_fetch_all_existing_articles(self):
article_data = {
"title": "This is Postman",
"description": "Hello Postman",
"body": "Hahahaha",
"tags": ["post", "man"]
}

make_article_request = self.factory.post('api/articles', data=json.dumps(
article_data), **self.headers, content_type='application/json')

ArticleCreateAPIView.as_view()(make_article_request)

request = self.factory.get('api/all/articles/')
response = ListAllArticlesAPIView.as_view()(request)
self.assertEqual(response.data['results'][count], 1)
self.assertEqual(
response.data['results']['articles'][0]['title'], "This is Postman")

def test_fetch_all_from_empty_db(self):
request = self.factory.get('api/all/articles/')
response = ListAllArticlesAPIView.as_view()(request)
self.assertEqual(response.data['results'][count], 0)
self.assertEqual(
len(response.data['results']['articles']), 0)
2 changes: 2 additions & 0 deletions authors/apps/articles/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
UpdateArticleAPIView,
SearchArticlesAPIView,
DeleteArticleAPIView,
ListAllArticlesAPIView,
)

app_name = "articles"
Expand All @@ -24,4 +25,5 @@
path('articles/<slug>/favorite', FavoriteArticleAPIView.as_view()),
path('articles/search', SearchArticlesAPIView.as_view()),
path('articles/<slug>/delete/', DeleteArticleAPIView.as_view()),
path('articles/all/', ListAllArticlesAPIView.as_view()),
]
11 changes: 10 additions & 1 deletion authors/apps/articles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
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
from rest_framework.permissions import IsAuthenticated, AllowAny
from authors.apps.authentication.backends import JWTAuthentication
from rest_framework.views import APIView
from rest_framework import status, exceptions
Expand Down Expand Up @@ -186,6 +186,15 @@ def get_queryset(self):
filtered_queryset.append(article)
return filtered_queryset

class ListAllArticlesAPIView(ListAPIView):
permission_classes = (AllowAny, )
serializer_class = CreateArticleSerializer
renderer_classes = (ListArticlesJSONRenderer,)
pagination_class = PageNumberPagination
def get_queryset(self):
articles = Article.objects.all()
return articles


class UpdateArticleAPIView(UpdateAPIView):
permission_classes = (IsAuthenticated, )
Expand Down
11 changes: 7 additions & 4 deletions authors/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,14 @@
'NON_FIELD_ERRORS_KEY': 'error',

'DEFAULT_AUTHENTICATION_CLASSES': (
'authors.apps.authentication.backends.JWTAuthentication',
),
'authors.apps.authentication.backends.JWTAuthentication',
),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 5
}

EMAIL_BACKEND="djmail.backends.default.EmailBackend"
DJMAIL_REAL_BACKEND="django.core.mail.backends.console.EmailBackend"
EMAIL_BACKEND = "djmail.backends.default.EmailBackend"
DJMAIL_REAL_BACKEND = "django.core.mail.backends.console.EmailBackend"

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
10 changes: 7 additions & 3 deletions authors/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@
from django.urls import include, path
from django.contrib import admin
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token
from django.conf.urls.static import static
from authors.settings.base import MEDIA_URL, MEDIA_ROOT


urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('authors.apps.articles.urls')),
path('api/', include('authors.apps.articles.urls')),

path('api/', include('authors.apps.authentication.urls', namespace='authentication')),
path('api/', include('authors.apps.authentication.urls',
namespace='authentication')),
path('api/profiles/', include('authors.apps.profiles.urls', namespace='profiles')),
path('api/users/', include('authors.apps.follower.urls', namespace = "follows"))
path('api/users/', include('authors.apps.follower.urls', namespace="follows"))
]

urlpatterns += static(MEDIA_URL, document_root=MEDIA_ROOT)

0 comments on commit ad3ff6c

Please sign in to comment.