diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index c7eea5c1f3..5fe8f575e4 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -418,8 +418,9 @@ class CourseGradingTest(CourseTestCase): subgrader = CourseGradingModel.fetch_grader(self.course.id, i) self.assertDictEqual(grader, subgrader, str(i) + "th graders not equal") + @mock.patch('contentstore.signals.signals.GRADING_POLICY_CHANGED.send') @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split) - def test_update_from_json(self, store): + def test_update_from_json(self, store, send_signal): self.course = CourseFactory.create(default_store=store) test_grader = CourseGradingModel.fetch(self.course.id) @@ -450,7 +451,17 @@ class CourseGradingTest(CourseTestCase): altered_grader = CourseGradingModel.update_from_json(self.course.id, test_grader.__dict__, self.user) self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "4 hour grace period") - def test_update_grader_from_json(self): + # one for each of the calls to update_from_json() + send_signal.assert_has_calls([ + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + ]) + + @mock.patch('contentstore.signals.signals.GRADING_POLICY_CHANGED.send') + def test_update_grader_from_json(self, send_signal): test_grader = CourseGradingModel.fetch(self.course.id) altered_grader = CourseGradingModel.update_grader_from_json( self.course.id, test_grader.graders[1], self.user @@ -467,6 +478,13 @@ class CourseGradingTest(CourseTestCase): self.course.id, test_grader.graders[1], self.user) self.assertDictEqual(test_grader.graders[1], altered_grader, "drop_count[1] + 2") + # one for each of the calls to update_grader_from_json() + send_signal.assert_has_calls([ + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + ]) + def test_update_cutoffs_from_json(self): test_grader = CourseGradingModel.fetch(self.course.id) CourseGradingModel.update_cutoffs_from_json(self.course.id, test_grader.grade_cutoffs, self.user) @@ -508,7 +526,8 @@ class CourseGradingTest(CourseTestCase): # Once deleted, the grace period should simply be None self.assertEqual(None, altered_grader.grace_period, "Delete grace period") - def test_update_section_grader_type(self): + @mock.patch('contentstore.signals.signals.GRADING_POLICY_CHANGED.send') + def test_update_section_grader_type(self, send_signal): # Get the descriptor and the section_grader_type and assert they are the default values descriptor = modulestore().get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) @@ -535,29 +554,44 @@ class CourseGradingTest(CourseTestCase): self.assertEqual(None, descriptor.format) self.assertEqual(False, descriptor.graded) + # one for each call to update_section_grader_type() + send_signal.assert_has_calls([ + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + ]) + + def _model_from_url(self, url_base): + response = self.client.get_json(url_base) + return json.loads(response.content) + def test_get_set_grader_types_ajax(self): """ - Test configuring the graders via ajax calls + Test creating and fetching the graders via ajax calls. """ grader_type_url_base = get_url(self.course.id, 'grading_handler') - # test get whole - response = self.client.get_json(grader_type_url_base) - whole_model = json.loads(response.content) + whole_model = self._model_from_url(grader_type_url_base) + self.assertIn('graders', whole_model) self.assertIn('grade_cutoffs', whole_model) self.assertIn('grace_period', whole_model) + # test post/update whole whole_model['grace_period'] = {'hours': 1, 'minutes': 30, 'seconds': 0} response = self.client.ajax_post(grader_type_url_base, whole_model) self.assertEqual(200, response.status_code) - response = self.client.get_json(grader_type_url_base) - whole_model = json.loads(response.content) + whole_model = self._model_from_url(grader_type_url_base) self.assertEqual(whole_model['grace_period'], {'hours': 1, 'minutes': 30, 'seconds': 0}) + # test get one grader self.assertGreater(len(whole_model['graders']), 1) # ensure test will make sense - response = self.client.get_json(grader_type_url_base + '/1') - grader_sample = json.loads(response.content) + grader_sample = self._model_from_url(grader_type_url_base + '/1') self.assertEqual(grader_sample, whole_model['graders'][1]) + + @mock.patch('contentstore.signals.signals.GRADING_POLICY_CHANGED.send') + def test_add_delete_grader(self, send_signal): + grader_type_url_base = get_url(self.course.id, 'grading_handler') + original_model = self._model_from_url(grader_type_url_base) + # test add grader new_grader = { "type": "Extra Credit", @@ -566,22 +600,31 @@ class CourseGradingTest(CourseTestCase): "short_label": None, "weight": 15, } + response = self.client.ajax_post( - '{}/{}'.format(grader_type_url_base, len(whole_model['graders'])), + '{}/{}'.format(grader_type_url_base, len(original_model['graders'])), new_grader ) + self.assertEqual(200, response.status_code) grader_sample = json.loads(response.content) - new_grader['id'] = len(whole_model['graders']) + new_grader['id'] = len(original_model['graders']) self.assertEqual(new_grader, grader_sample) - # test delete grader + + # test deleting the original grader response = self.client.delete(grader_type_url_base + '/1', HTTP_ACCEPT="application/json") + self.assertEqual(204, response.status_code) - response = self.client.get_json(grader_type_url_base) - updated_model = json.loads(response.content) + updated_model = self._model_from_url(grader_type_url_base) new_grader['id'] -= 1 # one fewer and the id mutates self.assertIn(new_grader, updated_model['graders']) - self.assertNotIn(whole_model['graders'][1], updated_model['graders']) + self.assertNotIn(original_model['graders'][1], updated_model['graders']) + send_signal.assert_has_calls([ + # once for the POST + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + # once for the DELETE + mock.call(sender=CourseGradingModel, user_id=self.user.id, course_id=self.course.id), + ]) def setup_test_set_get_section_grader_ajax(self): """ diff --git a/cms/djangoapps/models/settings/course_grading.py b/cms/djangoapps/models/settings/course_grading.py index 5585dd9442..ffd14ae19b 100644 --- a/cms/djangoapps/models/settings/course_grading.py +++ b/cms/djangoapps/models/settings/course_grading.py @@ -1,4 +1,6 @@ from datetime import timedelta + +from contentstore.signals.signals import GRADING_POLICY_CHANGED from xmodule.modulestore.django import modulestore @@ -65,6 +67,8 @@ class CourseGradingModel(object): CourseGradingModel.update_minimum_grade_credit_from_json(course_key, jsondict['minimum_grade_credit'], user) + GRADING_POLICY_CHANGED.send(sender=CourseGradingModel, user_id=user.id, course_id=course_key) + return CourseGradingModel.fetch(course_key) @staticmethod @@ -86,6 +90,8 @@ class CourseGradingModel(object): modulestore().update_item(descriptor, user.id) + GRADING_POLICY_CHANGED.send(sender=CourseGradingModel, user_id=user.id, course_id=course_key) + return CourseGradingModel.jsonize_grader(index, descriptor.raw_grader[index]) @staticmethod @@ -155,6 +161,8 @@ class CourseGradingModel(object): modulestore().update_item(descriptor, user.id) + GRADING_POLICY_CHANGED.send(sender=CourseGradingModel, user_id=user.id, course_id=course_key) + @staticmethod def delete_grace_period(course_key, user): """ @@ -184,6 +192,13 @@ class CourseGradingModel(object): del descriptor.graded modulestore().update_item(descriptor, user.id) + + GRADING_POLICY_CHANGED.send( + sender=CourseGradingModel, + user_id=user.id, + course_id=descriptor.location.course_key + ) + return {'graderType': grader_type} @staticmethod