From 94a88c57fa0584ff5d8f44749b70c4e12d4d72fe Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Mon, 27 Jul 2015 16:15:55 -0700 Subject: [PATCH 1/2] Fix: Unhelpful tooltips shown in sequential accordion in the LMS --- problem_builder/mentoring.py | 10 ++++++++-- problem_builder/tests/unit/test_mentoring.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/problem_builder/mentoring.py b/problem_builder/mentoring.py index 84e685fc..52b73a36 100644 --- a/problem_builder/mentoring.py +++ b/problem_builder/mentoring.py @@ -759,10 +759,16 @@ def get_content_titles(self): """ By default, each Sequential block in a course ("Subsection" in Studio parlance) will display the display_name of each descendant in a tooltip above the content. We don't - want that - we only want to display the mentoring block as a whole as one item. + want that - we only want to display one title for this mentoring block as a whole. Otherwise things like "Choice (yes) (Correct)" will appear in the tooltip. + + If this block has no title set, don't display any title. Then, if this is the only block + in the unit, the unit's title will be used. (Why isn't it always just used?) """ - return [self.display_name] + has_explicitly_set_title = self.fields['display_name'].is_set_on(self) + if has_explicitly_set_title: + return [self.display_name] + return [] @staticmethod def workbench_scenarios(): diff --git a/problem_builder/tests/unit/test_mentoring.py b/problem_builder/tests/unit/test_mentoring.py index 7f7c800d..184ec34e 100644 --- a/problem_builder/tests/unit/test_mentoring.py +++ b/problem_builder/tests/unit/test_mentoring.py @@ -6,6 +6,7 @@ from problem_builder.mentoring import _default_theme_config +@ddt.ddt class TestMentoringBlock(unittest.TestCase): def test_sends_progress_event_when_rendered_student_view_with_display_submit_false(self): block = MentoringBlock(MagicMock(), DictFieldData({ @@ -31,6 +32,21 @@ def test_does_not_send_progress_event_when_rendered_student_view_with_display_su self.assertFalse(patched_runtime.publish.called) + @ddt.data(True, False) + def test_get_content_titles(self, has_title_set): + """ + Test that we don't send a title to the LMS for the sequential's tooltips when no title + is set + """ + if has_title_set: + data = {'display_name': 'Custom Title'} + expected = ['Custom Title'] + else: + data = {} + expected = [] + block = MentoringBlock(MagicMock(), DictFieldData(data), Mock()) + self.assertEqual(block.get_content_titles(), expected) + @ddt.ddt class TestMentoringBlockTheming(unittest.TestCase): From 0bf1ae616e67a6e2f2d0593a849367d51ecd0f8d Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Mon, 27 Jul 2015 17:24:56 -0700 Subject: [PATCH 2/2] Fix: answers in the same LMS sequential should auto-refresh --- problem_builder/answer.py | 25 ++++++++++++++----- problem_builder/public/js/answer.js | 24 ++++++++++++++++++ problem_builder/public/js/answer_recap.js | 22 ++++++++++++++++ .../templates/html/answer_editable.html | 1 + .../templates/html/answer_read_only.html | 2 +- 5 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 problem_builder/public/js/answer_recap.js diff --git a/problem_builder/answer.py b/problem_builder/answer.py index 94522964..b1314f1c 100644 --- a/problem_builder/answer.py +++ b/problem_builder/answer.py @@ -85,6 +85,23 @@ def get_model_object(self, name=None): ) return answer_data + @property + def student_input(self): + if self.name: + return self.get_model_object().student_input + return '' + + @XBlock.json_handler + def answer_value(self, data, suffix=''): + """ Current value of the answer, for refresh by client """ + return {'value': self.student_input} + + @XBlock.json_handler + def refresh_html(self, data, suffix=''): + """ Complete HTML view of the XBlock, for refresh by client """ + frag = self.mentoring_view({}) + return {'html': frag.content} + def validate_field_data(self, validation, data): """ Validate this block's field data. @@ -277,12 +294,6 @@ class AnswerRecapBlock(AnswerMixin, StudioEditableXBlockMixin, XBlock): css_path = 'public/css/answer.css' - @property - def student_input(self): - if self.name: - return self.get_model_object().student_input - return '' - def mentoring_view(self, context=None): """ Render this XBlock within a mentoring block. """ context = context.copy() if context else {} @@ -308,6 +319,8 @@ def mentoring_view(self, context=None): fragment = Fragment(html) fragment.add_css_url(self.runtime.local_resource_url(self, self.css_path)) + fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/answer_recap.js')) + fragment.initialize_js('AnswerRecapBlock') return fragment def student_view(self, context=None): diff --git a/problem_builder/public/js/answer.js b/problem_builder/public/js/answer.js index 74f0ff4c..af9eb962 100644 --- a/problem_builder/public/js/answer.js +++ b/problem_builder/public/js/answer.js @@ -11,6 +11,11 @@ function AnswerBlock(runtime, element) { if (completed === 'True' && this.mode === 'standard') { checkmark.addClass('checkmark-correct icon-ok fa-check'); } + + // In the LMS, the HTML of multiple units can be loaded at once, + // and the user can flip among them. If that happens, the answer in + // our HTML may be out of date. + this.refreshAnswer(); }, submit: function() { @@ -69,6 +74,25 @@ function AnswerBlock(runtime, element) { } } return true; + }, + + refreshAnswer: function() { + $.ajax({ + type: 'POST', + url: runtime.handlerUrl(element, 'answer_value'), + data: '{}', + dataType: 'json', + success: function(data) { + // Update the answer to the latest, unless the user has made an edit + var newAnswer = data.value; + var $textarea = $(':input', element); + var currentAnswer = $textarea.val(); + var origAnswer = $('.orig-student-answer', element).text(); + if (currentAnswer == origAnswer && currentAnswer != newAnswer) { + $textarea.val(newAnswer); + } + }, + }); } }; } diff --git a/problem_builder/public/js/answer_recap.js b/problem_builder/public/js/answer_recap.js new file mode 100644 index 00000000..a267a17f --- /dev/null +++ b/problem_builder/public/js/answer_recap.js @@ -0,0 +1,22 @@ +function AnswerRecapBlock(runtime, element) { + return { + init: function(options) { + // In the LMS, the HTML of multiple units can be loaded at once, + // and the user can flip among them. If that happens, the answer in + // our HTML may be out of date. + this.refreshAnswer(); + }, + + refreshAnswer: function() { + $.ajax({ + type: 'POST', + url: runtime.handlerUrl(element, 'refresh_html'), + data: '{}', + dataType: 'json', + success: function(data) { + $(element).html(data.html); + } + }); + } + }; +} diff --git a/problem_builder/templates/html/answer_editable.html b/problem_builder/templates/html/answer_editable.html index 884156f8..5ef2915e 100644 --- a/problem_builder/templates/html/answer_editable.html +++ b/problem_builder/templates/html/answer_editable.html @@ -5,5 +5,6 @@ class="answer editable" cols="50" rows="10" name="input" data-min_characters="{{ self.min_characters }}" >{{ self.student_input }} + diff --git a/problem_builder/templates/html/answer_read_only.html b/problem_builder/templates/html/answer_read_only.html index ea376b8d..7ce10c09 100644 --- a/problem_builder/templates/html/answer_read_only.html +++ b/problem_builder/templates/html/answer_read_only.html @@ -1,5 +1,5 @@ {% load i18n %} -
+
{% if not hide_header %}

{{ title }}

{% endif %} {% if description %}

{{ description|safe }}

{% endif %}