Commit 47c24d8e by Diana Huang

Raise smarter exceptions when parsing rubrics.

parent cc67017d
...@@ -141,11 +141,7 @@ class CombinedOpenEndedModule(XModule): ...@@ -141,11 +141,7 @@ class CombinedOpenEndedModule(XModule):
self._max_score = int(self.metadata.get('max_score', MAX_SCORE)) self._max_score = int(self.metadata.get('max_score', MAX_SCORE))
rubric_renderer = CombinedOpenEndedRubric(system, True) rubric_renderer = CombinedOpenEndedRubric(system, True)
success, rubric_feedback = rubric_renderer.render_rubric(stringify_children(definition['rubric'])) rubric_feedback = rubric_renderer.render_rubric(stringify_children(definition['rubric']))
if not success:
error_message="Could not parse rubric : {0}".format(definition['rubric'])
log.exception(error_message)
raise Exception
#Static data is passed to the child modules to render #Static data is passed to the child modules to render
self.static_data = { self.static_data = {
'max_score': self._max_score, 'max_score': self._max_score,
......
...@@ -3,6 +3,9 @@ from lxml import etree ...@@ -3,6 +3,9 @@ from lxml import etree
log=logging.getLogger(__name__) log=logging.getLogger(__name__)
class RubricParsingError(Exception):
pass
class CombinedOpenEndedRubric(object): class CombinedOpenEndedRubric(object):
def __init__ (self, system, view_only = False): def __init__ (self, system, view_only = False):
...@@ -10,6 +13,7 @@ class CombinedOpenEndedRubric(object): ...@@ -10,6 +13,7 @@ class CombinedOpenEndedRubric(object):
self.view_only = view_only self.view_only = view_only
self.system = system self.system = system
def render_rubric(self, rubric_xml):
''' '''
render_rubric: takes in an xml string and outputs the corresponding render_rubric: takes in an xml string and outputs the corresponding
html for that xml, given the type of rubric we're generating html for that xml, given the type of rubric we're generating
...@@ -19,26 +23,15 @@ class CombinedOpenEndedRubric(object): ...@@ -19,26 +23,15 @@ class CombinedOpenEndedRubric(object):
Output: Output:
html: the html that corresponds to the xml given html: the html that corresponds to the xml given
''' '''
def render_rubric(self, rubric_xml):
success = False
try: try:
rubric_categories = self.extract_categories(rubric_xml) rubric_categories = self.extract_categories(rubric_xml)
html = self.system.render_template('open_ended_rubric.html', html = self.system.render_template('open_ended_rubric.html',
{'categories' : rubric_categories, {'categories' : rubric_categories,
'has_score': self.has_score, 'has_score': self.has_score,
'view_only': self.view_only}) 'view_only': self.view_only})
success = True
except: except:
log.exception("Could not parse the rubric.") raise RubricParsingError("[render_rubric] Could not parse the rubric with xml: {0}".format(rubric_xml))
try: return html
html = etree.tostring(rubric_xml, pretty_print=True)
except:
log.exception("Rubric XML is a string, not an XML object : {0}".format(rubric_xml))
if isinstance(rubric_xml, basestring):
html = rubric_xml
else:
html = "Invalid rubric. Please contact course staff."
return success, html
def extract_categories(self, element): def extract_categories(self, element):
''' '''
...@@ -57,7 +50,7 @@ class CombinedOpenEndedRubric(object): ...@@ -57,7 +50,7 @@ class CombinedOpenEndedRubric(object):
categories = [] categories = []
for category in element: for category in element:
if category.tag != 'category': if category.tag != 'category':
raise Exception("[extract_categories] Expected a <category> tag: got {0} instead".format(category.tag)) raise RubricParsingError("[extract_categories] Expected a <category> tag: got {0} instead".format(category.tag))
else: else:
categories.append(self.extract_category(category)) categories.append(self.extract_category(category))
return categories return categories
...@@ -83,12 +76,12 @@ class CombinedOpenEndedRubric(object): ...@@ -83,12 +76,12 @@ class CombinedOpenEndedRubric(object):
self.has_score = True self.has_score = True
# if we are missing the score tag and we are expecting one # if we are missing the score tag and we are expecting one
elif self.has_score: elif self.has_score:
raise Exception("[extract_category] Category {0} is missing a score".format(descriptionxml.text)) raise RubricParsingError("[extract_category] Category {0} is missing a score".format(descriptionxml.text))
# parse description # parse description
if descriptionxml.tag != 'description': if descriptionxml.tag != 'description':
raise Exception("[extract_category]: expected description tag, got {0} instead".format(descriptionxml.tag)) raise RubricParsingError("[extract_category]: expected description tag, got {0} instead".format(descriptionxml.tag))
description = descriptionxml.text description = descriptionxml.text
...@@ -98,7 +91,7 @@ class CombinedOpenEndedRubric(object): ...@@ -98,7 +91,7 @@ class CombinedOpenEndedRubric(object):
# parse options # parse options
for option in optionsxml: for option in optionsxml:
if option.tag != 'option': if option.tag != 'option':
raise Exception("[extract_category]: expected option tag, got {0} instead".format(option.tag)) raise RubricParsingError("[extract_category]: expected option tag, got {0} instead".format(option.tag))
else: else:
pointstr = option.get("points") pointstr = option.get("points")
if pointstr: if pointstr:
...@@ -107,7 +100,7 @@ class CombinedOpenEndedRubric(object): ...@@ -107,7 +100,7 @@ class CombinedOpenEndedRubric(object):
try: try:
points = int(pointstr) points = int(pointstr)
except ValueError: except ValueError:
raise Exception("[extract_category]: expected points to have int, got {0} instead".format(pointstr)) raise RubricParsingError("[extract_category]: expected points to have int, got {0} instead".format(pointstr))
elif autonumbering: elif autonumbering:
# use the generated one if we're in the right mode # use the generated one if we're in the right mode
points = cur_points points = cur_points
...@@ -132,12 +125,12 @@ class CombinedOpenEndedRubric(object): ...@@ -132,12 +125,12 @@ class CombinedOpenEndedRubric(object):
Validates a set of options. This can and should be extended to filter out other bad edge cases Validates a set of options. This can and should be extended to filter out other bad edge cases
''' '''
if len(options) == 0: if len(options) == 0:
raise Exception("[extract_category]: no options associated with this category") raise RubricParsingError("[extract_category]: no options associated with this category")
if len(options) == 1: if len(options) == 1:
return return
prev = options[0]['points'] prev = options[0]['points']
for option in options[1:]: for option in options[1:]:
if prev == option['points']: if prev == option['points']:
raise Exception("[extract_category]: found duplicate point values between two different options") raise RubricParsingError("[extract_category]: found duplicate point values between two different options")
else: else:
prev = option['points'] prev = option['points']
...@@ -383,7 +383,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -383,7 +383,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
feedback = self._convert_longform_feedback_to_html(response_items) feedback = self._convert_longform_feedback_to_html(response_items)
if response_items['rubric_scores_complete']==True: if response_items['rubric_scores_complete']==True:
rubric_renderer = CombinedOpenEndedRubric(system, True) rubric_renderer = CombinedOpenEndedRubric(system, True)
success, rubric_feedback = rubric_renderer.render_rubric(response_items['rubric_xml']) rubric_feedback = rubric_renderer.render_rubric(response_items['rubric_xml'])
if not response_items['success']: if not response_items['success']:
return system.render_template("open_ended_error.html", return system.render_template("open_ended_error.html",
......
...@@ -123,7 +123,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -123,7 +123,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
return '' return ''
rubric_renderer = CombinedOpenEndedRubric(system, True) rubric_renderer = CombinedOpenEndedRubric(system, True)
success, rubric_html = rubric_renderer.render_rubric(self.rubric) rubric_html = rubric_renderer.render_rubric(self.rubric)
# we'll render it # we'll render it
context = {'rubric': rubric_html, context = {'rubric': rubric_html,
......
...@@ -11,7 +11,7 @@ from django.http import HttpResponse, Http404 ...@@ -11,7 +11,7 @@ from django.http import HttpResponse, Http404
from courseware.access import has_access from courseware.access import has_access
from util.json_request import expect_json from util.json_request import expect_json
from xmodule.course_module import CourseDescriptor from xmodule.course_module import CourseDescriptor
from xmodule.combined_open_ended_rubric import CombinedOpenEndedRubric from xmodule.combined_open_ended_rubric import CombinedOpenEndedRubric, RubricParsingError
from lxml import etree from lxml import etree
from mitxmako.shortcuts import render_to_string from mitxmako.shortcuts import render_to_string
from xmodule.x_module import ModuleSystem from xmodule.x_module import ModuleSystem
...@@ -113,19 +113,20 @@ class GradingService(object): ...@@ -113,19 +113,20 @@ class GradingService(object):
if response_json.has_key('rubric'): if response_json.has_key('rubric'):
rubric = response_json['rubric'] rubric = response_json['rubric']
rubric_renderer = CombinedOpenEndedRubric(self.system, False) rubric_renderer = CombinedOpenEndedRubric(self.system, False)
success, rubric_html = rubric_renderer.render_rubric(rubric) rubric_html = rubric_renderer.render_rubric(rubric)
if not success:
error_message = "Could not render rubric: {0}".format(rubric)
log.exception(error_message)
return json.dumps({'success': False,
'error': error_message})
response_json['rubric'] = rubric_html response_json['rubric'] = rubric_html
return json.dumps(response_json) return json.dumps(response_json)
# if we can't parse the rubric into HTML, # if we can't parse the rubric into HTML,
except etree.XMLSyntaxError: except etree.XMLSyntaxError, RubricParsingError:
log.exception("Cannot parse rubric string. Raw string: {0}" log.exception("Cannot parse rubric string. Raw string: {0}"
.format(rubric)) .format(rubric))
return json.dumps({'success': False, return json.dumps({'success': False,
'error': 'Error displaying submission'}) 'error': 'Error displaying submission'})
except ValueError:
log.exception("Error parsing response: {0}".format(response))
return json.dumps({'success': False,
'error': "Error displaying submission"})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment