From 507b85ee3d42837fa26d7ae81719b513fda320d6 Mon Sep 17 00:00:00 2001 From: "Dave St.Germain" Date: Fri, 3 Jan 2020 15:07:44 -0500 Subject: [PATCH] Return sequence metadata from an ajax handler endpoint, to support the new courseware micro-frontend. --- common/lib/xmodule/xmodule/seq_module.py | 56 +++++++++++++------ .../xmodule/xmodule/tests/test_sequence.py | 10 ++++ 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py index f69eb705a0..401b7006da 100644 --- a/common/lib/xmodule/xmodule/seq_module.py +++ b/common/lib/xmodule/xmodule/seq_module.py @@ -237,6 +237,24 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule): return json.dumps({ 'complete': complete }) + elif dispatch == 'metadata': + if self.category != 'sequential': + return json.dumps({}) + context = {'exclude_units': True} + prereq_met = True + prereq_meta_info = {} + banner_text = None + display_items = self.get_display_items() + + if self._required_prereq(): + if self.runtime.user_is_staff: + banner_text = _('This subsection is unlocked for learners when they meet the prerequisite requirements.') + else: + # check if prerequisite has been met + prereq_met, prereq_meta_info = self._compute_is_prereq_met(True) + meta = self._get_render_metadata(context, display_items, prereq_met, prereq_meta_info, banner_text, STUDENT_VIEW) + meta['display_name'] = self.display_name_with_default + return json.dumps(meta) raise NotFoundError('Unexpected dispatch type') @classmethod @@ -345,27 +363,18 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule): # NOTE (CCB): We default to true to maintain the behavior in place prior to allowing anonymous access access. return context.get('user_authenticated', True) - def _student_or_public_view(self, context, prereq_met, prereq_meta_info, banner_text=None, view=STUDENT_VIEW): - """ - Returns the rendered student view of the content of this - sequential. If banner_text is given, it is added to the - content. - """ - _ = self.runtime.service(self, "i18n").ugettext - display_items = self.get_display_items() - self._update_position(context, len(display_items)) - + def _get_render_metadata(self, context, display_items, prereq_met, prereq_meta_info, banner_text=None, view=STUDENT_VIEW, fragment=None): if prereq_met and not self._is_gate_fulfilled(): banner_text = _( 'This section is a prerequisite. You must complete this section in order to unlock additional content.' ) - fragment = Fragment() items = self._render_student_view_for_items(context, display_items, fragment, view) if prereq_met else [] params = { 'items': items, 'element_id': self.location.html_id(), 'item_id': text_type(self.location), + 'is_time_limited': self.is_time_limited, 'position': self.position, 'tag': self.location.block_type, 'ajax_url': self.system.ajax_url, @@ -377,6 +386,20 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule): 'gated_content': self._get_gated_content_info(prereq_met, prereq_meta_info), 'exclude_units': context.get('exclude_units', False) } + return params + + def _student_or_public_view(self, context, prereq_met, prereq_meta_info, banner_text=None, view=STUDENT_VIEW): + """ + Returns the rendered student view of the content of this + sequential. If banner_text is given, it is added to the + content. + """ + _ = self.runtime.service(self, "i18n").ugettext + display_items = self.get_display_items() + self._update_position(context, len(display_items)) + + fragment = Fragment() + params = self._get_render_metadata(context, display_items, prereq_met, prereq_meta_info, banner_text, view, fragment) fragment.add_content(self.system.render_template("seq_module.html", params)) self._capture_full_seq_item_metrics(display_items) @@ -474,11 +497,11 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule): """ render_items = not context.get('exclude_units', False) is_user_authenticated = self.is_user_authenticated(context) + completion_service = self.runtime.service(self, 'completion') if render_items: bookmarks_service = self.runtime.service(self, 'bookmarks') - completion_service = self.runtime.service(self, 'completion') else: - bookmarks_service = completion_service = None + bookmarks_service = None context['username'] = self.runtime.service(self, 'user').get_current_user().opt_attrs.get( 'edx-platform.username') display_names = [ @@ -527,10 +550,9 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule): # The item url format can be defined in the template context like so: # context['item_url'] = '/my/item/path/{usage_key}/whatever' iteminfo['href'] = context.get('item_url', '').format(usage_key=usage_id) - if is_user_authenticated and render_items: - if item.location.block_type == 'vertical': - if completion_service: - iteminfo['complete'] = completion_service.vertical_is_complete(item) + if is_user_authenticated: + if item.location.block_type == 'vertical' and completion_service: + iteminfo['complete'] = completion_service.vertical_is_complete(item) contents.append(iteminfo) diff --git a/common/lib/xmodule/xmodule/tests/test_sequence.py b/common/lib/xmodule/xmodule/tests/test_sequence.py index c394397bbe..194760cd1d 100644 --- a/common/lib/xmodule/xmodule/tests/test_sequence.py +++ b/common/lib/xmodule/xmodule/tests/test_sequence.py @@ -343,6 +343,16 @@ class SequenceBlockTestCase(XModuleXmlImportTest): ) self.assertIs(completion_return, None) + def test_handle_ajax_metadata(self): + """ + Test that the sequence metadata is returned from the + metadata ajax handler. + """ + metadata = json.loads(self.sequence_3_1.handle_ajax('metadata', {})) + self.assertEqual(len(metadata['items']), 3) + self.assertEqual(metadata['tag'], 'sequential') + self.assertEqual(metadata['display_name'], self.sequence_3_1.display_name_with_default) + def get_context_dict_from_string(self, data): """ Retrieve dictionary from string.