Skip to content
Snippets Groups Projects
Verified Commit c99d7224 authored by Jan Maximilian Michal's avatar Jan Maximilian Michal
Browse files

Added the option delete feedback comments

parent b811df9a
No related branches found
No related tags found
1 merge request!62Added the option delete feedback comments
Pipeline #
# Generated by Django 2.0.2 on 2018-02-17 20:49
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20180217_1729'),
]
operations = [
migrations.RemoveField(
model_name='feedbackcomment',
name='id',
),
migrations.AddField(
model_name='feedbackcomment',
name='comment_id',
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
),
]
......@@ -425,6 +425,42 @@ class Feedback(models.Model):
return self.of_submission.type.full_score
class FeedbackComment(models.Model):
""" This Class contains the Feedback for a specific line of a Submission"""
comment_id = models.UUIDField(primary_key=True,
default=uuid.uuid4,
editable=False)
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
visible_to_student = models.BooleanField(default=True)
of_line = models.PositiveIntegerField(default=0)
of_tutor = models.ForeignKey(
get_user_model(),
related_name="comment_list",
on_delete=models.PROTECT
)
of_feedback = models.ForeignKey(
Feedback,
related_name="feedback_lines",
on_delete=models.CASCADE,
null=True
)
class Meta:
verbose_name = "Feedback Comment"
verbose_name_plural = "Feedback Comments"
ordering = ('created',)
unique_together = ('of_line', 'of_tutor', 'of_feedback')
def __str__(self):
return 'Comment on line {} of tutor {}: "{}"'.format(self.of_line,
self.of_tutor,
self.text)
class SubscriptionEnded(Exception):
pass
......@@ -654,36 +690,3 @@ class TutorSubmissionAssignment(models.Model):
class Meta:
unique_together = ('submission', 'subscription')
class FeedbackComment(models.Model):
""" This Class contains the Feedback for a specific line of a Submission"""
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
visible_to_student = models.BooleanField(default=True)
of_line = models.PositiveIntegerField(default=0)
of_tutor = models.ForeignKey(
get_user_model(),
related_name="comment_list",
on_delete=models.PROTECT
)
of_feedback = models.ForeignKey(
Feedback,
related_name="feedback_lines",
on_delete=models.CASCADE,
null=True
)
class Meta:
verbose_name = "Feedback Comment"
verbose_name_plural = "Feedback Comments"
ordering = ('created',)
unique_together = ('of_line', 'of_tutor', 'of_feedback')
def __str__(self):
return 'Comment on line {} of tutor {}: "{}"'.format(self.of_line,
self.of_tutor,
self.text)
......@@ -67,7 +67,8 @@ class FeedbackCommentSerializer(serializers.ModelSerializer):
class Meta:
model = models.FeedbackComment
fields = ('text',
fields = ('pk',
'text',
'created',
'of_tutor',
'of_line',
......
......@@ -387,3 +387,76 @@ class FeedbackPatchTestCase(APITestCase):
response = self.client.patch(self.url, data, format='json')
self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code)
class FeedbackCommentApiEndpointTest(APITestCase):
@classmethod
def setUpTestData(cls):
cls.burl = '/api/feedback/'
cls.data = make_test_data({
'submission_types': [
{
'name': '01. Sort this or that',
'full_score': 35,
'description': 'Very complicated',
'solution': 'Trivial!'
}],
'students': [
{'username': 'student01'}
],
'tutors': [
{'username': 'tutor01'},
{'username': 'tutor02'},
],
'reviewers': [
{'username': 'reviewer01'},
],
'submissions': [{
'text': 'function blabl\n'
' on multi lines\n'
' for blabla in bla:\n',
'type': '01. Sort this or that',
'user': 'student01',
'feedback': {
'score': 5,
'is_final': True,
'feedback_lines': {
'1': [{'text': 'This is very bad!',
'of_tutor': 'tutor01'}],
'2': [{'text': 'And this is even worse!',
'of_tutor': 'tutor02'}],
}
}
}]
})
def setUp(self):
self.url = '/api/feedback-comment/%s/'
self.tutor01 = self.data['tutors'][0]
self.tutor02 = self.data['tutors'][1]
def test_tutor_can_delete_own_comment(self):
self.client.force_authenticate(user=self.tutor01)
comment = FeedbackComment.objects.get(of_tutor=self.tutor01)
response = self.client.delete(self.url % comment.pk)
self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code)
def test_tutor_cannot_delete_foreign_comment(self):
self.client.force_authenticate(user=self.tutor02)
comment = FeedbackComment.objects.get(of_tutor=self.tutor02)
self.client.force_authenticate(self.tutor01)
response = self.client.delete(self.url % comment.pk)
self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code)
def test_reviewer_can_delete_everything_they_want(self):
reviewer = self.data['reviewers'][0]
self.client.force_authenticate(user=reviewer)
comment01 = FeedbackComment.objects.get(of_tutor=self.tutor02)
comment02 = FeedbackComment.objects.get(of_tutor=self.tutor02)
response = self.client.delete(self.url % comment01.pk)
self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code)
response = self.client.delete(self.url % comment02.pk)
self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code)
......@@ -9,12 +9,14 @@ router.register('student', views.StudentReviewerApiViewSet,
base_name='student')
router.register('examtype', views.ExamApiViewSet)
router.register('feedback', views.FeedbackApiView)
router.register('feedback-comment', views.FeedbackCommentApiView)
router.register('submission', views.SubmissionViewSet,
base_name='submission')
router.register('submissiontype', views.SubmissionTypeApiView)
router.register('tutor', views.TutorApiViewSet, base_name='tutor')
router.register('subscription', views.SubscriptionApiViewSet,
base_name='subscription')
router.register('assignment', views.AssignmentApiViewSet)
router.register('submission', views.SubmissionViewSet, base_name='submission')
# regular views that are not viewsets
regular_views_urlpatterns = [
......
from .feedback import FeedbackApiView # noqa
from .feedback import FeedbackApiView, FeedbackCommentApiView # noqa
from .subscription import SubscriptionApiViewSet, AssignmentApiViewSet # noqa
from .common_views import * # noqa
from .export import StudentCSVExport # noqa
......@@ -108,3 +108,18 @@ class FeedbackApiView(
serializer.save()
return Response(serializer.data)
class FeedbackCommentApiView(viewsets.GenericViewSet):
""" Gets a list of an individual exam by Id if provided """
permission_classes = (permissions.IsTutorOrReviewer,)
queryset = models.FeedbackComment.objects.all()
def destroy(self, request, *args, **kwargs):
comment = self.get_object()
user = request.user
if user.role == models.UserAccount.TUTOR and user != comment.of_tutor:
raise PermissionDenied(detail='Can only delete your own commits.')
return Response(status=status.HTTP_204_NO_CONTENT)
......@@ -7,6 +7,8 @@ services:
restart: always
networks:
- default
ports:
6543:5432
grady:
image: docker.gitlab.gwdg.de/j.michal/grady:master
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment