-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
495 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,4 +100,3 @@ db.sqlite3 | |
# vscode | ||
.vscode/ | ||
# migrations | ||
migrations/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from rest_framework.exceptions import APIException | ||
|
||
|
||
class ArticleNotFound(APIException): | ||
"""exception message""" | ||
status_code = 404 | ||
default_detail = "Sorry we couldn't find that article." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Generated by Django 2.2 on 2019-05-16 13:03 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
('articles', '0001_initial'), | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='EscalationModel', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('timestamp', models.DateTimeField(auto_now_add=True)), | ||
('reason', models.CharField(choices=[('Plagiarism', 'Reusing content without attribution (link and blockquotes)'), ('Rule Violation', 'Violates terms of agreement'), ('Spam', 'Creating multiple articles')], max_length=255)), | ||
('description', models.CharField(max_length=128)), | ||
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')), | ||
('reporter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'ordering': ['timestamp'], | ||
}, | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from django.db import models | ||
from django.contrib.auth import get_user_model | ||
|
||
from authors.apps.articles.models import Article | ||
|
||
|
||
User = get_user_model() | ||
|
||
|
||
class EscalationModel(models.Model): | ||
""" | ||
Cases for escalation of an article | ||
""" | ||
EXCALATION_CHOICES = ( | ||
('Plagiarism', | ||
'Reusing content without attribution (link and blockquotes)'), | ||
('Rule Violation', 'Violates terms of agreement'), | ||
('Spam', 'Creating multiple articles') | ||
|
||
) | ||
|
||
reporter = models.ForeignKey(User, on_delete=models.CASCADE) | ||
article = models.ForeignKey(Article, on_delete=models.CASCADE) | ||
timestamp = models.DateTimeField(auto_now_add=True) | ||
reason = models.CharField(max_length=255, choices=EXCALATION_CHOICES) | ||
description = models.CharField(max_length=128) | ||
|
||
class Meta: | ||
ordering = ['timestamp', ] | ||
|
||
def get_articles_details(self): | ||
""" | ||
Get all relevant articles details | ||
""" | ||
return { | ||
"slug": self.article.slug, | ||
"title": self.article.title, | ||
"description": self.article.description, | ||
"body": self.article.body, | ||
"created_at": self.article.created_at, | ||
"updated_at": self.article.updated_at | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from .models import EscalationModel | ||
|
||
from rest_framework import serializers | ||
|
||
|
||
class EscalationSerializer(serializers.ModelSerializer): | ||
""" | ||
Escalation serializer | ||
""" | ||
# reporter = serializers.ReadOnlyField() | ||
article = serializers.ReadOnlyField(source='get_articles_details') | ||
reason = serializers.ChoiceField( | ||
choices=['Plagiarism', 'Rule Violation', 'Spam']) | ||
|
||
class Meta: | ||
model = EscalationModel | ||
fields = ('article', 'reason', 'description') |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import json | ||
|
||
from django.contrib.auth import get_user_model | ||
from rest_framework.test import APITestCase, APIClient | ||
from rest_framework.reverse import reverse | ||
from rest_framework.authtoken.models import Token | ||
|
||
from authors.apps.escalation.models import EscalationModel | ||
from authors.apps.articles.models import Article | ||
from django.conf import settings | ||
|
||
|
||
User = get_user_model() | ||
|
||
|
||
class BaseTest(APITestCase): | ||
""" | ||
Class to set up test case | ||
""" | ||
|
||
def setUp(self): | ||
""" | ||
Setting up the client | ||
""" | ||
self.client = APIClient() | ||
self.login_url = reverse("authentication:login") | ||
self.article_url = reverse("articles:article") | ||
|
||
self.user1 = User.objects.create_user( | ||
username="Pablo", | ||
email="Escobar@pablo.com", | ||
password="@Us3r.com" | ||
) | ||
self.user1.is_verified = True | ||
self.user1.save() | ||
|
||
self.user2 = User.objects.create_user( | ||
username="Emirry", | ||
email="ess@rry.com", | ||
password="@Us3r.com" | ||
) | ||
self.user2.is_verified = True | ||
self.user2.save() | ||
|
||
self.user3 = User.objects.create_user( | ||
username="Mercy", | ||
email="mercy@hope.com", | ||
password="@Us3r.com" | ||
) | ||
self.user3.is_verified = True | ||
self.user3.is_staff = True | ||
self.user3.save() | ||
|
||
self.new_article = { | ||
"title": "How to train your dragon", | ||
"description": "Ever wonder how?", | ||
"body": "You have to believe" | ||
} | ||
|
||
self.article = Article.objects.create( | ||
title='this is mine', | ||
description='I do not want it', | ||
body='what is the purpose of body', | ||
author=self.user2, | ||
image='image/upload/v1557052449/tt30hqlfc3eaflfqtobo.jpg' | ||
) | ||
self.article.save() | ||
|
||
def login(self, email, password): | ||
""" | ||
Method to login users | ||
""" | ||
return self.client.post( | ||
self.login_url, | ||
data=json.dumps({ | ||
"email": email, | ||
"password": password | ||
}), content_type="application/json") | ||
|
||
def is_authenticated(self, email, password): | ||
""" | ||
Authenticate user | ||
""" | ||
login_details = self.login(email, password) | ||
token = login_details.data['token'] | ||
return self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
|
||
def create_article(self): | ||
""" | ||
Create a new article | ||
""" | ||
token = self.user2.token() | ||
self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
return self.client.post(self.article_url, | ||
data=json.dumps(self.new_article), | ||
content_type="application/json") | ||
|
||
def escalate_an_article(self): | ||
""" | ||
Report an article that doesn't exist | ||
""" | ||
slug = "how-to-help" | ||
url = reverse("escalation:escalate", args=[slug]) | ||
response = self.client.post( | ||
url, | ||
data=json.dumps({ | ||
"reason": "Rule Violation", | ||
"description": "He broke some rules" | ||
}), | ||
content_type="application/json" | ||
) | ||
return response | ||
|
||
def escalate_an_article_successfully(self): | ||
""" | ||
Report an article successfully | ||
""" | ||
slug = str(self.article.slug) | ||
url = reverse("escalation:escalate", args=[slug]) | ||
response = self.client.post( | ||
url, | ||
data=json.dumps({ | ||
"reason": "Rule Violation", | ||
"description": "He broke some rules" | ||
}), | ||
content_type="application/json" | ||
) | ||
return response | ||
|
||
def escalate_an_article_twice(self): | ||
""" | ||
Try and report an article twice | ||
""" | ||
slug = str(self.article.slug) | ||
url = reverse("escalation:escalate", args=[slug]) | ||
self.client.post( | ||
url, | ||
data=json.dumps({ | ||
"reason": "Rule Violation", | ||
"description": "He broke some rules" | ||
}), | ||
content_type="application/json" | ||
) | ||
return self.client.post( | ||
url, | ||
data=json.dumps({ | ||
"reason": "Rule Violation", | ||
"description": "He broke some rules" | ||
}), | ||
content_type="application/json" | ||
) | ||
|
||
def update_an_escalated_article(self): | ||
""" | ||
Update an eslation | ||
""" | ||
slug = str(self.article.slug) | ||
url = reverse("escalation:escalate", args=[slug]) | ||
self.client.post( | ||
url, | ||
data=json.dumps({ | ||
"reason": "Rule Violation", | ||
"description": "He broke some rules" | ||
}), | ||
content_type="application/json" | ||
) | ||
return self.client.post( | ||
url, | ||
data=json.dumps({ | ||
"reason": "Rule Violation", | ||
"description": "He broke some rules, he posted someone else work and pasted as his" | ||
}), | ||
content_type="application/json" | ||
) | ||
|
||
def delete_article(self): | ||
""" | ||
Try and delete an article | ||
""" | ||
slug = str(self.article.slug) | ||
url = reverse("escalation:escalate", args=[slug]) | ||
return self.client.delete( | ||
url, | ||
content_type="application/json" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import json | ||
|
||
from rest_framework.views import status | ||
from .basetest import BaseTest | ||
|
||
|
||
class EscalationArticlesTest(BaseTest): | ||
""" | ||
Test cases for article escalation | ||
""" | ||
msg = "Sorry we couldn't find that article." | ||
snitch = "You can't report your article." | ||
user_delete = "Only Admins can delete a reported article" | ||
report_twice = "You are not allowed to report twice" | ||
admin_delete = "You successfully deleted the article" | ||
|
||
def test_escalation_of_a_404_article(self): | ||
""" | ||
Test successful escalation of an article that doesn't exist | ||
""" | ||
token = self.user1.token() | ||
self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
resp = self.escalate_an_article() | ||
self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) | ||
self.assertEqual(resp.data["detail"], self.msg) | ||
|
||
def test_escalation_of_an_article(self): | ||
""" | ||
Test successful article escalation | ||
""" | ||
token = self.user1.token() | ||
self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
resp = self.escalate_an_article_successfully() | ||
self.assertEqual(resp.status_code, status.HTTP_201_CREATED) | ||
|
||
def test_update_escalation_of_an_article(self): | ||
""" | ||
Test successful updating an escalation | ||
""" | ||
token = self.user1.token() | ||
self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
resp = self.update_an_escalated_article() | ||
self.assertEqual(resp.status_code, status.HTTP_201_CREATED) | ||
|
||
def test_escalation_of_an_article_with_author(self): | ||
""" | ||
Test escalation with author | ||
""" | ||
token = self.user2.token() | ||
self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
resp = self.escalate_an_article_successfully() | ||
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST) | ||
|
||
def test_escalation_of_an_article_twice(self): | ||
""" | ||
Test unsuccessful article escalation twice | ||
""" | ||
token = self.user1.token() | ||
self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
resp = self.escalate_an_article_twice() | ||
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST) | ||
self.assertEqual(resp.data["error"], self.report_twice) | ||
|
||
def test_delete_of_an_escalated_article_with_user(self): | ||
""" | ||
Test unsuccessful article escalation deletion | ||
""" | ||
token = self.user1.token() | ||
self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
resp = self.delete_article() | ||
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN) | ||
self.assertEqual(resp.data["error"], self.user_delete) | ||
|
||
def test_delete_of_an_escalated_article_with_admin(self): | ||
""" | ||
Test unsuccessful article escalation deletion | ||
""" | ||
token = self.user3.token() | ||
self.client.credentials( | ||
HTTP_AUTHORIZATION='Bearer ' + token) | ||
resp = self.delete_article() | ||
self.assertEqual(resp.status_code, status.HTTP_200_OK) | ||
self.assertEqual(resp.data["message"], self.admin_delete) | ||
|
Oops, something went wrong.