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):
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'
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