Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions common/lib/capa/capa/capa_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,18 +373,18 @@ def get_html(self):
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the docstring mention the arguments and what they are?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely. In this pass I just updated the name of the variable (it was called get, but it comes from request.POST...), but didn't add anything to the docstrings. I will do it in a second pass.

The whole pipeline of how this "data" objects moves through xmodule and capa needs to be reworked. I opened an https://edx-wiki.atlassian.net/browse/LMS-486. The idea is to make the refactoring/improvements for the pipeline as the same time as we provide a consistent way to store student file submissions.


Also, parse out the dispatch from the get so that it can be passed onto the input type nicely
'''

# 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 {}
Expand Down
22 changes: 11 additions & 11 deletions common/lib/capa/capa/inputtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,13 @@ def setup(self):
"""
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.
Expand Down Expand Up @@ -677,20 +677,20 @@ def setup(self):
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):
Expand Down Expand Up @@ -751,7 +751,7 @@ def _parse_data(self, queue_msg):
msg = result['msg']
return msg

def _plot_data(self, get):
def _plot_data(self, data):
'''
AJAX handler for the plot button
Args:
Expand All @@ -765,7 +765,7 @@ def _plot_data(self, get):
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']
Expand Down Expand Up @@ -951,16 +951,16 @@ def _extra_context(self):
"""
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'.
Expand All @@ -974,7 +974,7 @@ def preview_chemcalc(self, get):

result = {'preview': '',
'error': ''}
formula = get['formula']
formula = data['formula']
if formula is None:
result['error'] = "No formula specified."
return result
Expand Down
8 changes: 4 additions & 4 deletions common/lib/capa/capa/tests/test_inputtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,8 @@ def test_rendering_while_queued(self):
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)

Expand All @@ -477,10 +477,10 @@ def test_plot_data(self):
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)
Expand Down
73 changes: 38 additions & 35 deletions common/lib/xmodule/xmodule/capa_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,11 +519,11 @@ def get_problem_html(self, encapsulate=True):
# 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,
Expand All @@ -547,18 +547,19 @@ def handle_ajax(self, dispatch, get):
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):
"""
Expand Down Expand Up @@ -633,61 +634,63 @@ def answer_available(self):

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:
empty dictionary

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.

Expand Down Expand Up @@ -717,10 +720,9 @@ def get_answer(self, get):
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
Expand All @@ -729,27 +731,27 @@ def get_problem(self, get):
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.
Expand All @@ -758,7 +760,7 @@ def make_dict_of_responses(get):
"""
answers = dict()

for key in get:
for key in data:
# e.g. input_resistor_1 ==> resistor_1
_, _, name = key.partition('_')

Expand All @@ -777,9 +779,9 @@ def make_dict_of_responses(get):
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
Expand All @@ -801,7 +803,7 @@ def publish_grade(self):
'max_value': score['total'],
})

def check_problem(self, get):
def check_problem(self, data):
"""
Checks whether answers to a problem are correct

Expand All @@ -813,8 +815,9 @@ def check_problem(self, get):
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'
Expand Down Expand Up @@ -972,7 +975,7 @@ def rescore_problem(self):

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 }
Expand All @@ -982,7 +985,7 @@ def save_problem(self, get):
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
Expand Down Expand Up @@ -1011,7 +1014,7 @@ def save_problem(self, get):
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.
Expand Down
5 changes: 2 additions & 3 deletions common/lib/xmodule/xmodule/combined_open_ended_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ def get_html(self):
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

Expand Down Expand Up @@ -266,4 +266,3 @@ def non_editable_metadata_fields(self):
non_editable_fields.extend([CombinedOpenEndedDescriptor.due, CombinedOpenEndedDescriptor.graceperiod,
CombinedOpenEndedDescriptor.markdown, CombinedOpenEndedDescriptor.version])
return non_editable_fields

2 changes: 1 addition & 1 deletion common/lib/xmodule/xmodule/conditional_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def get_html(self):
'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.
"""
Expand Down
Loading