From 728ccd4bcac2daf56bd98f6d15a3a2e986018018 Mon Sep 17 00:00:00 2001 From: Felix Sun Date: Thu, 27 Jun 2013 16:57:48 -0400 Subject: [PATCH] Fixed some docstring formatting things. Expanded test coverage a little. --- .../lib/xmodule/xmodule/crowdsource_hinter.py | 20 +++---- .../xmodule/tests/test_crowdsource_hinter.py | 15 +++++ lms/djangoapps/instructor/hint_manager.py | 30 +++++----- .../instructor/tests/test_hint_manager.py | 57 +++++++++++++++++-- 4 files changed, 92 insertions(+), 30 deletions(-) diff --git a/common/lib/xmodule/xmodule/crowdsource_hinter.py b/common/lib/xmodule/xmodule/crowdsource_hinter.py index 8a238a1779..341a2598ef 100644 --- a/common/lib/xmodule/xmodule/crowdsource_hinter.py +++ b/common/lib/xmodule/xmodule/crowdsource_hinter.py @@ -129,11 +129,11 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): Called by hinter javascript after a problem is graded as incorrect. Args: - get -- must be interpretable by capa_answer_to_str. + `get` -- must be interpretable by capa_answer_to_str. Output keys: - - best_hint is the hint text with the most votes. - - rand_hint_1 and rand_hint_2 are two random hints to the answer in get. - - answer is the parsed answer that was submitted. + - 'best_hint' is the hint text with the most votes. + - 'rand_hint_1' and 'rand_hint_2' are two random hints to the answer in `get`. + - 'answer' is the parsed answer that was submitted. """ answer = self.capa_answer_to_str(get) # Look for a hint to give. @@ -176,10 +176,10 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): The student got it correct. Ask him to vote on hints, or submit a hint. Args: - get -- not actually used. (It is assumed that the answer is correct.) + `get` -- not actually used. (It is assumed that the answer is correct.) Output keys: - - index_to_hints maps previous answer indices to hints that the user saw earlier. - - index_to_answer maps previous answer indices to the actual answer submitted. + - 'index_to_hints' maps previous answer indices to hints that the user saw earlier. + - 'index_to_answer' maps previous answer indices to the actual answer submitted. """ # The student got it right. # Did he submit at least one wrong answer? @@ -217,10 +217,10 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): Tally a user's vote on his favorite hint. Args: - get -- expected to have the following keys: + `get` -- expected to have the following keys: 'answer': ans_no (index in previous_answers) 'hint': hint_pk - Returns key hint_and_votes, a list of (hint_text, #votes) pairs. + Returns key 'hint_and_votes', a list of (hint_text, #votes) pairs. """ if self.user_voted: return json.dumps({'contents': 'Sorry, but you have already voted!'}) @@ -250,7 +250,7 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): Take a hint submission and add it to the database. Args: - get -- expected to have the following keys: + `get` -- expected to have the following keys: 'answer': answer index in previous_answers 'hint': text of the new hint that the user is adding Returns a thank-you message. diff --git a/common/lib/xmodule/xmodule/tests/test_crowdsource_hinter.py b/common/lib/xmodule/xmodule/tests/test_crowdsource_hinter.py index 1bb04654f0..b97fb34d9b 100644 --- a/common/lib/xmodule/xmodule/tests/test_crowdsource_hinter.py +++ b/common/lib/xmodule/xmodule/tests/test_crowdsource_hinter.py @@ -192,6 +192,21 @@ class CrowdsourceHinterTest(unittest.TestCase): self.assertTrue('This is supposed to be test html.' in out_html) self.assertTrue('this/is/a/fake/ajax/url' in out_html) + def test_gethtml_nochild(self): + """ + get_html, except the module has no child :( Should return a polite + error message. + """ + m = CHModuleFactory.create() + def fake_get_display_items(): + """ + Returns no children. + """ + return [] + m.get_display_items = fake_get_display_items + out_html = m.get_html() + self.assertTrue('Error in loading crowdsourced hinter' in out_html) + def test_gethtml_multiple(self): """ Makes sure that multiple crowdsourced hinters play nice, when get_html diff --git a/lms/djangoapps/instructor/hint_manager.py b/lms/djangoapps/instructor/hint_manager.py index 056784947d..4d5b35356b 100644 --- a/lms/djangoapps/instructor/hint_manager.py +++ b/lms/djangoapps/instructor/hint_manager.py @@ -54,17 +54,17 @@ def get_hints(request, course_id, field): Load all of the hints submitted to the course. Args: - request -- Django request object. - course_id -- The course id, like 'Me/19.002/test_course' - field -- Either 'hints' or 'mod_queue'; specifies which set of hints to load. + `request` -- Django request object. + `course_id` -- The course id, like 'Me/19.002/test_course' + `field` -- Either 'hints' or 'mod_queue'; specifies which set of hints to load. Keys in returned dict: - - field: Same as input - - other_field: 'mod_queue' if field == 'hints'; and vice-versa. - - field_label, other_field_label: English name for the above. - - all_hints: A list of [answer, pk dict] pairs, representing all hints. + - 'field': Same as input + - 'other_field': 'mod_queue' if `field` == 'hints'; and vice-versa. + - 'field_label', 'other_field_label': English name for the above. + - 'all_hints': A list of [answer, pk dict] pairs, representing all hints. Sorted by answer. - - id_to_name: A dictionary mapping problem id to problem name. + - 'id_to_name': A dictionary mapping problem id to problem name. """ if field == 'mod_queue': other_field = 'hints' @@ -92,8 +92,8 @@ def get_hints(request, course_id, field): def answer_sorter(thing): """ - thing is a tuple, where thing[0] contains an answer, and thing[1] contains - a dict of hints. This function returns an index based on thing[0], which + `thing` is a tuple, where `thing[0]` contains an answer, and `thing[1]` contains + a dict of hints. This function returns an index based on `thing[0]`, which is used as a key to sort the list of things. """ try: @@ -131,10 +131,10 @@ def delete_hints(request, course_id, field): """ Deletes the hints specified. - request.POST contains some fields keyed by integers. Each such field contains a + `request.POST` contains some fields keyed by integers. Each such field contains a [problem_defn_id, answer, pk] tuple. These tuples specify the hints to be deleted. - Example request.POST: + Example `request.POST`: {'op': 'delete_hints', 'field': 'mod_queue', 1: ['problem_whatever', '42.0', '3'], @@ -158,8 +158,8 @@ def change_votes(request, course_id, field): """ Updates the number of votes. - The numbered fields of request.POST contain [problem_id, answer, pk, new_votes] tuples. - - Very similar to delete_hints. Is there a way to merge them? Nah, too complicated. + The numbered fields of `request.POST` contain [problem_id, answer, pk, new_votes] tuples. + - Very similar to `delete_hints`. Is there a way to merge them? Nah, too complicated. """ for key in request.POST: @@ -176,7 +176,7 @@ def change_votes(request, course_id, field): def add_hint(request, course_id, field): """ - Add a new hint. request.POST: + Add a new hint. `request.POST`: op field problem - The problem id diff --git a/lms/djangoapps/instructor/tests/test_hint_manager.py b/lms/djangoapps/instructor/tests/test_hint_manager.py index 44e8458e19..39227f93d6 100644 --- a/lms/djangoapps/instructor/tests/test_hint_manager.py +++ b/lms/djangoapps/instructor/tests/test_hint_manager.py @@ -74,13 +74,17 @@ class HintManagerTest(ModuleStoreTestCase): rejected. """ out = self.c.post(self.url, {'op': 'delete hints', 'field': 'all your private data'}) - # Keep this around for reference - might be useful later. - # request = RequestFactory() - # post = request.post(self.url, {'op': 'delete hints', 'field': 'all your private data'}) - # out = view.hint_manager(post, 'Me/19.002/test_course') print out self.assertTrue('an invalid field was accessed' in out.content) + def test_switchfields(self): + """ + Checks that the op: 'switch fields' POST request works. + """ + out = self.c.post(self.url, {'op': 'switch fields', 'field': 'mod_queue'}) + print out + self.assertTrue('Hint 2' in out.content) + def test_gethints(self): """ Checks that gethints returns the right data. @@ -93,6 +97,21 @@ class HintManagerTest(ModuleStoreTestCase): expected = {self.problem_id: [(u'2.0', {u'2': [u'Hint 2', 1]})]} self.assertTrue(out['all_hints'] == expected) + def test_gethints_other(self): + """ + Same as above, with hints instead of mod_queue + """ + request = RequestFactory() + post = request.post(self.url, {'field': 'hints'}) + out = view.get_hints(post, self.course_id, 'hints') + print out + self.assertTrue(out['other_field'] == 'mod_queue') + expected = {self.problem_id: [('1.0', {'1': ['Hint 1', 2], + '3': ['Hint 3', 12]}), + ('2.0', {'4': ['Hint 4', 3]}) + ]} + self.assertTrue(out['all_hints'] == expected) + def test_deletehints(self): """ Checks that delete_hints deletes the right stuff. @@ -119,7 +138,35 @@ class HintManagerTest(ModuleStoreTestCase): print json.loads(problem_hints)['1.0']['1'] self.assertTrue(json.loads(problem_hints)['1.0']['1'][1] == 5) - + def test_addhint(self): + """ + Check that instructors can add new hints. + """ + request = RequestFactory() + post = request.post(self.url, {'field': 'mod_queue', + 'op': 'add hint', + 'problem': self.problem_id, + 'answer': '3.14', + 'hint': 'This is a new hint.'}) + view.add_hint(post, self.course_id, 'mod_queue') + problem_hints = XModuleContentField.objects.get(field_name='mod_queue', definition_id=self.problem_id).value + self.assertTrue('3.14' in json.loads(problem_hints)) + + def test_approve(self): + """ + Check that instructors can approve hints. (Move them + from the mod_queue to the hints.) + """ + request = RequestFactory() + post = request.post(self.url, {'field': 'mod_queue', + 'op': 'approve', + 1: [self.problem_id, '2.0', '2']}) + view.approve(post, self.course_id, 'mod_queue') + problem_hints = XModuleContentField.objects.get(field_name='mod_queue', definition_id=self.problem_id).value + self.assertTrue('2.0' not in json.loads(problem_hints) or len(json.loads(problem_hints)['2.0']) == 0) + problem_hints = XModuleContentField.objects.get(field_name='hints', definition_id=self.problem_id).value + self.assertTrue(json.loads(problem_hints)['2.0']['2'] == ['Hint 2', 1]) + self.assertTrue(len(json.loads(problem_hints)['2.0']) == 2)