def test_caching_site(self): site_cfg = SiteConfigurationFactory() site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=date(2018, 1, 1)) site_config.save() # Check that the site value is not retrieved from cache after save with self.assertNumQueries(1): self.assertTrue(ContentTypeGatingConfig.current(site=site_cfg.site).enabled) # Check that the site value can be retrieved from cache after read with self.assertNumQueries(0): self.assertTrue(ContentTypeGatingConfig.current(site=site_cfg.site).enabled) site_config.enabled = False site_config.save() # Check that the site value in cache was deleted on save with self.assertNumQueries(1): self.assertFalse(ContentTypeGatingConfig.current(site=site_cfg.site).enabled) global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=date(2018, 1, 1)) global_config.save() # Check that the site value is not updated in cache by changing the global value with self.assertNumQueries(0): self.assertFalse(ContentTypeGatingConfig.current(site=site_cfg.site).enabled)
def test_caching_site(self): site_cfg = SiteConfigurationFactory() site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1)) site_config.save() RequestCache.clear_all_namespaces() # Check that the site value is not retrieved from cache after save with self.assertNumQueries(1): self.assertTrue(ContentTypeGatingConfig.current(site=site_cfg.site).enabled) RequestCache.clear_all_namespaces() # Check that the site value can be retrieved from cache after read with self.assertNumQueries(0): self.assertTrue(ContentTypeGatingConfig.current(site=site_cfg.site).enabled) site_config.enabled = False site_config.save() RequestCache.clear_all_namespaces() # Check that the site value in cache was deleted on save with self.assertNumQueries(1): self.assertFalse(ContentTypeGatingConfig.current(site=site_cfg.site).enabled) global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1)) global_config.save() RequestCache.clear_all_namespaces() # Check that the site value is not updated in cache by changing the global value with self.assertNumQueries(0): self.assertFalse(ContentTypeGatingConfig.current(site=site_cfg.site).enabled)
def test_caching_org(self): course = CourseOverviewFactory.create(org='test-org') site_cfg = SiteConfigurationFactory.create(values={'course_org_filter': course.org}) org_config = ContentTypeGatingConfig(org=course.org, enabled=True, enabled_as_of=date(2018, 1, 1)) org_config.save() # Check that the org value is not retrieved from cache after save with self.assertNumQueries(2): self.assertTrue(ContentTypeGatingConfig.current(org=course.org).enabled) # Check that the org value can be retrieved from cache after read with self.assertNumQueries(0): self.assertTrue(ContentTypeGatingConfig.current(org=course.org).enabled) org_config.enabled = False org_config.save() # Check that the org value in cache was deleted on save with self.assertNumQueries(2): self.assertFalse(ContentTypeGatingConfig.current(org=course.org).enabled) global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=date(2018, 1, 1)) global_config.save() # Check that the org value is not updated in cache by changing the global value with self.assertNumQueries(0): self.assertFalse(ContentTypeGatingConfig.current(org=course.org).enabled) site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=date(2018, 1, 1)) site_config.save() # Check that the org value is not updated in cache by changing the site value with self.assertNumQueries(0): self.assertFalse(ContentTypeGatingConfig.current(org=course.org).enabled)
def test_caching_global(self): global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime( 2018, 1, 1)) global_config.save() RequestCache.clear_all_namespaces() # Check that the global value is not retrieved from cache after save with self.assertNumQueries(1): assert ContentTypeGatingConfig.current().enabled RequestCache.clear_all_namespaces() # Check that the global value can be retrieved from cache after read with self.assertNumQueries(0): assert ContentTypeGatingConfig.current().enabled global_config.enabled = False global_config.save() RequestCache.clear_all_namespaces() # Check that the global value in cache was deleted on save with self.assertNumQueries(1): assert not ContentTypeGatingConfig.current().enabled
def test_caching_org(self): course = CourseOverviewFactory.create(org='test-org') site_cfg = SiteConfigurationFactory.create(values={'course_org_filter': course.org}) org_config = ContentTypeGatingConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1)) org_config.save() # Check that the org value is not retrieved from cache after save with self.assertNumQueries(2): self.assertTrue(ContentTypeGatingConfig.current(org=course.org).enabled) # Check that the org value can be retrieved from cache after read with self.assertNumQueries(0): self.assertTrue(ContentTypeGatingConfig.current(org=course.org).enabled) org_config.enabled = False org_config.save() # Check that the org value in cache was deleted on save with self.assertNumQueries(2): self.assertFalse(ContentTypeGatingConfig.current(org=course.org).enabled) global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1)) global_config.save() # Check that the org value is not updated in cache by changing the global value with self.assertNumQueries(0): self.assertFalse(ContentTypeGatingConfig.current(org=course.org).enabled) site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1)) site_config.save() # Check that the org value is not updated in cache by changing the site value with self.assertNumQueries(0): self.assertFalse(ContentTypeGatingConfig.current(org=course.org).enabled)
def test_config_overrides(self, global_setting, site_setting, org_setting, course_setting): """ Test that the stacked configuration overrides happen in the correct order and priority. This is tested by exhaustively setting each combination of contexts, and validating that only the lowest level context that is set to not-None is applied. """ # Add a bunch of configuration outside the contexts that are being tested, to make sure # there are no leaks of configuration across contexts non_test_course_enabled = CourseOverviewFactory.create(org='non-test-org-enabled') non_test_course_disabled = CourseOverviewFactory.create(org='non-test-org-disabled') non_test_site_cfg_enabled = SiteConfigurationFactory.create( site_values={'course_org_filter': non_test_course_enabled.org} ) non_test_site_cfg_disabled = SiteConfigurationFactory.create( site_values={'course_org_filter': non_test_course_disabled.org} ) ContentTypeGatingConfig.objects.create( course=non_test_course_enabled, enabled=True, enabled_as_of=datetime(2018, 1, 1) ) ContentTypeGatingConfig.objects.create(course=non_test_course_disabled, enabled=False) ContentTypeGatingConfig.objects.create( org=non_test_course_enabled.org, enabled=True, enabled_as_of=datetime(2018, 1, 1) ) ContentTypeGatingConfig.objects.create(org=non_test_course_disabled.org, enabled=False) ContentTypeGatingConfig.objects.create( site=non_test_site_cfg_enabled.site, enabled=True, enabled_as_of=datetime(2018, 1, 1) ) ContentTypeGatingConfig.objects.create(site=non_test_site_cfg_disabled.site, enabled=False) # Set up test objects test_course = CourseOverviewFactory.create(org='test-org') test_site_cfg = SiteConfigurationFactory.create( site_values={'course_org_filter': test_course.org} ) ContentTypeGatingConfig.objects.create(enabled=global_setting, enabled_as_of=datetime(2018, 1, 1)) ContentTypeGatingConfig.objects.create( course=test_course, enabled=course_setting, enabled_as_of=datetime(2018, 1, 1) ) ContentTypeGatingConfig.objects.create( org=test_course.org, enabled=org_setting, enabled_as_of=datetime(2018, 1, 1) ) ContentTypeGatingConfig.objects.create( site=test_site_cfg.site, enabled=site_setting, enabled_as_of=datetime(2018, 1, 1) ) all_settings = [global_setting, site_setting, org_setting, course_setting] expected_global_setting = self._resolve_settings([global_setting]) expected_site_setting = self._resolve_settings([global_setting, site_setting]) expected_org_setting = self._resolve_settings([global_setting, site_setting, org_setting]) expected_course_setting = self._resolve_settings([global_setting, site_setting, org_setting, course_setting]) assert expected_global_setting == ContentTypeGatingConfig.current().enabled assert expected_site_setting == ContentTypeGatingConfig.current(site=test_site_cfg.site).enabled assert expected_org_setting == ContentTypeGatingConfig.current(org=test_course.org).enabled assert expected_course_setting == ContentTypeGatingConfig.current(course_key=test_course.id).enabled
def get_course_duration_info(course_key): """ Fetch course duration information from database. """ try: key = CourseKey.from_string(course_key) course = CourseOverview.objects.values('display_name').get(id=key) duration_config = CourseDurationLimitConfig.current(course_key=key) gating_config = ContentTypeGatingConfig.current(course_key=key) duration_enabled = CourseDurationLimitConfig.enabled_for_course(course_key=key) gating_enabled = ContentTypeGatingConfig.enabled_for_course(course_key=key) gating_dict = { 'enabled': gating_enabled, 'enabled_as_of': str(gating_config.enabled_as_of) if gating_config.enabled_as_of else 'N/A', 'reason': gating_config.provenances['enabled'].value } duration_dict = { 'enabled': duration_enabled, 'enabled_as_of': str(duration_config.enabled_as_of) if duration_config.enabled_as_of else 'N/A', 'reason': duration_config.provenances['enabled'].value } return { 'course_id': course_key, 'course_name': course.get('display_name'), 'gating_config': gating_dict, 'duration_config': duration_dict, } except (ObjectDoesNotExist, InvalidKeyError): return {}
def _get_course_duration_info(self, course_key): """ Fetch course duration information from database """ try: key = CourseKey.from_string(course_key) course = CourseOverview.objects.values('display_name').get(id=key) duration_config = CourseDurationLimitConfig.current(course_key=key) gating_config = ContentTypeGatingConfig.current(course_key=key) duration_enabled = CourseDurationLimitConfig.enabled_for_course(course_key=key) gating_enabled = ContentTypeGatingConfig.enabled_for_course(course_key=key) gating_dict = { 'enabled': gating_enabled, 'enabled_as_of': str(gating_config.enabled_as_of) if gating_config.enabled_as_of else 'N/A', 'reason': gating_config.provenances['enabled'].value } duration_dict = { 'enabled': duration_enabled, 'enabled_as_of': str(duration_config.enabled_as_of) if duration_config.enabled_as_of else 'N/A', 'reason': duration_config.provenances['enabled'].value } return { 'course_id': course_key, 'course_name': course.get('display_name'), 'gating_config': gating_dict, 'duration_config': duration_dict, } except (ObjectDoesNotExist, InvalidKeyError): return {}
def test_caching_global(self): global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=date(2018, 1, 1)) global_config.save() # Check that the global value is not retrieved from cache after save with self.assertNumQueries(1): self.assertTrue(ContentTypeGatingConfig.current().enabled) # Check that the global value can be retrieved from cache after read with self.assertNumQueries(0): self.assertTrue(ContentTypeGatingConfig.current().enabled) global_config.enabled = False global_config.save() # Check that the global value in cache was deleted on save with self.assertNumQueries(1): self.assertFalse(ContentTypeGatingConfig.current().enabled)
def test_caching_global(self): global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1)) global_config.save() # Check that the global value is not retrieved from cache after save with self.assertNumQueries(1): self.assertTrue(ContentTypeGatingConfig.current().enabled) # Check that the global value can be retrieved from cache after read with self.assertNumQueries(0): self.assertTrue(ContentTypeGatingConfig.current().enabled) global_config.enabled = False global_config.save() # Check that the global value in cache was deleted on save with self.assertNumQueries(1): self.assertFalse(ContentTypeGatingConfig.current().enabled)
def test_config_overrides(self, global_setting, site_setting, org_setting, course_setting): """ Test that the stacked configuration overrides happen in the correct order and priority. This is tested by exhaustively setting each combination of contexts, and validating that only the lowest level context that is set to not-None is applied. """ # Add a bunch of configuration outside the contexts that are being tested, to make sure # there are no leaks of configuration across contexts non_test_course_enabled = CourseOverviewFactory.create(org='non-test-org-enabled') non_test_course_disabled = CourseOverviewFactory.create(org='non-test-org-disabled') non_test_site_cfg_enabled = SiteConfigurationFactory.create(values={'course_org_filter': non_test_course_enabled.org}) non_test_site_cfg_disabled = SiteConfigurationFactory.create(values={'course_org_filter': non_test_course_disabled.org}) ContentTypeGatingConfig.objects.create(course=non_test_course_enabled, enabled=True, enabled_as_of=datetime(2018, 1, 1)) ContentTypeGatingConfig.objects.create(course=non_test_course_disabled, enabled=False) ContentTypeGatingConfig.objects.create(org=non_test_course_enabled.org, enabled=True, enabled_as_of=datetime(2018, 1, 1)) ContentTypeGatingConfig.objects.create(org=non_test_course_disabled.org, enabled=False) ContentTypeGatingConfig.objects.create(site=non_test_site_cfg_enabled.site, enabled=True, enabled_as_of=datetime(2018, 1, 1)) ContentTypeGatingConfig.objects.create(site=non_test_site_cfg_disabled.site, enabled=False) # Set up test objects test_course = CourseOverviewFactory.create(org='test-org') test_site_cfg = SiteConfigurationFactory.create(values={'course_org_filter': test_course.org}) ContentTypeGatingConfig.objects.create(enabled=global_setting, enabled_as_of=datetime(2018, 1, 1)) ContentTypeGatingConfig.objects.create(course=test_course, enabled=course_setting, enabled_as_of=datetime(2018, 1, 1)) ContentTypeGatingConfig.objects.create(org=test_course.org, enabled=org_setting, enabled_as_of=datetime(2018, 1, 1)) ContentTypeGatingConfig.objects.create(site=test_site_cfg.site, enabled=site_setting, enabled_as_of=datetime(2018, 1, 1)) all_settings = [global_setting, site_setting, org_setting, course_setting] expected_global_setting = self._resolve_settings([global_setting]) expected_site_setting = self._resolve_settings([global_setting, site_setting]) expected_org_setting = self._resolve_settings([global_setting, site_setting, org_setting]) expected_course_setting = self._resolve_settings([global_setting, site_setting, org_setting, course_setting]) self.assertEqual(expected_global_setting, ContentTypeGatingConfig.current().enabled) self.assertEqual(expected_site_setting, ContentTypeGatingConfig.current(site=test_site_cfg.site).enabled) self.assertEqual(expected_org_setting, ContentTypeGatingConfig.current(org=test_course.org).enabled) self.assertEqual(expected_course_setting, ContentTypeGatingConfig.current(course_key=test_course.id).enabled)
def test_caching_course(self): course = CourseOverviewFactory.create(org='test-org') site_cfg = SiteConfigurationFactory.create( site_values={'course_org_filter': course.org} ) course_config = ContentTypeGatingConfig(course=course, enabled=True, enabled_as_of=datetime(2018, 1, 1)) course_config.save() RequestCache.clear_all_namespaces() # Check that the org value is not retrieved from cache after save with self.assertNumQueries(2): assert ContentTypeGatingConfig.current(course_key=course.id).enabled RequestCache.clear_all_namespaces() # Check that the org value can be retrieved from cache after read with self.assertNumQueries(0): assert ContentTypeGatingConfig.current(course_key=course.id).enabled course_config.enabled = False course_config.save() RequestCache.clear_all_namespaces() # Check that the org value in cache was deleted on save with self.assertNumQueries(2): assert not ContentTypeGatingConfig.current(course_key=course.id).enabled global_config = ContentTypeGatingConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1)) global_config.save() RequestCache.clear_all_namespaces() # Check that the org value is not updated in cache by changing the global value with self.assertNumQueries(0): assert not ContentTypeGatingConfig.current(course_key=course.id).enabled site_config = ContentTypeGatingConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1)) site_config.save() RequestCache.clear_all_namespaces() # Check that the org value is not updated in cache by changing the site value with self.assertNumQueries(0): assert not ContentTypeGatingConfig.current(course_key=course.id).enabled org_config = ContentTypeGatingConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1)) org_config.save() RequestCache.clear_all_namespaces() # Check that the org value is not updated in cache by changing the site value with self.assertNumQueries(0): assert not ContentTypeGatingConfig.current(course_key=course.id).enabled
def _get_course_duration_info(self, course_key): """ Fetch course duration information from database """ results = [] try: key = CourseKey.from_string(course_key) course = CourseOverview.objects.values('display_name').get(id=key) duration_config = CourseDurationLimitConfig.current(course_key=key) gating_config = ContentTypeGatingConfig.current(course_key=key) partially_enabled = duration_config.enabled != gating_config.enabled if partially_enabled: if duration_config.enabled: enabled = 'Course Duration Limits Only' enabled_as_of = str( duration_config.enabled_as_of ) if duration_config.enabled_as_of else 'N/A' reason = 'Course duration limits are enabled for this course, but content type gating is disabled.' elif gating_config.enabled: enabled = 'Content Type Gating Only' enabled_as_of = str( gating_config.enabled_as_of ) if gating_config.enabled_as_of else 'N/A' reason = 'Content type gating is enabled for this course, but course duration limits are disabled.' else: enabled = duration_config.enabled or False enabled_as_of = str( duration_config.enabled_as_of ) if duration_config.enabled_as_of else 'N/A' reason = duration_config.provenances['enabled'] data = { 'course_id': course_key, 'course_name': course.get('display_name'), 'enabled': enabled, 'enabled_as_of': enabled_as_of, 'reason': reason, } results.append(data) except (ObjectDoesNotExist, InvalidKeyError): pass return results
def create_content_gating_partition(course): """ Create and return the Content Gating user partition. """ enabled_for_course = ContentTypeGatingConfig.enabled_for_course( course_key=course.id) studio_override_for_course = ContentTypeGatingConfig.current( course_key=course.id).studio_override_enabled if not (enabled_for_course or studio_override_for_course): return None try: content_gate_scheme = UserPartition.get_scheme( CONTENT_TYPE_GATING_SCHEME) except UserPartitionError: LOG.warning( u"No %r scheme registered, ContentTypeGatingPartitionScheme will not be created.", CONTENT_TYPE_GATING_SCHEME) return None used_ids = set(p.id for p in course.user_partitions) if CONTENT_GATING_PARTITION_ID in used_ids: # It's possible for course authors to add arbitrary partitions via XML import. If they do, and create a # partition with id 51, it will collide with the Content Gating Partition. We'll catch that here, and # then fix the course content as needed (or get the course team to). LOG.warning( u"Can't add %r partition, as ID %r is assigned to %r in course %s.", CONTENT_TYPE_GATING_SCHEME, CONTENT_GATING_PARTITION_ID, _get_partition_from_id(course.user_partitions, CONTENT_GATING_PARTITION_ID).name, six.text_type(course.id), ) return None partition = content_gate_scheme.create_user_partition( id=CONTENT_GATING_PARTITION_ID, # Content gating partition name should not be marked for translations # edX mobile apps expect it in english name=u"Feature-based Enrollments", description=_( u"Partition for segmenting users by access to gated content types" ), parameters={"course_id": six.text_type(course.id)}) return partition
def create_content_gating_partition(course): """ Create and return the Content Gating user partition. """ enabled_for_course = ContentTypeGatingConfig.enabled_for_course(course_key=course.id) studio_override_for_course = ContentTypeGatingConfig.current(course_key=course.id).studio_override_enabled if not (enabled_for_course or studio_override_for_course): return None try: content_gate_scheme = UserPartition.get_scheme(CONTENT_TYPE_GATING_SCHEME) except UserPartitionError: LOG.warning( u"No %r scheme registered, ContentTypeGatingPartitionScheme will not be created.", CONTENT_TYPE_GATING_SCHEME ) return None used_ids = set(p.id for p in course.user_partitions) if CONTENT_GATING_PARTITION_ID in used_ids: # It's possible for course authors to add arbitrary partitions via XML import. If they do, and create a # partition with id 51, it will collide with the Content Gating Partition. We'll catch that here, and # then fix the course content as needed (or get the course team to). LOG.warning( u"Can't add %r partition, as ID %r is assigned to %r in course %s.", CONTENT_TYPE_GATING_SCHEME, CONTENT_GATING_PARTITION_ID, _get_partition_from_id(course.user_partitions, CONTENT_GATING_PARTITION_ID).name, unicode(course.id), ) return None partition = content_gate_scheme.create_user_partition( id=CONTENT_GATING_PARTITION_ID, name=_(u"Feature-based Enrollments"), description=_(u"Partition for segmenting users by access to gated content types"), parameters={"course_id": unicode(course.id)} ) return partition
def _get_course_duration_info(self, course_key): """ Fetch course duration information from database """ results = [] try: key = CourseKey.from_string(course_key) course = CourseOverview.objects.values('display_name').get(id=key) duration_config = CourseDurationLimitConfig.current(course_key=key) gating_config = ContentTypeGatingConfig.current(course_key=key) partially_enabled = duration_config.enabled != gating_config.enabled if partially_enabled: if duration_config.enabled: enabled = 'Course Duration Limits Only' enabled_as_of = str(duration_config.enabled_as_of) if duration_config.enabled_as_of else 'N/A' reason = 'Course duration limits are enabled for this course, but content type gating is disabled.' elif gating_config.enabled: enabled = 'Content Type Gating Only' enabled_as_of = str(gating_config.enabled_as_of) if gating_config.enabled_as_of else 'N/A' reason = 'Content type gating is enabled for this course, but course duration limits are disabled.' else: enabled = duration_config.enabled or False enabled_as_of = str(duration_config.enabled_as_of) if duration_config.enabled_as_of else 'N/A' reason = duration_config.provenances['enabled'] data = { 'course_id': course_key, 'course_name': course.get('display_name'), 'enabled': enabled, 'enabled_as_of': enabled_as_of, 'reason': reason, } results.append(data) except (ObjectDoesNotExist, InvalidKeyError): pass return results
def create_content_gating_partition(course): """ Create and return the Content Gating user partition. """ enabled_for_course = ContentTypeGatingConfig.enabled_for_course(course_key=course.id) studio_override_for_course = ContentTypeGatingConfig.current(course_key=course.id).studio_override_enabled if not (enabled_for_course or studio_override_for_course): return None try: content_gate_scheme = UserPartition.get_scheme("content_type_gate") except UserPartitionError: LOG.warning("No 'content_type_gate' scheme registered, ContentTypeGatingPartitionScheme will not be created.") return None used_ids = set(p.id for p in course.user_partitions) if CONTENT_GATING_PARTITION_ID in used_ids: # It's possible for course authors to add arbitrary partitions via XML import. If they do, and create a # partition with id 51, it will collide with the Content Gating Partition. We'll catch that here, and # then fix the course content as needed (or get the course team to). LOG.warning( "Can't add 'content_type_gate' partition, as ID {id} is assigned to {partition} in course {course}.".format( id=CONTENT_GATING_PARTITION_ID, partition=_get_partition_from_id(course.user_partitions, CONTENT_GATING_PARTITION_ID).name, course=unicode(course.id) ) ) return None partition = content_gate_scheme.create_user_partition( id=CONTENT_GATING_PARTITION_ID, name=_(u"Feature-based Enrollments"), description=_(u"Partition for segmenting users by access to gated content types"), parameters={"course_id": unicode(course.id)} ) return partition
def get_visibility_partition_info(xblock, course=None): """ Retrieve user partition information for the component visibility editor. This pre-processes partition information to simplify the template. Arguments: xblock (XBlock): The component being edited. course (XBlock): The course descriptor. If provided, uses this to look up the user partitions instead of loading the course. This is useful if we're calling this function multiple times for the same course want to minimize queries to the modulestore. Returns: dict """ selectable_partitions = [] # We wish to display enrollment partitions before cohort partitions. enrollment_user_partitions = get_user_partition_info(xblock, schemes=["enrollment_track"], course=course) # For enrollment partitions, we only show them if there is a selected group or # or if the number of groups > 1. for partition in enrollment_user_partitions: if len(partition["groups"]) > 1 or any(group["selected"] for group in partition["groups"]): selectable_partitions.append(partition) course_key = xblock.scope_ids.usage_id.course_key is_library = isinstance(course_key, LibraryLocator) if not is_library and ContentTypeGatingConfig.current(course_key=course_key).studio_override_enabled: selectable_partitions += get_user_partition_info(xblock, schemes=[CONTENT_TYPE_GATING_SCHEME], course=course) # Now add the cohort user partitions. selectable_partitions = selectable_partitions + get_user_partition_info(xblock, schemes=["cohort"], course=course) # Find the first partition with a selected group. That will be the one initially enabled in the dialog # (if the course has only been added in Studio, only one partition should have a selected group). selected_partition_index = -1 # At the same time, build up all the selected groups as they are displayed in the dialog title. selected_groups_label = '' for index, partition in enumerate(selectable_partitions): for group in partition["groups"]: if group["selected"]: if len(selected_groups_label) == 0: selected_groups_label = group['name'] else: # Translators: This is building up a list of groups. It is marked for translation because of the # comma, which is used as a separator between each group. selected_groups_label = _(u'{previous_groups}, {current_group}').format( previous_groups=selected_groups_label, current_group=group['name'] ) if selected_partition_index == -1: selected_partition_index = index return { "selectable_partitions": selectable_partitions, "selected_partition_index": selected_partition_index, "selected_groups_label": selected_groups_label, }
def get_visibility_partition_info(xblock, course=None): """ Retrieve user partition information for the component visibility editor. This pre-processes partition information to simplify the template. Arguments: xblock (XBlock): The component being edited. course (XBlock): The course descriptor. If provided, uses this to look up the user partitions instead of loading the course. This is useful if we're calling this function multiple times for the same course want to minimize queries to the modulestore. Returns: dict """ selectable_partitions = [] # We wish to display enrollment partitions before cohort partitions. enrollment_user_partitions = get_user_partition_info( xblock, schemes=["enrollment_track"], course=course) # For enrollment partitions, we only show them if there is a selected group or # or if the number of groups > 1. for partition in enrollment_user_partitions: if len(partition["groups"]) > 1 or any( group["selected"] for group in partition["groups"]): selectable_partitions.append(partition) course_key = xblock.scope_ids.usage_id.course_key is_library = isinstance(course_key, LibraryLocator) if not is_library and ContentTypeGatingConfig.current( course_key=course_key).studio_override_enabled: selectable_partitions += get_user_partition_info( xblock, schemes=[CONTENT_TYPE_GATING_SCHEME], course=course) # Now add the cohort user partitions. selectable_partitions = selectable_partitions + get_user_partition_info( xblock, schemes=["cohort"], course=course) # Find the first partition with a selected group. That will be the one initially enabled in the dialog # (if the course has only been added in Studio, only one partition should have a selected group). selected_partition_index = -1 # At the same time, build up all the selected groups as they are displayed in the dialog title. selected_groups_label = '' for index, partition in enumerate(selectable_partitions): for group in partition["groups"]: if group["selected"]: if len(selected_groups_label) == 0: selected_groups_label = group['name'] else: # Translators: This is building up a list of groups. It is marked for translation because of the # comma, which is used as a separator between each group. selected_groups_label = _( u'{previous_groups}, {current_group}').format( previous_groups=selected_groups_label, current_group=group['name']) if selected_partition_index == -1: selected_partition_index = index return { "selectable_partitions": selectable_partitions, "selected_partition_index": selected_partition_index, "selected_groups_label": selected_groups_label, }