Commit ee5ffedf by Brian Wilson

Clean up task progress javascript. Add before/after scores to tracking of regrading requests.

parent 0d38789a
......@@ -655,7 +655,7 @@ class CapaModule(CapaFields, XModule):
@staticmethod
def make_dict_of_responses(get):
'''Make dictionary of student responses (aka "answers")
get is POST dictionary (Djano QueryDict).
get is POST dictionary (Django QueryDict).
The *get* dict has keys of the form 'x_y', which are mapped
to key 'y' in the returned dict. For example,
......@@ -739,13 +739,13 @@ class CapaModule(CapaFields, XModule):
# Too late. Cannot submit
if self.closed():
event_info['failure'] = 'closed'
self.system.track_function('save_problem_check_fail', event_info)
self.system.track_function('problem_check_fail', event_info)
raise NotFoundError('Problem is closed')
# Problem submitted. Student should reset before checking again
if self.done and self.rerandomize == "always":
event_info['failure'] = 'unreset'
self.system.track_function('save_problem_check_fail', event_info)
self.system.track_function('problem_check_fail', event_info)
raise NotFoundError('Problem must be reset before it can be checked again')
# Problem queued. Students must wait a specified waittime before they are allowed to submit
......@@ -800,7 +800,7 @@ class CapaModule(CapaFields, XModule):
event_info['correct_map'] = correct_map.get_dict()
event_info['success'] = success
event_info['attempts'] = self.attempts
self.system.track_function('save_problem_check', event_info)
self.system.track_function('problem_check', event_info)
if hasattr(self.system, 'psychometrics_handler'): # update PsychometricsData using callback
self.system.psychometrics_handler(self.get_state_for_lcp())
......@@ -813,21 +813,33 @@ class CapaModule(CapaFields, XModule):
}
def regrade_problem(self):
''' Checks whether answers to a problem are correct, and
returns a map of correct/incorrect answers:
"""
Checks whether the existing answers to a problem are correct.
{'success' : 'correct' | 'incorrect' | AJAX alert msg string,
'contents' : html}
'''
This is called when the correct answer to a problem has been changed,
and the grade should be re-evaluated.
Returns a dict with one key:
{'success' : 'correct' | 'incorrect' | AJAX alert msg string }
Raises NotFoundError if called on a problem that has not yet been answered
(since this is avoidable). Returns the error messages for exceptions
occurring while performing the regrading, rather than throwing them.
"""
event_info = dict()
event_info['state'] = self.lcp.get_state()
event_info['problem_id'] = self.location.url()
if not self.done:
event_info['failure'] = 'unanswered'
self.system.track_function('save_problem_regrade_fail', event_info)
self.system.track_function('problem_regrade_fail', event_info)
raise NotFoundError('Problem must be answered before it can be graded again')
# get old score, for comparison:
orig_score = self.lcp.get_score()
event_info['orig_score'] = orig_score['score']
event_info['orig_max_score'] = orig_score['total']
try:
correct_map = self.lcp.regrade_existing_answers()
# regrading should have no effect on attempts, so don't
......@@ -835,8 +847,12 @@ class CapaModule(CapaFields, XModule):
self.set_state_from_lcp()
except StudentInputError as inst:
log.exception("StudentInputError in capa_module:problem_regrade")
event_info['failure'] = 'student_input_error'
self.system.track_function('problem_regrade_fail', event_info)
return {'success': inst.message}
except Exception, err:
event_info['failure'] = 'unexpected'
self.system.track_function('problem_regrade_fail', event_info)
if self.system.DEBUG:
msg = "Error checking problem: " + str(err)
msg += '\nTraceback:\n' + traceback.format_exc()
......@@ -845,6 +861,10 @@ class CapaModule(CapaFields, XModule):
self.publish_grade()
new_score = self.lcp.get_score()
event_info['new_score'] = new_score['score']
event_info['new_max_score'] = new_score['total']
# success = correct if ALL questions in this problem are correct
success = 'correct'
for answer_id in correct_map:
......@@ -856,25 +876,20 @@ class CapaModule(CapaFields, XModule):
event_info['correct_map'] = correct_map.get_dict()
event_info['success'] = success
event_info['attempts'] = self.attempts
self.system.track_function('save_problem_regrade', event_info)
self.system.track_function('problem_regrade', event_info)
# TODO: figure out if psychometrics should be called on regrading requests
if hasattr(self.system, 'psychometrics_handler'): # update PsychometricsData using callback
self.system.psychometrics_handler(self.get_instance_state())
# render problem into HTML
html = self.get_problem_html(encapsulate=False)
return {'success': success,
'contents': html,
}
return {'success': success}
def save_problem(self, get):
'''
"""
Save the passed in answers.
Returns a dict { 'success' : bool, ['error' : error-msg]},
with the error key only present if success is False.
'''
Returns a dict { 'success' : bool, 'msg' : message }
The message is informative on success, and an error message on failure.
"""
event_info = dict()
event_info['state'] = self.lcp.get_state()
event_info['problem_id'] = self.location.url()
......
......@@ -20,7 +20,7 @@ from . import test_system
class DummySystem(ImportSystem):
@patch('xmodule.modulestore.xml.OSFS', lambda dir: MemoryFS())
@patch('xmodule.modulestore.xml.OSFS', lambda directory: MemoryFS())
def __init__(self, load_error_modules):
xmlstore = XMLModuleStore("data_dir", course_dirs=[], load_error_modules=load_error_modules)
......@@ -41,7 +41,8 @@ class DummySystem(ImportSystem):
)
def render_template(self, template, context):
raise Exception("Shouldn't be called")
raise Exception("Shouldn't be called")
class ConditionalFactory(object):
"""
......@@ -93,7 +94,7 @@ class ConditionalFactory(object):
# return dict:
return {'cond_module': cond_module,
'source_module': source_module,
'child_module': child_module }
'child_module': child_module}
class ConditionalModuleBasicTest(unittest.TestCase):
......@@ -109,12 +110,11 @@ class ConditionalModuleBasicTest(unittest.TestCase):
'''verify that get_icon_class works independent of condition satisfaction'''
modules = ConditionalFactory.create(self.test_system)
for attempted in ["false", "true"]:
for icon_class in [ 'other', 'problem', 'video']:
for icon_class in ['other', 'problem', 'video']:
modules['source_module'].is_attempted = attempted
modules['child_module'].get_icon_class = lambda: icon_class
self.assertEqual(modules['cond_module'].get_icon_class(), icon_class)
def test_get_html(self):
modules = ConditionalFactory.create(self.test_system)
# because test_system returns the repr of the context dict passed to render_template,
......@@ -224,4 +224,3 @@ class ConditionalModuleXmlTest(unittest.TestCase):
print "post-attempt ajax: ", ajax
html = ajax['html']
self.assertTrue(any(['This is a secret' in item for item in html]))
......@@ -193,3 +193,5 @@ PASSWORD_HASHERS = (
# By default don't use a worker, execute tasks as if they were local functions
CELERY_ALWAYS_EAGER = True
CELERY_RESULT_BACKEND = 'cache'
BROKER_TRANSPORT = 'memory'
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