Commit e4ee1c6c by Carlos Andrés Rocha

Rename arguments of modx_dispatch and handle_ajax related functions

Refactor a bit modx_dispatch
parent 2f02496c
...@@ -373,7 +373,7 @@ class LoncapaProblem(object): ...@@ -373,7 +373,7 @@ class LoncapaProblem(object):
html = contextualize_text(etree.tostring(self._extract_html(self.tree)), self.context) html = contextualize_text(etree.tostring(self._extract_html(self.tree)), self.context)
return html return html
def handle_input_ajax(self, get): def handle_input_ajax(self, data):
''' '''
InputTypes can support specialized AJAX calls. Find the correct input and pass along the correct data InputTypes can support specialized AJAX calls. Find the correct input and pass along the correct data
...@@ -381,10 +381,10 @@ class LoncapaProblem(object): ...@@ -381,10 +381,10 @@ class LoncapaProblem(object):
''' '''
# pull out the id # pull out the id
input_id = get['input_id'] input_id = data['input_id']
if self.inputs[input_id]: if self.inputs[input_id]:
dispatch = get['dispatch'] dispatch = data['dispatch']
return self.inputs[input_id].handle_ajax(dispatch, get) return self.inputs[input_id].handle_ajax(dispatch, data)
else: else:
log.warning("Could not find matching input for id: %s" % input_id) log.warning("Could not find matching input for id: %s" % input_id)
return {} return {}
......
...@@ -223,13 +223,13 @@ class InputTypeBase(object): ...@@ -223,13 +223,13 @@ class InputTypeBase(object):
""" """
pass pass
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
""" """
InputTypes that need to handle specialized AJAX should override this. InputTypes that need to handle specialized AJAX should override this.
Input: Input:
dispatch: a string that can be used to determine how to handle the data passed in dispatch: a string that can be used to determine how to handle the data passed in
get: a dictionary containing the data that was sent with the ajax call data: a dictionary containing the data that was sent with the ajax call
Output: Output:
a dictionary object that can be serialized into JSON. This will be sent back to the Javascript. a dictionary object that can be serialized into JSON. This will be sent back to the Javascript.
...@@ -677,20 +677,20 @@ class MatlabInput(CodeInput): ...@@ -677,20 +677,20 @@ class MatlabInput(CodeInput):
self.queue_len = 1 self.queue_len = 1
self.msg = self.plot_submitted_msg self.msg = self.plot_submitted_msg
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
''' '''
Handle AJAX calls directed to this input Handle AJAX calls directed to this input
Args: Args:
- dispatch (str) - indicates how we want this ajax call to be handled - dispatch (str) - indicates how we want this ajax call to be handled
- get (dict) - dictionary of key-value pairs that contain useful data - data (dict) - dictionary of key-value pairs that contain useful data
Returns: Returns:
dict - 'success' - whether or not we successfully queued this submission dict - 'success' - whether or not we successfully queued this submission
- 'message' - message to be rendered in case of error - 'message' - message to be rendered in case of error
''' '''
if dispatch == 'plot': if dispatch == 'plot':
return self._plot_data(get) return self._plot_data(data)
return {} return {}
def ungraded_response(self, queue_msg, queuekey): def ungraded_response(self, queue_msg, queuekey):
...@@ -751,7 +751,7 @@ class MatlabInput(CodeInput): ...@@ -751,7 +751,7 @@ class MatlabInput(CodeInput):
msg = result['msg'] msg = result['msg']
return msg return msg
def _plot_data(self, get): def _plot_data(self, data):
''' '''
AJAX handler for the plot button AJAX handler for the plot button
Args: Args:
...@@ -765,7 +765,7 @@ class MatlabInput(CodeInput): ...@@ -765,7 +765,7 @@ class MatlabInput(CodeInput):
return {'success': False, 'message': 'Cannot connect to the queue'} return {'success': False, 'message': 'Cannot connect to the queue'}
# pull relevant info out of get # pull relevant info out of get
response = get['submission'] response = data['submission']
# construct xqueue headers # construct xqueue headers
qinterface = self.system.xqueue['interface'] qinterface = self.system.xqueue['interface']
...@@ -951,16 +951,16 @@ class ChemicalEquationInput(InputTypeBase): ...@@ -951,16 +951,16 @@ class ChemicalEquationInput(InputTypeBase):
""" """
return {'previewer': '/static/js/capa/chemical_equation_preview.js', } return {'previewer': '/static/js/capa/chemical_equation_preview.js', }
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
''' '''
Since we only have chemcalc preview this input, check to see if it Since we only have chemcalc preview this input, check to see if it
matches the corresponding dispatch and send it through if it does matches the corresponding dispatch and send it through if it does
''' '''
if dispatch == 'preview_chemcalc': if dispatch == 'preview_chemcalc':
return self.preview_chemcalc(get) return self.preview_chemcalc(data)
return {} return {}
def preview_chemcalc(self, get): def preview_chemcalc(self, data):
""" """
Render an html preview of a chemical formula or equation. get should Render an html preview of a chemical formula or equation. get should
contain a key 'formula' and value 'some formula string'. contain a key 'formula' and value 'some formula string'.
...@@ -974,7 +974,7 @@ class ChemicalEquationInput(InputTypeBase): ...@@ -974,7 +974,7 @@ class ChemicalEquationInput(InputTypeBase):
result = {'preview': '', result = {'preview': '',
'error': ''} 'error': ''}
formula = get['formula'] formula = data['formula']
if formula is None: if formula is None:
result['error'] = "No formula specified." result['error'] = "No formula specified."
return result return result
......
...@@ -467,8 +467,8 @@ class MatlabTest(unittest.TestCase): ...@@ -467,8 +467,8 @@ class MatlabTest(unittest.TestCase):
self.assertEqual(context, expected) self.assertEqual(context, expected)
def test_plot_data(self): def test_plot_data(self):
get = {'submission': 'x = 1234;'} data = {'submission': 'x = 1234;'}
response = self.the_input.handle_ajax("plot", get) response = self.the_input.handle_ajax("plot", data)
test_system().xqueue['interface'].send_to_queue.assert_called_with(header=ANY, body=ANY) test_system().xqueue['interface'].send_to_queue.assert_called_with(header=ANY, body=ANY)
...@@ -477,10 +477,10 @@ class MatlabTest(unittest.TestCase): ...@@ -477,10 +477,10 @@ class MatlabTest(unittest.TestCase):
self.assertEqual(self.the_input.input_state['queuestate'], 'queued') self.assertEqual(self.the_input.input_state['queuestate'], 'queued')
def test_plot_data_failure(self): def test_plot_data_failure(self):
get = {'submission': 'x = 1234;'} data = {'submission': 'x = 1234;'}
error_message = 'Error message!' error_message = 'Error message!'
test_system().xqueue['interface'].send_to_queue.return_value = (1, error_message) test_system().xqueue['interface'].send_to_queue.return_value = (1, error_message)
response = self.the_input.handle_ajax("plot", get) response = self.the_input.handle_ajax("plot", data)
self.assertFalse(response['success']) self.assertFalse(response['success'])
self.assertEqual(response['message'], error_message) self.assertEqual(response['message'], error_message)
self.assertTrue('queuekey' not in self.the_input.input_state) self.assertTrue('queuekey' not in self.the_input.input_state)
......
...@@ -519,11 +519,11 @@ class CapaModule(CapaFields, XModule): ...@@ -519,11 +519,11 @@ class CapaModule(CapaFields, XModule):
# now do the substitutions which are filesystem based, e.g. '/static/' prefixes # now do the substitutions which are filesystem based, e.g. '/static/' prefixes
return self.system.replace_urls(html) return self.system.replace_urls(html)
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
""" """
This is called by courseware.module_render, to handle an AJAX call. This is called by courseware.module_render, to handle an AJAX call.
`get` is request.POST. `data` is request.POST.
Returns a json dictionary: Returns a json dictionary:
{ 'progress_changed' : True/False, { 'progress_changed' : True/False,
...@@ -547,18 +547,19 @@ class CapaModule(CapaFields, XModule): ...@@ -547,18 +547,19 @@ class CapaModule(CapaFields, XModule):
before = self.get_progress() before = self.get_progress()
try: try:
d = handlers[dispatch](get) result = handlers[dispatch](data)
except Exception as err: except Exception as err:
_, _, traceback_obj = sys.exc_info() _, _, traceback_obj = sys.exc_info()
raise ProcessingError, err.message, traceback_obj raise ProcessingError(err.message, traceback_obj)
after = self.get_progress() after = self.get_progress()
d.update({
result.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(result, cls=ComplexEncoder)
def is_past_due(self): def is_past_due(self):
""" """
...@@ -633,32 +634,32 @@ class CapaModule(CapaFields, XModule): ...@@ -633,32 +634,32 @@ class CapaModule(CapaFields, XModule):
return False return False
def update_score(self, get): def update_score(self, data):
""" """
Delivers grading response (e.g. from asynchronous code checking) to Delivers grading response (e.g. from asynchronous code checking) to
the capa problem, so its score can be updated the capa problem, so its score can be updated
`get` must have a field `response` which is a string that contains the 'data' must have a key 'response' which is a string that contains the
grader's response grader's response
No ajax return is needed. Return empty dict. No ajax return is needed. Return empty dict.
""" """
queuekey = get['queuekey'] queuekey = data['queuekey']
score_msg = get['xqueue_body'] score_msg = data['xqueue_body']
self.lcp.update_score(score_msg, queuekey) self.lcp.update_score(score_msg, queuekey)
self.set_state_from_lcp() self.set_state_from_lcp()
self.publish_grade() self.publish_grade()
return dict() # No AJAX return is needed return dict() # No AJAX return is needed
def handle_ungraded_response(self, get): def handle_ungraded_response(self, data):
""" """
Delivers a response from the XQueue to the capa problem Delivers a response from the XQueue to the capa problem
The score of the problem will not be updated The score of the problem will not be updated
Args: Args:
- get (dict) must contain keys: - data (dict) must contain keys:
queuekey - a key specific to this response queuekey - a key specific to this response
xqueue_body - the body of the response xqueue_body - the body of the response
Returns: Returns:
...@@ -666,28 +667,30 @@ class CapaModule(CapaFields, XModule): ...@@ -666,28 +667,30 @@ class CapaModule(CapaFields, XModule):
No ajax return is needed, so an empty dict is returned No ajax return is needed, so an empty dict is returned
""" """
queuekey = get['queuekey'] queuekey = data['queuekey']
score_msg = get['xqueue_body'] score_msg = data['xqueue_body']
# pass along the xqueue message to the problem # pass along the xqueue message to the problem
self.lcp.ungraded_response(score_msg, queuekey) self.lcp.ungraded_response(score_msg, queuekey)
self.set_state_from_lcp() self.set_state_from_lcp()
return dict() return dict()
def handle_input_ajax(self, get): def handle_input_ajax(self, data):
""" """
Handle ajax calls meant for a particular input in the problem Handle ajax calls meant for a particular input in the problem
Args: Args:
- get (dict) - data that should be passed to the input - data (dict) - data that should be passed to the input
Returns: Returns:
- dict containing the response from the input - dict containing the response from the input
""" """
response = self.lcp.handle_input_ajax(get) response = self.lcp.handle_input_ajax(data)
# save any state changes that may occur # save any state changes that may occur
self.set_state_from_lcp() self.set_state_from_lcp()
return response return response
def get_answer(self, get): def get_answer(self, data):
""" """
For the "show answer" button. For the "show answer" button.
...@@ -717,10 +720,9 @@ class CapaModule(CapaFields, XModule): ...@@ -717,10 +720,9 @@ class CapaModule(CapaFields, XModule):
return {'answers': new_answers} return {'answers': new_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, _data):
""" """
Return results of get_problem_html, as a simple dict for json-ing. Return results of get_problem_html, as a simple dict for json-ing.
{ 'html': <the-html> } { 'html': <the-html> }
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
...@@ -729,27 +731,27 @@ class CapaModule(CapaFields, XModule): ...@@ -729,27 +731,27 @@ class CapaModule(CapaFields, XModule):
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(data):
""" """
Make dictionary of student responses (aka "answers") Make dictionary of student responses (aka "answers")
`get` is POST dictionary (Django QueryDict). `data` is POST dictionary (Django QueryDict).
The `get` dict has keys of the form 'x_y', which are mapped The `data` dict has keys of the form 'x_y', which are mapped
to key 'y' in the returned dict. For example, to key 'y' in the returned dict. For example,
'input_1_2_3' would be mapped to '1_2_3' in the returned dict. 'input_1_2_3' would be mapped to '1_2_3' in the returned dict.
Some inputs always expect a list in the returned dict Some inputs always expect a list in the returned dict
(e.g. checkbox inputs). The convention is that (e.g. checkbox inputs). The convention is that
keys in the `get` dict that end with '[]' will always keys in the `data` dict that end with '[]' will always
have list values in the returned dict. have list values in the returned dict.
For example, if the `get` dict contains {'input_1[]': 'test' } For example, if the `data` dict contains {'input_1[]': 'test' }
then the output dict would contain {'1': ['test'] } then the output dict would contain {'1': ['test'] }
(the value is a list). (the value is a list).
Raises an exception if: Raises an exception if:
-A key in the `get` dictionary does not contain at least one underscore -A key in the `data` dictionary does not contain at least one underscore
(e.g. "input" is invalid, but "input_1" is valid) (e.g. "input" is invalid, but "input_1" is valid)
-Two keys end up with the same name in the returned dict. -Two keys end up with the same name in the returned dict.
...@@ -758,7 +760,7 @@ class CapaModule(CapaFields, XModule): ...@@ -758,7 +760,7 @@ class CapaModule(CapaFields, XModule):
""" """
answers = dict() answers = dict()
for key in get: for key in data:
# e.g. input_resistor_1 ==> resistor_1 # e.g. input_resistor_1 ==> resistor_1
_, _, name = key.partition('_') _, _, name = key.partition('_')
...@@ -777,9 +779,9 @@ class CapaModule(CapaFields, XModule): ...@@ -777,9 +779,9 @@ class CapaModule(CapaFields, XModule):
name = name[:-2] if is_list_key else name name = name[:-2] if is_list_key else name
if is_list_key: if is_list_key:
val = get.getlist(key) val = data.getlist(key)
else: else:
val = get[key] val = data[key]
# If the name already exists, then we don't want # If the name already exists, then we don't want
# to override it. Raise an error instead # to override it. Raise an error instead
...@@ -801,7 +803,7 @@ class CapaModule(CapaFields, XModule): ...@@ -801,7 +803,7 @@ class CapaModule(CapaFields, XModule):
'max_value': score['total'], 'max_value': score['total'],
}) })
def check_problem(self, get): def check_problem(self, data):
""" """
Checks whether answers to a problem are correct Checks whether answers to a problem are correct
...@@ -813,8 +815,9 @@ class CapaModule(CapaFields, XModule): ...@@ -813,8 +815,9 @@ class CapaModule(CapaFields, XModule):
event_info['state'] = self.lcp.get_state() event_info['state'] = self.lcp.get_state()
event_info['problem_id'] = self.location.url() event_info['problem_id'] = self.location.url()
answers = self.make_dict_of_responses(get) answers = self.make_dict_of_responses(data)
event_info['answers'] = convert_files_to_filenames(answers) event_info['answers'] = convert_files_to_filenames(answers)
# Too late. Cannot submit # Too late. Cannot submit
if self.closed(): if self.closed():
event_info['failure'] = 'closed' event_info['failure'] = 'closed'
...@@ -972,7 +975,7 @@ class CapaModule(CapaFields, XModule): ...@@ -972,7 +975,7 @@ class CapaModule(CapaFields, XModule):
return {'success': success} return {'success': success}
def save_problem(self, get): def save_problem(self, data):
""" """
Save the passed in answers. Save the passed in answers.
Returns a dict { 'success' : bool, 'msg' : message } Returns a dict { 'success' : bool, 'msg' : message }
...@@ -982,7 +985,7 @@ class CapaModule(CapaFields, XModule): ...@@ -982,7 +985,7 @@ class CapaModule(CapaFields, XModule):
event_info['state'] = self.lcp.get_state() event_info['state'] = self.lcp.get_state()
event_info['problem_id'] = self.location.url() event_info['problem_id'] = self.location.url()
answers = self.make_dict_of_responses(get) answers = self.make_dict_of_responses(data)
event_info['answers'] = answers event_info['answers'] = answers
# Too late. Cannot submit # Too late. Cannot submit
...@@ -1011,7 +1014,7 @@ class CapaModule(CapaFields, XModule): ...@@ -1011,7 +1014,7 @@ class CapaModule(CapaFields, XModule):
return {'success': True, return {'success': True,
'msg': msg} 'msg': msg}
def reset_problem(self, get): def reset_problem(self, _data):
""" """
Changes problem state to unfinished -- removes student answers, Changes problem state to unfinished -- removes student answers,
and causes problem to rerender itself. and causes problem to rerender itself.
......
...@@ -204,9 +204,9 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): ...@@ -204,9 +204,9 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule):
return_value = self.child_module.get_html() return_value = self.child_module.get_html()
return return_value return return_value
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
self.save_instance_data() self.save_instance_data()
return_value = self.child_module.handle_ajax(dispatch, get) return_value = self.child_module.handle_ajax(dispatch, data)
self.save_instance_data() self.save_instance_data()
return return_value return return_value
...@@ -266,4 +266,3 @@ class CombinedOpenEndedDescriptor(CombinedOpenEndedFields, RawDescriptor): ...@@ -266,4 +266,3 @@ class CombinedOpenEndedDescriptor(CombinedOpenEndedFields, RawDescriptor):
non_editable_fields.extend([CombinedOpenEndedDescriptor.due, CombinedOpenEndedDescriptor.graceperiod, non_editable_fields.extend([CombinedOpenEndedDescriptor.due, CombinedOpenEndedDescriptor.graceperiod,
CombinedOpenEndedDescriptor.markdown, CombinedOpenEndedDescriptor.version]) CombinedOpenEndedDescriptor.markdown, CombinedOpenEndedDescriptor.version])
return non_editable_fields return non_editable_fields
...@@ -135,7 +135,7 @@ class ConditionalModule(ConditionalFields, XModule): ...@@ -135,7 +135,7 @@ class ConditionalModule(ConditionalFields, XModule):
'depends': ';'.join(self.required_html_ids) 'depends': ';'.join(self.required_html_ids)
}) })
def handle_ajax(self, dispatch, post): def handle_ajax(self, _dispatch, _data):
"""This is called by courseware.moduleodule_render, to handle """This is called by courseware.moduleodule_render, to handle
an AJAX call. an AJAX call.
""" """
......
...@@ -500,10 +500,10 @@ class CombinedOpenEndedV1Module(): ...@@ -500,10 +500,10 @@ class CombinedOpenEndedV1Module():
pass pass
return return_html return return_html
def get_rubric(self, get): def get_rubric(self, _data):
""" """
Gets the results of a given grader via ajax. Gets the results of a given grader via ajax.
Input: AJAX get dictionary Input: AJAX data dictionary
Output: Dictionary to be rendered via ajax that contains the result html. Output: Dictionary to be rendered via ajax that contains the result html.
""" """
all_responses = [] all_responses = []
...@@ -532,10 +532,10 @@ class CombinedOpenEndedV1Module(): ...@@ -532,10 +532,10 @@ class CombinedOpenEndedV1Module():
html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context) html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True} return {'html': html, 'success': True}
def get_legend(self, get): def get_legend(self, _data):
""" """
Gets the results of a given grader via ajax. Gets the results of a given grader via ajax.
Input: AJAX get dictionary Input: AJAX data dictionary
Output: Dictionary to be rendered via ajax that contains the result html. Output: Dictionary to be rendered via ajax that contains the result html.
""" """
context = { context = {
...@@ -544,10 +544,10 @@ class CombinedOpenEndedV1Module(): ...@@ -544,10 +544,10 @@ class CombinedOpenEndedV1Module():
html = self.system.render_template('{0}/combined_open_ended_legend.html'.format(self.TEMPLATE_DIR), context) html = self.system.render_template('{0}/combined_open_ended_legend.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True} return {'html': html, 'success': True}
def get_results(self, get): def get_results(self, _data):
""" """
Gets the results of a given grader via ajax. Gets the results of a given grader via ajax.
Input: AJAX get dictionary Input: AJAX data dictionary
Output: Dictionary to be rendered via ajax that contains the result html. Output: Dictionary to be rendered via ajax that contains the result html.
""" """
self.update_task_states() self.update_task_states()
...@@ -588,19 +588,19 @@ class CombinedOpenEndedV1Module(): ...@@ -588,19 +588,19 @@ class CombinedOpenEndedV1Module():
html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context) html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True} return {'html': html, 'success': True}
def get_status_ajax(self, get): def get_status_ajax(self, _data):
""" """
Gets the results of a given grader via ajax. Gets the results of a given grader via ajax.
Input: AJAX get dictionary Input: AJAX data dictionary
Output: Dictionary to be rendered via ajax that contains the result html. Output: Dictionary to be rendered via ajax that contains the result html.
""" """
html = self.get_status(True) html = self.get_status(True)
return {'html': html, 'success': True} return {'html': html, 'success': True}
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
""" """
This is called by courseware.module_render, to handle an AJAX call. This is called by courseware.module_render, to handle an AJAX call.
"get" is request.POST. "data" is request.POST.
Returns a json dictionary: Returns a json dictionary:
{ 'progress_changed' : True/False, { 'progress_changed' : True/False,
...@@ -618,30 +618,30 @@ class CombinedOpenEndedV1Module(): ...@@ -618,30 +618,30 @@ class CombinedOpenEndedV1Module():
} }
if dispatch not in handlers: if dispatch not in handlers:
return_html = self.current_task.handle_ajax(dispatch, get, self.system) return_html = self.current_task.handle_ajax(dispatch, data, self.system)
return self.update_task_states_ajax(return_html) return self.update_task_states_ajax(return_html)
d = handlers[dispatch](get) d = handlers[dispatch](data)
return json.dumps(d, cls=ComplexEncoder) return json.dumps(d, cls=ComplexEncoder)
def next_problem(self, get): def next_problem(self, _data):
""" """
Called via ajax to advance to the next problem. Called via ajax to advance to the next problem.
Input: AJAX get request. Input: AJAX data request.
Output: Dictionary to be rendered Output: Dictionary to be rendered
""" """
self.update_task_states() self.update_task_states()
return {'success': True, 'html': self.get_html_nonsystem(), 'allow_reset': self.ready_to_reset} return {'success': True, 'html': self.get_html_nonsystem(), 'allow_reset': self.ready_to_reset}
def reset(self, get): def reset(self, data):
""" """
If resetting is allowed, reset the state of the combined open ended module. If resetting is allowed, reset the state of the combined open ended module.
Input: AJAX get dictionary Input: AJAX data dictionary
Output: AJAX dictionary to tbe rendered Output: AJAX dictionary to tbe rendered
""" """
if self.state != self.DONE: if self.state != self.DONE:
if not self.ready_to_reset: if not self.ready_to_reset:
return self.out_of_sync_error(get) return self.out_of_sync_error(data)
if self.student_attempts > self.attempts: if self.student_attempts > self.attempts:
return { return {
...@@ -789,13 +789,13 @@ class CombinedOpenEndedV1Module(): ...@@ -789,13 +789,13 @@ class CombinedOpenEndedV1Module():
return progress_object return progress_object
def out_of_sync_error(self, get, msg=''): def out_of_sync_error(self, data, msg=''):
""" """
return dict out-of-sync error message, and also log. return dict out-of-sync error message, and also log.
""" """
#This is a dev_facing_error #This is a dev_facing_error
log.warning("Combined module state out sync. state: %r, get: %r. %s", log.warning("Combined module state out sync. state: %r, data: %r. %s",
self.state, get, msg) self.state, data, msg)
#This is a student_facing_error #This is a student_facing_error
return {'success': False, return {'success': False,
'error': 'The problem state got out-of-sync. Please try reloading the page.'} 'error': 'The problem state got out-of-sync. Please try reloading the page.'}
......
...@@ -122,17 +122,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -122,17 +122,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
self.payload = {'grader_payload': updated_grader_payload} self.payload = {'grader_payload': updated_grader_payload}
def skip_post_assessment(self, get, system): def skip_post_assessment(self, _data, system):
""" """
Ajax function that allows one to skip the post assessment phase Ajax function that allows one to skip the post assessment phase
@param get: AJAX dictionary @param data: AJAX dictionary
@param system: ModuleSystem @param system: ModuleSystem
@return: Success indicator @return: Success indicator
""" """
self.child_state = self.DONE self.child_state = self.DONE
return {'success': True} return {'success': True}
def message_post(self, get, system): def message_post(self, data, system):
""" """
Handles a student message post (a reaction to the grade they received from an open ended grader type) Handles a student message post (a reaction to the grade they received from an open ended grader type)
Returns a boolean success/fail and an error message Returns a boolean success/fail and an error message
...@@ -141,7 +141,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -141,7 +141,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
event_info = dict() event_info = dict()
event_info['problem_id'] = self.location_string event_info['problem_id'] = self.location_string
event_info['student_id'] = system.anonymous_student_id event_info['student_id'] = system.anonymous_student_id
event_info['survey_responses'] = get event_info['survey_responses'] = data
survey_responses = event_info['survey_responses'] survey_responses = event_info['survey_responses']
for tag in ['feedback', 'submission_id', 'grader_id', 'score']: for tag in ['feedback', 'submission_id', 'grader_id', 'score']:
...@@ -587,10 +587,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -587,10 +587,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
html = system.render_template('{0}/open_ended_evaluation.html'.format(self.TEMPLATE_DIR), context) html = system.render_template('{0}/open_ended_evaluation.html'.format(self.TEMPLATE_DIR), context)
return html return html
def handle_ajax(self, dispatch, get, system): def handle_ajax(self, dispatch, data, system):
''' '''
This is called by courseware.module_render, to handle an AJAX call. This is called by courseware.module_render, to handle an AJAX call.
"get" is request.POST. "data" is request.POST.
Returns a json dictionary: Returns a json dictionary:
{ 'progress_changed' : True/False, { 'progress_changed' : True/False,
...@@ -612,7 +612,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -612,7 +612,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
return json.dumps({'error': 'Error handling action. Please try again.', 'success': False}) return json.dumps({'error': 'Error handling action. Please try again.', 'success': False})
before = self.get_progress() before = self.get_progress()
d = handlers[dispatch](get, system) d = handlers[dispatch](data, system)
after = self.get_progress() after = self.get_progress()
d.update({ d.update({
'progress_changed': after != before, 'progress_changed': after != before,
...@@ -620,20 +620,20 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -620,20 +620,20 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
}) })
return json.dumps(d, cls=ComplexEncoder) return json.dumps(d, cls=ComplexEncoder)
def check_for_score(self, get, system): def check_for_score(self, _data, system):
""" """
Checks to see if a score has been received yet. Checks to see if a score has been received yet.
@param get: AJAX get dictionary @param data: AJAX dictionary
@param system: Modulesystem (needed to align with other ajax functions) @param system: Modulesystem (needed to align with other ajax functions)
@return: Returns the current state @return: Returns the current state
""" """
state = self.child_state state = self.child_state
return {'state': state} return {'state': state}
def save_answer(self, get, system): def save_answer(self, data, system):
""" """
Saves a student answer Saves a student answer
@param get: AJAX get dictionary @param data: AJAX dictionary
@param system: modulesystem @param system: modulesystem
@return: Success indicator @return: Success indicator
""" """
...@@ -644,17 +644,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -644,17 +644,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
return msg return msg
if self.child_state != self.INITIAL: if self.child_state != self.INITIAL:
return self.out_of_sync_error(get) return self.out_of_sync_error(data)
# add new history element with answer and empty score and hint. # add new history element with answer and empty score and hint.
success, get = self.append_image_to_student_answer(get) success, data = self.append_image_to_student_answer(data)
error_message = "" error_message = ""
if success: if success:
success, allowed_to_submit, error_message = self.check_if_student_can_submit() success, allowed_to_submit, error_message = self.check_if_student_can_submit()
if allowed_to_submit: if allowed_to_submit:
get['student_answer'] = OpenEndedModule.sanitize_html(get['student_answer']) data['student_answer'] = OpenEndedModule.sanitize_html(data['student_answer'])
self.new_history_entry(get['student_answer']) self.new_history_entry(data['student_answer'])
self.send_to_grader(get['student_answer'], system) self.send_to_grader(data['student_answer'], system)
self.change_state(self.ASSESSING) self.change_state(self.ASSESSING)
else: else:
# Error message already defined # Error message already defined
...@@ -666,17 +666,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -666,17 +666,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
return { return {
'success': success, 'success': success,
'error': error_message, 'error': error_message,
'student_response': get['student_answer'] 'student_response': data['student_answer']
} }
def update_score(self, get, system): def update_score(self, data, system):
""" """
Updates the current score via ajax. Called by xqueue. Updates the current score via ajax. Called by xqueue.
Input: AJAX get dictionary, modulesystem Input: AJAX data dictionary, modulesystem
Output: None Output: None
""" """
queuekey = get['queuekey'] queuekey = data['queuekey']
score_msg = get['xqueue_body'] score_msg = data['xqueue_body']
# TODO: Remove need for cmap # TODO: Remove need for cmap
self._update_score(score_msg, queuekey, system) self._update_score(score_msg, queuekey, system)
......
...@@ -272,13 +272,13 @@ class OpenEndedChild(object): ...@@ -272,13 +272,13 @@ class OpenEndedChild(object):
return None return None
return None return None
def out_of_sync_error(self, get, msg=''): def out_of_sync_error(self, data, msg=''):
""" """
return dict out-of-sync error message, and also log. return dict out-of-sync error message, and also log.
""" """
# This is a dev_facing_error # This is a dev_facing_error
log.warning("Open ended child state out sync. state: %r, get: %r. %s", log.warning("Open ended child state out sync. state: %r, data: %r. %s",
self.child_state, get, msg) self.child_state, data, msg)
# This is a student_facing_error # This is a student_facing_error
return {'success': False, return {'success': False,
'error': 'The problem state got out-of-sync. Please try reloading the page.'} 'error': 'The problem state got out-of-sync. Please try reloading the page.'}
...@@ -345,24 +345,24 @@ class OpenEndedChild(object): ...@@ -345,24 +345,24 @@ class OpenEndedChild(object):
return success, image_ok, s3_public_url return success, image_ok, s3_public_url
def check_for_image_and_upload(self, get_data): def check_for_image_and_upload(self, data):
""" """
Checks to see if an image was passed back in the AJAX query. If so, it will upload it to S3 Checks to see if an image was passed back in the AJAX query. If so, it will upload it to S3
@param get_data: AJAX get data @param data: AJAX data
@return: Success, whether or not a file was in the get dictionary, @return: Success, whether or not a file was in the data dictionary,
and the html corresponding to the uploaded image and the html corresponding to the uploaded image
""" """
has_file_to_upload = False has_file_to_upload = False
uploaded_to_s3 = False uploaded_to_s3 = False
image_tag = "" image_tag = ""
image_ok = False image_ok = False
if 'can_upload_files' in get_data: if 'can_upload_files' in data:
if get_data['can_upload_files'] in ['true', '1']: if data['can_upload_files'] in ['true', '1']:
has_file_to_upload = True has_file_to_upload = True
file = get_data['student_file'][0] student_file = data['student_file'][0]
uploaded_to_s3, image_ok, s3_public_url = self.upload_image_to_s3(file) uploaded_to_s3, image_ok, s3_public_url = self.upload_image_to_s3(student_file)
if uploaded_to_s3: if uploaded_to_s3:
image_tag = self.generate_image_tag_from_url(s3_public_url, file.name) image_tag = self.generate_image_tag_from_url(s3_public_url, student_file.name)
return has_file_to_upload, uploaded_to_s3, image_ok, image_tag return has_file_to_upload, uploaded_to_s3, image_ok, image_tag
...@@ -371,27 +371,27 @@ class OpenEndedChild(object): ...@@ -371,27 +371,27 @@ class OpenEndedChild(object):
Makes an image tag from a given URL Makes an image tag from a given URL
@param s3_public_url: URL of the image @param s3_public_url: URL of the image
@param image_name: Name of the image @param image_name: Name of the image
@return: Boolean success, updated AJAX get data @return: Boolean success, updated AJAX data
""" """
image_template = """ image_template = """
<a href="{0}" target="_blank">{1}</a> <a href="{0}" target="_blank">{1}</a>
""".format(s3_public_url, image_name) """.format(s3_public_url, image_name)
return image_template return image_template
def append_image_to_student_answer(self, get_data): def append_image_to_student_answer(self, data):
""" """
Adds an image to a student answer after uploading it to S3 Adds an image to a student answer after uploading it to S3
@param get_data: AJAx get data @param data: AJAx data
@return: Boolean success, updated AJAX get data @return: Boolean success, updated AJAX data
""" """
overall_success = False overall_success = False
if not self.accept_file_upload: if not self.accept_file_upload:
# If the question does not accept file uploads, do not do anything # If the question does not accept file uploads, do not do anything
return True, get_data return True, data
has_file_to_upload, uploaded_to_s3, image_ok, image_tag = self.check_for_image_and_upload(get_data) has_file_to_upload, uploaded_to_s3, image_ok, image_tag = self.check_for_image_and_upload(data)
if uploaded_to_s3 and has_file_to_upload and image_ok: if uploaded_to_s3 and has_file_to_upload and image_ok:
get_data['student_answer'] += image_tag data['student_answer'] += image_tag
overall_success = True overall_success = True
elif has_file_to_upload and not uploaded_to_s3 and image_ok: elif has_file_to_upload and not uploaded_to_s3 and image_ok:
# In this case, an image was submitted by the student, but the image could not be uploaded to S3. Likely # In this case, an image was submitted by the student, but the image could not be uploaded to S3. Likely
...@@ -403,12 +403,12 @@ class OpenEndedChild(object): ...@@ -403,12 +403,12 @@ class OpenEndedChild(object):
overall_success = True overall_success = True
elif not has_file_to_upload: elif not has_file_to_upload:
# If there is no file to upload, probably the student has embedded the link in the answer text # If there is no file to upload, probably the student has embedded the link in the answer text
success, get_data['student_answer'] = self.check_for_url_in_text(get_data['student_answer']) success, data['student_answer'] = self.check_for_url_in_text(data['student_answer'])
overall_success = success overall_success = success
# log.debug("Has file: {0} Uploaded: {1} Image Ok: {2}".format(has_file_to_upload, uploaded_to_s3, image_ok)) # log.debug("Has file: {0} Uploaded: {1} Image Ok: {2}".format(has_file_to_upload, uploaded_to_s3, image_ok))
return overall_success, get_data return overall_success, data
def check_for_url_in_text(self, string): def check_for_url_in_text(self, string):
""" """
......
...@@ -75,10 +75,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -75,10 +75,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
html = system.render_template('{0}/self_assessment_prompt.html'.format(self.TEMPLATE_DIR), context) html = system.render_template('{0}/self_assessment_prompt.html'.format(self.TEMPLATE_DIR), context)
return html return html
def handle_ajax(self, dispatch, get, system): def handle_ajax(self, dispatch, data, system):
""" """
This is called by courseware.module_render, to handle an AJAX call. This is called by courseware.module_render, to handle an AJAX call.
"get" is request.POST. "data" is request.POST.
Returns a json dictionary: Returns a json dictionary:
{ 'progress_changed' : True/False, { 'progress_changed' : True/False,
...@@ -99,7 +99,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -99,7 +99,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
return json.dumps({'error': 'Error handling action. Please try again.', 'success': False}) return json.dumps({'error': 'Error handling action. Please try again.', 'success': False})
before = self.get_progress() before = self.get_progress()
d = handlers[dispatch](get, system) d = handlers[dispatch](data, system)
after = self.get_progress() after = self.get_progress()
d.update({ d.update({
'progress_changed': after != before, 'progress_changed': after != before,
...@@ -160,12 +160,12 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -160,12 +160,12 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
return system.render_template('{0}/self_assessment_hint.html'.format(self.TEMPLATE_DIR), context) return system.render_template('{0}/self_assessment_hint.html'.format(self.TEMPLATE_DIR), context)
def save_answer(self, get, system): def save_answer(self, data, system):
""" """
After the answer is submitted, show the rubric. After the answer is submitted, show the rubric.
Args: Args:
get: the GET dictionary passed to the ajax request. Should contain data: the request dictionary passed to the ajax request. Should contain
a key 'student_answer' a key 'student_answer'
Returns: Returns:
...@@ -178,16 +178,16 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -178,16 +178,16 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
return msg return msg
if self.child_state != self.INITIAL: if self.child_state != self.INITIAL:
return self.out_of_sync_error(get) return self.out_of_sync_error(data)
error_message = "" error_message = ""
# add new history element with answer and empty score and hint. # add new history element with answer and empty score and hint.
success, get = self.append_image_to_student_answer(get) success, data = self.append_image_to_student_answer(data)
if success: if success:
success, allowed_to_submit, error_message = self.check_if_student_can_submit() success, allowed_to_submit, error_message = self.check_if_student_can_submit()
if allowed_to_submit: if allowed_to_submit:
get['student_answer'] = SelfAssessmentModule.sanitize_html(get['student_answer']) data['student_answer'] = SelfAssessmentModule.sanitize_html(data['student_answer'])
self.new_history_entry(get['student_answer']) self.new_history_entry(data['student_answer'])
self.change_state(self.ASSESSING) self.change_state(self.ASSESSING)
else: else:
# Error message already defined # Error message already defined
...@@ -200,10 +200,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -200,10 +200,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
'success': success, 'success': success,
'rubric_html': self.get_rubric_html(system), 'rubric_html': self.get_rubric_html(system),
'error': error_message, 'error': error_message,
'student_response': get['student_answer'], 'student_response': data['student_answer'],
} }
def save_assessment(self, get, system): def save_assessment(self, data, _system):
""" """
Save the assessment. If the student said they're right, don't ask for a Save the assessment. If the student said they're right, don't ask for a
hint, and go straight to the done state. Otherwise, do ask for a hint. hint, and go straight to the done state. Otherwise, do ask for a hint.
...@@ -219,11 +219,11 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -219,11 +219,11 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
""" """
if self.child_state != self.ASSESSING: if self.child_state != self.ASSESSING:
return self.out_of_sync_error(get) return self.out_of_sync_error(data)
try: try:
score = int(get['assessment']) score = int(data['assessment'])
score_list = get.getlist('score_list[]') score_list = data.getlist('score_list[]')
for i in xrange(0, len(score_list)): for i in xrange(0, len(score_list)):
score_list[i] = int(score_list[i]) score_list[i] = int(score_list[i])
except ValueError: except ValueError:
...@@ -244,7 +244,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -244,7 +244,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
d['state'] = self.child_state d['state'] = self.child_state
return d return d
def save_hint(self, get, system): def save_hint(self, data, _system):
''' '''
Not used currently, as hints have been removed from the system. Not used currently, as hints have been removed from the system.
Save the hint. Save the hint.
...@@ -258,9 +258,9 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -258,9 +258,9 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
if self.child_state != self.POST_ASSESSMENT: if self.child_state != self.POST_ASSESSMENT:
# Note: because we only ask for hints on wrong answers, may not have # Note: because we only ask for hints on wrong answers, may not have
# the same number of hints and answers. # the same number of hints and answers.
return self.out_of_sync_error(get) return self.out_of_sync_error(data)
self.record_latest_post_assessment(get['hint']) self.record_latest_post_assessment(data['hint'])
self.change_state(self.DONE) self.change_state(self.DONE)
return {'success': True, return {'success': True,
......
...@@ -133,8 +133,8 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -133,8 +133,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
""" """
return {'success': False, 'error': msg} return {'success': False, 'error': msg}
def _check_required(self, get, required): def _check_required(self, data, required):
actual = set(get.keys()) actual = set(data.keys())
missing = required - actual missing = required - actual
if len(missing) > 0: if len(missing) > 0:
return False, "Missing required keys: {0}".format(', '.join(missing)) return False, "Missing required keys: {0}".format(', '.join(missing))
...@@ -153,7 +153,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -153,7 +153,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
else: else:
return self.peer_grading_problem({'location': self.link_to_location})['html'] return self.peer_grading_problem({'location': self.link_to_location})['html']
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
""" """
Needs to be implemented by child modules. Handles AJAX events. Needs to be implemented by child modules. Handles AJAX events.
@return: @return:
...@@ -173,7 +173,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -173,7 +173,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
# This is a dev_facing_error # This is a dev_facing_error
return json.dumps({'error': 'Error handling action. Please try again.', 'success': False}) return json.dumps({'error': 'Error handling action. Please try again.', 'success': False})
d = handlers[dispatch](get) d = handlers[dispatch](data)
return json.dumps(d, cls=ComplexEncoder) return json.dumps(d, cls=ComplexEncoder)
...@@ -244,7 +244,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -244,7 +244,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
max_grade = self.max_grade max_grade = self.max_grade
return max_grade return max_grade
def get_next_submission(self, get): def get_next_submission(self, data):
""" """
Makes a call to the grading controller for the next essay that should be graded Makes a call to the grading controller for the next essay that should be graded
Returns a json dict with the following keys: Returns a json dict with the following keys:
...@@ -263,11 +263,11 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -263,11 +263,11 @@ class PeerGradingModule(PeerGradingFields, XModule):
'error': if success is False, will have an error message with more info. 'error': if success is False, will have an error message with more info.
""" """
required = set(['location']) required = set(['location'])
success, message = self._check_required(get, required) success, message = self._check_required(data, required)
if not success: if not success:
return self._err_response(message) return self._err_response(message)
grader_id = self.system.anonymous_student_id grader_id = self.system.anonymous_student_id
location = get['location'] location = data['location']
try: try:
response = self.peer_gs.get_next_submission(location, grader_id) response = self.peer_gs.get_next_submission(location, grader_id)
...@@ -280,7 +280,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -280,7 +280,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
return {'success': False, return {'success': False,
'error': EXTERNAL_GRADER_NO_CONTACT_ERROR} 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR}
def save_grade(self, get): def save_grade(self, data):
""" """
Saves the grade of a given submission. Saves the grade of a given submission.
Input: Input:
...@@ -298,18 +298,18 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -298,18 +298,18 @@ class PeerGradingModule(PeerGradingFields, XModule):
required = set(['location', 'submission_id', 'submission_key', 'score', 'feedback', 'rubric_scores[]', required = set(['location', 'submission_id', 'submission_key', 'score', 'feedback', 'rubric_scores[]',
'submission_flagged']) 'submission_flagged'])
success, message = self._check_required(get, required) success, message = self._check_required(data, required)
if not success: if not success:
return self._err_response(message) return self._err_response(message)
grader_id = self.system.anonymous_student_id grader_id = self.system.anonymous_student_id
location = get.get('location') location = data.get('location')
submission_id = get.get('submission_id') submission_id = data.get('submission_id')
score = get.get('score') score = data.get('score')
feedback = get.get('feedback') feedback = data.get('feedback')
submission_key = get.get('submission_key') submission_key = data.get('submission_key')
rubric_scores = get.getlist('rubric_scores[]') rubric_scores = data.getlist('rubric_scores[]')
submission_flagged = get.get('submission_flagged') submission_flagged = data.get('submission_flagged')
try: try:
response = self.peer_gs.save_grade(location, grader_id, submission_id, response = self.peer_gs.save_grade(location, grader_id, submission_id,
...@@ -328,7 +328,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -328,7 +328,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
'error': EXTERNAL_GRADER_NO_CONTACT_ERROR 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR
} }
def is_student_calibrated(self, get): def is_student_calibrated(self, data):
""" """
Calls the grading controller to see if the given student is calibrated Calls the grading controller to see if the given student is calibrated
on the given problem on the given problem
...@@ -347,12 +347,12 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -347,12 +347,12 @@ class PeerGradingModule(PeerGradingFields, XModule):
""" """
required = set(['location']) required = set(['location'])
success, message = self._check_required(get, required) success, message = self._check_required(data, required)
if not success: if not success:
return self._err_response(message) return self._err_response(message)
grader_id = self.system.anonymous_student_id grader_id = self.system.anonymous_student_id
location = get['location'] location = data['location']
try: try:
response = self.peer_gs.is_student_calibrated(location, grader_id) response = self.peer_gs.is_student_calibrated(location, grader_id)
...@@ -367,7 +367,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -367,7 +367,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
'error': EXTERNAL_GRADER_NO_CONTACT_ERROR 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR
} }
def show_calibration_essay(self, get): def show_calibration_essay(self, data):
""" """
Fetch the next calibration essay from the grading controller and return it Fetch the next calibration essay from the grading controller and return it
Inputs: Inputs:
...@@ -392,13 +392,13 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -392,13 +392,13 @@ class PeerGradingModule(PeerGradingFields, XModule):
""" """
required = set(['location']) required = set(['location'])
success, message = self._check_required(get, required) success, message = self._check_required(data, required)
if not success: if not success:
return self._err_response(message) return self._err_response(message)
grader_id = self.system.anonymous_student_id grader_id = self.system.anonymous_student_id
location = get['location'] location = data['location']
try: try:
response = self.peer_gs.show_calibration_essay(location, grader_id) response = self.peer_gs.show_calibration_essay(location, grader_id)
return response return response
...@@ -417,8 +417,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -417,8 +417,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
return {'success': False, return {'success': False,
'error': 'Error displaying submission. Please notify course staff.'} 'error': 'Error displaying submission. Please notify course staff.'}
def save_calibration_essay(self, data):
def save_calibration_essay(self, get):
""" """
Saves the grader's grade of a given calibration. Saves the grader's grade of a given calibration.
Input: Input:
...@@ -437,17 +436,17 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -437,17 +436,17 @@ class PeerGradingModule(PeerGradingFields, XModule):
""" """
required = set(['location', 'submission_id', 'submission_key', 'score', 'feedback', 'rubric_scores[]']) required = set(['location', 'submission_id', 'submission_key', 'score', 'feedback', 'rubric_scores[]'])
success, message = self._check_required(get, required) success, message = self._check_required(data, required)
if not success: if not success:
return self._err_response(message) return self._err_response(message)
grader_id = self.system.anonymous_student_id grader_id = self.system.anonymous_student_id
location = get.get('location') location = data.get('location')
calibration_essay_id = get.get('submission_id') calibration_essay_id = data.get('submission_id')
submission_key = get.get('submission_key') submission_key = data.get('submission_key')
score = get.get('score') score = data.get('score')
feedback = get.get('feedback') feedback = data.get('feedback')
rubric_scores = get.getlist('rubric_scores[]') rubric_scores = data.getlist('rubric_scores[]')
try: try:
response = self.peer_gs.save_calibration_essay(location, grader_id, calibration_essay_id, response = self.peer_gs.save_calibration_essay(location, grader_id, calibration_essay_id,
...@@ -473,8 +472,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -473,8 +472,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
}) })
return html return html
def peer_grading(self, _data=None):
def peer_grading(self, get=None):
''' '''
Show a peer grading interface Show a peer grading interface
''' '''
...@@ -553,11 +551,11 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -553,11 +551,11 @@ class PeerGradingModule(PeerGradingFields, XModule):
return html return html
def peer_grading_problem(self, get=None): def peer_grading_problem(self, data=None):
''' '''
Show individual problem interface Show individual problem interface
''' '''
if get is None or get.get('location') is None: if data is None or data.get('location') is None:
if not self.use_for_single_location: if not self.use_for_single_location:
# This is an error case, because it must be set to use a single location to be called without get parameters # This is an error case, because it must be set to use a single location to be called without get parameters
# This is a dev_facing_error # This is a dev_facing_error
...@@ -566,8 +564,8 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -566,8 +564,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
return {'html': "", 'success': False} return {'html': "", 'success': False}
problem_location = self.link_to_location problem_location = self.link_to_location
elif get.get('location') is not None: elif data.get('location') is not None:
problem_location = get.get('location') problem_location = data.get('location')
ajax_url = self.ajax_url ajax_url = self.ajax_url
html = self.system.render_template('peer_grading/peer_grading_problem.html', { html = self.system.render_template('peer_grading/peer_grading_problem.html', {
...@@ -617,4 +615,3 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor): ...@@ -617,4 +615,3 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor):
non_editable_fields.extend([PeerGradingFields.due_date, PeerGradingFields.grace_period_string, non_editable_fields.extend([PeerGradingFields.due_date, PeerGradingFields.grace_period_string,
PeerGradingFields.max_grade]) PeerGradingFields.max_grade])
return non_editable_fields return non_editable_fields
...@@ -47,12 +47,12 @@ class PollModule(PollFields, XModule): ...@@ -47,12 +47,12 @@ class PollModule(PollFields, XModule):
css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]} css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]}
js_module_name = "Poll" js_module_name = "Poll"
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
"""Ajax handler. """Ajax handler.
Args: Args:
dispatch: string request slug dispatch: string request slug
get: dict request get parameters data: dict request data parameters
Returns: Returns:
json string json string
......
...@@ -62,10 +62,10 @@ class SequenceModule(SequenceFields, XModule): ...@@ -62,10 +62,10 @@ class SequenceModule(SequenceFields, XModule):
progress = reduce(Progress.add_counts, progresses) progress = reduce(Progress.add_counts, progresses)
return progress return progress
def handle_ajax(self, dispatch, get): # TODO: bounds checking def handle_ajax(self, dispatch, data): # TODO: bounds checking
''' get = request.POST instance ''' ''' get = request.POST instance '''
if dispatch == 'goto_position': if dispatch == 'goto_position':
self.position = int(get['position']) self.position = int(data['position'])
return json.dumps({'success': True}) return json.dumps({'success': True})
raise NotFoundError('Unexpected dispatch type') raise NotFoundError('Unexpected dispatch type')
......
...@@ -40,9 +40,9 @@ class LogicTest(unittest.TestCase): ...@@ -40,9 +40,9 @@ class LogicTest(unittest.TestCase):
self.raw_model_data self.raw_model_data
) )
def ajax_request(self, dispatch, get): def ajax_request(self, dispatch, data):
"""Call Xmodule.handle_ajax.""" """Call Xmodule.handle_ajax."""
return json.loads(self.xmodule.handle_ajax(dispatch, get)) return json.loads(self.xmodule.handle_ajax(dispatch, data))
class PollModuleTest(LogicTest): class PollModuleTest(LogicTest):
......
...@@ -98,7 +98,7 @@ class TimeLimitModule(TimeLimitFields, XModule): ...@@ -98,7 +98,7 @@ class TimeLimitModule(TimeLimitFields, XModule):
progress = reduce(Progress.add_counts, progresses) progress = reduce(Progress.add_counts, progresses)
return progress return progress
def handle_ajax(self, dispatch, get): def handle_ajax(self, _dispatch, _data):
raise NotFoundError('Unexpected dispatch type') raise NotFoundError('Unexpected dispatch type')
def render(self): def render(self):
...@@ -141,4 +141,3 @@ class TimeLimitDescriptor(TimeLimitFields, XMLEditingDescriptor, XmlDescriptor): ...@@ -141,4 +141,3 @@ class TimeLimitDescriptor(TimeLimitFields, XMLEditingDescriptor, XmlDescriptor):
xml_object.append( xml_object.append(
etree.fromstring(child.export_to_xml(resource_fs))) etree.fromstring(child.export_to_xml(resource_fs)))
return xml_object return xml_object
...@@ -54,9 +54,9 @@ class VideoModule(VideoFields, XModule): ...@@ -54,9 +54,9 @@ class VideoModule(VideoFields, XModule):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
XModule.__init__(self, *args, **kwargs) XModule.__init__(self, *args, **kwargs)
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
"""This is not being called right now and we raise 404 error.""" """This is not being called right now and we raise 404 error."""
log.debug(u"GET {0}".format(get)) log.debug(u"GET {0}".format(data))
log.debug(u"DISPATCH {0}".format(dispatch)) log.debug(u"DISPATCH {0}".format(dispatch))
raise Http404() raise Http404()
......
...@@ -125,9 +125,9 @@ class VideoAlphaModule(VideoAlphaFields, XModule): ...@@ -125,9 +125,9 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
return parse_time(xmltree.get('start_time')), parse_time(xmltree.get('end_time')) return parse_time(xmltree.get('start_time')), parse_time(xmltree.get('end_time'))
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, data):
"""This is not being called right now and we raise 404 error.""" """This is not being called right now and we raise 404 error."""
log.debug(u"GET {0}".format(get)) log.debug(u"GET {0}".format(data))
log.debug(u"DISPATCH {0}".format(dispatch)) log.debug(u"DISPATCH {0}".format(dispatch))
raise Http404() raise Http404()
......
...@@ -168,12 +168,12 @@ class WordCloudModule(WordCloudFields, XModule): ...@@ -168,12 +168,12 @@ class WordCloudModule(WordCloudFields, XModule):
)[:amount] )[:amount]
) )
def handle_ajax(self, dispatch, post): def handle_ajax(self, dispatch, data):
"""Ajax handler. """Ajax handler.
Args: Args:
dispatch: string request slug dispatch: string request slug
post: dict request get parameters data: dict request get parameters
Returns: Returns:
json string json string
...@@ -187,7 +187,7 @@ class WordCloudModule(WordCloudFields, XModule): ...@@ -187,7 +187,7 @@ class WordCloudModule(WordCloudFields, XModule):
# Student words from client. # Student words from client.
# FIXME: we must use raw JSON, not a post data (multipart/form-data) # FIXME: we must use raw JSON, not a post data (multipart/form-data)
raw_student_words = post.getlist('student_words[]') raw_student_words = data.getlist('student_words[]')
student_words = filter(None, map(self.good_word, raw_student_words)) student_words = filter(None, map(self.good_word, raw_student_words))
self.student_words = student_words self.student_words = student_words
......
...@@ -272,9 +272,9 @@ class XModule(XModuleFields, HTMLSnippet, XBlock): ...@@ -272,9 +272,9 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
''' '''
return None return None
def handle_ajax(self, _dispatch, _get): def handle_ajax(self, _dispatch, _data):
''' dispatch is last part of the URL. ''' dispatch is last part of the URL.
get is a dictionary-like object ''' data is a dictionary-like object with the content of the request'''
return "" return ""
......
...@@ -223,7 +223,7 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours ...@@ -223,7 +223,7 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
relative_xqueue_callback_url = reverse('xqueue_callback', relative_xqueue_callback_url = reverse('xqueue_callback',
kwargs=dict(course_id=course_id, kwargs=dict(course_id=course_id,
userid=str(user.id), userid=str(user.id),
id=descriptor.location.url(), mod_id=descriptor.location.url(),
dispatch=dispatch), dispatch=dispatch),
) )
return xqueue_callback_url_prefix + relative_xqueue_callback_url return xqueue_callback_url_prefix + relative_xqueue_callback_url
...@@ -399,40 +399,47 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours ...@@ -399,40 +399,47 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
@csrf_exempt @csrf_exempt
def xqueue_callback(request, course_id, userid, id, dispatch): def xqueue_callback(request, course_id, userid, mod_id, dispatch):
''' '''
Entry point for graded results from the queueing system. Entry point for graded results from the queueing system.
''' '''
data = request.POST.copy()
# Test xqueue package, which we expect to be: # Test xqueue package, which we expect to be:
# xpackage = {'xqueue_header': json.dumps({'lms_key':'secretkey',...}), # xpackage = {'xqueue_header': json.dumps({'lms_key':'secretkey',...}),
# 'xqueue_body' : 'Message from grader'} # 'xqueue_body' : 'Message from grader'}
get = request.POST.copy()
for key in ['xqueue_header', 'xqueue_body']: for key in ['xqueue_header', 'xqueue_body']:
if not get.has_key(key): if key not in data:
raise Http404 raise Http404
header = json.loads(get['xqueue_header'])
if not isinstance(header, dict) or not header.has_key('lms_key'): header = json.loads(data['xqueue_header'])
if not isinstance(header, dict) or 'lms_key' not in header:
raise Http404 raise Http404
# Retrieve target StudentModule # Retrieve target StudentModule
user = User.objects.get(id=userid) user = User.objects.get(id=userid)
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(course_id, course_id,
user, modulestore().get_instance(course_id, id), depth=0, select_for_update=True) user,
instance = get_module(user, request, id, model_data_cache, course_id, grade_bucket_type='xqueue') modulestore().get_instance(course_id, mod_id),
depth=0,
select_for_update=True
)
instance = get_module(user, request, mod_id, model_data_cache, course_id, grade_bucket_type='xqueue')
if instance is None: if instance is None:
log.debug("No module {0} for user {1}--access denied?".format(id, user)) msg = "No module {0} for user {1}--access denied?".format(mod_id, user)
log.debug(msg)
raise Http404 raise Http404
# Transfer 'queuekey' from xqueue response header to 'get'. This is required to # Transfer 'queuekey' from xqueue response header to the data.
# use the interface defined by 'handle_ajax' # This is required to use the interface defined by 'handle_ajax'
get.update({'queuekey': header['lms_key']}) data.update({'queuekey': header['lms_key']})
# We go through the "AJAX" path # We go through the "AJAX" path
# So far, the only dispatch from xqueue will be 'score_update' # So far, the only dispatch from xqueue will be 'score_update'
try: try:
# Can ignore the return value--not used for xqueue_callback # Can ignore the return value--not used for xqueue_callback
instance.handle_ajax(dispatch, get) instance.handle_ajax(dispatch, data)
except: except:
log.exception("error processing ajax call") log.exception("error processing ajax call")
raise raise
...@@ -466,23 +473,15 @@ def modx_dispatch(request, dispatch, location, course_id): ...@@ -466,23 +473,15 @@ def modx_dispatch(request, dispatch, location, course_id):
if not request.user.is_authenticated(): if not request.user.is_authenticated():
raise PermissionDenied raise PermissionDenied
# Check for submitted files and basic file size checks # Get the submitted data
p = request.POST.copy() data = request.POST.copy()
if request.FILES:
for fileinput_id in request.FILES.keys():
inputfiles = request.FILES.getlist(fileinput_id)
if len(inputfiles) > settings.MAX_FILEUPLOADS_PER_INPUT: # Get and check submitted files
too_many_files_msg = 'Submission aborted! Maximum %d files may be submitted at once' % \ files = request.FILES or {}
settings.MAX_FILEUPLOADS_PER_INPUT error_msg = _check_files_limits(files)
return HttpResponse(json.dumps({'success': too_many_files_msg})) if error_msg:
return HttpResponse(json.dumps({'success': error_msg}))
for inputfile in inputfiles: data.update(files) # Merge files into data dictionary
if inputfile.size > settings.STUDENT_FILEUPLOAD_MAX_SIZE: # Bytes
file_too_big_msg = 'Submission aborted! Your file "%s" is too large (max size: %d MB)' % \
(inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))
return HttpResponse(json.dumps({'success': file_too_big_msg}))
p[fileinput_id] = inputfiles
try: try:
descriptor = modulestore().get_instance(course_id, location) descriptor = modulestore().get_instance(course_id, location)
...@@ -495,8 +494,11 @@ def modx_dispatch(request, dispatch, location, course_id): ...@@ -495,8 +494,11 @@ def modx_dispatch(request, dispatch, location, course_id):
) )
raise Http404 raise Http404
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(course_id, model_data_cache = ModelDataCache.cache_for_descriptor_descendents(
request.user, descriptor) course_id,
request.user,
descriptor
)
instance = get_module(request.user, request, location, model_data_cache, course_id, grade_bucket_type='ajax') instance = get_module(request.user, request, location, model_data_cache, course_id, grade_bucket_type='ajax')
if instance is None: if instance is None:
...@@ -507,7 +509,7 @@ def modx_dispatch(request, dispatch, location, course_id): ...@@ -507,7 +509,7 @@ def modx_dispatch(request, dispatch, location, course_id):
# Let the module handle the AJAX # Let the module handle the AJAX
try: try:
ajax_return = instance.handle_ajax(dispatch, p) ajax_return = instance.handle_ajax(dispatch, data)
# If we can't find the module, respond with a 404 # If we can't find the module, respond with a 404
except NotFoundError: except NotFoundError:
...@@ -529,7 +531,6 @@ def modx_dispatch(request, dispatch, location, course_id): ...@@ -529,7 +531,6 @@ def modx_dispatch(request, dispatch, location, course_id):
return HttpResponse(ajax_return) return HttpResponse(ajax_return)
def get_score_bucket(grade, max_grade): def get_score_bucket(grade, max_grade):
""" """
Function to split arbitrary score ranges into 3 buckets. Function to split arbitrary score ranges into 3 buckets.
...@@ -542,3 +543,30 @@ def get_score_bucket(grade, max_grade): ...@@ -542,3 +543,30 @@ def get_score_bucket(grade, max_grade):
score_bucket = "correct" score_bucket = "correct"
return score_bucket return score_bucket
def _check_files_limits(files):
"""
Check if the files in a request are under the limits defined by
`settings.MAX_FILEUPLOADS_PER_INPUT` and
`settings.STUDENT_FILEUPLOAD_MAX_SIZE`.
Returns None if files are correct or an error messages otherwise.
"""
for fileinput_id in files.keys():
inputfiles = files.getlist(fileinput_id)
# Check number of files submitted
if len(inputfiles) > settings.MAX_FILEUPLOADS_PER_INPUT:
msg = 'Submission aborted! Maximum %d files may be submitted at once' %\
settings.MAX_FILEUPLOADS_PER_INPUT
return msg
# Check file sizes
for inputfile in inputfiles:
if inputfile.size > settings.STUDENT_FILEUPLOAD_MAX_SIZE: # Bytes
msg = 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %\
(inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))
return msg
return None
...@@ -188,7 +188,7 @@ if settings.COURSEWARE_ENABLED: ...@@ -188,7 +188,7 @@ if settings.COURSEWARE_ENABLED:
# into the database. # into the database.
url(r'^software-licenses$', 'licenses.views.user_software_license', name="user_software_license"), url(r'^software-licenses$', 'licenses.views.user_software_license', name="user_software_license"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xqueue/(?P<userid>[^/]*)/(?P<id>.*?)/(?P<dispatch>[^/]*)$', url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xqueue/(?P<userid>[^/]*)/(?P<mod_id>.*?)/(?P<dispatch>[^/]*)$',
'courseware.module_render.xqueue_callback', 'courseware.module_render.xqueue_callback',
name='xqueue_callback'), name='xqueue_callback'),
url(r'^change_setting$', 'student.views.change_setting', url(r'^change_setting$', 'student.views.change_setting',
...@@ -438,5 +438,3 @@ if settings.DEBUG: ...@@ -438,5 +438,3 @@ if settings.DEBUG:
#Custom error pages #Custom error pages
handler404 = 'static_template_view.views.render_404' handler404 = 'static_template_view.views.render_404'
handler500 = 'static_template_view.views.render_500' handler500 = 'static_template_view.views.render_500'
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