Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
89160d3
Merge pull request #9 from PatrickMugayaJoel/ch-create-tag-app-tag-fe…
PatrickMugayaJoel Mar 23, 2019
d53e39d
Merge branch 'develop' into deploy
PatrickMugayaJoel Mar 28, 2019
a07dc70
Update settings.py
PatrickMugayaJoel Mar 28, 2019
a3f6c6a
Merge branch 'develop' of https://github.com/bisonlou/questioner into…
PatrickMugayaJoel Mar 28, 2019
34f92c8
Merge branch 'bisonlou' into deploy
PatrickMugayaJoel Mar 28, 2019
4ae5190
migrations
PatrickMugayaJoel Mar 28, 2019
7be94ed
Update test_answer.py
PatrickMugayaJoel Mar 28, 2019
2605e80
Update views.py
PatrickMugayaJoel Mar 28, 2019
579f62b
Update test_answer.py
PatrickMugayaJoel Mar 28, 2019
c7dced7
Update views.py
PatrickMugayaJoel Mar 28, 2019
cad52c5
Update settings.py
PatrickMugayaJoel Mar 28, 2019
f4bf25b
Create 0003_reaction.py
PatrickMugayaJoel Mar 28, 2019
7c714a2
Update models.py
PatrickMugayaJoel Mar 28, 2019
f33bd4b
Update serializers.py
PatrickMugayaJoel Mar 28, 2019
2e84cdd
Update views.py
PatrickMugayaJoel Mar 28, 2019
9ea2269
Update settings.py
PatrickMugayaJoel Mar 28, 2019
7d74316
Update urls.py
PatrickMugayaJoel Mar 28, 2019
65f0340
Update views.py
PatrickMugayaJoel Mar 28, 2019
45d163c
Update settings.py
PatrickMugayaJoel Mar 28, 2019
73e70c6
Update serializers.py
PatrickMugayaJoel Mar 28, 2019
4941825
Update views.py
PatrickMugayaJoel Mar 29, 2019
34954a8
Update views.py
PatrickMugayaJoel Mar 29, 2019
062d1ef
Update settings.py
PatrickMugayaJoel Mar 29, 2019
681af4f
Update views.py
PatrickMugayaJoel Mar 29, 2019
d2db3e9
Update settings.py
PatrickMugayaJoel Mar 29, 2019
a1476d3
Update conftest.py
PatrickMugayaJoel Mar 29, 2019
0cb1b0e
Update test_answers.py
PatrickMugayaJoel Mar 29, 2019
8b51f30
Update views.py
PatrickMugayaJoel Mar 29, 2019
b438b6d
Merge branch 'develop' into bisonlou
PatrickMugayaJoel Mar 29, 2019
db9db4b
Merge branch 'deploy' into bisonlou
PatrickMugayaJoel Mar 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion answer/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest

from comment.models import Comment
from answer.models import Answers
from meetup.models import Meeting
from question.models import Question
Expand Down Expand Up @@ -39,3 +39,24 @@ def answered_question(staff1, question1, meetup1):
meetup=meetup1,
question=question1,
)


@pytest.mark.django_db
@pytest.fixture
def answer_comment(staff1, question1):
return Comment.objects.create(
is_answer=True,
created_by=staff1,
comment="test comment",
question=question1,
)


@pytest.mark.django_db
@pytest.fixture
def just_comment(staff1, question1):
return Comment.objects.create(
created_by=staff1,
comment="test comment",
question=question1,
)
41 changes: 41 additions & 0 deletions answer/tests/test_answers.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,44 @@ def test_admin_user_cannot_add_a_duplicate_answer_to_a_question(
"error": {"non_field_errors": ["You cannot add a duplicate Answer."]},
}:
raise AssertionError()


def test_post_a_reaction(api_client, db, admin_user, answer_comment):
api_client.force_authenticate(user=admin_user)
url = reverse('reaction', kwargs={'comment_id': answer_comment.id})
data = {
"reaction": "my cool reaction"
}
response = api_client.post(url, data, format="json")
if not response.status_code == 201:
raise AssertionError()

