Commit 20118614 by Calen Pennington

Get capa problems to display from a keystore

parent 69873495
...@@ -10,8 +10,8 @@ import StringIO ...@@ -10,8 +10,8 @@ import StringIO
from datetime import timedelta from datetime import timedelta
from lxml import etree from lxml import etree
from x_module import XModule from xmodule.x_module import XModule
from mako_module import MakoModuleDescriptor from xmodule.raw_module import RawDescriptor
from progress import Progress from progress import Progress
from capa.capa_problem import LoncapaProblem from capa.capa_problem import LoncapaProblem
from capa.responsetypes import StudentInputError from capa.responsetypes import StudentInputError
...@@ -64,37 +64,25 @@ class ComplexEncoder(json.JSONEncoder): ...@@ -64,37 +64,25 @@ class ComplexEncoder(json.JSONEncoder):
return json.JSONEncoder.default(self, obj) return json.JSONEncoder.default(self, obj)
class CapaModuleDescriptor(MakoModuleDescriptor): class CapaModule(XModule):
"""
Module implementing problems in the LON-CAPA format,
as implemented by capa.capa_problem
"""
mako_template = 'widgets/problem-edit.html'
class Module(XModule):
''' Interface between capa_problem and x_module. Originally a hack ''' Interface between capa_problem and x_module. Originally a hack
meant to be refactored out, but it seems to be serving a useful meant to be refactored out, but it seems to be serving a useful
prupose now. We can e.g .destroy and create the capa_problem on a prupose now. We can e.g .destroy and create the capa_problem on a
reset. reset.
''' '''
icon_class = 'problem'
def get_instance_state(self): def get_instance_state(self):
state = self.lcp.get_state() state = self.lcp.get_state()
state['attempts'] = self.attempts state['attempts'] = self.attempts
return json.dumps(state) return json.dumps(state)
def get_score(self): def get_score(self):
return self.lcp.get_score() return self.lcp.get_score()
def max_score(self): def max_score(self):
return self.lcp.get_max_score() return self.lcp.get_max_score()
def get_progress(self): def get_progress(self):
''' For now, just return score / max_score ''' For now, just return score / max_score
''' '''
...@@ -105,14 +93,13 @@ class Module(XModule): ...@@ -105,14 +93,13 @@ class Module(XModule):
return Progress(score, total) return Progress(score, total)
return None return None
def get_html(self): def get_html(self):
return self.system.render_template('problem_ajax.html', { return self.system.render_template('problem_ajax.html', {
'id': self.item_id, 'element_id': self.location.html_id(),
'ajax_url': self.ajax_url, 'id': self.id,
'ajax_url': self.system.ajax_url,
}) })
def get_problem_html(self, encapsulate=True): def get_problem_html(self, encapsulate=True):
'''Return html for the problem. Adds check, reset, save buttons '''Return html for the problem. Adds check, reset, save buttons
as necessary based on the problem config and state.''' as necessary based on the problem config and state.'''
...@@ -165,12 +152,12 @@ class Module(XModule): ...@@ -165,12 +152,12 @@ class Module(XModule):
explain = False explain = False
context = {'problem': content, context = {'problem': content,
'id': self.item_id, 'id': self.id,
'check_button': check_button, 'check_button': check_button,
'reset_button': reset_button, 'reset_button': reset_button,
'save_button': save_button, 'save_button': save_button,
'answer_available': self.answer_available(), 'answer_available': self.answer_available(),
'ajax_url': self.ajax_url, 'ajax_url': self.system.ajax_url,
'attempts_used': self.attempts, 'attempts_used': self.attempts,
'attempts_allowed': self.max_attempts, 'attempts_allowed': self.max_attempts,
'explain': explain, 'explain': explain,
...@@ -180,17 +167,17 @@ class Module(XModule): ...@@ -180,17 +167,17 @@ class Module(XModule):
html = self.system.render_template('problem.html', context) html = self.system.render_template('problem.html', context)
if encapsulate: if encapsulate:
html = '<div id="problem_{id}" class="problem" data-url="{ajax_url}">'.format( html = '<div id="problem_{id}" class="problem" data-url="{ajax_url}">'.format(
id=self.item_id, ajax_url=self.ajax_url) + html + "</div>" id=self.location.html_id(), ajax_url=self.system.ajax_url) + html + "</div>"
return html return html
def __init__(self, system, xml, item_id, instance_state=None, shared_state=None): def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs):
XModule.__init__(self, system, xml, item_id, instance_state, shared_state) XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs)
self.attempts = 0 self.attempts = 0
self.max_attempts = None self.max_attempts = None
dom2 = etree.fromstring(xml) dom2 = etree.fromstring(definition['data'])
self.explanation = "problems/" + only_one(dom2.xpath('/problem/@explain'), self.explanation = "problems/" + only_one(dom2.xpath('/problem/@explain'),
default="closed") default="closed")
...@@ -205,7 +192,7 @@ class Module(XModule): ...@@ -205,7 +192,7 @@ class Module(XModule):
self.display_due_date = None self.display_due_date = None
grace_period_string = only_one(dom2.xpath('/problem/@graceperiod')) grace_period_string = only_one(dom2.xpath('/problem/@graceperiod'))
if len(grace_period_string) >0 and self.display_due_date: if len(grace_period_string) > 0 and self.display_due_date:
self.grace_period = parse_timedelta(grace_period_string) self.grace_period = parse_timedelta(grace_period_string)
self.close_date = self.display_due_date + self.grace_period self.close_date = self.display_due_date + self.grace_period
#log.debug("Then parsed " + grace_period_string + " to closing date" + str(self.close_date)) #log.debug("Then parsed " + grace_period_string + " to closing date" + str(self.close_date))
...@@ -240,9 +227,9 @@ class Module(XModule): ...@@ -240,9 +227,9 @@ class Module(XModule):
self.attempts = instance_state['attempts'] self.attempts = instance_state['attempts']
# TODO: Should be: self.filename=only_one(dom2.xpath('/problem/@filename')) # TODO: Should be: self.filename=only_one(dom2.xpath('/problem/@filename'))
self.filename= "problems/"+only_one(dom2.xpath('/problem/@filename'))+".xml" self.filename = "problems/" + only_one(dom2.xpath('/problem/@filename')) + ".xml"
self.name=only_one(dom2.xpath('/problem/@name')) self.name = only_one(dom2.xpath('/problem/@name'))
self.weight=only_one(dom2.xpath('/problem/@weight')) self.weight = only_one(dom2.xpath('/problem/@weight'))
if self.rerandomize == 'never': if self.rerandomize == 'never':
seed = 1 seed = 1
elif self.rerandomize == "per_student" and hasattr(system, 'id'): elif self.rerandomize == "per_student" and hasattr(system, 'id'):
...@@ -250,27 +237,27 @@ class Module(XModule): ...@@ -250,27 +237,27 @@ class Module(XModule):
else: else:
seed = None seed = None
try: try:
fp = self.filestore.open(self.filename) fp = self.system.filestore.open(self.filename)
except Exception,err: except Exception:
log.exception('[courseware.capa.capa_module.Module.init] error %s: cannot open file %s' % (err,self.filename)) log.exception('cannot open file %s' % self.filename)
if self.DEBUG: if self.system.DEBUG:
# create a dummy problem instead of failing # create a dummy problem instead of failing
fp = StringIO.StringIO('<problem><text><font color="red" size="+2">Problem file %s is missing</font></text></problem>' % self.filename) fp = StringIO.StringIO('<problem><text><font color="red" size="+2">Problem file %s is missing</font></text></problem>' % self.filename)
fp.name = "StringIO" fp.name = "StringIO"
else: else:
raise raise
try: try:
self.lcp=LoncapaProblem(fp, self.item_id, instance_state, seed = seed, system=self.system) self.lcp = LoncapaProblem(fp, self.id, instance_state, seed=seed, system=self.system)
except Exception,err: except Exception:
msg = '[courseware.capa.capa_module.Module.init] error %s: cannot create LoncapaProblem %s' % (err,self.filename) msg = 'cannot create LoncapaProblem %s' % self.filename
log.exception(msg) log.exception(msg)
if self.DEBUG: if self.system.DEBUG:
msg = '<p>%s</p>' % msg.replace('<','&lt;') msg = '<p>%s</p>' % msg.replace('<', '&lt;')
msg += '<p><pre>%s</pre></p>' % traceback.format_exc().replace('<','&lt;') msg += '<p><pre>%s</pre></p>' % traceback.format_exc().replace('<', '&lt;')
# create a dummy problem with error message instead of failing # create a dummy problem with error message instead of failing
fp = StringIO.StringIO('<problem><text><font color="red" size="+2">Problem file %s has an error:</font>%s</text></problem>' % (self.filename,msg)) fp = StringIO.StringIO('<problem><text><font color="red" size="+2">Problem file %s has an error:</font>%s</text></problem>' % (self.filename, msg))
fp.name = "StringIO" fp.name = "StringIO"
self.lcp=LoncapaProblem(fp, self.item_id, instance_state, seed = seed, system=self.system) self.lcp = LoncapaProblem(fp, self.id, instance_state, seed=seed, system=self.system)
else: else:
raise raise
...@@ -299,8 +286,8 @@ class Module(XModule): ...@@ -299,8 +286,8 @@ class Module(XModule):
d = handlers[dispatch](get) d = handlers[dispatch](get)
after = self.get_progress() after = self.get_progress()
d.update({ d.update({
'progress_changed' : after != before, 'progress_changed': after != before,
'progress_status' : Progress.to_js_status_str(after), 'progress_status': Progress.to_js_status_str(after),
}) })
return json.dumps(d, cls=ComplexEncoder) return json.dumps(d, cls=ComplexEncoder)
...@@ -313,7 +300,6 @@ class Module(XModule): ...@@ -313,7 +300,6 @@ class Module(XModule):
return False return False
def answer_available(self): def answer_available(self):
''' Is the user allowed to see an answer? ''' Is the user allowed to see an answer?
''' '''
...@@ -334,7 +320,8 @@ class Module(XModule): ...@@ -334,7 +320,8 @@ class Module(XModule):
if self.show_answer == 'always': if self.show_answer == 'always':
return True return True
raise self.system.exception404 #TODO: Not 404 #TODO: Not 404
raise self.system.exception404
def get_answer(self, get): def get_answer(self, get):
''' '''
...@@ -348,8 +335,7 @@ class Module(XModule): ...@@ -348,8 +335,7 @@ class Module(XModule):
raise self.system.exception404 raise self.system.exception404
else: else:
answers = self.lcp.get_question_answers() answers = self.lcp.get_question_answers()
return {'answers' : answers} return {'answers': answers}
# Figure out if we should move these to capa_problem? # Figure out if we should move these to capa_problem?
def get_problem(self, get): def get_problem(self, get):
...@@ -359,7 +345,7 @@ class Module(XModule): ...@@ -359,7 +345,7 @@ class Module(XModule):
Used if we want to reconfirm we have the right thing e.g. after Used if we want to reconfirm we have the right thing e.g. after
several AJAX calls. several AJAX calls.
''' '''
return {'html' : self.get_problem_html(encapsulate=False)} return {'html': self.get_problem_html(encapsulate=False)}
@staticmethod @staticmethod
def make_dict_of_responses(get): def make_dict_of_responses(get):
...@@ -409,18 +395,16 @@ class Module(XModule): ...@@ -409,18 +395,16 @@ class Module(XModule):
correct_map = self.lcp.grade_answers(answers) correct_map = self.lcp.grade_answers(answers)
except StudentInputError as inst: except StudentInputError as inst:
# TODO (vshnayder): why is this line here? # TODO (vshnayder): why is this line here?
self.lcp = LoncapaProblem(self.filestore.open(self.filename), self.lcp = LoncapaProblem(self.system.filestore.open(self.filename),
id=lcp_id, state=old_state, system=self.system) id=lcp_id, state=old_state, system=self.system)
traceback.print_exc() traceback.print_exc()
return {'success': inst.message} return {'success': inst.message}
except: except:
# TODO: why is this line here? # TODO: why is this line here?
self.lcp = LoncapaProblem(self.filestore.open(self.filename), self.lcp = LoncapaProblem(self.system.filestore.open(self.filename),
id=lcp_id, state=old_state, system=self.system) id=lcp_id, state=old_state, system=self.system)
traceback.print_exc() traceback.print_exc()
raise Exception,"error in capa_module" raise Exception("error in capa_module")
# TODO: Dead code... is this a bug, or just old?
return {'success':'Unknown Error'}
self.attempts = self.attempts + 1 self.attempts = self.attempts + 1
self.lcp.done = True self.lcp.done = True
...@@ -431,21 +415,18 @@ class Module(XModule): ...@@ -431,21 +415,18 @@ class Module(XModule):
if not correct_map.is_correct(answer_id): if not correct_map.is_correct(answer_id):
success = 'incorrect' success = 'incorrect'
event_info['correct_map'] = correct_map.get_dict() # log this in the tracker # log this in the tracker
event_info['correct_map'] = correct_map.get_dict()
event_info['success'] = success event_info['success'] = success
self.tracker('save_problem_check', event_info) self.tracker('save_problem_check', event_info)
try: # render problem into HTML
html = self.get_problem_html(encapsulate=False) # render problem into HTML html = self.get_problem_html(encapsulate=False)
except Exception,err:
log.error('failed to generate html')
raise
return {'success': success, return {'success': success,
'contents': html, 'contents': html,
} }
def save_problem(self, get): def save_problem(self, get):
''' '''
Save the passed in answers. Save the passed in answers.
...@@ -471,8 +452,8 @@ class Module(XModule): ...@@ -471,8 +452,8 @@ class Module(XModule):
if self.lcp.done and self.rerandomize == "always": if self.lcp.done and self.rerandomize == "always":
event_info['failure'] = 'done' event_info['failure'] = 'done'
self.tracker('save_problem_fail', event_info) self.tracker('save_problem_fail', event_info)
return {'success' : False, return {'success': False,
'error' : "Problem needs to be reset prior to save."} 'error': "Problem needs to be reset prior to save."}
self.lcp.student_answers = answers self.lcp.student_answers = answers
...@@ -503,12 +484,21 @@ class Module(XModule): ...@@ -503,12 +484,21 @@ class Module(XModule):
self.lcp.do_reset() self.lcp.do_reset()
if self.rerandomize == "always": if self.rerandomize == "always":
# reset random number generator seed (note the self.lcp.get_state() in next line) # reset random number generator seed (note the self.lcp.get_state() in next line)
self.lcp.seed=None self.lcp.seed = None
self.lcp = LoncapaProblem(self.filestore.open(self.filename), self.lcp = LoncapaProblem(self.system.filestore.open(self.filename),
self.item_id, self.lcp.get_state(), system=self.system) self.id, self.lcp.get_state(), system=self.system)
event_info['new_state'] = self.lcp.get_state() event_info['new_state'] = self.lcp.get_state()
self.tracker('reset_problem', event_info) self.tracker('reset_problem', event_info)
return {'html' : self.get_problem_html(encapsulate=False)} return {'html': self.get_problem_html(encapsulate=False)}
class CapaDescriptor(RawDescriptor):
"""
Module implementing problems in the LON-CAPA format,
as implemented by capa.capa_problem
"""
module_class = CapaModule
...@@ -19,9 +19,10 @@ setup( ...@@ -19,9 +19,10 @@ setup(
"section = xmodule.translation_module:SemanticSectionDescriptor", "section = xmodule.translation_module:SemanticSectionDescriptor",
"sequential = xmodule.seq_module:SequenceDescriptor", "sequential = xmodule.seq_module:SequenceDescriptor",
"vertical = xmodule.vertical_module:VerticalDescriptor", "vertical = xmodule.vertical_module:VerticalDescriptor",
"problem = xmodule.capa_module:CapaDescriptor",
"problemset = xmodule.seq_module:SequenceDescriptor", "problemset = xmodule.seq_module:SequenceDescriptor",
"videosequence = xmodule.seq_module:SequenceDescriptor",
"video = xmodule.video_module:VideoDescriptor", "video = xmodule.video_module:VideoDescriptor",
"videosequence = xmodule.seq_module:SequenceDescriptor",
] ]
} }
) )
...@@ -10,6 +10,9 @@ class_priority = ['video', 'problem'] ...@@ -10,6 +10,9 @@ class_priority = ['video', 'problem']
class VerticalModule(XModule): class VerticalModule(XModule):
''' Layout module for laying out submodules vertically.''' ''' Layout module for laying out submodules vertically.'''
def get_html(self): def get_html(self):
if self.contents is None:
self.contents = [child.get_html() for child in self.get_display_items()]
return self.system.render_template('vert_module.html', { return self.system.render_template('vert_module.html', {
'items': self.contents 'items': self.contents
}) })
...@@ -31,7 +34,7 @@ class VerticalModule(XModule): ...@@ -31,7 +34,7 @@ class VerticalModule(XModule):
def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs): def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs):
XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs) XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs)
self.contents = [child.get_html() for child in self.get_display_items()] self.contents = None
class VerticalDescriptor(SequenceDescriptor): class VerticalDescriptor(SequenceDescriptor):
......
...@@ -60,13 +60,7 @@ class I4xSystem(object): ...@@ -60,13 +60,7 @@ class I4xSystem(object):
''' '''
self.ajax_url = ajax_url self.ajax_url = ajax_url
self.track_function = track_function self.track_function = track_function
if not filestore:
self.filestore = OSFS(settings.DATA_DIR)
else:
self.filestore = filestore self.filestore = filestore
if settings.DEBUG:
log.info("[courseware.module_render.I4xSystem] filestore path = %s",
filestore)
self.get_module = get_module self.get_module = get_module
self.render_function = render_function self.render_function = render_function
self.render_template = render_template self.render_template = render_template
...@@ -241,7 +235,7 @@ def get_module(user, request, location, student_module_cache, position=None): ...@@ -241,7 +235,7 @@ def get_module(user, request, location, student_module_cache, position=None):
shared_state = shared_module.state if shared_module is not None else None shared_state = shared_module.state if shared_module is not None else None
# Setup system context for module instance # Setup system context for module instance
ajax_url = settings.MITX_ROOT_URL + '/modx/' + descriptor.type + '/' + descriptor.url + '/' ajax_url = settings.MITX_ROOT_URL + '/modx/' + descriptor.url + '/'
def _get_module(location): def _get_module(location):
(module, _, _, _) = get_module(user, request, location, student_module_cache, position) (module, _, _, _) = get_module(user, request, location, student_module_cache, position)
...@@ -330,94 +324,33 @@ def render_x_module(user, request, module_xml, student_module_cache, position=No ...@@ -330,94 +324,33 @@ def render_x_module(user, request, module_xml, student_module_cache, position=No
return context return context
def modx_dispatch(request, module=None, dispatch=None, id=None): def modx_dispatch(request, dispatch=None, id=None):
''' Generic view for extensions. This is where AJAX calls go. ''' Generic view for extensions. This is where AJAX calls go.
Arguments: Arguments:
- request -- the django request. - request -- the django request.
- module -- the type of the module, as used in the course configuration xml.
e.g. 'problem', 'video', etc
- dispatch -- the command string to pass through to the module's handle_ajax call - dispatch -- the command string to pass through to the module's handle_ajax call
(e.g. 'problem_reset'). If this string contains '?', only pass (e.g. 'problem_reset'). If this string contains '?', only pass
through the part before the first '?'. through the part before the first '?'.
- id -- the module id. Used to look up the student module. - id -- the module id. Used to look up the XModule instance
e.g. filenamexformularesponse
''' '''
# ''' (fix emacs broken parsing) # ''' (fix emacs broken parsing)
if not request.user.is_authenticated():
return redirect('/')
# python concats adjacent strings
error_msg = ("We're sorry, this module is temporarily unavailable. "
"Our staff is working to fix it as soon as possible")
# If there are arguments, get rid of them # If there are arguments, get rid of them
dispatch, _, _ = dispatch.partition('?') dispatch, _, _ = dispatch.partition('?')
ajax_url = '{root}/modx/{module}/{id}'.format(root=settings.MITX_ROOT_URL, student_module_cache = StudentModuleCache(request.user, keystore().get_item(id))
module=module, id=id) instance, instance_module, shared_module, module_type = get_module(request.user, request, id, student_module_cache)
coursename = multicourse_settings.get_coursename_from_request(request)
if coursename and settings.ENABLE_MULTICOURSE:
xp = multicourse_settings.get_course_xmlpath(coursename)
data_root = settings.DATA_DIR + xp
else:
data_root = settings.DATA_DIR
# Grab the XML corresponding to the request from course.xml if instance_module is None:
try: log.debug("Couldn't find module '%s' for user '%s'",
xml = content_parser.module_xml(request.user, module, 'id', id, coursename) id, request.user)
except:
log.exception(
"Unable to load module during ajax call. module=%s, dispatch=%s, id=%s",
module, dispatch, id)
if accepts(request, 'text/html'):
return render_to_response("module-error.html", {})
else:
response = HttpResponse(json.dumps({'success': error_msg}))
return response
module_xml = etree.fromstring(xml)
student_module_cache = StudentModuleCache(request.user, module_xml)
(instance, instance_state, shared_state, module_type) = get_module(
request.user, request, module_xml,
student_module_cache, None)
if instance_state is None:
log.debug("Couldn't find module '%s' for user '%s' and id '%s'",
module, request.user, id)
raise Http404 raise Http404
oldgrade = instance_state.grade oldgrade = instance_module.grade
old_instance_state = instance_state.state old_instance_state = instance_module.state
old_shared_state = shared_state.state if shared_state is not None else None old_shared_state = shared_module.state if shared_module is not None else None
module_from_xml = make_module_from_xml_fn(
request.user, request, student_module_cache, None)
# Create the module
system = I4xSystem(track_function=make_track_function(request),
render_function=None,
module_from_xml=module_from_xml,
render_template=render_to_string,
ajax_url=ajax_url,
request=request,
filestore=OSFS(data_root),
)
try:
module_class = xmodule.get_module_class(module)
instance = module_class(
system, xml, id,
instance_state=old_instance_state,
shared_state=old_shared_state)
except:
log.exception("Unable to load module instance during ajax call")
if accepts(request, 'text/html'):
return render_to_response("module-error.html", {})
else:
response = HttpResponse(json.dumps({'success': error_msg}))
return response
# Let the module handle the AJAX # Let the module handle the AJAX
try: try:
...@@ -427,16 +360,16 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): ...@@ -427,16 +360,16 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
raise raise
# Save the state back to the database # Save the state back to the database
instance_state.state = instance.get_instance_state() instance_module.state = instance.get_instance_state()
if instance.get_score(): if instance.get_score():
instance_state.grade = instance.get_score()['score'] instance_module.grade = instance.get_score()['score']
if instance_state.grade != oldgrade or instance_state.state != old_instance_state: if instance_module.grade != oldgrade or instance_module.state != old_instance_state:
instance_state.save() instance_module.save()
if shared_state is not None: if shared_module is not None:
shared_state.state = instance.get_shared_state() shared_module.state = instance.get_shared_state()
if shared_state.state != old_shared_state: if shared_module.state != old_shared_state:
shared_state.save() shared_module.save()
# Return whatever the module wanted to return to the client/caller # Return whatever the module wanted to return to the client/caller
return HttpResponse(ajax_return) return HttpResponse(ajax_return)
...@@ -174,7 +174,7 @@ def quickedit(request, id=None, qetemplate='quickedit.html',coursename=None): ...@@ -174,7 +174,7 @@ def quickedit(request, id=None, qetemplate='quickedit.html',coursename=None):
module = 'problem' module = 'problem'
xml = content_parser.module_xml(request.user, module, 'id', id, coursename) xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/' ajax_url = settings.MITX_ROOT_URL + '/modx/'+id+'/'
# Create the module (instance of capa_module.Module) # Create the module (instance of capa_module.Module)
system = I4xSystem(track_function = make_track_function(request), system = I4xSystem(track_function = make_track_function(request),
......
...@@ -20,8 +20,8 @@ class @Courseware ...@@ -20,8 +20,8 @@ class @Courseware
id = $(this).attr('id').replace(/video_/, '') id = $(this).attr('id').replace(/video_/, '')
new Video id, $(this).data('streams') new Video id, $(this).data('streams')
$('.course-content .problems-wrapper').each -> $('.course-content .problems-wrapper').each ->
id = $(this).attr('id').replace(/problem_/, '') id = $(this).attr('problem-id')
new Problem id, $(this).data('url') new Problem id, $(this).attr('id'), $(this).data('url')
$('.course-content .histogram').each -> $('.course-content .histogram').each ->
id = $(this).attr('id').replace(/histogram_/, '') id = $(this).attr('id').replace(/histogram_/, '')
new Histogram id, $(this).data('histogram') new Histogram id, $(this).data('histogram')
class @Problem class @Problem
constructor: (@id, url) -> constructor: (@id, @element_id, url) ->
@element = $("#problem_#{id}") @element = $("##{element_id}")
@render() @render()
$: (selector) -> $: (selector) ->
...@@ -26,13 +26,13 @@ class @Problem ...@@ -26,13 +26,13 @@ class @Problem
@element.html(content) @element.html(content)
@bind() @bind()
else else
$.postWithPrefix "/modx/problem/#{@id}/problem_get", (response) => $.postWithPrefix "/modx/#{@id}/problem_get", (response) =>
@element.html(response.html) @element.html(response.html)
@bind() @bind()
check: => check: =>
Logger.log 'problem_check', @answers Logger.log 'problem_check', @answers
$.postWithPrefix "/modx/problem/#{@id}/problem_check", @answers, (response) => $.postWithPrefix "/modx/#{@id}/problem_check", @answers, (response) =>
switch response.success switch response.success
when 'incorrect', 'correct' when 'incorrect', 'correct'
@render(response.contents) @render(response.contents)
...@@ -42,14 +42,14 @@ class @Problem ...@@ -42,14 +42,14 @@ class @Problem
reset: => reset: =>
Logger.log 'problem_reset', @answers Logger.log 'problem_reset', @answers
$.postWithPrefix "/modx/problem/#{@id}/problem_reset", id: @id, (response) => $.postWithPrefix "/modx/#{@id}/problem_reset", id: @id, (response) =>
@render(response.html) @render(response.html)
@updateProgress response @updateProgress response
show: => show: =>
if !@element.hasClass 'showed' if !@element.hasClass 'showed'
Logger.log 'problem_show', problem: @id Logger.log 'problem_show', problem: @id
$.postWithPrefix "/modx/problem/#{@id}/problem_show", (response) => $.postWithPrefix "/modx/#{@id}/problem_show", (response) =>
answers = response.answers answers = response.answers
$.each answers, (key, value) => $.each answers, (key, value) =>
if $.isArray(value) if $.isArray(value)
...@@ -69,7 +69,7 @@ class @Problem ...@@ -69,7 +69,7 @@ class @Problem
save: => save: =>
Logger.log 'problem_save', @answers Logger.log 'problem_save', @answers
$.postWithPrefix "/modx/problem/#{@id}/problem_save", @answers, (response) => $.postWithPrefix "/modx/#{@id}/problem_save", @answers, (response) =>
if response.success if response.success
alert 'Saved' alert 'Saved'
@updateProgress response @updateProgress response
...@@ -94,4 +94,4 @@ class @Problem ...@@ -94,4 +94,4 @@ class @Problem
element.schematic.update_value() element.schematic.update_value()
@$(".CodeMirror").each (index, element) -> @$(".CodeMirror").each (index, element) ->
element.CodeMirror.save() if element.CodeMirror.save element.CodeMirror.save() if element.CodeMirror.save
@answers = @$("[id^=input_#{@id}_]").serialize() @answers = @$("[id^=input_#{@element_id}_]").serialize()
...@@ -88,7 +88,7 @@ class @Sequence ...@@ -88,7 +88,7 @@ class @Sequence
if @position != new_position if @position != new_position
if @position != undefined if @position != undefined
@mark_visited @position @mark_visited @position
$.postWithPrefix "/modx/#{@tag}/#{@id}/goto_position", position: new_position $.postWithPrefix "/modx/#{@id}/goto_position", position: new_position
@mark_active new_position @mark_active new_position
@$('#seq_content').html @elements[new_position - 1].content @$('#seq_content').html @elements[new_position - 1].content
......
<section id="problem_${id}" class="problems-wrapper" data-url="${ajax_url}"></section> <section id="problem_${element_id}" class="problems-wrapper" problem-id="${id}" data-url="${ajax_url}"></section>
...@@ -57,7 +57,7 @@ if settings.COURSEWARE_ENABLED: ...@@ -57,7 +57,7 @@ if settings.COURSEWARE_ENABLED:
url(r'^courseware/(?P<course>[^/]*)/$', 'courseware.views.index', name="courseware_course"), url(r'^courseware/(?P<course>[^/]*)/$', 'courseware.views.index', name="courseware_course"),
url(r'^jumpto/(?P<probname>[^/]+)/$', 'courseware.views.jump_to'), url(r'^jumpto/(?P<probname>[^/]+)/$', 'courseware.views.jump_to'),
url(r'^section/(?P<section>[^/]*)/$', 'courseware.views.render_section'), url(r'^section/(?P<section>[^/]*)/$', 'courseware.views.render_section'),
url(r'^modx/(?P<module>[^/]*)/(?P<id>[^/]*)/(?P<dispatch>[^/]*)$', 'courseware.module_render.modx_dispatch'), #reset_problem'), url(r'^modx/(?P<id>.*?)/(?P<dispatch>[^/]*)$', 'courseware.module_render.modx_dispatch'), #reset_problem'),
url(r'^profile$', 'courseware.views.profile'), url(r'^profile$', 'courseware.views.profile'),
url(r'^profile/(?P<student_id>[^/]*)/$', 'courseware.views.profile'), url(r'^profile/(?P<student_id>[^/]*)/$', 'courseware.views.profile'),
url(r'^change_setting$', 'student.views.change_setting'), url(r'^change_setting$', 'student.views.change_setting'),
......
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