diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index b805084ce4..1d3646fefc 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -735,142 +735,3 @@ class ChemicalEquationInput(InputTypeBase): registry.register(ChemicalEquationInput) #----------------------------------------------------------------------------- - -class RubricInput(InputTypeBase): - """ - This is the logic for parsing and displaying a rubric of type - """ - - template = "rubricinput.html" - tags = ['rubric'] - - # pulled out for testing - submitted_msg = ("Feedback not yet available. Reload to check again. " - "Once the problem is graded, this message will be " - "replaced with the grader's feedback.") - has_score = False - - @classmethod - def get_attributes(cls): - """ - Convert options to a convenient format. - """ - return [ - Attribute('height', None), - Attribute('width', None)] - - def _extra_context(self): - """ - Add in the various bits and pieces that we need - """ - return {'categories': self.categories, - 'view_only': False, - 'has_score': self.has_score} - - def setup(self): - # set the categories - self.categories = self.extract_categories(self.xml) - - def extract_categories(self, element): - ''' - Contstruct a list of categories such that the structure looks like: - [ { category: "Category 1 Name", - options: [{text: "Option 1 Name", points: 0}, {text:"Option 2 Name", points: 5}] - }, - { category: "Category 2 Name", - options: [{text: "Option 1 Name", points: 0}, - {text: "Option 2 Name", points: 1}, - {text: "Option 3 Name", points: 2]}] - - ''' - categories = [] - for category in element: - if category.tag != 'category': - raise Exception("[capa.inputtypes.extract_categories] Expected a tag: got {0} instead".format(category.tag)) - else: - categories.append(self.extract_category(category)) - return categories - - - def extract_category(self, category): - ''' - construct an individual category - {category: "Category 1 Name", - options: [{text: "Option 1 text", points: 1}, - {text: "Option 2 text", points: 2}]} - - all sorting and auto-point generation occurs in this function - ''' - descriptionxml = category[0] - optionsxml = category[1:] - scorexml = category[1] - score = None - if scorexml.tag == 'score': - score_text = scorexml.text - optionsxml = category[2:] - score = int(score_text) - self.has_score = True - # if we are missing the score tag and we are expecting one - elif self.has_score: - raise Exception("[inputtypes.extract_category] Category {0} is missing a score".format(descriptionxml.text)) - - - # parse description - if descriptionxml.tag != 'description': - raise Exception("[extract_category]: expected description tag, got {0} instead".format(descriptionxml.tag)) - - description = descriptionxml.text - - cur_points = 0 - options = [] - autonumbering = True - # parse options - for option in optionsxml: - if option.tag != 'option': - raise Exception("[extract_category]: expected option tag, got {0} instead".format(option.tag)) - else: - pointstr = option.get("points") - if pointstr: - autonumbering = False - # try to parse this into an int - try: - points = int(pointstr) - except ValueError: - raise Exception("[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 - points = cur_points - cur_points = cur_points + 1 - else: - raise Exception("[extract_category]: missing points attribute. Cannot continue to auto-create points values after a points value is explicitly dfined.") - - selected = score == points - optiontext = option.text - options.append({'text': option.text, 'points': points, 'selected': selected}) - - # sort and check for duplicates - options = sorted(options, key=lambda option: option['points']) - RubricInput.validate_options(options) - - return {'description': description, 'options': options} - - @staticmethod - def validate_options(options): - ''' - Validates a set of options. This can and should be extended to filter out other bad edge cases - ''' - if len(options) == 0: - raise Exception("[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']: - raise Exception("[extract_category]: found duplicate point values between two different options") - else: - prev = option['points'] - - -registry.register(RubricInput) - -#----------------------------------------------------------------------------- diff --git a/common/lib/capa/capa/templates/rubricinput.html b/common/lib/capa/capa/templates/rubricinput.html deleted file mode 100644 index 3ad5f64958..0000000000 --- a/common/lib/capa/capa/templates/rubricinput.html +++ /dev/null @@ -1,37 +0,0 @@ -
-

Rubric

- % if view_only and has_score: -

