Commit 14d9633f by Diana Huang

Merge branch 'diana/open-ended-ui-updates' into tests/diana/update-oe-unit-tests

parents 7d82b6cb 1d4b674c
...@@ -21,7 +21,7 @@ from .xml_module import XmlDescriptor ...@@ -21,7 +21,7 @@ from .xml_module import XmlDescriptor
from xmodule.modulestore import Location from xmodule.modulestore import Location
import self_assessment_module import self_assessment_module
import open_ended_module import open_ended_module
from combined_open_ended_rubric import CombinedOpenEndedRubric from combined_open_ended_rubric import CombinedOpenEndedRubric, RubricParsingError
from .stringify import stringify_children from .stringify import stringify_children
log = logging.getLogger("mitx.courseware") log = logging.getLogger("mitx.courseware")
...@@ -141,11 +141,11 @@ class CombinedOpenEndedModule(XModule): ...@@ -141,11 +141,11 @@ 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'])) try:
if not success: rubric_feedback = rubric_renderer.render_rubric(stringify_children(definition['rubric']))
error_message="Could not parse rubric : {0}".format(definition['rubric']) except RubricParsingError:
log.exception(error_message) log.error("Failed to parse rubric in location: {1}".format(location))
raise Exception raise
#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,35 +13,25 @@ class CombinedOpenEndedRubric(object): ...@@ -10,35 +13,25 @@ class CombinedOpenEndedRubric(object):
self.view_only = view_only self.view_only = view_only
self.system = system self.system = system
'''
render_rubric: takes in an xml string and outputs the corresponding
html for that xml, given the type of rubric we're generating
Input:
rubric_xml: an string that has not been parsed into xml that
represents this particular rubric
Output:
html: the html that corresponds to the xml given
'''
def render_rubric(self, rubric_xml): def render_rubric(self, rubric_xml):
success = False '''
render_rubric: takes in an xml string and outputs the corresponding
html for that xml, given the type of rubric we're generating
Input:
rubric_xml: an string that has not been parsed into xml that
represents this particular rubric
Output:
html: the html that corresponds to the xml given
'''
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
...@@ -106,26 +106,30 @@ class GradingService(object): ...@@ -106,26 +106,30 @@ class GradingService(object):
def _render_rubric(self, response, view_only=False): def _render_rubric(self, response, view_only=False):
""" """
Given an HTTP Response with the key 'rubric', render out the html Given an HTTP Response with the key 'rubric', render out the html
required to display the rubric required to display the rubric and put it back into the response
returns the updated response as a dictionary that can be serialized later
""" """
try: try:
response_json = json.loads(response) response_json = json.loads(response)
if response_json.has_key('rubric'): if 'rubric' in response_json:
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 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 {'success': False,
'error': 'Error displaying submission'}) 'error': 'Error displaying submission'}
except ValueError:
log.exception("Error parsing response: {0}".format(response))
return {'success': False,
'error': "Error displaying submission"}
...@@ -95,7 +95,7 @@ class PeerGradingService(GradingService): ...@@ -95,7 +95,7 @@ class PeerGradingService(GradingService):
def get_next_submission(self, problem_location, grader_id): def get_next_submission(self, problem_location, grader_id):
response = self.get(self.get_next_submission_url, response = self.get(self.get_next_submission_url,
{'location': problem_location, 'grader_id': grader_id}) {'location': problem_location, 'grader_id': grader_id})
return self._render_rubric(response) return json.dumps(self._render_rubric(response))
def save_grade(self, location, grader_id, submission_id, score, feedback, submission_key, rubric_scores): def save_grade(self, location, grader_id, submission_id, score, feedback, submission_key, rubric_scores):
data = {'grader_id' : grader_id, data = {'grader_id' : grader_id,
...@@ -115,7 +115,7 @@ class PeerGradingService(GradingService): ...@@ -115,7 +115,7 @@ class PeerGradingService(GradingService):
def show_calibration_essay(self, problem_location, grader_id): def show_calibration_essay(self, problem_location, grader_id):
params = {'problem_id' : problem_location, 'student_id': grader_id} params = {'problem_id' : problem_location, 'student_id': grader_id}
response = self.get(self.show_calibration_essay_url, params) response = self.get(self.show_calibration_essay_url, params)
return self._render_rubric(response) return json.dumps(self._render_rubric(response))
def save_calibration_essay(self, problem_location, grader_id, calibration_essay_id, submission_key, def save_calibration_essay(self, problem_location, grader_id, calibration_essay_id, submission_key,
score, feedback, rubric_scores): score, feedback, rubric_scores):
......
...@@ -112,7 +112,7 @@ class StaffGradingService(GradingService): ...@@ -112,7 +112,7 @@ class StaffGradingService(GradingService):
response = self.get(self.get_next_url, response = self.get(self.get_next_url,
params={'location': location, params={'location': location,
'grader_id': grader_id}) 'grader_id': grader_id})
return self._render_rubric(response) return json.dumps(self._render_rubric(response))
def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores): def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores):
...@@ -148,7 +148,6 @@ class StaffGradingService(GradingService): ...@@ -148,7 +148,6 @@ class StaffGradingService(GradingService):
# importing this file doesn't create objects that may not have the right config # importing this file doesn't create objects that may not have the right config
_service = None _service = None
module_system = ModuleSystem("", None, None, render_to_string, None)
def staff_grading_service(): def staff_grading_service():
""" """
......
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