From 4237df625a5ab65c146e85f60ea206e7f63ec0c9 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 12 Feb 2013 14:31:30 -0500 Subject: [PATCH 01/22] Add in a check function to see if students are allowed to submit peer grading --- .../open_ended_module.py | 1 + .../openendedchild.py | 23 +++++++++++++++++++ .../xmodule/xmodule/peer_grading_module.py | 1 + 3 files changed, 25 insertions(+) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index 95c631c8fd..5d22d6d1b4 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -73,6 +73,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): self.send_to_grader(self.latest_answer(), system) self.created = False + def _parse(self, oeparam, prompt, rubric, system): ''' Parse OpenEndedResponse XML: diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py index 1700dcaa07..6c3fd46d19 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py @@ -22,6 +22,7 @@ from xmodule.stringify import stringify_children from xmodule.xml_module import XmlDescriptor from xmodule.modulestore import Location from capa.util import * +from peer_grading_service import PeerGradingService from datetime import datetime @@ -104,7 +105,9 @@ class OpenEndedChild(object): # Used for progress / grading. Currently get credit just for # completion (doesn't matter if you self-assessed correct/incorrect). self._max_score = static_data['max_score'] + self.peer_gs = PeerGradingService(system.open_ended_grading_interface, system) + self.system = system self.setup_response(system, location, definition, descriptor) def setup_response(self, system, location, definition, descriptor): @@ -408,3 +411,23 @@ class OpenEndedChild(object): success = True return success, string + + def check_if_student_can_submit(self): + location = self.system.location.url() + student_id = self.system.anonymous_student_id + success = False + response = {} + try: + response = self.peer_gs.get_data_for_location(location, student_id) + count_graded = response['count_graded'] + count_required = response['count_required'] + success = True + except: + error_message = ("Need to peer grade more in order to make another submission. " + "You have graded {0}, {1} are required.").format(count_graded, count_required) + return success, False, error_message + if count_graded>=count_required: + return success, True, "" + else: + return success, False, "" + diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index 457c8ee72a..ba22e8d80c 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -155,6 +155,7 @@ class PeerGradingModule(XModule): count_graded = response['count_graded'] count_required = response['count_required'] if count_required > 0 and count_graded >= count_required: + #Ensures that once a student receives a final score for peer grading, that it does not change. self.student_data_for_location = response score_dict = { From 8d3f4932e7a87ebd7bc46481b595ad7026e64264 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 12 Feb 2013 14:41:36 -0500 Subject: [PATCH 02/22] Add in checks to see if student can submit (ie needs to peer grade more or not) --- .../open_ended_grading_classes/open_ended_module.py | 13 +++++++++---- .../open_ended_grading_classes/openendedchild.py | 8 +++++--- .../self_assessment_module.py | 11 ++++++++--- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index 5d22d6d1b4..57d53c41c6 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -606,10 +606,15 @@ class OpenEndedModule(openendedchild.OpenEndedChild): success, get = self.append_image_to_student_answer(get) error_message = "" if success: - get['student_answer'] = OpenEndedModule.sanitize_html(get['student_answer']) - self.new_history_entry(get['student_answer']) - self.send_to_grader(get['student_answer'], system) - self.change_state(self.ASSESSING) + success, allowed_to_submit, error_message = self.check_if_student_can_submit() + if allowed_to_submit: + get['student_answer'] = OpenEndedModule.sanitize_html(get['student_answer']) + self.new_history_entry(get['student_answer']) + self.send_to_grader(get['student_answer'], system) + self.change_state(self.ASSESSING) + else: + #Error message already defined, so we can just pass it down the chain + pass else: error_message = "There was a problem saving the image in your submission. Please try a different image, or try pasting a link to an image into the answer box." diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py index 6c3fd46d19..4f4159c612 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py @@ -416,6 +416,7 @@ class OpenEndedChild(object): location = self.system.location.url() student_id = self.system.anonymous_student_id success = False + allowed_to_submit = True response = {} try: response = self.peer_gs.get_data_for_location(location, student_id) @@ -425,9 +426,10 @@ class OpenEndedChild(object): except: error_message = ("Need to peer grade more in order to make another submission. " "You have graded {0}, {1} are required.").format(count_graded, count_required) - return success, False, error_message + return success, allowed_to_submit, error_message if count_graded>=count_required: - return success, True, "" + return success, allowed_to_submit, "" else: - return success, False, "" + allowed_to_submit = False + return success, allowed_to_submit, "" diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py index c608eeea06..27ef58ac98 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py @@ -177,9 +177,14 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): # add new history element with answer and empty score and hint. success, get = self.append_image_to_student_answer(get) if success: - get['student_answer'] = SelfAssessmentModule.sanitize_html(get['student_answer']) - self.new_history_entry(get['student_answer']) - self.change_state(self.ASSESSING) + success, allowed_to_submit, error_message = self.check_if_student_can_submit() + if allowed_to_submit: + get['student_answer'] = SelfAssessmentModule.sanitize_html(get['student_answer']) + self.new_history_entry(get['student_answer']) + self.change_state(self.ASSESSING) + else: + #Error message already defined, so we can just pass + pass else: error_message = "There was a problem saving the image in your submission. Please try a different image, or try pasting a link to an image into the answer box." From 1409f8aa6bb05b08afe6180bf427d98dfb54c311 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 12 Feb 2013 15:03:03 -0500 Subject: [PATCH 03/22] Add in checking for score list length to rubric rendering --- .../open_ended_grading_classes/combined_open_ended_rubric.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py index 817d0293e7..b6f080884f 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py @@ -50,7 +50,7 @@ class CombinedOpenEndedRubric(object): success = False try: rubric_categories = self.extract_categories(rubric_xml) - if score_list: + if score_list and len(score_list)==len(rubric_categories): for i in xrange(0,len(rubric_categories)): category = rubric_categories[i] for j in xrange(0,len(category['options'])): @@ -72,7 +72,7 @@ class CombinedOpenEndedRubric(object): success = True except: error_message = "[render_rubric] Could not parse the rubric with xml: {0}".format(rubric_xml) - log.error(error_message) + log.exception(error_message) raise RubricParsingError(error_message) return {'success' : success, 'html' : html, 'rubric_scores' : rubric_scores} @@ -200,7 +200,6 @@ class CombinedOpenEndedRubric(object): for grader_type in tuple[3]: rubric_categories[i]['options'][j]['grader_types'].append(grader_type) - log.debug(rubric_categories) html = self.system.render_template('open_ended_combined_rubric.html', {'categories': rubric_categories, 'has_score': True, From 74b1187a3985cb329b08571e596a42d87c5daf96 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 12 Feb 2013 15:04:49 -0500 Subject: [PATCH 04/22] Remove log statements --- .../open_ended_grading_classes/combined_open_ended_modulev1.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index ac5b952def..59ae61a66f 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -537,9 +537,6 @@ class CombinedOpenEndedV1Module(): rubric_scores = [all_responses[i]['rubric_scores'] for i in xrange(0,len(all_responses)) if len(all_responses[i]['rubric_scores'])>0 and all_responses[i]['grader_types'][0] in HUMAN_GRADER_TYPE.keys()] grader_types = [all_responses[i]['grader_types'] for i in xrange(0,len(all_responses)) if len(all_responses[i]['grader_types'])>0 and all_responses[i]['grader_types'][0] in HUMAN_GRADER_TYPE.keys()] feedback_items = [all_responses[i]['feedback_items'] for i in xrange(0,len(all_responses)) if len(all_responses[i]['feedback_items'])>0 and all_responses[i]['grader_types'][0] in HUMAN_GRADER_TYPE.keys()] - log.debug(rubric_scores) - log.debug(grader_types) - log.debug(feedback_items) rubric_html = self.rubric_renderer.render_combined_rubric(stringify_children(self.static_data['rubric']), rubric_scores, grader_types, feedback_items) From 53028b46c32ad851ffe67e9c5af931ad1be93c60 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 12 Feb 2013 15:23:11 -0500 Subject: [PATCH 05/22] Remove debug statements --- .../open_ended_grading_classes/peer_grading_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/peer_grading_service.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/peer_grading_service.py index 5b639be4f4..1fabf6d323 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/peer_grading_service.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/peer_grading_service.py @@ -30,8 +30,8 @@ class PeerGradingService(GradingService): self.system = system def get_data_for_location(self, problem_location, student_id): - response = self.get(self.get_data_for_location_url, - {'location': problem_location, 'student_id': student_id}) + params = {'location': problem_location, 'student_id': student_id} + response = self.get(self.get_data_for_location_url, params) return self.try_to_decode(response) def get_next_submission(self, problem_location, grader_id): From 5cf7441da1f785ccb04dafab48ef6d5e20df95a4 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 12 Feb 2013 15:31:30 -0500 Subject: [PATCH 06/22] Fix error message passing to JS and student --- .../open_ended_grading_classes/open_ended_module.py | 6 +++--- .../xmodule/open_ended_grading_classes/openendedchild.py | 9 ++++++--- .../open_ended_grading_classes/self_assessment_module.py | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index 57d53c41c6..2472244950 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -613,13 +613,13 @@ class OpenEndedModule(openendedchild.OpenEndedChild): self.send_to_grader(get['student_answer'], system) self.change_state(self.ASSESSING) else: - #Error message already defined, so we can just pass it down the chain - pass + #Error message already defined + success = False else: error_message = "There was a problem saving the image in your submission. Please try a different image, or try pasting a link to an image into the answer box." return { - 'success': True, + 'success': success, 'error': error_message, 'student_response': get['student_answer'] } diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py index 4f4159c612..58939e175f 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py @@ -418,18 +418,21 @@ class OpenEndedChild(object): success = False allowed_to_submit = True response = {} + error_string = ("You need to peer grade more in order to make another submission. " + "You have graded {0}, and {1} are required. You have made {2} successful peer grading submissions.") try: response = self.peer_gs.get_data_for_location(location, student_id) count_graded = response['count_graded'] count_required = response['count_required'] + student_sub_count = response['student_sub_count'] success = True except: - error_message = ("Need to peer grade more in order to make another submission. " - "You have graded {0}, {1} are required.").format(count_graded, count_required) + error_message = "Could not contact the grading controller." return success, allowed_to_submit, error_message if count_graded>=count_required: return success, allowed_to_submit, "" else: allowed_to_submit = False - return success, allowed_to_submit, "" + error_message = error_string.format(count_graded, count_required, student_sub_count) + return success, allowed_to_submit, error_message diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py index 27ef58ac98..f9fd3e301d 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py @@ -183,11 +183,12 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): self.new_history_entry(get['student_answer']) self.change_state(self.ASSESSING) else: - #Error message already defined, so we can just pass - pass + #Error message already defined + success = False else: error_message = "There was a problem saving the image in your submission. Please try a different image, or try pasting a link to an image into the answer box." + log.debug(error_message) return { 'success': success, 'rubric_html': self.get_rubric_html(system), From b40a035ff7f9eb9bc9c3dd348765f391f1fbbd38 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 12 Feb 2013 15:32:57 -0500 Subject: [PATCH 07/22] Update error message --- .../xmodule/open_ended_grading_classes/openendedchild.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py index 58939e175f..567c882189 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py @@ -418,8 +418,8 @@ class OpenEndedChild(object): success = False allowed_to_submit = True response = {} - error_string = ("You need to peer grade more in order to make another submission. " - "You have graded {0}, and {1} are required. You have made {2} successful peer grading submissions.") + error_string = ("You need to peer grade {0} more in order to make another submission. " + "You have graded {1}, and {2} are required. You have made {3} successful peer grading submissions.") try: response = self.peer_gs.get_data_for_location(location, student_id) count_graded = response['count_graded'] @@ -433,6 +433,6 @@ class OpenEndedChild(object): return success, allowed_to_submit, "" else: allowed_to_submit = False - error_message = error_string.format(count_graded, count_required, student_sub_count) + error_message = error_string.format(count_required-count_graded, count_graded, count_required, student_sub_count) return success, allowed_to_submit, error_message From 8f7c14595792e3b76c3c65c1cc8284bbe2d4c730 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 09:45:29 -0500 Subject: [PATCH 08/22] Remove old import statements --- lms/djangoapps/open_ended_grading/open_ended_notifications.py | 1 - lms/djangoapps/open_ended_grading/views.py | 1 - 2 files changed, 2 deletions(-) diff --git a/lms/djangoapps/open_ended_grading/open_ended_notifications.py b/lms/djangoapps/open_ended_grading/open_ended_notifications.py index 34596218e8..ecec29fce3 100644 --- a/lms/djangoapps/open_ended_grading/open_ended_notifications.py +++ b/lms/djangoapps/open_ended_grading/open_ended_notifications.py @@ -4,7 +4,6 @@ from staff_grading_service import StaffGradingService from open_ended_grading.controller_query_service import ControllerQueryService import json from student.models import unique_id_for_user -import open_ended_util from courseware.models import StudentModule import logging from courseware.access import has_access diff --git a/lms/djangoapps/open_ended_grading/views.py b/lms/djangoapps/open_ended_grading/views.py index bb094e72fd..80f50b282d 100644 --- a/lms/djangoapps/open_ended_grading/views.py +++ b/lms/djangoapps/open_ended_grading/views.py @@ -16,7 +16,6 @@ from xmodule.open_ended_grading_classes.grading_service_module import GradingSer import json from student.models import unique_id_for_user -import open_ended_util import open_ended_notifications from xmodule.modulestore.django import modulestore From b0e55df13e71bd26d715767154c00b8e0a11fd46 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 11:24:21 -0500 Subject: [PATCH 09/22] Alter max attempts --- .../open_ended_grading_classes/combined_open_ended_modulev1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index 59ae61a66f..6c23df3c6c 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -23,7 +23,7 @@ log = logging.getLogger("mitx.courseware") # Set the default number of max attempts. Should be 1 for production # Set higher for debugging/testing # attempts specified in xml definition overrides this. -MAX_ATTEMPTS = 10000 +MAX_ATTEMPTS = 1 # Set maximum available number of points. # Overriden by max_score specified in xml. From 02b0bc2923fe72635acda6adfa1894ce4fb357f8 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 15:37:42 -0500 Subject: [PATCH 10/22] Add in answer unknown container/checkbox to pass back to controller --- .../xmodule/js/src/peergrading/peer_grading_problem.coffee | 6 ++++++ lms/templates/peer_grading/peer_grading_problem.html | 1 + 2 files changed, 7 insertions(+) diff --git a/common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee b/common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee index 5770238649..63c58e1766 100644 --- a/common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee +++ b/common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee @@ -175,6 +175,7 @@ class @PeerGradingProblem @prompt_container = $('.prompt-container') @rubric_container = $('.rubric-container') @flag_student_container = $('.flag-student-container') + @answer_unknown_container = $('.answer-unknown-container') @calibration_panel = $('.calibration-panel') @grading_panel = $('.grading-panel') @content_panel = $('.content-panel') @@ -208,6 +209,7 @@ class @PeerGradingProblem @interstitial_page_button = $('.interstitial-page-button') @calibration_interstitial_page_button = $('.calibration-interstitial-page-button') @flag_student_checkbox = $('.flag-checkbox') + @answer_unknown_checkbox = $('.answer-unknown-checkbox') @collapse_question() Collapsible.setCollapsibles(@content_panel) @@ -262,6 +264,7 @@ class @PeerGradingProblem submission_key: @submission_key_input.val() feedback: @feedback_area.val() submission_flagged: @flag_student_checkbox.is(':checked') + answer_unknown: @answer_unknown_checkbox.is(':checked') return data @@ -360,6 +363,8 @@ class @PeerGradingProblem @calibration_panel.find('.grading-text').hide() @grading_panel.find('.grading-text').hide() @flag_student_container.hide() + @answer_unknown_container.hide() + @feedback_area.val("") @submit_button.unbind('click') @@ -388,6 +393,7 @@ class @PeerGradingProblem @calibration_panel.find('.grading-text').show() @grading_panel.find('.grading-text').show() @flag_student_container.show() + @answer_unknown_container.show() @feedback_area.val("") @submit_button.unbind('click') diff --git a/lms/templates/peer_grading/peer_grading_problem.html b/lms/templates/peer_grading/peer_grading_problem.html index 9468b594a2..853fa750e8 100644 --- a/lms/templates/peer_grading/peer_grading_problem.html +++ b/lms/templates/peer_grading/peer_grading_problem.html @@ -44,6 +44,7 @@

Flag this submission for review by course staff (use if the submission contains inappropriate content):

+

I do not know how to grade this question:

From 48a5c9f0b2c8dfdca71b09a6f46ef8056e7b1b27 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 16:05:12 -0500 Subject: [PATCH 11/22] Remove score display from status bar --- lms/templates/combined_open_ended_status.html | 2 +- lms/templates/instructor/staff_grading.html | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lms/templates/combined_open_ended_status.html b/lms/templates/combined_open_ended_status.html index fa84e7bab7..d13077737f 100644 --- a/lms/templates/combined_open_ended_status.html +++ b/lms/templates/combined_open_ended_status.html @@ -16,7 +16,7 @@ %else: ${status['human_task']} %endif - (${status['human_state']}) ${status['score']} / ${status['max_score']} + (${status['human_state']}) %endfor diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 6b2e6a8136..dbaa8c9e1b 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -76,7 +76,6 @@ -
From aca73d21230f2ef2790d3ff7be967deab0ccf124 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 16:13:34 -0500 Subject: [PATCH 12/22] Start to document and sanitize error messages --- .../combined_open_ended_modulev1.py | 8 +++++++- .../combined_open_ended_rubric.py | 18 ++++++++++++++++-- .../grading_service_module.py | 6 ++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index 6c23df3c6c..40560f59f3 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -157,6 +157,7 @@ class CombinedOpenEndedV1Module(): try: self.display_due_date = dateutil.parser.parse(display_due_date_string) except ValueError: + #This is a staff_facing_error log.error("Could not parse due date {0} for location {1}".format(display_due_date_string, location)) raise else: @@ -168,6 +169,7 @@ class CombinedOpenEndedV1Module(): self.grace_period = parse_timedelta(grace_period_string) self.close_date = self.display_due_date + self.grace_period except: + #This is a staff_facing_error log.error("Error parsing the grace period {0} for location {1}".format(grace_period_string, location)) raise else: @@ -662,7 +664,10 @@ class CombinedOpenEndedV1Module(): if self.attempts > self.max_attempts: return { 'success': False, - 'error': 'Too many attempts.' + #This is a student_facing_error + 'error': ('You have attempted this question {0} times. ' + 'You are only allowed to attempt it {1} times.').format( + self.attempts, self.max_attempts) } self.state = self.INITIAL self.allow_reset = False @@ -801,6 +806,7 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor): expected_children = ['task', 'rubric', 'prompt'] for child in expected_children: if len(xml_object.xpath(child)) == 0: + #This is a staff_facing_error raise ValueError("Combined Open Ended definition must include at least one '{0}' tag".format(child)) def parse_task(k): diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py index b6f080884f..483b21fddc 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py @@ -71,6 +71,7 @@ class CombinedOpenEndedRubric(object): }) success = True except: + #This is a staff_facing_error error_message = "[render_rubric] Could not parse the rubric with xml: {0}".format(rubric_xml) log.exception(error_message) raise RubricParsingError(error_message) @@ -81,6 +82,7 @@ class CombinedOpenEndedRubric(object): success = rubric_dict['success'] rubric_feedback = rubric_dict['html'] if not success: + #This is a staff_facing_error error_message = "Could not parse rubric : {0} for location {1}".format(rubric_string, location.url()) log.error(error_message) raise RubricParsingError(error_message) @@ -90,12 +92,14 @@ class CombinedOpenEndedRubric(object): for category in rubric_categories: total = total + len(category['options']) - 1 if len(category['options']) > (max_score_allowed + 1): + #This is a staff_facing_error error_message = "Number of score points in rubric {0} higher than the max allowed, which is {1}".format( len(category['options']), max_score_allowed) log.error(error_message) raise RubricParsingError(error_message) if total != max_score: + #This is a staff_facing_error error_msg = "The max score {0} for problem {1} does not match the total number of points in the rubric {2}".format( max_score, location, total) log.error(error_msg) @@ -118,6 +122,7 @@ class CombinedOpenEndedRubric(object): categories = [] for category in element: if category.tag != 'category': + #This is a staff_facing_error raise RubricParsingError("[extract_categories] Expected a tag: got {0} instead".format(category.tag)) else: categories.append(self.extract_category(category)) @@ -144,11 +149,13 @@ class CombinedOpenEndedRubric(object): self.has_score = True # if we are missing the score tag and we are expecting one elif self.has_score: + #This is a staff_facing_error raise RubricParsingError("[extract_category] Category {0} is missing a score".format(descriptionxml.text)) # parse description if descriptionxml.tag != 'description': + #This is a staff_facing_error raise RubricParsingError("[extract_category]: expected description tag, got {0} instead".format(descriptionxml.tag)) description = descriptionxml.text @@ -159,6 +166,7 @@ class CombinedOpenEndedRubric(object): # parse options for option in optionsxml: if option.tag != 'option': + #This is a staff_facing_error raise RubricParsingError("[extract_category]: expected option tag, got {0} instead".format(option.tag)) else: pointstr = option.get("points") @@ -168,6 +176,7 @@ class CombinedOpenEndedRubric(object): try: points = int(pointstr) except ValueError: + #This is a staff_facing_error raise RubricParsingError("[extract_category]: expected points to have int, got {0} instead".format(pointstr)) elif autonumbering: # use the generated one if we're in the right mode @@ -218,12 +227,14 @@ class CombinedOpenEndedRubric(object): Validates a set of options. This can and should be extended to filter out other bad edge cases ''' if len(options) == 0: + #This is a staff_facing_error raise RubricParsingError("[extract_category]: no options associated with this category") if len(options) == 1: return prev = options[0]['points'] for option in options[1:]: if prev == option['points']: + #This is a staff_facing_error raise RubricParsingError("[extract_category]: found duplicate point values between two different options") else: prev = option['points'] @@ -232,11 +243,14 @@ class CombinedOpenEndedRubric(object): def reformat_scores_for_rendering(scores, score_types, feedback_types): success = False if len(scores)==0: - log.error("Score length is 0.") + #This is a dev_facing_error + log.error("Score length is 0 when trying to reformat rubric scores for rendering.") return success, "" if len(scores) != len(score_types) or len(feedback_types) != len(scores): - log.error("Length mismatches.") + #This is a dev_facing_error + log.error("Length mismatches when trying to reformat rubric scores for rendering. " + "Scores: {0}, Score Types: {1} Feedback Types: {2}".format(scores, score_types, feedback_types)) return success, "" score_lists = [] diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py index 6bd7a6fd0e..8a4caa1291 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py @@ -51,6 +51,8 @@ class GradingService(object): r = self._try_with_login(op) except (RequestException, ConnectionError, HTTPError) as err: # reraise as promised GradingServiceError, but preserve stacktrace. + #This is a dev_facing_error + log.error("Problem posting data to the grading controller. URL: {0}, data: {1}".format(url, data)) raise GradingServiceError, str(err), sys.exc_info()[2] return r.text @@ -67,6 +69,8 @@ class GradingService(object): r = self._try_with_login(op) except (RequestException, ConnectionError, HTTPError) as err: # reraise as promised GradingServiceError, but preserve stacktrace. + #This is a dev_facing_error + log.error("Problem getting data from the grading controller. URL: {0}, params: {1}".format(url, params)) raise GradingServiceError, str(err), sys.exc_info()[2] return r.text @@ -119,11 +123,13 @@ class GradingService(object): return response_json # if we can't parse the rubric into HTML, except etree.XMLSyntaxError, RubricParsingError: + #This is a dev_facing_error log.exception("Cannot parse rubric string. Raw string: {0}" .format(rubric)) return {'success': False, 'error': 'Error displaying submission'} except ValueError: + #This is a dev_facing_error log.exception("Error parsing response: {0}".format(response)) return {'success': False, 'error': "Error displaying submission"} From 26713e072f1221bf521feddc07f74ddd0884472f Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 16:30:34 -0500 Subject: [PATCH 13/22] Work on fixing and documenting error messages shown to students --- .../open_ended_image_submission.py | 5 +- .../open_ended_module.py | 46 +++++++++++++------ .../openendedchild.py | 21 ++++++--- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_image_submission.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_image_submission.py index 88921c1429..edae69854f 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_image_submission.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_image_submission.py @@ -251,8 +251,9 @@ def upload_to_s3(file_to_upload, keyname, s3_interface): return True, public_url except: - error_message = "Could not connect to S3." - log.exception(error_message) + #This is a dev_facing_error + error_message = "Could not connect to S3 to upload peer grading image. Trying to utilize bucket: {0}".format(bucketname.lower()) + log.error(error_message) return False, error_message diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index 2472244950..a018e9c6a5 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -59,12 +59,14 @@ class OpenEndedModule(openendedchild.OpenEndedChild): self.submission_id = None self.grader_id = None + error_message = "No {0} found in problem xml for open ended problem." if oeparam is None: - raise ValueError("No oeparam found in problem xml.") + #This is a staff_facing_error + raise ValueError(error_message.format('oeparam')) if self.prompt is None: - raise ValueError("No prompt found in problem xml.") + raise ValueError(error_message.format('prompt')) if self.rubric is None: - raise ValueError("No rubric found in problem xml.") + raise ValueError(error_message.format('rubric')) self._parse(oeparam, self.prompt, self.rubric, system) @@ -99,7 +101,8 @@ class OpenEndedModule(openendedchild.OpenEndedChild): # __init__ adds it (easiest way to get problem location into # response types) except TypeError, ValueError: - log.exception("Grader payload %r is not a json object!", grader_payload) + #This is a dev_facing_error + log.exception("Grader payload from external open ended grading server is not a json object! Object: {0}".format(grader_payload)) self.initial_display = find_with_default(oeparam, 'initial_display', '') self.answer = find_with_default(oeparam, 'answer_display', 'No answer given.') @@ -142,17 +145,20 @@ class OpenEndedModule(openendedchild.OpenEndedChild): survey_responses = event_info['survey_responses'] for tag in ['feedback', 'submission_id', 'grader_id', 'score']: if tag not in survey_responses: - return {'success': False, 'msg': "Could not find needed tag {0}".format(tag)} + #This is a student_facing_error + return {'success': False, 'msg': "Could not find needed tag {0} in the survey responses. Please try submitting again.".format(tag)} try: submission_id = int(survey_responses['submission_id']) grader_id = int(survey_responses['grader_id']) feedback = str(survey_responses['feedback'].encode('ascii', 'ignore')) score = int(survey_responses['score']) except: + #This is a dev_facing_error error_message = ("Could not parse submission id, grader id, " "or feedback from message_post ajax call. Here is the message data: {0}".format( survey_responses)) log.exception(error_message) + #This is a student_facing_error return {'success': False, 'msg': "There was an error saving your feedback. Please contact course staff."} qinterface = system.xqueue['interface'] @@ -189,6 +195,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): self.state = self.DONE + #This is a student_facing_message return {'success': success, 'msg': "Successfully submitted your feedback."} def send_to_grader(self, submission, system): @@ -338,18 +345,22 @@ class OpenEndedModule(openendedchild.OpenEndedChild): for tag in ['success', 'feedback', 'submission_id', 'grader_id']: if tag not in response_items: - return format_feedback('errors', 'Error getting feedback') + #This is a student_facing_error + return format_feedback('errors', 'Error getting feedback from grader.') feedback_items = response_items['feedback'] try: feedback = json.loads(feedback_items) except (TypeError, ValueError): - log.exception("feedback_items have invalid json %r", feedback_items) - return format_feedback('errors', 'Could not parse feedback') + #This is a dev_facing_error + log.exception("feedback_items from external open ended grader have invalid json {0}".format(feedback_items)) + #This is a student_facing_error + return format_feedback('errors', 'Error getting feedback from grader.') if response_items['success']: if len(feedback) == 0: - return format_feedback('errors', 'No feedback available') + #This is a student_facing_error + return format_feedback('errors', 'No feedback available from grader.') for tag in do_not_render: if tag in feedback: @@ -358,6 +369,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): feedback_lst = sorted(feedback.items(), key=get_priority) feedback_list_part1 = u"\n".join(format_feedback(k, v) for k, v in feedback_lst) else: + #This is a student_facing_error feedback_list_part1 = format_feedback('errors', response_items['feedback']) feedback_list_part2 = (u"\n".join([format_feedback_hidden(feedback_type, value) @@ -433,14 +445,16 @@ class OpenEndedModule(openendedchild.OpenEndedChild): try: score_result = json.loads(score_msg) except (TypeError, ValueError): - error_message = ("External grader message should be a JSON-serialized dict." + #This is a dev_facing_error + error_message = ("External open ended grader message should be a JSON-serialized dict." " Received score_msg = {0}".format(score_msg)) log.error(error_message) fail['feedback'] = error_message return fail if not isinstance(score_result, dict): - error_message = ("External grader message should be a JSON-serialized dict." + #This is a dev_facing_error + error_message = ("External open ended grader message should be a JSON-serialized dict." " Received score_result = {0}".format(score_result)) log.error(error_message) fail['feedback'] = error_message @@ -448,7 +462,8 @@ class OpenEndedModule(openendedchild.OpenEndedChild): for tag in ['score', 'feedback', 'grader_type', 'success', 'grader_id', 'submission_id']: if tag not in score_result: - error_message = ("External grader message is missing required tag: {0}" + #This is a dev_facing_error + error_message = ("External open ended grader message is missing required tag: {0}" .format(tag)) log.error(error_message) fail['feedback'] = error_message @@ -565,7 +580,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild): } if dispatch not in handlers: - return 'Error' + #This is a dev_facing_error + log.error("Cannot find {0} in handlers in handle_ajax function for open_ended_module.py".format(dispatch)) + #This is a dev_facing_error + return json.dumps({'error': 'Error handling action. Please try again.', 'success' : False}) before = self.get_progress() d = handlers[dispatch](get, system) @@ -616,6 +634,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): #Error message already defined success = False else: + #This is a student_facing_error error_message = "There was a problem saving the image in your submission. Please try a different image, or try pasting a link to an image into the answer box." return { @@ -696,6 +715,7 @@ class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor): """ for child in ['openendedparam']: if len(xml_object.xpath(child)) != 1: + #This is a staff_facing_error raise ValueError("Open Ended definition must include exactly one '{0}' tag".format(child)) def parse(k): diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py index 567c882189..69697abcc9 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py @@ -130,12 +130,14 @@ class OpenEndedChild(object): if self.closed(): return True, { 'success': False, - 'error': 'This problem is now closed.' + #This is a student_facing_error + 'error': 'The problem close date has passed, and this problem is now closed.' } elif self.attempts > self.max_attempts: return True, { 'success': False, - 'error': 'Too many attempts.' + #This is a student_facing_error + 'error': 'You have attempted this problem {0} times. You are allowed {1} attempts.'.format(self.attempts, self.max_attempts) } else: return False, {} @@ -254,7 +256,8 @@ class OpenEndedChild(object): try: return Progress(self.get_score()['score'], self._max_score) except Exception as err: - log.exception("Got bad progress") + #This is a dev_facing_error + log.exception("Got bad progress from open ended child module. Max Score: {1}".format(self._max_score)) return None return None @@ -262,10 +265,12 @@ class OpenEndedChild(object): """ return dict out-of-sync error message, and also log. """ - log.warning("Assessment module state out sync. state: %r, get: %r. %s", + #This is a dev_facing_error + log.warning("Open ended child state out sync. state: %r, get: %r. %s", self.state, get, msg) + #This is a student_facing_error return {'success': False, - 'error': 'The problem state got out-of-sync'} + 'error': 'The problem state got out-of-sync. Please try reloading the page.'} def get_html(self): """ @@ -418,6 +423,7 @@ class OpenEndedChild(object): success = False allowed_to_submit = True response = {} + #This is a student_facing_error error_string = ("You need to peer grade {0} more in order to make another submission. " "You have graded {1}, and {2} are required. You have made {3} successful peer grading submissions.") try: @@ -427,7 +433,10 @@ class OpenEndedChild(object): student_sub_count = response['student_sub_count'] success = True except: - error_message = "Could not contact the grading controller." + #This is a dev_facing_error + log.error("Could not contact external open ended graders for location {0} and student {1}".format(location,student_id)) + #This is a student_facing_error + error_message = "Could not contact the graders. Please notify course staff." return success, allowed_to_submit, error_message if count_graded>=count_required: return success, allowed_to_submit, "" From 0627b7b9f262fef3ae100279299d50150d0a117c Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 16:38:33 -0500 Subject: [PATCH 14/22] Fix error messages, document --- .../open_ended_grading_classes/openendedchild.py | 1 + .../self_assessment_module.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py index 69697abcc9..dd8fa2a54e 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py @@ -442,6 +442,7 @@ class OpenEndedChild(object): return success, allowed_to_submit, "" else: allowed_to_submit = False + #This is a student_facing_error error_message = error_string.format(count_required-count_graded, count_graded, count_required, student_sub_count) return success, allowed_to_submit, error_message diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py index f9fd3e301d..9e1c06323e 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py @@ -123,7 +123,8 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): elif self.state in (self.POST_ASSESSMENT, self.DONE): context['read_only'] = True else: - raise ValueError("Illegal state '%r'" % self.state) + #This is a dev_facing_error + raise ValueError("Self assessment module is in an illegal state '{0}'".format(self.state)) return system.render_template('self_assessment_rubric.html', context) @@ -148,7 +149,8 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): elif self.state == self.DONE: context['read_only'] = True else: - raise ValueError("Illegal state '%r'" % self.state) + #This is a dev_facing_error + raise ValueError("Self Assessment module is in an illegal state '{0}'".format(self.state)) return system.render_template('self_assessment_hint.html', context) @@ -186,9 +188,9 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): #Error message already defined success = False else: + #This is a student_facing_error error_message = "There was a problem saving the image in your submission. Please try a different image, or try pasting a link to an image into the answer box." - log.debug(error_message) return { 'success': success, 'rubric_html': self.get_rubric_html(system), @@ -220,7 +222,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): for i in xrange(0,len(score_list)): score_list[i] = int(score_list[i]) except ValueError: - return {'success': False, 'error': "Non-integer score value, or no score list"} + #This is a dev_facing_error + log.error("Non-integer score value passed to save_assessment , or no score list present.") + #This is a student_facing_error + return {'success': False, 'error': "Error saving your score. Please notify course staff."} #Record score as assessment and rubric scores as post assessment self.record_latest_score(score) @@ -262,6 +267,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): try: rubric_scores = json.loads(latest_post_assessment) except: + #This is a dev_facing_error log.error("Cannot parse rubric scores in self assessment module from {0}".format(latest_post_assessment)) rubric_scores = [] return [rubric_scores] @@ -293,6 +299,7 @@ class SelfAssessmentDescriptor(XmlDescriptor, EditingDescriptor): expected_children = [] for child in expected_children: if len(xml_object.xpath(child)) != 1: + #This is a staff_facing_error raise ValueError("Self assessment definition must include exactly one '{0}' tag".format(child)) def parse(k): From 1cc5e615071b3a3f8fd7ab8ac978a75df6d98876 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 16:52:09 -0500 Subject: [PATCH 15/22] More fixes on error messages --- .../xmodule/combined_open_ended_module.py | 7 ++- .../js/src/combinedopenended/display.coffee | 14 +++--- .../self_assessment_module.py | 7 ++- .../xmodule/xmodule/peer_grading_module.py | 47 ++++++++++++++----- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index f7b9b6a026..fcf4a13fd6 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -108,11 +108,13 @@ class CombinedOpenEndedModule(XModule): instance_state = {} self.version = self.metadata.get('version', DEFAULT_VERSION) + version_error_string = "Version of combined open ended module {0} is not correct. Going with version {1}" if not isinstance(self.version, basestring): try: self.version = str(self.version) except: - log.error("Version {0} is not correct. Going with version {1}".format(self.version, DEFAULT_VERSION)) + #This is a dev_facing_error + log.info(version_error_string.format(self.version, DEFAULT_VERSION)) self.version = DEFAULT_VERSION versions = [i[0] for i in VERSION_TUPLES] @@ -122,7 +124,8 @@ class CombinedOpenEndedModule(XModule): try: version_index = versions.index(self.version) except: - log.error("Version {0} is not correct. Going with version {1}".format(self.version, DEFAULT_VERSION)) + #This is a dev_facing_error + log.error(version_error_string.format(self.version, DEFAULT_VERSION)) self.version = DEFAULT_VERSION version_index = versions.index(self.version) diff --git a/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee b/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee index 1b14d67016..2bae88f962 100644 --- a/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee @@ -89,6 +89,8 @@ class @CombinedOpenEnded @can_upload_files = false @open_ended_child= @$('.open-ended-child') + @out_of_sync_message = 'The problem state got out of sync. Try reloading the page.' + if @task_number>1 @prompt_hide() else if @task_number==1 and @child_state!='initial' @@ -293,7 +295,7 @@ class @CombinedOpenEnded $.ajaxWithPrefix("#{@ajax_url}/save_answer",settings) else - @errors_area.html('Problem state got out of sync. Try reloading the page.') + @errors_area.html(@out_of_sync_message) save_assessment: (event) => event.preventDefault() @@ -315,7 +317,7 @@ class @CombinedOpenEnded else @errors_area.html(response.error) else - @errors_area.html('Problem state got out of sync. Try reloading the page.') + @errors_area.html(@out_of_sync_message) save_hint: (event) => event.preventDefault() @@ -330,7 +332,7 @@ class @CombinedOpenEnded else @errors_area.html(response.error) else - @errors_area.html('Problem state got out of sync. Try reloading the page.') + @errors_area.html(@out_of_sync_message) skip_post_assessment: => if @child_state == 'post_assessment' @@ -342,7 +344,7 @@ class @CombinedOpenEnded else @errors_area.html(response.error) else - @errors_area.html('Problem state got out of sync. Try reloading the page.') + @errors_area.html(@out_of_sync_message) reset: (event) => event.preventDefault() @@ -362,7 +364,7 @@ class @CombinedOpenEnded else @errors_area.html(response.error) else - @errors_area.html('Problem state got out of sync. Try reloading the page.') + @errors_area.html(@out_of_sync_message) next_problem: => if @child_state == 'done' @@ -385,7 +387,7 @@ class @CombinedOpenEnded else @errors_area.html(response.error) else - @errors_area.html('Problem state got out of sync. Try reloading the page.') + @errors_area.html(@out_of_sync_message) gentle_alert: (msg) => if @el.find('.open-ended-alert').length diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py index 9e1c06323e..6a18011d85 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py @@ -90,7 +90,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): } if dispatch not in handlers: - return 'Error' + #This is a dev_facing_error + log.error("Cannot find {0} in handlers in handle_ajax function for open_ended_module.py".format(dispatch)) + #This is a dev_facing_error + return json.dumps({'error': 'Error handling action. Please try again.', 'success' : False}) before = self.get_progress() d = handlers[dispatch](get, system) @@ -223,7 +226,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): score_list[i] = int(score_list[i]) except ValueError: #This is a dev_facing_error - log.error("Non-integer score value passed to save_assessment , or no score list present.") + log.error("Non-integer score value passed to save_assessment ,or no score list present.") #This is a student_facing_error return {'success': False, 'error': "Error saving your score. Please notify course staff."} diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index ba22e8d80c..d4f3e71ffa 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -21,6 +21,8 @@ TRUE_DICT = [True, "True", "true", "TRUE"] MAX_SCORE = 1 IS_GRADED = True +EXTERNAL_GRADER_NO_CONTACT_ERROR = "Failed to contact external graders. Please notify course staff." + class PeerGradingModule(XModule): _VERSION = 1 @@ -112,7 +114,10 @@ class PeerGradingModule(XModule): } if dispatch not in handlers: - return 'Error' + #This is a dev_facing_error + log.error("Cannot find {0} in handlers in handle_ajax function for open_ended_module.py".format(dispatch)) + #This is a dev_facing_error + return json.dumps({'error': 'Error handling action. Please try again.', 'success' : False}) d = handlers[dispatch](get) @@ -130,6 +135,7 @@ class PeerGradingModule(XModule): count_required = response['count_required'] success = True except GradingServiceError: + #This is a dev_facing_error log.exception("Error getting location data from controller for location {0}, student {1}" .format(location, student_id)) @@ -205,10 +211,12 @@ class PeerGradingModule(XModule): response = self.peer_gs.get_next_submission(location, grader_id) return response except GradingServiceError: + #This is a dev_facing_error log.exception("Error getting next submission. server url: {0} location: {1}, grader_id: {2}" .format(self.peer_gs.url, location, grader_id)) + #This is a student_facing_error return {'success': False, - 'error': 'Could not connect to grading service'} + 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR} def save_grade(self, get): """ @@ -245,14 +253,16 @@ class PeerGradingModule(XModule): score, feedback, submission_key, rubric_scores, submission_flagged) return response except GradingServiceError: - log.exception("""Error saving grade. server url: {0}, location: {1}, submission_id:{2}, + #This is a dev_facing_error + log.exception("""Error saving grade to open ended grading service. server url: {0}, location: {1}, submission_id:{2}, submission_key: {3}, score: {4}""" .format(self.peer_gs.url, location, submission_id, submission_key, score) ) + #This is a student_facing_error return { 'success': False, - 'error': 'Could not connect to grading service' + 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR } def is_student_calibrated(self, get): @@ -285,11 +295,13 @@ class PeerGradingModule(XModule): response = self.peer_gs.is_student_calibrated(location, grader_id) return response except GradingServiceError: - log.exception("Error from grading service. server url: {0}, grader_id: {0}, location: {1}" + #This is a dev_facing_error + log.exception("Error from open ended grading service. server url: {0}, grader_id: {0}, location: {1}" .format(self.peer_gs.url, grader_id, location)) + #This is a student_facing_error return { 'success': False, - 'error': 'Could not connect to grading service' + 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR } def show_calibration_essay(self, get): @@ -328,16 +340,20 @@ class PeerGradingModule(XModule): response = self.peer_gs.show_calibration_essay(location, grader_id) return response except GradingServiceError: - log.exception("Error from grading service. server url: {0}, location: {0}" + #This is a dev_facing_error + log.exception("Error from open ended grading service. server url: {0}, location: {0}" .format(self.peer_gs.url, location)) + #This is a student_facing_error return {'success': False, - 'error': 'Could not connect to grading service'} + 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR} # if we can't parse the rubric into HTML, except etree.XMLSyntaxError: + #This is a dev_facing_error log.exception("Cannot parse rubric string. Raw string: {0}" .format(rubric)) + #This is a student_facing_error return {'success': False, - 'error': 'Error displaying submission'} + 'error': 'Error displaying submission. Please notify course staff.'} def save_calibration_essay(self, get): @@ -376,8 +392,10 @@ class PeerGradingModule(XModule): submission_key, score, feedback, rubric_scores) return response except GradingServiceError: + #This is a dev_facing_error log.exception("Error saving calibration grade, location: {0}, submission_id: {1}, submission_key: {2}, grader_id: {3}".format(location, submission_id, submission_key, grader_id)) - return self._err_response('Could not connect to grading service') + #This is a student_facing_error + return self._err_response('') def peer_grading(self, get=None): ''' @@ -398,11 +416,13 @@ class PeerGradingModule(XModule): problem_list = problem_list_dict['problem_list'] except GradingServiceError: - error_text = "Error occured while contacting the grading service" + #This is a student_facing_error + error_text = EXTERNAL_GRADER_NO_CONTACT_ERROR success = False # catch error if if the json loads fails except ValueError: - error_text = "Could not get problem list" + #This is a student_facing_error + error_text = "Could not get list of problems to peer grade. Please notify course staff." success = False ajax_url = self.ajax_url @@ -426,6 +446,8 @@ class PeerGradingModule(XModule): if get == None or get.get('location') == None: if not self.use_for_single_location: #This is an error case, because it must be set to use a single location to be called without get parameters + #This is a dev_facing_error + log.error("Peer grading problem in peer_grading_module called with no get parameters, but use_for_single_location is False.") return {'html': "", 'success': False} problem_location = self.link_to_location @@ -490,6 +512,7 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor): expected_children = [] for child in expected_children: if len(xml_object.xpath(child)) == 0: + #This is a staff_facing_error raise ValueError("Peer grading definition must include at least one '{0}' tag".format(child)) def parse_task(k): From 8b12f26626669fe39f91129e4c3eae3689ec6658 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 18:24:33 -0500 Subject: [PATCH 16/22] Fix error messages to staff --- .../staff_grading_service.py | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lms/djangoapps/open_ended_grading/staff_grading_service.py b/lms/djangoapps/open_ended_grading/staff_grading_service.py index 5bde7a2bc8..0c916b2ef8 100644 --- a/lms/djangoapps/open_ended_grading/staff_grading_service.py +++ b/lms/djangoapps/open_ended_grading/staff_grading_service.py @@ -18,6 +18,7 @@ from mitxmako.shortcuts import render_to_string log = logging.getLogger(__name__) +STAFF_ERROR_MESSAGE = 'Could not contact the external grading server. Please contact the development team. If you do not have a point of contact, you can contact Vik at vik@edx.org.' class MockStaffGradingService(object): """ @@ -253,10 +254,12 @@ def get_problem_list(request, course_id): return HttpResponse(response, mimetype="application/json") except GradingServiceError: - log.exception("Error from grading service. server url: {0}" + #This is a dev_facing_error + log.exception("Error from staff grading service in open ended grading. server url: {0}" .format(staff_grading_service().url)) + #This is a staff_facing_error return HttpResponse(json.dumps({'success': False, - 'error': 'Could not connect to grading service'})) + 'error': STAFF_ERROR_MESSAGE})) def _get_next(course_id, grader_id, location): @@ -266,10 +269,12 @@ def _get_next(course_id, grader_id, location): try: return staff_grading_service().get_next(course_id, location, grader_id) except GradingServiceError: - log.exception("Error from grading service. server url: {0}" + #This is a dev facing error + log.exception("Error from staff grading service in open ended grading. server url: {0}" .format(staff_grading_service().url)) + #This is a staff_facing_error return json.dumps({'success': False, - 'error': 'Could not connect to grading service'}) + 'error': STAFF_ERROR_MESSAGE}) @expect_json @@ -314,18 +319,23 @@ def save_grade(request, course_id): skipped, p.getlist('rubric_scores[]')) except GradingServiceError: - log.exception("Error saving grade") - return _err_response('Could not connect to grading service') + #This is a dev_facing_error + log.exception("Error saving grade in the staff grading interface in open ended grading. Request: {0} Course ID: {1}".format(request, course_id)) + #This is a staff_facing_error + return _err_response(STAFF_ERROR_MESSAGE) try: result = json.loads(result_json) except ValueError: - log.exception("save_grade returned broken json: %s", result_json) - return _err_response('Grading service returned mal-formatted data.') + #This is a dev_facing_error + log.exception("save_grade returned broken json in the staff grading interface in open ended grading: {0}".format(result_json)) + #This is a staff_facing_error + return _err_response(STAFF_ERROR_MESSAGE) if not result.get('success', False): - log.warning('Got success=False from grading service. Response: %s', result_json) - return _err_response('Grading service failed') + #This is a dev_facing_error + log.warning('Got success=False from staff grading service in open ended grading. Response: {0}'.format(result_json)) + return _err_response(STAFF_ERROR_MESSAGE) # Ok, save_grade seemed to work. Get the next submission to grade. return HttpResponse(_get_next(course_id, grader_id, location), From 00bc8deefae19e89971155d73262245efce93e0f Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 18:45:34 -0500 Subject: [PATCH 17/22] Minor work on errors shown within the JS --- lms/static/coffee/src/open_ended/open_ended.coffee | 2 +- lms/static/coffee/src/staff_grading/staff_grading.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lms/static/coffee/src/open_ended/open_ended.coffee b/lms/static/coffee/src/open_ended/open_ended.coffee index cc8bad5473..bfb0fa5931 100644 --- a/lms/static/coffee/src/open_ended/open_ended.coffee +++ b/lms/static/coffee/src/open_ended/open_ended.coffee @@ -41,7 +41,7 @@ class OpenEnded post: (cmd, data, callback) -> # if this post request fails, the error callback will catch it $.post(@ajax_url + cmd, data, callback) - .error => callback({success: false, error: "Error occured while performing this operation"}) + .error => callback({success: false, error: "Error occured while performing javascript ajax post."}) after_action_wrapper: (target, action_type) -> tr_parent = target.parent().parent() diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index e26a96ebed..49fe9d6cda 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -143,7 +143,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t else # TODO: replace with postWithPrefix when that's loaded $.post(@ajax_url + cmd, data, callback) - .error => callback({success: false, error: "Error occured while performing this operation"}) + .error => callback({success: false, error: "Error occured while performing javascript AJAX post."}) class @StaffGrading From 6a10ffc7daf87d49c162f9823c67904d5af1b866 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 19:12:16 -0500 Subject: [PATCH 18/22] Fix up error messages to staff and students --- .../open_ended_notifications.py | 9 ++- lms/djangoapps/open_ended_grading/views.py | 60 ++++++++++++------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/lms/djangoapps/open_ended_grading/open_ended_notifications.py b/lms/djangoapps/open_ended_grading/open_ended_notifications.py index ecec29fce3..c4054895d3 100644 --- a/lms/djangoapps/open_ended_grading/open_ended_notifications.py +++ b/lms/djangoapps/open_ended_grading/open_ended_notifications.py @@ -45,7 +45,8 @@ def staff_grading_notifications(course, user): except: #Non catastrophic error, so no real action notifications = {} - log.info("Problem with getting notifications from staff grading service.") + #This is a dev_facing_error + log.info("Problem with getting notifications from staff grading service for course {0} user {1}.".format(course_id, student_id)) if pending_grading: img_path = "/static/images/grading_notification.png" @@ -78,7 +79,8 @@ def peer_grading_notifications(course, user): except: #Non catastrophic error, so no real action notifications = {} - log.info("Problem with getting notifications from peer grading service.") + #This is a dev_facing_error + log.info("Problem with getting notifications from peer grading service for course {0} user {1}.".format(course_id, student_id)) if pending_grading: img_path = "/static/images/grading_notification.png" @@ -123,7 +125,8 @@ def combined_notifications(course, user): except: #Non catastrophic error, so no real action notifications = {} - log.exception("Problem with getting notifications from controller query service.") + #This is a dev_facing_error + log.exception("Problem with getting notifications from controller query service for course {0} user {1}.".format(course_id, student_id)) if pending_grading: img_path = "/static/images/grading_notification.png" diff --git a/lms/djangoapps/open_ended_grading/views.py b/lms/djangoapps/open_ended_grading/views.py index 80f50b282d..77c1cda6bc 100644 --- a/lms/djangoapps/open_ended_grading/views.py +++ b/lms/djangoapps/open_ended_grading/views.py @@ -60,6 +60,8 @@ ALERT_DICT = { 'Flagged Submissions': "Submissions have been flagged for review" } +STUDENT_ERROR_MESSAGE = "Error occured while contacting the grading service. Please notify course staff." +STAFF_ERROR_MESSAGE = "Error occured while contacting the grading service. Please notify the development team. If you do not have a point of contact, please email Vik at vik@edx.org" @cache_control(no_cache=True, no_store=True, must_revalidate=True) def staff_grading(request, course_id): @@ -96,7 +98,9 @@ def peer_grading(request, course_id): return HttpResponseRedirect(problem_url) except: + #This is a student_facing_error error_message = "Error with initializing peer grading. Centralized module does not exist. Please contact course staff." + #This is a dev_facing_error log.exception(error_message + "Current course is: {0}".format(course_id)) return HttpResponse(error_message) @@ -132,30 +136,34 @@ def student_problem_list(request, course_id): problem_list = [] base_course_url = reverse('courses') - #try: - problem_list_json = controller_qs.get_grading_status_list(course_id, unique_id_for_user(request.user)) - problem_list_dict = json.loads(problem_list_json) - success = problem_list_dict['success'] - if 'error' in problem_list_dict: - error_text = problem_list_dict['error'] - problem_list = [] - else: - problem_list = problem_list_dict['problem_list'] + try: + problem_list_json = controller_qs.get_grading_status_list(course_id, unique_id_for_user(request.user)) + problem_list_dict = json.loads(problem_list_json) + success = problem_list_dict['success'] + if 'error' in problem_list_dict: + error_text = problem_list_dict['error'] + problem_list = [] + else: + problem_list = problem_list_dict['problem_list'] - for i in xrange(0, len(problem_list)): - problem_url_parts = search.path_to_location(modulestore(), course.id, problem_list[i]['location']) - problem_url = generate_problem_url(problem_url_parts, base_course_url) - problem_list[i].update({'actual_url': problem_url}) + for i in xrange(0, len(problem_list)): + problem_url_parts = search.path_to_location(modulestore(), course.id, problem_list[i]['location']) + problem_url = generate_problem_url(problem_url_parts, base_course_url) + problem_list[i].update({'actual_url': problem_url}) - """ except GradingServiceError: - error_text = "Error occured while contacting the grading service" + #This is a student_facing_error + error_text = STUDENT_ERROR_MESSAGE + #This is a dev facing error + log.error("Problem contacting open ended grading service.") success = False # catch error if if the json loads fails except ValueError: - error_text = "Could not get problem list" + #This is a student facing error + error_text = STUDENT_ERROR_MESSAGE + #This is a dev_facing_error + log.error("Problem with results from external grading service for open ended.") success = False - """ ajax_url = _reverse_with_slash('open_ended_problems', course_id) @@ -195,11 +203,17 @@ def flagged_problem_list(request, course_id): problem_list = problem_list_dict['flagged_submissions'] except GradingServiceError: - error_text = "Error occured while contacting the grading service" + #This is a staff_facing_error + error_text = STAFF_ERROR_MESSAGE + #This is a dev_facing_error + log.error("Could not get flagged problem list from external grading service for open ended.") success = False # catch error if if the json loads fails except ValueError: - error_text = "Could not get problem list" + #This is a staff_facing_error + error_text = STAFF_ERROR_MESSAGE + #This is a dev_facing_error + log.error("Could not parse problem list from external grading service response.") success = False ajax_url = _reverse_with_slash('open_ended_flagged_problems', course_id) @@ -283,7 +297,8 @@ def take_action_on_flags(request, course_id): required = ['submission_id', 'action_type', 'student_id'] for key in required: if key not in request.POST: - return HttpResponse(json.dumps({'success': False, 'error': 'Missing key {0}'.format(key)}), + #This is a staff_facing_error + return HttpResponse(json.dumps({'success': False, 'error': STAFF_ERROR_MESSAGE + 'Missing key {0} from submission. Please reload and try again.'.format(key)}), mimetype="application/json") p = request.POST @@ -297,5 +312,6 @@ def take_action_on_flags(request, course_id): response = controller_qs.take_action_on_flags(course_id, student_id, submission_id, action_type) return HttpResponse(response, mimetype="application/json") except GradingServiceError: - log.exception("Error saving calibration grade, submission_id: {0}, submission_key: {1}, grader_id: {2}".format(submission_id, submission_key, grader_id)) - return _err_response('Could not connect to grading service') + #This is a dev_facing_error + log.exception("Error taking action on flagged peer grading submissions, submission_id: {0}, action_type: {1}, grader_id: {2}".format(submission_id, action_type, grader_id)) + return _err_response(STAFF_ERROR_MESSAGE) From 11076e759ee79d4233a9b311fcaee136d2cbee6b Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 13 Feb 2013 19:28:30 -0500 Subject: [PATCH 19/22] Add who to contact for all staff facing messages --- .../combined_open_ended_modulev1.py | 6 ++--- .../combined_open_ended_rubric.py | 22 +++++++++---------- .../open_ended_module.py | 4 ++-- .../self_assessment_module.py | 2 +- .../xmodule/xmodule/peer_grading_module.py | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index 40560f59f3..47461639e0 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -158,7 +158,7 @@ class CombinedOpenEndedV1Module(): self.display_due_date = dateutil.parser.parse(display_due_date_string) except ValueError: #This is a staff_facing_error - log.error("Could not parse due date {0} for location {1}".format(display_due_date_string, location)) + log.error("Could not parse due date {0} for location {1}. Contact the learning sciences group for assistance.".format(display_due_date_string, location)) raise else: self.display_due_date = None @@ -170,7 +170,7 @@ class CombinedOpenEndedV1Module(): self.close_date = self.display_due_date + self.grace_period except: #This is a staff_facing_error - log.error("Error parsing the grace period {0} for location {1}".format(grace_period_string, location)) + log.error("Error parsing the grace period {0} for location {1}. Contact the learning sciences group for assistance.".format(grace_period_string, location)) raise else: self.grace_period = None @@ -807,7 +807,7 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor): for child in expected_children: if len(xml_object.xpath(child)) == 0: #This is a staff_facing_error - raise ValueError("Combined Open Ended definition must include at least one '{0}' tag".format(child)) + raise ValueError("Combined Open Ended definition must include at least one '{0}' tag. Contact the learning sciences group for assistance.".format(child)) def parse_task(k): """Assumes that xml_object has child k""" diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py index 483b21fddc..a6b23caa37 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py @@ -72,7 +72,7 @@ class CombinedOpenEndedRubric(object): success = True except: #This is a staff_facing_error - error_message = "[render_rubric] Could not parse the rubric with xml: {0}".format(rubric_xml) + error_message = "[render_rubric] Could not parse the rubric with xml: {0}. Contact the learning sciences group for assistance.".format(rubric_xml) log.exception(error_message) raise RubricParsingError(error_message) return {'success' : success, 'html' : html, 'rubric_scores' : rubric_scores} @@ -83,7 +83,7 @@ class CombinedOpenEndedRubric(object): rubric_feedback = rubric_dict['html'] if not success: #This is a staff_facing_error - error_message = "Could not parse rubric : {0} for location {1}".format(rubric_string, location.url()) + error_message = "Could not parse rubric : {0} for location {1}. Contact the learning sciences group for assistance.".format(rubric_string, location.url()) log.error(error_message) raise RubricParsingError(error_message) @@ -93,14 +93,14 @@ class CombinedOpenEndedRubric(object): total = total + len(category['options']) - 1 if len(category['options']) > (max_score_allowed + 1): #This is a staff_facing_error - error_message = "Number of score points in rubric {0} higher than the max allowed, which is {1}".format( + error_message = "Number of score points in rubric {0} higher than the max allowed, which is {1}. Contact the learning sciences group for assistance.".format( len(category['options']), max_score_allowed) log.error(error_message) raise RubricParsingError(error_message) if total != max_score: #This is a staff_facing_error - error_msg = "The max score {0} for problem {1} does not match the total number of points in the rubric {2}".format( + error_msg = "The max score {0} for problem {1} does not match the total number of points in the rubric {2}. Contact the learning sciences group for assistance.".format( max_score, location, total) log.error(error_msg) raise RubricParsingError(error_msg) @@ -123,7 +123,7 @@ class CombinedOpenEndedRubric(object): for category in element: if category.tag != 'category': #This is a staff_facing_error - raise RubricParsingError("[extract_categories] Expected a tag: got {0} instead".format(category.tag)) + raise RubricParsingError("[extract_categories] Expected a tag: got {0} instead. Contact the learning sciences group for assistance.".format(category.tag)) else: categories.append(self.extract_category(category)) return categories @@ -150,13 +150,13 @@ class CombinedOpenEndedRubric(object): # if we are missing the score tag and we are expecting one elif self.has_score: #This is a staff_facing_error - raise RubricParsingError("[extract_category] Category {0} is missing a score".format(descriptionxml.text)) + raise RubricParsingError("[extract_category] Category {0} is missing a score. Contact the learning sciences group for assistance.".format(descriptionxml.text)) # parse description if descriptionxml.tag != 'description': #This is a staff_facing_error - raise RubricParsingError("[extract_category]: expected description tag, got {0} instead".format(descriptionxml.tag)) + raise RubricParsingError("[extract_category]: expected description tag, got {0} instead. Contact the learning sciences group for assistance.".format(descriptionxml.tag)) description = descriptionxml.text @@ -167,7 +167,7 @@ class CombinedOpenEndedRubric(object): for option in optionsxml: if option.tag != 'option': #This is a staff_facing_error - raise RubricParsingError("[extract_category]: expected option tag, got {0} instead".format(option.tag)) + raise RubricParsingError("[extract_category]: expected option tag, got {0} instead. Contact the learning sciences group for assistance.".format(option.tag)) else: pointstr = option.get("points") if pointstr: @@ -177,7 +177,7 @@ class CombinedOpenEndedRubric(object): points = int(pointstr) except ValueError: #This is a staff_facing_error - raise RubricParsingError("[extract_category]: expected points to have int, got {0} instead".format(pointstr)) + raise RubricParsingError("[extract_category]: expected points to have int, got {0} instead. Contact the learning sciences group for assistance.".format(pointstr)) elif autonumbering: # use the generated one if we're in the right mode points = cur_points @@ -228,14 +228,14 @@ class CombinedOpenEndedRubric(object): ''' if len(options) == 0: #This is a staff_facing_error - raise RubricParsingError("[extract_category]: no options associated with this category") + raise RubricParsingError("[extract_category]: no options associated with this category. Contact the learning sciences group for assistance.") if len(options) == 1: return prev = options[0]['points'] for option in options[1:]: if prev == option['points']: #This is a staff_facing_error - raise RubricParsingError("[extract_category]: found duplicate point values between two different options") + raise RubricParsingError("[extract_category]: found duplicate point values between two different options. Contact the learning sciences group for assistance.") else: prev = option['points'] diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index a018e9c6a5..96d75b366c 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -59,7 +59,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): self.submission_id = None self.grader_id = None - error_message = "No {0} found in problem xml for open ended problem." + error_message = "No {0} found in problem xml for open ended problem. Contact the learning sciences group for assistance." if oeparam is None: #This is a staff_facing_error raise ValueError(error_message.format('oeparam')) @@ -716,7 +716,7 @@ class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor): for child in ['openendedparam']: if len(xml_object.xpath(child)) != 1: #This is a staff_facing_error - raise ValueError("Open Ended definition must include exactly one '{0}' tag".format(child)) + raise ValueError("Open Ended definition must include exactly one '{0}' tag. Contact the learning sciences group for assistance.".format(child)) def parse(k): """Assumes that xml_object has child k""" diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py index 6a18011d85..7ecb3c4d5e 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py @@ -303,7 +303,7 @@ class SelfAssessmentDescriptor(XmlDescriptor, EditingDescriptor): for child in expected_children: if len(xml_object.xpath(child)) != 1: #This is a staff_facing_error - raise ValueError("Self assessment definition must include exactly one '{0}' tag".format(child)) + raise ValueError("Self assessment definition must include exactly one '{0}' tag. Contact the learning sciences group for assistance.".format(child)) def parse(k): """Assumes that xml_object has child k""" diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index d4f3e71ffa..a321374c07 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -513,7 +513,7 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor): for child in expected_children: if len(xml_object.xpath(child)) == 0: #This is a staff_facing_error - raise ValueError("Peer grading definition must include at least one '{0}' tag".format(child)) + raise ValueError("Peer grading definition must include at least one '{0}' tag. Contact the learning sciences group for assistance.".format(child)) def parse_task(k): """Assumes that xml_object has child k""" From 44fdc0135a45c4ebb811d777456ac22733e848fb Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 19 Feb 2013 10:43:10 -0500 Subject: [PATCH 20/22] Remove magic 8 ball grading --- .../open_ended_grading_classes/combined_open_ended_rubric.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py index a6b23caa37..2a0233d3cc 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py @@ -4,7 +4,6 @@ from lxml import etree log = logging.getLogger(__name__) GRADER_TYPE_IMAGE_DICT = { - '8B' : '/static/images/random_grading_icon.png', 'SA' : '/static/images/self_assessment_icon.png', 'PE' : '/static/images/peer_grading_icon.png', 'ML' : '/static/images/ml_grading_icon.png', @@ -13,7 +12,6 @@ GRADER_TYPE_IMAGE_DICT = { } HUMAN_GRADER_TYPE = { - '8B' : 'Magic-8-Ball-Assessment', 'SA' : 'Self-Assessment', 'PE' : 'Peer-Assessment', 'IN' : 'Instructor-Assessment', From dbe93502f1415fabfbd1b755e665730c911ff28f Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 20 Feb 2013 12:44:12 -0500 Subject: [PATCH 21/22] Fix tests --- common/lib/xmodule/xmodule/tests/__init__.py | 12 +++++++++++- lms/envs/test.py | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/common/lib/xmodule/xmodule/tests/__init__.py b/common/lib/xmodule/xmodule/tests/__init__.py index 04e7ee19b1..9474717cb2 100644 --- a/common/lib/xmodule/xmodule/tests/__init__.py +++ b/common/lib/xmodule/xmodule/tests/__init__.py @@ -19,6 +19,15 @@ import xmodule from xmodule.x_module import ModuleSystem from mock import Mock +open_ended_grading_interface = { + 'url': 'http://sandbox-grader-001.m.edx.org/peer_grading', + 'username': 'incorrect_user', + 'password': 'incorrect_pass', + 'staff_grading' : 'staff_grading', + 'peer_grading' : 'peer_grading', + 'grading_controller' : 'grading_controller' + } + test_system = ModuleSystem( ajax_url='courses/course_id/modx/a_location', track_function=Mock(), @@ -31,7 +40,8 @@ test_system = ModuleSystem( debug=True, xqueue={'interface': None, 'callback_url': '/', 'default_queuename': 'testqueue', 'waittime': 10}, node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"), - anonymous_student_id='student' + anonymous_student_id='student', + open_ended_grading_interface= open_ended_grading_interface ) diff --git a/lms/envs/test.py b/lms/envs/test.py index 6cad6416d0..c1863349fb 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -170,4 +170,4 @@ PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', # 'django.contrib.auth.hashers.CryptPasswordHasher', -) +) \ No newline at end of file From c3d485a609ac48487391b1e676087035d3ddbae6 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Wed, 20 Feb 2013 13:33:17 -0500 Subject: [PATCH 22/22] Fix error message --- common/lib/xmodule/xmodule/peer_grading_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index a321374c07..3f1977f78f 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -395,7 +395,7 @@ class PeerGradingModule(XModule): #This is a dev_facing_error log.exception("Error saving calibration grade, location: {0}, submission_id: {1}, submission_key: {2}, grader_id: {3}".format(location, submission_id, submission_key, grader_id)) #This is a student_facing_error - return self._err_response('') + return self._err_response('There was an error saving your score. Please notify course staff.') def peer_grading(self, get=None): '''