This is the rubric that was used to grade your submission.The highlighted selection matches how the grader feels you performed in each category.

- % elif view_only: -

Use the below rubric to rate this submission.

- % else: -

Select the criteria you feel best represents this submission in each category.

- % endif - - % for i in range(len(categories)): - <% category = categories[i] %> - - - % for j in range(len(category['options'])): - <% option = category['options'][j] %> - - % endfor - - % endfor -
${category['description']} - % if view_only: - ## if this is the selected rubric block, show it - % if option['selected']: -
- % else: -
- % endif - ${option['text']} -
[${option['points']} points]
-
- % else: - - - % endif -
-
diff --git a/common/lib/xmodule/xmodule/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/combined_open_ended_rubric.py index 0b2ca1ca2c..a9fd027ddf 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_rubric.py @@ -6,41 +6,48 @@ log=logging.getLogger(__name__) class CombinedOpenEndedRubric: - @staticmethod - def render_rubric(rubric_xml): + def __init__ (self, view_only = False): + self.has_score = False + self.view_only = view_only + + def render_rubric(self, rubric_xml): try: - rubric_categories = CombinedOpenEndedRubric.extract_rubric_categories(rubric_xml) - html = render_to_string('open_ended_rubric.html', {'rubric_categories' : rubric_categories}) + rubric_categories = self.extract_categories(rubric_xml) + html = render_to_string('open_ended_rubric.html', + {'categories' : rubric_categories, + 'has_score': self.has_score, + 'view_only': self.view_only}) except: log.exception("Could not parse the rubric.") - html = rubric_xml + html = etree.tostring(rubric_xml, pretty_print=True) return html - @staticmethod - def extract_rubric_categories(element): + def extract_categories(self, element): ''' Contstruct a list of categories such that the structure looks like: [ { category: "Category 1 Name", options: [{text: "Option 1 Name", points: 0}, {text:"Option 2 Name", points: 5}] }, { category: "Category 2 Name", - options: [{text: "Option 1 Name", points: 0}, - {text: "Option 2 Name", points: 1}, + options: [{text: "Option 1 Name", points: 0}, + {text: "Option 2 Name", points: 1}, {text: "Option 3 Name", points: 2]}] ''' - element = etree.fromstring(element) + if element.tag != 'rubric': + raise Exception("[extract_categories] Expected a tag: got {0} instead".format(element.tag)) + categorylist = list(element) categories = [] - for category in element: + for category in categorylist: if category.tag != 'category': - raise Exception("[capa.inputtypes.extract_categories] Expected a tag: got {0} instead".format(category.tag)) + raise Exception("[extract_categories] Expected a tag: got {0} instead".format(category.tag)) else: - categories.append(CombinedOpenEndedRubric.extract_category(category)) + categories.append(self.extract_category(category)) return categories - @staticmethod - def extract_category(category): - ''' + + def extract_category(self, category): + ''' construct an individual category {category: "Category 1 Name", options: [{text: "Option 1 text", points: 1}, @@ -48,41 +55,32 @@ class CombinedOpenEndedRubric: all sorting and auto-point generation occurs in this function ''' - - has_score=False descriptionxml = category[0] + optionsxml = category[1:] scorexml = category[1] - if scorexml.tag == "option": - optionsxml = category[1:] - else: + score = None + if scorexml.tag == 'score': + score_text = scorexml.text optionsxml = category[2:] - has_score=True + score = int(score_text) + self.has_score = True + # if we are missing the score tag and we are expecting one + elif self.has_score: + raise Exception("[extract_category] Category {0} is missing a score".format(descriptionxml.text)) + # parse description if descriptionxml.tag != 'description': raise Exception("[extract_category]: expected description tag, got {0} instead".format(descriptionxml.tag)) - if has_score: - if scorexml.tag != 'score': - raise Exception("[extract_category]: expected score tag, got {0} instead".format(scorexml.tag)) - - for option in optionsxml: - if option.tag != "option": - raise Exception("[extract_category]: expected option tag, got {0} instead".format(option.tag)) - description = descriptionxml.text - if has_score: - score = int(scorexml.text) - else: - score = 0 - cur_points = 0 options = [] autonumbering = True # parse options 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)) else: pointstr = option.get("points") @@ -99,18 +97,17 @@ class CombinedOpenEndedRubric: cur_points = cur_points + 1 else: raise Exception("[extract_category]: missing points attribute. Cannot continue to auto-create points values after a points value is explicitly dfined.") + + selected = score == points optiontext = option.text - selected = False - if has_score: - if points == score: - selected = True - options.append({'text': option.text, 'points': points, 'selected' : selected}) + options.append({'text': option.text, 'points': points, 'selected': selected}) # sort and check for duplicates options = sorted(options, key=lambda option: option['points']) CombinedOpenEndedRubric.validate_options(options) - return {'description': description, 'options': options, 'score' : score, 'has_score' : has_score} + return {'description': description, 'options': options} + @staticmethod def validate_options(options): @@ -126,4 +123,4 @@ class CombinedOpenEndedRubric: if prev == option['points']: raise Exception("[extract_category]: found duplicate point values between two different options") else: - prev = option['points'] \ No newline at end of file + prev = option['points'] diff --git a/lms/djangoapps/open_ended_grading/staff_grading_service.py b/lms/djangoapps/open_ended_grading/staff_grading_service.py index 9727336791..feb5add72e 100644 --- a/lms/djangoapps/open_ended_grading/staff_grading_service.py +++ b/lms/djangoapps/open_ended_grading/staff_grading_service.py @@ -19,7 +19,7 @@ from xmodule.course_module import CourseDescriptor from student.models import unique_id_for_user from xmodule.x_module import ModuleSystem from mitxmako.shortcuts import render_to_string -from capa import inputtypes +from xmodule.combined_open_ended_rubric import CombinedOpenEndedRubric from lxml import etree log = logging.getLogger(__name__) @@ -263,8 +263,9 @@ def _get_next(course_id, grader_id, location): response = staff_grading_service().get_next(course_id, location, grader_id) response_json = json.loads(response) rubric = response_json['rubric'] - rubric_input = inputtypes.RubricInput(module_system, etree.XML(rubric), {'id': location}) - rubric_html = etree.tostring(rubric_input.get_html()) + rubric_renderer = CombinedOpenEndedRubric(False) + rubric_xml = etree.XML(rubric) + rubric_html = rubric_renderer.render_rubric(rubric_xml) response_json['rubric'] = rubric_html return json.dumps(response_json) except GradingServiceError: diff --git a/lms/templates/open_ended_rubric.html b/lms/templates/open_ended_rubric.html index 9f8a2ece4e..3ad5f64958 100644 --- a/lms/templates/open_ended_rubric.html +++ b/lms/templates/open_ended_rubric.html @@ -1,30 +1,37 @@ - - % for i in range(len(rubric_categories)): - <% category = rubric_categories[i] %> - - - % for j in range(len(category['options'])): - <% option = category['options'][j] %> -
- ${category['description']} - % if category['has_score'] == True: - (Your score: ${category['score']}) - % endif - -
- ${option['text']} - % if option.has_key('selected'): - % if option['selected'] == True: -
[${option['points']} points]
- %else: -
[${option['points']} points]
+
+

Rubric

+ % if view_only and has_score: +

This is the rubric that was used to grade your submission.The highlighted selection matches how the grader feels you performed in each category.

+ % elif view_only: +

Use the below rubric to rate this submission.

+ % else: +

Select the criteria you feel best represents this submission in each category.

+ % endif + + % for i in range(len(categories)): + <% category = categories[i] %> + + + % for j in range(len(category['options'])): + <% option = category['options'][j] %> + + + + % endif + + % endfor + % endfor - - % endfor -
${category['description']} + % if view_only: + ## if this is the selected rubric block, show it + % if option['selected']: +
+ % else: +
% endif + ${option['text']} +
[${option['points']} points]
+
% else: -
[${option['points']} points]
- %endif -
-
\ No newline at end of file +
+