Commit 866399ba by Albert St. Aubin

Changes for ui correctness state when saving a problem

TNL-1955
parent b65d245b
...@@ -143,6 +143,7 @@ class LoncapaProblem(object): ...@@ -143,6 +143,7 @@ class LoncapaProblem(object):
state (dict): containing the following keys: state (dict): containing the following keys:
- `seed` (int) random number generator seed - `seed` (int) random number generator seed
- `student_answers` (dict) maps input id to the stored answer for that input - `student_answers` (dict) maps input id to the stored answer for that input
- 'has_saved_answers' (Boolean) True if the answer has been saved since last submit.
- `correct_map` (CorrectMap) a map of each input to their 'correctness' - `correct_map` (CorrectMap) a map of each input to their 'correctness'
- `done` (bool) indicates whether or not this problem is considered done - `done` (bool) indicates whether or not this problem is considered done
- `input_state` (dict) maps input_id to a dictionary that holds the state for that input - `input_state` (dict) maps input_id to a dictionary that holds the state for that input
...@@ -165,6 +166,7 @@ class LoncapaProblem(object): ...@@ -165,6 +166,7 @@ class LoncapaProblem(object):
assert self.seed is not None, "Seed must be provided for LoncapaProblem." assert self.seed is not None, "Seed must be provided for LoncapaProblem."
self.student_answers = state.get('student_answers', {}) self.student_answers = state.get('student_answers', {})
self.has_saved_answers = state.get('has_saved_answers', False)
if 'correct_map' in state: if 'correct_map' in state:
self.correct_map.set_dict(state['correct_map']) self.correct_map.set_dict(state['correct_map'])
self.done = state.get('done', False) self.done = state.get('done', False)
...@@ -257,6 +259,7 @@ class LoncapaProblem(object): ...@@ -257,6 +259,7 @@ class LoncapaProblem(object):
Reset internal state to unfinished, with no answers Reset internal state to unfinished, with no answers
""" """
self.student_answers = dict() self.student_answers = dict()
self.has_saved_answers = False
self.correct_map = CorrectMap() self.correct_map = CorrectMap()
self.done = False self.done = False
...@@ -283,6 +286,7 @@ class LoncapaProblem(object): ...@@ -283,6 +286,7 @@ class LoncapaProblem(object):
return {'seed': self.seed, return {'seed': self.seed,
'student_answers': self.student_answers, 'student_answers': self.student_answers,
'has_saved_answers': self.has_saved_answers,
'correct_map': self.correct_map.get_dict(), 'correct_map': self.correct_map.get_dict(),
'input_state': self.input_state, 'input_state': self.input_state,
'done': self.done} 'done': self.done}
...@@ -789,8 +793,14 @@ class LoncapaProblem(object): ...@@ -789,8 +793,14 @@ class LoncapaProblem(object):
answervariable = None answervariable = None
if problemid in self.correct_map: if problemid in self.correct_map:
pid = input_id pid = input_id
status = self.correct_map.get_correctness(pid)
msg = self.correct_map.get_msg(pid) # If the the problem has not been saved since the last submit set the status to the
# current correctness value and set the message as expected. Otherwise we do not want to
# display correctness because the answer may have changed since the problem was graded.
if not self.has_saved_answers:
status = self.correct_map.get_correctness(pid)
msg = self.correct_map.get_msg(pid)
hint = self.correct_map.get_hint(pid) hint = self.correct_map.get_hint(pid)
hintmode = self.correct_map.get_hintmode(pid) hintmode = self.correct_map.get_hintmode(pid)
answervariable = self.correct_map.get_property(pid, 'answervariable') answervariable = self.correct_map.get_property(pid, 'answervariable')
...@@ -810,6 +820,7 @@ class LoncapaProblem(object): ...@@ -810,6 +820,7 @@ class LoncapaProblem(object):
'input_state': self.input_state[input_id], 'input_state': self.input_state[input_id],
'answervariable': answervariable, 'answervariable': answervariable,
'response_data': response_data, 'response_data': response_data,
'has_saved_answers': self.has_saved_answers,
'feedback': { 'feedback': {
'message': msg, 'message': msg,
'hint': hint, 'hint': hint,
......
...@@ -19,21 +19,13 @@ ...@@ -19,21 +19,13 @@
<% <%
label_class = 'response-label field-label label-inline' label_class = 'response-label field-label label-inline'
%> %>
<label id="${id}-${choice_id}-label" <label id="${id}-${choice_id}-label"
## If the student has selected this choice... ## If the student has selected this choice...
% if is_radio_input(choice_id): % if is_radio_input(choice_id):
<%
if status == 'correct': % if status.classname and not show_correctness == 'never':
correctness = 'correct' <% label_class += ' choicegroup_' + status.classname %>
elif status == 'partially-correct':
correctness = 'partially-correct'
elif status == 'incorrect':
correctness = 'incorrect'
else:
correctness = None
%>
% if correctness and not show_correctness == 'never':
<% label_class += ' choicegroup_' + correctness %>
% endif % endif
% endif % endif
class="${label_class}" class="${label_class}"
...@@ -47,7 +39,6 @@ ...@@ -47,7 +39,6 @@
checked="true" checked="true"
% endif % endif
/> ${HTML(choice_label)} /> ${HTML(choice_label)}
% if is_radio_input(choice_id): % if is_radio_input(choice_id):
% if not show_correctness == 'never' and status.classname != 'unanswered': % if not show_correctness == 'never' and status.classname != 'unanswered':
<%include file="status_span.html" args="status=status, status_id=id"/> <%include file="status_span.html" args="status=status, status_id=id"/>
......
...@@ -19,19 +19,9 @@ from openedx.core.djangolib.markup import HTML ...@@ -19,19 +19,9 @@ from openedx.core.djangolib.markup import HTML
<% choice_id = choice_id %> <% choice_id = choice_id %>
<section id="forinput${choice_id}" <section id="forinput${choice_id}"
% if input_type == 'radio' and choice_id in value : % if input_type == 'radio' and choice_id in value :
<% % if status.classname:
if status == 'correct': class="choicetextgroup_${status.classname}"
correctness = 'correct' % endif
elif status == 'incorrect':
correctness = 'incorrect'
elif status == 'partially-correct':
correctness = 'partially-correct'
else:
correctness = None
%>
% if correctness:
class="choicetextgroup_${correctness}"
% endif
% endif % endif
> >
<input class="ctinput" type="${input_type}" name="choiceinput_${id}" id="${choice_id}" value="${choice_id}" <input class="ctinput" type="${input_type}" name="choiceinput_${id}" id="${choice_id}" value="${choice_id}"
......
...@@ -1084,7 +1084,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -1084,7 +1084,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
conditions = [ conditions = [
{'input_type': 'radio', 'value': self.VALUE_DICT}] {'input_type': 'radio', 'value': self.VALUE_DICT}]
self.context['status'] = 'correct' self.context['status'] = Status('correct')
for test_conditions in conditions: for test_conditions in conditions:
self.context.update(test_conditions) self.context.update(test_conditions)
...@@ -1104,7 +1104,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -1104,7 +1104,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
conditions = [ conditions = [
{'input_type': 'radio', 'value': self.VALUE_DICT}] {'input_type': 'radio', 'value': self.VALUE_DICT}]
self.context['status'] = 'incorrect' self.context['status'] = Status('incorrect')
for test_conditions in conditions: for test_conditions in conditions:
self.context.update(test_conditions) self.context.update(test_conditions)
......
...@@ -705,17 +705,13 @@ class MatlabTest(unittest.TestCase): ...@@ -705,17 +705,13 @@ class MatlabTest(unittest.TestCase):
self.assertEqual( self.assertEqual(
etree.tostring(output), etree.tostring(output),
textwrap.dedent(""" textwrap.dedent("""
<div>{\'status\': Status(\'queued\'), \'button_enabled\': True, <div>{\'status\': Status(\'queued\'), \'button_enabled\': True, \'rows\': \'10\', \'queue_len\': \'3\',
\'rows\': \'10\', \'queue_len\': \'3\', \'mode\': \'\', \'mode\': \'\', \'tabsize\': 4, \'cols\': \'80\', \'STATIC_URL\': \'/dummy-static/\', \'linenumbers\':
\'tabsize\': 4, \'cols\': \'80\', \'true\', \'queue_msg\': \'\', \'value\': \'print "good evening"\',
\'STATIC_URL\': \'/dummy-static/\', \'linenumbers\': \'true\', \'queue_msg\': \'\', \'msg\': u\'Submitted. As soon as a response is returned, this message will be replaced by that feedback.\',
\'value\': \'print "good evening"\',
\'msg\': u\'Submitted. As soon as a response is returned,
this message will be replaced by that feedback.\',
\'matlab_editor_js\': \'/dummy-static/js/vendor/CodeMirror/octave.js\', \'matlab_editor_js\': \'/dummy-static/js/vendor/CodeMirror/octave.js\',
\'hidden\': \'\', \'id\': \'prob_1_2\', \'hidden\': \'\', \'id\': \'prob_1_2\',
\'describedby_html\': Markup(u\'aria-describedby="status_prob_1_2"\'), \'describedby_html\': Markup(u\'aria-describedby="status_prob_1_2"\'), \'response_data\': {}}</div>
\'response_data\': {}}</div>
""").replace('\n', ' ').strip() """).replace('\n', ' ').strip()
) )
......
...@@ -162,6 +162,8 @@ class CapaFields(object): ...@@ -162,6 +162,8 @@ class CapaFields(object):
scope=Scope.user_state, default={}) scope=Scope.user_state, default={})
input_state = Dict(help=_("Dictionary for maintaining the state of inputtypes"), scope=Scope.user_state) input_state = Dict(help=_("Dictionary for maintaining the state of inputtypes"), scope=Scope.user_state)
student_answers = Dict(help=_("Dictionary with the current student responses"), scope=Scope.user_state) student_answers = Dict(help=_("Dictionary with the current student responses"), scope=Scope.user_state)
has_saved_answers = Boolean(help=_("Whether or not the answers have been saved since last submit"),
scope=Scope.user_state)
done = Boolean(help=_("Whether the student has answered the problem"), scope=Scope.user_state) done = Boolean(help=_("Whether the student has answered the problem"), scope=Scope.user_state)
seed = Integer(help=_("Random seed for this student"), scope=Scope.user_state) seed = Integer(help=_("Random seed for this student"), scope=Scope.user_state)
last_submission_time = Date(help=_("Last submission time"), scope=Scope.user_state) last_submission_time = Date(help=_("Last submission time"), scope=Scope.user_state)
...@@ -326,6 +328,7 @@ class CapaMixin(CapaFields): ...@@ -326,6 +328,7 @@ class CapaMixin(CapaFields):
'done': self.done, 'done': self.done,
'correct_map': self.correct_map, 'correct_map': self.correct_map,
'student_answers': self.student_answers, 'student_answers': self.student_answers,
'has_saved_answers': self.has_saved_answers,
'input_state': self.input_state, 'input_state': self.input_state,
'seed': self.seed, 'seed': self.seed,
} }
...@@ -339,6 +342,7 @@ class CapaMixin(CapaFields): ...@@ -339,6 +342,7 @@ class CapaMixin(CapaFields):
self.correct_map = lcp_state['correct_map'] self.correct_map = lcp_state['correct_map']
self.input_state = lcp_state['input_state'] self.input_state = lcp_state['input_state']
self.student_answers = lcp_state['student_answers'] self.student_answers = lcp_state['student_answers']
self.has_saved_answers = lcp_state['has_saved_answers']
self.seed = lcp_state['seed'] self.seed = lcp_state['seed']
def set_last_submission_time(self): def set_last_submission_time(self):
...@@ -675,6 +679,12 @@ class CapaMixin(CapaFields): ...@@ -675,6 +679,12 @@ class CapaMixin(CapaFields):
answer_notification_type, answer_notification_message = self._get_answer_notification( answer_notification_type, answer_notification_message = self._get_answer_notification(
render_notifications=submit_notification) render_notifications=submit_notification)
save_message = None
if self.has_saved_answers:
save_message = _(
"Your answers were previously saved. Click '{button_name}' to grade them."
).format(button_name=self.submit_button_name())
context = { context = {
'problem': content, 'problem': content,
'id': self.location.to_deprecated_string(), 'id': self.location.to_deprecated_string(),
...@@ -691,6 +701,8 @@ class CapaMixin(CapaFields): ...@@ -691,6 +701,8 @@ class CapaMixin(CapaFields):
'should_enable_next_hint': should_enable_next_hint, 'should_enable_next_hint': should_enable_next_hint,
'answer_notification_type': answer_notification_type, 'answer_notification_type': answer_notification_type,
'answer_notification_message': answer_notification_message, 'answer_notification_message': answer_notification_message,
'has_saved_answers': self.has_saved_answers,
'save_message': save_message,
} }
html = self.runtime.render_template('problem.html', context) html = self.runtime.render_template('problem.html', context)
...@@ -1080,6 +1092,7 @@ class CapaMixin(CapaFields): ...@@ -1080,6 +1092,7 @@ class CapaMixin(CapaFields):
event_info['state'] = self.lcp.get_state() event_info['state'] = self.lcp.get_state()
event_info['problem_id'] = self.location.to_deprecated_string() event_info['problem_id'] = self.location.to_deprecated_string()
self.lcp.has_saved_answers = False
answers = self.make_dict_of_responses(data) answers = self.make_dict_of_responses(data)
answers_without_files = convert_files_to_filenames(answers) answers_without_files = convert_files_to_filenames(answers)
event_info['answers'] = answers_without_files event_info['answers'] = answers_without_files
...@@ -1490,6 +1503,7 @@ class CapaMixin(CapaFields): ...@@ -1490,6 +1503,7 @@ class CapaMixin(CapaFields):
} }
self.lcp.student_answers = answers self.lcp.student_answers = answers
self.lcp.has_saved_answers = True
self.set_state_from_lcp() self.set_state_from_lcp()
......
...@@ -779,6 +779,7 @@ ...@@ -779,6 +779,7 @@
edx.HtmlUtils.HTML(saveMessage) edx.HtmlUtils.HTML(saveMessage)
); );
that.clear_all_notifications(); that.clear_all_notifications();
that.el.find('.wrapper-problem-response .message').hide();
that.saveNotification.show(); that.saveNotification.show();
that.focus_on_save_notification(); that.focus_on_save_notification();
} else { } else {
...@@ -938,7 +939,7 @@ ...@@ -938,7 +939,7 @@
return $(element).find('input').on('input', function() { return $(element).find('input').on('input', function() {
var $p; var $p;
$p = $(element).find('span.status'); $p = $(element).find('span.status');
return $p.parent().removeClass().addClass('unsubmitted'); return $p.parent().removeAttr('class').addClass('unsubmitted');
}); });
}, },
choicegroup: function(element) { choicegroup: function(element) {
...@@ -949,7 +950,7 @@ ...@@ -949,7 +950,7 @@
var $status; var $status;
$status = $('#status_' + id); $status = $('#status_' + id);
if ($status[0]) { if ($status[0]) {
$status.removeClass().addClass('unanswered'); $status.removeAttr('class').addClass('unanswered');
} else { } else {
$('<span>', { $('<span>', {
class: 'unanswered', class: 'unanswered',
...@@ -957,7 +958,7 @@ ...@@ -957,7 +958,7 @@
id: 'status_' + id id: 'status_' + id
}); });
} }
return $element.find('label').removeClass(); return $element.find('label').removeAttr('class');
}); });
}, },
'option-input': function(element) { 'option-input': function(element) {
...@@ -965,7 +966,7 @@ ...@@ -965,7 +966,7 @@
$select = $(element).find('select'); $select = $(element).find('select');
id = ($select.attr('id').match(/^input_(.*)$/))[1]; id = ($select.attr('id').match(/^input_(.*)$/))[1];
return $select.on('change', function() { return $select.on('change', function() {
return $('#status_' + id).removeClass().addClass('unanswered') return $('#status_' + id).removeAttr('class').addClass('unanswered')
.find('.sr') .find('.sr')
.text(gettext('unsubmitted')); .text(gettext('unsubmitted'));
}); });
......
...@@ -34,6 +34,13 @@ class ProblemPage(PageObject): ...@@ -34,6 +34,13 @@ class ProblemPage(PageObject):
return self.q(css="div.problem p").text return self.q(css="div.problem p").text
@property @property
def problem_input_content(self):
"""
Return the text of the question of the problem.
"""
return self.q(css="div.wrapper-problem-response").text[0]
@property
def problem_content(self): def problem_content(self):
""" """
Return the content of the problem Return the content of the problem
...@@ -144,6 +151,12 @@ class ProblemPage(PageObject): ...@@ -144,6 +151,12 @@ class ProblemPage(PageObject):
""" """
return self.q(css='.notification.notification-hint').visible return self.q(css='.notification.notification-hint').visible
def is_feedback_message_notification_visible(self):
"""
Is the Feedback Messaged notification visible
"""
return self.q(css='.wrapper-problem-response .message').visible
def is_save_notification_visible(self): def is_save_notification_visible(self):
""" """
Is the Save Notification Visible? Is the Save Notification Visible?
...@@ -156,6 +169,13 @@ class ProblemPage(PageObject): ...@@ -156,6 +169,13 @@ class ProblemPage(PageObject):
""" """
return self.q(css='.notification.success.notification-submit').visible return self.q(css='.notification.success.notification-submit').visible
def wait_for_feedback_message_visibility(self):
"""
Wait for the Feedback Message notification to be visible.
"""
self.wait_for_element_visibility('.wrapper-problem-response .message',
'Waiting for the Feedback message to be visible')
def wait_for_save_notification(self): def wait_for_save_notification(self):
""" """
Wait for the Save Notification to be present Wait for the Save Notification to be present
...@@ -237,6 +257,15 @@ class ProblemPage(PageObject): ...@@ -237,6 +257,15 @@ class ProblemPage(PageObject):
msg = "Wait for status to be {}".format(message) msg = "Wait for status to be {}".format(message)
self.wait_for_element_visibility(status_selector, msg) self.wait_for_element_visibility(status_selector, msg)
def is_expected_status_visible(self, status_selector):
"""
check for the expected status indicator to be visible.
Args:
status_selector(str): status selector string.
"""
return self.q(css=status_selector).visible
def wait_success_notification(self): def wait_success_notification(self):
""" """
Check for visibility of the success notification and icon. Check for visibility of the success notification and icon.
......
...@@ -764,14 +764,16 @@ class ProblemStateOnNavigationTest(UniqueCourseTest): ...@@ -764,14 +764,16 @@ class ProblemStateOnNavigationTest(UniqueCourseTest):
self.problem_page.wait_for_save_notification() self.problem_page.wait_for_save_notification()
# Save problem 1's content state as we're about to switch units in the sequence. # Save problem 1's content state as we're about to switch units in the sequence.
problem1_content_before_switch = self.problem_page.problem_content problem1_content_before_switch = self.problem_page.problem_input_content
# Go to sequential position 2 and assert that we are on problem 2. # Go to sequential position 2 and assert that we are on problem 2.
self.go_to_tab_and_assert_problem(2, self.problem2_name) self.go_to_tab_and_assert_problem(2, self.problem2_name)
self.problem_page.wait_for_expected_status('span.unanswered', 'unanswered')
# Come back to our original unit in the sequence and assert that the content hasn't changed. # Come back to our original unit in the sequence and assert that the content hasn't changed.
self.go_to_tab_and_assert_problem(1, self.problem1_name) self.go_to_tab_and_assert_problem(1, self.problem1_name)
problem1_content_after_coming_back = self.problem_page.problem_content problem1_content_after_coming_back = self.problem_page.problem_input_content
self.assertIn(problem1_content_after_coming_back, problem1_content_before_switch) self.assertIn(problem1_content_after_coming_back, problem1_content_before_switch)
def test_perform_problem_reset_and_navigate(self): def test_perform_problem_reset_and_navigate(self):
......
...@@ -225,6 +225,94 @@ class ProblemNotificationTests(ProblemsTest): ...@@ -225,6 +225,94 @@ class ProblemNotificationTests(ProblemsTest):
self.assertFalse(problem_page.is_save_notification_visible()) self.assertFalse(problem_page.is_save_notification_visible())
class ProblemFeedbackNotificationTests(ProblemsTest):
"""
Tests that the feedback notifications are visible when expected.
"""
def get_problem(self):
"""
Problem structure.
"""
xml = dedent("""
<problem>
<label>Which of the following countries has the largest population?</label>
<multiplechoiceresponse>
<choicegroup type="MultipleChoice">
<choice correct="false">Brazil <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint></choice>
<choice correct="false">Germany</choice>
<choice correct="true">Indonesia</choice>
<choice correct="false">Russia</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>
""")
return XBlockFixtureDesc('problem', 'TEST PROBLEM', data=xml,
metadata={'max_attempts': 10},
grader_type='Final Exam')
def test_feedback_notification_hides_after_save(self):
self.courseware_page.visit()
problem_page = ProblemPage(self.browser)
problem_page.click_choice("choice_0")
problem_page.click_submit()
problem_page.wait_for_feedback_message_visibility()
problem_page.click_choice("choice_1")
problem_page.click_save()
self.assertFalse(problem_page.is_feedback_message_notification_visible())
class ProblemSaveStatusUpdateTests(ProblemsTest):
"""
Tests the problem status updates correctly with an answer change and save.
"""
def get_problem(self):
"""
Problem structure.
"""
xml = dedent("""
<problem>
<label>Which of the following countries has the largest population?</label>
<multiplechoiceresponse>
<choicegroup type="MultipleChoice">
<choice correct="false">Brazil <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint></choice>
<choice correct="false">Germany</choice>
<choice correct="true">Indonesia</choice>
<choice correct="false">Russia</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>
""")
return XBlockFixtureDesc('problem', 'TEST PROBLEM', data=xml,
metadata={'max_attempts': 10},
grader_type='Final Exam')
def test_status_removed_after_save_before_submit(self):
"""
Scenario: User should see the status removed when saving after submitting an answer and reloading the page.
Given that I have loaded the problem page
And a choice has been selected and submitted
When I change the choice
And Save the problem
And reload the problem page
Then I should see the save notification and I should not see any indication of problem status
"""
self.courseware_page.visit()
problem_page = ProblemPage(self.browser)
problem_page.click_choice("choice_1")
problem_page.click_submit()
problem_page.wait_incorrect_notification()
problem_page.wait_for_expected_status('label.choicegroup_incorrect', 'incorrect')
problem_page.click_choice("choice_2")
self.assertFalse(problem_page.is_expected_status_visible('label.choicegroup_incorrect'))
problem_page.click_save()
problem_page.wait_for_save_notification()
# Refresh the page and the status should not be added
self.courseware_page.visit()
self.assertFalse(problem_page.is_expected_status_visible('label.choicegroup_incorrect'))
self.assertTrue(problem_page.is_save_notification_visible())
class ProblemSubmitButtonMaxAttemptsTest(ProblemsTest): class ProblemSubmitButtonMaxAttemptsTest(ProblemsTest):
""" """
Tests that the Submit button disables after the number of max attempts is reached. Tests that the Submit button disables after the number of max attempts is reached.
......
...@@ -74,6 +74,7 @@ from openedx.core.djangolib.markup import HTML ...@@ -74,6 +74,7 @@ from openedx.core.djangolib.markup import HTML
notification_type='success', notification_type='success',
notification_icon='fa-check', notification_icon='fa-check',
notification_name='submit', notification_name='submit',
is_hidden=False,
notification_message=answer_notification_message" notification_message=answer_notification_message"
/> />
% endif % endif
...@@ -82,6 +83,7 @@ from openedx.core.djangolib.markup import HTML ...@@ -82,6 +83,7 @@ from openedx.core.djangolib.markup import HTML
notification_type='error', notification_type='error',
notification_icon='fa-close', notification_icon='fa-close',
notification_name='submit', notification_name='submit',
is_hidden=False,
notification_message=answer_notification_message" notification_message=answer_notification_message"
/> />
% endif % endif
...@@ -90,6 +92,7 @@ from openedx.core.djangolib.markup import HTML ...@@ -90,6 +92,7 @@ from openedx.core.djangolib.markup import HTML
notification_type='success', notification_type='success',
notification_icon='fa-asterisk', notification_icon='fa-asterisk',
notification_name='submit', notification_name='submit',
is_hidden=False,
notification_message=answer_notification_message" notification_message=answer_notification_message"
/> />
% endif % endif
...@@ -98,6 +101,7 @@ from openedx.core.djangolib.markup import HTML ...@@ -98,6 +101,7 @@ from openedx.core.djangolib.markup import HTML
notification_type='warning', notification_type='warning',
notification_icon='fa-save', notification_icon='fa-save',
notification_name='save', notification_name='save',
notification_message=''" notification_message=save_message,
is_hidden=not has_saved_answers"
/> />
</div> </div>
<%page expression_filter="h" args="notification_name, notification_type, notification_icon, <%page expression_filter="h" args="notification_name, notification_type, notification_icon,
notification_message, should_enable_next_hint"/> notification_message, should_enable_next_hint, is_hidden=True"/>
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
<div class="notification ${notification_type} ${'notification-'}${notification_name} <div class="notification ${notification_type} ${'notification-'}${notification_name}
${'' if notification_name == 'submit' else 'is-hidden' }" ${'' if not is_hidden else 'is-hidden' }"
tabindex="-1"> tabindex="-1">
<span class="icon fa ${notification_icon}" aria-hidden="true"></span> <span class="icon fa ${notification_icon}" aria-hidden="true"></span>
<span class="notification-message" aria-describedby="${ short_id }-problem-title">${notification_message} <span class="notification-message" aria-describedby="${ short_id }-problem-title">${notification_message}
......
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