From 2de278b808ce61e300f9b1881c4e9c068f883532 Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Tue, 6 Aug 2019 15:43:27 +0200 Subject: [PATCH] Closes #155 --- core/models/assignment.py | 18 +++++ core/models/submission.py | 2 + core/serializers/feedback.py | 3 +- core/signals.py | 10 --- core/tests/test_custom_subscription_filter.py | 5 ++ core/tests/test_feedback.py | 35 +++++----- .../test_subscription_assignment_service.py | 7 +- core/tests/test_tutor_api_endpoints.py | 1 + core/urls.py | 1 + core/views/feedback.py | 68 +++++-------------- core/views/subscription.py | 43 ++++++++++-- core/views/util.py | 33 +++++++++ frontend/src/api.ts | 11 ++- .../src/store/modules/submission-notes.ts | 13 ++-- functional_tests/test_export_modal.py | 2 +- 15 files changed, 155 insertions(+), 97 deletions(-) create mode 100644 core/views/util.py diff --git a/core/models/assignment.py b/core/models/assignment.py index fc8cfa17..77967461 100644 --- a/core/models/assignment.py +++ b/core/models/assignment.py @@ -14,6 +14,10 @@ class DeletionOfDoneAssignmentsNotPermitted(Exception): pass +class CanOnlyCallFinishOnUnfinishedAssignments(Exception): + pass + + class TutorSubmissionAssignment(models.Model): assignment_id = models.UUIDField(primary_key=True, @@ -32,6 +36,20 @@ class TutorSubmissionAssignment(models.Model): return (f'{self.subscription.owner} assigned to {self.submission}' f' (done={self.is_done})') + def finish(self): + self.refresh_from_db() + if self.is_done: + raise CanOnlyCallFinishOnUnfinishedAssignments() + + meta = self.submission.meta + # TODO add reviewer final + meta.feedback_authors.add(self.subscription.owner) + meta.done_assignments += 1 + meta.has_active_assignment = False + self.is_done = True + self.save() + meta.save() + def delete(self, *args, **kwargs): if self.is_done: raise DeletionOfDoneAssignmentsNotPermitted() diff --git a/core/models/submission.py b/core/models/submission.py index 3687fb67..f968a657 100644 --- a/core/models/submission.py +++ b/core/models/submission.py @@ -63,7 +63,9 @@ class MetaSubmission(models.Model): done_assignments = models.PositiveIntegerField(default=0) has_active_assignment = models.BooleanField(default=False) + # Managed by signal! has_feedback = models.BooleanField(default=False) + # Managed by signal! has_final_feedback = models.BooleanField(default=False) feedback_authors = models.ManyToManyField(get_user_model()) diff --git a/core/serializers/feedback.py b/core/serializers/feedback.py index 92416e1e..238d5d1c 100644 --- a/core/serializers/feedback.py +++ b/core/serializers/feedback.py @@ -192,8 +192,7 @@ class FeedbackSerializer(DynamicFieldsModelSerializer): raise serializers.ValidationError( 'Sorry, you have to explain why this does not get full score') - http_method = self.context['request'].method - if hasattr(submission, 'feedback') and http_method == 'POST': + if hasattr(submission, 'feedback') and not self.instance: raise serializers.ValidationError( 'Feedback for this submission already exists') diff --git a/core/signals.py b/core/signals.py index 95cdcc1a..110a15e8 100644 --- a/core/signals.py +++ b/core/signals.py @@ -50,16 +50,6 @@ def update_after_feedback_save(sender, instance, created, **kwargs): meta = instance.of_submission.meta meta.has_feedback = True meta.has_final_feedback = instance.is_final - - undone_assignment = meta.submission.assignments.filter(is_done=False) - assert undone_assignment.count() <= 1 - if undone_assignment.count() > 0: - log.debug('SIGNAL -- Completed: %s' % undone_assignment.first()) - meta.feedback_authors.add(undone_assignment.first().subscription.owner) - meta.done_assignments += 1 - meta.has_active_assignment = False - undone_assignment.update(is_done=True) - meta.save() diff --git a/core/tests/test_custom_subscription_filter.py b/core/tests/test_custom_subscription_filter.py index 64525f58..c6ac1767 100644 --- a/core/tests/test_custom_subscription_filter.py +++ b/core/tests/test_custom_subscription_filter.py @@ -140,6 +140,7 @@ class StopOnPass(APITestCase): self.assertEqual(35, self.data['students'][0].student.total_score) self.assertTrue(self.data['students'][0].student.passes_exam) + # TODO why is this code commented?!? # def test_submissions_left_after_not_pass_only_student_passed_exam(self): # Feedback.objects.create( # of_submission=self.data['submissions'][3], score=20) @@ -164,8 +165,10 @@ class StopOnPass(APITestCase): # signals recognize the open assignments Feedback.objects.create( of_submission=a1.submission, score=20) + a1.finish() Feedback.objects.create( of_submission=a2.submission, score=15) + a2.finish() subscription_other_tutor = SubmissionSubscription.objects.create( owner=self.tutor02, @@ -188,8 +191,10 @@ class StopOnPass(APITestCase): # signals recognize the open assignments Feedback.objects.create( of_submission=a1.submission, score=20) + a1.finish() Feedback.objects.create( of_submission=a2.submission, score=15) + a2.finish() subscription_other_tutor = SubmissionSubscription.objects.create( owner=self.tutor02, diff --git a/core/tests/test_feedback.py b/core/tests/test_feedback.py index dea21a72..7217cc86 100644 --- a/core/tests/test_feedback.py +++ b/core/tests/test_feedback.py @@ -93,7 +93,7 @@ class FeedbackCreateTestCase(APITestCase): @classmethod def setUpTestData(cls): - cls.url = '/api/feedback/' + cls.url = lambda self: f'/api/assignment/{self.assignment.pk}/finish/' cls.user_factory = GradyUserFactory() cls.tutor = cls.user_factory.make_tutor(password='p') cls.exam = make_exams(exams=[{ @@ -138,7 +138,7 @@ class FeedbackCreateTestCase(APITestCase): } self.assertEqual(Feedback.objects.count(), 0) - response = self.client.post(self.url, data, format='json') + response = self.client.post(self.url(), data, format='json') self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) self.assertEqual(Feedback.objects.count(), 0) @@ -149,7 +149,7 @@ class FeedbackCreateTestCase(APITestCase): 'of_submission': self.assignment.submission.pk } self.assertEqual(Feedback.objects.count(), 0) - response = self.client.post(self.url, data, format='json') + 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) @@ -159,7 +159,7 @@ class FeedbackCreateTestCase(APITestCase): 'is_final': True, 'of_submission': self.assignment.submission.pk } - response = self.client.post(self.url, data, format='json') + response = self.client.post(self.url(), data, format='json') self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code) self.assertEqual(Feedback.objects.count(), 0) @@ -169,7 +169,7 @@ class FeedbackCreateTestCase(APITestCase): 'is_final': False, 'of_submission': self.assignment.submission.pk } - response = self.client.post(self.url, data, format='json') + response = self.client.post(self.url(), data, format='json') self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) self.assertEqual(Feedback.objects.count(), 0) @@ -180,7 +180,7 @@ class FeedbackCreateTestCase(APITestCase): 'of_submission': self.assignment.submission.pk } self.assertEqual(Feedback.objects.count(), 0) - response = self.client.post(self.url, data, format='json') + 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) @@ -191,7 +191,7 @@ class FeedbackCreateTestCase(APITestCase): 'of_submission': self.assignment.submission.pk } self.assertEqual(Feedback.objects.count(), 0) - response = self.client.post(self.url, data, format='json') + 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) @@ -209,7 +209,7 @@ class FeedbackCreateTestCase(APITestCase): } } self.assertEqual(self.fst_label.feedback.count(), 0) - response = self.client.post(self.url, data, format='json') + response = self.client.post(self.url(), data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.fst_label.refresh_from_db() self.snd_label.refresh_from_db() @@ -229,7 +229,7 @@ class FeedbackCreateTestCase(APITestCase): } } } - self.client.post(self.url, data, format='json') + self.client.post(self.url(), data, format='json') object_score = self.sub.feedback.score self.assertEqual(object_score, 0.5) @@ -245,7 +245,7 @@ class FeedbackCreateTestCase(APITestCase): } } } - self.client.post(self.url, data, format='json') + self.client.post(self.url(), data, format='json') object_score = self.sub.feedback.score self.assertEqual(object_score, 5) @@ -262,7 +262,7 @@ class FeedbackCreateTestCase(APITestCase): } } self.assertEqual(FeedbackComment.objects.count(), 0) - response = self.client.post(self.url, data, format='json') + response = self.client.post(self.url(), data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(FeedbackComment.objects.count(), 1) @@ -278,7 +278,7 @@ class FeedbackCreateTestCase(APITestCase): } } } - self.client.post(self.url, data, format='json') + 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!') @@ -298,8 +298,8 @@ class FeedbackCreateTestCase(APITestCase): } } self.assignment.delete() - response = self.client.post(self.url, data, format='json') - self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code) + response = self.client.post(self.url(), data, format='json') + self.assertEqual(status.HTTP_404_NOT_FOUND, response.status_code) def test_cannot_create_with_someoneelses_assignment(self): data = { @@ -314,8 +314,9 @@ class FeedbackCreateTestCase(APITestCase): } other_tutor = self.user_factory.make_tutor('Berta') self.client.force_authenticate(other_tutor) - response = self.client.post(self.url, data, format='json') - self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code) + response = self.client.post(self.url(), data, format='json') + # returns 404 since the other users assignment is not visible to this one + self.assertEqual(status.HTTP_404_NOT_FOUND, response.status_code) def test_can_create_multiple_feedback_comments(self): data = { @@ -333,7 +334,7 @@ class FeedbackCreateTestCase(APITestCase): } } } - self.client.post(self.url, data, format='json') + 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) diff --git a/core/tests/test_subscription_assignment_service.py b/core/tests/test_subscription_assignment_service.py index 79f0f14a..c4522609 100644 --- a/core/tests/test_subscription_assignment_service.py +++ b/core/tests/test_subscription_assignment_service.py @@ -235,7 +235,6 @@ class TestApiEndpoints(APITestCase): response_subscription_create = self.client.post( '/api/subscription/', {'query_type': 'random'}) - subscription_pk = response_subscription_create.data['pk'] subscription_pk = response_subscription_create.data['pk'] response_assignment = self.client.post( @@ -374,7 +373,7 @@ class TestApiEndpoints(APITestCase): }) self.assertEqual(status.HTTP_201_CREATED, response.status_code) response = self.client.post( - f'/api/feedback/', { + f'/api/assignment/{response.data["pk"]}/finish/', { "score": 23, "of_submission": response.data['submission']['pk'], "feedback_lines": { @@ -415,8 +414,8 @@ class TestApiEndpoints(APITestCase): assignment = models.TutorSubmissionAssignment.objects.get( pk=response.data['pk']) self.assertFalse(assignment.is_done) - response = self.client.patch( - '/api/feedback/%s/' % submission_id_in_response, { + response = self.client.post( + f'/api/assignment/{assignment.pk}/finish/', { "score": 20, "is_final": True, "feedback_lines": { diff --git a/core/tests/test_tutor_api_endpoints.py b/core/tests/test_tutor_api_endpoints.py index 7d0f0fc7..343b649d 100644 --- a/core/tests/test_tutor_api_endpoints.py +++ b/core/tests/test_tutor_api_endpoints.py @@ -107,6 +107,7 @@ class TutorListTests(APITestCase): Feedback.objects.update_or_create( of_submission=assignment.submission, score=35) + assignment.finish() tutor01 = data['tutors'][0] tutor02 = data['tutors'][1] diff --git a/core/urls.py b/core/urls.py index b0e100b4..aee6e268 100644 --- a/core/urls.py +++ b/core/urls.py @@ -7,6 +7,7 @@ from rest_framework.permissions import AllowAny from core import views # Create a router and register our viewsets with it. + router = DefaultRouter() router.register('student', views.StudentReviewerApiViewSet, basename='student') diff --git a/core/views/feedback.py b/core/views/feedback.py index 27211eb7..78d943e8 100644 --- a/core/views/feedback.py +++ b/core/views/feedback.py @@ -4,8 +4,11 @@ from multiprocessing import Lock from rest_framework import mixins, status, viewsets from rest_framework.exceptions import PermissionDenied from rest_framework.response import Response +from rest_framework import decorators from core import models, permissions, serializers +from core.views.util import tutor_attempts_to_patch_first_feedback_final, \ + get_implicit_assignment_for_user log = logging.getLogger(__name__) @@ -17,11 +20,11 @@ class FeedbackApiView( viewsets.GenericViewSet): """ Gets a list of an individual exam by Id if provided """ permission_classes = (permissions.IsTutorOrReviewer,) - queryset = models.Feedback.objects\ - .select_related('of_submission')\ - .select_related('of_submission__type')\ - .select_related('of_submission__student')\ - .select_related('of_submission__student__user')\ + queryset = models.Feedback.objects \ + .select_related('of_submission') \ + .select_related('of_submission__type') \ + .select_related('of_submission__student') \ + .select_related('of_submission__student__user') \ .all() serializer_class = serializers.FeedbackSerializer lookup_field = 'of_submission__pk' @@ -35,44 +38,11 @@ class FeedbackApiView( role=models.UserAccount.REVIEWER).exists() return feedback_is_final and set_by_reviewer and user_is_tutor - def _get_implicit_assignment_for_user(self, submission): - """ Check for tutor if it exists. Not relevant for reviewer """ - try: - return models.TutorSubmissionAssignment.objects.get( - subscription__owner=self.request.user, - submission=submission - ) - except models.TutorSubmissionAssignment.DoesNotExist: - if self.request.user.role == models.UserAccount.REVIEWER: - return None - raise PermissionDenied( - detail='This user has no permission to create this feedback') - def _tutor_attempts_to_set_first_feedback_final(self, serializer): is_final_set = serializer.validated_data.get('is_final', False) user_is_tutor = self.request.user.role == models.UserAccount.TUTOR return is_final_set and user_is_tutor - # unused - def _tutor_is_allowed_to_change_own_feedback(self, serializer): - submission = self.get_object().of_submission - assignment = self._get_implicit_assignment_for_user(submission) - youngest = models.TutorSubmissionAssignment.objects \ - .filter(submission=submission) \ - .order_by('-created') \ - .first() - - return assignment == youngest - - def _tutor_attempts_to_patch_first_feedback_final(self, serializer): - if self.request.user.role == models.UserAccount.REVIEWER: - return False - is_final_set = serializer.validated_data.get('is_final', False) - submission = self.get_object().of_submission - assignment = self._get_implicit_assignment_for_user(submission) - in_creation = assignment.subscription.feedback_stage == models.SubmissionSubscription.FEEDBACK_CREATION # noqa - return is_final_set and in_creation - def get_queryset(self): if self.request.user.is_reviewer(): return self.queryset \ @@ -84,19 +54,16 @@ class FeedbackApiView( of_submission__assignments__subscription__owner=self.request.user ) + @decorators.permission_classes((permissions.IsReviewer,)) def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) - - self._get_implicit_assignment_for_user( - serializer.validated_data['of_submission']) - - if self._tutor_attempts_to_set_first_feedback_final(serializer): - return Response( - {'For tutors it is not allowed to create feedback final.'}, - status=status.HTTP_403_FORBIDDEN) - self.perform_create(serializer) + + # update MetaSubmission information + meta = serializer.validated_data.get('of_submission').meta + # TODO add reviewer final + meta.feedback_authors.add(self.request.user) return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -106,16 +73,17 @@ class FeedbackApiView( partial=True) serializer.is_valid(raise_exception=True) - self._get_implicit_assignment_for_user(feedback.of_submission) + assignment = get_implicit_assignment_for_user(feedback.of_submission, self.request.user) + # TODO change this to check reviewer final if self._tutor_attempts_to_change_final_feedback_of_reviewer(serializer): # noqa raise PermissionDenied( detail="Changing final feedback is not allowed.") - if self._tutor_attempts_to_patch_first_feedback_final(serializer): + if tutor_attempts_to_patch_first_feedback_final(serializer, self.request.user, assignment): raise PermissionDenied( detail='Cannot set the first feedback final.') - + # TODO add reviewer final serializer.save() return Response(serializer.data) diff --git a/core/views/subscription.py b/core/views/subscription.py index bf77f467..2ea0b0c0 100644 --- a/core/views/subscription.py +++ b/core/views/subscription.py @@ -2,7 +2,8 @@ import logging from django.core.exceptions import ObjectDoesNotExist from rest_framework import mixins, status, viewsets -from rest_framework.decorators import action, permission_classes +from rest_framework import decorators +from rest_framework.exceptions import PermissionDenied from rest_framework.response import Response from core import models, permissions, serializers @@ -12,6 +13,8 @@ from core.serializers import AssignmentDetailSerializer, AssignmentSerializer from multiprocessing import Lock +from core.views.util import tutor_attempts_to_patch_first_feedback_final + log = logging.getLogger(__name__) @@ -76,6 +79,7 @@ class AssignmentApiViewSet( queryset = TutorSubmissionAssignment.objects\ .select_related('subscription').all() serializer_class = AssignmentSerializer + permission_classes = (IsTutorOrReviewer, ) def get_queryset(self): if self.action in ['list', 'active', 'destroy']: @@ -97,11 +101,11 @@ class AssignmentApiViewSet( status=status.HTTP_403_FORBIDDEN) return Response(serializer.data, status=status.HTTP_201_CREATED) - @permission_classes((IsReviewer,)) + @decorators.permission_classes((IsReviewer,)) def list(self, *args, **kwargs): return super().list(*args, **kwargs) - @action(detail=False, permission_classes=(IsReviewer,), methods=['get', 'delete']) + @decorators.action(detail=False, permission_classes=(IsReviewer,), methods=['get', 'delete']) def active(self, request): if request.method == 'GET': queryset = self.get_queryset().filter(is_done=False) @@ -111,7 +115,36 @@ class AssignmentApiViewSet( self.get_queryset().filter(is_done=False).delete() return Response(status=status.HTTP_204_NO_CONTENT) - @permission_classes((IsTutorOrReviewer,)) + @decorators.action(detail=True, methods=['post']) + def finish(self, request, *args, **kwargs): + context = self.get_serializer_context() + instance = self.get_object() + if instance.is_done or (instance.subscription.owner != request.user): + return Response(status=status.HTTP_403_FORBIDDEN) + try: + orig_feedback = instance.submission.feedback + serializer = serializers.FeedbackSerializer( + orig_feedback, + data=request.data, + context=context, + partial=True) + except models.Feedback.DoesNotExist: + serializer = serializers.FeedbackSerializer( + data=request.data, + context=context) + + serializer.is_valid(raise_exception=True) + if tutor_attempts_to_patch_first_feedback_final(serializer, self.request.user, instance): + raise PermissionDenied( + detail='Cannot set the first feedback final.') + # TODO check if is reviewer final + serializer.save() + instance.finish() + response_status = status.HTTP_201_CREATED if \ + instance.subscription.feedback_stage == \ + models.SubmissionSubscription.FEEDBACK_CREATION else status.HTTP_200_OK + return Response(serializer.data, status=response_status) + def destroy(self, request, pk=None): """ Stop working on the assignment before it is finished """ instance = self.get_object() @@ -123,7 +156,6 @@ class AssignmentApiViewSet( instance.delete() return Response(status=status.HTTP_204_NO_CONTENT) - @permission_classes((IsTutorOrReviewer,)) def create(self, request, *args, **kwargs): with Lock(): context = self.get_serializer_context() @@ -133,7 +165,6 @@ class AssignmentApiViewSet( assignment = self._fetch_assignment(serializer) return assignment - @permission_classes((IsTutorOrReviewer,)) def retrieve(self, request, *args, **kwargs): assignment = self.get_object() if assignment.subscription.owner != request.user: diff --git a/core/views/util.py b/core/views/util.py new file mode 100644 index 00000000..b0301873 --- /dev/null +++ b/core/views/util.py @@ -0,0 +1,33 @@ +from rest_framework.exceptions import PermissionDenied + +from core import models + + +class NoAssignmentForTutor(Exception): + pass + + +def tutor_attempts_to_patch_first_feedback_final(feedback_serializer, + user, + assignment=None): + if user.role == models.UserAccount.REVIEWER: + return False + if user.role == models.UserAccount.TUTOR and assignment is None: + raise NoAssignmentForTutor() + is_final_set = feedback_serializer.validated_data.get('is_final', False) + in_creation = assignment.subscription.feedback_stage == models.SubmissionSubscription.FEEDBACK_CREATION # noqa + return is_final_set and in_creation + + +def get_implicit_assignment_for_user(submission, user): + """ Check for tutor if it exists. Not relevant for reviewer """ + try: + return models.TutorSubmissionAssignment.objects.get( + subscription__owner=user, + submission=submission + ) + except models.TutorSubmissionAssignment.DoesNotExist: + if user.role == models.UserAccount.REVIEWER: + return None + raise PermissionDenied( + detail='This user has no permission to create this feedback') diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 1fa6700d..89674f81 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -151,14 +151,19 @@ export async function createAssignment ( return (await ax.post(`/api/assignment/`, data)).data } -export async function submitFeedbackForAssignment ({ feedback }: {feedback: Partial<Feedback>}): Promise<Feedback> { - return (await ax.post('/api/feedback/', feedback)).data +export async function submitFeedbackForAssignment ({ feedback, assignment }: + {feedback: Partial<Feedback>, assignment: Assignment}): Promise<Feedback> { + return (await ax.post(`/api/assignment/${assignment.pk}/finish/`, feedback)).data } -export async function submitUpdatedFeedback ({ feedback }: {feedback: Feedback}): Promise<Feedback> { +export async function submitUpdatedFeedback ({ feedback }: {feedback: Partial<Feedback>}): Promise<Feedback> { return (await ax.patch(`/api/feedback/${feedback.ofSubmission}/`, feedback)).data } +export async function submitFeedback ({feedback}: {feedback: Partial<Feedback>}): Promise<Feedback> { + return (await ax.post(`/api/feedback/`, feedback)).data +} + export async function fetchSubmissionTypes (): Promise<Array<SubmissionType>> { const url = '/api/submissiontype/' return (await ax.get(url)).data diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts index 9055ea81..7afa5c09 100644 --- a/frontend/src/store/modules/submission-notes.ts +++ b/frontend/src/store/modules/submission-notes.ts @@ -6,6 +6,7 @@ import { RootState } from '@/store/store' import { getStoreBuilder, BareActionContext } from 'vuex-typex' import { syntaxPostProcess } from '@/util/helpers'; import { AxiosResponse } from 'axios'; +import { Subscriptions } from './subscriptions'; export interface SubmissionNotesState { submission: SubmissionNoType @@ -181,11 +182,15 @@ Promise<AxiosResponse<void>[]> { } else if (feedback.score! < SubmissionNotes.submissionType.fullScore! && !state.hasOrigFeedback) { throw new Error('You need to add or change a comment when setting a non full score.') } - if (!state.hasOrigFeedback) { - await api.submitFeedbackForAssignment({ feedback }) - } else { + + const assignment = Subscriptions.state.currentAssignment + if (assignment) { + await api.submitFeedbackForAssignment({ feedback , assignment}) + } else if (state.origFeedback) { feedback.pk = state.origFeedback.pk - await api.submitUpdatedFeedback(<{ feedback: Feedback }>{ feedback }) + await api.submitUpdatedFeedback({ feedback }) + } else { + await api.submitFeedback({feedback}) } // delete those comments that have been marked for deletion return SubmissionNotes.deleteComments() diff --git a/functional_tests/test_export_modal.py b/functional_tests/test_export_modal.py index e6af2003..6fa6aa16 100644 --- a/functional_tests/test_export_modal.py +++ b/functional_tests/test_export_modal.py @@ -117,8 +117,8 @@ class ExportTestModal(LiveServerTestCase): os.remove(JSON_EXPORT_FILE) def test_export_instance(self): - self._login() fact.SubmissionFactory() + self._login() self.browser.find_element_by_id('export-btn').click() self.browser.find_element_by_id('export-list1').click() instance_export_modal = self.browser.find_element_by_id('instance-export-modal') -- GitLab