Skip to content

Commit

Permalink
feat(reports): Add user create report of article functionality
Browse files Browse the repository at this point in the history
- Modify article table
- Create model for reports
- Create view function for adding a report to an article
- Add logic to prevent the same user reporting on an article more than once

[Starts: #165273492]
  • Loading branch information
KabohaJeanMark committed May 8, 2019
1 parent 2a9c8c7 commit 838a13b
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 26 deletions.
5 changes: 4 additions & 1 deletion authors/apps/articles/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from django.contrib import admin

# Register your models here.
from .models import Article, Report

admin.site.register(Article)
admin.site.register(Report)
44 changes: 44 additions & 0 deletions authors/apps/articles/migrations/0004_auto_20190508_0526.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 2.1 on 2019-05-08 05:26

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


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('articles', '0003_auto_20190505_1829'),
]

operations = [
migrations.CreateModel(
name='Report',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('reason', models.TextField()),
('date_reported', models.DateTimeField(auto_now_add=True)),
],
),
migrations.AddField(
model_name='article',
name='report_count',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='article',
name='reported',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='report',
name='article',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article'),
),
migrations.AddField(
model_name='report',
name='reporter',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='username'),
),
]
20 changes: 16 additions & 4 deletions authors/apps/articles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ class Article(models.Model):
"""
Model class for creating an article
"""

title = models.CharField(max_length=120)
slug = models.SlugField(blank=True,
null=True)
Expand All @@ -21,6 +20,8 @@ class Article(models.Model):
updated_at = models.DateTimeField(auto_now_add=True)
likes_count = models.IntegerField(default=0)
dislikes_count = models.IntegerField(default=0)
reported = models.BooleanField(default=False)
report_count = models.IntegerField(default=0)
favorited = models.BooleanField(default=False)
favorite_count = models.IntegerField(default=0)
tagList = models.ManyToManyField(ArticleTag, related_name='articles')
Expand All @@ -39,7 +40,6 @@ def slug_generator(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = unique_slug_generator(instance)


pre_save.connect(slug_generator, sender=Article)


Expand All @@ -52,5 +52,17 @@ class Like_Dislike(models.Model):
reviewer = models.ForeignKey(
User, to_field='username', on_delete=models.CASCADE, null=False)
article = models.ForeignKey(
Article, to_field='id', on_delete=models.CASCADE, null=False
)
Article, to_field='id', on_delete=models.CASCADE, null=False)


class Report(models.Model):
"""
Model class for reporting an article to the administrator
for plagiarism or violating terms of agreement
"""
article = models.ForeignKey(
Article, to_field='id', on_delete=models.CASCADE, null=False)
reason = models.TextField()
reporter = models.ForeignKey(
User, to_field='username', on_delete=models.CASCADE, null=False)
date_reported = models.DateTimeField(auto_now_add=True, editable=False)
18 changes: 17 additions & 1 deletion authors/apps/articles/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from rest_framework import serializers

from authors.apps.article_tags.models import ArticleTag
from .models import Article
from .models import Article, Report
from .validations import ValidateArticleCreation


Expand Down Expand Up @@ -70,3 +70,19 @@ class Meta:
)
read_only_fields = ('created_at', 'updated_at', 'author',
'favorited', 'favorite_count', 'slug')


class ReportSerializer(serializers.ModelSerializer):
""" Class to serialize the data in the report body """
reason = serializers.CharField(
required=True,
error_messages={
"required": "The reason field is required",
"blank": "The reason field cannot be left blank"
}
)

class Meta:
"""class for returning our fields."""
model = Report
fields = '__all__'
34 changes: 34 additions & 0 deletions authors/apps/articles/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,23 @@ def setUp(self):
admin_test_token = self.admin_login_response.data['token']
self.auth_header = 'Bearer {}'.format(admin_test_token)

self.user2 = User.objects.create_superuser(
username="superuser", email="super@gmail.com", password="SuperPassword1234")
setattr(self.user2, 'email_verified', True)
self.user2.save()

self.super_login_data = {
"user": {
"email": "super@gmail.com",
"password": "SuperPassword1234"
}
}

self.super_login_response = self.client.post(
self.login_url, self.super_login_data, format='json')
super_test_token = self.super_login_response.data['token']
self.super_auth_header = 'Bearer {}'.format(super_test_token)

self.create_article_data = {
"title": "Fresh kid wonders on stage at lugogo",
"description": "he wows the kids",
Expand Down Expand Up @@ -100,3 +117,20 @@ def setUp(self):
"author": self.user.username,
"tagList": ["edna"]
}

self.report = {
"report": {
"reason": "Plagiarism. He stole intellectual property and refused to site"
}
}

self.report_with_blank_reason = {
"report": {
"reason": ""
}
}

self.report_with_no_reason = {
"report": {
}
}
134 changes: 134 additions & 0 deletions authors/apps/articles/tests/test_reports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from django.urls import reverse

from rest_framework import status

from .base import BaseTestCase
from authors.apps.authentication.models import User


class ReportTest(BaseTestCase):
"""
Class Test Case for Testing reporting functionality
"""

def test_user_report_article_status(self):
"""
Method tests the status on report article response
"""
url = reverse('articles-list-create')
response = self.client.post(
url, self.create_article_data, HTTP_AUTHORIZATION=self.auth_header, format="json")
article_id = response.data['article']['id']
url = '/api/articles/{}/report/'.format(article_id)
response = self.client.post(
url, self.report, HTTP_AUTHORIZATION=self.auth_header, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_user_report_article_message(self):
"""
Method tests the message when a report is successfull
"""
url = reverse('articles-list-create')
response = self.client.post(
url, self.create_article_data, HTTP_AUTHORIZATION=self.auth_header, format="json")
article_id = response.data['article']['id']
url = '/api/articles/{}/report/'.format(article_id)
response = self.client.post(
url, self.report, HTTP_AUTHORIZATION=self.auth_header, format="json")
self.assertEqual(response.data['message'],
"Report successfully created.")

def test_user_report_article_twice_message(self):
"""
Method tests the message when the same user reports an article twice
"""
url = reverse('articles-list-create')
response = self.client.post(
url, self.create_article_data, HTTP_AUTHORIZATION=self.auth_header, format="json")
article_id = response.data['article']['id']
url = '/api/articles/{}/report/'.format(article_id)
response = self.client.post(
url, self.report, HTTP_AUTHORIZATION=self.auth_header, format="json")
response = self.client.post(
url, self.report, HTTP_AUTHORIZATION=self.auth_header, format="json")
self.assertEqual(response.data['error'],
"You already reported this article")

def test_user_report_article_blank_reason(self):
"""
Method tests the message when a reason is blank
"""
url = reverse('articles-list-create')
response = self.client.post(
url, self.create_article_data, HTTP_AUTHORIZATION=self.auth_header, format="json")
article_id = response.data['article']['id']
url = '/api/articles/{}/report/'.format(article_id)
response = self.client.post(
url, self.report_with_blank_reason, HTTP_AUTHORIZATION=self.auth_header, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_user_report_article_no_reason(self):
"""
Method tests the message when there is no reason in request body
"""
url = reverse('articles-list-create')
response = self.client.post(
url, self.create_article_data, HTTP_AUTHORIZATION=self.auth_header, format="json")
article_id = response.data['article']['id']
url = '/api/articles/{}/report/'.format(article_id)
response = self.client.post(
url, self.report_with_no_reason, HTTP_AUTHORIZATION=self.auth_header, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_user_without_permisions_view_report(self):
"""
Method tests whether a non admin user can view all reports endpoint
"""
url = '/api/articles/reports/'
response = self.client.get(
url, HTTP_AUTHORIZATION=self.auth_header, format="json")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_user_without_permisions_view_report_message(self):
"""
Method tests message when a non admin user can view all reports endpoint
"""
url = '/api/articles/reports/'
response = self.client.get(
url, HTTP_AUTHORIZATION=self.auth_header, format="json")
self.assertEqual(
response.data['error'], "You do not have permission to view the reported articles")

def test_user_with_permisions_view_report_status(self):
"""
Method tests status when a admin user can view all reports endpoint
"""
url = reverse('articles-list-create')
response = self.client.post(
url, self.create_article_data, HTTP_AUTHORIZATION=self.auth_header, format="json")
article_id = response.data['article']['id']
url = '/api/articles/{}/report/'.format(article_id)
response = self.client.post(
url, self.report, HTTP_AUTHORIZATION=self.auth_header, format="json")
url = '/api/articles/reports/'
response = self.client.get(
url, HTTP_AUTHORIZATION=self.super_auth_header, format="json")
self.assertEqual(
response.status_code, status.HTTP_200_OK)

def test_user_with_permisions_view_report_message(self):
"""
Method tests status when a admin user can view all reports endpoint
"""
url = reverse('articles-list-create')
response = self.client.post(
url, self.create_article_data, HTTP_AUTHORIZATION=self.auth_header, format="json")
article_id = response.data['article']['id']
url = '/api/articles/{}/report/'.format(article_id)
response = self.client.post(
url, self.report, HTTP_AUTHORIZATION=self.auth_header, format="json")
url = '/api/articles/reports/'
response = self.client.get(
url, HTTP_AUTHORIZATION=self.super_auth_header, format="json")
self.assertIn(
"report", str(response.data))
9 changes: 7 additions & 2 deletions authors/apps/articles/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.urls import path
from .views import RetrieveUpdateDestroyArticle, Like, Dislike, ListArticles, CreateArticles
from .views import RetrieveUpdateDestroyArticle, Like, Dislike,\
ListArticles, CreateArticles, Reports, ReportView


urlpatterns = [
Expand All @@ -9,5 +10,9 @@
path('articles/<int:pk>/', RetrieveUpdateDestroyArticle.as_view(),
name="article-get-update-delete"),
path('articles/<article_id>/like/', Like.as_view(), name="like-article"),
path('articles/<article_id>/dislike/', Dislike.as_view(), name="dislike-article")
path('articles/<article_id>/dislike/',
Dislike.as_view(), name="dislike-article"),
path('articles/<article_id>/report/',
Reports.as_view(), name="report-article"),
path('articles/reports/', ReportView.as_view(), name="view-reports"),
]
2 changes: 0 additions & 2 deletions authors/apps/articles/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ def unique_slug_generator(instance, new_slug=None, **kwargs):
if new_slug is not None:
slug = new_slug
slug = slugify(instance.title)
print(slug)

Klass = instance.__class__
qs_exists = Klass.objects.filter(slug=slug).exists()
if qs_exists:
random_string = random_string_generator(size=4)
new_slug = slug+"-"+random_string
print(new_slug)
return new_slug
return slug
Loading

0 comments on commit 838a13b

Please sign in to comment.