Commit 85f5c93d by Arjun Singh

Added MultipleChoiceResponse, TrueFalseResponse, and choicegroup back in.

parent a23c51e3
......@@ -39,7 +39,7 @@ import responsetypes
# dict of tagname, Response Class -- this should come from auto-registering
response_tag_dict = dict([(x.response_tag,x) for x in responsetypes.__all__])
entry_types = ['textline', 'schematic', 'textbox', 'imageinput', 'optioninput', 'radiogroup', 'checkboxgroup']
entry_types = ['textline', 'schematic', 'textbox', 'imageinput', 'optioninput', 'choicegroup', 'radiogroup', 'checkboxgroup']
solution_types = ['solution'] # extra things displayed after "show answers" is pressed
response_properties = ["responseparam", "answer"] # these get captured as student responses
......
......@@ -8,6 +8,7 @@ Module containing the problem elements which render into input objects
- textline
- textbox (change this to textarea?)
- schemmatic
- choicegroup
- radiogroup
- checkboxgroup
- imageinput (for clickable image)
......@@ -147,6 +148,39 @@ def optioninput(element, value, status, render_template, msg=''):
return etree.XML(html)
#-----------------------------------------------------------------------------
# TODO: consolidate choicegroup, radiogroup, checkboxgroup after discussion of
# desired semantics.
@register_render_function
def choicegroup(element, value, status, render_template, msg=''):
'''
Radio button inputs: multiple choice or true/false
TODO: allow order of choices to be randomized, following lon-capa spec. Use "location" attribute,
ie random, top, bottom.
'''
eid=element.get('id')
if element.get('type') == "MultipleChoice":
type="radio"
elif element.get('type') == "TrueFalse":
type="checkbox"
else:
type="radio"
choices=[]
for choice in element:
if not choice.tag=='choice':
raise Exception("[courseware.capa.inputtypes.choicegroup] Error only <choice> tags should be immediate children of a <choicegroup>, found %s instead" % choice.tag)
ctext = ""
ctext += ''.join([etree.tostring(x) for x in choice]) # TODO: what if choice[0] has math tags in it?
if choice.text is not None:
ctext += choice.text # TODO: fix order?
choices.append((choice.get("name"),ctext))
context={'id':eid, 'value':value, 'state':status, 'input_type':type, 'choices':choices, 'inline':True, 'name_array_suffix':''}
html = render_template("choicegroup.html", context)
return etree.XML(html)
#-----------------------------------------------------------------------------
def extract_choices(element):
'''
Extracts choices for a few input types, such as radiogroup and
......@@ -169,6 +203,8 @@ def extract_choices(element):
return choices
# TODO: consolidate choicegroup, radiogroup, checkboxgroup after discussion of
# desired semantics.
@register_render_function
def radiogroup(element, value, status, render_template, msg=''):
'''
......@@ -179,11 +215,13 @@ def radiogroup(element, value, status, render_template, msg=''):
choices = extract_choices(element)
context = { 'id':eid, 'value':value, 'state':status, 'input_type': 'radio', 'choices':choices }
context = { 'id':eid, 'value':value, 'state':status, 'input_type': 'radio', 'choices':choices, 'inline': False, 'name_array_suffix': '[]' }
html = render_template("choicegroup.html", context)
return etree.XML(html)
# TODO: consolidate choicegroup, radiogroup, checkboxgroup after discussion of
# desired semantics.
@register_render_function
def checkboxgroup(element, value, status, render_template, msg=''):
'''
......@@ -194,7 +232,7 @@ def checkboxgroup(element, value, status, render_template, msg=''):
choices = extract_choices(element)
context = { 'id':eid, 'value':value, 'state':status, 'input_type': 'checkbox', 'choices':choices }
context = { 'id':eid, 'value':value, 'state':status, 'input_type': 'checkbox', 'choices':choices, 'inline': False, 'name_array_suffix': '[]' }
html = render_template("choicegroup.html", context)
return etree.XML(html)
......
......@@ -315,6 +315,85 @@ class ChoiceResponse(LoncapaResponse):
#-----------------------------------------------------------------------------
class MultipleChoiceResponse(LoncapaResponse):
# TODO: handle direction and randomize
snippets = [{'snippet': '''<multiplechoiceresponse direction="vertical" randomize="yes">
<choicegroup type="MultipleChoice">
<choice location="random" correct="false"><span>`a+b`<br/></span></choice>
<choice location="random" correct="true"><span><math>a+b^2</math><br/></span></choice>
<choice location="random" correct="false"><math>a+b+c</math></choice>
<choice location="bottom" correct="false"><math>a+b+d</math></choice>
</choicegroup>
</multiplechoiceresponse>
'''}]
response_tag = 'multiplechoiceresponse'
max_inputfields = 1
allowed_inputfields = ['choicegroup']
def setup_response(self):
self.mc_setup_response() # call secondary setup for MultipleChoice questions, to set name attributes
# define correct choices (after calling secondary setup)
xml = self.xml
cxml = xml.xpath('//*[@id=$id]//choice[@correct="true"]',id=xml.get('id'))
self.correct_choices = [choice.get('name') for choice in cxml]
def mc_setup_response(self):
'''
Initialize name attributes in <choice> stanzas in the <choicegroup> in this response.
'''
i=0
for response in self.xml.xpath("choicegroup"):
rtype = response.get('type')
if rtype not in ["MultipleChoice"]:
response.set("type", "MultipleChoice") # force choicegroup to be MultipleChoice if not valid
for choice in list(response):
if choice.get("name") is None:
choice.set("name", "choice_"+str(i))
i+=1
else:
choice.set("name", "choice_"+choice.get("name"))
def get_score(self, student_answers):
'''
grade student response.
'''
# log.debug('%s: student_answers=%s, correct_choices=%s' % (unicode(self),student_answers,self.correct_choices))
if self.answer_id in student_answers and student_answers[self.answer_id] in self.correct_choices:
return CorrectMap(self.answer_id,'correct')
else:
return CorrectMap(self.answer_id,'incorrect')
def get_answers(self):
return {self.answer_id:self.correct_choices}
class TrueFalseResponse(MultipleChoiceResponse):
response_tag = 'truefalseresponse'
def mc_setup_response(self):
i=0
for response in self.xml.xpath("choicegroup"):
response.set("type", "TrueFalse")
for choice in list(response):
if choice.get("name") is None:
choice.set("name", "choice_"+str(i))
i+=1
else:
choice.set("name", "choice_"+choice.get("name"))
def get_score(self, student_answers):
correct = set(self.correct_choices)
answers = set(student_answers.get(self.answer_id, []))
if correct == answers:
return CorrectMap( self.answer_id , 'correct')
return CorrectMap(self.answer_id ,'incorrect')
#-----------------------------------------------------------------------------
class OptionResponse(LoncapaResponse):
'''
TODO: handle direction and randomize
......@@ -1179,5 +1258,5 @@ class ImageResponse(LoncapaResponse):
# TEMPORARY: List of all response subclasses
# FIXME: To be replaced by auto-registration
__all__ = [ CodeResponse, NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, ExternalResponse, ImageResponse, OptionResponse, SymbolicResponse, StringResponse, ChoiceResponse ]
__all__ = [ CodeResponse, NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, ExternalResponse, ImageResponse, OptionResponse, SymbolicResponse, StringResponse, ChoiceResponse, MultipleChoiceResponse, TrueFalseResponse ]
<form class="choicegroup">
% for choice_id, choice_description in choices:
<label for="input_${id}_${choice_id}"> <input type="${input_type}" name="input_${id}[]" id="input_${id}_${choice_id}" value="${choice_id}"
<label for="input_${id}_${choice_id}"> <input type="${input_type}" name="input_${id}${name_array_suffix}" id="input_${id}_${choice_id}" value="${choice_id}"
% if choice_id in value:
checked="true"
% endif
/> ${choice_description} </label>
<br/>
% if not inline:
<br/>
% endif
% endfor
<span id="answer_${id}"></span>
......
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