Commit fd0e2f85 by Arjun Singh

Merged two multiple-choice-like response types into a single response; created…

Merged two multiple-choice-like response types into a single response; created inputtypes for checkboxes and radio buttons adn cleaned up the code a bit. Fixed checkbox responses.
parent defdc611
......@@ -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', 'choicegroup', 'textbox', 'imageinput', 'optioninput']
entry_types = ['textline', 'schematic', 'textbox', 'imageinput', 'optioninput', 'radiogroup', 'checkboxgroup']
solution_types = ['solution'] # extra things displayed after "show answers" is pressed
response_properties = ["responseparam", "answer"] # these get captured as student responses
......
......@@ -8,7 +8,8 @@ Module containing the problem elements which render into input objects
- textline
- textbox (change this to textarea?)
- schemmatic
- choicegroup (for multiplechoice: checkbox, radio, or select option)
- radiogroup
- checkboxgroup
- imageinput (for clickable image)
- optioninput (for option list)
......@@ -145,31 +146,55 @@ def optioninput(element, value, status, render_template, msg=''):
return etree.XML(html)
#-----------------------------------------------------------------------------
@register_render_function
def choicegroup(element, value, status, render_template, msg=''):
def extract_choices(element):
'''
Radio button inputs: multiple choice or true/false
Extracts choices for a few input types, such as radiogroup and
checkboxgroup.
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={}
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[choice.get("name")] = ctext
context={'id':eid, 'value':value, 'state':status, 'type':type, 'choices':choices}
raise Exception("[courseware.capa.inputtypes.extract_choices] \
Expected a <choice> tag; got %s instead"
% choice.tag)
choice_text = ''.join([etree.tostring(x) for x in choice])
choices.append((choice.get("name"), choice_text))
return choices
@register_render_function
def radiogroup(element, value, status, render_template, msg=''):
'''
Radio button inputs: (multiple choice)
'''
eid=element.get('id')
choices = extract_choices(element)
context = { 'id':eid, 'value':value, 'state':status, 'input_type': 'radio', 'choices':choices }
html = render_template("choicegroup.html", context)
return etree.XML(html)
@register_render_function
def checkboxgroup(element, value, status, render_template, msg=''):
'''
Checkbox inputs: (select one or more choices)
'''
eid=element.get('id')
choices = extract_choices(element)
context = { 'id':eid, 'value':value, 'state':status, 'input_type': 'checkbox', 'choices':choices }
html = render_template("choicegroup.html", context)
return etree.XML(html)
......
......@@ -267,83 +267,51 @@ class LoncapaResponse(object):
return u'LoncapaProblem Response %s' % self.xml.tag
#-----------------------------------------------------------------------------
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']
response_tag = 'choiceresponse'
max_inputfields = 1
allowed_inputfields = ['checkboxgroup', 'radiogroup']
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]
self.assign_choice_names()
correct_xml = self.xml.xpath('//*[@id=$id]//choice[@correct="true"]',
id=self.xml.get('id'))
def mc_setup_response(self):
self.correct_choices = set([choice.get('name') for choice in correct_xml])
def assign_choice_names(self):
'''
Initialize name attributes in <choice> stanzas in the <choicegroup> in this response.
Initialize name attributes in <choice> tags for his 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"))
for index, choice in enumerate(self.xml.xpath('//*[@id=$id]//choice',
id=self.xml.get('id'))):
choice.set("name", "choice_"+str(index))
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:
student_answer = student_answers.get(self.answer_id, [])
if not isinstance(student_answer, list):
student_answer = [student_answer]
student_answer = set(student_answer)
required_selected = len(self.correct_choices - student_answer) == 0
no_extra_selected = len(student_answer - self.correct_choices) == 0
correct = required_selected & no_extra_selected
if correct:
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')
return { self.answer_id : self.correct_choices }
#-----------------------------------------------------------------------------
......@@ -1211,5 +1179,5 @@ class ImageResponse(LoncapaResponse):
# TEMPORARY: List of all response subclasses
# FIXME: To be replaced by auto-registration
__all__ = [ CodeResponse, NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse, TrueFalseResponse, ExternalResponse, ImageResponse, OptionResponse, SymbolicResponse, StringResponse ]
__all__ = [ CodeResponse, NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, ExternalResponse, ImageResponse, OptionResponse, SymbolicResponse, StringResponse, ChoiceResponse ]
<form class="multiple-choice">
<form class="choicegroup">
% for choice_id, choice_description in choices.items():
<label for="input_${id}_${choice_id}"> <input type="${type}" name="input_${id}" id="input_${id}_${choice_id}" value="${choice_id}"
% 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}"
% if choice_id in value:
checked="true"
% endif
/> ${choice_description} </label>
<br/>
% 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