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

Changed the Feedback model to use lines instead of text

* Feedback consists of lines which can hold up to three comments
* The Feedback model contains almost no logic since all has been
  migrated to the assignemnt mechanism
parent 4eb7b424
Branches
Tags
1 merge request!32Resolve "Create new Model FeedbackLine and integrate it"
Pipeline #
......@@ -32,6 +32,7 @@ public/
.importer*
*.sublime-*
.idea/
.vscode/
# node
node_modules
# -*- coding: utf-8 -*-
# Generated by Django 1.11.8 on 2017-12-22 10:51
from __future__ import unicode_literals
# Generated by Django 2.0.1 on 2018-01-07 14:37
import uuid
......@@ -19,7 +17,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0008_alter_user_username_max_length'),
('auth', '0009_alter_user_last_name_max_length'),
]
operations = [
......@@ -32,13 +30,14 @@ class Migration(migrations.Migration):
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('fullname', models.CharField(blank=True, max_length=70, verbose_name='full name')),
('is_admin', models.BooleanField(default=False)),
('role', models.CharField(choices=[('Student', 'student'), ('Tutor', 'tutor'), ('Reviewer', 'reviewer')], max_length=50)),
('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')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
......@@ -73,6 +72,7 @@ class Migration(migrations.Migration):
('score', models.PositiveIntegerField(default=0)),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)),
('is_final', models.BooleanField(default=False)),
('origin', models.IntegerField(choices=[(0, 'was empty'), (1, 'passed unittests'), (2, 'did not compile'), (3, 'could not link'), (4, 'created by a human. yak!')], default=4)),
],
options={
......@@ -81,23 +81,43 @@ class Migration(migrations.Migration):
},
),
migrations.CreateModel(
name='GeneralTaskSubscription',
name='FeedbackComment',
fields=[
('subscription_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('query_key', models.CharField(blank=True, max_length=75)),
('query_type', models.CharField(choices=[('random', 'Query for any submission'), ('student', 'Query for submissions of student'), ('exam', 'Query for submissions of exam type'), ('submission_type', 'Query for submissions of submissions_type')], default='random', max_length=75)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='susbscriptions', to=settings.AUTH_USER_MODEL)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField()),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)),
('is_final', models.BooleanField(default=True)),
],
options={
'verbose_name': 'Feedback Comment',
'verbose_name_plural': 'Feedback Comments',
'ordering': ('created',),
},
),
migrations.CreateModel(
name='Reviewer',
name='FeedbackForSubmissionLine',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='reviewer', to=settings.AUTH_USER_MODEL)),
('of_line', models.PositiveIntegerField()),
('of_feedback', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feedbacklines', to='core.Feedback')),
],
options={
'verbose_name': 'Feedback for Submissionline',
'verbose_name_plural': 'Feedback Submissionlines',
},
),
migrations.CreateModel(
name='GeneralTaskSubscription',
fields=[
('subscription_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('query_key', models.CharField(blank=True, max_length=75)),
('query_type', models.CharField(choices=[('random', 'Query for any submission'), ('student', 'Query for submissions of student'), ('exam', 'Query for submissions of exam type'), ('submission_type', 'Query for submissions of submissions_type')], default='random', max_length=75)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='susbscriptions', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Student',
name='StudentInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('has_logged_in', models.BooleanField(default=False)),
......@@ -116,7 +136,7 @@ class Migration(migrations.Migration):
('submission_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('seen_by_student', models.BooleanField(default=False)),
('text', models.TextField(blank=True)),
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submissions', to='core.Student')),
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submissions', to='core.StudentInfo')),
],
options={
'verbose_name': 'Submission',
......@@ -152,19 +172,13 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Tests',
},
),
migrations.CreateModel(
name='Tutor',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='tutor', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='TutorSubmissionAssignment',
fields=[
('assignment_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('active', models.BooleanField(default=False)),
('submission', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='assignment', to='core.Submission')),
('is_done', models.BooleanField(default=False)),
('created', models.DateTimeField(auto_now_add=True)),
('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assignments', to='core.Submission')),
('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assignments', to='core.GeneralTaskSubscription')),
],
),
......@@ -174,9 +188,14 @@ class Migration(migrations.Migration):
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='submissions', to='core.SubmissionType'),
),
migrations.AddField(
model_name='feedback',
name='of_reviewer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='reviewed_submissions', to='core.Reviewer'),
model_name='feedbackcomment',
name='of_line',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comment_list', to='core.FeedbackForSubmissionLine'),
),
migrations.AddField(
model_name='feedbackcomment',
name='of_tutor',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='comment_list', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='feedback',
......@@ -186,18 +205,18 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='feedback',
name='of_tutor',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='feedback_list', to='core.Tutor'),
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='feedback_list', to=settings.AUTH_USER_MODEL),
),
migrations.AlterUniqueTogether(
name='test',
unique_together=set([('submission', 'name')]),
unique_together={('submission', 'name')},
),
migrations.AlterUniqueTogether(
name='submission',
unique_together=set([('type', 'student')]),
unique_together={('type', 'student')},
),
migrations.AlterUniqueTogether(
name='generaltasksubscription',
unique_together=set([('owner', 'query_key', 'query_type')]),
unique_together={('owner', 'query_key', 'query_type')},
),
]
# Generated by Django 2.0 on 2017-12-22 11:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='useraccount',
name='last_name',
field=models.CharField(blank=True, max_length=150, verbose_name='last name'),
),
]
# Generated by Django 2.0.1 on 2018-01-04 16:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0002_auto_20171222_1116'),
]
operations = [
migrations.RenameField(
model_name='tutorsubmissionassignment',
old_name='active',
new_name='is_done',
),
]
# Generated by Django 2.0.1 on 2018-01-04 16:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_auto_20180104_1631'),
]
operations = [
migrations.AddField(
model_name='feedback',
name='is_final',
field=models.BooleanField(default=False),
),
]
# Generated by Django 2.0.1 on 2018-01-04 18:51
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_feedback_is_final'),
]
operations = [
migrations.AddField(
model_name='tutorsubmissionassignment',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AlterField(
model_name='tutorsubmissionassignment',
name='submission',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assignments', to='core.Submission'),
),
]
# Generated by Django 2.0.1 on 2018-01-04 20:01
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_auto_20180104_1851'),
]
operations = [
migrations.AlterField(
model_name='feedback',
name='of_tutor',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='feedback_list', to=settings.AUTH_USER_MODEL),
),
]
# Generated by Django 2.0 on 2018-01-05 11:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20180104_2001'),
]
operations = [
migrations.RenameModel(
old_name='Student',
new_name='StudentInfo',
),
migrations.RemoveField(
model_name='reviewer',
name='user',
),
migrations.RemoveField(
model_name='tutor',
name='user',
),
migrations.RemoveField(
model_name='feedback',
name='of_reviewer',
),
migrations.AddField(
model_name='useraccount',
name='role',
field=models.CharField(choices=[('Student', 'student'), ('Tutor', 'tutor'), ('Reviewer', 'reviewer')], default='Student', max_length=50),
preserve_default=False,
),
migrations.DeleteModel(
name='Reviewer',
),
migrations.DeleteModel(
name='Tutor',
),
]
......@@ -319,7 +319,7 @@ class Submission(models.Model):
----------
seen_by_student : BooleanField
True if the student saw his accepted feedback.
student : OneToOneField
student : ForgeignKey
The student how cause all of this
text : TextField
The code/text submitted by the student
......@@ -579,5 +579,48 @@ class TutorSubmissionAssignment(models.Model):
self.save()
def __str__(self):
return (f'{self.assignee} assigned to {self.submission}'
f' (active={self.active})')
return (f'{self.subscription.owner} assigned to {self.submission}'
f' (done={self.is_done})')
class FeedbackForSubmissionLine(models.Model):
of_line = models.PositiveIntegerField()
of_feedback = models.ForeignKey(
Feedback,
related_name="feedbacklines",
on_delete=models.CASCADE
)
class Meta:
verbose_name = "Feedback for Submissionline"
verbose_name_plural = "Feedback Submissionlines"
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)
of_line = models.ForeignKey(
FeedbackForSubmissionLine,
related_name="comment_list",
on_delete=models.CASCADE
)
is_final = models.BooleanField(default=True)
of_tutor = models.ForeignKey(
get_user_model(),
related_name="comment_list",
on_delete=models.PROTECT
)
def save(self, *args, **kwargs):
self.of_line.comment_list.update(is_final=False)
super().save(*args, **kwargs)
class Meta:
verbose_name = "Feedback Comment"
verbose_name_plural = "Feedback Comments"
ordering = ('created',)
......@@ -3,6 +3,7 @@ import logging
from django.core.exceptions import ObjectDoesNotExist
from drf_dynamic_fields import DynamicFieldsMixin
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from core import models
from core.models import (ExamType, Feedback, GeneralTaskSubscription,
......@@ -27,8 +28,49 @@ class ExamSerializer(DynamicFieldsModelSerializer):
'pass_score', 'pass_only',)
class FeedbackForSubmissionLineSerializer(serializers.BaseSerializer):
def to_representation(self, obj):
return {feedback.of_line:
FeedbackCommentSerializer(
feedback.comment_list, many=True).data
for feedback in obj.all()}
def to_internal_value(self, data):
return data
class FeedbackSerializer(DynamicFieldsModelSerializer):
assignment_id = serializers.UUIDField(write_only=True)
feedbacklines = FeedbackForSubmissionLineSerializer(
required=False
)
isFinal = serializers.BooleanField(source="is_final")
ofSubmission = serializers.PrimaryKeyRelatedField(
source='of_submission',
required=False,
queryset=Submission.objects.all(),
validators=[UniqueValidator(queryset=Feedback.objects.all()), ])
def create(self, validated_data) -> Feedback:
feedback = Feedback.objects.create(
score=validated_data['score'],
is_final=validated_data.get('is_final', False),
of_submission=validated_data['of_submission']
)
assignment = validated_data.pop('assignment')
if 'feedbacklines' in validated_data:
for line_no, comment in validated_data['feedbacklines'].items():
line_obj = models.FeedbackForSubmissionLine.objects.create(
of_line=line_no,
of_feedback=feedback
)
models.FeedbackComment.objects.create(
of_line=line_obj,
of_tutor=self.context['request'].user,
**comment
)
assignment.set_done()
return feedback
def validate(self, data):
log.debug(data)
......@@ -56,15 +98,24 @@ class FeedbackSerializer(DynamicFieldsModelSerializer):
'of_submission': submission
}
def create(self, validated_data) -> Feedback:
assignment = validated_data.pop('assignment')
assignment.set_done()
class Meta:
model = Feedback
fields = ('assignment_id', 'isFinal', 'score',
'ofSubmission', 'feedbacklines')
return Feedback.objects.create(**validated_data)
class FeedbackCommentSerializer(serializers.ModelSerializer):
ofTutor = serializers.StringRelatedField(source='of_tutor.username')
isFinalComment = serializers.BooleanField(source='is_final')
def to_internal_value(self, data):
return data
class Meta:
model = Feedback
fields = ('assignment_id', 'text', 'score')
model = models.FeedbackComment
fields = ('text', 'ofTutor', 'created', 'isFinalComment')
read_only_fields = ('created',)
class SubmissionTypeSerializer(DynamicFieldsModelSerializer):
......
from rest_framework import status
from rest_framework.test import (APIRequestFactory, APITestCase,
force_authenticate)
from core import models
from core.models import (Feedback, FeedbackComment, FeedbackForSubmissionLine,
Submission, SubmissionType)
from core.views import FeedbackApiView
from util.factories import GradyUserFactory
class FeedbackRetrieveTestCase(APITestCase):
factory = GradyUserFactory()
EXPECTED_SCORE = 23
@classmethod
def setUpTestData(cls):
cls.score = 23
cls.tutor = cls.factory.make_tutor()
cls.student = cls.factory.make_student()
cls.tutor2 = cls.factory.make_tutor()
cls.tutors = [cls.tutor, cls.tutor2]
cls.request_factory = APIRequestFactory()
cls.submission_type = SubmissionType.objects.create(
name='Cooking some crystal with Jesse')
cls.sub = Submission.objects.create(student=cls.student.student,
type=cls.submission_type)
cls.feedback = Feedback.objects.create(of_tutor=cls.tutor,
score=23, is_final=False,
of_submission=cls.sub)
cls.linelist = []
for line in range(2):
cls.linelist.append(FeedbackForSubmissionLine.objects.create(
of_line=line + 1, of_feedback=cls.feedback))
for line in cls.linelist:
for tutor in cls.tutors:
FeedbackComment.objects.create(text='fortytwo',
of_tutor=tutor,
of_line=line)
def setUp(self):
self.request = self.request_factory.get(f'/api/feedback/{self.sub.pk}')
force_authenticate(self.request, self.tutor)
self.view = FeedbackApiView.as_view({'get': 'retrieve'})
self.response = self.view(
self.request,
submission_id=str(self.sub.pk)
)
self.data = self.response.data
def test_only_one_final_comment_per_line(self):
comments_on_first_line = FeedbackForSubmissionLine.objects \
.first().comment_list.all()
self.assertEqual(2, len(comments_on_first_line))
final_comments = [comment for comment in comments_on_first_line
if comment.is_final]
self.assertEqual(1, len(final_comments))
def test_can_retrieve_feedback_via_endpoint(self):
self.assertEqual(self.response.status_code, status.HTTP_200_OK)
def test_if_feedback_contains_correct_score(self):
self.assertIn('score', self.data)
self.assertEqual(self.data.get('score'), self.EXPECTED_SCORE)
def test_if_feedback_contains_linekeys(self):
self.assertIn('feedbacklines', self.data)
self.assertIn(1, self.data['feedbacklines'])
self.assertIn(2, self.data['feedbacklines'])
def test_if_feedback_contains_final(self):
self.assertIn('isFinal', self.data)
self.assertIsNotNone(self.data['isFinal'])
def test_if_comment_contains_text(self):
self.assertIn('text', self.data['feedbacklines'][1][0])
self.assertEqual(
'fortytwo', self.data['feedbacklines'][1][0]['text'])
def test_if_comment_contains_created(self):
self.assertIn('created', self.data['feedbacklines'][1][0])
self.assertIsNotNone(self.data['feedbacklines'][1][0]['created'])
def test_if_comment_has_tutor(self):
self.assertIn('ofTutor', self.data['feedbacklines'][1][0])
self.assertEqual(
self.tutor.username,
self.data['feedbacklines'][1][0]['ofTutor'])
def test_if_comment_has_final(self):
self.assertIn('isFinalComment', self.data['feedbacklines'][1][0])
self.assertIsNotNone(
self.data['feedbacklines'][1][0]['isFinalComment'])
class FeedbackCreateTestCase(APITestCase):
@classmethod
def setUpTestData(cls):
cls.url = '/api/feedback/'
cls.user_factory = GradyUserFactory()
cls.tutor = cls.user_factory.make_tutor(password='p')
cls.student = cls.user_factory.make_student()
cls.submission_type = SubmissionType.objects.create(
name='Cooking some crystal with Jesse',
full_score=100
)
cls.sub = Submission.objects.create(student=cls.student.student,
type=cls.submission_type)
def setUp(self):
self.client.force_authenticate(user=self.tutor)
self.subscription = models.GeneralTaskSubscription.objects.create(
owner=self.tutor,
query_type='random',
query_key=''
)
self.assignment = self.subscription.get_or_create_work_assignment()
def test_can_create_feedback_without_feedback_lines(self):
# TODO this test has to be adapted to test the various constraints
# e.g. feedback without lines can only be given if the score is equal
# to the max Score for this submission
data = {
'score': 10,
'isFinal': False,
'assignment_id': self.assignment.assignment_id
}
self.assertEqual(Feedback.objects.count(), 0)
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Feedback.objects.count(), 1)
def test_cannot_create_feedback_with_score_higher_than_max(self):
data = {
'score': 101,
'isFinal': False,
'assignment_id': self.assignment.assignment_id
}
self.assertEqual(Feedback.objects.count(), 0)
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(Feedback.objects.count(), 0)
def test_cannot_create_feedback_with_score_less_than_zero(self):
data = {
'score': -1,
'isFinal': False,
'assignment_id': self.assignment.assignment_id
}
self.assertEqual(Feedback.objects.count(), 0)
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(Feedback.objects.count(), 0)
def test_check_score_is_set_accordingly(self):
data = {
'score': 5,
'isFinal': False,
'assignment_id': self.assignment.assignment_id
}
self.client.post(self.url, data, format='json')
object_score = self.sub.feedback.score
self.assertEqual(object_score, 5)
def test_can_create_feedback_with_comment(self):
data = {
'score': 0,
'isFinal': False,
'assignment_id': self.assignment.assignment_id,
'feedbacklines': {
'5': {
'text': 'Nice meth!'
}
}
}
self.assertEqual(FeedbackComment.objects.count(), 0)
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(FeedbackComment.objects.count(), 1)
def test_feedback_comment_is_created_correctly(self):
data = {
'score': 0,
'isFinal': False,
'assignment_id': self.assignment.assignment_id,
'feedbacklines': {
'5': {
'text': 'Nice meth!'
}
}
}
self.client.post(self.url, data, format='json')
comment = FeedbackComment.objects.first()
self.assertEqual(comment.of_tutor, self.tutor)
self.assertEqual(comment.text, 'Nice meth!')
self.assertIsNotNone(comment.created)
self.assertEqual(comment.of_line.of_line, 5)
self.assertTrue(comment.is_final)
def test_can_create_multiple_feedback_comments(self):
data = {
'score': 0,
'isFinal': False,
'assignment_id': self.assignment.assignment_id,
'feedbacklines': {
'5': {
'text': 'Nice meth!'
},
'7': {
'text': 'Good one!'
}
}
}
self.client.post(self.url, data, format='json')
first_comment = FeedbackComment.objects.get(text='Nice meth!')
self.assertEqual(first_comment.of_tutor, self.tutor)
self.assertIsNotNone(first_comment.created)
self.assertEqual(first_comment.of_line.of_line, 5)
self.assertTrue(first_comment.is_final)
second_comment = FeedbackComment.objects.get(text='Good one!')
self.assertEqual(second_comment.of_tutor, self.tutor)
self.assertIsNotNone(second_comment.created)
self.assertEqual(second_comment.of_line.of_line, 7)
self.assertTrue(second_comment.is_final)
......@@ -63,17 +63,14 @@ class FeedbackApiView(
permission_classes = (IsTutorOrReviewer,)
queryset = Feedback.objects.all()
serializer_class = FeedbackSerializer
lookup_field = 'submission__submission_id'
lookup_field = 'of_submission__submission_id'
lookup_url_kwarg = 'submission_id'
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(
data={**request.data, 'of_tutor': request.user})
serializer.is_valid(raise_exception=True)
if serializer.data['assignment'].subscription.owner != request.user:
return Response({'You do not have permission to edit this'},
status=status.HTTP_403_FORBIDDEN)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment