Commit 7f742978 by Sarina Canelake

Merge pull request #2219 from edx/sarina/i19-capa-new

i19 base capa strings
parents 207f3c58 0efa75d6
......@@ -63,13 +63,13 @@ class CorrectMap(object):
return repr(self.cmap)
def get_dict(self):
'''
"""
return dict version of self
'''
"""
return self.cmap
def set_dict(self, correct_map):
'''
"""
Set internal dict of CorrectMap to provided correct_map dict
correct_map is saved by LMS as a plaintext JSON dump of the correctmap dict. This
......@@ -85,7 +85,7 @@ class CorrectMap(object):
Special migration case:
If correct_map is a one-level dict, then convert it to the new dict of dicts format.
'''
"""
# empty current dict
self.__init__()
......@@ -149,17 +149,17 @@ class CorrectMap(object):
return self.get_property(answer_id, 'hintmode', None)
def set_hint_and_mode(self, answer_id, hint, hintmode):
'''
"""
- hint : (string) HTML text for hint
- hintmode : (string) mode for hint display ('always' or 'on_request')
'''
"""
self.set_property(answer_id, 'hint', hint)
self.set_property(answer_id, 'hintmode', hintmode)
def update(self, other_cmap):
'''
"""
Update this CorrectMap with the contents of another CorrectMap
'''
"""
if not isinstance(other_cmap, CorrectMap):
raise Exception('CorrectMap.update called with invalid argument %s' % other_cmap)
self.cmap.update(other_cmap.get_dict())
......
......@@ -26,7 +26,7 @@ class MathRenderer(object):
tags = ['math']
def __init__(self, system, xml):
r'''
r"""
Render math using latex-like formatting.
Examples:
......@@ -37,7 +37,7 @@ class MathRenderer(object):
We convert these to [mathjax]...[/mathjax] and [mathjaxinline]...[/mathjaxinline]
TODO: use shorter tags (but this will require converting problem XML files!)
'''
"""
self.system = system
self.xml = xml
......@@ -79,13 +79,13 @@ registry.register(MathRenderer)
class SolutionRenderer(object):
'''
"""
A solution is just a <span>...</span> which is given an ID, that is used for displaying an
extended answer (a problem "solution") after "show answers" is pressed.
Note that the solution content is NOT rendered and returned in the HTML. It is obtained by an
ajax call.
'''
"""
tags = ['solution']
def __init__(self, system, xml):
......
......@@ -313,7 +313,7 @@ class FileSubmissionTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/',
'id': 'prob_1_2',
'status': 'queued',
'msg': input_class.submitted_msg,
'msg': the_input.submitted_msg,
'value': 'BumbleBee.py',
'queue_len': '3',
'allowed_files': '["runme.py", "nooooo.rb", "ohai.java"]',
......@@ -362,7 +362,7 @@ class CodeInputTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': 'queued',
'msg': input_class.submitted_msg,
'msg': the_input.submitted_msg,
'mode': mode,
'linenumbers': linenumbers,
'rows': rows,
......@@ -415,7 +415,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': 'queued',
'msg': self.input_class.submitted_msg,
'msg': self.the_input.submitted_msg,
'mode': self.mode,
'rows': self.rows,
'cols': self.cols,
......@@ -444,7 +444,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': 'queued',
'msg': self.input_class.submitted_msg,
'msg': the_input.submitted_msg,
'mode': self.mode,
'rows': self.rows,
'cols': self.cols,
......@@ -501,7 +501,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': 'queued',
'msg': self.input_class.plot_submitted_msg,
'msg': the_input.submitted_msg,
'mode': self.mode,
'rows': self.rows,
'cols': self.cols,
......
......@@ -1150,7 +1150,7 @@ class NumericalResponseTest(ResponseTest):
"""A fake gettext.Translations object."""
def ugettext(self, text):
"""Return the 'translation' of `text`."""
if text == "There was a problem with the staff answer to this problem":
if text == "There was a problem with the staff answer to this problem.":
text = "TRANSLATED!"
return text
problem.capa_system.i18n = FakeTranslations()
......
......@@ -8,10 +8,10 @@ default_tolerance = '0.001%'
def compare_with_tolerance(v1, v2, tol=default_tolerance):
'''
"""
Compare v1 to v2 with maximum tolerance tol.
tol is relative if it ends in %; otherwise, it is absolute
tol is relative if it ends in %; otherwise, it is absolute.
- v1 : student result (float complex number)
- v2 : instructor result (float complex number)
......@@ -26,7 +26,7 @@ def compare_with_tolerance(v1, v2, tol=default_tolerance):
Out[183]: -3.3881317890172014e-21
In [212]: 1.9e24 - 1.9*10**24
Out[212]: 268435456.0
'''
"""
relative = tol.endswith('%')
if relative:
tolerance_rel = evaluator(dict(), dict(), tol[:-1]) * 0.01
......@@ -46,8 +46,10 @@ def compare_with_tolerance(v1, v2, tol=default_tolerance):
def contextualize_text(text, context): # private
''' Takes a string with variables. E.g. $a+$b.
Does a substitution of those variables from the context '''
"""
Takes a string with variables. E.g. $a+$b.
Does a substitution of those variables from the context
"""
if not text:
return text
for key in sorted(context, lambda x, y: cmp(len(y), len(x))):
......@@ -66,10 +68,10 @@ def contextualize_text(text, context): # private
def convert_files_to_filenames(answers):
'''
"""
Check for File objects in the dict of submitted answers,
convert File objects to their filename (string)
'''
"""
new_answers = dict()
for answer_id in answers.keys():
answer = answers[answer_id]
......@@ -86,9 +88,9 @@ def is_list_of_files(files):
def is_file(file_to_test):
'''
"""
Duck typing to check if 'file_to_test' is a File object
'''
"""
return all(hasattr(file_to_test, method) for method in ['read', 'name'])
......
......@@ -12,9 +12,9 @@ dateformat = '%Y%m%d%H%M%S'
def make_hashkey(seed):
'''
"""
Generate a string key by hashing
'''
"""
h = hashlib.md5()
h.update(str(seed))
return h.hexdigest()
......@@ -57,9 +57,9 @@ def parse_xreply(xreply):
class XQueueInterface(object):
'''
"""
Interface to the external grading system
'''
"""
def __init__(self, url, django_auth, requests_auth=None):
self.url = url
......@@ -106,8 +106,10 @@ class XQueueInterface(object):
return self._http_post(self.url + '/xqueue/login/', payload)
def _send_to_queue(self, header, body, files_to_upload):
payload = {'xqueue_header': header,
'xqueue_body': body}
payload = {
'xqueue_header': header,
'xqueue_body': body
}
files = {}
if files_to_upload is not None:
for f in files_to_upload:
......
......@@ -497,22 +497,26 @@ class CapaMixin(CapaFields):
self.set_state_from_lcp()
# Prepend a scary warning to the student
warning = '<div class="capa_reset">'\
'<h2>Warning: The problem has been reset to its initial state!</h2>'\
'The problem\'s state was corrupted by an invalid submission. ' \
'The submission consisted of:'\
'<ul>'
_ = self.runtime.service(self, "i18n").ugettext
warning_msg = _("Warning: The problem has been reset to its initial state!")
warning = '<div class="capa_reset"> <h2> ' + warning_msg + '</h2>'
# Translators: Following this message, there will be a bulleted list of items.
warning_msg = _("The problem's state was corrupted by an invalid submission. The submission consisted of:")
warning += warning_msg + '<ul>'
for student_answer in student_answers.values():
if student_answer != '':
warning += '<li>' + cgi.escape(student_answer) + '</li>'
warning += '</ul>'\
'If this error persists, please contact the course staff.'\
'</div>'
warning_msg = _('If this error persists, please contact the course staff.')
warning += '</ul>' + warning_msg + '</div>'
html = warning
try:
html += self.lcp.get_html()
except Exception: # Couldn't do it. Give up
except Exception: # pylint: disable=broad-except
# Couldn't do it. Give up.
log.exception("Unable to generate html from LoncapaProblem")
raise
......@@ -541,12 +545,14 @@ class CapaMixin(CapaFields):
else:
check_button = False
content = {'name': self.display_name_with_default,
content = {
'name': self.display_name_with_default,
'html': html,
'weight': self.weight,
}
context = {'problem': content,
context = {
'problem': content,
'id': self.id,
'check_button': check_button,
'reset_button': self.should_show_reset_button(),
......@@ -563,7 +569,7 @@ class CapaMixin(CapaFields):
id=self.location.html_id(), ajax_url=self.runtime.ajax_url
) + html + "</div>"
# now do all the substitutions which the LMS module_render normally does, but
# Now do all the substitutions which the LMS module_render normally does, but
# we need to do here explicitly since we can get called for our HTML via AJAX
html = self.runtime.replace_urls(html)
if self.runtime.replace_course_urls:
......@@ -855,17 +861,19 @@ class CapaMixin(CapaFields):
answers = self.make_dict_of_responses(data)
event_info['answers'] = convert_files_to_filenames(answers)
_ = self.runtime.service(self, "i18n").ugettext
# Too late. Cannot submit
if self.closed():
event_info['failure'] = 'closed'
self.runtime.track_function('problem_check_fail', event_info)
raise NotFoundError('Problem is closed')
raise NotFoundError(_("Problem is closed."))
# Problem submitted. Student should reset before checking again
if self.done and self.rerandomize == "always":
event_info['failure'] = 'unreset'
self.runtime.track_function('problem_check_fail', event_info)
raise NotFoundError('Problem must be reset before it can be checked again')
raise NotFoundError(_("Problem must be reset before it can be checked again."))
# Problem queued. Students must wait a specified waittime before they are allowed to submit
if self.lcp.is_queued():
......@@ -873,7 +881,7 @@ class CapaMixin(CapaFields):
prev_submit_time = self.lcp.get_recentmost_queuetime()
waittime_between_requests = self.runtime.xqueue['waittime']
if (current_time - prev_submit_time).total_seconds() < waittime_between_requests:
msg = u'You must wait at least {wait} seconds between submissions'.format(
msg = _(u"You must wait at least {wait} seconds between submissions.").format(
wait=waittime_between_requests)
return {'success': msg, 'html': ''} # Prompts a modal dialog in ajax callback
......@@ -899,7 +907,8 @@ class CapaMixin(CapaFields):
# Otherwise, display just an error message,
# without a stack trace
else:
msg = u"Error: {msg}".format(msg=inst.message)
# Translators: {msg} will be replaced with a problem's error message.
msg = _(u"Error: {msg}").format(msg=inst.message)
return {'success': msg}
......@@ -936,7 +945,8 @@ class CapaMixin(CapaFields):
# render problem into HTML
html = self.get_problem_html(encapsulate=False)
return {'success': success,
return {
'success': success,
'contents': html,
}
......@@ -958,15 +968,18 @@ class CapaMixin(CapaFields):
"""
event_info = {'state': self.lcp.get_state(), 'problem_id': self.location.url()}
_ = self.runtime.service(self, "i18n").ugettext
if not self.lcp.supports_rescoring():
event_info['failure'] = 'unsupported'
self.runtime.track_function('problem_rescore_fail', event_info)
raise NotImplementedError("Problem's definition does not support rescoring")
# Translators: 'rescoring' refers to the act of re-submitting a student's solution so it can get a new score.
raise NotImplementedError(_("Problem's definition does not support rescoring."))
if not self.done:
event_info['failure'] = 'unanswered'
self.runtime.track_function('problem_rescore_fail', event_info)
raise NotFoundError('Problem must be answered before it can be graded again')
raise NotFoundError(_("Problem must be answered before it can be graded again."))
# get old score, for comparison:
orig_score = self.lcp.get_score()
......@@ -1032,32 +1045,40 @@ class CapaMixin(CapaFields):
answers = self.make_dict_of_responses(data)
event_info['answers'] = answers
_ = self.runtime.service(self, "i18n").ugettext
# Too late. Cannot submit
if self.closed() and not self.max_attempts == 0:
event_info['failure'] = 'closed'
self.runtime.track_function('save_problem_fail', event_info)
return {'success': False,
'msg': "Problem is closed"}
return {
'success': False,
# Translators: 'closed' means the problem's due date has passed. You may no longer attempt to solve the problem.
'msg': _("Problem is closed.")
}
# Problem submitted. Student should reset before saving
# again.
if self.done and self.rerandomize == "always":
event_info['failure'] = 'done'
self.runtime.track_function('save_problem_fail', event_info)
return {'success': False,
'msg': "Problem needs to be reset prior to save"}
return {
'success': False,
'msg': _("Problem needs to be reset prior to save.")
}
self.lcp.student_answers = answers
self.set_state_from_lcp()
self.runtime.track_function('save_problem_success', event_info)
msg = "Your answers have been saved"
msg = _("Your answers have been saved.")
if not self.max_attempts == 0:
msg += " but not graded. Hit 'Check' to grade them."
return {'success': True,
'msg': msg}
msg = _("Your answers have been saved but not graded. Click 'Check' to grade them.")
return {
'success': True,
'msg': msg,
}
def reset_problem(self, _data):
"""
......@@ -1074,18 +1095,24 @@ class CapaMixin(CapaFields):
event_info = dict()
event_info['old_state'] = self.lcp.get_state()
event_info['problem_id'] = self.location.url()
_ = self.runtime.service(self, "i18n").ugettext
if self.closed():
event_info['failure'] = 'closed'
self.runtime.track_function('reset_problem_fail', event_info)
return {'success': False,
'error': "Problem is closed"}
return {
'success': False,
# Translators: 'closed' means the problem's due date has passed. You may no longer attempt to solve the problem.
'error': _("Problem is closed."),
}
if not self.done:
event_info['failure'] = 'not_done'
self.runtime.track_function('reset_problem_fail', event_info)
return {'success': False,
'error': "Refresh the page and make an attempt before resetting."}
return {
'success': False,
'error': _("Refresh the page and make an attempt before resetting."),
}
if self.rerandomize in ["always", "onreset"]:
# Reset random number generator seed.
......@@ -1100,5 +1127,7 @@ class CapaMixin(CapaFields):
event_info['new_state'] = self.lcp.get_state()
self.runtime.track_function('reset_problem', event_info)
return {'success': True,
'html': self.get_problem_html(encapsulate=False)}
return {
'success': True,
'html': self.get_problem_html(encapsulate=False),
}
......@@ -62,12 +62,14 @@ class CapaModule(CapaMixin, XModule):
'ungraded_response': self.handle_ungraded_response
}
generic_error_message = (
_ = self.runtime.service(self, "i18n").ugettext
generic_error_message = _(
"We're sorry, there was an error with processing your request. "
"Please try reloading your page and trying again."
)
not_found_error_message = (
not_found_error_message = _(
"The state of this problem has changed since you loaded this page. "
"Please refresh your page."
)
......
......@@ -265,10 +265,10 @@ class TestRescoringTask(TestIntegrationTask):
self.assertEqual(instructor_task.task_state, FAILURE)
status = json.loads(instructor_task.task_output)
self.assertEqual(status['exception'], 'NotImplementedError')
self.assertEqual(status['message'], "Problem's definition does not support rescoring")
self.assertEqual(status['message'], "Problem's definition does not support rescoring.")
status = InstructorTaskModuleTestCase.get_task_status(instructor_task.task_id)
self.assertEqual(status['message'], "Problem's definition does not support rescoring")
self.assertEqual(status['message'], "Problem's definition does not support rescoring.")
def define_randomized_custom_response_problem(self, problem_url_name, redefine=False):
"""
......
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