def test_can_get_usage_info_when_special_characters_are_used(self): """ Test if group configurations json updated successfully when special characters are being used in content experiment """ self._add_user_partitions(count=1) vertical, __ = self._create_content_experiment(cid=0, name_suffix='0', special_characters=u"JOSÉ ANDRÉS") actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course, ) expected = [{ 'id': 0, 'name': 'Name 0', 'scheme': 'random', 'description': 'Description 0', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [{ 'url': '/container/{}'.format(vertical.location), 'label': u"Test Unit 0 / Test Content Experiment 0JOSÉ ANDRÉS", 'validation': None, }], }] self.assertEqual(actual, expected)
def test_can_get_correct_usage_info_with_orphan(self, module_store_type): """ Test if content group json updated successfully with usage information even if there is an orphan in content group. """ self.course = CourseFactory.create(default_store=module_store_type) self._add_user_partitions(count=1, scheme_id='cohort') vertical, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0', orphan=True) # Assert that there is an orphan in the course, and that it's the vertical self.assertEqual(len(self.store.get_orphans(self.course.id)), 1) self.assertIn(vertical.location, self.store.get_orphans(self.course.id)) # Get the expected content group information based on module store. if module_store_type == ModuleStoreEnum.Type.mongo: expected = self._get_expected_content_group(usage_for_group=[ { 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Problem 0' } ]) else: expected = self._get_expected_content_group(usage_for_group=[]) # Get the actual content group information actual = GroupConfiguration.get_or_create_content_group(self.store, self.course) # Assert that actual content group information is same as expected one. self.assertEqual(actual, expected)
def test_can_use_one_content_group_in_multiple_problems(self): """ Test if multiple problems are present in usage info when they use same content group. """ self._add_user_partitions(scheme_id='cohort') vertical, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0') vertical1, __ = self._create_problem_with_content_group( cid=0, group_id=1, name_suffix='1') actual = GroupConfiguration.get_or_create_content_group( self.store, self.course) expected = self._get_expected_content_group( usage_for_group=[{ 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Problem 0' }, { 'url': '/container/{}'.format(vertical1.location), 'label': 'Test Unit 1 / Test Problem 1' }]) self.assertEqual(actual, expected)
def test_can_get_usage_info_when_special_characters_are_used(self): """ Test if group configurations json updated successfully when special characters are being used in content experiment """ self._add_user_partitions(count=1) __, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0', special_characters=u"JOSÉ ANDRÉS") actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course, ) expected = [{ 'id': 0, 'name': 'Name 0', 'scheme': 'random', 'description': 'Description 0', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [{ 'url': reverse_usage_url("container_handler", split_test.location), 'label': u"Test Unit 0 / Test Content Experiment 0JOSÉ ANDRÉS", 'validation': None, }], 'parameters': {}, 'active': True, }] self.assertEqual(actual, expected)
def test_can_use_one_configuration_in_multiple_experiments(self): """ Test if multiple experiments are present in usage info when they use same group configuration. """ self._add_user_partitions() __, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0') __, split_test1, __ = self._create_content_experiment(cid=0, name_suffix='1') actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course) expected = [{ 'id': 0, 'name': 'Name 0', 'scheme': 'random', 'description': 'Description 0', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [{ 'url': '/container/{}'.format(split_test.location), 'label': 'Test Unit 0 / Test Content Experiment 0', 'validation': None, }, { 'url': '/container/{}'.format(split_test1.location), 'label': 'Test Unit 1 / Test Content Experiment 1', 'validation': None, }], 'parameters': {}, 'active': True, }] self.assertEqual(actual, expected)
def test_can_get_correct_usage_info_with_orphan(self, module_store_type): """ Test if content group json updated successfully with usage information even if there is an orphan in content group. """ self.course = CourseFactory.create(default_store=module_store_type) self._add_user_partitions(count=1, scheme_id='cohort') vertical, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0', orphan=True) # Assert that there is an orphan in the course, and that it's the vertical self.assertEqual(len(self.store.get_orphans(self.course.id)), 1) self.assertIn(vertical.location, self.store.get_orphans(self.course.id)) # Get the expected content group information based on module store. if module_store_type == ModuleStoreEnum.Type.mongo: expected = self._get_expected_content_group(usage_for_group=[{ 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Problem 0' }]) else: expected = self._get_expected_content_group(usage_for_group=[]) # Get the actual content group information actual = GroupConfiguration.get_or_create_content_group( self.store, self.course) # Assert that actual content group information is same as expected one. self.assertEqual(actual, expected)
def test_can_use_one_configuration_in_multiple_experiments(self): """ Test if multiple experiments are present in usage info when they use same group configuration. """ self._add_user_partitions() vertical, __ = self._create_content_experiment(cid=0, name_suffix='0') vertical1, __ = self._create_content_experiment(cid=0, name_suffix='1') actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course) expected = [{ 'id': 0, 'name': 'Name 0', 'scheme': 'random', 'description': 'Description 0', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [{ 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Content Experiment 0', 'validation': None, }, { 'url': '/container/{}'.format(vertical1.location), 'label': 'Test Unit 1 / Test Content Experiment 1', 'validation': None, }], }] self.assertEqual(actual, expected)
def _get_user_partition(self, scheme): """ Returns the first user partition with the specified scheme. """ for group in GroupConfiguration.get_all_user_partition_details(self.store, self.course): if group['scheme'] == scheme: return group return None
def fetch_group_usage(cls, modulestore, structure): groups_usage_dict = {} groups_usage_info = GroupConfiguration.get_content_groups_usage_info( modulestore, structure).items() groups_usage_info.extend( GroupConfiguration.get_content_groups_items_usage_info( modulestore, structure).items()) if groups_usage_info: for name, group in groups_usage_info: for module in group: view, args, kwargs = resolve(module['url']) # pylint: disable=unused-variable usage_key_string = unicode(kwargs['usage_key_string']) if groups_usage_dict.get(usage_key_string, None): groups_usage_dict[usage_key_string].append(name) else: groups_usage_dict[usage_key_string] = [name] return groups_usage_dict
def test_content_group_not_used(self): """ Test that right data structure will be created if content group is not used. """ self._add_user_partitions(scheme_id='cohort') actual = GroupConfiguration.get_or_create_content_group(self.store, self.course) expected = self._get_expected_content_group(usage_for_group=[]) self.assertEqual(actual, expected)
def fetch_group_usage(cls, modulestore, structure): groups_usage_dict = {} partitions_info = GroupConfiguration.get_partitions_usage_info(modulestore, structure) content_group_info = GroupConfiguration.get_content_groups_items_usage_info( modulestore, structure ) for group_info in (partitions_info, content_group_info): for groups in group_info.values(): for name, group in groups.items(): for module in group: view, args, kwargs = resolve(module['url']) # pylint: disable=unused-variable usage_key_string = unicode(kwargs['usage_key_string']) if groups_usage_dict.get(usage_key_string, None): groups_usage_dict[usage_key_string].append(name) else: groups_usage_dict[usage_key_string] = [name] return groups_usage_dict
def fetch_group_usage(cls, modulestore, structure): groups_usage_dict = {} partitions_info = GroupConfiguration.get_partitions_usage_info(modulestore, structure) content_group_info = GroupConfiguration.get_content_groups_items_usage_info( modulestore, structure ) for group_info in (partitions_info, content_group_info): for groups in group_info.values(): for name, group in groups.items(): for module in group: view, args, kwargs = resolve(module['url']) # pylint: disable=unused-variable usage_key_string = text_type(kwargs['usage_key_string']) if groups_usage_dict.get(usage_key_string, None): groups_usage_dict[usage_key_string].append(name) else: groups_usage_dict[usage_key_string] = [name] return groups_usage_dict
def test_content_group_not_used(self): """ Test that right data structure will be created if content group is not used. """ self._add_user_partitions(scheme_id='cohort') actual = GroupConfiguration.get_or_create_content_group( self.store, self.course) expected = self._get_expected_content_group(usage_for_group=[]) self.assertEqual(actual, expected)
def test_can_handle_multiple_partitions(self): # Create the user partitions self.course.user_partitions = [ UserPartition( id=0, name='Cohort user partition', scheme=UserPartition.get_scheme('cohort'), description='Cohorted user partition', groups=[ Group(id=0, name="Group A"), Group(id=1, name="Group B"), ], ), UserPartition( id=1, name='Random user partition', scheme=UserPartition.get_scheme('random'), description='Random user partition', groups=[ Group(id=0, name="Group A"), Group(id=1, name="Group B"), ], ), ] self.store.update_item(self.course, ModuleStoreEnum.UserID.test) # Assign group access rules for multiple partitions, one of which is a cohorted partition __, problem = self._create_problem_with_content_group(0, 1) problem.group_access = { 0: [0], 1: [1], } self.store.update_item(problem, ModuleStoreEnum.UserID.test) # This used to cause an exception since the code assumed that # only one partition would be available. actual = GroupConfiguration.get_content_groups_usage_info( self.store, self.course) self.assertEqual(actual.keys(), [0]) actual = GroupConfiguration.get_content_groups_items_usage_info( self.store, self.course) self.assertEqual(actual.keys(), [0])
def test_can_handle_multiple_partitions(self): # Create the user partitions self.course.user_partitions = [ UserPartition( id=0, name='Cohort user partition', scheme=UserPartition.get_scheme('cohort'), description='Cohorted user partition', groups=[ Group(id=0, name="Group A"), Group(id=1, name="Group B"), ], ), UserPartition( id=1, name='Random user partition', scheme=UserPartition.get_scheme('random'), description='Random user partition', groups=[ Group(id=0, name="Group A"), Group(id=1, name="Group B"), ], ), ] self.store.update_item(self.course, ModuleStoreEnum.UserID.test) # Assign group access rules for multiple partitions, one of which is a cohorted partition __, problem = self._create_problem_with_content_group(0, 1) problem.group_access = { 0: [0], 1: [1], } self.store.update_item(problem, ModuleStoreEnum.UserID.test) # This used to cause an exception since the code assumed that # only one partition would be available. actual = GroupConfiguration.get_content_groups_usage_info(self.store, self.course) self.assertEqual(actual.keys(), [0]) actual = GroupConfiguration.get_content_groups_items_usage_info(self.store, self.course) self.assertEqual(actual.keys(), [0])
def test_can_handle_duplicate_group_ids(self): # Create the user partitions self.course.user_partitions = [ UserPartition( id=0, name='Cohort user partition 1', scheme=UserPartition.get_scheme('cohort'), description='Cohorted user partition', groups=[ Group(id=2, name="Group 1A"), Group(id=3, name="Group 1B"), ], ), UserPartition( id=1, name='Cohort user partition 2', scheme=UserPartition.get_scheme('cohort'), description='Random user partition', groups=[ Group(id=2, name="Group 2A"), Group(id=3, name="Group 2B"), ], ), ] self.store.update_item(self.course, ModuleStoreEnum.UserID.test) # Assign group access rules for multiple partitions, one of which is a cohorted partition self._create_problem_with_content_group(0, 2, name_suffix='0') self._create_problem_with_content_group(1, 3, name_suffix='1') # This used to cause an exception since the code assumed that # only one partition would be available. actual = GroupConfiguration.get_partitions_usage_info(self.store, self.course) self.assertEqual(actual.keys(), [0, 1]) self.assertEqual(actual[0].keys(), [2]) self.assertEqual(actual[1].keys(), [3]) actual = GroupConfiguration.get_content_groups_items_usage_info(self.store, self.course) self.assertEqual(actual.keys(), [0, 1]) self.assertEqual(actual[0].keys(), [2]) self.assertEqual(actual[1].keys(), [3])
def verify_validation_add_usage_info(self, expected_result, mocked_message, mocked_validation_messages): """ Helper method for testing validation information present after add_usage_info. """ self._add_user_partitions() split_test = self._create_content_experiment(cid=0, name_suffix='0')[1] validation = StudioValidation(split_test.location) validation.add(mocked_message) mocked_validation_messages.return_value = validation group_configuration = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course)[0] self.assertEqual(expected_result.to_json(), group_configuration['usage'][0]['validation'])
def val_items_visibility_by_group(self): """Составление таблицы видимости элементов для групп""" with self.store.bulk_operations(self.course_key): course = self.course content_group_configuration = GroupConfiguration.get_or_create_content_group(self.store, course) groups = content_group_configuration["groups"] group_names = [g["name"] for g in groups] head = [_("Item type"), _("Usual student")] + group_names visibility_by_group_categories = self.visibility_by_group_categories get_items_by_type = lambda x: [y for y in self.items if y.category == x] # Словарь (категория - итемы) cat_items = dict([(c, get_items_by_type(c)) for c in visibility_by_group_categories]) # Словарь id группы - название группы group_id_dict = dict([(g["id"], g["name"]) for g in groups]) conf_id = content_group_configuration["id"] group_visible_strs = [] for cat in visibility_by_group_categories: items = cat_items[cat] vis = dict((g, 0) for g in group_names) vis["student"] = 0 for it in items: if conf_id not in it.group_access: for key in group_names: vis[key] += 1 else: ids = it.group_access[conf_id] item_vis_for_groups = [group_id_dict[i] for i in ids] for group in item_vis_for_groups: vis[group] += 1 if not it.visible_to_staff_only: vis["student"] += 1 item_category = "{}({})".format(cat, len(items)) stud_vis_for_cat = str(vis["student"]) cat_list = [item_category] + [stud_vis_for_cat] + [str(vis[gn]) for gn in group_names] cat_str = cat_list group_visible_strs.append(cat_str) return Report(name=self.scenarios_names["items_visibility_by_group"], head=head, body=group_visible_strs, warnings=[] )
def test_can_get_correct_usage_info_for_content_groups(self): """ Test if content group json updated successfully with usage information. """ self._add_user_partitions(count=1, scheme_id='cohort') vertical, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0') actual = GroupConfiguration.get_or_create_content_group(self.store, self.course) expected = self._get_expected_content_group(usage_for_group=[ { 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Problem 0' } ]) self.assertEqual(actual, expected)
def test_can_get_correct_usage_info(self): """ Test if group configurations json updated successfully with usage information. """ self._add_user_partitions(count=2) __, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0') self._create_content_experiment(name_suffix='1') actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course) expected = [{ 'id': 0, 'name': 'Name 0', 'scheme': 'random', 'description': 'Description 0', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [{ 'url': '/container/{}'.format(split_test.location), 'label': 'Test Unit 0 / Test Content Experiment 0', 'validation': None, }], 'parameters': {}, 'active': True, }, { 'id': 1, 'name': 'Name 1', 'scheme': 'random', 'description': 'Description 1', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [], 'parameters': {}, 'active': True, }] self.assertEqual(actual, expected)
def test_can_get_correct_usage_info(self): """ Test if group configurations json updated successfully with usage information. """ self._add_user_partitions(count=2) vertical, __ = self._create_content_experiment(cid=0, name_suffix='0') self._create_content_experiment(name_suffix='1') actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course) expected = [{ 'id': 0, 'name': 'Name 0', 'scheme': 'random', 'description': 'Description 0', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [{ 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Content Experiment 0', 'validation': None, }], 'parameters': {}, 'active': True, }, { 'id': 1, 'name': 'Name 1', 'scheme': 'random', 'description': 'Description 1', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [], 'parameters': {}, 'active': True, }] self.assertEqual(actual, expected)
def test_can_handle_without_parent(self): """ Test if it possible to handle case when split_test has no parent. """ self._add_user_partitions() # Create split test without parent. with modulestore().branch_setting(ModuleStoreEnum.Branch.published_only): orphan = modulestore().create_item( ModuleStoreEnum.UserID.test, self.course.id, 'split_test', ) orphan.user_partition_id = 0 orphan.display_name = 'Test Content Experiment' modulestore().update_item(orphan, ModuleStoreEnum.UserID.test) self.save_course() actual = GroupConfiguration.get_content_experiment_usage_info(self.store, self.course) self.assertEqual(actual, {0: []})
def val_group(self): """Проверка наличия и использования в курсе групп""" with self.store.bulk_operations(self.course_key): course = self.course content_group_configuration = GroupConfiguration.get_or_create_content_group(self.store, course) groups = content_group_configuration["groups"] is_group_used = lambda x: bool(len(x["usage"])) # запись для каждой группы ее использования group_strs = [[g["name"], str(is_group_used(g))] for g in groups] head = [_("Group name"), _("Group used")] results = Report(name=self.scenarios_names["group"], head=head, body=group_strs, warnings=[], ) return results
def test_can_get_correct_usage_info_for_content_groups(self): """ Test if content group json updated successfully with usage information. """ self._add_user_partitions(count=1, scheme_id='cohort') vertical, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0') actual = GroupConfiguration.get_or_create_content_group( self.store, self.course) expected = self._get_expected_content_group( usage_for_group=[{ 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Problem 0' }]) self.assertEqual(actual, expected)
def test_group_configuration_not_used(self): """ Test that right data structure will be created if group configuration is not used. """ self._add_user_partitions() actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course) expected = [{ 'id': 0, 'name': 'Name 0', 'scheme': 'random', 'description': 'Description 0', 'version': UserPartition.VERSION, 'groups': [ {'id': 0, 'name': 'Group A', 'version': 1}, {'id': 1, 'name': 'Group B', 'version': 1}, {'id': 2, 'name': 'Group C', 'version': 1}, ], 'usage': [], }] self.assertEqual(actual, expected)
def test_group_configuration_not_used(self): """ Test that right data structure will be created if group configuration is not used. """ self._add_user_partitions() actual = GroupConfiguration.get_split_test_partitions_with_usage( self.store, self.course) expected = [{ 'id': 0, 'name': 'Name 0', 'scheme': 'random', 'description': 'Description 0', 'version': UserPartition.VERSION, 'groups': [ { 'id': 0, 'name': 'Group A', 'version': 1 }, { 'id': 1, 'name': 'Group B', 'version': 1 }, { 'id': 2, 'name': 'Group C', 'version': 1 }, ], 'usage': [], 'parameters': {}, 'active': True, }] self.assertEqual(actual, expected)
def test_can_get_correct_usage_info_with_orphan(self, module_store_type): """ Test if content group json updated successfully with usage information even if there is an orphan in content group. """ self.course = CourseFactory.create(default_store=module_store_type) self._add_user_partitions(count=1, scheme_id='cohort') vertical, problem = self._create_problem_with_content_group( cid=0, group_id=1, name_suffix='0') # Assert that there is no orphan in the course yet. self.assertEqual(len(self.store.get_orphans(self.course.id)), 0) # Update problem(created earlier) to an orphan. with self.store.branch_setting(ModuleStoreEnum.Branch.published_only): vertical = self.store.get_item(vertical.location) vertical.children.remove(problem.location) self.store.update_item(vertical, self.user.id) # Assert that the problem is orphan now. self.assertIn(problem.location, self.store.get_orphans(self.course.id)) # Get the expected content group information based on module store. if module_store_type == ModuleStoreEnum.Type.mongo: expected = self._get_expected_content_group(usage_for_group=[{ 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Problem 0' }]) else: expected = self._get_expected_content_group(usage_for_group=[]) # Get the actual content group information actual = GroupConfiguration.get_or_create_content_group( self.store, self.course) # Assert that actual content group information is same as expected one. self.assertEqual(actual, expected)
def test_can_use_one_content_group_in_multiple_problems(self): """ Test if multiple problems are present in usage info when they use same content group. """ self._add_user_partitions(scheme_id='cohort') vertical, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0') vertical1, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='1') actual = GroupConfiguration.get_or_create_content_group(self.store, self.course) expected = self._get_expected_content_group(usage_for_group=[ { 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Problem 0' }, { 'url': '/container/{}'.format(vertical1.location), 'label': 'Test Unit 1 / Test Problem 1' } ]) self.assertEqual(actual, expected)
def test_can_get_correct_usage_info_with_orphan(self, module_store_type): """ Test if content group json updated successfully with usage information even if there is an orphan in content group. """ self.course = CourseFactory.create(default_store=module_store_type) self._add_user_partitions(count=1, scheme_id='cohort') vertical, problem = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0') # Assert that there is no orphan in the course yet. self.assertEqual(len(self.store.get_orphans(self.course.id)), 0) # Update problem(created earlier) to an orphan. with self.store.branch_setting(ModuleStoreEnum.Branch.published_only): vertical = self.store.get_item(vertical.location) vertical.children.remove(problem.location) self.store.update_item(vertical, self.user.id) # Assert that the problem is orphan now. self.assertIn(problem.location, self.store.get_orphans(self.course.id)) # Get the expected content group information based on module store. if module_store_type == ModuleStoreEnum.Type.mongo: expected = self._get_expected_content_group(usage_for_group=[ { 'url': '/container/{}'.format(vertical.location), 'label': 'Test Unit 0 / Test Problem 0' } ]) else: expected = self._get_expected_content_group(usage_for_group=[]) # Get the actual content group information actual = GroupConfiguration.get_or_create_content_group(self.store, self.course) # Assert that actual content group information is same as expected one. self.assertEqual(actual, expected)
def handle(self, *args, **options): errors = [] module_store = modulestore() print "Starting Swap from Auto Track Cohort Pilot command" verified_track_cohorts_setting = self._latest_settings() if not verified_track_cohorts_setting: raise CommandError("No MigrateVerifiedTrackCohortsSetting found") if not verified_track_cohorts_setting.enabled: raise CommandError("No enabled MigrateVerifiedTrackCohortsSetting found") old_course_key = verified_track_cohorts_setting.old_course_key rerun_course_key = verified_track_cohorts_setting.rerun_course_key audit_cohort_names = verified_track_cohorts_setting.get_audit_cohort_names() # Verify that the MigrateVerifiedTrackCohortsSetting has all required fields if not old_course_key: raise CommandError("No old_course_key set for MigrateVerifiedTrackCohortsSetting with ID: '%s'" % verified_track_cohorts_setting.id) if not rerun_course_key: raise CommandError("No rerun_course_key set for MigrateVerifiedTrackCohortsSetting with ID: '%s'" % verified_track_cohorts_setting.id) if not audit_cohort_names: raise CommandError("No audit_cohort_names set for MigrateVerifiedTrackCohortsSetting with ID: '%s'" % verified_track_cohorts_setting.id) print "Running for MigrateVerifiedTrackCohortsSetting with old_course_key='%s' and rerun_course_key='%s'" % \ (verified_track_cohorts_setting.old_course_key, verified_track_cohorts_setting.rerun_course_key) # Get the CourseUserGroup IDs for the audit course names from the old course audit_course_user_group_ids = CourseUserGroup.objects.filter( name__in=audit_cohort_names, course_id=old_course_key, group_type=CourseUserGroup.COHORT, ).values_list('id', flat=True) if not audit_course_user_group_ids: raise CommandError( "No Audit CourseUserGroup found for course_id='%s' with group_type='%s' for names='%s'" % (old_course_key, CourseUserGroup.COHORT, audit_cohort_names) ) # Get all of the audit CourseCohorts from the above IDs that are RANDOM random_audit_course_user_group_ids = CourseCohort.objects.filter( course_user_group_id__in=audit_course_user_group_ids, assignment_type=CourseCohort.RANDOM ).values_list('course_user_group_id', flat=True) if not random_audit_course_user_group_ids: raise CommandError( "No Audit CourseCohorts found for course_user_group_ids='%s' with assignment_type='%s" % (audit_course_user_group_ids, CourseCohort.RANDOM) ) # Get the CourseUserGroupPartitionGroup for the above IDs, these contain the partition IDs and group IDs # that are set for group_access inside of modulestore random_audit_course_user_group_partition_groups = list(CourseUserGroupPartitionGroup.objects.filter( course_user_group_id__in=random_audit_course_user_group_ids )) if not random_audit_course_user_group_partition_groups: raise CommandError( "No Audit CourseUserGroupPartitionGroup found for course_user_group_ids='%s'" % random_audit_course_user_group_ids ) # Get the single VerifiedTrackCohortedCourse for the old course try: verified_track_cohorted_course = VerifiedTrackCohortedCourse.objects.get(course_key=old_course_key) except VerifiedTrackCohortedCourse.DoesNotExist: raise CommandError("No VerifiedTrackCohortedCourse found for course: '%s'" % old_course_key) if not verified_track_cohorted_course.enabled: raise CommandError("VerifiedTrackCohortedCourse not enabled for course: '%s'" % old_course_key) # Get the single CourseUserGroupPartitionGroup for the verified_track # based on the verified_track name for the old course try: verified_course_user_group = CourseUserGroup.objects.get( course_id=old_course_key, group_type=CourseUserGroup.COHORT, name=verified_track_cohorted_course.verified_cohort_name ) except CourseUserGroup.DoesNotExist: raise CommandError( "No Verified CourseUserGroup found for course_id='%s' with group_type='%s' for names='%s'" % (old_course_key, CourseUserGroup.COHORT, verified_track_cohorted_course.verified_cohort_name) ) try: verified_course_user_group_partition_group = CourseUserGroupPartitionGroup.objects.get( course_user_group_id=verified_course_user_group.id ) except CourseUserGroupPartitionGroup.DoesNotExist: raise CommandError( "No Verified CourseUserGroupPartitionGroup found for course_user_group_ids='%s'" % random_audit_course_user_group_ids ) # Verify the enrollment track CourseModes exist for the new course try: CourseMode.objects.get( course_id=rerun_course_key, mode_slug=CourseMode.AUDIT ) except CourseMode.DoesNotExist: raise CommandError("Audit CourseMode is not defined for course: '%s'" % rerun_course_key) try: CourseMode.objects.get( course_id=rerun_course_key, mode_slug=CourseMode.VERIFIED ) except CourseMode.DoesNotExist: raise CommandError("Verified CourseMode is not defined for course: '%s'" % rerun_course_key) items = module_store.get_items(rerun_course_key) if not items: raise CommandError("Items for Course with key '%s' not found." % rerun_course_key) items_to_update = [] all_cohorted_track_group_ids = set() for audit_course_user_group_partition_group in random_audit_course_user_group_partition_groups: all_cohorted_track_group_ids.add(audit_course_user_group_partition_group.group_id) all_cohorted_track_group_ids.add(verified_course_user_group_partition_group.group_id) for item in items: # Verify that there exists group access for this xblock, otherwise skip these checks if item.group_access: set_audit_enrollment_track = False set_verified_enrollment_track = False # Check the partition and group IDs for the audit course groups, if they exist in # the xblock's access settings then set the audit track flag to true for audit_course_user_group_partition_group in random_audit_course_user_group_partition_groups: audit_partition_group_access = item.group_access.get( audit_course_user_group_partition_group.partition_id, None ) if (audit_partition_group_access and audit_course_user_group_partition_group.group_id in audit_partition_group_access): print "Queueing XBlock at location: '%s' for Audit Content Group update " % item.location set_audit_enrollment_track = True # Check the partition and group IDs for the verified course group, if it exists in # the xblock's access settings then set the verified track flag to true verified_partition_group_access = item.group_access.get( verified_course_user_group_partition_group.partition_id, None ) if verified_partition_group_access: non_verified_track_access_groups = (set(verified_partition_group_access) - all_cohorted_track_group_ids) # If the item has group_access that is not the # verified or audit group IDs then raise an error # This only needs to be checked for this partition_group once if non_verified_track_access_groups: errors.append( "Non audit/verified cohorted content group set for xblock, location '%s' with IDs '%s'" % (item.location, non_verified_track_access_groups) ) if verified_course_user_group_partition_group.group_id in verified_partition_group_access: print "Queueing XBlock at location: '%s' for Verified Content Group update " % item.location set_verified_enrollment_track = True # Add the enrollment track ids to a group access array enrollment_track_group_access = [] if set_audit_enrollment_track: enrollment_track_group_access.append(settings.COURSE_ENROLLMENT_MODES['audit']) if set_verified_enrollment_track: enrollment_track_group_access.append(settings.COURSE_ENROLLMENT_MODES['verified']) # If there are no errors, and either the audit track, or verified # track needed an update, set the access, update and publish if set_verified_enrollment_track or set_audit_enrollment_track: # Sets whether or not an xblock has changes has_changes = module_store.has_changes(item) # Check that the xblock does not have changes and add it to be updated, otherwise add an error if not has_changes: item.group_access = {ENROLLMENT_TRACK_PARTITION_ID: enrollment_track_group_access} items_to_update.append(item) else: errors.append("XBlock '%s' with location '%s' needs access changes, but is a draft" % (item.display_name, item.location)) partitions_to_delete = random_audit_course_user_group_partition_groups partitions_to_delete.append(verified_course_user_group_partition_group) # If there are no errors iterate over and update all of the items that had the access changed if not errors: for item in items_to_update: module_store.update_item(item, ModuleStoreEnum.UserID.mgmt_command) module_store.publish(item.location, ModuleStoreEnum.UserID.mgmt_command) print "Updated and published XBlock at location: '%s'" % item.location # Check if we should delete any partition groups if there are no errors. # If there are errors, none of the xblock items will have been updated, # so this section will throw errors for each partition in use if partitions_to_delete and not errors: partition_service = PartitionService(rerun_course_key) course = partition_service.get_course() for partition_to_delete in partitions_to_delete: # Get the user partition, and the index of that partition in the course partition = partition_service.get_user_partition(partition_to_delete.partition_id) if partition: partition_index = course.user_partitions.index(partition) group_id = int(partition_to_delete.group_id) # Sanity check to verify that all of the groups being deleted are empty, # since they should have been converted to use enrollment tracks instead. # Taken from contentstore/views/course.py.remove_content_or_experiment_group usages = GroupConfiguration.get_partitions_usage_info(module_store, course) used = group_id in usages if used: errors.append("Content group '%s' is in use and cannot be deleted." % partition_to_delete.group_id) # If there are not errors, proceed to update the course and user_partitions if not errors: # Remove the groups that match the group ID of the partition to be deleted # Else if there are no match groups left, remove the user partition matching_groups = [group for group in partition.groups if group.id == group_id] if matching_groups: group_index = partition.groups.index(matching_groups[0]) partition.groups.pop(group_index) # Update the course user partition with the updated groups if partition.groups: course.user_partitions[partition_index] = partition else: course.user_partitions.pop(partition_index) module_store.update_item(course, ModuleStoreEnum.UserID.mgmt_command) # If there are any errors, join them together and raise the CommandError if errors: raise CommandError( ("Error for MigrateVerifiedTrackCohortsSetting with ID='%s'\n" % verified_track_cohorts_setting.id) + "\t\n".join(errors) ) print "Finished for MigrateVerifiedTrackCohortsSetting with ID='%s" % verified_track_cohorts_setting.id
def val_openassessment(self): head = [_("Name"), _("Location"), _("Publishing date"), _("Submission start"), _("Submission due"), _("Peer start"), _("Peer due"), _("Cohorts where visible"), _("Assessment steps")] openassessments = [i for i in self.items if i.category == "openassessment"] additional_info = {} body = [] with self.store.bulk_operations(self.course_key): course = self.course content_group_configuration = GroupConfiguration.get_or_create_content_group(self.store, course) groups = content_group_configuration["groups"] conf_id = content_group_configuration["id"] group_id_dict = dict([(g["id"], g["name"]) for g in groups]) unicode2date = lambda x: datetime.strptime(x.split('+')[0], '%Y-%m-%dT%H:%M:%S') date2str = lambda x: x.strftime("%d.%m.%Y, %H:%M") for num, oa in enumerate(openassessments): url_key = (num, 0) additional_info[url_key] = reverse_usage_url("container_handler", oa.location) current = [] name = oa.display_name current.append(name) parent = oa.get_parent() location = u"{}, {}".format(parent.display_name, parent.get_parent().display_name) current.append(location) publish_date = oa.published_on if publish_date: current.append(date2str(publish_date)) else: current.append(_("Not published")) submission_start = unicode2date(oa.submission_start) current.append(date2str(submission_start)) submission_due = unicode2date(oa.submission_due) current.append(date2str(submission_due)) peer_start = unicode2date(oa.rubric_assessments[1]["start"]) current.append(date2str(peer_start)) peer_due = unicode2date(oa.rubric_assessments[1]["due"]) current.append(date2str(peer_due)) if conf_id in oa.group_access: accessed = oa.group_access[conf_id] visible_groups = u", ".join(group_id_dict[i] for i in accessed) else: visible_groups = _("Usual student") current.append(visible_groups) steps = oa.assessment_steps current.append(u",".join(str(s) for s in range(1, len(steps) + 1))) body.append(current) self.additional_info.update({self.scenarios_names["openassessment"]: additional_info}) return Report(name=self.scenarios_names["openassessment"], head=head, body=body, warnings=[] )