Commit 00abaffe by Mark L. Chang

Merge remote-tracking branch 'origin/master' into feature/markchang/studio-analytics

parents db1c0bf0 1b7e552d
...@@ -655,9 +655,9 @@ class MatlabInput(CodeInput): ...@@ -655,9 +655,9 @@ class MatlabInput(CodeInput):
# Check if problem has been queued # Check if problem has been queued
self.queuename = 'matlab' self.queuename = 'matlab'
self.queue_msg = '' self.queue_msg = ''
if 'queue_msg' in self.input_state and self.status in ['queued','incomplete', 'unsubmitted']: if 'queue_msg' in self.input_state and self.status in ['queued', 'incomplete', 'unsubmitted']:
self.queue_msg = self.input_state['queue_msg'] self.queue_msg = self.input_state['queue_msg']
if 'queued' in self.input_state and self.input_state['queuestate'] is not None: if 'queuestate' in self.input_state and self.input_state['queuestate'] == 'queued':
self.status = 'queued' self.status = 'queued'
self.queue_len = 1 self.queue_len = 1
self.msg = self.plot_submitted_msg self.msg = self.plot_submitted_msg
...@@ -702,7 +702,7 @@ class MatlabInput(CodeInput): ...@@ -702,7 +702,7 @@ class MatlabInput(CodeInput):
def _extra_context(self): def _extra_context(self):
''' Set up additional context variables''' ''' Set up additional context variables'''
extra_context = { extra_context = {
'queue_len': self.queue_len, 'queue_len': str(self.queue_len),
'queue_msg': self.queue_msg 'queue_msg': self.queue_msg
} }
return extra_context return extra_context
......
...@@ -361,7 +361,6 @@ class MatlabTest(unittest.TestCase): ...@@ -361,7 +361,6 @@ class MatlabTest(unittest.TestCase):
'feedback': {'message': '3'}, } 'feedback': {'message': '3'}, }
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
input_class = lookup_tag('matlabinput')
the_input = self.input_class(test_system, elt, state) the_input = self.input_class(test_system, elt, state)
context = the_input._get_render_context() context = the_input._get_render_context()
...@@ -381,6 +380,31 @@ class MatlabTest(unittest.TestCase): ...@@ -381,6 +380,31 @@ class MatlabTest(unittest.TestCase):
self.assertEqual(context, expected) self.assertEqual(context, expected)
def test_rendering_while_queued(self):
state = {'value': 'print "good evening"',
'status': 'incomplete',
'input_state': {'queuestate': 'queued'},
}
elt = etree.fromstring(self.xml)
the_input = self.input_class(test_system, elt, state)
context = the_input._get_render_context()
expected = {'id': 'prob_1_2',
'value': 'print "good evening"',
'status': 'queued',
'msg': self.input_class.plot_submitted_msg,
'mode': self.mode,
'rows': self.rows,
'cols': self.cols,
'queue_msg': '',
'linenumbers': 'true',
'hidden': '',
'tabsize': int(self.tabsize),
'queue_len': '1',
}
self.assertEqual(context, expected)
def test_plot_data(self): def test_plot_data(self):
get = {'submission': 'x = 1234;'} get = {'submission': 'x = 1234;'}
response = self.the_input.handle_ajax("plot", get) response = self.the_input.handle_ajax("plot", get)
...@@ -391,6 +415,43 @@ class MatlabTest(unittest.TestCase): ...@@ -391,6 +415,43 @@ class MatlabTest(unittest.TestCase):
self.assertTrue(self.the_input.input_state['queuekey'] is not None) self.assertTrue(self.the_input.input_state['queuekey'] is not None)
self.assertEqual(self.the_input.input_state['queuestate'], 'queued') self.assertEqual(self.the_input.input_state['queuestate'], 'queued')
def test_ungraded_response_success(self):
queuekey = 'abcd'
input_state = {'queuekey': queuekey, 'queuestate': 'queued'}
state = {'value': 'print "good evening"',
'status': 'incomplete',
'input_state': input_state,
'feedback': {'message': '3'}, }
elt = etree.fromstring(self.xml)
the_input = self.input_class(test_system, elt, state)
inner_msg = 'hello!'
queue_msg = json.dumps({'msg': inner_msg})
the_input.ungraded_response(queue_msg, queuekey)
self.assertTrue(input_state['queuekey'] is None)
self.assertTrue(input_state['queuestate'] is None)
self.assertEqual(input_state['queue_msg'], inner_msg)
def test_ungraded_response_key_mismatch(self):
queuekey = 'abcd'
input_state = {'queuekey': queuekey, 'queuestate': 'queued'}
state = {'value': 'print "good evening"',
'status': 'incomplete',
'input_state': input_state,
'feedback': {'message': '3'}, }
elt = etree.fromstring(self.xml)
the_input = self.input_class(test_system, elt, state)
inner_msg = 'hello!'
queue_msg = json.dumps({'msg': inner_msg})
the_input.ungraded_response(queue_msg, 'abc')
self.assertEqual(input_state['queuekey'], queuekey)
self.assertEqual(input_state['queuestate'], 'queued')
self.assertFalse('queue_msg' in input_state)
......
...@@ -108,7 +108,6 @@ class CapaModule(CapaFields, XModule): ...@@ -108,7 +108,6 @@ class CapaModule(CapaFields, XModule):
''' '''
icon_class = 'problem' icon_class = 'problem'
js = {'coffee': [resource_string(__name__, 'js/src/capa/display.coffee'), js = {'coffee': [resource_string(__name__, 'js/src/capa/display.coffee'),
resource_string(__name__, 'js/src/collapsible.coffee'), resource_string(__name__, 'js/src/collapsible.coffee'),
resource_string(__name__, 'js/src/javascript_loader.coffee'), resource_string(__name__, 'js/src/javascript_loader.coffee'),
...@@ -388,7 +387,6 @@ class CapaModule(CapaFields, XModule): ...@@ -388,7 +387,6 @@ class CapaModule(CapaFields, XModule):
return html return html
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.'''
...@@ -401,7 +399,6 @@ class CapaModule(CapaFields, XModule): ...@@ -401,7 +399,6 @@ class CapaModule(CapaFields, XModule):
except Exception, err: except Exception, err:
html = self.handle_problem_html_error(err) html = self.handle_problem_html_error(err)
# The convention is to pass the name of the check button # The convention is to pass the name of the check button
# if we want to show a check button, and False otherwise # if we want to show a check button, and False otherwise
# This works because non-empty strings evaluate to True # This works because non-empty strings evaluate to True
...@@ -535,7 +532,6 @@ class CapaModule(CapaFields, XModule): ...@@ -535,7 +532,6 @@ class CapaModule(CapaFields, XModule):
return False return False
def update_score(self, get): def update_score(self, get):
""" """
Delivers grading response (e.g. from asynchronous code checking) to Delivers grading response (e.g. from asynchronous code checking) to
...@@ -590,7 +586,6 @@ class CapaModule(CapaFields, XModule): ...@@ -590,7 +586,6 @@ class CapaModule(CapaFields, XModule):
self.set_state_from_lcp() self.set_state_from_lcp()
return response return response
def get_answer(self, get): def get_answer(self, get):
''' '''
For the "show answer" button. For the "show answer" button.
...@@ -700,7 +695,6 @@ class CapaModule(CapaFields, XModule): ...@@ -700,7 +695,6 @@ class CapaModule(CapaFields, XModule):
'max_value': score['total'], 'max_value': score['total'],
}) })
def check_problem(self, get): def check_problem(self, get):
''' Checks whether answers to a problem are correct, and ''' Checks whether answers to a problem are correct, and
returns a map of correct/incorrect answers: returns a map of correct/incorrect answers:
...@@ -783,7 +777,7 @@ class CapaModule(CapaFields, XModule): ...@@ -783,7 +777,7 @@ class CapaModule(CapaFields, XModule):
self.system.track_function('save_problem_check', event_info) self.system.track_function('save_problem_check', event_info)
if hasattr(self.system, 'psychometrics_handler'): # update PsychometricsData using callback if hasattr(self.system, 'psychometrics_handler'): # update PsychometricsData using callback
self.system.psychometrics_handler(self.get_instance_state()) self.system.psychometrics_handler(self.get_state_for_lcp())
# render problem into HTML # render problem into HTML
html = self.get_problem_html(encapsulate=False) html = self.get_problem_html(encapsulate=False)
......
...@@ -76,6 +76,11 @@ class Command(BaseCommand): ...@@ -76,6 +76,11 @@ class Command(BaseCommand):
for hist_module in hist_modules: for hist_module in hist_modules:
self.remove_studentmodulehistory_input_state(hist_module, save_changes) self.remove_studentmodulehistory_input_state(hist_module, save_changes)
if self.num_visited % 1000 == 0:
LOG.info(" Progress: updated {0} of {1} student modules".format(self.num_changed, self.num_visited))
LOG.info(" Progress: updated {0} of {1} student history modules".format(self.num_hist_changed,
self.num_hist_visited))
@transaction.autocommit @transaction.autocommit
def remove_studentmodule_input_state(self, module, save_changes): def remove_studentmodule_input_state(self, module, save_changes):
''' Fix the grade assigned to a StudentModule''' ''' Fix the grade assigned to a StudentModule'''
......
...@@ -15,7 +15,6 @@ from scipy.optimize import curve_fit ...@@ -15,7 +15,6 @@ from scipy.optimize import curve_fit
from django.conf import settings from django.conf import settings
from django.db.models import Sum, Max from django.db.models import Sum, Max
from psychometrics.models import * from psychometrics.models import *
from xmodule.modulestore import Location
log = logging.getLogger("mitx.psychometrics") log = logging.getLogger("mitx.psychometrics")
...@@ -246,6 +245,7 @@ def generate_plots_for_problem(problem): ...@@ -246,6 +245,7 @@ def generate_plots_for_problem(problem):
yset['ydat'] = ydat yset['ydat'] = ydat
if len(ydat) > 3: # try to fit to logistic function if enough data points if len(ydat) > 3: # try to fit to logistic function if enough data points
try:
cfp = curve_fit(func_2pl, xdat, ydat, [1.0, max_attempts / 2.0]) cfp = curve_fit(func_2pl, xdat, ydat, [1.0, max_attempts / 2.0])
yset['fitparam'] = cfp yset['fitparam'] = cfp
yset['fitpts'] = func_2pl(np.array(xdat), *cfp[0]) yset['fitpts'] = func_2pl(np.array(xdat), *cfp[0])
...@@ -253,6 +253,8 @@ def generate_plots_for_problem(problem): ...@@ -253,6 +253,8 @@ def generate_plots_for_problem(problem):
fitx = np.linspace(xdat[0], xdat[-1], 100) fitx = np.linspace(xdat[0], xdat[-1], 100)
yset['fitx'] = fitx yset['fitx'] = fitx
yset['fity'] = func_2pl(np.array(fitx), *cfp[0]) yset['fity'] = func_2pl(np.array(fitx), *cfp[0])
except Exception as err:
log.debug('Error in psychoanalyze curve fitting: %s' % err)
dataset['grade_%d' % grade] = yset dataset['grade_%d' % grade] = yset
...@@ -302,7 +304,7 @@ def make_psychometrics_data_update_handler(course_id, user, module_state_key): ...@@ -302,7 +304,7 @@ def make_psychometrics_data_update_handler(course_id, user, module_state_key):
Construct and return a procedure which may be called to update Construct and return a procedure which may be called to update
the PsychometricsData instance for the given StudentModule instance. the PsychometricsData instance for the given StudentModule instance.
""" """
sm = studentmodule.objects.get_or_create( sm, status = StudentModule.objects.get_or_create(
course_id=course_id, course_id=course_id,
student=user, student=user,
module_state_key=module_state_key, module_state_key=module_state_key,
...@@ -329,7 +331,11 @@ def make_psychometrics_data_update_handler(course_id, user, module_state_key): ...@@ -329,7 +331,11 @@ def make_psychometrics_data_update_handler(course_id, user, module_state_key):
return return
pmd.done = done pmd.done = done
pmd.attempts = state['attempts'] try:
pmd.attempts = state.get('attempts', 0)
except:
log.exception("no attempts for %s (state=%s)" % (sm, sm.state))
try: try:
checktimes = eval(pmd.checktimes) # update log of attempt timestamps checktimes = eval(pmd.checktimes) # update log of attempt timestamps
except: except:
......
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