Commit 54026886 by Lyla Fischer

multiple choice

parent c5598334
...@@ -14,8 +14,8 @@ from lxml.etree import Element ...@@ -14,8 +14,8 @@ from lxml.etree import Element
from mako.template import Template from mako.template import Template
from util import contextualize_text from util import contextualize_text
from inputtypes import textline, schematic import inputtypes
from responsetypes import numericalresponse, formularesponse, customresponse, schematicresponse, StudentInputError from responsetypes import numericalresponse, formularesponse, customresponse, schematicresponse, multiplechoiceresponse, StudentInputError
import calc import calc
import eia import eia
...@@ -25,8 +25,9 @@ log = logging.getLogger("mitx.courseware") ...@@ -25,8 +25,9 @@ log = logging.getLogger("mitx.courseware")
response_types = {'numericalresponse':numericalresponse, response_types = {'numericalresponse':numericalresponse,
'formularesponse':formularesponse, 'formularesponse':formularesponse,
'customresponse':customresponse, 'customresponse':customresponse,
'schematicresponse':schematicresponse} 'schematicresponse':schematicresponse,
entry_types = ['textline', 'schematic'] 'multiplechoiceresponse':multiplechoiceresponse}
entry_types = ['textline', 'schematic', 'choicegroup']
response_properties = ["responseparam", "answer"] response_properties = ["responseparam", "answer"]
# How to convert from original XML to HTML # How to convert from original XML to HTML
# We should do this with xlst later # We should do this with xlst later
...@@ -35,6 +36,7 @@ html_transforms = {'problem': {'tag':'div'}, ...@@ -35,6 +36,7 @@ html_transforms = {'problem': {'tag':'div'},
"customresponse": {'tag':'span'}, "customresponse": {'tag':'span'},
"schematicresponse": {'tag':'span'}, "schematicresponse": {'tag':'span'},
"formularesponse": {'tag':'span'}, "formularesponse": {'tag':'span'},
"multiplechoiceresponse": {'tag':'span'},
"text": {'tag':'span'}} "text": {'tag':'span'}}
global_context={'random':random, global_context={'random':random,
...@@ -48,29 +50,20 @@ global_context={'random':random, ...@@ -48,29 +50,20 @@ global_context={'random':random,
html_problem_semantics = ["responseparam", "answer", "script"] html_problem_semantics = ["responseparam", "answer", "script"]
# These should be removed from HTML output, but keeping subelements # These should be removed from HTML output, but keeping subelements
html_skip = ["numericalresponse", "customresponse", "schematicresponse", "formularesponse", "text"] html_skip = ["numericalresponse", "customresponse", "schematicresponse", "formularesponse", "text"]
# These should be transformed
html_special_response = {"textline":textline.render,
"schematic":schematic.render}
class LoncapaProblem(object): 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 ## Initialize class variables from state
self.seed = None self.seed = None
self.student_answers = dict() self.student_answers = dict()
self.correct_map = dict() self.correct_map = dict()
self.done = False self.done = False
self.filename = filename self.filename = filename
self.problem_id = id
if seed != None: if seed != None:
self.seed = seed 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 state:
if 'seed' in state: if 'seed' in state:
self.seed = state['seed'] self.seed = state['seed']
...@@ -82,7 +75,6 @@ class LoncapaProblem(object): ...@@ -82,7 +75,6 @@ class LoncapaProblem(object):
self.done = state['done'] self.done = state['done']
# print self.seed # print self.seed
# TODO: Does this deplete the Linux entropy pool? Is this fast enough? # TODO: Does this deplete the Linux entropy pool? Is this fast enough?
if not self.seed: if not self.seed:
self.seed=struct.unpack('i', os.urandom(4))[0] self.seed=struct.unpack('i', os.urandom(4))[0]
...@@ -175,7 +167,7 @@ class LoncapaProblem(object): ...@@ -175,7 +167,7 @@ class LoncapaProblem(object):
if problemtree.tag in html_problem_semantics: if problemtree.tag in html_problem_semantics:
return return
if problemtree.tag in html_special_response: if hasattr(inputtypes, problemtree.tag):
status = "unsubmitted" status = "unsubmitted"
if problemtree.get('id') in self.correct_map: if problemtree.get('id') in self.correct_map:
status = self.correct_map[problemtree.get('id')] status = self.correct_map[problemtree.get('id')]
...@@ -184,7 +176,7 @@ class LoncapaProblem(object): ...@@ -184,7 +176,7 @@ class LoncapaProblem(object):
if self.student_answers and problemtree.get('id') in self.student_answers: if self.student_answers and problemtree.get('id') in self.student_answers:
value = self.student_answers[problemtree.get('id')] 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) tree=Element(problemtree.tag)
for item in problemtree: for item in problemtree:
...@@ -210,7 +202,6 @@ class LoncapaProblem(object): ...@@ -210,7 +202,6 @@ class LoncapaProblem(object):
# TODO: Fix. This loses Element().tail # TODO: Fix. This loses Element().tail
#if problemtree.tag in html_skip: #if problemtree.tag in html_skip:
# return tree # return tree
return [tree] return [tree]
def preprocess_problem(self, tree, correct_map=dict(), answer_map=dict()): # private def preprocess_problem(self, tree, correct_map=dict(), answer_map=dict()): # private
......
from lxml.etree import Element from lxml.etree import Element
from lxml import etree from lxml import etree
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_string
class textline(object): #takes the xml tree as 'element', the student's previous answer as 'value', and the graded status as 'state'
@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)
class schematic(object): def choicegroup(element, value, state):
@staticmethod eid=element.get('id')
def render(element, value, state): type="radio" #because right now, we are only doing multiple choice
eid = element.get('id') choices={}
height = element.get('height') for choice in element:
width = element.get('width') assert choice.tag =="choice", "only <choice> tags should be immediate children of a <choicegroup>"
parts = element.get('parts') choices[choice.get("name")] = etree.tostring(choice[0])
analyses = element.get('analyses') context={'id':eid, 'value':value, 'state':state, 'type':type, 'choices':choices}
initial_value = element.get('initial_value') html=render_to_string("choicegroup.html", context)
submit_analyses = element.get('submit_analyses') return etree.XML(html)
context = {
'id':eid, def textline(element, value, state):
'value':value, eid=element.get('id')
'initial_value':initial_value, count = int(eid.split('_')[-2])-1 # HACK
'state':state, size = element.get('size')
'width':width, context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size}
'height':height, html=render_to_string("textinput.html", context)
'parts':parts, return etree.XML(html)
'analyses':analyses,
'submit_analyses':submit_analyses, def schematic(element, value, state):
} eid = element.get('id')
html=render_to_string("schematicinput.html", context) height = element.get('height')
return etree.XML(html) 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)
...@@ -9,6 +9,8 @@ import traceback ...@@ -9,6 +9,8 @@ import traceback
from calc import evaluator, UndefinedVariable from calc import evaluator, UndefinedVariable
from django.conf import settings from django.conf import settings
from util import contextualize_text from util import contextualize_text
from lxml import etree
from lxml.etree import Element
import calc import calc
import eia import eia
...@@ -34,6 +36,32 @@ def compare_with_tolerance(v1, v2, tol): ...@@ -34,6 +36,32 @@ def compare_with_tolerance(v1, v2, tol):
tolerance = evaluator(dict(),dict(),tol) tolerance = evaluator(dict(),dict(),tol)
return abs(v1-v2) <= tolerance 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): class numericalresponse(object):
def __init__(self, xml, context): def __init__(self, xml, context):
self.xml = xml self.xml = xml
......
<form class="multiple-choice">
% 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}"
% if value == choice_id:
checked="true"
% endif
/> ${choice_description} </label>
% endfor
<span id="answer_${id}"></span>
% if state == 'unsubmitted':
<span class="unanswered" style="display:inline-block;" id="status_${id}"></span>
% elif state == 'correct':
<span class="correct" id="status_${id}"></span>
% elif state == 'incorrect':
<span class="incorrect" id="status_${id}"></span>
% elif state == 'incomplete':
<span class="incorrect" id="status_${id}"></span>
% endif
</form>
...@@ -5,11 +5,23 @@ function ${ id }_load() { ...@@ -5,11 +5,23 @@ function ${ id }_load() {
update_schematics(); update_schematics();
$('#check_${ id }').click(function() { $('#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={}; var submit_data={};
$.each($("[id^=input_${ id }_]"), function(index,value){ $.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', postJSON('/modx/problem/${ id }/problem_check',
submit_data, submit_data,
function(json) { function(json) {
......
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