def test_post_wrong_reaction(api_client, db, admin_user, answer_comment):
api_client.force_authenticate(user=admin_user)
url = reverse('reaction', kwargs={'comment_id': answer_comment.id})
data = {
"itisjoel": "my cool reaction"
}
response = api_client.post(url, data, format="json")
if not response.status_code == 400:
raise AssertionError()

def test_reaction_on_non_answer(api_client, db, admin_user, just_comment):
api_client.force_authenticate(user=admin_user)
url = reverse('reaction', kwargs={'comment_id': just_comment.id})
data = {
"reaction": "my cool reaction"
}
response = api_client.post(url, data, format="json")
if not response.status_code == 403:
raise AssertionError()

def test_reaction_on_missing_comment(api_client, db, admin_user, just_comment):
api_client.force_authenticate(user=admin_user)
url = reverse('reaction', kwargs={'comment_id': 100})
data = {
"reaction": "my cool reaction"
}
response = api_client.post(url, data, format="json")
if not response.status_code == 404:
raise AssertionError()
18 changes: 18 additions & 0 deletions comment/migrations/0002_comment_is_answer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-03-28 12:06

from django.db import migrations, models


class Migration(migrations.Migration):

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

operations = [
migrations.AddField(
model_name='comment',
name='is_answer',
field=models.BooleanField(default=False),
),
]
22 changes: 22 additions & 0 deletions comment/migrations/0003_reaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 2.1.7 on 2019-03-28 17:51

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


class Migration(migrations.Migration):

dependencies = [
('comment', '0002_comment_is_answer'),
]

operations = [
migrations.CreateModel(
name='Reaction',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('reaction', models.TextField()),
('comment', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='reactions', to='comment.Comment')),
],
),
]
9 changes: 9 additions & 0 deletions comment/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ class Comment(models.Model):
def __str__(self):
"""Return a readable representation of the comment model instance."""
return self.comment


class Reaction(models.Model):
comment = models.ForeignKey(Comment, on_delete=models.DO_NOTHING,
related_name='reactions')
reaction = models.TextField()

def __str__(self):
return (self.reaction, self.comment,)
19 changes: 17 additions & 2 deletions comment/serializers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
from rest_framework import serializers
from .models import Comment
from .models import Comment, Reaction


class ReactionsField(serializers.RelatedField):
@classmethod
def to_representation(cls, value, queryset=Reaction.objects.all()):
return value.reaction


class CommentSerializer(serializers.ModelSerializer):
"""Map the comment model instance into JSON format."""

reactions = ReactionsField(many=True, read_only=True)

created_by = serializers.ReadOnlyField(source='created_by.username')
# question_name = serializers.ReadOnlyField(source='question.title')

class Meta:
"""Map serializer fields to comment model fields."""
model = Comment
fields = "__all__"
# read_only_fields = ("created_by_name", "question_name", )
read_only_fields = ("is_answer", "question", )


class ReactionSerializer(serializers.ModelSerializer):
class Meta:
model = Reaction
fields = "__all__"
read_only_fields = ("comment",)
22 changes: 19 additions & 3 deletions comment/tests/test_answer.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,26 @@ def test_succesful_comment_answer_toggle(self):
}
)
self.client.force_authenticate(user=self.admin)
response = self.client.patch(url, format="json")
response = self.client.get(url, format="json")

self.assertEqual(response.status_code, 200)

def test_missing_comment_toggle(self):
"""
Ensure admin can toggle a comment to an answer
"""

url = reverse("toggle_answer",
kwargs={'meetup_id': self.meetup.id,
'question_id': self.question.id,
'pk': 100
}
)
self.client.force_authenticate(user=self.admin)
response = self.client.get(url, format="json")

self.assertEqual(response.status_code, 404)

