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