Skip to content

Commit

Permalink
Merge de90565 into fb965ce
Browse files Browse the repository at this point in the history
  • Loading branch information
BagzieGracious committed Mar 14, 2019
2 parents fb965ce + de90565 commit 5657a20
Show file tree
Hide file tree
Showing 15 changed files with 209 additions and 37 deletions.
13 changes: 13 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
version: "2"
prepare:
check:
enabled: true
plugins:
shellcheck:
enabled: true
exclude_patterns:
- "**/migrations/"
- "**/tests/"
- "**/*.md"

18 changes: 14 additions & 4 deletions authors/apps/articles/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Generated by Django 2.1.5 on 2019-03-12 14:56
# Generated by Django 2.1.5 on 2019-03-14 07:12

from django.db import migrations, models
import django.db.models.deletion

Expand All @@ -8,7 +9,6 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
('profiles', '0001_initial'),
]

operations = [
Expand All @@ -20,14 +20,24 @@ class Migration(migrations.Migration):
('body', models.TextField()),
('slug', models.SlugField(blank=True, unique=True)),
('description', models.CharField(max_length=100)),
('created_at', models.DateTimeField(auto_now_add=True)),
('likes', models.IntegerField(default=0)),
('dislikes', models.IntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True, null=True)),
('image', models.URLField(blank=True)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='profiles.Profile')),
],
options={
'ordering': ['-created_at'],
'get_latest_by': ['id'],
},
),
migrations.CreateModel(
name='ArticleLikesDislikes',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('likes', models.BooleanField(default=False, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')),
],
),
]
30 changes: 30 additions & 0 deletions authors/apps/articles/migrations/0002_auto_20190314_0712.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 2.1.5 on 2019-03-14 07:12

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
('articles', '0001_initial'),
('profiles', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='articlelikesdislikes',
name='user',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to='profiles.Profile'
),
),
migrations.AddField(
name='author',
model_name='article',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='profiles.Profile'),
),
]
9 changes: 9 additions & 0 deletions authors/apps/articles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class Article(models.Model):
body = models.TextField()
slug = models.SlugField(unique=True, blank=True)
description = models.CharField(max_length=100)
likes = models.IntegerField(default=0)
dislikes = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, null=True)
image = models.URLField(blank=True)
Expand All @@ -18,3 +20,10 @@ class Meta:

def __str__(self):
return f"{self.title}"


class ArticleLikesDislikes(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
likes = models.BooleanField(default=False, null=True)
created_at = models.DateTimeField(auto_now_add=True)
22 changes: 21 additions & 1 deletion authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from rest_framework import serializers
from .models import Article
from .models import Article, ArticleLikesDislikes

from ..profiles.serializers import ProfileSerializer

Expand All @@ -20,10 +20,30 @@ class Meta:
"body",
"author",
"image",
"likes",
"dislikes",
)
read_only_fields = (
'author',
'slug',
'created_at',
'updated_at',
)


class ArticleLikeDislikeSerializer(serializers.ModelSerializer):

class Meta:
model = ArticleLikesDislikes

fields = (
'user',
'article',
'likes',
'created_at'
)

extra_kwargs = {
'user': {'write_only': True},
'article': {'write_only': True},
}
13 changes: 13 additions & 0 deletions authors/apps/articles/tests/base_class.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework.reverse import reverse
from authors.apps.authentication.tests import test_base
from .test_data import valid_article
from ..models import Article


class ArticlesBaseTest(test_base.BaseTest):
Expand All @@ -13,3 +14,15 @@ def add_article(self):
self.client.post(self.articles_url,
data=valid_article,
format='json')

def like_article(self):
article = Article.objects.all().first()
return self.client.post(
reverse(
'articles:article-like',
kwargs={
"slug": article.slug
}
),
format='json'
)
16 changes: 16 additions & 0 deletions authors/apps/articles/tests/test_articles.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,19 @@ def test_delete_innexistent_article(self):
'errors': 'that article was not found'}
self.assertDictEqual(expected_dict, response.data)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_successful_article_like(self):
"""Tests if a user can like an article successfully."""
self.add_article()
article = Article.objects.all().first()
response = self.like_article()
self.assertEqual(response.data['details']['likes'], True)
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_successful_article_dislike(self):
"""Tests if a user can like an article successfully."""
self.add_article()
self.like_article()
response = self.like_article()
self.assertEqual(response.data['details']['likes'], False)
self.assertEqual(response.status_code, status.HTTP_200_OK)
18 changes: 15 additions & 3 deletions authors/apps/articles/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
from django.urls import path

from .views import ArticlesApiView, ArticleDetailApiView
from .views import (
ArticlesApiView, ArticleDetailApiView,
ArticleLikeApiView,
)

