Commit 94b149f9 by Piotr Mitros

MathJAX and formula answers work

parent b09ab3a7
# For calculator:
# http://pyparsing.wikispaces.com/file/view/fourFn.py
import random, numpy, math, scipy, sys, StringIO, os, struct, json import random, numpy, math, scipy, sys, StringIO, os, struct, json
from xml.dom.minidom import parse, parseString from xml.dom.minidom import parse, parseString
from calc import evaluator
def strip_dict(d):
''' Takes a dict. Returns an identical dict, with all non-word keys stripped out. '''
d=dict([(k, float(d[k])) for k in d if type(k)==str and \
k.isalnum() and \
(type(d[k]) == float or type(d[k]) == int) ])
return d
class LoncapaProblem(): class LoncapaProblem():
def get_state(self): def get_state(self):
''' Stored per-user session data neeeded to: ''' Stored per-user session data neeeded to:
...@@ -71,15 +77,15 @@ class LoncapaProblem(): ...@@ -71,15 +77,15 @@ class LoncapaProblem():
# Loop through the nodes of the problem, and # Loop through the nodes of the problem, and
for e in dom.childNodes: for e in dom.childNodes:
print e, ot # print e, ot
# #
if e.localName=='script': if e.localName=='script':
print e.childNodes[0].data #print e.childNodes[0].data
exec e.childNodes[0].data in g,self.context exec e.childNodes[0].data in g,self.context
elif e.localName=='endouttext': elif e.localName=='endouttext':
ot=False ot=False
elif ot: elif ot:
print e, "::", e.toxml() # print e, "::", e.toxml()
e.writexml(buf) e.writexml(buf)
elif e.localName=='startouttext': elif e.localName=='startouttext':
ot=True ot=True
...@@ -93,7 +99,7 @@ class LoncapaProblem(): ...@@ -93,7 +99,7 @@ class LoncapaProblem():
self.text=buf.getvalue() self.text=buf.getvalue()
self.text=self.contextualize_text(self.text) self.text=self.contextualize_text(self.text)
print self.text # print self.text
self.filename=filename self.filename=filename
done=False done=False
...@@ -129,29 +135,34 @@ class LoncapaProblem(): ...@@ -129,29 +135,34 @@ class LoncapaProblem():
if id not in answers: if id not in answers:
correct_map[id]='incorrect' # Should always be there correct_map[id]='incorrect' # Should always be there
else: else:
correct_map[id]=self.grade_nr(self.questions[key], #correct_map[id]=self.grade_nr(self.questions[key],
self.answers[id]) # self.answers[id])
grader=self.graders[self.questions[key]['type']]
print grader
correct_map[id]=grader(self, self.questions[key],
self.answers[id])
self.correct_map=correct_map self.correct_map=correct_map
return correct_map return correct_map
## Internal methods ## Internal methods
def number(self,text): # def number(self,text):
''' Convert a number to a float, understanding suffixes ''' # ''' Convert a number to a float, understanding suffixes '''
try: # try:
text.strip() # text.strip()
suffixes={'%':0.01,'k':1e3,'M':1e6,'G':1e9,'T':1e12,'P':1e15, # suffixes={'%':0.01,'k':1e3,'M':1e6,'G':1e9,'T':1e12,'P':1e15,
'E':1e18,'Z':1e21,'Y':1e24,'c':1e-2,'m':1e-3,'u':1e-6, # 'E':1e18,'Z':1e21,'Y':1e24,'c':1e-2,'m':1e-3,'u':1e-6,
'n':1e-9,'p':1e-12,'f':1e-15,'a':1e-18,'z':1e-21,'y':1e-24} # 'n':1e-9,'p':1e-12,'f':1e-15,'a':1e-18,'z':1e-21,'y':1e-24}
if text[-1] in suffixes: # if text[-1] in suffixes:
return float(text[:-1])*suffixes[text[-1]] # return float(text[:-1])*suffixes[text[-1]]
else: # else:
return float(text) # return float(text)
except: # except:
return 0 # TODO: Better error handling? # return 0 # TODO: Better error handling?
def grade_nr(self, question, answer): def grade_nr(self, question, answer):
error = abs(self.number(answer) - question['answer']) error = abs(evaluator({},{},answer) - question['answer'])
allowed_error = abs(question['answer']*question['tolerance']) allowed_error = abs(question['answer']*question['tolerance'])
if error <= allowed_error: if error <= allowed_error:
return 'correct' return 'correct'
...@@ -165,13 +176,14 @@ class LoncapaProblem(): ...@@ -165,13 +176,14 @@ class LoncapaProblem():
tolerance=e.getAttribute('default') tolerance=e.getAttribute('default')
self.lid+=1 self.lid+=1
id=str(self.gid)+'_'+str(self.lid) id=str(self.gid)+'_'+str(self.lid)
problem={"answer":self.number(self.contextualize_text(answer)), problem={"answer":evaluator({},{},self.contextualize_text(answer)),
"type":"numericalresponse", "type":"numericalresponse",
"tolerance":self.number(self.contextualize_text(tolerance)), "tolerance":evaluator({},{},self.contextualize_text(tolerance)),
"id":id, "id":id,
"lid":self.lid, "lid":self.lid,
} }
self.questions[self.lid]=problem self.questions[self.lid]=problem
if id in self.answers: if id in self.answers:
value=self.answers[id] value=self.answers[id]
else: else:
...@@ -185,9 +197,64 @@ class LoncapaProblem(): ...@@ -185,9 +197,64 @@ class LoncapaProblem():
html='<input type="text" name="input_{id}" id="input_{id}" value="{value}"><span class="ui-icon ui-icon-{icon}" style="display:inline-block;" id="status_{id}"></span> '.format(id=id,value=value,icon=icon) html='<input type="text" name="input_{id}" id="input_{id}" value="{value}"><span class="ui-icon ui-icon-{icon}" style="display:inline-block;" id="status_{id}"></span> '.format(id=id,value=value,icon=icon)
return html return html
def grade_fr(self, question, answer):
correct = True
for i in range(question['samples_count']):
instructor_variables = strip_dict(dict(self.context))
student_variables = dict()
for var in question['sample_range']:
value = random.uniform(*question['sample_range'][var])
instructor_variables[str(var)] = value
student_variables[str(var)] = value
instructor_result = evaluator(instructor_variables,{},str(question['answer']))
student_result = evaluator(student_variables,{},str(answer))
if abs( student_result - instructor_result ) > question['tolerance']:
return "incorrect"
return "correct"
def handle_fr(self, element):
## Extract description from element
samples=element.getAttribute('samples')
variables=samples.split('@')[0].split(',')
numsamples=int(samples.split('@')[1].split('#')[1])
sranges=zip(*map(lambda x:map(float, x.split(",")), samples.split('@')[1].split('#')[0].split(':')))
answer=element.getAttribute('answer')
for e in element.childNodes:
if e.nodeType==1 and e.getAttribute('type')=="tolerance":
tolerance=e.getAttribute('default')
# Store element
self.lid+=1
id=str(self.gid)+'_'+str(self.lid)
problem={"answer":self.contextualize_text(answer),
"type":"formularesponse",
"tolerance":evaluator({},{},self.contextualize_text(tolerance)),
"sample_range":dict(zip(variables, sranges)),
"samples_count": numsamples,
"id":id,
"lid":self.lid,
}
self.questions[self.lid]=problem
# Generate HTML
if id in self.answers:
value=self.answers[id]
else:
value=""
icon='bullet'
if id in self.correct_map and self.correct_map[id]=='correct':
icon='check'
if id in self.correct_map and self.correct_map[id]=='incorrect':
icon='close'
html='<input type="text" name="input_{id}" id="input_{id}" value="{value}"><span class="ui-icon ui-icon-{icon}" style="display:inline-block;" id="status_{id}"></span> '.format(id=id,value=value,icon=icon)
return html
graders={'numericalresponse':grade_nr} graders={'numericalresponse':grade_nr,
handlers={'numericalresponse':handle_nr} 'formularesponse':grade_fr}
handlers={'numericalresponse':handle_nr,
'formularesponse':handle_fr}
def contextualize_text(self, text): def contextualize_text(self, text):
''' Takes a string with variables. E.g. $a+$b. ''' Takes a string with variables. E.g. $a+$b.
......
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