From 59493ed0900346ca56218c80a3157bfd7df6dae3 Mon Sep 17 00:00:00 2001 From: Lyla Fischer Date: Mon, 9 Apr 2012 11:37:12 -0400 Subject: [PATCH] multiple choice --- djangoapps/courseware/capa/capa_problem.py | 29 +++----- djangoapps/courseware/capa/inputtypes.py | 75 ++++++++++++--------- djangoapps/courseware/capa/responsetypes.py | 28 ++++++++ templates/choicegroup.html | 21 ++++++ templates/problem.js | 16 ++++- 5 files changed, 115 insertions(+), 54 deletions(-) create mode 100644 templates/choicegroup.html diff --git a/djangoapps/courseware/capa/capa_problem.py b/djangoapps/courseware/capa/capa_problem.py index c5a81e8100..1337a9470f 100644 --- a/djangoapps/courseware/capa/capa_problem.py +++ b/djangoapps/courseware/capa/capa_problem.py @@ -14,8 +14,8 @@ from lxml.etree import Element from mako.template import Template from util import contextualize_text -from inputtypes import textline, schematic -from responsetypes import numericalresponse, formularesponse, customresponse, schematicresponse, StudentInputError +import inputtypes +from responsetypes import numericalresponse, formularesponse, customresponse, schematicresponse, multiplechoiceresponse, StudentInputError import calc import eia @@ -25,8 +25,9 @@ log = logging.getLogger("mitx.courseware") response_types = {'numericalresponse':numericalresponse, 'formularesponse':formularesponse, 'customresponse':customresponse, - 'schematicresponse':schematicresponse} -entry_types = ['textline', 'schematic'] + 'schematicresponse':schematicresponse, + 'multiplechoiceresponse':multiplechoiceresponse} +entry_types = ['textline', 'schematic', 'choicegroup'] response_properties = ["responseparam", "answer"] # How to convert from original XML to HTML # We should do this with xlst later @@ -35,6 +36,7 @@ html_transforms = {'problem': {'tag':'div'}, "customresponse": {'tag':'span'}, "schematicresponse": {'tag':'span'}, "formularesponse": {'tag':'span'}, + "multiplechoiceresponse": {'tag':'span'}, "text": {'tag':'span'}} global_context={'random':random, @@ -48,29 +50,20 @@ global_context={'random':random, html_problem_semantics = ["responseparam", "answer", "script"] # These should be removed from HTML output, but keeping subelements html_skip = ["numericalresponse", "customresponse", "schematicresponse", "formularesponse", "text"] -# These should be transformed -html_special_response = {"textline":textline.render, - "schematic":schematic.render} class LoncapaProblem(object): - def __init__(self, filename, id=None, state=None, seed=None): + def __init__(self, filename, id, state=None, seed=None): ## Initialize class variables from state self.seed = None self.student_answers = dict() self.correct_map = dict() self.done = False self.filename = filename + self.problem_id = id if seed != None: self.seed = seed - if id: - self.problem_id = id - else: - print "NO ID" - raise Exception("This should never happen (183)") - #self.problem_id = filename - if state: if 'seed' in state: self.seed = state['seed'] @@ -82,7 +75,6 @@ class LoncapaProblem(object): self.done = state['done'] # print self.seed - # TODO: Does this deplete the Linux entropy pool? Is this fast enough? if not self.seed: self.seed=struct.unpack('i', os.urandom(4))[0] @@ -175,7 +167,7 @@ class LoncapaProblem(object): if problemtree.tag in html_problem_semantics: return - if problemtree.tag in html_special_response: + if hasattr(inputtypes, problemtree.tag): status = "unsubmitted" if problemtree.get('id') in self.correct_map: status = self.correct_map[problemtree.get('id')] @@ -184,7 +176,7 @@ class LoncapaProblem(object): if self.student_answers and problemtree.get('id') in self.student_answers: value = self.student_answers[problemtree.get('id')] - return html_special_response[problemtree.tag](problemtree, value, status) #TODO + return getattr(inputtypes, problemtree.tag)(problemtree, value, status) #TODO tree=Element(problemtree.tag) for item in problemtree: @@ -210,7 +202,6 @@ class LoncapaProblem(object): # TODO: Fix. This loses Element().tail #if problemtree.tag in html_skip: # return tree - return [tree] def preprocess_problem(self, tree, correct_map=dict(), answer_map=dict()): # private diff --git a/djangoapps/courseware/capa/inputtypes.py b/djangoapps/courseware/capa/inputtypes.py index 789887243d..8002932b66 100644 --- a/djangoapps/courseware/capa/inputtypes.py +++ b/djangoapps/courseware/capa/inputtypes.py @@ -1,40 +1,49 @@ from lxml.etree import Element from lxml import etree -from mitxmako.shortcuts import render_to_response, render_to_string +from mitxmako.shortcuts import render_to_string -class textline(object): - @staticmethod - def render(element, value, state): - eid=element.get('id') - count = int(eid.split('_')[-2])-1 # HACK - size = element.get('size') - context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size} - html=render_to_string("textinput.html", context) - return etree.XML(html) +#takes the xml tree as 'element', the student's previous answer as 'value', and the graded status as 'state' -class schematic(object): - @staticmethod - def render(element, value, state): - eid = element.get('id') - height = element.get('height') - width = element.get('width') - parts = element.get('parts') - analyses = element.get('analyses') - initial_value = element.get('initial_value') - submit_analyses = element.get('submit_analyses') - context = { - 'id':eid, - 'value':value, - 'initial_value':initial_value, - 'state':state, - 'width':width, - 'height':height, - 'parts':parts, - 'analyses':analyses, - 'submit_analyses':submit_analyses, - } - html=render_to_string("schematicinput.html", context) - return etree.XML(html) +def choicegroup(element, value, state): + eid=element.get('id') + type="radio" #because right now, we are only doing multiple choice + choices={} + for choice in element: + assert choice.tag =="choice", "only tags should be immediate children of a " + choices[choice.get("name")] = etree.tostring(choice[0]) + context={'id':eid, 'value':value, 'state':state, 'type':type, 'choices':choices} + html=render_to_string("choicegroup.html", context) + return etree.XML(html) + +def textline(element, value, state): + eid=element.get('id') + count = int(eid.split('_')[-2])-1 # HACK + size = element.get('size') + context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size} + html=render_to_string("textinput.html", context) + return etree.XML(html) + +def schematic(element, value, state): + eid = element.get('id') + height = element.get('height') + width = element.get('width') + parts = element.get('parts') + analyses = element.get('analyses') + initial_value = element.get('initial_value') + submit_analyses = element.get('submit_analyses') + context = { + 'id':eid, + 'value':value, + 'initial_value':initial_value, + 'state':state, + 'width':width, + 'height':height, + 'parts':parts, + 'analyses':analyses, + 'submit_analyses':submit_analyses, + } + html=render_to_string("schematicinput.html", context) + return etree.XML(html) diff --git a/djangoapps/courseware/capa/responsetypes.py b/djangoapps/courseware/capa/responsetypes.py index 62705d3a70..d35b8a26e3 100644 --- a/djangoapps/courseware/capa/responsetypes.py +++ b/djangoapps/courseware/capa/responsetypes.py @@ -9,6 +9,8 @@ import traceback from calc import evaluator, UndefinedVariable from django.conf import settings from util import contextualize_text +from lxml import etree +from lxml.etree import Element import calc import eia @@ -34,6 +36,32 @@ def compare_with_tolerance(v1, v2, tol): tolerance = evaluator(dict(),dict(),tol) return abs(v1-v2) <= tolerance +#Every response type needs methods "grade" and "get_answers" + +class multiplechoiceresponse(object): + def __init__(self, xml, context): + self.xml = xml + self.correct_choices = xml.xpath('//*[@id=$id]//choice[@correct="true"]', + id=xml.get('id')) + self.correct_choices = [choice.get('name') for choice in self.correct_choices] + self.context = context + + self.answer_id = xml.xpath('//*[@id=$id]//choicegroup/@id', + id=xml.get('id')) + assert len(self.answer_id) == 1, "should have exactly one choice group per multiplechoicceresponse" + self.answer_id=self.answer_id[0] + + def grade(self, student_answers): + answers={} + + if self.answer_id in student_answers and student_answers[self.answer_id] in self.correct_choices: + return {self.answer_id:'correct'} + else: + return {self.answer_id:'incorrect'} + + def get_answers(self): + return {self.answer_id:self.correct_choices} + class numericalresponse(object): def __init__(self, xml, context): self.xml = xml diff --git a/templates/choicegroup.html b/templates/choicegroup.html new file mode 100644 index 0000000000..5e281c330b --- /dev/null +++ b/templates/choicegroup.html @@ -0,0 +1,21 @@ +
+ + % for choice_id, choice_description in choices.items(): + + % endfor + + + % if state == 'unsubmitted': + + % elif state == 'correct': + + % elif state == 'incorrect': + + % elif state == 'incomplete': + + % endif +
diff --git a/templates/problem.js b/templates/problem.js index 547a1f0e81..736875efc0 100644 --- a/templates/problem.js +++ b/templates/problem.js @@ -5,11 +5,23 @@ function ${ id }_load() { update_schematics(); $('#check_${ id }').click(function() { - $("input.schematic").each(function(index,element){ element.schematic.update_value(); }); + $("input.schematic").each(function(index,element){ element.schematic.update_value(); }); var submit_data={}; $.each($("[id^=input_${ id }_]"), function(index,value){ - submit_data[value.id]=value.value; + if (value.type==="radio" || value.type==="checkbox"){ + if (value.checked) { + console.log("adding a radio or checkbox value"); + if (typeof submit_data[value.name] == 'undefined') + submit_data[value.name]=[] + submit_data[value.name]+=value.value; + } + } + else{ + console.log("adding a standard value"); + submit_data[value.id]=value.value; + } }); + console.log(submit_data) postJSON('/modx/problem/${ id }/problem_check', submit_data, function(json) {