urlpatterns = [
path('articles/', ArticlesApiView.as_view(), name='articles'),
path('articles/<slug>/', ArticleDetailApiView.as_view(),
name='article-detail'),
path(
'articles/<slug>/',
ArticleDetailApiView.as_view(),
name='article-detail'
),
path(
'articles/<slug>/like/',
ArticleLikeApiView.as_view(),
name='article-like'
),
]
55 changes: 54 additions & 1 deletion authors/apps/articles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
)
from .renderers import ArticleJSONRenderer
from .utils import Utils
from authors.apps.articles.models import Article
from authors.apps.articles.models import Article, ArticleLikesDislikes
from ..profiles.models import Profile


Expand Down Expand Up @@ -92,3 +92,56 @@ def delete(self, request, slug):

def get_object(self, slug):
return Article.objects.filter(slug=slug).first()


class ArticleLikeApiView(generics.GenericAPIView):

serializer_class = serializers.ArticleLikeDislikeSerializer

def post(self, request, slug):
"""This function enables a user to like or dislike an article."""
article = Article.objects.filter(slug=slug).first()
liked_article = ArticleLikesDislikes.objects.filter(
article_id=article.id,
user_id=request.user.id
)

if len(liked_article) <= 0:
# when a user likes the article for the first time
# the article is liked.
serializer_data = self.serializer_class(data={
"user": request.user.id,
"article": article.id,
"likes": True
})
serializer_data.is_valid(raise_exception=True)
serializer_data.save()
data = {
"article": article.title,
"username": request.user.username,
"details": serializer_data.data
}
else:
# this section is triggered when a user clicks the endpoint the
# the second time, and it is toggled
value = not ((liked_article.first()).likes)
liked_article.update(likes=value)
data = {
"article": article.title,
"username": request.user.username,
"details": {
"likes": liked_article.first().likes,
"created_at": liked_article.first().created_at
}
}
# updates the number of likes and dislikes of a given article
likes = ArticleLikesDislikes.objects.filter(
article_id=article.id, likes=True)
dislikes = ArticleLikesDislikes.objects.filter(
article_id=article.id, likes=False)
Article.objects.filter(slug=slug).update(
likes=(len(likes)),
dislikes=(len(dislikes)),
)

return Response(data, status=status.HTTP_200_OK)
3 changes: 2 additions & 1 deletion authors/apps/authentication/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-02-26 10:59
# Generated by Django 2.1.5 on 2019-03-14 07:12

from django.db import migrations, models

Expand All @@ -23,6 +23,7 @@ class Migration(migrations.Migration):
('email', models.EmailField(db_index=True, max_length=254, unique=True)),
('is_active', models.BooleanField(default=True)),
('is_staff', models.BooleanField(default=False)),
('is_verified', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
Expand Down
18 changes: 0 additions & 18 deletions authors/apps/authentication/migrations/0002_user_is_verified.py

This file was deleted.

3 changes: 1 addition & 2 deletions authors/apps/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ class Utilities:
def email_renderer(data):
"""This function sends email to users."""

url = f"http://{get_current_site(data[0]).domain}\
/api/users/{data[1]}?token={data[2]}"
url = f"http://{get_current_site(data[0]).domain}/api/users/{data[1]}?token={data[2]}"
subject = f"[Authors Heaven] {data[3]}"
body = f"Hello, \
\nYou are receiving this e-mail because you have {data[4]}' \
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-06 06:58
# Generated by Django 2.1.5 on 2019-03-14 07:12

from django.conf import settings
from django.db import migrations, models
Expand Down
26 changes: 20 additions & 6 deletions authors/apps/profiles/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,24 @@
ListAuthorsAPIView, ProfileUpdateAPIView, AuthorsAPIView)

urlpatterns = [
path('profiles/<username>', ProfileRetrieveAPIView.as_view(), name="get_profile"),
path('profiles/<username>/edit',
ProfileUpdateAPIView.as_view(), name="update_profile"),
path('profiles/', ListAuthorsAPIView.as_view(), name="users_profiles"),
path('authorslist/', AuthorsAPIView.as_view(), name="authors_list"),

path(
'profiles/<username>',
ProfileRetrieveAPIView.as_view(),
name="get_profile"
),
path(
'profiles/<username>/edit',
ProfileUpdateAPIView.as_view(),
name="update_profile"
),
path(
'profiles/',
ListAuthorsAPIView.as_view(),
name="users_profiles"
),
path(
'authorslist/',
AuthorsAPIView.as_view(),
name="authors_list"
),
]
Empty file.

0 comments on commit 5657a20

Please sign in to comment.