def test_non_admin_comment_answer_toggle(self):
"""
Ensure non admin cannot toggle a comment to an answer
Expand All @@ -69,7 +85,7 @@ def test_non_admin_comment_answer_toggle(self):
}
)
self.client.force_authenticate(user=self.user)
response = self.client.patch(url, format="json")
response = self.client.get(url, format="json")

self.assertEqual(response.status_code, 403)

Expand All @@ -80,6 +96,6 @@ def test_non_existent_comment(self):

url = f"/meetups/{self.meetup.id}/questions/{self.question.id}/comments/100/toggle_answer/"
self.client.force_authenticate(user=self.admin)
response = self.client.patch(url, format="json")
response = self.client.get(url, format="json")

self.assertEqual(response.status_code, 404)
78 changes: 75 additions & 3 deletions comment/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from meetup.models import Meeting
from question.models import Question
from .models import Comment
from .serializers import CommentSerializer
from .serializers import CommentSerializer, ReactionSerializer


class CommentList(APIView):
Expand Down Expand Up @@ -116,7 +116,7 @@ class ToggleAnswer(APIView):

permission_classes = (IsAuthenticated,)

def patch(self, request, *args, **kwags):
def get(self, request, *args, **kwags):

try:
question_id = self.kwargs['question_id']
Expand All @@ -136,7 +136,7 @@ def patch(self, request, *args, **kwags):
partial=True
)
if serializer.is_valid():
serializer.save()
serializer.save(is_answer=True)
return Response(
{
"status": status.HTTP_200_OK,
Expand Down Expand Up @@ -290,3 +290,75 @@ def delete(self, request, pk, **kwargs):
},
status=status.HTTP_404_NOT_FOUND
)

# Add a reaction
class AddReaction(APIView):
"""
post: reaction
"""

permission_classes = (IsAuthenticated,)
serializer_class = ReactionSerializer

@classmethod
# @swagger_auto_schema(
# operation_description="Add a tag to a meetup",
# operation_id="Add a tag to a meetup.",
# request_body=MeetingTagSerializer,
# responses={
# 201: MeetingTagSerializer(many=False),
# 401: "Unathorized Access",
# 403: "Tag is disabled",
# 404: "Tag Does not exist",
# 400: "Meet up does not exist or Tag already exists",
# },
# )
def post(cls, request, comment_id):

try:
comment = Comment.objects.get(pk=comment_id)
serializer = ReactionSerializer(data=request.data)

except Exception:
return Response(
data={
"status": status.HTTP_404_NOT_FOUND,
"error": "Comment does not exist!",
},
status=status.HTTP_404_NOT_FOUND,
)

response = None
if not comment.is_answer:
response = Response(
data={
"status": status.HTTP_403_FORBIDDEN,
"error": "Sorry, reactions can only be made on answers.",
},
status=status.HTTP_403_FORBIDDEN,
)

elif serializer.is_valid():
serializer.save(comment_id=comment_id)

response = Response(
data={
"status": status.HTTP_201_CREATED,
"data": [
{
"success": "Reaction successfully added.",
}
],
},
status=status.HTTP_201_CREATED,
)

else:
response = Response(
data={
"status": status.HTTP_400_BAD_REQUEST,
"detail": serializer.errors,
},
status=status.HTTP_400_BAD_REQUEST,
)
return response
8 changes: 2 additions & 6 deletions questioner/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,9 @@
SECRET_KEY = "n%fg(03shlp=ipqb_u%_o@=&7bhcwq8hpedk-sn)+5tnx6#*kb"

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = False

ALLOWED_HOSTS = [
"127.0.0.1",
"snaketech.herokuapp.com",
"questionerdojo.herokuapp.com",
]
ALLOWED_HOSTS = ["*", ]

# Application definition

Expand Down
3 changes: 3 additions & 0 deletions questioner/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from rest_framework.urlpatterns import format_suffix_patterns

from tag import views as tag_views
from comment import views as comment_views
from . import views


Expand Down Expand Up @@ -50,6 +51,8 @@
path("meetups/", include("vote.urls")),
path("meetups/", include("comment.urls")),
path("tags/", tag_views.TagList.as_view(), name="tags"),
path("comments/<int:comment_id>/reactions",
comment_views.AddReaction.as_view(), name="reaction"),
path("tags/<int:tag_id>", tag_views.ATag.as_view(), name="tag"),
path("admin/", admin.site.urls),
path(
Expand Down