Rename arguments of modx_dispatch and handle_ajax related functions
Refactor a bit modx_dispatch
This commit is contained in:
@@ -373,7 +373,7 @@ class LoncapaProblem(object):
|
||||
html = contextualize_text(etree.tostring(self._extract_html(self.tree)), self.context)
|
||||
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
|
||||
|
||||
@@ -381,10 +381,10 @@ class LoncapaProblem(object):
|
||||
'''
|
||||
|
||||
# pull out the id
|
||||
input_id = get['input_id']
|
||||
input_id = data['input_id']
|
||||
if self.inputs[input_id]:
|
||||
dispatch = get['dispatch']
|
||||
return self.inputs[input_id].handle_ajax(dispatch, get)
|
||||
dispatch = data['dispatch']
|
||||
return self.inputs[input_id].handle_ajax(dispatch, data)
|
||||
else:
|
||||
log.warning("Could not find matching input for id: %s" % input_id)
|
||||
return {}
|
||||
|
||||
@@ -223,13 +223,13 @@ class InputTypeBase(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def handle_ajax(self, dispatch, get):
|
||||
def handle_ajax(self, dispatch, data):
|
||||
"""
|
||||
InputTypes that need to handle specialized AJAX should override this.
|
||||
|
||||
Input:
|
||||
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:
|
||||
a dictionary object that can be serialized into JSON. This will be sent back to the Javascript.
|
||||
@@ -677,20 +677,20 @@ class MatlabInput(CodeInput):
|
||||
self.queue_len = 1
|
||||
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
|
||||
|
||||
Args:
|
||||
- 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:
|
||||
dict - 'success' - whether or not we successfully queued this submission
|
||||
- 'message' - message to be rendered in case of error
|
||||
'''
|
||||
|
||||
if dispatch == 'plot':
|
||||
return self._plot_data(get)
|
||||
return self._plot_data(data)
|
||||
return {}
|
||||
|
||||
def ungraded_response(self, queue_msg, queuekey):
|
||||
@@ -751,7 +751,7 @@ class MatlabInput(CodeInput):
|
||||
msg = result['msg']
|
||||
return msg
|
||||
|
||||
def _plot_data(self, get):
|
||||
def _plot_data(self, data):
|
||||
'''
|
||||
AJAX handler for the plot button
|
||||
Args:
|
||||
@@ -765,7 +765,7 @@ class MatlabInput(CodeInput):
|
||||
return {'success': False, 'message': 'Cannot connect to the queue'}
|
||||
|
||||
# pull relevant info out of get
|
||||
response = get['submission']
|
||||
response = data['submission']
|
||||
|
||||
# construct xqueue headers
|
||||
qinterface = self.system.xqueue['interface']
|
||||
@@ -951,16 +951,16 @@ class ChemicalEquationInput(InputTypeBase):
|
||||
"""
|
||||
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
|
||||
matches the corresponding dispatch and send it through if it does
|
||||
'''
|
||||
if dispatch == 'preview_chemcalc':
|
||||
return self.preview_chemcalc(get)
|
||||
return self.preview_chemcalc(data)
|
||||
return {}
|
||||
|
||||
def preview_chemcalc(self, get):
|
||||
def preview_chemcalc(self, data):
|
||||
"""
|
||||
Render an html preview of a chemical formula or equation. get should
|
||||
contain a key 'formula' and value 'some formula string'.
|
||||
@@ -974,7 +974,7 @@ class ChemicalEquationInput(InputTypeBase):
|
||||
|
||||
result = {'preview': '',
|
||||
'error': ''}
|
||||
formula = get['formula']
|
||||
formula = data['formula']
|
||||
if formula is None:
|
||||
result['error'] = "No formula specified."
|
||||
return result
|
||||
|
||||
@@ -467,8 +467,8 @@ class MatlabTest(unittest.TestCase):
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
def test_plot_data(self):
|
||||
get = {'submission': 'x = 1234;'}
|
||||
response = self.the_input.handle_ajax("plot", get)
|
||||
data = {'submission': 'x = 1234;'}
|
||||
response = self.the_input.handle_ajax("plot", data)
|
||||
|
||||
test_system().xqueue['interface'].send_to_queue.assert_called_with(header=ANY, body=ANY)
|
||||
|
||||
@@ -477,10 +477,10 @@ class MatlabTest(unittest.TestCase):
|
||||
self.assertEqual(self.the_input.input_state['queuestate'], 'queued')
|
||||
|
||||
def test_plot_data_failure(self):
|
||||
get = {'submission': 'x = 1234;'}
|
||||
data = {'submission': 'x = 1234;'}
|
||||
error_message = '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.assertEqual(response['message'], error_message)
|
||||
self.assertTrue('queuekey' not in self.the_input.input_state)
|
||||
|
||||
@@ -519,11 +519,11 @@ class CapaModule(CapaFields, XModule):
|
||||
# now do the substitutions which are filesystem based, e.g. '/static/' prefixes
|
||||
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.
|
||||
|
||||
`get` is request.POST.
|
||||
`data` is request.POST.
|
||||
|
||||
Returns a json dictionary:
|
||||
{ 'progress_changed' : True/False,
|
||||
@@ -547,18 +547,19 @@ class CapaModule(CapaFields, XModule):
|
||||
before = self.get_progress()
|
||||
|
||||
try:
|
||||
d = handlers[dispatch](get)
|
||||
|
||||
result = handlers[dispatch](data)
|
||||
except Exception as err:
|
||||
_, _, traceback_obj = sys.exc_info()
|
||||
raise ProcessingError, err.message, traceback_obj
|
||||
raise ProcessingError(err.message, traceback_obj)
|
||||
|
||||
after = self.get_progress()
|
||||
d.update({
|
||||
|
||||
result.update({
|
||||
'progress_changed': after != before,
|
||||
'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):
|
||||
"""
|
||||
@@ -633,32 +634,32 @@ class CapaModule(CapaFields, XModule):
|
||||
|
||||
return False
|
||||
|
||||
def update_score(self, get):
|
||||
def update_score(self, data):
|
||||
"""
|
||||
Delivers grading response (e.g. from asynchronous code checking) to
|
||||
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
|
||||
|
||||
No ajax return is needed. Return empty dict.
|
||||
"""
|
||||
queuekey = get['queuekey']
|
||||
score_msg = get['xqueue_body']
|
||||
queuekey = data['queuekey']
|
||||
score_msg = data['xqueue_body']
|
||||
self.lcp.update_score(score_msg, queuekey)
|
||||
self.set_state_from_lcp()
|
||||
self.publish_grade()
|
||||
|
||||
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
|
||||
|
||||
The score of the problem will not be updated
|
||||
|
||||
Args:
|
||||
- get (dict) must contain keys:
|
||||
- data (dict) must contain keys:
|
||||
queuekey - a key specific to this response
|
||||
xqueue_body - the body of the response
|
||||
Returns:
|
||||
@@ -666,28 +667,30 @@ class CapaModule(CapaFields, XModule):
|
||||
|
||||
No ajax return is needed, so an empty dict is returned
|
||||
"""
|
||||
queuekey = get['queuekey']
|
||||
score_msg = get['xqueue_body']
|
||||
queuekey = data['queuekey']
|
||||
score_msg = data['xqueue_body']
|
||||
|
||||
# pass along the xqueue message to the problem
|
||||
self.lcp.ungraded_response(score_msg, queuekey)
|
||||
self.set_state_from_lcp()
|
||||
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
|
||||
|
||||
Args:
|
||||
- get (dict) - data that should be passed to the input
|
||||
- data (dict) - data that should be passed to the input
|
||||
Returns:
|
||||
- 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
|
||||
self.set_state_from_lcp()
|
||||
return response
|
||||
|
||||
def get_answer(self, get):
|
||||
def get_answer(self, data):
|
||||
"""
|
||||
For the "show answer" button.
|
||||
|
||||
@@ -717,10 +720,9 @@ class CapaModule(CapaFields, XModule):
|
||||
return {'answers': new_answers}
|
||||
|
||||
# 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.
|
||||
|
||||
{ 'html': <the-html> }
|
||||
|
||||
Used if we want to reconfirm we have the right thing e.g. after
|
||||
@@ -729,27 +731,27 @@ class CapaModule(CapaFields, XModule):
|
||||
return {'html': self.get_problem_html(encapsulate=False)}
|
||||
|
||||
@staticmethod
|
||||
def make_dict_of_responses(get):
|
||||
def make_dict_of_responses(data):
|
||||
"""
|
||||
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,
|
||||
'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
|
||||
(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.
|
||||
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'] }
|
||||
(the value is a list).
|
||||
|
||||
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)
|
||||
|
||||
-Two keys end up with the same name in the returned dict.
|
||||
@@ -758,7 +760,7 @@ class CapaModule(CapaFields, XModule):
|
||||
"""
|
||||
answers = dict()
|
||||
|
||||
for key in get:
|
||||
for key in data:
|
||||
# e.g. input_resistor_1 ==> resistor_1
|
||||
_, _, name = key.partition('_')
|
||||
|
||||
@@ -777,9 +779,9 @@ class CapaModule(CapaFields, XModule):
|
||||
name = name[:-2] if is_list_key else name
|
||||
|
||||
if is_list_key:
|
||||
val = get.getlist(key)
|
||||
val = data.getlist(key)
|
||||
else:
|
||||
val = get[key]
|
||||
val = data[key]
|
||||
|
||||
# If the name already exists, then we don't want
|
||||
# to override it. Raise an error instead
|
||||
@@ -801,7 +803,7 @@ class CapaModule(CapaFields, XModule):
|
||||
'max_value': score['total'],
|
||||
})
|
||||
|
||||
def check_problem(self, get):
|
||||
def check_problem(self, data):
|
||||
"""
|
||||
Checks whether answers to a problem are correct
|
||||
|
||||
@@ -813,8 +815,9 @@ class CapaModule(CapaFields, XModule):
|
||||
event_info['state'] = self.lcp.get_state()
|
||||
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)
|
||||
|
||||
# Too late. Cannot submit
|
||||
if self.closed():
|
||||
event_info['failure'] = 'closed'
|
||||
@@ -972,7 +975,7 @@ class CapaModule(CapaFields, XModule):
|
||||
|
||||
return {'success': success}
|
||||
|
||||
def save_problem(self, get):
|
||||
def save_problem(self, data):
|
||||
"""
|
||||
Save the passed in answers.
|
||||
Returns a dict { 'success' : bool, 'msg' : message }
|
||||
@@ -982,7 +985,7 @@ class CapaModule(CapaFields, XModule):
|
||||
event_info['state'] = self.lcp.get_state()
|
||||
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
|
||||
|
||||
# Too late. Cannot submit
|
||||
@@ -1011,7 +1014,7 @@ class CapaModule(CapaFields, XModule):
|
||||
return {'success': True,
|
||||
'msg': msg}
|
||||
|
||||
def reset_problem(self, get):
|
||||
def reset_problem(self, _data):
|
||||
"""
|
||||
Changes problem state to unfinished -- removes student answers,
|
||||
and causes problem to rerender itself.
|
||||
|
||||
@@ -204,9 +204,9 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule):
|
||||
return_value = self.child_module.get_html()
|
||||
return return_value
|
||||
|
||||
def handle_ajax(self, dispatch, get):
|
||||
def handle_ajax(self, dispatch, 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()
|
||||
return return_value
|
||||
|
||||
@@ -266,4 +266,3 @@ class CombinedOpenEndedDescriptor(CombinedOpenEndedFields, RawDescriptor):
|
||||
non_editable_fields.extend([CombinedOpenEndedDescriptor.due, CombinedOpenEndedDescriptor.graceperiod,
|
||||
CombinedOpenEndedDescriptor.markdown, CombinedOpenEndedDescriptor.version])
|
||||
return non_editable_fields
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ class ConditionalModule(ConditionalFields, XModule):
|
||||
'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
|
||||
an AJAX call.
|
||||
"""
|
||||
|
||||
@@ -500,10 +500,10 @@ class CombinedOpenEndedV1Module():
|
||||
pass
|
||||
return return_html
|
||||
|
||||
def get_rubric(self, get):
|
||||
def get_rubric(self, _data):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
all_responses = []
|
||||
@@ -532,10 +532,10 @@ class CombinedOpenEndedV1Module():
|
||||
html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
|
||||
return {'html': html, 'success': True}
|
||||
|
||||
def get_legend(self, get):
|
||||
def get_legend(self, _data):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
context = {
|
||||
@@ -544,10 +544,10 @@ class CombinedOpenEndedV1Module():
|
||||
html = self.system.render_template('{0}/combined_open_ended_legend.html'.format(self.TEMPLATE_DIR), context)
|
||||
return {'html': html, 'success': True}
|
||||
|
||||
def get_results(self, get):
|
||||
def get_results(self, _data):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
self.update_task_states()
|
||||
@@ -588,19 +588,19 @@ class CombinedOpenEndedV1Module():
|
||||
html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
|
||||
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.
|
||||
Input: AJAX get dictionary
|
||||
Input: AJAX data dictionary
|
||||
Output: Dictionary to be rendered via ajax that contains the result html.
|
||||
"""
|
||||
html = self.get_status(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.
|
||||
"get" is request.POST.
|
||||
"data" is request.POST.
|
||||
|
||||
Returns a json dictionary:
|
||||
{ 'progress_changed' : True/False,
|
||||
@@ -618,30 +618,30 @@ class CombinedOpenEndedV1Module():
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
d = handlers[dispatch](get)
|
||||
d = handlers[dispatch](data)
|
||||
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.
|
||||
Input: AJAX get request.
|
||||
Input: AJAX data request.
|
||||
Output: Dictionary to be rendered
|
||||
"""
|
||||
self.update_task_states()
|
||||
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.
|
||||
Input: AJAX get dictionary
|
||||
Input: AJAX data dictionary
|
||||
Output: AJAX dictionary to tbe rendered
|
||||
"""
|
||||
if self.state != self.DONE:
|
||||
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:
|
||||
return {
|
||||
@@ -789,13 +789,13 @@ class CombinedOpenEndedV1Module():
|
||||
|
||||
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.
|
||||
"""
|
||||
#This is a dev_facing_error
|
||||
log.warning("Combined module state out sync. state: %r, get: %r. %s",
|
||||
self.state, get, msg)
|
||||
log.warning("Combined module state out sync. state: %r, data: %r. %s",
|
||||
self.state, data, msg)
|
||||
#This is a student_facing_error
|
||||
return {'success': False,
|
||||
'error': 'The problem state got out-of-sync. Please try reloading the page.'}
|
||||
|
||||
@@ -122,17 +122,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
|
||||
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
|
||||
@param get: AJAX dictionary
|
||||
@param data: AJAX dictionary
|
||||
@param system: ModuleSystem
|
||||
@return: Success indicator
|
||||
"""
|
||||
self.child_state = self.DONE
|
||||
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)
|
||||
Returns a boolean success/fail and an error message
|
||||
@@ -141,7 +141,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
event_info = dict()
|
||||
event_info['problem_id'] = self.location_string
|
||||
event_info['student_id'] = system.anonymous_student_id
|
||||
event_info['survey_responses'] = get
|
||||
event_info['survey_responses'] = data
|
||||
|
||||
survey_responses = event_info['survey_responses']
|
||||
for tag in ['feedback', 'submission_id', 'grader_id', 'score']:
|
||||
@@ -587,10 +587,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
html = system.render_template('{0}/open_ended_evaluation.html'.format(self.TEMPLATE_DIR), context)
|
||||
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.
|
||||
"get" is request.POST.
|
||||
"data" is request.POST.
|
||||
|
||||
Returns a json dictionary:
|
||||
{ 'progress_changed' : True/False,
|
||||
@@ -612,7 +612,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
return json.dumps({'error': 'Error handling action. Please try again.', 'success': False})
|
||||
|
||||
before = self.get_progress()
|
||||
d = handlers[dispatch](get, system)
|
||||
d = handlers[dispatch](data, system)
|
||||
after = self.get_progress()
|
||||
d.update({
|
||||
'progress_changed': after != before,
|
||||
@@ -620,20 +620,20 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
})
|
||||
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.
|
||||
@param get: AJAX get dictionary
|
||||
@param data: AJAX dictionary
|
||||
@param system: Modulesystem (needed to align with other ajax functions)
|
||||
@return: Returns the current state
|
||||
"""
|
||||
state = self.child_state
|
||||
return {'state': state}
|
||||
|
||||
def save_answer(self, get, system):
|
||||
def save_answer(self, data, system):
|
||||
"""
|
||||
Saves a student answer
|
||||
@param get: AJAX get dictionary
|
||||
@param data: AJAX dictionary
|
||||
@param system: modulesystem
|
||||
@return: Success indicator
|
||||
"""
|
||||
@@ -644,17 +644,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
return msg
|
||||
|
||||
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.
|
||||
success, get = self.append_image_to_student_answer(get)
|
||||
success, data = self.append_image_to_student_answer(data)
|
||||
error_message = ""
|
||||
if success:
|
||||
success, allowed_to_submit, error_message = self.check_if_student_can_submit()
|
||||
if allowed_to_submit:
|
||||
get['student_answer'] = OpenEndedModule.sanitize_html(get['student_answer'])
|
||||
self.new_history_entry(get['student_answer'])
|
||||
self.send_to_grader(get['student_answer'], system)
|
||||
data['student_answer'] = OpenEndedModule.sanitize_html(data['student_answer'])
|
||||
self.new_history_entry(data['student_answer'])
|
||||
self.send_to_grader(data['student_answer'], system)
|
||||
self.change_state(self.ASSESSING)
|
||||
else:
|
||||
# Error message already defined
|
||||
@@ -666,17 +666,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
return {
|
||||
'success': success,
|
||||
'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.
|
||||
Input: AJAX get dictionary, modulesystem
|
||||
Input: AJAX data dictionary, modulesystem
|
||||
Output: None
|
||||
"""
|
||||
queuekey = get['queuekey']
|
||||
score_msg = get['xqueue_body']
|
||||
queuekey = data['queuekey']
|
||||
score_msg = data['xqueue_body']
|
||||
# TODO: Remove need for cmap
|
||||
self._update_score(score_msg, queuekey, system)
|
||||
|
||||
|
||||
@@ -272,13 +272,13 @@ class OpenEndedChild(object):
|
||||
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.
|
||||
"""
|
||||
# This is a dev_facing_error
|
||||
log.warning("Open ended child state out sync. state: %r, get: %r. %s",
|
||||
self.child_state, get, msg)
|
||||
log.warning("Open ended child state out sync. state: %r, data: %r. %s",
|
||||
self.child_state, data, msg)
|
||||
# This is a student_facing_error
|
||||
return {'success': False,
|
||||
'error': 'The problem state got out-of-sync. Please try reloading the page.'}
|
||||
@@ -345,24 +345,24 @@ class OpenEndedChild(object):
|
||||
|
||||
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
|
||||
@param get_data: AJAX get data
|
||||
@return: Success, whether or not a file was in the get dictionary,
|
||||
@param data: AJAX data
|
||||
@return: Success, whether or not a file was in the data dictionary,
|
||||
and the html corresponding to the uploaded image
|
||||
"""
|
||||
has_file_to_upload = False
|
||||
uploaded_to_s3 = False
|
||||
image_tag = ""
|
||||
image_ok = False
|
||||
if 'can_upload_files' in get_data:
|
||||
if get_data['can_upload_files'] in ['true', '1']:
|
||||
if 'can_upload_files' in data:
|
||||
if data['can_upload_files'] in ['true', '1']:
|
||||
has_file_to_upload = True
|
||||
file = get_data['student_file'][0]
|
||||
uploaded_to_s3, image_ok, s3_public_url = self.upload_image_to_s3(file)
|
||||
student_file = data['student_file'][0]
|
||||
uploaded_to_s3, image_ok, s3_public_url = self.upload_image_to_s3(student_file)
|
||||
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
|
||||
|
||||
@@ -371,27 +371,27 @@ class OpenEndedChild(object):
|
||||
Makes an image tag from a given URL
|
||||
@param s3_public_url: URL 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 = """
|
||||
<a href="{0}" target="_blank">{1}</a>
|
||||
""".format(s3_public_url, image_name)
|
||||
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
|
||||
@param get_data: AJAx get data
|
||||
@return: Boolean success, updated AJAX get data
|
||||
@param data: AJAx data
|
||||
@return: Boolean success, updated AJAX data
|
||||
"""
|
||||
overall_success = False
|
||||
if not self.accept_file_upload:
|
||||
# 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:
|
||||
get_data['student_answer'] += image_tag
|
||||
data['student_answer'] += image_tag
|
||||
overall_success = True
|
||||
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
|
||||
@@ -403,12 +403,12 @@ class OpenEndedChild(object):
|
||||
overall_success = True
|
||||
elif not has_file_to_upload:
|
||||
# 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
|
||||
|
||||
# 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):
|
||||
"""
|
||||
|
||||
@@ -75,10 +75,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
html = system.render_template('{0}/self_assessment_prompt.html'.format(self.TEMPLATE_DIR), context)
|
||||
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.
|
||||
"get" is request.POST.
|
||||
"data" is request.POST.
|
||||
|
||||
Returns a json dictionary:
|
||||
{ 'progress_changed' : True/False,
|
||||
@@ -99,7 +99,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
return json.dumps({'error': 'Error handling action. Please try again.', 'success': False})
|
||||
|
||||
before = self.get_progress()
|
||||
d = handlers[dispatch](get, system)
|
||||
d = handlers[dispatch](data, system)
|
||||
after = self.get_progress()
|
||||
d.update({
|
||||
'progress_changed': after != before,
|
||||
@@ -160,12 +160,12 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
|
||||
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.
|
||||
|
||||
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'
|
||||
|
||||
Returns:
|
||||
@@ -178,16 +178,16 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
return msg
|
||||
|
||||
if self.child_state != self.INITIAL:
|
||||
return self.out_of_sync_error(get)
|
||||
return self.out_of_sync_error(data)
|
||||
|
||||
error_message = ""
|
||||
# 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:
|
||||
success, allowed_to_submit, error_message = self.check_if_student_can_submit()
|
||||
if allowed_to_submit:
|
||||
get['student_answer'] = SelfAssessmentModule.sanitize_html(get['student_answer'])
|
||||
self.new_history_entry(get['student_answer'])
|
||||
data['student_answer'] = SelfAssessmentModule.sanitize_html(data['student_answer'])
|
||||
self.new_history_entry(data['student_answer'])
|
||||
self.change_state(self.ASSESSING)
|
||||
else:
|
||||
# Error message already defined
|
||||
@@ -200,10 +200,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
'success': success,
|
||||
'rubric_html': self.get_rubric_html(system),
|
||||
'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
|
||||
hint, and go straight to the done state. Otherwise, do ask for a hint.
|
||||
@@ -219,11 +219,11 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
"""
|
||||
|
||||
if self.child_state != self.ASSESSING:
|
||||
return self.out_of_sync_error(get)
|
||||
return self.out_of_sync_error(data)
|
||||
|
||||
try:
|
||||
score = int(get['assessment'])
|
||||
score_list = get.getlist('score_list[]')
|
||||
score = int(data['assessment'])
|
||||
score_list = data.getlist('score_list[]')
|
||||
for i in xrange(0, len(score_list)):
|
||||
score_list[i] = int(score_list[i])
|
||||
except ValueError:
|
||||
@@ -244,7 +244,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
d['state'] = self.child_state
|
||||
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.
|
||||
Save the hint.
|
||||
@@ -258,9 +258,9 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
if self.child_state != self.POST_ASSESSMENT:
|
||||
# Note: because we only ask for hints on wrong answers, may not have
|
||||
# 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)
|
||||
|
||||
return {'success': True,
|
||||
|
||||
@@ -133,8 +133,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
"""
|
||||
return {'success': False, 'error': msg}
|
||||
|
||||
def _check_required(self, get, required):
|
||||
actual = set(get.keys())
|
||||
def _check_required(self, data, required):
|
||||
actual = set(data.keys())
|
||||
missing = required - actual
|
||||
if len(missing) > 0:
|
||||
return False, "Missing required keys: {0}".format(', '.join(missing))
|
||||
@@ -153,7 +153,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
else:
|
||||
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.
|
||||
@return:
|
||||
@@ -173,7 +173,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
# This is a dev_facing_error
|
||||
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)
|
||||
|
||||
@@ -244,7 +244,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
max_grade = self.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
|
||||
Returns a json dict with the following keys:
|
||||
@@ -263,11 +263,11 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
'error': if success is False, will have an error message with more info.
|
||||
"""
|
||||
required = set(['location'])
|
||||
success, message = self._check_required(get, required)
|
||||
success, message = self._check_required(data, required)
|
||||
if not success:
|
||||
return self._err_response(message)
|
||||
grader_id = self.system.anonymous_student_id
|
||||
location = get['location']
|
||||
location = data['location']
|
||||
|
||||
try:
|
||||
response = self.peer_gs.get_next_submission(location, grader_id)
|
||||
@@ -280,7 +280,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
return {'success': False,
|
||||
'error': EXTERNAL_GRADER_NO_CONTACT_ERROR}
|
||||
|
||||
def save_grade(self, get):
|
||||
def save_grade(self, data):
|
||||
"""
|
||||
Saves the grade of a given submission.
|
||||
Input:
|
||||
@@ -298,18 +298,18 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
|
||||
required = set(['location', 'submission_id', 'submission_key', 'score', 'feedback', 'rubric_scores[]',
|
||||
'submission_flagged'])
|
||||
success, message = self._check_required(get, required)
|
||||
success, message = self._check_required(data, required)
|
||||
if not success:
|
||||
return self._err_response(message)
|
||||
grader_id = self.system.anonymous_student_id
|
||||
|
||||
location = get.get('location')
|
||||
submission_id = get.get('submission_id')
|
||||
score = get.get('score')
|
||||
feedback = get.get('feedback')
|
||||
submission_key = get.get('submission_key')
|
||||
rubric_scores = get.getlist('rubric_scores[]')
|
||||
submission_flagged = get.get('submission_flagged')
|
||||
location = data.get('location')
|
||||
submission_id = data.get('submission_id')
|
||||
score = data.get('score')
|
||||
feedback = data.get('feedback')
|
||||
submission_key = data.get('submission_key')
|
||||
rubric_scores = data.getlist('rubric_scores[]')
|
||||
submission_flagged = data.get('submission_flagged')
|
||||
|
||||
try:
|
||||
response = self.peer_gs.save_grade(location, grader_id, submission_id,
|
||||
@@ -328,7 +328,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
'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
|
||||
on the given problem
|
||||
@@ -347,12 +347,12 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
"""
|
||||
|
||||
required = set(['location'])
|
||||
success, message = self._check_required(get, required)
|
||||
success, message = self._check_required(data, required)
|
||||
if not success:
|
||||
return self._err_response(message)
|
||||
grader_id = self.system.anonymous_student_id
|
||||
|
||||
location = get['location']
|
||||
location = data['location']
|
||||
|
||||
try:
|
||||
response = self.peer_gs.is_student_calibrated(location, grader_id)
|
||||
@@ -367,7 +367,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
'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
|
||||
Inputs:
|
||||
@@ -392,13 +392,13 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
"""
|
||||
|
||||
required = set(['location'])
|
||||
success, message = self._check_required(get, required)
|
||||
success, message = self._check_required(data, required)
|
||||
if not success:
|
||||
return self._err_response(message)
|
||||
|
||||
grader_id = self.system.anonymous_student_id
|
||||
|
||||
location = get['location']
|
||||
location = data['location']
|
||||
try:
|
||||
response = self.peer_gs.show_calibration_essay(location, grader_id)
|
||||
return response
|
||||
@@ -417,8 +417,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
return {'success': False,
|
||||
'error': 'Error displaying submission. Please notify course staff.'}
|
||||
|
||||
|
||||
def save_calibration_essay(self, get):
|
||||
def save_calibration_essay(self, data):
|
||||
"""
|
||||
Saves the grader's grade of a given calibration.
|
||||
Input:
|
||||
@@ -437,17 +436,17 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
"""
|
||||
|
||||
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:
|
||||
return self._err_response(message)
|
||||
grader_id = self.system.anonymous_student_id
|
||||
|
||||
location = get.get('location')
|
||||
calibration_essay_id = get.get('submission_id')
|
||||
submission_key = get.get('submission_key')
|
||||
score = get.get('score')
|
||||
feedback = get.get('feedback')
|
||||
rubric_scores = get.getlist('rubric_scores[]')
|
||||
location = data.get('location')
|
||||
calibration_essay_id = data.get('submission_id')
|
||||
submission_key = data.get('submission_key')
|
||||
score = data.get('score')
|
||||
feedback = data.get('feedback')
|
||||
rubric_scores = data.getlist('rubric_scores[]')
|
||||
|
||||
try:
|
||||
response = self.peer_gs.save_calibration_essay(location, grader_id, calibration_essay_id,
|
||||
@@ -473,8 +472,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
})
|
||||
return html
|
||||
|
||||
|
||||
def peer_grading(self, get=None):
|
||||
def peer_grading(self, _data=None):
|
||||
'''
|
||||
Show a peer grading interface
|
||||
'''
|
||||
@@ -553,11 +551,11 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
|
||||
return html
|
||||
|
||||
def peer_grading_problem(self, get=None):
|
||||
def peer_grading_problem(self, data=None):
|
||||
'''
|
||||
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:
|
||||
# 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
|
||||
@@ -566,8 +564,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
return {'html': "", 'success': False}
|
||||
problem_location = self.link_to_location
|
||||
|
||||
elif get.get('location') is not None:
|
||||
problem_location = get.get('location')
|
||||
elif data.get('location') is not None:
|
||||
problem_location = data.get('location')
|
||||
|
||||
ajax_url = self.ajax_url
|
||||
html = self.system.render_template('peer_grading/peer_grading_problem.html', {
|
||||
@@ -617,4 +615,3 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor):
|
||||
non_editable_fields.extend([PeerGradingFields.due_date, PeerGradingFields.grace_period_string,
|
||||
PeerGradingFields.max_grade])
|
||||
return non_editable_fields
|
||||
|
||||
|
||||
@@ -47,12 +47,12 @@ class PollModule(PollFields, XModule):
|
||||
css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]}
|
||||
js_module_name = "Poll"
|
||||
|
||||
def handle_ajax(self, dispatch, get):
|
||||
def handle_ajax(self, dispatch, data):
|
||||
"""Ajax handler.
|
||||
|
||||
Args:
|
||||
dispatch: string request slug
|
||||
get: dict request get parameters
|
||||
data: dict request data parameters
|
||||
|
||||
Returns:
|
||||
json string
|
||||
|
||||
@@ -62,10 +62,10 @@ class SequenceModule(SequenceFields, XModule):
|
||||
progress = reduce(Progress.add_counts, progresses)
|
||||
return progress
|
||||
|
||||
def handle_ajax(self, dispatch, get): # TODO: bounds checking
|
||||
def handle_ajax(self, dispatch, data): # TODO: bounds checking
|
||||
''' get = request.POST instance '''
|
||||
if dispatch == 'goto_position':
|
||||
self.position = int(get['position'])
|
||||
self.position = int(data['position'])
|
||||
return json.dumps({'success': True})
|
||||
raise NotFoundError('Unexpected dispatch type')
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ class LogicTest(unittest.TestCase):
|
||||
self.raw_model_data
|
||||
)
|
||||
|
||||
def ajax_request(self, dispatch, get):
|
||||
def ajax_request(self, dispatch, data):
|
||||
"""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):
|
||||
|
||||
@@ -98,7 +98,7 @@ class TimeLimitModule(TimeLimitFields, XModule):
|
||||
progress = reduce(Progress.add_counts, progresses)
|
||||
return progress
|
||||
|
||||
def handle_ajax(self, dispatch, get):
|
||||
def handle_ajax(self, _dispatch, _data):
|
||||
raise NotFoundError('Unexpected dispatch type')
|
||||
|
||||
def render(self):
|
||||
@@ -141,4 +141,3 @@ class TimeLimitDescriptor(TimeLimitFields, XMLEditingDescriptor, XmlDescriptor):
|
||||
xml_object.append(
|
||||
etree.fromstring(child.export_to_xml(resource_fs)))
|
||||
return xml_object
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ class VideoModule(VideoFields, XModule):
|
||||
def __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."""
|
||||
log.debug(u"GET {0}".format(get))
|
||||
log.debug(u"GET {0}".format(data))
|
||||
log.debug(u"DISPATCH {0}".format(dispatch))
|
||||
raise Http404()
|
||||
|
||||
|
||||
@@ -125,9 +125,9 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
|
||||
|
||||
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."""
|
||||
log.debug(u"GET {0}".format(get))
|
||||
log.debug(u"GET {0}".format(data))
|
||||
log.debug(u"DISPATCH {0}".format(dispatch))
|
||||
raise Http404()
|
||||
|
||||
|
||||
@@ -168,12 +168,12 @@ class WordCloudModule(WordCloudFields, XModule):
|
||||
)[:amount]
|
||||
)
|
||||
|
||||
def handle_ajax(self, dispatch, post):
|
||||
def handle_ajax(self, dispatch, data):
|
||||
"""Ajax handler.
|
||||
|
||||
Args:
|
||||
dispatch: string request slug
|
||||
post: dict request get parameters
|
||||
data: dict request get parameters
|
||||
|
||||
Returns:
|
||||
json string
|
||||
@@ -187,7 +187,7 @@ class WordCloudModule(WordCloudFields, XModule):
|
||||
|
||||
# Student words from client.
|
||||
# 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))
|
||||
|
||||
self.student_words = student_words
|
||||
|
||||
@@ -272,9 +272,9 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
|
||||
'''
|
||||
return None
|
||||
|
||||
def handle_ajax(self, _dispatch, _get):
|
||||
def handle_ajax(self, _dispatch, _data):
|
||||
''' 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 ""
|
||||
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
|
||||
relative_xqueue_callback_url = reverse('xqueue_callback',
|
||||
kwargs=dict(course_id=course_id,
|
||||
userid=str(user.id),
|
||||
id=descriptor.location.url(),
|
||||
mod_id=descriptor.location.url(),
|
||||
dispatch=dispatch),
|
||||
)
|
||||
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
|
||||
|
||||
|
||||
@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.
|
||||
'''
|
||||
data = request.POST.copy()
|
||||
|
||||
# Test xqueue package, which we expect to be:
|
||||
# xpackage = {'xqueue_header': json.dumps({'lms_key':'secretkey',...}),
|
||||
# 'xqueue_body' : 'Message from grader'}
|
||||
get = request.POST.copy()
|
||||
for key in ['xqueue_header', 'xqueue_body']:
|
||||
if not get.has_key(key):
|
||||
if key not in data:
|
||||
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
|
||||
|
||||
# Retrieve target StudentModule
|
||||
user = User.objects.get(id=userid)
|
||||
|
||||
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(course_id,
|
||||
user, modulestore().get_instance(course_id, id), depth=0, select_for_update=True)
|
||||
instance = get_module(user, request, id, model_data_cache, course_id, grade_bucket_type='xqueue')
|
||||
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(
|
||||
course_id,
|
||||
user,
|
||||
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:
|
||||
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
|
||||
|
||||
# Transfer 'queuekey' from xqueue response header to 'get'. This is required to
|
||||
# use the interface defined by 'handle_ajax'
|
||||
get.update({'queuekey': header['lms_key']})
|
||||
# Transfer 'queuekey' from xqueue response header to the data.
|
||||
# This is required to use the interface defined by 'handle_ajax'
|
||||
data.update({'queuekey': header['lms_key']})
|
||||
|
||||
# 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:
|
||||
# Can ignore the return value--not used for xqueue_callback
|
||||
instance.handle_ajax(dispatch, get)
|
||||
instance.handle_ajax(dispatch, data)
|
||||
except:
|
||||
log.exception("error processing ajax call")
|
||||
raise
|
||||
@@ -466,23 +473,15 @@ def modx_dispatch(request, dispatch, location, course_id):
|
||||
if not request.user.is_authenticated():
|
||||
raise PermissionDenied
|
||||
|
||||
# Check for submitted files and basic file size checks
|
||||
p = request.POST.copy()
|
||||
if request.FILES:
|
||||
for fileinput_id in request.FILES.keys():
|
||||
inputfiles = request.FILES.getlist(fileinput_id)
|
||||
# Get the submitted data
|
||||
data = request.POST.copy()
|
||||
|
||||
if len(inputfiles) > settings.MAX_FILEUPLOADS_PER_INPUT:
|
||||
too_many_files_msg = 'Submission aborted! Maximum %d files may be submitted at once' % \
|
||||
settings.MAX_FILEUPLOADS_PER_INPUT
|
||||
return HttpResponse(json.dumps({'success': too_many_files_msg}))
|
||||
|
||||
for inputfile in inputfiles:
|
||||
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
|
||||
# Get and check submitted files
|
||||
files = request.FILES or {}
|
||||
error_msg = _check_files_limits(files)
|
||||
if error_msg:
|
||||
return HttpResponse(json.dumps({'success': error_msg}))
|
||||
data.update(files) # Merge files into data dictionary
|
||||
|
||||
try:
|
||||
descriptor = modulestore().get_instance(course_id, location)
|
||||
@@ -495,8 +494,11 @@ def modx_dispatch(request, dispatch, location, course_id):
|
||||
)
|
||||
raise Http404
|
||||
|
||||
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(course_id,
|
||||
request.user, descriptor)
|
||||
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(
|
||||
course_id,
|
||||
request.user,
|
||||
descriptor
|
||||
)
|
||||
|
||||
instance = get_module(request.user, request, location, model_data_cache, course_id, grade_bucket_type='ajax')
|
||||
if instance is None:
|
||||
@@ -507,7 +509,7 @@ def modx_dispatch(request, dispatch, location, course_id):
|
||||
|
||||
# Let the module handle the AJAX
|
||||
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
|
||||
except NotFoundError:
|
||||
@@ -529,7 +531,6 @@ def modx_dispatch(request, dispatch, location, course_id):
|
||||
return HttpResponse(ajax_return)
|
||||
|
||||
|
||||
|
||||
def get_score_bucket(grade, max_grade):
|
||||
"""
|
||||
Function to split arbitrary score ranges into 3 buckets.
|
||||
@@ -542,3 +543,30 @@ def get_score_bucket(grade, max_grade):
|
||||
score_bucket = "correct"
|
||||
|
||||
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:
|
||||
# into the database.
|
||||
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',
|
||||
name='xqueue_callback'),
|
||||
url(r'^change_setting$', 'student.views.change_setting',
|
||||
@@ -438,5 +438,3 @@ if settings.DEBUG:
|
||||
#Custom error pages
|
||||
handler404 = 'static_template_view.views.render_404'
|
||||
handler500 = 'static_template_view.views.render_500'
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user