dynamic math - changes to javascript, responsetypes, and inputtypes
main.html now includes mathjax_include for all mathjax stuff problem.js refreshes mathjax formulas on change all previous *_fromjs renamed to *_dynamath, eg textline_dynamath
This commit is contained in:
@@ -209,6 +209,11 @@ def choicegroup(element, value, status, msg=''):
|
||||
|
||||
@register_render_function
|
||||
def textline(element, value, state, msg=""):
|
||||
'''
|
||||
Simple text line input, with optional size specification.
|
||||
'''
|
||||
if element.get('math') or element.get('dojs'): # 'dojs' flag is temporary, for backwards compatibility with 8.02x
|
||||
return SimpleInput.xml_tags['textline_dynamath'](element,value,state,msg)
|
||||
eid=element.get('id')
|
||||
count = int(eid.split('_')[-2])-1 # HACK
|
||||
size = element.get('size')
|
||||
@@ -219,27 +224,25 @@ def textline(element, value, state, msg=""):
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@register_render_function
|
||||
def js_textline(element, value, status, msg=''):
|
||||
def textline_dynamath(element, value, status, msg=''):
|
||||
'''
|
||||
Plan: We will inspect element to figure out type
|
||||
Text line input with dynamic math display (equation rendered on client in real time during input).
|
||||
'''
|
||||
# TODO: Make a wrapper for <formulainput>
|
||||
# TODO: Make an AJAX loop to confirm equation is okay in real-time as user types
|
||||
## TODO: Code should follow PEP8 (4 spaces per indentation level)
|
||||
'''
|
||||
textline is used for simple one-line inputs, like formularesponse and symbolicresponse.
|
||||
uses a <span id=display_eid>`{::}`</span>
|
||||
and a hidden textarea with id=input_eid_fromjs for the mathjax rendering and return.
|
||||
'''
|
||||
eid=element.get('id')
|
||||
count = int(eid.split('_')[-2])-1 # HACK
|
||||
size = element.get('size')
|
||||
dojs = element.get('dojs') # dojs is used for client-side javascript display & return
|
||||
# when dojs=='math', a <span id=display_eid>`{::}`</span>
|
||||
# and a hidden textarea with id=input_eid_fromjs will be output
|
||||
context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size,
|
||||
'dojs':dojs,
|
||||
'msg':msg,
|
||||
}
|
||||
html=render_to_string("jstext.html", context)
|
||||
html=render_to_string("textinput_dynamath.html", context)
|
||||
return etree.XML(html)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@@ -8,7 +8,9 @@ Used by capa_problem.py
|
||||
'''
|
||||
|
||||
# standard library imports
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import numbers
|
||||
import numpy
|
||||
@@ -34,6 +36,8 @@ import eia
|
||||
|
||||
from util import contextualize_text
|
||||
|
||||
log = logging.getLogger("mitx.courseware")
|
||||
|
||||
def compare_with_tolerance(v1, v2, tol):
|
||||
''' Compare v1 to v2 with maximum tolerance tol
|
||||
tol is relative if it ends in %; otherwise, it is absolute
|
||||
@@ -271,6 +275,9 @@ def sympy_check2():
|
||||
self.expect = xml.get('expect')
|
||||
self.myid = xml.get('id')
|
||||
|
||||
if settings.DEBUG:
|
||||
log.info('answer_ids=%s' % self.answer_ids)
|
||||
|
||||
# the <answer>...</answer> stanza should be local to the current <customresponse>. So try looking there first.
|
||||
self.code = None
|
||||
answer = None
|
||||
@@ -283,7 +290,7 @@ def sympy_check2():
|
||||
# ie the comparison function is defined in the <script>...</script> stanza instead
|
||||
cfn = xml.get('cfn')
|
||||
if cfn:
|
||||
if settings.DEBUG: print "[courseware.capa.responsetypes] cfn = ",cfn
|
||||
if settings.DEBUG: log.info("[courseware.capa.responsetypes] cfn = %s" % cfn)
|
||||
if cfn in context:
|
||||
self.code = context[cfn]
|
||||
else:
|
||||
@@ -321,7 +328,8 @@ def sympy_check2():
|
||||
msg += '\n idset = %s, error = %s' % (idset,err)
|
||||
raise Exception,msg
|
||||
|
||||
fromjs = [ getkey2(student_answers,k+'_fromjs',None) for k in idset ] # ordered list of fromjs_XXX responses (if exists)
|
||||
# global variable in context which holds the Presentation MathML from dynamic math input
|
||||
dynamath = [ student_answers.get(k+'_dynamath',None) for k in idset ] # ordered list of dynamath responses
|
||||
|
||||
# if there is only one box, and it's empty, then don't evaluate
|
||||
if len(idset)==1 and not submission[0]:
|
||||
@@ -339,7 +347,7 @@ def sympy_check2():
|
||||
'expect': self.expect, # expected answer (if given as attribute)
|
||||
'submission':submission, # ordered list of student answers from entry boxes in our subtree
|
||||
'idset':idset, # ordered list of ID's of all entry boxes in our subtree
|
||||
'fromjs':fromjs, # ordered list of all javascript inputs in our subtree
|
||||
'dynamath':dynamath, # ordered list of all javascript inputs in our subtree
|
||||
'answers':student_answers, # dict of student's responses, with keys being entry box IDs
|
||||
'correct':correct, # the list to be filled in by the check function
|
||||
'messages':messages, # the list of messages to be filled in by the check function
|
||||
@@ -359,30 +367,41 @@ def sympy_check2():
|
||||
# this is an interface to the Tutor2 check functions
|
||||
fn = self.code
|
||||
ret = None
|
||||
if settings.DEBUG: log.info(" submission = %s" % submission)
|
||||
try:
|
||||
answer_given = submission[0] if (len(idset)==1) else submission
|
||||
if fn.func_code.co_argcount>=4: # does it want four arguments (the answers dict, myname)?
|
||||
ret = fn(self.expect,answer_given,student_answers,self.answer_ids[0])
|
||||
elif fn.func_code.co_argcount>=3: # does it want a third argument (the answers dict)?
|
||||
ret = fn(self.expect,answer_given,student_answers)
|
||||
else:
|
||||
ret = fn(self.expect,answer_given)
|
||||
# handle variable number of arguments in check function, for backwards compatibility
|
||||
# with various Tutor2 check functions
|
||||
args = [self.expect,answer_given,student_answers,self.answer_ids[0]]
|
||||
argspec = inspect.getargspec(fn)
|
||||
nargs = len(argspec.args)-len(argspec.defaults or [])
|
||||
kwargs = {}
|
||||
for argname in argspec.args[nargs:]:
|
||||
kwargs[argname] = self.context[argname] if argname in self.context else None
|
||||
|
||||
if settings.DEBUG:
|
||||
print '[courseware.capa.responsetypes.customresponse] answer_given=%s' % answer_given
|
||||
log.debug('[courseware.capa.responsetypes.customresponse] answer_given=%s' % answer_given)
|
||||
# log.info('nargs=%d, args=%s' % (nargs,args))
|
||||
|
||||
ret = fn(*args[:nargs],**kwargs)
|
||||
except Exception,err:
|
||||
print "oops in customresponse (cfn) error %s" % err
|
||||
log.error("oops in customresponse (cfn) error %s" % err)
|
||||
# print "context = ",self.context
|
||||
print traceback.format_exc()
|
||||
log.error(traceback.format_exc())
|
||||
raise Exception,"oops in customresponse (cfn) error %s" % err
|
||||
if settings.DEBUG: print "[courseware.capa.responsetypes.customresponse.get_score] ret = ",ret
|
||||
if settings.DEBUG: log.info("[courseware.capa.responsetypes.customresponse.get_score] ret = %s" % ret)
|
||||
if type(ret)==dict:
|
||||
correct = ['correct']*len(idset) if ret['ok'] else ['incorrect']*len(idset)
|
||||
msg = ret['msg']
|
||||
|
||||
if 1:
|
||||
# try to clean up message html
|
||||
log.info('unicode2html(msg) = %s' % msg)
|
||||
msg = '<html>'+msg+'</html>'
|
||||
msg = etree.tostring(fromstring_bs(msg),pretty_print=True)
|
||||
msg = msg.replace('<','<')
|
||||
#msg = msg.replace('<','<')
|
||||
msg = etree.tostring(fromstring_bs(msg,convertEntities=None),pretty_print=True)
|
||||
#msg = etree.tostring(fromstring_bs(msg),pretty_print=True)
|
||||
msg = msg.replace(' ','')
|
||||
#msg = re.sub('<html>(.*)</html>','\\1',msg,flags=re.M|re.DOTALL) # python 2.7
|
||||
msg = re.sub('(?ms)<html>(.*)</html>','\\1',msg)
|
||||
|
||||
@@ -28,6 +28,12 @@ def check_problem_code(ans,the_lcp,correct_answers,false_answers):
|
||||
msg += '<hr width="100%"/>'
|
||||
|
||||
is_ok = True
|
||||
if (not correct_answers) or (not false_answers):
|
||||
ret = {'ok':is_ok,
|
||||
'msg': msg,
|
||||
}
|
||||
return ret
|
||||
|
||||
try:
|
||||
# check correctness
|
||||
fp = the_lcp.system.filestore.open('problems/%s.xml' % pfn)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
from formula import *
|
||||
from sympy_check2 import *
|
||||
|
||||
@@ -234,7 +234,10 @@ class formula(object):
|
||||
if self.the_cmathml: return self.the_cmathml
|
||||
|
||||
# pre-process the presentation mathml before sending it to snuggletex to convert to content mathml
|
||||
xml = self.preprocess_pmathml(self.expr)
|
||||
try:
|
||||
xml = self.preprocess_pmathml(self.expr)
|
||||
except Exception,err:
|
||||
return "<html>Error! Cannot process pmathml</html>"
|
||||
pmathml = etree.tostring(xml,pretty_print=True)
|
||||
self.the_pmathml = pmathml
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@ from formula import *
|
||||
#-----------------------------------------------------------------------------
|
||||
# check function interface
|
||||
|
||||
def sympy_check(expect,ans,adict={},symtab=None,extra_options=None):
|
||||
def sympy_check_simple(expect,ans,adict={},symtab=None,extra_options=None):
|
||||
'''
|
||||
Check a symbolic mathematical expression using sympy.
|
||||
The input is an ascii string (not MathML)
|
||||
'''
|
||||
|
||||
options = {'__MATRIX__':False,'__ABC__':False,'__LOWER__':False}
|
||||
if extra_options: options.update(extra_options)
|
||||
@@ -130,7 +134,11 @@ def check(expect,given,numerical=False,matrix=False,normphase=False,abcsym=False
|
||||
#-----------------------------------------------------------------------------
|
||||
# Check function interface, which takes pmathml input
|
||||
|
||||
def sympy_check2(expect,ans,adict={},abname=''):
|
||||
def sympy_check(expect,ans,adict={},abname=''):
|
||||
'''
|
||||
Check a symbolic mathematical expression using sympy.
|
||||
The input may be presentation MathML
|
||||
'''
|
||||
|
||||
msg = ''
|
||||
# msg += '<p/>abname=%s' % abname
|
||||
|
||||
@@ -36,18 +36,12 @@
|
||||
<script src="${static.url('js/html5shiv.js')}"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script type="text/x-mathjax-config">
|
||||
MathJax.Hub.Config({
|
||||
tex2jax: {inlineMath: [["\\(","\\)"]],
|
||||
displayMath: [["\\[","\\]"]]}
|
||||
});
|
||||
</script>
|
||||
<%block name="headextra"/>
|
||||
<%block name="headextra"/>
|
||||
|
||||
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates.
|
||||
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
|
||||
MathJax extension libraries -->
|
||||
<script type="text/javascript" src="/static/js/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-AMS_HTML-full"></script>
|
||||
<%include file="mathjax_include.html" />
|
||||
</head>
|
||||
|
||||
<body class="<%block name='bodyclass'/>">
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
// function to queue in MathJax to get put the MathML expression in in the right document element
|
||||
function UpdateMathML(jax,id) {
|
||||
toMathML(jax,function (mml) {
|
||||
// document.getElementById(id+'_fromjs').value=math.originalText+ "\n\n=>\n\n"+ mml;
|
||||
delem = document.getElementById("input_" + id + "_fromjs");
|
||||
// document.getElementById(id+'_dynamath').value=math.originalText+ "\n\n=>\n\n"+ mml;
|
||||
delem = document.getElementById("input_" + id + "_dynamath");
|
||||
if (delem) { delem.value=mml; };
|
||||
mmlset[id] = mml;
|
||||
})
|
||||
@@ -83,7 +83,8 @@ function DoUpdateMath(inputId) {
|
||||
|
||||
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates -->
|
||||
<!-- TODO: move to settings -->
|
||||
<script type="text/javascript"
|
||||
src="http://cdn.mathjax.org/mathjax/2.0-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full">
|
||||
## <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/2.0-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full">
|
||||
## <script type="text/javascript" src="/static/js/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-AMS_HTML-full"></script>
|
||||
<script type="text/javascript" src="/static/js/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-MML-AM_HTMLorMML-full"></script>
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,6 +2,23 @@ function ${ id }_content_updated() {
|
||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
|
||||
update_schematics();
|
||||
|
||||
// dynamic math display: add to jaxset for automatic rendering
|
||||
$.each($("[id^=input_${ id }_]"), function(index,value){
|
||||
theid = value.id.replace("input_",""); // ID of the response
|
||||
if (document.getElementById("display_" + theid)){
|
||||
MathJax.Hub.queue.Push(function () {
|
||||
math = MathJax.Hub.getAllJax("display_" + theid)[0];
|
||||
if (math){
|
||||
jaxset[theid] = math;
|
||||
math.Text(document.getElementById(value.id).defaultValue);
|
||||
x = document.getElementById("input_" + theid + "_dynamath");
|
||||
UpdateMathML(math,theid);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
$('#check_${ id }').unbind('click').click(function() {
|
||||
$("input.schematic").each(function(index,element){ element.schematic.update_value(); });
|
||||
$(".CodeMirror").each(function(index,element){ if (element.CodeMirror.save) element.CodeMirror.save(); });
|
||||
|
||||
@@ -24,20 +24,13 @@
|
||||
<script type="text/javascript" src="/static/coffee/src/calculator.js"></script>
|
||||
<script type="text/javascript" src="/static/coffee/src/main.js"></script>
|
||||
|
||||
## <%include file="mathjax_include.html" />
|
||||
|
||||
<script type="text/x-mathjax-config">
|
||||
MathJax.Hub.Config({
|
||||
tex2jax: {inlineMath: [["\\(","\\)"]],
|
||||
displayMath: [["\\[","\\]"]]}
|
||||
});
|
||||
</script>
|
||||
<%block name="headextra"/>
|
||||
<%block name="headextra"/>
|
||||
|
||||
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates.
|
||||
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
|
||||
MathJax extension libraries -->
|
||||
<script type="text/javascript" src="/static/js/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-AMS_HTML-full"></script>
|
||||
<%include file="mathjax_include.html" />
|
||||
## <script type="text/javascript" src="/static/js/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-AMS_HTML-full"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user